Arc sweep direction in two.js - javascript

two.js library has a function as follows;
makeArcSegment two.makeArcSegment(ox, oy, ir, or, sa, ea, res);
I calculate my parameters as follows;
var prevPt = points[i - 1];
var dxNext = nextPt.x - pt.x;
var dyNext = nextPt.y - pt.y;
var angleNext = Math.atan2(dyNext, dxNext);
var dxPrev = prevPt.x - pt.x;
var dyPrev = prevPt.y - pt.y;
var anglePrev = Math.atan2(dyPrev, dxPrev);
var innerRadius = 0;
var outerRadius = 20;
var arc = two.makeArcSegment(pt.x, pt.y, innerRadius, outerRadius, anglePrev, angleNext)
And my output is kind of unexpected.
At first node (after the line with the length of 225.46), sweep direction is counter clockwise.
At second node (after 142.35), sweep direction is clockwise.How do I force it to be always counter clockwise?
Thanks.

This is the workaround I found.
if (anglePrev < 0.0)
{
anglePrev += Math.PI * 2.0;
}
if (angleNext < 0.0)
{
angleNext += Math.PI * 2.0;
}
if (angleNext > anglePrev)
{
anglePrev += Math.PI * 2.0;
}

According to this if you make the arc.endAngle less than the arc.startAngle then it should go in the opposite direction..
So you can try to draw between min(prev,next) and max(prev,next) as workaround.
Also make all angles positive (adding 2*Pi to negative ones).
Looks like serious flaw of library though.

Related

Trimming length of a random drawn line in HTML canvas over time

