I am trying to create a kind of square clock with Raphael JS, rather than 12 digits mine has 8 digits. I have found a great example for round clocks on this page;
Emanuele Feronato
I wanted to create my clock based on this one, but I am having trouble while aligning digit dashes to their places. For the round one the base example uses some cosines and sins for calculating their places. But on the square one I want all the dashes to be aligned so that they will touch to the border of square.
Aiming something like this;
I was wondering what is the easiest and best way to calculate these dashes' places for a square one? Is there also a clean mathematical solution as used in the round clock for square one?
Here is the fiddle of my current template;
fiddle
Here is also my current template;
function draw_square_clock(svgId) {
canvas = Raphael(svgId, 200, 200);
var clock = canvas.rect(3, 3, 194, 194);
clock.attr({
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": "4"
});
var hour_sign;
var text_sign;
for (i = 0.0; i < 12; i = i + 1.5) {
//How am I going to calculate these four values to align hour signs to the edges of square
var start_x = 100 + Math.round(80 * Math.cos(30 * i * Math.PI / 180));
var start_y = 100 + Math.round(80 * Math.sin(30 * i * Math.PI / 180));
var end_x = 100 + Math.round(90 * Math.cos(30 * i * Math.PI / 180));
var end_y = 100 + Math.round(90 * Math.sin(30 * i * Math.PI / 180));
hour_sign = canvas.path("M" + start_x + " " + start_y + "L" + end_x + " " + end_y);
hour_sign.attr({
"stroke-width": "6"
});
hour_sign.transform("t0,0 s2");
}
hour_hand = canvas.path("M100 100L100 50");
hour_hand.attr({
stroke: "#000000",
"stroke-width": 6
});
var pin = canvas.circle(100, 100, 8);
pin.attr("fill", "#000000");
}
$(document).ready(function() {
draw_square_clock('clock_id');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
<div id="clock_id"></div>
Mathematically:
Calculate the length of a corner tick:
// length of tick a 0 degrees
var tickLengthAt0=30;
// calculate length of corner tick
var cornerTickLength=Math.sqrt(2*(tickLengthAt0*tickLengthAt0));
Use trigonometry to calc each of the tick line segments:
var PI=Math.PI;
var cx=150;
var cy=150;
var width=200;
var height=200;
function calcTickPoints(x0,y0,length,angle){
x1=x0+length*Math.cos(angle);
y1=y0+length*Math.sin(angle);
return({ x0:x0, y0:y0, x1:x1, y1:y1 });
}
// Use calcTickPoints to calc the points of each tick
// top-right
var TR=calcTickPoints(cx+width/2,cy-height/2,cornerTickLength,PI*3/4);
// bottom-right
var BR=calcTickPoints(cx+width/2,cy+height/2,cornerTickLength,PI*5/4);
// bottom-left
var BL=calcTickPoints(cx-width/2,cy+height/2,cornerTickLength,PI*7/4);
// top-left
var TL=calcTickPoints(cx-width/2,cy-height/2,cornerTickLength,PI*9/4);
// right
var R=calcTickPoints(cx+width/2,cy,tickLengthAt0,PI);
// bottom
var B=calcTickPoints(cx,cy+height/2,tickLengthAt0,PI*3/2);
// left
var L=calcTickPoints(cx-width/2,cy,tickLengthAt0,PI*2);
// top
var T=calcTickPoints(cx,cy-height/2,tickLengthAt0,PI/2);
Example code (drawn on Canvas, but you can use SVG if desired):
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var PI=Math.PI;
var PI2=PI*2;
var cx=150;
var cy=150;
var width=200;
var height=200;
var tickLengthAt0=30;
var cornerTickLength=Math.sqrt(2*(tickLengthAt0*tickLengthAt0));
drawFace();
function drawFace(){
// rect
ctx.strokeRect(cx-width/2,cy-height/2,width,height);
// top-right
drawTick(cx+width/2,cy-height/2,cornerTickLength,PI*3/4);
// bottom-right
drawTick(cx+width/2,cy+height/2,cornerTickLength,PI*5/4);
// bottom-left
drawTick(cx-width/2,cy+height/2,cornerTickLength,PI*7/4);
// top-left
drawTick(cx-width/2,cy-height/2,cornerTickLength,PI*9/4);
// right
drawTick(cx+width/2,cy,tickLengthAt0,PI);
// bottom
drawTick(cx,cy+height/2,tickLengthAt0,PI*3/2);
// left
drawTick(cx-width/2,cy,tickLengthAt0,PI*2);
// top
drawTick(cx,cy-height/2,tickLengthAt0,PI/2);
}
function drawTick(x0,y0,length,angle){
x1=x0+length*Math.cos(angle);
y1=y0+length*Math.sin(angle);
ctx.beginPath();
ctx.moveTo(x0,y0);
ctx.lineTo(x1,y1);
ctx.stroke();
return({ x0:x0, y0:y0, x1:x1, y1:y1 });
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
Related
I am having a problem: I need to draw a Needle over a Speedometer. I am using simple Line Function in JS that draws a line/Needle. I want my Needle to be thick from base and thin from tip as shown below. Please advise how to draw such needle in JavaScript. The Desired and Current Needles are below.
Code of Line:
function drawNeedle(options) {
/* Draw the needle at the
* angle that represents the options.speed value.
*/
var iSpeedAsAngle = convertSpeedToAngle(options),
iSpeedAsAngleRad = degToRad(iSpeedAsAngle),
gaugeOptions = options.gaugeOptions,
innerTickX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * 10),
innerTickY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * 10),
fromX = (options.center.X - gaugeOptions.radius) + innerTickX,//+ innerTickX ,// /2,
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY ,//+ innerTickY, // /2,
endNeedleX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * gaugeOptions.radius),//+40,
endNeedleY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * gaugeOptions.radius),//+60, // controlled height of nedle
toX = (options.center.X - gaugeOptions.radius) + endNeedleX,
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + endNeedleY,
line = createLine(options.center.X + 80, options.center.Y + 60, toX+75, toY+60, "rgb(3,2,245)", 5, 0.6); //80.60.75.65
// line = createLine(fromX, fromY, toX, toY, "rgb(3,2,245)", 5, 0.6);
drawLine(options, line);
}
drawLine Function
function drawLine(options, line) {
// Draw a line using the line object passed in
options.ctx.beginPath();
// Set attributes of open
options.ctx.globalAlpha = line.alpha;
options.ctx.lineWidth = line.lineWidth;
options.ctx.fillStyle = line.fillStyle;
options.ctx.strokeStyle = line.fillStyle;
options.ctx.moveTo(line.from.X,
line.from.Y);
// Plot the line
options.ctx.lineTo(
(line.to.X),
line.to.Y
);
options.ctx.stroke();
}
Here's how to draw a needle guage:
Translate to the center of the guage,
Rotate to a specified angle,
Draw the needle as a triangle,
Un-rotate by the same specified angle,
Un-translate by the center coordinates.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var degrees=-90;
var radians=degrees*Math.PI/180;
$myslider=$('#myslider');
$myslider.attr({min:-180,max:0}).val(degrees);
$myslider.on('input change',function(){
var degrees=parseInt($(this).val());
var radians=degrees*Math.PI/180;
drawNeedle(cw/2,ch/2,150,radians);
});
drawNeedle(cw/2,ch/2,150,radians);
function drawNeedle(cx,cy,radius,radianAngle){
ctx.clearRect(0,0,cw,ch);
ctx.translate(cx,cy);
ctx.rotate(radianAngle);
ctx.beginPath();
ctx.moveTo(0,-5);
ctx.lineTo(radius,0);
ctx.lineTo(0,5);
ctx.fillStyle='blue';
ctx.fill();
ctx.rotate(-radianAngle);
ctx.translate(-cx,-cy);
ctx.beginPath();
ctx.arc(cx,cy,10,0,Math.PI*2);
ctx.fill();
}
body{ background-color:white; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Needle angle  <input id=myslider type=range><br>
<canvas id="canvas" width=512 height=512></canvas>
I am putting together a simulation of an urban transportation system, and trying to improve my Javascript and Canvas skills. I have provided a bare-bones version here: https://jsfiddle.net/ftmzm9vp/
Two questions:
1) I want the "pods" to run at a uniform rate. Right now they are all arriving at their destinations at the same time, which means they are traveling at different speeds. How do I correct this?
2) There is obviously more I have to do -- get the pods to travel along existing lines, work out the best path to their destination, expand the number of lines and stations -- all of which will increase the computing overhead. Right now, with the 500 pods I want to use, the animation is starting to crawl. I rewrote the whole thing to use requestAnimFrame, as I thought it would be faster, but it doesn't seem to be as smooth at it should be. What can I do to improve this?
Thanks!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<title>Pod Stations Lines Test</title>
<body>
<canvas id="layer1" style="z-index: 2;
position:absolute;
left:0px;
top:0px;
" height="600px" width="1000">This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<canvas id="layer2" style="z-index: 3;
position:absolute;
left:0px;
top:0px;
" height="600px" width="1000">This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<canvas id="layer3" style="z-index: 1;
position:absolute;
left:0px;
top:0px;
" height="600px" width="1000">This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<script>
//Modified Source: http://jsfiddle.net/m1erickson/HAbfm/
//
layer1 = document.getElementById("layer1");
ctx1 = layer1.getContext("2d");
layer2 = document.getElementById("layer2");
ctx2 = layer2.getContext("2d");
layer3 = document.getElementById("layer3");
ctx3 = layer3.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
//STATION LIST
var station = [
['A', 100, 50],
['B', 300, 50],
['C', 200, 150],
['D', 100, 250],
['E', 300, 250],
['F', 400, 250]
];
//DRAW LINES
function drawLines() {
ctx1.clearRect(0, 0, layer3.width, layer3.height);
var linkAB = ctx1.beginPath();
ctx1.moveTo(station[0][1], station[0][2]);
ctx1.lineTo(station[1][1], station[1][2]);
ctx1.stroke();
var linkBC = ctx1.beginPath();
ctx1.moveTo(station[1][1], station[1][2]);
ctx1.lineTo(station[2][1], station[2][2]);
ctx1.stroke();
var linkCD = ctx1.beginPath();
ctx1.moveTo(station[2][1], station[2][2]);
ctx1.lineTo(station[3][1], station[3][2]);
ctx1.stroke();
var linkDE = ctx1.beginPath();
ctx1.moveTo(station[3][1], station[3][2]);
ctx1.lineTo(station[4][1], station[4][2]);
ctx1.stroke();
var linkCE = ctx1.beginPath();
ctx1.moveTo(station[2][1], station[2][2]);
ctx1.lineTo(station[4][1], station[4][2]);
ctx1.stroke();
var linkEF = ctx1.beginPath();
ctx1.moveTo(station[4][1], station[4][2]);
ctx1.lineTo(station[5][1], station[5][2]);
ctx1.stroke();
}
//CREATE PODS
var podArray = [];
function Pod(startX, startY, endX, endY, riders, color) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
this.riders = riders;
this.color = color;
}
var colorArray = ["gold", "orange", "red", "green", "blue", "black"];
function randomPass() {
occ = 1 + Math.floor(Math.random() * 6);
return occ;
console.log("Riders " + occ);
}
for (i = 0; i < 500; i++) {
var origNum = Math.floor(Math.random() * station.length);
var origin = {
x: station[origNum][1],
y: station[origNum][2]
}
var destNum = Math.floor(Math.random() * station.length);
while (origNum == destNum) {
destNum = Math.floor(Math.random() * station.length);
}
var destination = {
x: station[destNum][1],
y: station[destNum][2]
}
podArray.push(new Pod(
startX = origin.x,
startY = origin.y,
endX = destination.x,
endY = destination.y,
riders = randomPass(),
color = colorArray[riders - 1]
));
}
var pct = 0.00;
var fps = 60;
//CALL DRAWING AND ANIMATION
drawLines();
animate();
function animate() {
setTimeout(function() {
if (pct <= 1.00) {
requestAnimFrame(animate)
};
// increment the percent (from 0.00 to 1.00)
pct += .01;
// clear the canvas
ctx3.clearRect(0, 0, layer3.width, layer3.height);
// draw all podArray
for (var i = 0; i < podArray.length; i++) {
// get reference to next aPod
var aPod = podArray[i];
var dx = aPod.endX - aPod.startX;
var dy = aPod.endY - aPod.startY;
var nextX = aPod.startX + dx * pct;
var nextY = aPod.startY + dy * pct;
//create pod on screen
ctx3.fillStyle = aPod.color;
ctx3.beginPath();
ctx3.arc(nextX, nextY, 5, 0, Math.PI * 2, true);
ctx3.fillStyle = aPod.color;
ctx3.fill();
ctx3.closePath();
//STATION LETTERS
for (s = 0; s < station.length; s++) {
ctx2.font = '12pt Calibri';
ctx2.fillStyle = 'red';
ctx2.textAlign = 'center';
ctx2.fillText(station[s][0], station[s][1], (station[s][2]) + 4);
}
}
}, 1000 / fps);
}
</script>
</body>
Your vehicles all reach their destination at the same time because you are changing their position based on a percentage. So when pct==1.00 all vehicles arrive simultaneously at their own endpoints regardless of the distance they need to travel to get there.
// increment the percent (from 0.00 to 1.00)
pct += .01;
To make a vehicle arrive based on distance traveled
Question#1: You can calculate each waypoint (waypoint==unique pixel) the vehicle must travel to complete it's route. Advance the vehicle to it's next waypoint with each new animation frame. This causes each vehicle to arrive based on the length of their route rather than a uniform percentage.
Question#2: For each vehicle, if you pre-calculate & save its waypoints into an array, you can easily get 500 vehicles drawn on the canvas during each animation frame.
Here's annotated and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
ctx.lineWidth=2;
// define routes
var routes=[];
routes.push({
points:linePoints({x:10,y:10},{x:150,y:10}),
currentPoint:0,
color:'red',
});
routes.push({
points:linePoints({x:10,y:50},{x:250,y:65}),
currentPoint:0,
color:'green',
});
routes.push({
points:linePoints({x:10,y:90},{x:325,y:105}),
currentPoint:0,
color:'blue',
});
// animation related vars
var lastTime=0;
var delay=1000/60*5;
// start animating
requestAnimationFrame(animate);
function animate(time){
// return if the desired time hasn't elapsed
if(time<lastTime){requestAnimationFrame(animate);return;}
// redraw each route
ctx.clearRect(0,0,cw,ch);
// var used to stop animating if all routes are completed
var isComplete=true;
for(var i=0;i<routes.length;i++){
var r=routes[i];
// increase the currentPoint, but not beyond points.length-1
if((r.currentPoint+1)<r.points.length-1){
isComplete=false;
r.currentPoint++;
}
// draw the route to its current point
ctx.strokeStyle=r.color;
ctx.beginPath();
ctx.moveTo(r.points[0].x,r.points[0].y);
ctx.lineTo(r.points[r.currentPoint].x,r.points[r.currentPoint].y);
ctx.stroke();
ctx.fillStyle=r.color;
ctx.beginPath();
ctx.arc(r.points[r.currentPoint].x,r.points[r.currentPoint].y,5,0,Math.PI*2);
ctx.fill();
}
// request another frame unless all routes are completed
if(!isComplete){
requestAnimationFrame(animate);
}
}
function linePoints(p1,p2){
// start building a points array with the starting point
var points=[p1];
var dx=p2.x-p1.x;
var dy=p2.y-p1.y;
var count=Math.sqrt(dx*dx+dy*dy)*3;
for(var pct=0;pct<count;pct++){
// calc next waypoint on the line
var x=p1.x+dx*pct/count;
var y=p1.y+dy*pct/count;
var lastPt=points[points.length-1];
// add new waypoint if the its integer pixel value has
// changed from last waypoint
if(parseInt(x)!==parseInt(lastPt.x) || parseInt(y)!==parseInt(lastPt.y)){
points.push({x:x,y:y});
}
}
// force the last point to be the ending point
points[points.length-1]=p2;
// return a unique points[] forming a line from p1 to p2
return(points);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=350 height=300></canvas>
To make your pods go the same speed you will probably want to use the Pythagorean theorem. Hypotenuse is the distance you want the node to travel each time the rAF comes around. Then calculate your x's and y's from that.
I'm not quite sure if I understand what pct does.
To speed up the rAF you'll want to:
kill your set timeout
prerender
Prerending is a bit more work but instead of drawing each circle each and every time you have canvases that aren't in the DOM that you draw to. Then you essentially lay the 'hidden' canvas where ever you want on top of the visible DOM canvas. It keeps the drawing in memory this way.
You also draw each circle on the canvas at the end of the for-loop. Pull the fill method outside of it this way the canvas can batch draw instead of a bunch of little calls (this can really kill performance).
Setting font stuff each time can be removed.
Canvas is awesome for performance but you just have to be careful because one small mistake can lead to a huge bottle-neck.
This is a good article: http://www.html5rocks.com/en/tutorials/canvas/performance/
Lemme know if you have any more questions.
I've written a loop in JavaScript that will render rings of concentric hexagons around a central hexagon on the HTML canvas.
I start with the innermost ring, draw the hex at 3 o'clock, then continue around in a circle until all hexes are rendered. Then I move on to the next ring and repeat.
When you draw hexagons this way (instead of tiling them using solely x and y offsets) any hexagon that is not divisible by 60 is not the same distance to the center hex as those that are divisible by 60 (because these hexes comprise the flat edges, not the vertices, of the larger hex).
The problem I'm having is these hexes (those not divisible by 60 degrees) are rendering in a slightly off position. I'm not sure if it is a floating point math problem, the problem with my algorithm, the problem with my rusty trig, or just plain stupidity. I'm betting 3 out of 4. To cut to the chase, look at the line if (alpha % 60 !== 0) in the code below.
As a point of information, I decided to draw the grid this way because I needed an easy way to map the coordinates of each hex into a data structure, with each hex being identified by its ring # and ID# within that ring. If there is a better way to do it I'm all ears, however, I'd still like to know why my rendering is off.
Here is my very amateur code, so bear with me.
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasApp(){
var xOrigin;
var yOrigin;
var scaleFactor = 30;
var theCanvas = document.getElementById("canvas");
var context;
if (canvas.getContext) {
context = theCanvas.getContext("2d");
window.addEventListener('resize', resizeCanvas, false);
window.addEventListener('orientationchange', resizeCanvas, false);
resizeCanvas();
}
drawScreen();
function resizeCanvas() {
var imgData = context.getImageData(0,0, theCanvas.width, theCanvas.height);
theCanvas.width = window.innerWidth;
theCanvas.height = window.innerHeight;
context.putImageData(imgData,0,0);
xOrigin = theCanvas.width / 2;
yOrigin = theCanvas.height / 2;
}
function drawScreen() {
var rings = 3;
var alpha = 0;
var modifier = 1;
context.clearRect(0, 0, theCanvas.width, theCanvas.height);
drawHex(0,0);
for (var i = 1; i<=rings; i++) {
for (var j = 1; j<=i*6; j++) {
if (alpha % 60 !== 0) {
var h = modifier * scaleFactor / Math.cos(dtr(360 / (6 * i)));
drawHex(h * (Math.cos(dtr(alpha))), h * Math.sin(dtr(alpha)));
}
else {
drawHex(2 * scaleFactor * i * Math.cos(dtr(alpha)), 2 * scaleFactor * i * Math.sin(dtr(alpha)));
}
alpha += 360 / (i*6);
}
modifier+=2;
}
}
function drawHex(xOff, yOff) {
context.fillStyle = '#aaaaaa';
context.strokeStyle = 'black';
context.lineWidth = 2;
context.lineCap = 'square';
context.beginPath();
context.moveTo(xOrigin+xOff-scaleFactor,yOrigin+yOff-Math.tan(dtr(30))*scaleFactor);
context.lineTo(xOrigin+xOff,yOrigin+yOff-scaleFactor/Math.cos(dtr(30)));
context.lineTo(xOrigin+xOff+scaleFactor,yOrigin+yOff-Math.tan(dtr(30))*scaleFactor);
context.lineTo(xOrigin+xOff+scaleFactor,yOrigin+yOff+Math.tan(dtr(30))*scaleFactor);
context.lineTo(xOrigin+xOff,yOrigin+yOff+scaleFactor/Math.cos(dtr(30)));
context.lineTo(xOrigin+xOff-scaleFactor,yOrigin+yOff+Math.tan(dtr(30))*scaleFactor);
context.closePath();
context.stroke();
}
function dtr(ang) {
return ang * Math.PI / 180;
}
function rtd(ang) {
return ang * 180 / Math.PI;
}
}
</script>
Man it took me longer than I'd like to admit to find the pattern for the hexagonal circles. I'm too tired right now to explain since I think I'll need to make some assisting illustrations in order to explain it.
In short, each "circle" of hexagonal shapes is itself hexagonal. The number of hexagonal shapes along one edge is the same as the number of the steps from the center.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
c.width = 500;
c.height = 500;
var hexRadius = 20;
var innerCircleRadius = hexRadius/2*Math.sqrt(3);
var TO_RADIANS = Math.PI/180;
function drawHex(x,y) {
var r = hexRadius;
ctx.beginPath();
ctx.moveTo(x,y-r);
for (var i = 0; i<=6; i++) {
ctx.lineTo(x+Math.cos((i*60-90)*TO_RADIANS)*r,y+Math.sin((i*60-90)*TO_RADIANS)*r);
}
ctx.closePath();
ctx.stroke();
}
drawHexCircle(250,250,4);
function drawHexCircle(x,y,circles) {
var rc = innerCircleRadius;
drawHex(250,250); //center
for (var i = 1; i<=circles; i++) {
for (var j = 0; j<6; j++) {
var currentX = x+Math.cos((j*60)*TO_RADIANS)*rc*2*i;
var currentY = y+Math.sin((j*60)*TO_RADIANS)*rc*2*i;
drawHex(currentX,currentY);
for (var k = 1; k<i; k++) {
var newX = currentX + Math.cos((j*60+120)*TO_RADIANS)*rc*2*k;
var newY = currentY + Math.sin((j*60+120)*TO_RADIANS)*rc*2*k;
drawHex(newX,newY);
}
}
}
}
canvas {
border: 1px solid black;
}
<canvas id="canvas"></canvas>
I think you're trying to use radial coordinates for something that isn't a circle.
As you noted correctly, the (centers of) the vertex hexagons are indeed laid out in a circle and you can use basic radial positioning to lay them out. However, the non-vertex ones are not laid out on an arc of that circle, but on a chord of it (the line connecting two vertex hexagons). So your algorithm, which tries to use a constant h (radius) value for these hexagons, will not lay them out correctly.
You can try interpolating the non-vertex hexagons from the vertex hexagons: the position of of the Kth (out of N) non-vertex hexagon H between vertex hexagons VH1 and VH2 is:
Pos(H) = Pos(VH1) + (K / (N + 1)) * (Pos(VH2)-Pos(VH1))
e.g. in a ring with 4 hexagons per edge (i.e. 2 non-vertex hexagons), look at the line of hexagons between the 3 o'clock and the 5 o'clock: the 3 o'clock is at 0% along that line, the one after that is at 1/3 of the way, the next is at 2/3 of the way, and the 5 o'clock is at 100% of the way. Alternatively you can think of each hexagon along that line as "advancing" by a predetermined vector in the direction between the two vertices until you reach the end of the line.
So basically your algorithm could go through the 6 primary vertex hexagons, each time interpolating the hexagons from the current vertex hexagon to the next. Thus you should probably have three nested loops: one for rings, one for angles on a ring (always six steps), and one for interpolating hexagons along a given angle (number of steps according to ring number).
I want to make a box to move as a sinusoidal graph.
At the point where i am now i simply can't represent the box into the canvas. At the beginning I was able to, but after working out the trigonometry part the box disappeared and a get no error...
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="canvas" width="600" height="300" style="background-color:red"></canvas>
<script type="text/javascript">
var canvas = document.querySelector("canvas");//isoute me document.getElementsByTagName()
var ctx = canvas.getContext("2d");
var can_width = canvas.width;
var can_height = canvas.height;
var x,y;
function PVector(x_,y_){
var y = [];
var x = [0, Math.PI/6, Math.PI/4, Math.PI/3, Math.PI/2, 2/3*Math.PI, 3/4*Math.PI,
5/6*Math.PI, Math.PI, 7/6*Math.PI, 5/4*Math.PI, 4/3*Math.PI, 3/2*Math.PI,
5/3*Math.PI, 7/4*Math.PI, 11/6*Math.PI, 2*Math.PI];
for (var i=0, len=x["length"]; i<len; i++){
var A;
A = Math.sin(x[i]);
y.push(A);
}console.log(y);console.log(x);
return{
x:x,
y:y
};
}
var Point = {
location : {x:0, y: can_height/2},//initial location
velocity : new PVector(x,y),
display : ctx.fillRect(can_width/2,can_height/2 , 25, 25),//initial position of the box
step : function(){
this.location.x += this.velocity.x;
this.location.y += this.velocity.y;
},
display : function(){
ctx.fillRect(this.location.x, this.location.y, 25, 25);
}
};
function update(){
Point["step"]();
ctx.clearRect(0,0, can_width, can_height);
Point["display"]();
window.setTimeout(update, 1000/30);
}
function init(){
update();
}
init();
</script>
</body>
Problem
In your PVector object you are returning Arrays for x and y, while you use them as values in the step() method. This will cause the entire array to be added as a string.
Solution
You need something that traverse that array. Here is an example, it may not be the result you're after, but it shows the principle which you need to apply:
// first note the return type here:
function PVector(x_,y_){
var y = [];
var x = [0, Math.PI/6, Math.PI/4, Math.PI/3, Math.PI/2, 2/3*Math.PI,
...snipped---
return {
x:x, // x and y are arrays
y:y
};
}
var Point = {
location: {
x: 0,
y: can_height / 2,
step: 0 // some counter to keep track of array position
}, //initial location
velocity: new PVector(x, y),
step: function () {
this.location.step++; // increase step for arrays
// using modulo will keep the step as a valid value within the array length:
// if step = 7 and length = 5, index will become 2 (sort of "wrap around")
var indexX = this.location.step % this.velocity.x.length;
var indexY = this.location.step % this.velocity.y.length
this.location.x += this.velocity.x[indexX];
this.location.y += this.velocity.y[indexY];
},
...
Updated fiddle
Tip: I would as Robin in his answer, recommend to simplify the sinus calculation. Sinus-tables are good when performance is needed and the browser can't keep up (ie. will thousands of objects), but in simpler scenario, direct calculation will work too.
If your goal is just to have a box moving in a sinusoidal graph, it can be done simpler.
This jsfiddle shows a slightly simpler example of a box moving in a sinusoidal graph where I just removed parts of your code and calculate the path with Math.sin and use time instead of precalculated values for x.
function update(){
time += 0.1;
ctx.clearRect(0,0, can_width, can_height);
x = time;
y = (can_height/2)+(can_height/2)*Math.sin(time);
console.log(x, y);
ctx.fillRect(x*16, y, 25, 25);
window.setTimeout(update, 1000/30);
}
The variables are modified to make it look ok on the canvas. You can edit the addition to time, and the altitude and base line for y, to fit your needs.
If you need to follow the specification in your code, look at the answer by Ken.
Since you want a sinusoidal move, it makes sense to use... the sin function.
The formula for a sinusoidal move is :
y = maxValue * sin ( 2 * PI * frequency * time ) ;
where the frequency is in Herz (== 'number of times per second') and time is in second.
Most likely you'll use Date.now(), so you'll have a time in millisecond, that you need to convert. Since the value of PI should not change in the near future, you can compute once the magic number
k = 2 * PI / 1000 = 0.006283185307179587
and the formula becomes :
y = sin( k * frequency * Date.now() );
Here's a simple example on how to use the formula :
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var can_width = canvas.width;
var can_height = canvas.height;
// magic number
var k = 0.006283185307179587;
// oscillations per second
var frequency = 1/5;
// ...
var amplitude = can_width / 8;
function animate() {
ctx.clearRect(0,0,can_width, can_height);
ctx.fillStyle= '#000';
var y = amplitude * Math.sin ( k * frequency * Date.now() );
ctx.fillRect(can_width/2, can_height/2 + y, 20, 20 );
}
setInterval(animate, 30);
<canvas id="canvas" width="400" height="200" style="background-color:red"></canvas>
So to explain simply i've this SVG circle element as so:
var circle = function(cx,cy,r) {
var svgCircle = document.createElementNS(NS,"circle");
svgCircle.setAttributeNS(null,"cx", cx);
svgCircle.setAttributeNS(null,"cy", cy);
svgCircle.setAttributeNS(null,"r", r);
return svgCircle;
}
it does the job it needs todo however i now need to create an abitrary amount of circles I.E 7, and position each so that they look like the outline of a circle.
so a circle made up of circles.
I just can't seem to understand how it works. Also an explanation or some guidance would go a long way!
This will draw satellite circles around a center-point
The key is to convert your calculated polar (angle,radius) coordinates to cartesian (x,y). See the added function polarToCartesian() in the example below:
<svg id="my_svg" height="400" width="400"></svg>
<script type="text/javascript">
function drawCircle(cx,cy,r){
var svgCircle = document.createElementNS('http://www.w3.org/2000/svg',"circle");
svgCircle.setAttributeNS(null,"cx", cx);
svgCircle.setAttributeNS(null,"cy", cy);
svgCircle.setAttributeNS(null,"r", r);
svgCircle.setAttributeNS(null,"stroke",'blue')
svgCircle.setAttributeNS(null,"fill",'transparent')
return svgCircle;
}
function polarToCartesian(center_x, center_y, radius, angle_in_degrees) {
var return_value = {}
var angle_in_radians = angle_in_degrees * Math.PI / 180.0;
return_value.x = center_x + radius * Math.cos(angle_in_radians);
return_value.y = center_y + radius * Math.sin(angle_in_radians);
return return_value;
}
/* ==============================
Loop to Draw Satellite circles
============================== */
// The center is the same for all circles
var cx = 200
var cy = 200
var radius_of_satellites_from_center = 100
var radius_of_small_circles = 10
var number_of_satellite_circles = 7
// The angle increments for each circle drawn
for(var n=0; n<number_of_satellite_circles; n++){
// Find how many degrees separate each circle
var degrees_of_separation = 360/number_of_satellite_circles
var angle_as_degrees = degrees_of_separation * n
var coordinates = polarToCartesian(cx, cy, radius_of_satellites_from_center, angle_as_degrees)
document.getElementById('my_svg').appendChild( drawCircle(coordinates.x,coordinates.y,radius_of_small_circles) )
}
</script>
This will draw concentric circles:
<svg id="my_svg" height="400" width="400"></svg>
<script type="text/javascript">
function drawCircle(cx,cy,r){
var svgCircle = document.createElementNS('http://www.w3.org/2000/svg',"circle");
svgCircle.setAttributeNS(null,"cx", cx);
svgCircle.setAttributeNS(null,"cy", cy);
svgCircle.setAttributeNS(null,"r", r);
svgCircle.setAttributeNS(null,"stroke",'blue')
svgCircle.setAttributeNS(null,"fill",'transparent')
return svgCircle;
}
/* ===============================
Loop to Draw concentric circles
=============================== */
// The center is the same for all circles
var cx = 200
var cy = 200
// More settings you can change
var starting_radius = 50
var number_of_circles = 7
var gap_between_circles = 3
// The radius increments for each circle drawn
for(var n=number_of_circles; n>0; n--){
var radius = starting_radius + n * gap_between_circles
document.getElementById('my_svg').appendChild( drawCircle(cx,cy,radius) )
}
</script>