Simple Clock with Raphael 2.1.4 - javascript

Newbie here, I have a simple clock with Raphael 2.1.4 based on a tutorial everything renders on the page fine but the handles are not rotating. However it does seem to work on Raphael 1.5.X.
Can you tell me what I am doing wrong?
Code pen page attached
http://codepen.io/scorpion_rain/pen/dGrmQj
Code JS:
window.onload = function() {
var cWidth = 800, cHeight = 600;
var paper = new Raphael(document.getElementById('canvas_container'), cWidth, cHeight);
var clockBgX = 200, clockBgY = 300, clockBgRadius = 100;
var cBgCol = '#f5f5f5', cBgStrokeCol = '#444444', cBgStrokeWidth = '5';
var centerDialX = clockBgX, centerDialY = clockBgY;
var hourHandStrokeCol = "#444444", hourHandStrokeWidth = "5";
var minuteHandStrokeCol = "#444444", minuteHandStrokeWidth = "3";
var secondsHandStrokeCol = "#444444", secondsHandStrokeWidth = "2";
var pinDotX = clockBgX, pinDotY = clockBgY, pinDotRadius = 5, pinDotFill = "#000000";
// main clock background and number display
function mainClockBg(){
var clockBg = paper.circle(clockBgX, clockBgY, clockBgRadius); // outer circle
clockBg.attr({ "fill": cBgCol, // inner background
"stroke": cBgStrokeCol,
"stroke-width": cBgStrokeWidth })
var radius = Math.PI / 180; // converting from degrees to radian
var cx = centerDialX; // center of dial - horizontal
var cy = centerDialY; // center of dial - vertical
var r = 90; // radius distance
var startA = -90; // start of numbers
var angle = 30; // distance between numbers | 12/360 = 12 stops
var x; // circle distance inner marker
var y; // circle distance outer marker
var endA; // end of text
// adding numbers to the dial
for(i = 1; i < 13; i++){
endA = startA + angle;
x = cx + r * Math.cos( endA * radius );
y = cy + r * Math.sin( endA * radius );
paper.text( x, y, i + "" );
startA = endA;
}
}
function dialsDisplay(){
var hourHand = paper.path("M200, 300L200, 230");
hourHand.attr({
"stroke": hourHandStrokeCol,
"stroke-width": hourHandStrokeWidth
});
var minuteHand = paper.path("M200, 300L200, 220");
minuteHand.attr({
"stroke": minuteHandStrokeCol,
"stroke-width": minuteHandStrokeWidth
})
var secondsHand = paper.path("M200, 300L200, 216");
secondsHand.attr({
"stroke": secondsHandStrokeCol,
"stroke-width": secondsHandStrokeWidth
})
var pinDot = paper.circle(pinDotX, pinDotY, pinDotRadius);
pinDot.attr({
"fill": pinDotFill
});
updateClock();
setInterval("updateClock()", 1000);
}
function updateClock(){
var time = new Date();
var hours = time.getHours();
var minutes = time.getMinutes();
var seconds = time.getSeconds();
hourHand.rotate( 30 * hours + (minutes / 2.5), 200, 300 );
minuteHand.rotate( 6 * minutes, 200, 300 );
secondsHand.rotete(6 * seconds, 200, 300);
}
mainClockBg();
dialsDisplay();
}
in the console of the browser i get the error 'cant find variable hourHand'
Any help appreciated
Cheers.

You are defining a variable with 'var' in one function, and then referencing it in another, this sets where the scope is.
Javascript has functional scope, so one function won't know about a variable unless its defined in a scope it can access. So move the hourHand, minuteHand, secondsHand outside of the function. Then the updateClock method will be able to access those variables.
// THIS WONT WORK
function func1() {
var hand = 1;
}
function func2() {
var test = hand; //hand was declared in other function, so not available here
}
So try this form instead
var hand;
function func1() {
hand = 1;
}
function func2() {
var test = hand;
}
Also you will want
setInterval(updateClock, 1000);
rather than
setInterval("updateClock()", 1000);
as setInterval wants a function to call, rather than a string to execute.
Then I'm guessing there is some issue with the movement of the hands you will need to adjust :).
codepen

Related

I can't get all the circles to change color in the codehs javascript changing circles program

var NUM_CIRCLES = 15;
var MIN_RADIUS = 10;
var MAX_RADIUS = 40;
var DELAY = 500;
function start(){
drawCircles();
setTimer(update, DELAY);
}
function drawCircles(){
for(var i = 0; i < NUM_CIRCLES; i++){
circle = new Circle(Randomizer.nextInt(MIN_RADIUS, MAX_RADIUS));
var x = Randomizer.nextInt(MIN_RADIUS, getWidth() - MAX_RADIUS);
var y = Randomizer.nextInt(MIN_RADIUS, getHeight() - MAX_RADIUS);
circle.setPosition(x, y);
add(circle);
}
}
function update(){
var colors = Randomizer.nextColor();
circle.setColor(colors);
}
Write a program that creates a list of NUM_CIRCLES circles on the screen of different sizes from MIN_RADIUS to MAX_RADIUS. Every DELAY milliseconds, get a random color and change all of the circles to this color. All the circles should have the same color at all times.
First, you should declare circle as a global variable since you use circle in multiple functions. Next you should get rid of the drawCircles function in the start function and put it inside the timer. Then call the update function in your drawCircles function.
This is the revised code.
var NUM_CIRCLES = 15;
var MIN_RADIUS = 10;
var MAX_RADIUS = 40;
var DELAY = 500;
var circle;
function start(){
setTimer(drawCircles, DELAY);
}
function drawCircles(){
for(var i = 0; i < NUM_CIRCLES; i++){
circle = new Circle(Randomizer.nextInt(MIN_RADIUS, MAX_RADIUS));
var x = Randomizer.nextInt(MIN_RADIUS, getWidth() - MAX_RADIUS);
var y = Randomizer.nextInt(MIN_RADIUS, getHeight() - MAX_RADIUS);
update();
circle.setPosition(x, y);
add(circle);
}
}
function update(){
var colors = Randomizer.nextColor();
circle.setColor(colors);
}
setTimer(update, DELAY);
here when you call your update function you do not have () after update change it to:
setTimer(update(), DELAY);