I have the code below that is drawing a continue line on to a canvas. The line is using live input from sliders as seen here My test page, this simulates a Spirograph with 3 axis. (EDIT! you have to move slider to start)
I want to keep the lines a set length removing the tail as i go, but because the sliders update the line in real time I am not sure how best to do this and i cant simple recalculate the line unless i record the time the values change.
I was thinking that I could store a list of all the point in an array to make up the of the length of line I am interested in and then clear and redraw each time, but this seems like a lot of duplication. it would be an array of several 100 to 1000 points.
I think this is the way to go and just push the old points out the bottom as new ones are calculated but does any one have any better solutions.
// Your code here!
var globalID;
var r1 = 80;
var ang1 = 0;
var ang2 = 0;
var ang3 = 0;
var flag = null;
var x2 = 0;
var y2 = 0
function drawpatten() {
var canvas = document.getElementById("graphicsView");
var ctx = canvas.getContext('2d');
ctx.strokeStyle = "#0000FF";
ctx.beginPath();
// move the start if the line to the last know point caculated
ctx.moveTo(x2 + 200, y2 + 200);
// get current value of sliders and devide the value by 1000 (sliders are -100 to + 100 so this gives a value of 0.1 to 0.0001 for each ajustment of angle)
S1 = document.getElementById("slider1");
angm1 = S1.value / 1000;
S2 = document.getElementById("slider2");
angm2 = S2.value / 1000;
S3 = document.getElementById("slider3");
angm3 = S3.value / 1000;
// we are only going to draw to screen for each 10 points we caculate, this allows us to have finer resolutions with out the over head of writing to screen so often
for (i = 0; i < 10; i++) {
//increments the angle and reset after 360 full circle
ang1 = ang1 + angm1;
ang2 = ang2 + angm2;
ang3 = ang3 + angm3;
if (ang1 > 360) { ang1 = ang1 - 360 };
if (ang2 > 360) { ang2 = ang2 - 360 };
if (ang3 > 360) { ang3 = ang3 - 360 };
// caculate the x y cordinates the points on each circle and of sets them
x = (Math.cos(ang1) * r1);
y = (Math.sin(ang1) * r1);
x1 = (Math.cos(ang2) * r1) + x;
y1 = (Math.sin(ang2) * r1) + y;
x2 = (Math.cos(ang3) * r1) + x1;
y2 = (Math.sin(ang3) * r1) + y1;
// draws the next sections of the line
ctx.lineTo(x2 + 200, y2 + 200);
}
// better way to do this but this flag just skips drawing the first time, this is becasue the first step will have a line from 0,0 to first cacualted point)
if (flag > 0) {
ctx.stroke();
}
// set flag after first caculate and stroke
flag = 1
// recussivaly call function
globalID = requestAnimationFrame(drawpatten);
}
As always, clear all and redraw all every frame.
Keep all your points in an Array, iterate through them to create a new Path at every frame, and draw that.
var r1 = 80;
var ang1 = 0;
var ang2 = 0;
var ang3 = 0;
var points = [];
// the anim loop
function anim()  {
// push new points
makepattern();
// remove old points
cleanoldies();
// draw all
draw();
// do it again
requestAnimationFrame(anim);
}
anim();
function cleanoldies() {
var max_length = slider4.value * 2;
while(points.length > max_length) {
points.shift();
}
}
function draw() {
//Here we'll only draw
var canvas = document.getElementById("graphicsView");
var ctx = canvas.getContext('2d');
ctx.strokeStyle = "#0000FF";
// clear all
ctx.clearRect(0, 0, canvas.width, canvas.height);
// a single Path
ctx.beginPath();
// points are stored in a flat array [x, y, x, y, x...]
for (let i = 0; i < points.length; i += 2)
ctx.lineTo(points[i], points[i + 1]);
ctx.stroke();
}
function makepattern() {
// push new points
S1 = document.getElementById("slider1");
angm1 = S1.value / 1000;
S2 = document.getElementById("slider2");
angm2 = S2.value / 1000;
S3 = document.getElementById("slider3");
angm3 = S3.value / 1000;
for (i = 0; i < 10; i++) {
ang1 = ang1 + angm1;
ang2 = ang2 + angm2;
ang3 = ang3 + angm3;
if (ang1 > 360) {
ang1 = ang1 - 360
};
if (ang2 > 360) {
ang2 = ang2 - 360
};
if (ang3 > 360) {
ang3 = ang3 - 360
};
var x = (Math.cos(ang1) * r1),
y = (Math.sin(ang1) * r1),
x1 = (Math.cos(ang2) * r1) + x,
y1 = (Math.sin(ang2) * r1) + y,
x2 = (Math.cos(ang3) * r1) + x1,
y2 = (Math.sin(ang3) * r1) + y1;
// store the next sections of the line
points.push(x2 + 200, y2 + 200);
}
}
<input type="range" min="-100" max="100" value="10" id="slider1"><br>
<input type="range" min="-100" max="100" value="20" id="slider2"><br>
<input type="range" min="-100" max="100" value="10" id="slider3"><br>
<label>length<input type="range" min="0" max="10000" id="slider4" value="300"></label><br>
<canvas id="graphicsView" height="400" width="500"></canvas>
I think the easy way to do this would be, as you suggest, maintain an array of the line segments, pushing newly calculated segments onto the end of the array, and shifting the oldest segments from the beginning. Then, paint the old segment white. This has the side effect of also erasing little bits and pieces of line that shouldn't be erased, but the line moves so fast in your example that it shouldn't be too noticeable.
If that imperfection is not acceptable, then I can't see any other way than to redraw the whole curve every frame. Painting in HTML canvases is nice because the browser won't update the screen until the frame is completely drawn (so you don't have to worry about managing another framebuffer.)

Adding perspective to fake 3D animation

I'm working on a canvas-based animation, and I'm trying to get a 3D effect in a 2D canvas.
So far, things are going well! I've got my "orbiting line of triangles" working very well:
var c = document.createElement('canvas');
c.width = c.height = 100;
document.body.appendChild(c);
var ctx = c.getContext("2d");
function Triangles() {
this.rotation = {
x: Math.random()*Math.PI*2,
y: Math.random()*Math.PI*2,
z: Math.random()*Math.PI*2
};
/* Uncomment this for testing perspective...
this.rotation = {
x: Math.PI/2,
y: 0,
z: 0
};
*/
}
Triangles.prototype.draw = function(t) {
this.rotation.z += t/1000;
var i, points;
for( i=0; i<15; i++) {
points = [
this.computeRotation(Math.cos(0.25*i),-Math.sin(0.25*i),0),
this.computeRotation(Math.cos(0.25*(i+1)),-Math.sin(0.25*(i+1)),-0.1),
this.computeRotation(Math.cos(0.25*(i+1)),-Math.sin(0.25*(i+1)),0.1)
];
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(50+40*points[0][0],50+40*points[0][1]);
ctx.lineTo(50+40*points[1][0],50+40*points[1][1]);
ctx.lineTo(50+40*points[2][0],50+40*points[2][1]);
ctx.closePath();
ctx.fill();
}
};
Triangles.prototype.computeRotation = function(x,y,z) {
var rz, ry, rx;
rz = [
Math.cos(this.rotation.z) * x - Math.sin(this.rotation.z) * y,
Math.sin(this.rotation.z) * x + Math.cos(this.rotation.z) * y,
z
];
ry = [
Math.cos(this.rotation.y) * rz[0] + Math.sin(this.rotation.y) * rz[2],
rz[1],
-Math.sin(this.rotation.y) * rz[0] + Math.cos(this.rotation.y) * rz[2]
];
rx = [
ry[0],
Math.cos(this.rotation.x) * ry[1] - Math.sin(this.rotation.x) * ry[2],
Math.sin(this.rotation.x) * ry[1] + Math.cos(this.rotation.x) * ry[2]
];
return rx;
};
var tri = new Triangles();
requestAnimationFrame(function(start) {
function step(t) {
var delta = t-start;
ctx.clearRect(0,0,100,100)
tri.draw(delta);
start = t;
requestAnimationFrame(step);
}
step(start);
});
As you can see it's using rotation matrices for calculating the position of the points after their rotation, and I'm using this to draw the triangles using the output x and y coordinates.
I want to take this a step further by using the z coordinate and adding perspective to this animation, which will make the triangles slightly bigger when in the foreground, and smaller when in the background. However, I'm not sure how to go about doing this.
I guess this is more of a maths question than a programming one, sorry about that!
Define a focal length to control the amount of perspective. The greater the value the less the amount of perspective. Then
var fl = 200; // focal length;
var px = 100; // point in 3D space
var py = 200;
var pz = 500;
Then to get the screen X,Y
var sx = (px * fl) / pz;
var sy = (py * fl) / pz;
The resulting point is relative to the center of the veiw so you need to center it to the canvas.
sx += canvas.width/2;
sy += canvas.height/2;
That is a point.
It assumes that the point being viewed is in front of the view and further than the focal length from the focal point.
I've managed to figure out a basic solution, but I'm sure there's better ones, so if you have a more complete answer feel free to add it! But for now...
Since the coordinate system is already based around the origin with the viewpoint directly on the Z axis looking at the (x,y) plane, it's actually sufficient to just multiply the (x,y) coordinates by a value proportional to z. For example, x * (z+2)/2 will do just fine in this case
There's bound to be a more proper, general solution though!

How can I create a WebGL basic firework

I kinda have like a fountain of particles, but I want to make them ''explode'' making more of them where I click like a firework.
var nFireworks = 10000;
function initParticleSystem() {
var particlesData = [];
for (var i= 0; i < nFireworks; i++) {
// angulos del cono
var theta = Math.PI / 6.0 * Math.random();
var phi = 5.0 * Math.PI * Math.random();
// direccion
var x1 = Math.sin(theta) * Math.cos(phi) ;
var y1 = velocity;
var z1 = 0.0;
// velocidad
var alpha = Math.random();
var velocity = (1.4 * alpha) + (0.80 * (1.0 - alpha));
particlesData[i * 4 + 0] = x1 * velocity;
particlesData[i * 4 + 1] = y1 * velocity;
particlesData[i * 4 + 2] = z1 * velocity;
particlesData[i * 4 + 3] = i * 0.095;
}
}
Your code is a bit odd, it uses velocity before it defines it, and you don't actually show the step function or anything else, but hey, I'll give it a go.
Your code (probably) generates a cone of particles where they all move along y at a constant velocity, and the x velocity is spread randomly in a PI/6 wide cone. If you want your particles to spread out in all directions randomly I would suggest starting by changing it like this:
before your for loop, set velocity to a constant first instead of all that nonsense:
var velocity = 5;
Then, you want particles to move outwards from the point in all equally random x and y directions, so change your x and y values to:
var x1 = ((Math.random() - 0.5) * velocity) * 2;
var y1 = ((Math.random() - 0.5) * velocity) * 2;
to form particles where their x and y velocities are random between -velocity and +velocity
Then, I don't know why your code generates particle data in a single array like that, I would make it
particleData.push([x1,y1,z1,i]);
and then reference each particle that way, or a possibly less performant but much more readable:
particleData.push({x: x1, y: y1, z: z1, brightness: i]};
(I'm just going to guess that i is brightness there).
Good luck buddy, it's not really a WebGL question, it's just you asking someone how to write your code for you, but hopefully that helps.

Make and animate wave in canvas

I’m looking for a way to create a wave in a shape designed in canvas. After much research I found something that is pretty close to what I want:
var c = document.getElementById('c'),
ctx = c.getContext('2d'),
cw = c.width = window.innerWidth,
ch = c.height = window.innerHeight,
points = [],
tick = 0,
opt = {
count: 5,
range: {
x: 20,
y: 80
},
duration: {
min: 20,
max: 40
},
thickness: 10,
strokeColor: '#444',
level: .35,
curved: true
},
rand = function(min, max) {
return Math.floor((Math.random() * (max - min + 1)) + min);
},
ease = function(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b;
return -c / 2 * ((--t) * (t - 2) - 1) + b;
};
ctx.lineJoin = 'round';
ctx.lineWidth = opt.thickness;
ctx.strokeStyle = opt.strokeColor;
var Point = function(config) {
this.anchorX = config.x;
this.anchorY = config.y;
this.x = config.x;
this.y = config.y;
this.setTarget();
};
Point.prototype.setTarget = function() {
this.initialX = this.x;
this.initialY = this.y;
this.targetX = this.anchorX + rand(0, opt.range.x * 2) - opt.range.x;
this.targetY = this.anchorY + rand(0, opt.range.y * 2) - opt.range.y;
this.tick = 0;
this.duration = rand(opt.duration.min, opt.duration.max);
}
Point.prototype.update = function() {
var dx = this.targetX - this.x;
var dy = this.targetY - this.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (Math.abs(dist) <= 0) {
this.setTarget();
} else {
var t = this.tick;
var b = this.initialY;
var c = this.targetY - this.initialY;
var d = this.duration;
this.y = ease(t, b, c, d);
b = this.initialX;
c = this.targetX - this.initialX;
d = this.duration;
this.x = ease(t, b, c, d);
this.tick++;
}
};
Point.prototype.render = function() {
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
ctx.fillStyle = '#000';
ctx.fill();
};
var updatePoints = function() {
var i = points.length;
while (i--) {
points[i].update();
}
};
var renderPoints = function() {
var i = points.length;
while (i--) {
points[i].render();
}
};
var renderShape = function() {
ctx.beginPath();
var pointCount = points.length;
ctx.moveTo(points[0].x, points[0].y);
var i;
for (i = 0; i < pointCount - 1; i++) {
var c = (points[i].x + points[i + 1].x) / 2;
var d = (points[i].y + points[i + 1].y) / 2;
ctx.quadraticCurveTo(points[i].x, points[i].y, c, d);
}
ctx.lineTo(-opt.range.x - opt.thickness, ch + opt.thickness);
ctx.lineTo(cw + opt.range.x + opt.thickness, ch + opt.thickness);
ctx.closePath();
ctx.fillStyle = 'hsl(' + (tick / 2) + ', 80%, 60%)';
ctx.fill();
ctx.stroke();
};
var clear = function() {
ctx.clearRect(0, 0, cw, ch);
};
var loop = function() {
window.requestAnimFrame(loop, c);
tick++;
clear();
updatePoints();
renderShape();
//renderPoints();
};
var i = opt.count + 2;
var spacing = (cw + (opt.range.x * 2)) / (opt.count - 1);
while (i--) {
points.push(new Point({
x: (spacing * (i - 1)) - opt.range.x,
y: ch - (ch * opt.level)
}));
}
window.requestAnimFrame = function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(a) {
window.setTimeout(a, 1E3 / 60)
}
}();
loop();
canvas {
display: block;
}
<canvas id="c"></canvas>
http://codepen.io/jackrugile/pen/BvLHg
The problem is that the movement of the wave appears a bit unreal. I'd like to keep this notion of random motion and not have a shape that repeats itself by moving from left to right but it will be great if I found a way to create a ‘realistic’ water movement (good fluid dynamics, collisions of this wave with the edges of its container (custom shape)).
I think I'm asking a lot but ... A small line of research could help :)
Interference
You can make a more realistic wave using interference.
Have one big wave (swell) running slowly with a big motion
Have another one or two smaller sine waves running (oscillators)
All with random amplitudes
Mix the waves horizontally using average and calculate the various points
Draw the result using a cardinal spline (or if the resolution is high you can just draw simple lines between the points instead).
Use various parameters so you can adjust it live to find a good combination.
You can also add oscillators to represent the z axis to make it more realistic in case you want to layer the waves to make a pseudo-3D wave.
Example
I cannot give you wave collision, fluid dynamics - that would be a bit too broad for SO but I can give you a fluid-ish wave example (as you have the point of each segment you can use that for collision detection).
And example would be to create an oscillator object which you could set the various settings on such as amplitude, rotation speed (phase) etc.
Then have a mixer function which mixes the result of these oscillators that you use.
Live demo here (full-screen version here)
The oscillator object in this demo look like this:
function osc() {
/// various settings
this.variation = 0.4; /// how much variation between random and max
this.max = 100; /// max amplitude (radius)
this.speed = 0.02; /// rotation speed (for radians)
var me = this, /// keep reference to 'this' (getMax)
a = 0, /// current angle
max = getMax(); /// create a temp. current max
/// this will be called by mixer
this.getAmp = function() {
a += this.speed; /// add to rotation angle
if (a >= 2.0) { /// at break, reset (see note)
a = 0;
max = getMax();
}
/// calculate y position
return max * Math.sin(a * Math.PI) + this.horizon;
}
function getMax() {
return Math.random() * me.max * me.variation +
me.max * (1 - me.variation);
}
return this;
}
This do all the setup and calculations for us and all we need to do is to call the getAmp() to get a new value for each frame.
Instead of doing it manually we can use a "mixer". This mixer allows us to add as many oscillators we want to the mix:
function mixer() {
var d = arguments.length, /// number of arguments
i = d, /// initialize counter
sum = 0; /// sum of y-points
if (d < 1) return horizon; /// if none, return
while(i--) sum += arguments[i].getAmp(); /// call getAmp and sum
return sum / d + horizon; /// get average and add horizon
}
Putting this in a loop with a point recorder which shifts the point in one direction will create a fluid looking wave.
The demo above uses three oscillators. (A tip in that regard is to keep the rotation speed lower than the big swell or else you will get small bumps on it.)
NOTE: The way I create a new random max is not the best way as I use a break point (but simple for demo purpose). You can instead replace this with something better.
Since you are searching for a realistic effect, best idea might be to simulate the water. It is not that hard, in fact : water can be nicely enough approximated by a network of springs linked together.
Result is quite good, you can find it here :
http://jsfiddle.net/gamealchemist/Z7fs5/
So i assumed it was 2D effect and built an array holding, for each point of a water surface, its acceleration, speed, and position. I store them in a single array, at 3*i + 0, 3*i + 1, and 3*i+2.
Then on each update, i simply apply newton's laws with elasticity, and with some friction to get the movement to stop.
I influence each point so it goes to its stable state + get influenced by its right and left neighboor.
(If you want smoother animation, use also i-2 and i+2 points, and lower kFactor.)
var left = 0, y = -1;
var right = water[2];
for (pt = 0 ; pt < pointCount; pt++, i += 3) {
y = right;
right = (pt < pointCount - 1) ? water[i + 5] : 0;
if (right === undefined) alert('nooo');
// acceleration
water[i] = (-0.3 * y + (left - y) + (right - y)) * kFactor - water[i + 1] * friction;
// speed
water[i + 1] += water[i] * dt;
// height
water[i + 2] += water[i + 1] * dt;
left = y;
}
The draw is very simple : just iterate though the points and draw. But it's hard to get a smooth effect while drawing, since it's hard to have bezier and quadraticCurve to have their derivates match. I suggested a few drawing methods, you can switch if you want.
Then i added rain, so that the water can move in a random way. Here it's just very simple trajectory, then i compute if there's collision with the water, and, if so, i add some velocity and shift the point.
I'd like to create a ‘realistic’ water movement with good fluid dynamics, collisions of this wave with the edges of a custom
container..
Oh boy.. That is quite a mouthful.
You should probably ask your Question here: gamedev.stackexchange
Anyways, have you tried to program any sort of wave yet, or are you just looking for WaveCreator.js ?
Go and Google some Non-language-specific Guides on how to create 2D water.
If you are a beginner, then start with something simple to get the idea behind things.
How about creating a bunch of Boxes for "minecraft-style" water ?
Here every "line" of water could be represented as a position in an Array. Then loop through it and set the "height" of the water based on the previous Array Index.
(You could smooth the water out by either making the blocks very thin (More work for your program!) or by smoothing out the edges and giving them an angle based on the other Squares.
I think this could be a neat solution. Anyhow. Hope that gave you some ideas.

Faking a 3d sphere in canvas

I have a question about Faking 3d in HTML5/Canvas/Javascript...
Basically, I have a 2d image drawn on a <canvas> using drawImage().
What I would like to do, is draw the image, then displace the texture on the sphere to look... sphere-like...
see image below for clarity:
Any ideas?
I have googled myself to near-death over this, also it cannot be WebGL as it has to work on Mobiles... is there a way to do this?
You can check a working prototype here: http://jsbin.com/ipaliq/edit
I bet there's ton's of room for optimization and better/faster algorithms, but I hope this proof of concepts will point you in the right direction.
The basic algorithm goes through each pixel of the original image and calculates its new position if it was subject to an spherical deformation.
Here's the result:
Code:
var w = 150, h = 150;
var canvas=document.getElementById("myCanvas");
var ctx=canvas.getContext("2d");
ctx.fillStyle="red";
ctx.fillRect(0,0,w,h);
//-- Init Canvas
var initCanvas = function()
{
var imgData=ctx.getImageData(0,0,w,h);
for (i=0; i<imgData.width*imgData.height*4;i+=4)
{
imgData.data[i]=i%150*1.5;
imgData.data[i+1]=i%150*1.5;
imgData.data[i+2]=(i/imgData.width)%150*1.5;
imgData.data[i+3]=255;
}
ctx.putImageData(imgData,0,0);
};
initCanvas();
var doSpherize = function()
{
var refractionIndex = 0.5; // [0..1]
//refraction index of the sphere
var radius = 75;
var radius2 = radius * radius;
var centerX = 75;
var centerY = 75;
//center of the sphere
var origX = 0;
var origY = 0;
for (x=0; x<w;x+=1)
for (y=0; y<h;y+=1)
{
var distX = x - centerX;
var distY = y - centerY;
var r2 = distX * distX + distY * distY;
origX = x;
origY = y;
if ( r2 > 0.0 && r2 < radius2 )
{
// distance
var z2 = radius2 - r2;
var z = Math.sqrt(z2);
// refraction
var xa = Math.asin( distX / Math.sqrt( distX * distX + z2 ) );
var xb = xa - xa * refractionIndex;
var ya = Math.asin( distY / Math.sqrt( distY * distY + z2 ) );
var yb = ya - ya * refractionIndex;
// displacement
origX = origX - z * Math.tan( xb );
origY = origY - z * Math.tan( yb );
}
// read
var imgData=ctx.getImageData(origX,origY,1,1);
// write
ctx.putImageData(imgData,x+w,y+h);
}
};
doSpherize();
Note
I would advice agains such an intense operation to be handed by the frontend without webGL pixel shaders. Those are very optimized and will be able to take advantage of GPU accelaration.
You can definitely do something like this. I'm not sure if the quality and speed will be good enough though, particularly on mobile.
You're going to want to getImageData the pixels, perform some mathematical transformation on them I can't think of at the moment, and then putImageData them back.
It's probably going to look kind of blurry or pixelated (you could try interpolating but that will only get you so far). And you may have to optimize your javascript considerably to get it done in a reasonable time on a mobile device.
The transformation is probably something like an inverse azimuthal projection.

Categories