I have created a circle in which I can choose two points along the circumference of of circle.
I want to fill the portion between those two points.
Demo
If you see the demo, I want to fill the angle between two points.
JS:
(function (Raphael) {
Raphael.colorwheel = function (x, y, size, initcolor, element) {
return new ColorWheel(x, y, size, initcolor, element);
};
var pi = Math.PI,
doc = document,
win = window,
ColorWheel = function (x, y, size, initcolor, element) {
size = size || 200;
var w3 = 3 * size / 200,
w1 = size / 200,
fi = 1.6180339887,
segments = 3,//pi * size / 50,
size20 = size / 20,
size2 = size / 2,
padding = 2 * size / 200,
t = this;
var H = 1, S = 1, B = 1, s = size - (size20 * 4);
var r = element ? Raphael(element, size, size) : Raphael(x, y, size, size),
xy = s / 6 + size20 * 2 + padding,
wh = s * 2 / 3 - padding * 2;
w1 < 1 && (w1 = 1);
w3 < 1 && (w3 = 1);
// ring drawing
var a = pi / 2 - pi * 2 / segments * 1.3,
R = size2 - padding,
R2 = size2 - padding - size20 * 2,
path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", R2 * Math.cos(a) + R + padding, R - R2 * Math.sin(a) + padding, "A", R2, R2, 0, 0, 0, size2, padding + size20 * 2, "z"].join();
for (var i = 0; i < segments; i++) {
r.path(path).attr({
stroke: "none",
fill: "#8fd117",
transform: "r" + [(360 / segments) * i, size2, size2]
});
}
r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0", "M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
"stroke-width": w3,
stroke: "#fff"
});
t.startCursor = r.set();
var h = size20 * 2 + 2;
t.startCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
stroke: "#00A0C6",
opacity: .5,
"stroke-width": w3
}));
t.startCursor.push(t.startCursor[0].clone().attr({
stroke: "#00A0C6",
opacity: 1,
"stroke-width": w1
}));
t.endCursor = r.set();
var h = size20 * 2 + 2;
t.endCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
stroke: "#F96E5B",
opacity: .5,
"stroke-width": w3
}));
t.endCursor.push(t.endCursor[0].clone().attr({
stroke: "#F96E5B",
opacity: 1,
"stroke-width": w1
}));
t.ring = r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
fill: "#000",
opacity: 0,
stroke: "none"
});
t.H = t.S = t.B = 1;
t.raphael = r;
t.size2 = size2;
t.wh = wh;
t.x = x;
t.xy = xy;
t.y = y;
t.endCursor.attr({transform: "r" + [50, t.size2, t.size2]});
// events
t.ring.drag(function (dx, dy, x, y) {
t.docOnMove(dx, dy, x, y);
}, function (x, y) {
// Rotate on click
t.setH(x - t.x - t.size2, y - t.y - t.size2);
}, function () {
});
},
proto = ColorWheel.prototype;
proto.setH = function (x, y) {
var d = Raphael.angle(x, y, 0, 0);
this.H = (d + 90) / 360;
var a = 0;
if(d > 270) {
d = d - 270;
}
else {
d = d + 90;
}
var m = Math.abs(d - this.startCursor[0]._.deg);
var n = Math.abs(d - this.endCursor[0]._.deg);
if(m > 180) {
m = 360 - m ;
}
if(n > 180) {
n = 360 - n;
}
if( m <= n) {
this.startCursor.attr({transform: "r" + [d, this.size2, this.size2]});
}
else {
this.endCursor.attr({transform: "r" + [d, this.size2, this.size2]});
}
m = this.startCursor[0]._.deg ;
n = this.endCursor[0]._.deg;
if(m > 360) {
m = m - 360;
}
if( n > 360 ) {
n = n - 360;
}
var diff = m > n ? m - n : n - m;
this.onchange(m,n,diff);
};
proto.docOnMove = function (dx, dy, x, y) {
this.setH(x - this.x - this.size2, y - this.y - this.size2);
};
})(window.Raphael);
window.onload = function () {
var cp2 = Raphael.colorwheel(60, 20, 200, "#eee");
var X = document.getElementById('x');
var Y = document.getElementById('y');
var angle = document.getElementById('angle');
cp2.onchange = function (x, y, ang) {
X.innerHTML = Math.round(x * 100) / 100;
Y.innerHTML = Math.round(y * 100) / 100;
angle.innerHTML = Math.round(ang * 100) / 100;
}
};
HTML:
<div id="wrapper">X : <span id="x">0</span>
<br>Y: <span id="y">50</span>
<br>Angle: <span id="angle">50</span>
</div>
CSS:
body {
background: #e6e6e6;
}
#wrapper {
position: absolute;
top: 240px;
left: 100px;
}
UPDATE:
With Chris's help,
I have got some success.
See Demo
Bugs :
1. If you start green first, red breaks,
2. If you start red first and makes angle of greater than 180 degree and when green reduces that below 180 degree, it breaks again.
UPDATE 2
DEMO
BUGS:
1. If you start red first and makes angle of greater than 180 degree and when green reduces that below 180 degree, it breaks again.
2. Sometimes arcs in opposite direction.
Cool project. You just need to add an elliptical arc to the color wheel and redraw the path on the onchange event.
I got you half the way here: It works if you move the orange cursor, completely breaks if you move the blue cursor.
To start:
t.x0 = t.startCursor[0].attr("x") + t.startCursor[0].attr("width") / 2;
t.y0 = t.startCursor[0].attr("y") + t.startCursor[0].attr("height") / 2;
t.R1 = (R2 + R) / 2;
t.x1 = t.x0 + t.R1 * Math.sin(50 * Math.PI / 180);
t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(50 * Math.PI / 180);
t.arc = r.path("M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " 50 0,1 " + t.x1 + "," + t.y1)
.attr({
stroke: "#009900",
"stroke-width": 10
});
On update:
if (n > 180) {
flag = 1;
}
var diff = m > n ? m - n : n - m;
t.x0 = t.x0 + t.R1 * Math.sin(m * Math.PI / 180);
t.y0 = t.y0 + t.R1 - t.R1 * Math.cos(m * Math.PI / 180);
t.x1 = t.x0 + t.R1 * Math.sin(diff * Math.PI / 180);
t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(diff * Math.PI / 180);
t.arc = t.arc.attr("path", "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " " + diff + " " + flag + ",1 " + t.x1 + "," + t.y1);
jsfiddle
Should be able to take it from here.
UPDATE, May 8:
You can fix your first problem by changing the flag on the diff, not on the second angle:
if (diff > 180) {
flag = 1;
}
The event that's triggering the second problem is the second angle (the red handle) passing the 0-degree mark. The easiest way to catch this is just to add 360 to the angle IF it's less than the first angle:
var m = this.startCursor[0]._.deg ;
var n = this.endCursor[0]._.deg;
var t = this;
var flag = 0;
var sweep = 1;
var path = "";
if (n < m) {
m += 360;
}
var diff = Math.abs(m - n);
if (diff > 180) {
flag = 1;
}
Here's the fiddle
Note: You were catching situations where (n > 360) and (m > 360), but this doesn't appear necessary -- the angles arrive at this point in the code already set below 360, at least in Chrome.
Here's working solution:
Demo
(function (Raphael) {
Raphael.colorwheel = function (x, y, size, initcolor, element) {
return new ColorWheel(x, y, size, initcolor, element);
};
var pi = Math.PI,
doc = document,
win = window,
ColorWheel = function (x, y, size, initcolor, element) {
size = size || 200;
var w3 = 3 * size / 200,
w1 = size / 200,
fi = 1.6180339887,
segments = 3,//pi * size / 50,
size20 = size / 20,
size2 = size / 2,
padding = 2 * size / 200,
t = this;
var H = 1, S = 1, B = 1, s = size - (size20 * 4);
var r = element ? Raphael(element, size, size) : Raphael(x, y, size, size),
xy = s / 6 + size20 * 2 + padding,
wh = s * 2 / 3 - padding * 2;
w1 < 1 && (w1 = 1);
w3 < 1 && (w3 = 1);
// ring drawing
var a = pi / 2 - pi * 2 / segments * 1.3,
R = size2 - padding,
R2 = size2 - padding - size20 * 2,
path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", R2 * Math.cos(a) + R + padding, R - R2 * Math.sin(a) + padding, "A", R2, R2, 0, 0, 0, size2, padding + size20 * 2, "z"].join();
for (var i = 0; i < segments; i++) {
r.path(path).attr({
stroke: "none",
fill: "#8fd117",
transform: "r" + [(360 / segments) * i, size2, size2]
});
}
r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0", "M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
"stroke-width": w3,
stroke: "#fff"
});
t.startCursor = r.set();
var h = size20 * 2 + 2;
t.startCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
stroke: "#00A0C6",
opacity: 1,
"stroke-width": w3
}));
t.startCursor.push(t.startCursor[0].clone().attr({
stroke: "#00A0C6",
fill : "#8fd117",
opacity: 1,
"stroke-width": w1
}));
t.endCursor = r.set();
var h = size20 * 2 + 2;
t.endCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
stroke: "#F96E5B",
opacity: 1,
"stroke-width": w3,
}));
t.endCursor.push(t.endCursor[0].clone().attr({
stroke: "#F96E5B",
fill : "#8fd117",
opacity: 1,
"stroke-width": w1
}));
t.ring = r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
fill: "#000",
opacity: 0,
stroke: "none"
});
t.H = t.S = t.B = 1;
t.raphael = r;
t.size2 = size2;
t.wh = wh;
t.x = x;
t.xy = xy;
t.y = y;
t.endCursor.attr({transform: "r" + [50, t.size2, t.size2]});
t.x0 = t.startCursor[0].attr("x") + t.startCursor[0].attr("width") / 2;
t.y0 = t.startCursor[0].attr("y") + t.startCursor[0].attr("height") / 2;
t.initX0 = t.x0;
t.initY0 = t.y0;
t.R1 = (R2 + R) / 2;
t.x1 = t.x0 + t.R1 * Math.sin(50 * Math.PI / 180);
t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(50 * Math.PI / 180);
t.initX1 = t.x1;
t.initY1 = t.y1;
var path = "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " 50 0,1 " + t.x1 + "," + t.y1;
t.arc = r.path(path)
.attr({
stroke: "#009900",
"stroke-width": 10
});
t.startCursor.drag(function (dx, dy, x, y) {
t.docOnMove(dx, dy, x, y,'startCursor');
}, function (x, y) {
t.setH(x - t.x - t.size2, y - t.y - t.size2,'startCursor');
}, function () {
});
t.endCursor.drag(function (dx, dy, x, y) {
t.docOnMove(dx, dy, x, y,'endCursor');
}, function (x, y) {
t.setH(x - t.x - t.size2, y - t.y - t.size2,'endCursor');
}, function () {
});
t.startCursor.toFront();
t.endCursor.toFront();
},
proto = ColorWheel.prototype;
proto.setH = function (x, y,cursor) {
var d = Raphael.angle(x, y, 0, 0);
if(d > 270) {
d = d - 270;
}
else {
d = d + 90;
}
if((cursor === 'startCursor' && d > this.endCursor[0]._.deg) || (cursor === 'endCursor' && d <= this.startCursor[0]._.deg)) {
return;
}
if(cursor === 'startCursor') {
this.startCursor.attr({transform: "r" + [d, this.size2, this.size2]});
}
else {
this.endCursor.attr({transform: "r" + [d, this.size2, this.size2]});
}
var m = this.startCursor[0]._.deg ;
var n = this.endCursor[0]._.deg;
var t = this;
var flag = 0;
if(m > 360) {
m = m - 360;
flag = 1;
}
if( n > 360 ) {
n = n - 360;
}
var diff = Math.abs(m - n);
if (diff > 180) {
flag = 1;
}
var path = "";
var sweep = 1;
if(cursor === 'endCursor') {
t.x1 = t.initX0 + t.R1 * Math.sin(n * Math.PI / 180);
t.y1 = t.initY0 + t.R1 - t.R1 * Math.cos(n * Math.PI / 180);
}
else {
t.x0 = t.initX0 + t.R1 * Math.sin(m * Math.PI / 180);
t.y0 = t.initY0 + t.R1 - t.R1 * Math.cos(m * Math.PI / 180);
}
console.log(m,t.x0,t.y0,t.x1,t.y1);
path = "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " " + diff + " " + flag + "," + sweep + " " + t.x1 + "," + t.y1;
t.arc = t.arc.attr("path", path );
this.onchange(m,n,diff);
};
proto.docOnMove = function (dx, dy, x, y,cursor) {
this.setH(x - this.x - this.size2, y - this.y - this.size2,cursor);
};
})(window.Raphael);
window.onload = function () {
var cp2 = Raphael.colorwheel(60, 20, 200, "#eee");
var X = document.getElementById('x');
var Y = document.getElementById('y');
var angle = document.getElementById('angle');
cp2.onchange = function (x, y, ang) {
X.innerHTML = Math.round(x * 100) / 100;
Y.innerHTML = Math.round(y * 100) / 100;
angle.innerHTML = Math.round(ang * 100) / 100;
}
};
Related
I created a simple perlin noise generator using p5.js, which is based on
this link
. For the most part, I got 80% of the algorithm working. The only issue is that there is defined discontinuities aligned with each gradient vector. the code for this project is shown below.
// noprotect
var screen_width = 400;
var screen_height = 400;
var res = 2;
var gvecs = {};
function setup() {
createCanvas(screen_width, screen_height);
initialize_gvecs();
draw_perlin_noise();
}
function draw() {
console.log(gvecs);
noLoop();
}
function initialize_gvecs() {
for (var y = 0; y <= res; y++) {
for (var x = 0; x <= res; x++) {
let theta = Math.random() * 2 * Math.PI;
gvecs[[x, y]] = {
x: Math.cos(theta),
y: Math.sin(theta)
};
}
}
}
function draw_perlin_noise() {
loadPixels();
for (var y = 0; y < screen_height; y++) {
for (var x = 0; x < screen_width; x++) {
var world_coordx = map(x, 0, screen_width, 0, res);
var world_coordy = map(y, 0, screen_height, 0, res);
var top_L_x = Math.floor(world_coordx);
var top_L_y = Math.floor(world_coordy);
var top_R_x = top_L_x + 1;
var top_R_y = top_L_y;
var bottom_L_x = top_L_x;
var bottom_L_y = top_L_y + 1;
var bottom_R_x = top_L_x + 1;
var bottom_R_y = top_L_y + 1;
var top_L_g = gvecs[[top_L_x, top_L_y]];
var top_R_g = gvecs[[top_R_x, top_R_y]];
var bottom_L_g = gvecs[[bottom_L_x, bottom_L_y]];
var bottom_R_g = gvecs[[bottom_R_x, bottom_R_y]];
var btw_top_L = {
x: world_coordx - top_L_x,
y: world_coordy - top_L_y
};
var btw_top_R = {
x: world_coordx - top_R_x,
y: world_coordy - top_R_y
};
var btw_bottom_L = {
x: world_coordx - bottom_L_x,
y: world_coordy - bottom_L_y
};
var btw_bottom_R = {
x: world_coordx - bottom_R_x,
y: world_coordy - bottom_R_y
};
var v = top_L_g.x * btw_top_L.x + top_L_g.y * btw_top_L.y;
var u = top_R_g.x * btw_top_R.x + top_R_g.y * btw_top_R.y;
var s = bottom_L_g.x * btw_bottom_L.x + bottom_L_g.y * btw_bottom_L.y;
var t = bottom_R_g.x * btw_bottom_R.x + bottom_R_g.y * btw_bottom_R.y;
var Sx = ease_curve(world_coordx - top_L_x);
var a = s + Sx * (t - s);
var b = u + Sx * (v - u);
var Sy = ease_curve(world_coordy - top_L_y);
var final_val = a + Sy * (b - a);
pixels[(x + y * screen_width) * 4] = map(final_val, -1, 1, 0, 255);
pixels[(x + y * screen_width) * 4 + 1] = map(final_val, -1, 1, 0, 255);
pixels[(x + y * screen_width) * 4 + 2] = map(final_val, -1, 1, 0, 255);
pixels[(x + y * screen_width) * 4 + 3] = 255;
}
}
updatePixels();
}
function ease_curve(x) {
return 6 * x ** 5 - 15 * x ** 4 + 10 * x ** 3;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.5.0/lib/p5.js"></script>
an image of the issue I'm having is shown below.
I suspect that my issue has to do with the value between each gradient vector not properly using the adjacent gradient vectors, but I've tested and debugged this extensively and I cant find the issue. I've also tried several different ease_curve functions, but none of them seem to change anything. Any help would be greatly appreciated.
Bilinear interpolation might be twisted.
var Sx = ease_curve(world_coordx - top_L_x);
var a = s + Sx * (t - s);
//var b = u + Sx * (v - u);
var b = v + Sx * (u - v);
var Sy = ease_curve(world_coordy - top_L_y);
//var final_val = a + Sy * (b - a);
var final_val = b + Sy * (a - b);
I am developing a game in ionic 4 'spin the wheel'. so far I am able to spin the wheel which stops randomly on any of the sections. Can anyone help me with setting probability to sections so that users will have fewer chances to win? I would like to pre-define each section with some percentage value. for eg section with 0 will have 50% 4 will have 10% etc, and the greatest value will have a 1% chance to win. below code, I have tried.
SpinWheel.ts
export class SpinWheel {
#ViewChild('myCanvas', { static: false }) myCanvas: ElementRef;
colors = ["#2a8251", "#dc0927", "#414243", "#dc0927", "#2E2C75",
"#dc0927", "#414243", "#dc0927", "#2E2C75",
"#dc0927"];
restaraunts = ["0", "32", "15", "19",
"4", "21", "2", "25",
"17", "34"];
startAngle = 0;
arc = 2 * Math.PI / 10;
spinTimeout = null;
spinArcStart = 10;
spinTime = 0;
spinTimeTotal = 0;
ctx;
spinAngleStart;
constructor() {
}
ngOnInit() {
}
ngAfterViewInit(): void {
this.draw();
}
draw() {
this.drawRouletteWheel();
}
drawRouletteWheel() {
var canvas = document.getElementById("wheelcanvas");
var outsideRadius = 140;
var textRadius = 120;
var insideRadius = 25;
this.ctx = (<HTMLCanvasElement>this.myCanvas.nativeElement).getContext('2d');
this.ctx.clearRect(0, 0, 500, 500);
this.ctx.strokeStyle = "black";
this.ctx.lineWidth = 2;
this.ctx.font = 'bold 12px sans-serif';
for (var i = 0; i < 10; i++) {
var angle = this.startAngle + i * this.arc;
this.ctx.fillStyle = this.colors[i];
this.ctx.beginPath();
this.ctx.arc(150, 150, outsideRadius, angle, angle + this.arc, false);
this.ctx.arc(150, 150, insideRadius, angle + this.arc, angle, true);
this.ctx.stroke();
this.ctx.fill();
this.ctx.save();
this.ctx.shadowOffsetX = -1;
this.ctx.shadowOffsetY = -1;
this.ctx.shadowBlur = 0;
//this.ctx.shadowColor = "rgb(220,220,220)";
this.ctx.fillStyle = "white";
this.ctx.translate(150 + Math.cos(angle + this.arc / 2) * textRadius, 150 + Math.sin(angle + this.arc / 2) * textRadius);
this.ctx.rotate(angle + this.arc / 2 + Math.PI / 2);
var text = this.restaraunts[i];
this.ctx.fillText(text, -this.ctx.measureText(text).width / 2, 0);
this.ctx.restore();
}
//Arrow
this.ctx.fillStyle = "black";
this.ctx.beginPath();
this.ctx.moveTo(150 - 4, 150 - (outsideRadius + 5));
this.ctx.lineTo(150 + 4, 150 - (outsideRadius + 5));
this.ctx.lineTo(150 + 4, 150 - (outsideRadius - 5));
this.ctx.lineTo(150 + 9, 150 - (outsideRadius - 5));
this.ctx.lineTo(150 + 0, 150 - (outsideRadius - 13));
this.ctx.lineTo(150 - 9, 150 - (outsideRadius - 5));
this.ctx.lineTo(150 - 4, 150 - (outsideRadius - 5));
this.ctx.lineTo(150 - 4, 150 - (outsideRadius + 5));
this.ctx.fill();
}
spin() {
this.spinAngleStart = Math.random() * 10 + 10;
this.spinTime = 0;
this.spinTimeTotal = Math.random() * 3 + 4 * 1000;
this.rotateWheel();
}
rotateWheel() {
this.spinTime += 30;
if (this.spinTime >= this.spinTimeTotal) {
this.stopRotateWheel();
return;
}
var spinAngle = this.spinAngleStart - this.easeOut(this.spinTime, 0, this.spinAngleStart, this.spinTimeTotal);
this.startAngle += (spinAngle * Math.PI / 180);
this.drawRouletteWheel();
this.spinTimeout = setTimeout(() => {
this.rotateWheel();
}, 30);
}
stopRotateWheel() {
clearTimeout(this.spinTimeout);
var degrees = this.startAngle * 180 / Math.PI + 90;
var arcd = this.arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
this.ctx.save();
this.ctx.font = 'bold 30px sans-serif';
var text = this.restaraunts[index]
//this.ctx.fillText(text, 150 - this.ctx.measureText(text).width / 2, 150 + 10);
alert("You got:\n" + text);
this.ctx.restore();
}
// t: current time
// b: start value
// c: change in value
// d: duration
easeOut(t, b, c, d) {
return c * Math.sin(t/d * (Math.PI/2)) + b;
}
}
spin_wheel.html
<div class="wheel">
<canvas #myCanvas width="auto" height="300"></canvas>
<div class="icon-center"><img class="app-logo" (click)="spin()" src="../../assets/icon/favicon.png"/></div>
</div>
See attached image spin wheel screen
Thanks in advance.
I found this js spinner and I like how it has the counter at the end. It counts down from 15 seconds. I was wondering if it would be possible to make it so that if you land on geography for example it counts down 5 seconds and then redirects you to a separate website. And history would bring you to a different link and so on. Thanks
JavaScript
var colors = ["#ffff00" , "#1be11b", "#0000ff", "#7e7e7e", "#8a2be2", "#006400", "#2980B9", "#E74C3C"];
// NEED to pre load this data prior
var prize_descriptions = ["GENERAL", "GEOGRAPHY", "HISTORY", "ARTS", "SCIENCE", "SPORTS", "RELIGION", "MEDIA"];
var current_user_status = {};
var startAngle = 0;
var arc = Math.PI / 4;
var spinTimeout = null;
var spinArcStart = 10;
var spinTime = 0;
var spinTimeTotal = 0;
var current_user_status = null;
var spin_results = null;
var wheel;
var counter, tt;
function drawSpinnerWheel() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var outsideRadius = 200;
var textRadius = 160;
var insideRadius = 125;
wheel = canvas.getContext("2d");
wheel.clearRect(0, 0, 500, 500);
wheel.strokeStyle = "#ecf0f1";
wheel.lineWidth = 5;
wheel.font = '12px Helvetica, Arial';
for (var i = 0; i < 8; i++) {
var angle = startAngle + i * arc;
wheel.fillStyle = colors[i];
wheel.beginPath();
wheel.arc(250, 250, outsideRadius, angle, angle + arc, false);
wheel.arc(250, 250, insideRadius, angle + arc, angle, true);
wheel.stroke();
wheel.fill();
wheel.save();
wheel.shadowOffsetX = -1;
wheel.shadowOffsetY = -1;
wheel.shadowBlur = 0;
wheel.shadowColor = "rgb(220,220,220)";
wheel.fillStyle = "#ecf0f1";
wheel.translate(250 + Math.cos(angle + arc / 2) * textRadius, 250 + Math.sin(angle + arc / 2) * textRadius);
wheel.rotate(angle + arc / 2 + Math.PI / 2);
var text = prize_descriptions[i];
if (text === undefined) text = "Not this time!";
wheel.fillText(text, -wheel.measureText(text).width / 2, 0);
wheel.restore();
}
//Arrow
wheel.fillStyle = "#ecf0f1";
wheel.beginPath();
wheel.moveTo(250 - 4, 250 - (outsideRadius + 5));
wheel.lineTo(250 + 4, 250 - (outsideRadius + 5));
wheel.lineTo(250 + 4, 250 - (outsideRadius - 5));
wheel.lineTo(250 + 9, 250 - (outsideRadius - 5));
wheel.lineTo(250 + 0, 250 - (outsideRadius - 13));
wheel.lineTo(250 - 9, 250 - (outsideRadius - 5));
wheel.lineTo(250 - 4, 250 - (outsideRadius - 5));
wheel.lineTo(250 - 4, 250 - (outsideRadius + 5));
wheel.fill();
}
}
function spin() {
$("#spin").unbind('click');
$("#spin").attr("id", "nospin");
document.getElementById('timer').innerHTML = " ";
document.getElementById('category').innerHTML = " ";
spinMovement = Math.floor(Math.random() * 20) + prize_descriptions.length * 2;
spinAngleStart = 1 * 10 + spinMovement;
spinTime = 0;
spinTimeTotal = Math.floor(Math.random() * 4) * Math.floor(Math.random() * 6) + Math.floor(Math.random() * 8) * Math.floor(Math.random() * 2000) + 2000;
console.log(spinMovement + " - " + spinTimeTotal);
rotateWheel();
}
function rotateWheel() {
spinTime += 30;
if (spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawSpinnerWheel();
spinTimeout = setTimeout('rotateWheel()', 30);
}
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
wheel.save();
wheel.font = '30px "Homestead-Inline", Helvetica, Arial';
var text = prize_descriptions[index];
//wheel.fillText(text, 250 - wheel.measureText(text).width / 2, 250 + 10);
wheel.restore();
document.getElementById('timer').innerHTML = "15";
document.getElementById('category').innerHTML = "Your Category is: " + text;
counter = 15;
tt=setInterval(function(){startTime()},1000);
}
function easeOut(t, b, c, d) {
var ts = (t /= d) * t;
var tc = ts * t;
return b + c * (tc + -3 * ts + 3 * t);
}
drawSpinnerWheel();
function startTime() {
if(counter == 0) {
clearInterval(tt);
$("#nospin").attr("id", "spin");
$("#spin").bind('click', function(e) {
e.preventDefault();
spin();
});
} else {
counter--;
}
document.getElementById('timer').innerHTML = counter;
}
$("#spin").bind('click', function(e) {
e.preventDefault();
spin();
});
To see it in action click here
It seems that all your changes should be made in the function stopRotateWheel() and startTime()
When the function is called, a variable called text holds the result ("Geography" or "Science", etc.).
From that, we can perform conditions based on the value of text and determine the total time of countdown, plus the link when the countdown expires.
Something like this:
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
wheel.save();
wheel.font = '30px "Homestead-Inline", Helvetica, Arial';
var text = prize_descriptions[index];
//wheel.fillText(text, 250 - wheel.measureText(text).width / 2, 250 + 10);
wheel.restore();
document.getElementById('timer').innerHTML = "15";
document.getElementById('category').innerHTML = "Your Category is: " + text;
/*do an if else*/
if(text=="Geography")
{
counter = 5;
tt=setInterval(function(){startTime("www.geography.com")},1000);
/*countdown, and when timer expires... go to another link*/
}
else if (text=="Science")
{
//do the same as above :)
}
}
notice the code startTime("www.geography.com")? that's because we also modify function startTime to accept a parameter (in this case, the website) so that when the countdown is finished the webpage goes to that link :)
function startTime(gotoLink) {
if(counter == 0) {
/*go to that link */
window.location.replace(gotoLink)
} else {
counter--;
}
document.getElementById('timer').innerHTML = counter;
}
try it out!
I need to calculate the foot of a perpendicular line drawn from a point P to a line segment AB. I need coordinates of point C where PC is perpendicular drawn from point P to line AB.
I found few answers on SO here but the vector product process does not work for me.
Here is what I tried:
function nearestPointSegment(a, b, c) {
var t = nearestPointGreatCircle(a,b,c);
return t;
}
function nearestPointGreatCircle(a, b, c) {
var a_cartesian = normalize(Cesium.Cartesian3.fromDegrees(a.x,a.y))
var b_cartesian = normalize(Cesium.Cartesian3.fromDegrees(b.x,b.y))
var c_cartesian = normalize(Cesium.Cartesian3.fromDegrees(c.x,c.y))
var G = vectorProduct(a_cartesian, b_cartesian);
var F = vectorProduct(c_cartesian, G);
var t = vectorProduct(G, F);
t = multiplyByScalar(normalize(t), R);
return fromCartesianToDegrees(t);
}
function vectorProduct(a, b) {
var result = new Object();
result.x = a.y * b.z - a.z * b.y;
result.y = a.z * b.x - a.x * b.z;
result.z = a.x * b.y - a.y * b.x;
return result;
}
function normalize(t) {
var length = Math.sqrt((t.x * t.x) + (t.y * t.y) + (t.z * t.z));
var result = new Object();
result.x = t.x/length;
result.y = t.y/length;
result.z = t.z/length;
return result;
}
function multiplyByScalar(normalize, k) {
var result = new Object();
result.x = normalize.x * k;
result.y = normalize.y * k;
result.z = normalize.z * k;
return result;
}
function fromCartesianToDegrees(pos) {
var carto = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pos);
var lon = Cesium.Math.toDegrees(carto.longitude);
var lat = Cesium.Math.toDegrees(carto.latitude);
return [lon,lat];
}
What I am missing in this?
Here's a vector-based way:
function foot(A, B, P) {
const AB = {
x: B.x - A.x,
y: B.y - A.y
};
const k = ((P.x - A.x) * AB.x + (P.y - A.y) * AB.y) / (AB.x * AB.x + AB.y * AB.y);
return {
x: A.x + k * AB.x,
y: A.y + k * AB.y
};
}
const A = { x: 1, y: 1 };
const B = { x: 4, y: 5 };
const P = { x: 4.5, y: 3 };
const C = foot(A, B, P);
console.log(C);
// perpendicular?
const AB = {
x: B.x - A.x,
y: B.y - A.y
};
const PC = {
x: C.x - P.x,
y: C.y - P.y
};
console.log((AB.x * PC.x + AB.y * PC.y).toFixed(3));
Theory:
I start with the vector from A to B, A➞B. By multiplying this vector by a scalar k and adding it to point A I can get to any point C on the line AB.
I) C = A + k × A➞B
Next I need to establish the 90° angle, which means the dot product of A➞B and P➞C is zero.
II) A➞B · P➞C = 0
Now solve for k.
function closestPointOnLineSegment(pt, segA, segB) {
const A = pt.x - segA.x,
B = pt.y - segA.y,
C = segB.x - segA.x,
D = segB.y - segA.y
const segLenSq = C**2 + D**2
const t = (segLenSq != 0) ? (A*C + B*D) / segLenSq : -1
return (t<0) ? segA : (t>1) ? segB : {
x: segA.x + t * C,
y: segA.y + t * D
}
}
can.width = can.offsetWidth
can.height = can.offsetHeight
const ctx = can.getContext('2d')
const segA = {x:100,y:100},
segB = {x:400, y:200},
pt = {x:250, y:250}
visualize()
function visualize() {
ctx.clearRect(0, 0, can.width, can.height)
const t = Date.now()
pt.x = Math.cos(t/1000) * 150 + 250
pt.y = Math.sin(t/1000) * 100 + 150
segA.x = Math.cos(t / 2000) * 50 + 150
segA.y = Math.sin(t / 2500) * 50 + 50
segB.x = Math.cos(t / 3000) * 75 + 400
segB.y = Math.sin(t / 2700) * 75 + 100
line(segA, segB, 'gray', 2)
const closest = closestPointOnLineSegment(pt, segA, segB)
ctx.setLineDash([5, 8])
line(pt, closest, 'orange', 2)
ctx.setLineDash([])
dot(closest, 'rgba(255, 0, 0, 0.8)', 10)
dot(pt, 'blue', 7)
dot(segA, 'black', 7)
dot(segB, 'black', 7)
window.requestAnimationFrame(visualize)
}
function dot(p, color, w) {
ctx.fillStyle = color
ctx.fillRect(p.x - w/2, p.y - w/2, w, w)
}
function line(a, b, color, n) {
ctx.strokeStyle = color
ctx.lineWidth = n
ctx.beginPath()
ctx.moveTo(a.x, a.y)
ctx.lineTo(b.x, b.y)
ctx.stroke()
}
html, body { height:100%; min-height:100%; margin:0; padding:0; overflow:hidden }
canvas { width:100%; height:100%; background:#ddd }
<canvas id="can"></canvas>
I'd like to be able to cater for missing graph points by breaking the graph line where data is missing.
I think I can achieve this by specifying a fixed range?
For example, if the x-axis should contain every hour in the day and the y-axis contains percentage values, I want the x-axis to always have a full range of 24 hour values.
However, the code is taking the set of times for which data exists and is using them as the range for the x-axis. If no data was present for times between 4 - 11 then the graph shows a straight line between 4 and 11, 5,6,7,8,9 and 10 don't appear on the x-axis and this is not what I want.
Here is the code...
Raphael.fn.drawGrid = function (x, y, w, h, wv, hv, color) {
color = color || "#000";
var path = ["M", Math.round(x) + .5, Math.round(y) + .5, "L", Math.round(x + w) + .5, Math.round(y) + .5, Math.round(x + w) + .5, Math.round(y + h) + .5, Math.round(x) + .5, Math.round(y + h) + .5, Math.round(x) + .5, Math.round(y) + .5],
rowHeight = h / hv,
columnWidth = w / wv;
for (var i = 1; i < hv; i++) {
path = path.concat(["M", Math.round(x) + .5, Math.round(y + i * rowHeight) + .5, "H", Math.round(x + w) + .5]);
}
for (i = 1; i < wv; i++) {
path = path.concat(["M", Math.round(x + i * columnWidth) + .5, Math.round(y) + .5, "V", Math.round(y + h) + .5]);
}
return this.path(path.join(",")).attr({stroke: color});
};
$(function () {
$("#data").css({
position: "absolute",
left: "-9999em",
top: "-9999em"
});
});
window.onload = function () {
function getAnchors(p1x, p1y, p2x, p2y, p3x, p3y) {
var l1 = (p2x - p1x) / 2,
l2 = (p3x - p2x) / 2,
a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y));
a = p1y < p2y ? Math.PI - a : a;
b = p3y < p2y ? Math.PI - b : b;
var alpha = Math.PI / 2 - ((a + b) % (Math.PI * 2)) / 2,
dx1 = l1 * Math.sin(alpha + a),
dy1 = l1 * Math.cos(alpha + a),
dx2 = l2 * Math.sin(alpha + b),
dy2 = l2 * Math.cos(alpha + b);
return {
x1: p2x - dx1,
y1: p2y + dy1,
x2: p2x + dx2,
y2: p2y + dy2
};
}
// Grab the data
var labels = [],
data = [];
$("#data tfoot th").each(function () {
labels.push($(this).html());
});
$("#data tbody td").each(function () {
data.push($(this).html());
});
// Draw
var width = 800,
height = 250,
leftgutter = 30,
bottomgutter = 20,
topgutter = 20,
colorhue = .6 || Math.random(),
color = "hsl(" + [colorhue, .5, .5] + ")",
r = Raphael("holder", width, height),
txt = {font: '12px Helvetica, Arial', fill: "#fff"},
txt1 = {font: '10px Helvetica, Arial', fill: "#fff"},
txt2 = {font: '12px Helvetica, Arial', fill: "#000"},
X = (width - leftgutter) / labels.length,
max = Math.max.apply(Math, data),
Y = (height - bottomgutter - topgutter) / max;
r.drawGrid(leftgutter + X * .5 + .5, topgutter + .5, width - leftgutter - X, height - topgutter - bottomgutter, 10, 10, "#000");
var path = r.path().attr({stroke: color, "stroke-width": 4, "stroke-linejoin": "round"}),
bgp = r.path().attr({stroke: "none", opacity: .3, fill: color}),
label = r.set(),
lx = 0, ly = 0,
is_label_visible = false,
leave_timer,
blanket = r.set();
label.push(r.text(60, 12, "24 hits").attr(txt));
label.push(r.text(60, 27, "22 September 2008").attr(txt1).attr({fill: color}));
label.hide();
var frame = r.popup(100, 100, label, "right").attr({fill: "#000", stroke: "#666", "stroke-width": 2, "fill-opacity": .7}).hide();
var p, bgpp;
for (var i = 0, ii = labels.length; i < ii; i++) {
var y = Math.round(height - bottomgutter - Y * data[i]),
x = Math.round(leftgutter + X * (i + .5)),
t = r.text(x, height - 6, labels[i]).attr(txt).toBack();
if (!i) {
p = ["M", x, y, "C", x, y];
bgpp = ["M", leftgutter + X * .5, height - bottomgutter, "L", x, y, "C", x, y];
}
if (i && i < ii - 1) {
var Y0 = Math.round(height - bottomgutter - Y * data[i - 1]),
X0 = Math.round(leftgutter + X * (i - .5)),
Y2 = Math.round(height - bottomgutter - Y * data[i + 1]),
X2 = Math.round(leftgutter + X * (i + 1.5));
var a = getAnchors(X0, Y0, x, y, X2, Y2);
p = p.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
bgpp = bgpp.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
}
var dot = r.circle(x, y, 4).attr({fill: "#333", stroke: color, "stroke-width": 2});
blanket.push(r.rect(leftgutter + X * i, 0, X, height - bottomgutter).attr({stroke: "none", fill: "#fff", opacity: 0}));
var rect = blanket[blanket.length - 1];
(function (x, y, data, lbl, dot) {
var timer, i = 0;
rect.hover(function () {
clearTimeout(leave_timer);
var side = "right";
if (x + frame.getBBox().width > width) {
side = "left";
}
var ppp = r.popup(x, y, label, side, 1),
anim = Raphael.animation({
path: ppp.path,
transform: ["t", ppp.dx, ppp.dy]
}, 200 * is_label_visible);
lx = label[0].transform()[0][1] + ppp.dx;
ly = label[0].transform()[0][2] + ppp.dy;
frame.show().stop().animate(anim);
label[0].attr({text: data + " hit" + (data == 1 ? "" : "s")}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
label[1].attr({text: lbl + " September 2008"}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
dot.attr("r", 6);
is_label_visible = true;
}, function () {
dot.attr("r", 4);
leave_timer = setTimeout(function () {
frame.hide();
label[0].hide();
label[1].hide();
is_label_visible = false;
}, 1);
});
})(x, y, data[i], labels[i], dot);
}
p = p.concat([x, y, x, y]);
bgpp = bgpp.concat([x, y, x, y, "L", x, height - bottomgutter, "z"]);
path.attr({path: p});
bgp.attr({path: bgpp});
frame.toFront();
label[0].toFront();
label[1].toFront();
blanket.toFront();
};