unlimited stack of tile(object/sprite) in que after dragging the first to toppest one in phaser

i am making a game using phaser and i want to make unlimited no of tiles can think like stacked tiles. When the user drags the topmost tile and places it in the drop area, the same tile below it should appear and should be draggable.
My present code:- `
var greenTile = this.add.sprite(250,500,'greenTile');
greenTile.inputEnabled=true;
greenTile.input.enableDrag();
greenTile.events.onDragStart.add(onDragStart, this);
greenTile.events.onDragStop.add(onDragStop, this);`
the total code is of my game state is :-
`
var patternsRatio = patternsRatio || {};
var x = 50;
var y = 100;
var canvas;
var canvasBG;
var canvasGrid;
var canvasSprite;
var canvasZoom = 32;
var spriteWidth = 8;
var spriteHeight = 8;
patternsRatio.game1 = function () {};
patternsRatio.game1.prototype = {
preload: function () {
this.load.image('greenTile','assets/images/greentile.png')
this.load.image('redTile','assets/images/redtile.png')
},
create: function () {
this.game.stage.setBackgroundColor(0x2d2d2d);
var question_l1 = "Puzzle 1: Enlarge this pattern by a factor of 2."
var style_l1 = {font: "25px Comic Sans MS Bold", fill: "#fff", align:"center"};
var quet_l1=this.game.add.text(this.game.world.centerX,this.game.height-600, question_l1, style_l1)
quet_l1.anchor.setTo(0.5)
this.placetiles()
var greenTile = this.add.sprite(250,500,'greenTile');
greenTile.inputEnabled=true;
greenTile.input.enableDrag();
greenTile.events.onDragStart.add(onDragStart, this);
greenTile.events.onDragStop.add(onDragStop, this);
var redTile = this.add.sprite(100,500,'redTile');
redTile.inputEnabled=true;
redTile.input.enableDrag();
redTile.events.onDragStart.add(onDragStart, this);
redTile.events.onDragStop.add(onDragStop, this);
},
update: function () {
},
placetiles: function () {
this.game.create.grid('drawingGrid', 16 * canvasZoom, 16 * canvasZoom, canvasZoom, canvasZoom, 'rgba(0,191,243,0.8)');
canvas = this.game.make.bitmapData(spriteWidth * canvasZoom, spriteHeight * canvasZoom);
canvasBG = this.game.make.bitmapData(canvas.width + 2, canvas.height + 2);
canvasBG.rect(0, 0, canvasBG.width, canvasBG.height, '#fff');
canvasBG.rect(1, 1, canvasBG.width - 2, canvasBG.height - 2, '#3f5c67');
var x = 80;
var y = 64;
canvasBG.addToWorld(x, y);
canvasSprite = canvas.addToWorld( x + 1, y + 1); canvasZoom,canvasZoom
canvasGrid = this.game.add.sprite(x + 1, y + 1, 'drawingGrid');
canvasGrid.crop(new Phaser.Rectangle(0, 0, spriteWidth * canvasZoom, spriteHeight * canvasZoom));
}
};
`
I am also facing problem when i am adding a input.enable code below the "greenTile" variable then the redTile vanishes of can any tell why this happens.
In the image below as we can see both red and green tile are appearing.
In this image as we can i had added input enable and the red Tile stopped appearing, Secondly as have dragged the green tile to the grid, i want on the original place their should be again the greenTile should come.(this is in reference to the stack able unlimited tile question)
I could figure this out by just adding a function and calling it on the drag start event as below:-
The function that i added is :-
addGreenTiles: function(){
this.greenTile1 = this.add.sprite(250,500,'greenTile');
this.greenTile1.inputEnabled=true;
this.greenTile1.input.enableSnap(canvasZoom, canvasZoom, false, true);
this.greenTile1.input.enableDrag();
this.greenTile1.originalPosition = this.greenTile1.position.clone();
this.greenTile1.events.onDragStart.add(this.addGreenTiles, this);
this.greenTile1.events.onDragStop.add(function(currentSprite){
this.stopDrag(currentSprite, this.greenTile1);
}, this);
Then i called this function at :
this.greenTile1.events.onDragStart.add(this.addGreenTiles, this);

drawImage with Canvas is sending image to the back of the canvas [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have been doing a little experimenting with canvas, creating drawings using lines shapes text etc, and inserting .png files. The inserting .png files is the bit that I cannot get to work.
Edit:
Undesired behaviour of this code: I load shapes to the graphics context, then load an image file to the graphics context, however when the graphics context is drawn, the image is at behind the shapes, despite being drawn last.
I wanted the image file to be at the top, in front of the shapes.
Desired behaviour: To bring image file to the front of the canvas, so it is not hidden by shapes drawn in the graphics context.
function loadImage(name) {
images[name] = new Image();
images[name].src = "DogWalking/" + name + ".png";
images[name].onload = function() {
graphics.drawImage(this, 0, 300);
canvas.bringToFront(this);
};
}
the function for drawing is called here:
function draw() {
graphics.save(); // to make sure changes don't carry over from one call to the next
graphics.fillStyle = "transparent"; // background color
graphics.fillRect(0,0,wWidth, wHeight);
graphics.fillStyle = "black";
applyLimits(graphics,xleft,xright,ytop,ybottom,true);
graphics.lineWidth = pixelSize;
world.draw(graphics);
graphics.drawImage(images["dog-walking11"],200,200);
graphics.restore();
}
code for the whole page is
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
<title>Hierarchical Modeling 2D</title>
<style>
#messagediv {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 0;
background-color: indigo;
}
#canvasdiv {
position: absolute;
left: 0;
top: 0;
z-index: 10;
background-color: transparent;
}
</style>
<script type="text/javascript" src="rgbcolor.js"></script>
<script type="text/javascript">
"use strict";
var totalResources = 17;
var numResourcesLoaded = 0;
var images = {};
function loadImage(name) {
images[name] = new Image();
images[name].src = "DogWalking/" + name + ".png";
images[name].onload = function() {
//
graphics.drawImage(this, 0, 300);
canvas.bringToFront(this);
}
}
var canvas; // DOM object corresponding to the canvas
var graphics; // 2D graphics context for drawing on the canvas
var ctx; // 2D graphics context for drawing on the canvas
var myNumber = 0, myNumber2 = 0, myInterval, myInterval2, myelement, thisdiv, printx;
var mycoords = new Array();
var pcoords = new Array(); //coordinates of the portal.
//var pcoords = [[0,0], [50,300], [250,150]]; //coordinates of the portal.
var nocoords = 2;
var frameNumber = 0; // Current frame number.
var frameNumber2 = 0;
var sun;
var sun2;
var ground;
var world;
var pixelSize;
var wWidth;
var wHeight;
var portals = new Array("calendar1","alternativsearch","art1", "directory1");
var portalsval = new Array();
var portalsobj;
var leftj = new Array(3,1,4,2);
var forwards = "http://www.alternativworld.com";
// ---------------- Set Page Layout ----------------
// function to set size of canvas and location of portals
function pageLayout() {
var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0];
wWidth = w.innerWidth || e.clientWidth || g.clientWidth;
wHeight = w.innerHeight|| e.clientHeight|| g.clientHeight;
// Adjust wWidth and wHeight if ratio does not match scenary 7 by 5.
if (wWidth/wHeight != 7/5)
if (wWidth/wHeight > 7/5) {
var widthPortion = 5 * wWidth/wHeight;
wWidth = wWidth * 7 / widthPortion;
} else {
var heightPortion = 7 * wHeight/wWidth;
wHeight = wHeight * 5 / heightPortion;
}
var widthheight, localerror = false;
widthheight = Math.min(wWidth, wHeight);
if(widthheight < 400){
var localerror = true;
}
if (localerror == true)
alert("Warning, the page size of your browser or your screen resolution may be too small to correctly view this web page.");
var theCanvas = d.getElementById("theCanvas");
theCanvas.height = wHeight;
theCanvas.width = wWidth;
}
//Function to listen to the mouse events and see if a link is selected.
function doMouseDown(evt) {
var r = canvas.getBoundingClientRect();
var x = Math.round(evt.clientX - r.left);
var y = Math.round(evt.clientY - r.top);
alert(evt.clientX+ " " + evt.clientY);
for (var i = portals.length+1; i >= 0; i--) {
var p = pcoords[i];
if (Math.abs(p[0] - x) <= 50 && Math.abs(p[1] - y) <= 50) {
document.location.href = forwards;
return;
} else if (Math.abs(0 - x) <= 50 && Math.abs(0 - y) <= 50){
document.location.href = "http://www.alternativeuk.co.uk";
return;
}
}
}
// ---------------- The object-oriented scene graph API ------------------
/**
* The base class for all nodes in the scene graph data structure.
*/
function SceneGraphNode() {
this.fillColor = null; // If non-null, the default fillStyle for this node.
this.strokeColor = null; // If non-null, the default strokeStyle for this node.
}
SceneGraphNode.prototype.doDraw = function(g) {
// This method is meant to be abstract and must be
// OVERRIDDEN in any actual object in the scene graph.
// It is not meant to be called; it is called by draw().
throw "doDraw not implemented in SceneGraphNode"
}
SceneGraphNode.prototype.draw = function(g) {
// This method should be CALLED to draw the object
// represented by this SceneGraphNode. It should NOT
// ordinarily be overridden in subclasses.
graphics.save();
if (this.fillColor) {
g.fillStyle = this.fillColor;
}
if (this.strokeColor) {
g.strokeStyle = this.strokeColor;
}
this.doDraw(g);
graphics.restore();
}
SceneGraphNode.prototype.setFillColor = function(color) {
// Sets fillColor for this node to color.
// Color should be a legal CSS color string, or null.
this.fillColor = color;
return this;
}
SceneGraphNode.prototype.setStrokeColor = function(color) {
// Sets strokeColor for this node to color.
// Color should be a legal CSS color string, or null.
this.strokeColor = color;
return this;
}
SceneGraphNode.prototype.setColor = function(color) {
// Sets both the fillColor and strokeColor to color.
// Color should be a legal CSS color string, or null.
this.fillColor = color;
this.strokeColor = color;
return this;
}
/**
* Defines a subclass, CompoundObject, of SceneGraphNode to represent
* an object that is made up of sub-objects. Initially, there are no
* sub-objects.
*/
function CompoundObject() {
SceneGraphNode.call(this); // do superclass initialization
this.subobjects = []; // the list of sub-objects of this object
}
CompoundObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
CompoundObject.prototype.add = function(node) {
// Add node a subobject of this object. Note that the
// return value is a reference to this node, to allow chaining
// of method calls.
this.subobjects.push(node);
return this;
}
CompoundObject.prototype.doDraw = function(g) {
// Just call the sub-objects' draw() methods.
for (var i = 0; i < this.subobjects.length; i++)
this.subobjects[i].draw(g);
}
/**
* Define a subclass, TransformedObject, of SceneGraphNode that
* represents an object along with a modeling transformation to
* be applied to that object. The object must be specified in
* the constructor. The transformation is specified by calling
* the setScale(), setRotate() and setTranslate() methods. Note that
* each of these methods returns a reference to the TransformedObject
* as its return value, to allow for chaining of method calls.
* The modeling transformations are always applied to the object
* in the order scale, then rotate, then translate.
*/
function TransformedObject(object) {
SceneGraphNode.call(this); // do superclass initialization
this.object = object;
this.rotationInDegrees = 0;
this.scaleX = 1;
this.scaleY = 1;
this.translateX = 0;
this.translateY = 0;
}
TransformedObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
TransformedObject.prototype.setRotation = function(angle) {
// Set the angle of rotation, measured in DEGREES. The rotation
// is always about the origin.
this.rotationInDegrees = angle;
return this;
}
TransformedObject.prototype.setScale = function(sx, sy) {
// Sets scaling factors.
this.scaleX = sx;
this.scaleY = sy;
return this;
}
TransformedObject.prototype.setTranslation = function(dx,dy) {
// Set translation mounts.
this.translateX = dx;
this.translateY = dy;
return this;
}
TransformedObject.prototype.doDraw = function(g) {
// Draws the object, with its modeling transformation.
g.save();
if (this.translateX != 0 || this.translateY != 0) {
g.translate(this.translateX, this.translateY);
}
if (this.rotationInDegrees != 0) {
g.rotate(this.rotationInDegrees/180*Math.PI);
}
if (this.scaleX != 1 || this.scaleY != 1) {
g.scale(this.scaleX, this.scaleY);
}
this.object.draw(g);
g.restore();
}
/**
* A subclass of SceneGraphNode representing filled triangles.
* The constructor specifies the vertices of the triangle:
* (x1,y1), (x2,y2), and (x3,y3).
*/
function Triangle(x1,y1,x2,y2,x3,y3) {
SceneGraphNode.call(this);
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
}
Triangle.prototype = new SceneGraphNode();
Triangle.prototype.doDraw = function(g) {
g.beginPath();
g.moveTo(this.x1,this.y1);
g.lineTo(this.x2,this.y2);
g.lineTo(this.x3,this.y3);
g.closePath();
g.fill();
}
/**
* Directly create a line object as a SceneGraphNode with a
* custom doDraw() method. line is of length 1 and
* extends along the x-axis from (0,0) to (1,0).
*/
var line = new SceneGraphNode();
line.doDraw = function(g) {
g.beginPath();
g.moveTo(0,0);
g.lineTo(1,0);
g.stroke();
}
/**
* Directly create a filled rectangle object as a SceneGraphNode with a
* custom doDraw() method. filledRect is a square with side 1, centered
* at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5).
*/
var filledRect = new SceneGraphNode();
filledRect.doDraw = function(g) {
g.fillRect(-0.5,-0.5,1,1);
}
/**
* Directly create a rectangle object as a SceneGraphNode with a
* custom doDraw() method. rect is a square with side 1, centered
* at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5). Only the
* outline of the square is drawn.
*/
var rect = new SceneGraphNode();
rect.doDraw = function(g) {
g.strokeRect(-0.5,-0.5,1,1);
}
/**
* Directly create a filled circle object as a SceneGraphNode with a
* custom doDraw() method. filledCircle is a circle with radius 0.5
* (diameter 1), centered at (0,0).
*/
var filledCircle = new SceneGraphNode();
filledCircle.doDraw = function(g) {
g.beginPath();
g.arc(0,0,0.5,0,2*Math.PI);
g.fill();
}
var clickHere = new SceneGraphNode();
clickHere.doDraw = function(g) {
g.fillText("click here :)",0,0)
}
/**
* Directly create a circle object as a SceneGraphNode with a
* custom doDraw() method. filledCircle is a circle with radius 0.5
* (diameter 1), centered at (0,0). Only the outline of the circle
* is drawn.
*/
var circle = new SceneGraphNode();
circle.doDraw = function(g) {
g.beginPath();
g.arc(0,0,0.5,0,2*Math.PI);
g.stroke();
}
var dog = new SceneGraphNode();
dog.doDraw = function(g) {
g.drawImage(images["dog-walking11"],-2, 2);
alert(images["dog-walking11"].name);
}
// -------------------- Specific to this application ----------------------------
/*
* Define two extra basic objects as SceneGraphNodes with custom doDraw() methods.
* One represents the ground, the other a vane for a windmill.
*/
var ground = new SceneGraphNode();
ground.doDraw = function(g) {
g.beginPath();
g.moveTo(0,-1);
g.lineTo(0,0.8);
g.lineTo(1.5,1.65);
g.lineTo(1.8,1.3);
g.lineTo(3,2.1);
g.lineTo(4.7,0.7);
g.lineTo(6.1,1.2);
g.lineTo(7,0.8);
g.lineTo(7,-1);
g.closePath();
g.fill();
}
var windmillVane = new SceneGraphNode();
windmillVane.doDraw = function(g) {
g.beginPath();
g.moveTo(0,0);
g.lineTo(0.5,0.1);
g.lineTo(1.5,0);
g.lineTo(0.5,-0.1);
g.closePath();
g.fill();
}
var world; // A SceneGraphNode representing the entire picture. This should
// be created in the createWorld() method.
var pixelSize; // The size of one pixel, in the transformed coordinates.
// This is used as the default width of a stroke.
var background = "#C8C8FF"; // A CSS color string giving the background color.
// the draw() function fills the canvas with this color.
var xleft = 0; // The requested xy-limits on the canvas, after the
var xright = 7; // coordinate transformation has been applied.
var ybottom = -1; // The transformation is applied in the draw() function.
var ytop = 4;
var frameNumber = 0; // Current frame number.
var cart; // TransformedObjects that are animated.
var wheel;
var sun;
var clickText1;
var clickText2;
var rotor;
/**
* Create the scene graph data structure. The global variable world must
* refer to the root node of the scene graph. This function is called in
* the init() function.
*/
function createWorld() {
pageLayout();
var i;
var sunTemp = new CompoundObject();
sunTemp.setColor("yellow"); // color for filled circle and light rays
for (i = 0; i < 12; i++) { // add the 12 light rays, with different rotations
sunTemp.add( new TransformedObject(line).setScale(0.75,0.75).setRotation(i*30) );
}
sunTemp.add( filledCircle ); // the face of the sun
sunTemp.add( new TransformedObject(circle).setColor("#B40000") ); // outlines the face
sun = new TransformedObject(sunTemp);
clickText1 = new TransformedObject(clickHere).setColor("#B40000").setScale(0.01,-0.01);
var wheelTemp = new CompoundObject();
wheelTemp.setColor("black"); // color for all but one of the subobjects
wheelTemp.add( new TransformedObject(filledCircle).setScale(2,2) );
wheelTemp.add( new TransformedObject(filledCircle).setScale(1.6,1.6).setColor("#CCCCCC") );
wheelTemp.add( new TransformedObject(filledCircle).setScale(0.4,0.4) );
for (i = 0; i < 12; i++) { // add the 12 spokes
wheelTemp.add( new TransformedObject(line).setRotation(i*30) );
}
wheel = new TransformedObject(wheelTemp);
var cartTemp = new CompoundObject();
cartTemp.setColor("red"); // color for the rectangular body of the cart
cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(1.5,-0.1) );
cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(-1.5,-0.1) );
cartTemp.add( new TransformedObject(filledRect).setScale(5,2).setTranslation(0,1) ); // the body of the cart
cart = new TransformedObject(cartTemp).setScale(0.3,0.3);
clickText2 = new TransformedObject(clickHere).setColor("yellow").setScale(0.01,-0.01);
var rotorTemp = new CompoundObject(); // a "rotor" consisting of three vanes
rotorTemp.setColor( "#C86464" ); // color for all of the vanes
rotorTemp.add( windmillVane );
rotorTemp.add( new TransformedObject(windmillVane).setRotation(120) );
rotorTemp.add( new TransformedObject(windmillVane).setRotation(240) );
rotor = new TransformedObject(rotorTemp);
var windmill = new CompoundObject();
windmill.setColor("#E0C8C8"); // color for the pole
windmill.add( new TransformedObject(filledRect).setScale(0.1,3).setTranslation(0,1.5) ); // the pole
windmill.add( new TransformedObject(rotor).setTranslation(0,3) ); // the rotating vanes
world = new CompoundObject();
world.setColor("#00961E"); // color used for the ground only
world.add(ground);
//world.add( new TransformedObject(filledRect).setScale(7,0.8).setTranslation(3.5,0).setColor("#646496") ); // road
//world.add( new TransformedObject(filledRect).setScale(7,0.06).setTranslation(3.5,0).setColor("white") ); // line in road
world.add( new TransformedObject(windmill).setScale(0.6,0.6).setTranslation(0.75,1) );
world.add( new TransformedObject(windmill).setScale(0.4,0.4).setTranslation(2.2,1.3) );
world.add( new TransformedObject(windmill).setScale(0.7,0.7).setTranslation(3.7,0.8) );
world.add( new TransformedObject(sun).setTranslation(5.5,3.3) );
world.add( new TransformedObject(clickText1).setTranslation(5.25,3.3) );
world.add( cart );
world.add( clickText2 );
//alert(2);
}
/**
* This will be called before each frame is drawn.
*/
function updateFrame() {
frameNumber++;
if (frameNumber>= 312){
frameNumber = 0;
frameNumber2 = 1;
}
cart.setTranslation(-3 + 13*(frameNumber % 300) / 300.0, 0);
clickText2.setTranslation(-3.3 + 13*(frameNumber % 300) / 300.0, 0.25);
if (typeof(pcoords[5]) != 'undefined') {
pcoords[5][0] = (-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*canvas.width / (xright-xleft);
pcoords[5][1] = (0.25-ytop)*canvas.height / (ybottom-ytop);
}
wheel.setRotation(-frameNumber*3.1);
sun.setRotation(-frameNumber);
rotor.setRotation(frameNumber * 2.7);
}
// ------------------------------- graphics support functions --------------------------
/**
* Draw one frame of the animation. Probably doesn't need to be changed,
* except maybe to change the setting of preserveAspect in applyLimits().
*/
function draw() {
graphics.save(); // to make sure changes don't carry over from one call to the next
graphics.fillStyle = "transparent"; // background color
graphics.fillRect(0,0,wWidth, wHeight);
graphics.fillStyle = "black";
applyLimits(graphics,xleft,xright,ytop,ybottom,true);
graphics.lineWidth = pixelSize;
world.draw(graphics);
graphics.drawImage(images["dog-walking11"],200,200);
graphics.restore();
}
/**
* Applies a coordinate transformation to the graphics context, to map
* xleft,xright,ytop,ybottom to the edges of the canvas. This is called
* by draw(). This does not need to be changed.
*/
//pcoords[0][0] =
//pcoords[0][1]=
function applyLimits(g, xleft, xright, ytop, ybottom, preserveAspect) {
var width = canvas.width; // The width of this drawing area, in pixels.
var height = canvas.height; // The height of this drawing area, in pixels.
var k = portals.length;
var j;
var i = 0, widthheight, myradius;
var localerror = false;
if (pcoords.length < k) {
while (portals[i]){
j = i + 1;
if (width > 100){
var rWidth = width/(k + 1);
rWidth= Math.floor(rWidth);
} else {
var lWidth = 0;
var rWidth = 0;
}
if (height > 100){
var bHeight = height/(k + 1);
bHeight= Math.floor(bHeight);
} else {
var tHeight = 0;
var bHeight = 0;
}
var myleft = leftj[i] * rWidth - 50;
var mytop = j * bHeight - 50;
pcoords[i]= new Array;
pcoords[i][0] = myleft;
pcoords[i][1] = mytop;
i = i + 1;
}
}
if (preserveAspect) {
// Adjust the limits to match the aspect ratio of the drawing area.
var displayAspect = Math.abs(height / width);
var requestedAspect = Math.abs(( ybottom-ytop ) / ( xright-xleft ));
var excess;
if (displayAspect > requestedAspect) {
excess = (ybottom-ytop) * (displayAspect/requestedAspect - 1);
ybottom += excess/2;
ytop -= excess/2;
}
else if (displayAspect < requestedAspect) {
excess = (xright-xleft) * (requestedAspect/displayAspect - 1);
xright += excess/2;
xleft -= excess/2;
}
}
var pixelWidth = Math.abs(( xright - xleft ) / width);
var pixelHeight = Math.abs(( ybottom - ytop ) / height);
pixelSize = Math.min(pixelWidth,pixelHeight);
if (frameNumber == 4 || frameNumber == 5){
pcoords.push([(5.25-xleft)*width / (xright-xleft),(3.3-ytop)*height / (ybottom-ytop)]);
pcoords.push([(-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*width / (xright-xleft), (0.25-ytop)*height / (ybottom-ytop)]);
}
g.scale( width / (xright-xleft), height / (ybottom-ytop) );
g.translate( -xleft, -ytop );
// if (frameNumber < 3)
}
//------------------ Animation framework ------------------------------
var animationTimeout = null; // A null value means the animation is off.
// Otherwise, this is the timeout ID.
function frame() {
// Draw one frame of the animation, and schedule the next frame.
updateFrame();
draw();
canvas.addEventListener("mousedown", doMouseDown, false);
animationTimeout = setTimeout(frame, 33);
}
function setAnimationRunning(run) {
if ( run ) {
if (animationTimeout == null) {
// If the animation is not already running, start
// it by scheduling a call to frame().
animationTimeout = setTimeout(frame, 33);
}
}
else {
if (animationTimeout != null) {
// If the animation is running, stop it by
// canceling the next scheduled call to frame().
clearTimeout(animationTimeout);
}
animationTimeout = null; // Indicates that animation is off.
}
}
//----------------------- initialization -------------------------------
function init() {
try {
canvas = document.getElementById("theCanvas");
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
graphics = canvas.getContext("2d");
}
catch (e) {
document.getElementById("message").innerHTML =
"Sorry, this page requires canvas graphics, but<br>" +
"it looks like your browser does not support it<br>" +
"Reported error: " + e;
return;
}
// add any other necessary initialization
document.getElementById("animateCheck").checked = true; // Make sure box is checked!
loadImage("dog-walking11");
createWorld();
setAnimationRunning(true); // start the animation
}
</script>
</head>
<body onload="init()" style="background-color: rgb(220,220,220)">
<div id="messagediv">
<h2>Hierarchical Modeling Example</h2>
<!-- For error reporting: the contents of the noscript tag are
shown only if JavaScript is not available. The paragraph with
id="message" is for reporting errors using JavaScript.-->
<noscript><b>This page requires JavaScript, which is<br>
not enabled in your browser.</b></noscript>
<p id="message" style="color:red"></p>
<p><input type="checkbox" id="animateCheck" onchange="setAnimationRunning(this.checked)">
<label for="animateCheck">Run Animation</label>
</p>
</div>
<div id="canvasdiv">
<canvas id="theCanvas" width= "400" height= "300"
style="background-color: transparent"></canvas>
</div>
</body>
</html>
It seems that I had to use graphics.drawImage() after using graphics.restore().
Though I was trying to draw the image in the correct order (after), compared to drawing the rectangles, circles etc, the shapes did not come out of the buffer onto the page until after the restore function().
When I was calling drawImage, I assumed that it was loading it onto the buffered canvas with the rest of the things, ready to be drawn in the correct order, when in fact it was putting it straight onto the page.
It seems strange since it was being called with the graphics context, so I thought it would be added to the rest of the canvas graphics, but i appear to be wrong.

maximum call stack size exceeded - no apparent recursion

I've spent about 12 hours looking through this code, and fiddling with it, trying to find out where there's a recursion problem because I'm getting the, "maximum call stack size exceeded," error, and haven't found it. Someone smarter than me please help me!
so far, all I found was that when I make the object, spot, a circle, object, the problem disappears, but when I make it a, 'pip', I get this stack overflow error. I've gone over the pip class with a friggin' microscope, and still have no idea why this is happening!
var canvas = document.getElementById('myCanvas');
//-------------------------------------------------------------------------------------
// Classes
//-------------------------------------------------------------------------------------
//=====================================================================================
//CLASS - point
function point(x,y){
this.x = x;
this.y = y;
}
//=====================================================================================
// CLASS - drawableItem
function drawableItem() {
var size = 0;
this.center = new point(0,0);
this.lineWidth = 1;
this.dependentDrawableItems = new Array();
}
//returns the size
drawableItem.prototype.getSize = function getSize(){
return this.size;
}
// changes the size of this item and the relative size of all dependents
drawableItem.prototype.changeSize = function(newSize){
var relativeItemSizes = new Array;
relativeItemSizes.length = this.dependentDrawableItems.length;
// get the relative size of all dependent items
for (var i = 0; i < this.dependentDrawableItems.length; i++){
relativeItemSizes[i] = this.dependentDrawableItems[i].getSize() / this.size;
}
// change the size
this.size = newSize;
// apply the ratio of change back to all dependent items
for (var i = 0; i < relativeItemSizes.length; i++){
this.dependentDrawableItems[i].changeSize(relativeItemSizes[i] * newSize);
}
}
//moves all the vertices and every dependent to an absolute point based on center
drawableItem.prototype.moveTo = function(moveX,moveY){
//record relative coordinates
var relativeItems = new Array;
relativeItems.length = this.dependentDrawableItems.length;
for (var i = 0; i < relativeItems.length; i++){
relativeItems[i] = new point;
relativeItems[i].x = this.dependentDrawableItems[i].center.x - this.center.x;
relativeItems[i].y = this.dependentDrawableItems[i].center.y - this.center.y;
}
//move the center
this.center.x = moveX;
this.center.y = moveY;
//move all the items relative to the center
for (var i = 0; i < relativeItems.length; i++){
this.dependentDrawableItems[i].moveItemTo(this.center.x + relativeItems[i].x,
this.center.y + relativeItems[i].y);
}
}
// draws every object in dependentDrawableItems
drawableItem.prototype.draw = function(ctx){
for (var i = 0; i < this.dependentDrawableItems.length; i++) {
this.dependentDrawableItems[i].draw(ctx);
}
}
//=====================================================================================
//CLASS - circle
function circle(isFilledCircle){
drawableItem.call(this);
this.isFilled = isFilledCircle
}
circle.prototype = new drawableItem();
circle.prototype.parent = drawableItem.prototype;
circle.prototype.constructor = circle;
circle.prototype.draw = function(ctx){
ctx.moveTo(this.center.x,this.center.y);
ctx.beginPath();
ctx.arc(this.center.x, this.center.y, this.size, 0, 2*Math.PI);
ctx.closePath();
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.outlineColor;
if (this.isFilled === true){
ctx.fill();
}else {
ctx.stroke();
}
this.parent.draw.call(this,ctx);
}
//=====================================================================================
//CLASS - pip
function pip(size){
circle.call(this,true);
}
pip.prototype = new circle(false);
pip.prototype.parent = circle.prototype;
pip.prototype.constructor = pip;
//----------------------------------------------------------------------
// Objects/variables - top layer is last (except drawable area is first)
//----------------------------------------------------------------------
var drawableArea = new drawableItem();
var spot = new pip();
spot.changeSize(20);
drawableArea.dependentDrawableItems[drawableArea.dependentDrawableItems.length] = spot;
//------------------------------------------
// Draw loop
//------------------------------------------
function drawScreen() {
var context = canvas.getContext('2d');
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
spot.moveTo(context.canvas.width/2, context.canvas.height/2);
drawableArea.draw(context);
}
window.addEventListener('resize', drawScreen);
Here's the demo: http://jsfiddle.net/DSU8w/
this.parent.draw.call(this,ctx);
is your problem. On a pip object, the parent will be circle.prototype. So when you now call spot.draw(), it will call spot.parent.draw.call(spot), where this.parent is still the circle.prototype…
You will need to explicitly invoke drawableItem.prototype.draw.call(this) from circle.prototype.draw. Btw, you should not use new for the prototype chain.
Why would you write code like that? It's so difficult to understand and debug. When I'm creating lots of classes I usually use augment to structure my code. This is how I would rewrite your code:
var Point = Object.augment(function () {
this.constructor = function (x, y) {
this.x = x;
this.y = y;
};
});
Using augment you can create classes cleanly. For example your drawableItem class could be restructured as follows:
var DrawableItem = Object.augment(function () {
this.constructor = function () {
this.size = 0;
this.lineWidth = 1;
this.dependencies = [];
this.center = new Point(0, 0);
};
this.changeSize = function (toSize) {
var fromSize = this.size;
var ratio = toSize / fromSize;
this.size = toSize;
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) {
var dependency = dependencies[index++];
dependency.changeSize(dependency.size * ratio);
}
};
this.moveTo = function (x, y) {
var center = this.center;
var dx = x - center.x;
var dy = y - center.y;
center.x = x;
center.y = y;
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) {
var dependency = dependencies[index++];
var center = dependency.center;
dependency.moveTo(center.x + dx, center.y + dy);
}
};
this.draw = function (context) {
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) dependencies[index++].draw(context);
};
});
Inheritance is also very simple. For example you can restructure your circle and pip classes as follows:
var Circle = DrawableItem.augment(function (base) {
this.constructor = function (filled) {
base.constructor.call(this);
this.filled = filled;
};
this.draw = function (context) {
var center = this.center;
var x = center.x;
var y = center.y;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, this.size, 0, 2 * Math.PI);
context.closePath();
context.lineWidth = this.lineWidth;
context[this.filled ? "fill" : "stroke"]();
base.draw.call(this, context);
};
});
var Pip = Circle.augment(function (base) {
this.constructor = function () {
base.constructor.call(this, true);
};
});
Now that you've created all your classes you can finally get down to the drawing:
window.addEventListener("DOMContentLoaded", function () {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var drawableArea = new DrawableItem;
var spot = new Pip;
spot.changeSize(20);
drawableArea.dependencies.push(spot);
window.addEventListener("resize", drawScreen, false);
drawScreen();
function drawScreen() {
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
spot.moveTo(width / 2, height / 2);
drawableArea.draw(context);
}
}, false);
We're done. See the demo for yourself: http://jsfiddle.net/b5vNk/
Not only have we made your code more readable, understandable and maintainable but we have also solved your recursion problem.
As Bergi mentioned the problem was with the statement this.parent.draw.call(this,ctx) in the circle.prototype.draw function. Since spot.parent is circle.prototype the this.parent.draw.call(this,ctx) statement is equivalent to circle.prototype.draw.call(this,ctx). As you can see the circle.prototype.draw function now calls itself recursively until it exceeds the maximum recursion depth and throws an error.
The augment library solves this problem elegantly. Instead of having to create a parent property on every prototype when you augment a class augment provides you the prototype of that class as a argument (we call it base):
var DerivedClass = BaseClass.augment(function (base) {
console.log(base === BaseClass.prototype); // true
});
The base argument should be treated as a constant. Because it's a constant base.draw.call(this, context) in the Circle class above will always be equivalent to DrawableItem.prototype.draw.call(this, context). Hence you will never have unwanted recursion. Unlike this.parent the base argument will alway point to the correct prototype.
Bergi's answer is correct, if you don't want to hard code the parent name multiple times you could use a helper function to set up inheritance:
function inherits(Child,Parent){
Child.prototype=Object.create(Parent.prototype);
Child.parent=Parent.prototype;
Child.prototype.constructor=Child;
};
function DrawableItem() {
this.name="DrawableItem";
}
DrawableItem.prototype.changeSize = function(newSize){
console.log("changeSize from DrawableItem");
console.log("invoking object is:",this.name);
}
function Circle(isFilledCircle){
Circle.parent.constructor.call(this);
this.name="Circle";//override name
}
inherits(Circle,DrawableItem);
Circle.prototype.changeSize = function(newSize){
Circle.parent.changeSize.call(this);
console.log("and some more from circle");
};
function Pip(size){
Pip.parent.constructor.call(this,true);
this.name="Pip";
}
inherits(Pip,Circle);
var spot = new Pip();
spot.changeSize();
For a polyfill on Object.create look here.

