I am working on canvas mini game and i want to build a function that calculates cost of moving your ship from point A to point B.
I need 2 things:
total cost before ship is dispatched
cost for every tick of move (server loop tick)
Cost is charged every time ship is moved (every time server loop ticks) so total must match sum of all ticks that server made to get it there.
I have simple server loop that moves ship:
setInterval(function() {
ship.move();
}, 10);
now the simplified ship code:
var rad = (p1, p2) => Math.atan2(p2.y - p1.y, p2.x - p1.x),
distance = (p1, p2) => Math.sqrt( (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) );
var ship = function() {
this.x; // current ship x
this.y; // current ship y
this.destination; // destination object
this.total_distance; // total distance before dispatch
this.remaining_distance; // remaining distance before arrival
this.speed = 0.5; // ship speed modifier
this.set_travel = function(target) {
this.destination = target;
this.total_distance = distance( { x: this.x, y: this.y }, { x: this.destination.x, y: this.destination.y } );
};
this.move = function() {
this.remaining_distance = distance( { x: this.x, y: this.y }, { x: this.destination.x, y: this.destination.y } );
var _rad = rad( { x: this.x, y: this.y }, { x: this.destination.x, y: this.destination.y } );
this.x += Math.cos(rad) * this.speed;
this.y += Math.sin(rad) * this.speed;
};
};
Now we could introduce fuel cost and add to above code like this:
var ship = function() {
...
this.total_fuel_cost; // total fuel cost that player will consume during entire travel
this.set_travel = function(target) {
...
this.total_fuel_cost = ?
};
this.move = function() {
...
player.fuel -= ? // fuel cost every tick that must match total after arrival
};
};
Maybe someone could help solve this problem. Maybe it could be good approach to assume that every 1 distance made cost x fuel, but i dont know if it can be done like this.
----- edit
when ship is created it is instantiated in this way:
objects.push(new ship())
as i explained in my question, its not acceptable to refuse flying if total is not enough, ship must go as long as it has fuel
I assembled a little demo using settings that has been provided:
move() loop going on 10 ms interval
fuel being consumed on the fly
fuel consumed throughout journey must be equal assumed consumption at the beginning
Logic explanation:
The only variable that is really needed you already have in your logic, it is ship speed.
Your code would look like this:
this.move = function() {
this.remaining_distance = distance( { x: this.x, y: this.y }, { x: this.destination.x, y: this.destination.y } );
var _rad = rad( { x: this.x, y: this.y }, { x: this.destination.x, y: this.destination.y } );
this.x += Math.cos(rad) * this.speed;
this.y += Math.sin(rad) * this.speed;
player.fuel -= this.speed;
};
Lets say that player has 1000 fuel and you need to fly distance of 845. If we assume that 1 distance === 1 fuel, we expect that at the end of journey you should have 155 fuel left. Additionally it doesn't matter if you fly fast (say 2) or slow (say 0.5), fuel consumption should be the same, as you consume more fuel while flying faster. Above code will do exactly that.
Demo link:
https://jsfiddle.net/7khvxkaa/1/
Fuel cost can be tempered with to deal with smaller numbers for example if assumed that 1 distance === 0.01 fuel you will end up consuming 10 fuel throughout 1000 distance journey. To account above situation this is the only change that needs to be made:
player.fuel -= this.speed / 100;
Also remember that demo is only for debugging purposes to verify proof of concept, in real environment where you have more variables (for example moving destination or ship acceleration / deceleration) it may be difficult to predict total fuel consume, however logic will still work as expected.
I think you mostly need an explanation about OOP.
Your function ship is not an object; it's (the constructor of) a class.
(Well, that's what other languages would call it.)
To make an object, you do this:
var myship = new ship();
Here is an example; a bit similar your game. But my main point is for you to look at how you can use an object. As you can see, the property myship.fuel keeps track of the fuel; it will refuse to move if a trip requires more fuel than it has.
Calculations you can handle yourself. Just look at "what properties do I have?" "What do I need?", ...
<script>
// constructor
function Ship(elementId) {
// properties
this.elm = document.getElementById(elementId);
this.x = 0; // current ship x
this.y = 0; // current ship y
this.fuel = 300;
this.fuelprice = 0.2; // moving 1 pixel costs 0.2 fuel
this.destination = {x:0, y:0}; // destination object
// static function, doesn't interact with any property, just takes parameters ans returns something
this.distance = function(p1, p2) {
return Math.sqrt( (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) ); // Pythagoras
}
this.rad = function(p1, p2) {
return Math.atan2(p2.y - p1.y, p2.x - p1.x);
}
// method. this is a function bound to an object. Whomever calls this function will be affected.
// in this case myship will be moved, but there could be many boats in the game; many buttons that each move 1 boat, ...
this.moveTo = function(to) {
var from = {x: this.x, y: this.y};
var totalDistance = this.distance(from, to);
// see if there is enougn fuel
var fuelNeeded = this.fuelprice * totalDistance;
if(this.fuel >= fuelNeeded) {
display(
'<h3>This trip</h3>'+
'total distance: ' + totalDistance + '<br/>'+
'fuel at start: ' + this.fuel +'<br/>'+
'fuel needed: ' + fuelNeeded +'<br/>'
);
this.moveStep(to, 10, 10);
//
this.fuel -= fuelNeeded;
}
else {
display(
'<h3>Not enough fuel</h3>'+
'fuel at start: ' + this.fuel +'<br/>'+
'fuel needed: ' + fuelNeeded +'<br/>'
);
}
}
// function that calls itself, until stepsLeft = 0
this.moveStep = function(to, steps, stepsLeft) {
var self = this; // within a setTimeout the "this" changes its meaning. So I make a copy.
var x = to.x + (this.x - to.x) * (stepsLeft / steps);
var y = to.y + (this.y - to.y) * (stepsLeft / steps);
if(stepsLeft > 0) {
this.elm.style.left = x;
this.elm.style.top = y;
setTimeout(function() { self.moveStep(to, steps, stepsLeft - 1) }, 100);
}
else {
// animation is finished, so the "to" situation becomes the new x and y
this.x = to.x;
this.y = to.y;
}
}
}
// function that uses the myship object
function submitMove() {
myship.moveTo({
x: Number(document.getElementById('x').value),
y: Number(document.getElementById('y').value)
});
}
// just to display messages to screen
function display(message) {
document.getElementById('display').innerHTML = message;
}
// now this is our object: myship
var myship;
// when the page is loaded we can make myship a Ship object
window.onload = function() {
myship = new Ship('boat');
}
</script>
<style>
#sea {
position: relative;
background: #2040f0;
height: 500px;
width: 500px;
}
#boat {
position: absolute;
}
</style>
<div id="sea">
<img id="boat" src="http://icons.veryicon.com/32/Leisure/Summer%20Holiday/sailing%20boat.png" />
</div>
<hr/>
<input id="x" placeholder="X" value="200">
<input id="y" placeholder="X" value="40">
<input type="button" value="GO" onclick="submitMove()">
<div id="display"></display>
Related
I am current running into a bit of a math conundrum that has stumped me for days.
I am building a JavaScript game and attempting to create boundary coordinates to manage the pathing and movement of sprites, however it appears that lag/jitter/delay is reeking havoc on different entities moving in coordination with one another.
I believe I must calculate the jitter/lag/offset and somehow apply it to the coordinate range detection and movement functions but I have yet to crack the code correctly and alleviate the mis-aligning sprites.
Here is a replication of the issue in a CodeSandbox and the bulk of the code that shows it in action:
https://codesandbox.io/s/movetime-boundries-issue-example-2prow?file=/src/App.js
var obj = { x: 10, speed: 250 };
var obj2 = { x: 100 };
var objHighestX = { max: 0 };
var direction = 0;
var canvas = document.getElementById("mainScene");
var ctx = canvas && canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.font = "15px Courier";
var render = function () {};
var update = function (modifier) {
// console.log("Updating");
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
ctx.fillRect(obj2.x, 60, 15, 15);
if (obj.x > objHighestX.max) {
objHighestX.max = obj.x;
}
ctx.fillText(String("X" + obj.x), 25, 100);
ctx.fillText(String("Furthest" + objHighestX.max), 125, 100);
if (obj.x >= obj2.x - 15) {
direction = 1;
} else if (obj.x <= 0) {
direction = 0;
}
if (direction === 0) {
obj.x += obj.speed * modifier;
ctx.clearRect(obj.x - 7, 9, 17, 17);
ctx.fillRect(obj.x, 60, 15, 15);
}
if (direction === 1) {
obj.x -= obj.speed * modifier;
ctx.clearRect(obj.x, 9, 17, 17);
ctx.fillRect(obj.x, 60, 15, 15);
}
};
var lastUpdate = Date.now();
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - lastUpdate;
lastUpdate = now;
update(delta / 1000);
render();
requestAnimationFrame(main);
};
main();
If anyone has any suggestions or questions towards my case, I'm very eager to hear of it.
Perhaps I have to use the rate of change to create an offset for the boundaries?
Which I've tried like:
if (obj.x >= obj2.x - (15 * 1 * modifier))
But am still not yet getting this one down. Thank you all, greatly, in advance.
First, you're delta time calculations aren't complete.
var now = Date.now();
var delta = now - lastUpdate;
lastUpdate = now;
update(delta / 1000);
If you now request update() to be invoked via requestAnimationFrame, the number passed as a parameter will be the number of miliseconds passed between the last and the current frame. So if the screen refresh rate is 60hz it's roughly 16.6ms.
This value alone though isn't meaningful - you need to compare it against a target value.
Say we want to achieve a framerate of 30fps - equal to ~33.3ms. If we take this value and divide it from the 16.6ms above, we get roughly 0.5. This makes complete sense. We want 30fps, the monitor refreshes at 60hz, so everything should move at half the speed.
Let's modify your main() function to reflect that:
var main = function() {
var targetFrameRate = 30;
var frameTime = 1000 / targetFrameRate;
var now = Date.now();
var delta = now - lastUpdate;
lastUpdate = now;
update(delta / frameTime);
render();
requestAnimationFrame(main);
};
Second problem is the update() function itself.
Let's have a look at the following block:
if (direction === 0) {
obj.x += obj.speed * modifier;
ctx.clearRect(obj.x - 7, 9, 17, 17);
ctx.fillRect(obj.x, 60, 15, 15);
}
That means, wherever obj currently is, move it to the right by some amount. We are missing the boundary check at this point. You need to check if it would leave the bounds if we would move it to the right. In case it does, just move it next to the bounds.
Something like this:
var maxX=100;
if (direction === 0) {
var speed = obj.speed * modifier;
if (obj.x + obj.width + speed > maxX) {
direction = 1;
obj.x = maxX - obj.width;
} else {
obj.x += speed;
}
}
Maintain correct speed during collision frame
I notice that the object is always moving, which means the given answer does not correctly solve the problem.
An object should not slow down between frames if it has a constant speed
The illustration shows an object moving
At top how far it would move without interruption.
At center the point of collision. Note that there is still a lot of distance needed to cover to maintain the same speed.
At bottom the object is moved left the remaining distance such the total distance traveled matches the speed.
To maintain speed the total distance traveled between frames must remain the same. Positioning the object at the point of collision reduces the distance traveled and thus the speed of the object during the collision frame can be greatly reduced
The correct calculation is as follows
const directions = {
LEFT: 0,
RIGHT: 1,
};
const rightWallX = 100;
const leftWallX = 0;
if (obj.direction === directions.RIGHT) {
obj.x = obj.x + obj.speed;
const remainDist = (rightWallX - obj.width) - obj.x;
if (remainDist <= 0) {
obj.direction = directions.LEFT;
obj.x = (rightWallX - obj.width) + remainDist;
}
} else if (obj.direction === directions.LEFT) {
obj.x = obj.x - obj.speed;
const remainDist = leftWallX - obj.x;
if (remainDist >= 0) {
obj.direction = directions.RIGHT;
obj.x = leftWallX + remainDist;
}
}
I have a player, which looks like this:
{
x: [could be any integer],
y: [could be any integer],
facing: {
x: [could be any integer],
y: [could be any integer]
}
}
Assuming the player is at (player.x, player.y), and the player is facing in the direction of the mouse, which is at (player.facing.x, player.facing.y), what is a formula that I could use to move the player n units in the direction of the mouse?
Here is what I've tried so far, but it always results in null:
var facingDistance = Math.sqrt(Math.pow(game.players[i].facing.x, 2) - Math.pow(game.players[i].x, 2));
game.players[i].x += (game.players[i].speed/facingDistance) *
(game.players[i].x - game.players[i].facing.x);
game.players[i].y += (game.players[i].speed/facingDistance) *
(game.players[i].y - game.players[i].facing.y);
// prefetch player object for cleaner code
var plr = game.players[i];
// normalized player direction
var facingDX = plr.facing.x - plr.x;
var facingDY = plr.facing.y - plr.y;
var facingLen = Math.sqrt(facingDX * facingDX + facingDY * facingDY);
facingDX /= facingLen;
facingDY /= facingLen;
// add n times this to position + round to integer coordinates
plr.x = Math.round(plr.x + facingDX * n);
plr.y = Math.round(plr.y + facingDY * n);
I'm working on a fish sprite animation. Currently when I add a piece of food then the sprite animation will move forward to eat it. But I am not able to make it swim smoothly toward the food. Sometimes the sprite animation will move up and down till it reach the food.
Here is how the sprite animation is moving towards the food:
Fish.prototype.chaseFood = function(index) {
if (this.xPos > foodArray[index].x + foodWidth) {
this.speedX = -1 * Math.abs(this.speedX);
} else if (this.xPos < foodArray[index].x) {
this.speedX = Math.abs(this.speedX);
}
if (this.yPos > foodArray[index].y + foodHeight) {
this.speedY = -1 * Math.abs(this.speedY);
} else if (this.yPos < foodArray[index].y) {
this.speedY = Math.abs(this.speedY);
}
};
Is there anyway to make it swim more smoothly towards the food and not moving up and down towards it.
I'd calculate angle between the fish and the food and make the fish move towards that angle.
Here are some helper functions to get you going:
function distanceBetweenPoints(a, b)
{
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
}
function angleBetweenPoints(a, b)
{
return Math.atan2(b.y-a.y,b.x-a.x)*180/Math.PI;
}
Usage:
var angle = angleBetweenPoints({ x: fish.x, y: fish.y }, { x: food.x, y: food.y });
Then you can do something like:
fish.x += Math.sin(angle * Math.PI / 180) * 10;
fish.y += Math.cos(angle * Math.PI / 180) * 10;
I think you want your sprite to move in a direct line from one set of coordinates (it's location) to another set of coordinates (the food's location).
So if your if your x coordinates are a difference of 6 pixels and your y coordinates are a difference of 4, we want to keep the ratio of 6 to 4 when we increase our x and y offset for our happy little fish.
In this case, we could change the program to move x by 1 pixel at a time and then move y by 4/6 pixel at a time and thus go on a path straight towards the goal. If we were to increase the y by a full pixel, it would arrive directly below the target and then go straight up. This would be an indirect path and less realistic looking.
Actually I think it would arrive to the left of the goal and then go directly right in the old version if you use the 1 to 1 ratio, but I think you knew what I meant.
I'll try to adapt an example I have to your code:
int fishSpeed = 2;
float xOffset = fishSpeed;
float yOffset = fishSpeed;
float xDis = abs(this.xPos-(foodArray[index].x + foodWidth));
float yDis = abs(this.yPos-(foodArray[index].y + foodWidth));
//each offset changes depending on how far it is from goal
xOffset = xOffset * (xDis / (xDis + yDis));
yOffset = yOffset * (yDis / (xDis + yDis));
if(this.xPos > foodArray[index].x) this.xPos+=xOffset;
if(this.yPos > foodArray[index].y) this.yPos+=yOffset;
if(this.xPos < foodArray[index].x) this.xPos-=xOffset;
if(this.yPos < foodArray[index].y) this.yPos-=yOffset;
Sorry I couldn't make it work using your example. I don't know if it will help, but here is a complete .htm file that has a bunny wabbit that is controlled by the mouse and one that is chased directly by another wabbit. The wabbit will go directly for its goal. The file needs processing.js in the same directory.
<!DOCTYPE html>
<html>
<body bgcolor="lightblue" style="margin:0;">
<center>
<script src="processing.js"></script>
<script type="application/processing">
void setup(){
size(screen.width*.9,screen.height*.9);
blueWabbit = new wabbit(600,600,105);
pinkWabbit = new wabbit(100,100,100);
blueWabbit.blue = 255;
blueWabbit.red = 128;
blueWabbit.green = 128;
wabbitSpeed = 5;
}
void draw() {
float xOffset = wabbitSpeed;
float yOffset = wabbitSpeed;
float xDis = abs(pinkWabbit.xpos-blueWabbit.xpos);
float yDis = abs(pinkWabbit.ypos-blueWabbit.ypos);
xOffset = xOffset * (xDis / (xDis + yDis));
yOffset = yOffset * (yDis / (xDis + yDis));
if(pinkWabbit.xpos > blueWabbit.xpos) blueWabbit.xpos+=xOffset;
if(pinkWabbit.ypos > blueWabbit.ypos) blueWabbit.ypos+=yOffset;
if(pinkWabbit.xpos < blueWabbit.xpos) blueWabbit.xpos-=xOffset;
if(pinkWabbit.ypos < blueWabbit.ypos) blueWabbit.ypos-=yOffset;
if (xDis+yDis<wabbitSpeed){
for(int a =0;a<20; a++)babyWabbit();
}
else background(0,0,0,0);
pinkWabbit.show();
blueWabbit.show();
pinkWabbit.xpos = mouseX;
pinkWabbit.ypos = mouseY;
/*
fill(0);
text("blue x = "+(int)blueWabbit.xpos,10,10);
text("blue y = "+(int)blueWabbit.ypos,10,20);
text("pink x = "+pinkWabbit.xpos,10,30);
text("pink y = "+pinkWabbit.ypos,10,40);
text("xOffset = "+xOffset,10,50);
text("yOffset = "+yOffset,10,60);
text("xDis = "+(int)xDis,10,70);
text("yDis = "+(int)yDis,10,80);
*/
}
class wabbit {
//declare the properties that will be used as variables for the object
float xpos, ypos, diameter;
int red, blue, green;
//define the parameters for the creation of a new class
wabbit (float x, float y, float wSize) {
xpos = x;
ypos = y;
diameter = wSize;
//radius = .5 * diameter;
//make it pink if user did not define colors
if (!(0>red>256)) red = 255;
if (!(0>green>256))green = 200;
if (!(0>blue>256)) blue = 200;
}
void show() {
noStroke();
for(a = diameter; a > 0; a-=5){
fill(red-a,green-a,blue-a);
//belly and head
ellipse(xpos, ypos, a, a);
ellipse(xpos, ypos-diameter*.7, a*.7,a*.7);
//feets
ellipse(xpos-.2*diameter, ypos+diameter*.4, a*.4,a*.4);
ellipse(xpos+.2*diameter, ypos+diameter*.4, a*.4,a*.4);
//ears
ellipse(xpos-.2*diameter, ypos-diameter, a*.2,a*.8);
ellipse(xpos+.2*diameter, ypos-diameter, a*.2,a*.8);
}
}
}
void babyWabbit(){
noStroke();
var red=random(1,255);
var green=random(1,255);
var blue=random(1,255);
var xpos=random(1,width);
var ypos=random(1,height);
var diameter = random(20,80);
for(var a = diameter; a > 0; a-=5){
fill(red-a,green-a,blue-a);
//belly and head
ellipse(xpos, ypos, a, a);
ellipse(xpos, ypos-diameter*.7, a*.7,a*.7);
//feets
ellipse(xpos-.2*diameter, ypos+diameter*.4, a*.4,a*.4);
ellipse(xpos+.2*diameter, ypos+diameter*.4, a*.4,a*.4);
//ears
ellipse(xpos-.2*diameter, ypos-diameter, a*.2,a*.8);
ellipse(xpos+.2*diameter, ypos-diameter, a*.2,a*.8);
}
}
</script><canvas></canvas>
</body>
</html>
I am attempting to create a small asteroids game in Javascript, and I've gotten it to the point where the ship draws on the screen and can fly around. However, whenever I try to rotate it by a certain amount, it can only rotate between +/- PI/2. I need it to cover more than just that range of 180 degrees, or else the ship can never turn around. I'm trying to do this from scratch using a custom 2D Vector class, and I've had a couple people look at it with no luck as to what to do.
Here's my vector code, or at least the constructor and rotation functions.
function Vec2D(x, y) {
var self = this;
var sqrt = Math.sqrt;
this.x = x !== null ? Number(x) : 0;
this.y = y !== null ? Number(y) : 0;
}
Vec2D.prototype.rotate = function (deg) {
var theta = deg * (Math.PI / 180),
xTemp = this.x;
this.x = this.x * Math.cos(theta) - this.y * Math.sin(theta);
this.y = xTemp * Math.sin(theta) + this.y * Math.cos(theta);
return this;
}
And here's the code for where my ship is trying to rotate.
function Ship(x_, y_, size_) {
this.position = new Vec2D(x_, y_);
this.velocity = new Vec2D(0, 0);
this.forward = new Vec2D(0, 0);
//some other things
this.turningRight = false;
this.turningLeft = false;
this.turnAmt = 5;
//some more things
}
Ship.prototype.update = function () {
//other update code
if (this.turningRight) {
this.forward.rotate(this.turnAmt);
console.log("right");
}
if (this.turningLeft) {
this.forward.rotate(-1.0 * this.turnAmt);
console.log("left");
}
//end of rotation code in update
}
I can reproduce more code if necessary, but this is all the relevant code as far as I can tell. I've tried console printing, I've tried messing with the rotation matrix, and I've even tried using only radians as opposed to converting it from degrees every time (which in all honesty I really should be doing anyway).
Any thoughts on my gross novice JavaScript?
I was quite inspired by Bret Victor's Inventing on Principle video (http://vimeo.com/36579366).
Also, I was very fascinated by that tree drawn using Javascript. I have not done much of graphics programming. All my career I have been a middle-tier and database developer. But looking at that programmatically draw tree, I am motivated to learn. I have started learning Javascript. I know I will eventually (in a few weeks or months, depending on how much time I get) be able to write such program myself from scratch.
However, I am really really eager to get some source code that does similar drawing in Javascript and play with it. Any links/pointers you guys can provide would be very useful.
Drawing a tree with the canvas is simple enough. See below for a solution in about 80 lines of code.
Several people have started attempts at re-creating the interactive environment from the video. One of those attempts was made by github user tnlogy. His environment allows you to select a number in the code and change the running demo on-the-fly using a slider. I've forked his code to include a tree demo.
Demo for interactive tree:
https://brianpeiris.github.io/live-coding/client/
Demo for simple tree:
http://jsfiddle.net/brianpeiris/v9zD6/show
Source for simple tree demo
var
drawLeaf = function (cx, xx, yy) {
var
leafAlpha = 8/10,
leafSize = 7;
cx.beginPath();
cx.fillStyle = (
"rgba(" +
Math.round(220 + (Math.random() * 50)) + ", " +
Math.round(180 + (Math.random() * 50)) + ", " +
Math.round(220 + (Math.random() * 50)) + ", " +
leafAlpha +
")"
);
cx.arc(xx, yy, leafSize, 0, Math.PI * 2);
cx.fill();
},
drawBranch = function (ii, cx, xx, yy, level, levels, angle, numBranches) {
var
branchLength = 44,
subBranchWidthFactor = 2,
sweep = Math.PI * 25/30,
branchTweakMagnitude = 52/50,
tt;
cx.beginPath();
// Draw thinner branches away from the trunk
cx.lineWidth = (levels - level) * subBranchWidthFactor;
// Calculate the angle of the branch, with some random tweaks
tt = (
sweep * ii / (numBranches - 1) + angle -
sweep / 2 + Math.PI +
Math.random() * 0.5 * branchTweakMagnitude
);
cx.moveTo(xx, yy);
newXX = xx + Math.sin(tt) * branchLength;
newYY = yy + Math.cos(tt) * branchLength;
cx.lineTo(newXX, newYY);
cx.stroke();
// Recursively draw more branches
drawBranchesAndLeaves(cx, newXX, newYY, level + 1, levels, Math.PI + tt);
},
drawBranchesAndLeaves = function (cx, xx, yy, level, levels, angle) {
var
numBranches = 5,
ii, newXY;
// This function is called recursively. The recursion terminates when we
// have reached the specified number of recursive levels.
if (level === levels) {
drawLeaf(cx, xx, yy);
return;
}
else {
for (ii = 0; ii < numBranches; ii++) {
drawBranch(ii, cx, xx, yy, level, levels, angle, numBranches);
}
}
},
drawTree = function(cx, ww, hh) {
var trunkX = ww / 2, trunkY = hh - 165;
cx.strokeStyle = "black";
cx.lineWidth = 13;
cx.lineCap = "round";
cx.moveTo(trunkX, hh);
cx.lineTo(trunkX, trunkY);
cx.stroke();
drawBranchesAndLeaves(cx, trunkX, trunkY, 0, 3, 0);
},
width = 350,
height = 350,
canvas = $('<canvas width="' + width + '" height="' + height + '"></canvas>'),
ctx = canvas[0].getContext("2d");
$('body').append(canvas);
drawTree(ctx, width, height);
This is a good place to start, if you are very new to 2D graphics in Javascript.
https://developer.mozilla.org/en/Drawing_Graphics_with_Canvas
As for the tree source code, I would be interested in looking at it too!
Credit to Ian Johnson ( #enjalot ) for sharing this with me, but here is a link to d3js version of a tree. http://tributary.io/inlet/4b0a56692447fa75d8a1 which is adapted from this version by Peter Cook http://prcweb.co.uk/lab/d3-tree/
Uses combo of paths:
var pathGenerator = d3.svg.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
})
And functions that fetch parents, points and paths:
function getPoints(branches) {
var points = [];
branches.forEach(function(branch) {
points.push( {x: x1(branch), y: y1(branch) });
points.push( {x: x2(branch), y: y2(branch) });
});
return points;
}
function getParent(branch, p, branches) {
if(!branch.parent) return;
var b = branches[branch.parent];
p.push({x: b.x, y: b.y})
getParent(b, p, branches);
}
function getPaths(branches) {
var paths = [];
var i = 0;
branches.forEach(function(branch) {
if(branch.d < maxDepth) return;
var p = [{x: branch.x, y: branch.y}];
getParent(branch, p, branches);
p.push(seed);
paths.push(p);
});
return paths;
}
Again, HT to Ian. Live demo here.