How to draw an infinite Hexagon Spiral - javascript

I'm trying to create a Structure as shown in the Screenshot below. Is there a way to build an Algorithm for this in JavaScript to get the X and Y Coordinates of each red Point in chronological order to generate an infinite Spiral depending on a specific amount?
Screenshot of how it should look and work
This Code generates me a regular Hexagon:
function hexagon(centerX, centerY) {
var ctx = canvas.getContext('2d');
var x = centerX + Math.cos(Math.PI * 2 / 6) * 50;
var y = centerY + Math.sin(Math.PI * 2 / 6) * 50;
ctx.beginPath();
ctx.moveTo(x, y);
for (var i = 1; i < 7; i++) {
x = centerX + Math.cos(Math.PI * 2 / 6 * i) * 50;
y = centerY + Math.sin(Math.PI * 2 / 6 * i) * 50;
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.stroke();
}
And this is the Cluster-function so far:
function cluster(centerX, centerY, count) {
var ctx = canvas.getContext('2d');
for (var i = 0; i < count; i++) {
if (i == 0) {
var x = centerX;
var y = centerY;
} else {
var x = centerX + Math.cos(-Math.PI / 2) * (100 * (Math.sqrt(3) / 2));
var y = centerY + Math.sin(-Math.PI / 2) * (100 * (Math.sqrt(3) / 2));
}
hexagon(x, y);
}
}
Thank you!

Your cluster function could be like this:
function cluster(centerX, centerY, count) {
var x = centerX,
y = centerY,
angle = Math.PI / 3,
dist = Math.sin(angle) * 100,
i = 1,
side = 0;
hexagon(x, y);
count--;
while (count > 0) {
for (var t = 0; t < Math.floor((side+4)/6)+(side%6==0) && count; t++) {
y = y - dist * Math.cos(side * angle);
x = x - dist * Math.sin(side * angle);
hexagon(x, y);
count--;
}
side++;
}
}
function hexagon(centerX, centerY) {
var ctx = canvas.getContext('2d');
var x = centerX + Math.cos(Math.PI * 2 / 6) * 50;
var y = centerY + Math.sin(Math.PI * 2 / 6) * 50;
ctx.beginPath();
ctx.moveTo(x, y);
for (var i = 1; i < 7; i++) {
x = centerX + Math.cos(Math.PI * 2 / 6 * i) * 50;
y = centerY + Math.sin(Math.PI * 2 / 6 * i) * 50;
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.stroke();
}
function cluster(centerX, centerY, count) {
var x = centerX,
y = centerY,
angle = Math.PI / 3,
dist = Math.sin(angle) * 100,
i = 1,
side = 0;
hexagon(x, y);
count--;
while (count > 0) {
for (var t = 0; t < Math.floor((side+4)/6)+(side%6==0) && count; t++) {
y = y - dist * Math.cos(side * angle);
x = x - dist * Math.sin(side * angle);
hexagon(x, y);
count--;
}
side++;
}
}
cluster(200,230,9);
<canvas id="canvas" width="400" height="400"></canvas>

Related

Textures in a raycasting world

I've been working on a JavaScript raycasting project, I use DDA to cast the rays. I want to add textures, but I'm not exactly sure how to do that (maybe exporting images in the .ppm file format, but I wouldn't know how to actually cast the textures on each wall).
Here's my code (two canvases, #canvas is for the 2D world, #canvas2 is for the projection):
const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");
const canvas2 = document.querySelector("#canvas2");
const ctx2 = canvas2.getContext("2d");
let width = 513;
let height = 513;
let width2 = 1024;
let height2 = 512;
const columns = 2000;
let columnWidth = width2 / columns;
canvas.width = width;
canvas.height = height;
canvas2.width = width2;
canvas2.height = height2;
let deltaTime = 0;
let lastFrame = Date.now();
const moveSpeed = 3.5;
const rotationSpeed = 175;
const FOV = 80;
let map = [
"##########",
"#...#....#",
"#...#....#",
"### #....#",
"#...#....#",
"# ###....#",
"#...#### #",
"#........#",
"#........#",
"##########"
];
let tileHeight = 512 / map.length;
let tileWidth = 512 / map[0].length;
const player = {
x: 2.5,
y: 8.5,
heading: 270
}
const keyMap = {};
const deg2rad = deg => deg * Math.PI / 180;
const willCollide = ({ xC, yC }, { x, y, w, h }) => {
boundingBox = {
left: x - w / 2,
right: x + w / 2,
top: y - h / 2,
bottom: y + h / 2
}
return xC > boundingBox.left && xC < boundingBox.right && yC > boundingBox.top && yC < boundingBox.bottom;
}
const handlePlayerMovement = () => {
if (keyMap.w) {
let dx = Math.cos(player.heading * Math.PI / 180);
let dy = Math.sin(player.heading * Math.PI / 180);
const xCheck = player.x + dx * 5 * deltaTime;
const yCheck = player.y + dy * 5 * deltaTime;
let canMoveX = true;
let canMoveY = true;
if (map[Math.floor(yCheck)][Math.floor(player.x)] == "#") canMoveY = false;
if (map[Math.floor(player.y)][Math.floor(xCheck)] == "#") canMoveX = false;
if (canMoveX) player.x += dx * moveSpeed * deltaTime;
if (canMoveY) player.y += dy * moveSpeed * deltaTime;
}
if (keyMap.s) {
let dx = Math.cos((player.heading * Math.PI / 180) + Math.PI);
let dy = Math.sin((player.heading * Math.PI / 180) + Math.PI);
const xCheck = player.x + dx * 5 * deltaTime;
const yCheck = player.y + dy * 5 * deltaTime;
let canMoveX = true;
let canMoveY = true;
if (map[Math.floor(yCheck)][Math.floor(player.x)] == "#") canMoveY = false;
if (map[Math.floor(player.y)][Math.floor(xCheck)] == "#") canMoveX = false;
if (canMoveX) player.x += dx * moveSpeed * deltaTime;
if (canMoveY) player.y += dy * moveSpeed * deltaTime;
}
if (keyMap.a) player.heading -= rotationSpeed * deltaTime;
if (keyMap.d) player.heading += rotationSpeed * deltaTime;
}
const generateColumns = () => {
for (let i = 0; i < columns; i++) {
let collided = false;
let angle = (player.heading - FOV / 2) + (i / columns) * FOV;
let dx = Math.cos(angle * Math.PI / 180);
let dy = Math.sin(angle * Math.PI / 180);
let stepX = Math.sign(dx);
let stepY = Math.sign(dy);
let dispX = dx == 0 ? 0 : Math.abs(1 / dx);
let dispY = dy == 0 ? 0 : Math.abs(1 / dy);
let distX = (dx > 0 ? Math.ceil(player.x) - player.x : player.x - Math.floor(player.x)) * dispX;
let distY = (dy > 0 ? Math.ceil(player.y) - player.y : player.y - Math.floor(player.y)) * dispY;
let gridX = Math.floor(player.x);
let gridY = Math.floor(player.y);
let dist = 0;
let side = 0;
while (!collided) {
if (distX < distY) {
gridX += stepX;
dist = distX;
distX += dispX;
side = 1;
} else {
gridY += stepY;
dist = distY;
distY += dispY;
side = 0;
}
// ctx.beginPath();
// ctx.arc((player.x + dist * dx) * tileWidth, (player.y + dist * dy) * tileHeight, 3, 0, Math.PI * 2);
// ctx.fill();
if (map[gridY][gridX] == "#") {
collided = true;
let fix_dist = dist * Math.cos((player.heading - angle) * Math.PI / 180); // fisheye fix
let h = 550 / fix_dist;
let fill = `rgb(0, ${side ? 220 : 255}, 0`;
ctx2.fillStyle = fill;
ctx2.fillRect(Math.round(columnWidth * i), height2 / 2 - h / 2, columnWidth + 1, h);
}
}
ctx.strokeStyle = "#fff";
ctx.beginPath();
ctx.moveTo(player.x * tileWidth, player.y * tileHeight);
ctx.lineTo((player.x + dist * dx) * tileWidth, (player.y + dist * dy) * tileHeight);
ctx.stroke();
}
}
const update = () => {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, width, height);
ctx2.fillStyle = "#000";
ctx2.fillRect(0, 0, width2, height2 / 2);
ctx2.fillStyle = "#fff";
ctx2.fillRect(0, height2 - height2 / 2, width2, height2 / 2);
if (player.heading < 0) player.heading += 360;
if (player.heading > 360) player.heading -= 360;
handlePlayerMovement();
for (let y = 0; y < map.length; y++) {
for (let x = 0; x < map[y].length; x++) {
ctx.fillStyle = "#fff";
if (map[y][x] == "#") ctx.fillRect(x * tileWidth, y * tileHeight, tileWidth, tileHeight);
ctx.fillStyle = "#666";
ctx.fillRect(0, y * tileHeight, 512, 1);
ctx.fillRect(x * tileWidth, 0, 1, 512);
}
}
generateColumns();
deltaTime = (Date.now() / 1000 - lastFrame / 1000);
lastFrame = Date.now();
requestAnimationFrame(update);
}
update();
window.onkeydown = (e) => {
keyMap[e.key.toLowerCase()] = true;
}
window.onkeyup = (e) => {
keyMap[e.key.toLowerCase()] = false;
}

how to use html canvas in between 2 <div>s

I tried to use canvas in middle of my html page.but it does't work fine. I want to use ribbon effect in div. it works fine when the there is no div and when there is div or other element it doesn't work. I want use canvas in between two div. I used ribbon pen in codepen and will post the code below. I want to how to use it in my html page.
var TWO_PI = Math.PI * 2;
var HALF_PI = Math.PI * 0.5;
var THICKNESS = 12;
var LENGTH = 10;
var STEP = 0.1;
var FPS = 1000 / 60;
function Particle(x, y, mass) {
this.x = x || 0;
this.y = y || 0;
this.ox = this.x;
this.oy = this.y;
this.mass = mass || 1.0;
this.massInv = 1.0 / this.mass;
this.fixed = false;
this.update = function (dt) {
if (!this.fixed) {
var fx = 0.0000;
var fy = 0.0000;
var tx = this.x,
ty = this.y;
this.x += (this.x - this.ox) + fx * this.massInv * dt * dt;
this.y += (this.y - this.oy) + fy * this.massInv * dt * dt;
this.ox = tx;
this.oy = ty;
}
};
};
function Spring(p1, p2, restLength, strength) {
this.p1 = p1;
this.p2 = p2;
this.restLength = restLength || 10;
this.strength = strength || 1.0;
this.update = function (dt) {
// Compute desired force
var dx = p2.x - p1.x,
dy = p2.y - p1.y,
dd = Math.sqrt(dx * dx + dy * dy) + 0.0001,
tf = (dd - this.restLength) / (dd * (p1.massInv + p2.massInv)) * this.strength,
f;
// Apply forces
if (!p1.fixed) {
f = tf * p1.massInv;
p1.x += dx * f;
p1.y += dy * f;
}
if (!p2.fixed) {
f = -tf * p2.massInv;
p2.x += dx * f;
p2.y += dy * f;
}
}
};
function Sim() {
this.particles = [];
this.springs = [];
this.tick = function (dt) {
var i, n;
for (i = 0, n = this.springs.length; i < n; ++i) {
this.springs[i].update(dt);
}
for (i = 0, n = this.particles.length; i < n; ++i) {
this.particles[i].update(dt);
}
}
};
// Create a new system
var sim = new Sim(),
old = new Date().getTime(),
canvas = document.getElementById('world'),
context = canvas.getContext('2d');
function init() {
var np,
op,
mouse,
anchor,
step = STEP,
length = LENGTH,
count = length / step;
var sx = canvas.width * 0.5;
var sy = canvas.height * 0.5;
for (var i = 0; i < count; ++i) {
//np = new Particle(i*8,i*8,0.1+Math.random()*0.01);
np = new Particle(sx + (Math.random() - 0.5) * 200, sy + (Math.random() - 0.5) * 200, 0.1 + Math.random() * 0.01);
sim.particles.push(np);
if (i > 0) {
s = new Spring(np, op, step, 0.95);
sim.springs.push(s);
}
op = np;
}
// Fix the first particle
anchor = sim.particles[0];
//anchor.fixed = true;
anchor.x = 50;
anchor.y = 50;
// Move last particle with mouse
mouse = sim.particles[count - 1];
mouse.fixed = true;
canvas.addEventListener('mousemove', function (event) {
mouse.x = event.clientX;
mouse.y = event.clientY;
});
};
function step() {
var now = new Date().getTime(),
delta = now - old;
sim.tick(delta);
// Clear canvas
canvas.width = canvas.width;
var points = []; // Midpoints
var angles = []; // Delta angles
var i, n, p1, p2, dx, dy, mx, my, sin, cos, theta;
// Compute midpoints and angles
for (i = 0, n = sim.particles.length - 1; i < n; ++i) {
p1 = sim.particles[i];
p2 = sim.particles[i + 1];
dx = p2.x - p1.x;
dy = p2.y - p1.y;
mx = p1.x + dx * 0.5;
my = p1.y + dy * 0.5;
points[i] = {
x: mx,
y: my
};
angles[i] = Math.atan2(dy, dx);
}
// Render
context.beginPath();
for (i = 0, n = points.length; i < n; ++i) {
p1 = sim.particles[i];
p2 = points[i];
theta = angles[i];
r = Math.sin((i / n) * Math.PI) * THICKNESS;
sin = Math.sin(theta - HALF_PI) * r;
cos = Math.cos(theta - HALF_PI) * r;
context.quadraticCurveTo(
p1.x + cos,
p1.y + sin,
p2.x + cos,
p2.y + sin);
}
for (i = points.length - 1; i >= 0; --i) {
p1 = sim.particles[i + 1];
p2 = points[i];
theta = angles[i];
r = Math.sin((i / n) * Math.PI) * THICKNESS;
sin = Math.sin(theta + HALF_PI) * r;
cos = Math.cos(theta + HALF_PI) * r;
context.quadraticCurveTo(
p1.x + cos,
p1.y + sin,
p2.x + cos,
p2.y + sin);
}
context.strokeStyle = 'rgba(255,255,255,0.1)';
context.lineWidth = 8;
context.stroke();
context.strokeStyle = 'rgba(0,0,0,0.8)';
context.lineWidth = 0.5;
context.stroke();
context.fillStyle = 'rgba(255,255,255,0.9)';
context.fill();
old = now;
setTimeout(step, FPS);
};
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener("resize", resize);
resize();
init();
step();
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,600,700' rel='stylesheet' type='text/css'>
<canvas id='world' width='500' height='500'></canvas>
<header><h1>Wiggle your mouse...</h1></header>
Here is one way:
HTML:
<div class="top">
top
</div>
<div class="middle">
<canvas id='world' width='500' height='500'></canvas>
<header><h1>Wiggle your mouse...</h1></header>
</div>
<div class="bottom">
bottom
</div>
CSS:
div {
border: 1px solid black
}
.top, .bottom {
height: 200px
}
The js remains the same. The CSS gives the top and bottom divs some height. The canvas is in the middle div. Here is a jsfiddle: https://jsfiddle.net/av902pcs/

HTML5 Canvas + JavaScript wireframe sphere transformation issue

I have written some JS code to plot a 3D wireframe sphere into a HTML5 canvas.
I started from this post and improved it by using Qt3D vertices generation for a sphere mesh. The JS code does 2 passes on the vertices: the first to display rings, and the second to display slices. Normally OpenGL would connect all the vertices automatically with triangles.
I kept the slices/rings configurable but I have issues with the transformation code, for example when I rotate the sphere along the X axis.
So, starting from the basics. Here's a 1-pass, 4 rings, 4 slices, no transformation:
Seems all good. Now 2-passes, 10 rings, 10 slices, no transformation:
Still good, but if I rotate it 30° on the X Axis, the top and bottom vertices (Y position only apparently) get messed up.
I suspect there is something wrong in the rotation functions, or in the projection function.
Can someone please help me to figure out what's going on here ?
(Note that I don't want to use Three.js cause my goal is to port this in a QML application)
Here's the full code.
var sphere = new Sphere3D();
var rotation = new Point3D();
var distance = 1000;
var lastX = -1;
var lastY = -1;
function Point3D() {
this.x = 0;
this.y = 0;
this.z = 0;
}
function Sphere3D(radius) {
this.vertices = new Array();
this.radius = (typeof(radius) == "undefined" || typeof(radius) != "number") ? 20.0 : radius;
this.rings = 10;
this.slices = 10;
this.numberOfVertices = 0;
var M_PI_2 = Math.PI / 2;
var dTheta = (Math.PI * 2) / this.slices;
var dPhi = Math.PI / this.rings;
// Iterate over latitudes (rings)
for (var lat = 0; lat < this.rings + 1; ++lat) {
var phi = M_PI_2 - lat * dPhi;
var cosPhi = Math.cos(phi);
var sinPhi = Math.sin(phi);
// Iterate over longitudes (slices)
for (var lon = 0; lon < this.slices + 1; ++lon) {
var theta = lon * dTheta;
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
p = this.vertices[this.numberOfVertices] = new Point3D();
p.x = this.radius * cosTheta * cosPhi;
p.y = this.radius * sinPhi;
p.z = this.radius * sinTheta * cosPhi;
this.numberOfVertices++;
}
}
}
function rotateX(point, radians) {
var y = point.y;
point.y = (y * Math.cos(radians)) + (point.z * Math.sin(radians) * -1.0);
point.z = (y * Math.sin(radians)) + (point.z * Math.cos(radians));
}
function rotateY(point, radians) {
var x = point.x;
point.x = (x * Math.cos(radians)) + (point.z * Math.sin(radians) * -1.0);
point.z = (x * Math.sin(radians)) + (point.z * Math.cos(radians));
}
function rotateZ(point, radians) {
var x = point.x;
point.x = (x * Math.cos(radians)) + (point.y * Math.sin(radians) * -1.0);
point.y = (x * Math.sin(radians)) + (point.y * Math.cos(radians));
}
function projection(xy, z, xyOffset, zOffset, distance) {
return ((distance * xy) / (z - zOffset)) + xyOffset;
}
function strokeSegment(index, ctx, width, height) {
var x, y;
var p = sphere.vertices[index];
rotateX(p, rotation.x);
rotateY(p, rotation.y);
rotateZ(p, rotation.z);
x = projection(p.x, p.z, width / 2.0, 100.0, distance);
y = projection(p.y, p.z, height / 2.0, 100.0, distance);
if (lastX == -1 && lastY == -1) {
lastX = x;
lastY = y;
return;
}
if (x >= 0 && x < width && y >= 0 && y < height) {
if (p.z < 0) {
ctx.strokeStyle = "gray";
} else {
ctx.strokeStyle = "white";
}
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(x, y);
ctx.stroke();
ctx.closePath();
lastX = x;
lastY = y;
}
}
function render() {
var canvas = document.getElementById("sphere3d");
var width = canvas.getAttribute("width");
var height = canvas.getAttribute("height");
var ctx = canvas.getContext('2d');
var p = new Point3D();
ctx.fillStyle = "black";
ctx.clearRect(0, 0, width, height);
ctx.fillRect(0, 0, width, height);
// draw each vertex to get the first sphere skeleton
for (i = 0; i < sphere.numberOfVertices; i++) {
strokeSegment(i, ctx, width, height);
}
// now walk through rings to draw the slices
for (i = 0; i < sphere.slices + 1; i++) {
for (var j = 0; j < sphere.rings + 1; j++) {
strokeSegment(i + (j * (sphere.slices + 1)), ctx, width, height);
}
}
}
function init() {
rotation.x = Math.PI / 6;
render();
}
canvas {
background: black;
display: block;
}
<body onLoad="init();">
<canvas id="sphere3d" width="500" height="500">
Your browser does not support HTML5 canvas.
</canvas>
</body>
Your problem is that the contents of your sphere.vertices[] array is being modified inside your strokeSegment() call so the rotation gets applied twice when you invoke it the second time on each point. So, in strokeSegment() you need to replace:
var p = sphere.vertices[index];
with:
var p = new Point3D();
p.x = sphere.vertices[index].x;
p.y = sphere.vertices[index].y;
p.z = sphere.vertices[index].z;
Then it works perfectly as shown below:
var sphere = new Sphere3D();
var rotation = new Point3D();
var distance = 1000;
var lastX = -1;
var lastY = -1;
function Point3D() {
this.x = 0;
this.y = 0;
this.z = 0;
}
function Sphere3D(radius) {
this.vertices = new Array();
this.radius = (typeof(radius) == "undefined" || typeof(radius) != "number") ? 20.0 : radius;
this.rings = 10;
this.slices = 10;
this.numberOfVertices = 0;
var M_PI_2 = Math.PI / 2;
var dTheta = (Math.PI * 2) / this.slices;
var dPhi = Math.PI / this.rings;
// Iterate over latitudes (rings)
for (var lat = 0; lat < this.rings + 1; ++lat) {
var phi = M_PI_2 - lat * dPhi;
var cosPhi = Math.cos(phi);
var sinPhi = Math.sin(phi);
// Iterate over longitudes (slices)
for (var lon = 0; lon < this.slices + 1; ++lon) {
var theta = lon * dTheta;
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
p = this.vertices[this.numberOfVertices] = new Point3D();
p.x = this.radius * cosTheta * cosPhi;
p.y = this.radius * sinPhi;
p.z = this.radius * sinTheta * cosPhi;
this.numberOfVertices++;
}
}
}
function rotateX(point, radians) {
var y = point.y;
point.y = (y * Math.cos(radians)) + (point.z * Math.sin(radians) * -1.0);
point.z = (y * Math.sin(radians)) + (point.z * Math.cos(radians));
}
function rotateY(point, radians) {
var x = point.x;
point.x = (x * Math.cos(radians)) + (point.z * Math.sin(radians) * -1.0);
point.z = (x * Math.sin(radians)) + (point.z * Math.cos(radians));
}
function rotateZ(point, radians) {
var x = point.x;
point.x = (x * Math.cos(radians)) + (point.y * Math.sin(radians) * -1.0);
point.y = (x * Math.sin(radians)) + (point.y * Math.cos(radians));
}
function projection(xy, z, xyOffset, zOffset, distance) {
return ((distance * xy) / (z - zOffset)) + xyOffset;
}
function strokeSegment(index, ctx, width, height) {
var x, y;
var p = new Point3D();
p.x = sphere.vertices[index].x;
p.y = sphere.vertices[index].y;
p.z = sphere.vertices[index].z;
rotateX(p, rotation.x);
rotateY(p, rotation.y);
rotateZ(p, rotation.z);
x = projection(p.x, p.z, width / 2.0, 100.0, distance);
y = projection(p.y, p.z, height / 2.0, 100.0, distance);
if (lastX == -1 && lastY == -1) {
lastX = x;
lastY = y;
return;
}
if (x >= 0 && x < width && y >= 0 && y < height) {
if (p.z < 0) {
ctx.strokeStyle = "gray";
} else {
ctx.strokeStyle = "white";
}
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(x, y);
ctx.stroke();
ctx.closePath();
lastX = x;
lastY = y;
}
}
function render() {
var canvas = document.getElementById("sphere3d");
var width = canvas.getAttribute("width");
var height = canvas.getAttribute("height");
var ctx = canvas.getContext('2d');
var p = new Point3D();
ctx.fillStyle = "black";
ctx.clearRect(0, 0, width, height);
ctx.fillRect(0, 0, width, height);
// draw each vertex to get the first sphere skeleton
for (i = 0; i < sphere.numberOfVertices; i++) {
strokeSegment(i, ctx, width, height);
}
// now walk through rings to draw the slices
for (i = 0; i < sphere.slices + 1; i++) {
for (var j = 0; j < sphere.rings + 1; j++) {
strokeSegment(i + (j * (sphere.slices + 1)), ctx, width, height);
}
}
}
function init() {
rotation.x = Math.PI / 3;
render();
}
canvas {
background: black;
display: block;
}
<body onLoad="init();">
<canvas id="sphere3d" width="500" height="500">
Your browser does not support HTML5 canvas.
</canvas>
</body>
Short answer
The bug is in strokeSegment function
function strokeSegment(index, ctx, width, height) {
var x, y;
var p = sphere.vertices[index];
rotateX(p, rotation.x);
rotateY(p, rotation.y);
rotateZ(p, rotation.z);
...
The bug is that all the rotate functions modify p inplace and thus the modify the value stored in sphere.vertices! So the way to fix it is simply to clone the point:
function strokeSegment(index, ctx, width, height) {
var x, y;
var p0 = sphere.vertices[index];
var p = new Point3D();
p.x = p0.x;
p.y = p0.y;
p.z = p0.z;
rotateX(p, rotation.x);
rotateY(p, rotation.y);
rotateZ(p, rotation.z);
...
You may find demo with fixed code at https://plnkr.co/edit/zs5ZxbglFxo9cbwA6MI5?p=preview
Longer addition
Before I found this issue I played with your code a bit and I think improved it. Improved version is available at https://plnkr.co/edit/tpTZ8GH9eByVARUIYZBi?p=preview
var sphere = new Sphere3D();
var rotation = new Point3D(0, 0, 0);
var distance = 1000;
var EMPTY_VALUE = Number.MIN_VALUE;
function Point3D(x, y, z) {
if (arguments.length == 3) {
this.x = x;
this.y = y;
this.z = z;
}
else if (arguments.length == 1) {
fillPointFromPoint(this, x); // 1 argument means point
}
else {
clearPoint(this); // no arguments mean creat empty
}
}
function fillPointFromPoint(target, src) {
target.x = src.x;
target.y = src.y;
target.z = src.z;
}
function clearPoint(p) {
p.x = EMPTY_VALUE;
p.y = EMPTY_VALUE;
p.z = EMPTY_VALUE;
}
function Sphere3D(radius) {
this.radius = (typeof(radius) == "undefined" || typeof(radius) != "number") ? 20.0 : radius;
this.innerRingsCount = 9; // better be odd so we have explicit Equator
this.slicesCount = 8;
var M_PI_2 = Math.PI / 2;
var dTheta = (Math.PI * 2) / this.slicesCount;
var dPhi = Math.PI / this.innerRingsCount;
this.rings = [];
// always add both poles
this.rings.push([new Point3D(0, this.radius, 0)]);
// Iterate over latitudes (rings)
for (var lat = 0; lat < this.innerRingsCount; ++lat) {
var phi = M_PI_2 - lat * dPhi - dPhi / 2;
var cosPhi = Math.cos(phi);
var sinPhi = Math.sin(phi);
console.log("lat = " + lat + " phi = " + (phi / Math.PI) + " sinPhi = " + sinPhi);
var vertices = [];
// Iterate over longitudes (slices)
for (var lon = 0; lon < this.slicesCount; ++lon) {
var theta = lon * dTheta;
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
var p = new Point3D();
p.x = this.radius * cosTheta * cosPhi;
p.y = this.radius * sinPhi;
p.z = this.radius * sinTheta * cosPhi;
vertices.push(p);
}
this.rings.push(vertices);
}
// always add both poles
this.rings.push([new Point3D(0, -this.radius, 0)]);
}
function rotateX(point, radians) {
var y = point.y;
point.y = (y * Math.cos(radians)) + (point.z * Math.sin(radians) * -1.0);
point.z = (y * Math.sin(radians)) + (point.z * Math.cos(radians));
}
function rotateY(point, radians) {
var x = point.x;
point.x = (x * Math.cos(radians)) + (point.z * Math.sin(radians) * -1.0);
point.z = (x * Math.sin(radians)) + (point.z * Math.cos(radians));
}
function rotateZ(point, radians) {
var x = point.x;
point.x = (x * Math.cos(radians)) + (point.y * Math.sin(radians) * -1.0);
point.y = (x * Math.sin(radians)) + (point.y * Math.cos(radians));
}
function projection(xy, z, xyOffset, zOffset, distance) {
return ((distance * xy) / (z - zOffset)) + xyOffset;
}
var lastP = new Point3D();
var firstP = new Point3D();
function startRenderingPortion() {
clearPoint(lastP);
clearPoint(firstP);
}
function closeRenderingPortion(ctx, width, height) {
strokeSegmentImpl(ctx, firstP.x, firstP.y, firstP.z, width, height);
clearPoint(lastP);
clearPoint(firstP);
}
function strokeSegmentImpl(ctx, x, y, z, width, height) {
if (x >= 0 && x < width && y >= 0 && y < height) {
// as we work with floating point numbers, there might near zero that != 0
// choose gray if one of two points is definitely (z < 0) and other has (z <= 0)
// Note also that in term of visibility this is a wrong logic! Line is invisible
// only if it is shadowed by another polygon and this depends on relative "Z" not
// absolute values
var eps = 0.01;
if (((z < -eps) && (lastP.z < eps))
|| ((z < eps) && (lastP.z < -eps))) {
ctx.strokeStyle = "gray";
} else {
ctx.strokeStyle = "white";
}
if ((x === lastP.x) && (y == lastP.y)) {
ctx.beginPath();
// draw single point
ctx.moveTo(x, y);
ctx.lineTo(x + 1, y + 1);
ctx.stroke();
ctx.closePath();
} else {
ctx.beginPath();
ctx.moveTo(lastP.x, lastP.y);
ctx.lineTo(x, y);
ctx.stroke();
ctx.closePath();
}
lastP.x = x;
lastP.y = y;
lastP.z = z;
}
}
function strokeSegment(p0, ctx, width, height) {
var p = new Point3D(p0); // clone original point to not mess it up with rotation!
rotateX(p, rotation.x);
rotateY(p, rotation.y);
rotateZ(p, rotation.z);
var x, y;
x = projection(p.x, p.z, width / 2.0, 100.0, distance);
y = projection(p.y, p.z, height / 2.0, 100.0, distance);
if (lastP.x === EMPTY_VALUE && lastP.y === EMPTY_VALUE) {
lastP = new Point3D(x, y, p.z);
fillPointFromPoint(firstP, lastP);
return;
}
strokeSegmentImpl(ctx, x, y, p.z, width, height);
}
function renderSphere(ctx, width, height, sphere) {
var i, j;
var vertices;
// draw each vertex to get the first sphere skeleton
for (i = 0; i < sphere.rings.length; i++) {
startRenderingPortion();
vertices = sphere.rings[i];
for (j = 0; j < vertices.length; j++) {
strokeSegment(vertices[j], ctx, width, height);
}
closeRenderingPortion(ctx, width, height);
}
// now walk through rings to draw the slices
for (i = 0; i < sphere.slicesCount; i++) {
startRenderingPortion();
for (j = 0; j < sphere.rings.length; j++) {
vertices = sphere.rings[j];
var p = vertices[i % vertices.length];// for top and bottom vertices.length = 1
strokeSegment(p, ctx, width, height);
}
//closeRenderingPortion(ctx, width, height); // don't close back!
}
}
function render() {
var canvas = document.getElementById("sphere3d");
var width = canvas.getAttribute("width");
var height = canvas.getAttribute("height");
var ctx = canvas.getContext('2d');
ctx.fillStyle = "black";
ctx.clearRect(0, 0, width, height);
ctx.fillRect(0, 0, width, height);
renderSphere(ctx, width, height, sphere);
}
function init() {
rotation.x = Math.PI / 6;
//rotation.y = Math.PI / 6;
rotation.z = Math.PI / 6;
render();
}
Main changes are:
I explicitly separated plain array vertices in array of arrays rings and also explicitly add both poles to it.
Separation of rings allowed me to clear lastX/Y more often to avoid some spurious lines by introducing startRenderingPortion.
I also introduced closeRenderingPortion that is logically similar to closePath. Using this method I was able to remove duplication of points that you needed.
Generally I tried to avoid more-OOP-ish style as you do in your code (see renderSphere or clearPoint) but I changed Point3D constructor to support 3 modes: (x,y,z), point, empty.
Use more explicit marker value for empty lastX/Y var EMPTY_VALUE = Number.MIN_VALUE;. -1 is a possible value
Note also that there is a potential bug with your gray/white color selection that I didn't fix. I assume your color should reflect "invisible" lines and simple logic of Z > 0 vs Z < 0 doesn't solve this issue properly. Actually single line might be visible only partially if it is obscured by other things in the scene.

Js - calculate distance from point mapped to colour forms square?

I am working on a project where I visualise the effect of a magnetic dipole, on a range of vectors, I'm just testing with one pole at the moment and something doesn't work but I don't know why.
the force that a vector receives is mapped onto a color to check if I did it right, these are my results:
so this is the canvas I'm working with
and when I lower the size of each vector and increase the density you can see this forms diamonds rather than a circular pattern.
Does anybody know why this is or what could be causing it?
code below here:
function calcForce(magnet, vector){
return 1/distance(magnet.x,magnet.y,vector.centerx,vector.centery) * magnet.force;
}
function distance(cx, cy, ex, ey){
var dy = Math.abs(ey - cy);
var dx = Math.abs(ex - cx);
return Math.sqrt((dx^2) + (dy^2));
}
function mapRainbow(value) {
return 'hsl(' + value + ',100%,50%)';
}
function map_range(value, low1, high1, low2, high2) {
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
}
function mapForce(force){
return map_range(force,10,1000,20,40);
}
function drawStroke(stroke){
ctx.beginPath();
ctx.moveTo(stroke.x1,stroke.y1);
ctx.lineTo(stroke.x2,stroke.y2);
stroke.color = mapRainbow(stroke.force);
ctx.strokeStyle = stroke.color;
ctx.stroke();
ctx.closePath();
}
*this is not all the code by far but I think this is enough, need to see more? just ask.
Use distance to generate gradient:
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
document.body.appendChild(canvas);
function distance(x1, y1, x2, y2) {
return Math.sqrt((x2 -= x1) * x2 + (y2 -= y1) * y2);
}
function angleBetweenPoints(x1, y1, x2, y2) {
return (Math.atan2(x2 - x1, y2 - y1) + 2 * Math.PI);
}
var center = { x: 250, y: 250 };
var vectorLength = 15;
function max(v, m) {
if (v > m) {
return m;
}
return v;
}
function draw() {
if (Math.random() > 0.5) {
center.x += (Math.random() - 0.5) * 10;
}
else {
center.y += (Math.random() - 0.5) * 10;
}
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var xIndex = 0; xIndex < canvas.width; xIndex += vectorLength) {
for (var yIndex = 0; yIndex < canvas.height; yIndex += vectorLength) {
var x = xIndex - (Math.random() * vectorLength * 0.0);
var y = yIndex - (Math.random() * vectorLength * 0.0);
var angle = angleBetweenPoints(center.x, center.y, x, y);
var dist = distance(x, y, center.x, center.y);
ctx.fillStyle = "rgb(" + Math.floor(max(dist, 255)) + "," + Math.floor((255 - max(dist, 255))) + ",0)";
ctx.translate((x + vectorLength * 0.5), (y + vectorLength * 0.5));
ctx.rotate(-angle);
ctx.fillRect(0 - vectorLength * 0.5, 0 - vectorLength * 0.5, vectorLength * 0.25, vectorLength * 0.75);
ctx.rotate(angle);
ctx.translate(0 - (x + vectorLength * 0.5), 0 - (y + vectorLength * 0.5));
}
}
ctx.fillRect(center.x + vectorLength / 2, center.y + vectorLength / 2, vectorLength, vectorLength);
requestAnimationFrame(function () {
setTimeout(draw, 1000 / 60);
});
}
draw();

How To Get Value Of Canvas From A Wheel

How To Get The Value Of A Canvas . I have wheel which is rotating on mouse over the wheel stops now i want to echo out the value on which it was stopped. It is printing the whole array . Not the one on which the wheel stop.
$("#canvas").mouseover(function(){
backup= ctx;
alert(myData);
ctx = null;
});
this is the fiddle: https://jsfiddle.net/z61n9ccx/3/
Here is the full code:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var PI2 = Math.PI * 2;
var myData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var cx = 150;
var cy = 150;
var radius = 150;
var wheel = document.createElement('canvas');
var wheelCtx = wheel.getContext('2d');
var indicator = document.createElement('canvas');
var indicatorCtx = indicator.getContext('2d');
var angle = PI2 - PI2 / 4;
var myColor = [];
for (var i = 0; i < myData.length; i++) {
myColor.push(randomColor());
}
makeWheel();
makeIndicator();
requestAnimationFrame(animate);
function makeWheel() {
wheel.width = wheel.height = radius * 2 + 2;
wheelCtx.lineWidth = 1;
wheelCtx.font = '40px Pacifico, cursive';
wheelCtx.textAlign = 'center';
wheelCtx.textBaseline = 'middle';
var cx = wheel.width / 2;
var cy = wheel.height / 2;
var sweepAngle = PI2 / myData.length;
var startAngle = 0;
for (var i = 0; i < myData.length; i++) {
// calc ending angle based on starting angle
var endAngle = startAngle + sweepAngle;
// draw the wedge
wheelCtx.beginPath();
wheelCtx.moveTo(cx, cy);
wheelCtx.arc(cx, cy, radius, startAngle, endAngle, false);
wheelCtx.closePath();
wheelCtx.fillStyle = myColor[i];
wheelCtx.strokeStyle = 'black';
wheelCtx.fill();
wheelCtx.stroke();
// draw the label
var midAngle = startAngle + (endAngle - startAngle) / 2;
var labelRadius = radius * .85;
var x = cx + (labelRadius) * Math.cos(midAngle);
var y = cy + (labelRadius) * Math.sin(midAngle);
wheelCtx.fillStyle = 'gold';
wheelCtx.fillText(myData[i], x, y);
wheelCtx.strokeText(myData[i], x, y);
// increment angle
startAngle += sweepAngle;
}
}
function makeIndicator() {
indicator.width = indicator.height = radius + radius / 10;
indicatorCtx.font = '40px Georgia';
indicatorCtx.textAlign = 'center';
indicatorCtx.textBaseline = 'middle';
indicatorCtx.fillStyle = 'skyblue';
indicatorCtx.strokeStyle = 'blue';
indicatorCtx.lineWidth = 1;
var cx = indicator.width / 2;
var cy = indicator.height / 2;
indicatorCtx.beginPath();
indicatorCtx.moveTo(cx - radius / 8, cy);
indicatorCtx.lineTo(cx, cy - indicator.height / 2);
indicatorCtx.lineTo(cx + radius / 8, cy);
indicatorCtx.closePath();
indicatorCtx.fillStyle = 'skyblue'
indicatorCtx.fill();
indicatorCtx.stroke();
indicatorCtx.beginPath();
indicatorCtx.arc(cx, cy, radius / 3, 0, PI2);
indicatorCtx.closePath();
indicatorCtx.fill();
indicatorCtx.stroke();
indicatorCtx.fillStyle = 'blue';
indicatorCtx.fillText('Prizes', cx, cy);
}
function animate(time) {
ctx.clearRect(0, 0, cw, ch);
ctx.translate(cw / 2, ch / 2);
ctx.rotate(angle);
ctx.drawImage(wheel, -wheel.width / 2, -wheel.height / 2);
ctx.rotate(-angle);
ctx.translate(-cw / 2, -ch / 2);
ctx.drawImage(indicator, cw / 2 - indicator.width / 2, ch / 2 - indicator.height / 2)
angle += PI2 / 360;
requestAnimationFrame(animate);
}
function randomColor() {
return ('#' + Math.floor(Math.random() * 16777215).toString(16));
}
var backup = null;
$("#canvas").mouseover(function() {
backup = ctx;
alert(myData);
ctx = null;
});
$("#canvas").mouseout(function() {
// backup= ctx;
ctx = backup;
animate();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas id="canvas" width="600" height="600" style="background-color:#ffff">
</canvas>
I added a counter, and then use that as a index: https://jsfiddle.net/Twisty/L6nws9yz/2/
HTML
<canvas id="canvas" width="310" height="310" style="background-color:#ffff">
</canvas>
<div id="counterBox">
<label>Counter:</label>
<span></span>
</div>
<div id="countBox">
<label>Index:</label>
<span></span>
</div>
JS
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var PI2 = Math.PI * 2;
var myData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var cx = 150;
var cy = 150;
var radius = 150;
var wheel = document.createElement('canvas');
var wheelCtx = wheel.getContext('2d');
var indicator = document.createElement('canvas');
var indicatorCtx = indicator.getContext('2d');
var currentSelection = 12;
var counter = 360;
var angle = PI2 - PI2 / 4;
var myColor = [];
for (var i = 0; i < myData.length; i++) {
myColor.push(randomColor());
}
makeWheel();
makeIndicator();
requestAnimationFrame(animate);
function makeWheel() {
wheel.width = wheel.height = radius * 2 + 2;
wheelCtx.lineWidth = 1;
wheelCtx.font = '40px Pacifico, cursive';
wheelCtx.textAlign = 'center';
wheelCtx.textBaseline = 'middle';
var cx = wheel.width / 2;
var cy = wheel.height / 2;
var sweepAngle = PI2 / myData.length;
var startAngle = 0;
for (var i = 0; i < myData.length; i++) {
// calc ending angle based on starting angle
var endAngle = startAngle + sweepAngle;
// draw the wedge
wheelCtx.beginPath();
wheelCtx.moveTo(cx, cy);
wheelCtx.arc(cx, cy, radius, startAngle, endAngle, false);
wheelCtx.closePath();
wheelCtx.fillStyle = myColor[i];
wheelCtx.strokeStyle = 'black';
wheelCtx.fill();
wheelCtx.stroke();
// draw the label
var midAngle = startAngle + (endAngle - startAngle) / 2;
var labelRadius = radius * .85;
var x = cx + (labelRadius) * Math.cos(midAngle);
var y = cy + (labelRadius) * Math.sin(midAngle);
wheelCtx.fillStyle = 'gold';
wheelCtx.fillText(myData[i], x, y);
wheelCtx.strokeText(myData[i], x, y);
// increment angle
startAngle += sweepAngle;
}
}
function makeIndicator() {
indicator.width = indicator.height = radius + radius / 10;
indicatorCtx.font = '40px Georgia';
indicatorCtx.textAlign = 'center';
indicatorCtx.textBaseline = 'middle';
indicatorCtx.fillStyle = 'skyblue';
indicatorCtx.strokeStyle = 'blue';
indicatorCtx.lineWidth = 1;
var cx = indicator.width / 2;
var cy = indicator.height / 2;
indicatorCtx.beginPath();
indicatorCtx.moveTo(cx - radius / 8, cy);
indicatorCtx.lineTo(cx, cy - indicator.height / 2);
indicatorCtx.lineTo(cx + radius / 8, cy);
indicatorCtx.closePath();
indicatorCtx.fillStyle = 'skyblue'
indicatorCtx.fill();
indicatorCtx.stroke();
indicatorCtx.beginPath();
indicatorCtx.arc(cx, cy, radius / 3, 0, PI2);
indicatorCtx.closePath();
indicatorCtx.fill();
indicatorCtx.stroke();
indicatorCtx.fillStyle = 'blue';
indicatorCtx.fillText('Prizes', cx, cy);
}
var lastloop = new Date;
var thisloop = new Date;
var fps = 0;
function animate(time) {
ctx.clearRect(0, 0, cw, ch);
ctx.translate(cw / 2, ch / 2);
ctx.rotate(angle);
ctx.drawImage(wheel, -wheel.width / 2, -wheel.height / 2);
ctx.rotate(-angle);
ctx.translate(-cw / 2, -ch / 2);
ctx.drawImage(indicator, cw / 2 - indicator.width / 2, ch / 2 - indicator.height / 2)
angle += PI2 / 360;
thisloop = new Date;
fps = 1000 / (thisloop - lastloop);
lastloop = thisloop;
counter--;
if (counter < 1) {
counter = 360;
}
$("#counterBox span").html(counter);
var index = counter / 30;
$("#countBox span").html(Math.round(index));
//$("#fpsBox span").html(fps);
requestAnimationFrame(animate);
}
function randomColor() {
return ('#' + Math.floor(Math.random() * 16777215).toString(16));
}
var backup = null;
$("#canvas").mouseover(function() {
backup = ctx;
alert(myData[Math.round(counter / 30)-1]);
ctx = null;
});
$("#canvas").mouseout(function() {
// backup= ctx;
ctx = backup;
animate();
});
Counter is set to 360 and then each frame decreases it. Take that and divide by 30 (360 / 12), and you can count each wedge. I round up and now I have 0 - 11 count.
Update
I moved the Index into a global space. To make it more precise, I used the % operator like so:
counter--;
if (counter == 0) {
counter = 360;
}
$("#counterBox span").html(counter);
if (counter % 30 === 0) {
index--;
}
$("#countBox span").html(Math.round(index));
if (index === 0) {
index = 12;
}
When you mouse over, you get the selection:
$("#canvas").mouseover(function() {
backup = ctx;
alert(index);
ctx = null;
});
I wrapped everything in an IIFE so that there aren't any global variables.
Updated Example
It's important to note that the angle calculation is:
angle = degree * Math.PI / 180;
With that being said, you can calculate the current degree and normalize it using:
(angle * (180 / Math.PI)) % 360
I added a function called getValue which takes an angle parameter:
function getValue(angle) {
var degree = (angle * (180 / Math.PI)) % 360,
offsetIndex = (Math.floor(degree / sweepDegree) + offset) % myData.length,
normalizedIndex = Math.abs(offsetIndex - (myData.length - 1));
return myData[normalizedIndex];
}
It essentially calculates the current degree, normalizes it taking into account what the initial degree was when the animation was initialized (which is the offset). Then it divides the degree by the sweep degree, which is 30 in this case since there are 12 items (i.e., 360/12 === 30) and rounds down.
var sweepDegree = 360 / myData.length;
var offset = (360 - (angle * (180 / Math.PI)) % 360) / sweepDegree;
This should work for a varying number of array items. In other words, nothing is hardcoded for a set length of 12 items (like in your case), so it should work for any given number of items.
Then you can simply use the getValue function in the mouseover event listener:
Updated Example
$("#canvas").mouseover(function() {
// ...
alert(getValue(angle));
});
(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var PI2 = Math.PI * 2;
var myData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var cx = 150;
var cy = 150;
var radius = 150;
var wheel = document.createElement('canvas');
var wheelCtx = wheel.getContext('2d');
var indicator = document.createElement('canvas');
var indicatorCtx = indicator.getContext('2d');
var angle = PI2 - PI2 / 4;
var sweepDegree = 360 / myData.length;
var offset = (360 - (angle * (180 / Math.PI)) % 360) / sweepDegree;
var myColor = [];
for (var i = 0; i < myData.length; i++) {
myColor.push(randomColor());
}
makeWheel();
makeIndicator();
requestAnimationFrame(animate);
function makeWheel() {
wheel.width = wheel.height = radius * 2 + 2;
wheelCtx.lineWidth = 1;
wheelCtx.font = '40px Pacifico, cursive';
wheelCtx.textAlign = 'center';
wheelCtx.textBaseline = 'middle';
var cx = wheel.width / 2;
var cy = wheel.height / 2;
var sweepAngle = PI2 / myData.length;
var startAngle = 0;
for (var i = 0; i < myData.length; i++) {
// calc ending angle based on starting angle
var endAngle = startAngle + sweepAngle;
// draw the wedge
wheelCtx.beginPath();
wheelCtx.moveTo(cx, cy);
wheelCtx.arc(cx, cy, radius, startAngle, endAngle, false);
wheelCtx.closePath();
wheelCtx.fillStyle = myColor[i];
wheelCtx.strokeStyle = 'black';
wheelCtx.fill();
wheelCtx.stroke();
// draw the label
var midAngle = startAngle + (endAngle - startAngle) / 2;
var labelRadius = radius * .85;
var x = cx + (labelRadius) * Math.cos(midAngle);
var y = cy + (labelRadius) * Math.sin(midAngle);
wheelCtx.fillStyle = 'gold';
wheelCtx.fillText(myData[i], x, y);
wheelCtx.strokeText(myData[i], x, y);
// increment angle
startAngle += sweepAngle;
}
}
function makeIndicator() {
indicator.width = indicator.height = radius + radius / 10;
indicatorCtx.font = '40px Georgia';
indicatorCtx.textAlign = 'center';
indicatorCtx.textBaseline = 'middle';
indicatorCtx.fillStyle = 'skyblue';
indicatorCtx.strokeStyle = 'blue';
indicatorCtx.lineWidth = 1;
var cx = indicator.width / 2;
var cy = indicator.height / 2;
indicatorCtx.beginPath();
indicatorCtx.moveTo(cx - radius / 8, cy);
indicatorCtx.lineTo(cx, cy - indicator.height / 2);
indicatorCtx.lineTo(cx + radius / 8, cy);
indicatorCtx.closePath();
indicatorCtx.fillStyle = 'skyblue'
indicatorCtx.fill();
indicatorCtx.stroke();
indicatorCtx.beginPath();
indicatorCtx.arc(cx, cy, radius / 3, 0, PI2);
indicatorCtx.closePath();
indicatorCtx.fill();
indicatorCtx.stroke();
indicatorCtx.fillStyle = 'blue';
indicatorCtx.fillText('Prizes', cx, cy);
}
function animate(time) {
if (ctx === null) {
return
}
ctx.clearRect(0, 0, cw, ch);
ctx.translate(cw / 2, ch / 2);
ctx.rotate(angle);
ctx.drawImage(wheel, -wheel.width / 2, -wheel.height / 2);
ctx.rotate(-angle);
ctx.translate(-cw / 2, -ch / 2);
ctx.drawImage(indicator, cw / 2 - indicator.width / 2, ch / 2 - indicator.height / 2)
angle += PI2 / 360;
requestAnimationFrame(animate);
}
function randomColor() {
return ('#' + Math.floor(Math.random() * 16777215).toString(16));
}
var backup = null;
$("#canvas").mouseover(function() {
backup = ctx;
ctx = null;
alert(getValue(angle));
});
$("#canvas").mouseout(function() {
ctx = backup;
animate();
});
function getValue(angle) {
var degree = (angle * (180 / Math.PI)) % 360,
offsetIndex = (Math.floor(degree / sweepDegree) + offset) % myData.length,
normalizedIndex = Math.abs(offsetIndex - (myData.length - 1));
return myData[normalizedIndex];
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas id="canvas" width="600" height="600" style="background-color:#ffff">
</canvas>

Categories

Resources