Box2D body not rotating on collision

I've been trying to get into Box2DWeb which is a JS port of the AS port of Box2D. Just trying to get a simple setup going where there is a static ramp and a dynamic box that falls on it. Here is my code:
var b2World = Box2D.Dynamics.b2World;
var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
var b2Vec2 = Box2D.Common.Math.b2Vec2;
var b2BodyDef = Box2D.Dynamics.b2BodyDef;
var b2Body = Box2D.Dynamics.b2Body;
var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
var width = 8;
var height = 4;
var world = new b2World(new b2Vec2(0, 10), true);
var debugDraw = new b2DebugDraw();
debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
debugDraw.SetDrawScale(100); //Arena is 8 meters by 4 meters
debugDraw.SetFillAlpha(0.5);
debugDraw.SetLineThickness(1);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
world.SetDebugDraw(debugDraw);
var bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_staticBody;
bodyDef.position.Set(3.5, 3)
var body = world.CreateBody(bodyDef);
body.SetAngle(Math.PI / 4);
var shape = new b2PolygonShape();
shape.SetAsBox(1, 0.25);
var fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1;
fixtureDef.friction = 0.3;
body.CreateFixture(fixtureDef);
var bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(3.5, 1)
var body = world.CreateBody(bodyDef);
var shape = new b2PolygonShape();
shape.SetAsBox(0.10, 0.10);
var fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
body.CreateFixture(fixtureDef);
setInterval(function() {
world.Step(1 / 60, 10, 10);
world.DrawDebugData();
world.ClearForces();
console.log(body.GetAngle());
}, 1000 / 60);
You can see the live result on jsFiddle. As you can see, the box doesn't rotate when it hits the ramp. What am I doing wrong?
Thanks.
Some ressources:
AS3 Manual: http://www.box2dflash.org/docs/2.0.2/manual
AS3 Reference: http://www.box2dflash.org/docs/2.0.2/reference/
Add friction and density to the box.
...and don't leave the irc channel immediately, sometimes it takes more than 3 minutes.
Ah, you have to set the friction & density on the falling object's fixture, not the body.
var fixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
fixtureDef.friction = 0.3
fixtureDef.density = 1
body.CreateFixture(fixtureDef);
btw thanks for pointing me in the direction of Box2D - I hadn't seen that physics engine before, looks interesting :)

Categories