Changing the position of a sphere in P5.js - javascript

By default, it seems like a sphere object in P5 is located at (0,0). I want to create an object that is visually represented by a sphere with the ability to define the x and y coordinates of the sphere object.
Because I want to deal with multiple of these objects and draw connections between them, I don't want to use the translate function for a sphere to position it every time. Is there a way to position the sphere to the coordinates I want without the translate function?

You can still use translate() without affecting the global coordinate system by isolating local coordinate systems within push()/pop() calls. You can read more in the 2D Transformation tutorial. Even through it's for Processing, the only difference is swapping pushMatrix() for push() and popMatrix() for pop() and the concept translates to 3D as well, not just 2D.
function drawSphere(x, y, z, radius){
push(); // enter local coordinate system
translate(x, y, z);
sphere(radius);
pop(); // exit local coordinate system (back to global coordinates)
}
Here's a basic example drawing multiple spheres and lines between them:
const NUM_POINTS = 12;
let points = new Array(NUM_POINTS).fill();;
function setup(){
createCanvas(600, 600, WEBGL);
noFill();
// shorthand for initialising 12 random points at a set distance
points.forEach((p,i) => points[i] = p5.Vector.random3D().mult(150));
}
function draw(){
background(255);
rotateY(frameCount * 0.01);
// draw spheres (and lines from centre)
for(let i = 0 ; i < NUM_POINTS; i++){
// get each point
const point = points[i];
// draw sphere
drawSphere(point.x, point.y, point.z, 30);
// draw lines (optional)
const nextPoint = points[(i + 1) % NUM_POINTS];
line(nextPoint.x, nextPoint.y, nextPoint.z, point.x, point.y, point.z);
}
}
function drawSphere(x, y, z, radius){
push(); // enter local coordinate system
translate(x, y, z);
sphere(radius, 6);
pop(); // exit local coordinate system (back to global coordinates)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js"></script>
(If you're familiar with 3D packages such as Blender, C4D, Maya, etc. most of these allow you to nest an object inside another object (inheriting the parent's transformation, but allowing indepedent transformations which don't affect the parent. This is roughtly what you can achieve with push()/pop() calls).

function createSphere(x, y, z, r) {
translate(x, y, z)
sphere(r)
}
usage
createSphere(1, 2, 3, 10)

Related

Moving object from A to B smoothly across canvas

I am trying to move an object smoothly from point A to point B using HTML canvas and regular javascript.
Point A is a set of coordinates
Point B is in the case the cursor location.
I made a jsfiddle of what I have so far: https://jsfiddle.net/as9fhmw8/
while(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
ctx.save();
ctx.beginPath();
ctx.translate(projectile.x, projectile.y);
ctx.arc(0,0,5,0,2*Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
ctx.restore();
if(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
var stepsize = (projectile.mouseX - projectile.x) / (projectile.y - projectile.mouseY);
projectile.x += (stepsize + 1);
}
if(projectile.mouseY < projectile.y)
{
var stepsize = (projectile.y - projectile.mouseY) / (projectile.mouseX - projectile.x);
projectile.y -= (stepsize + 1);
}
}
Essentially what I can't figure out to do is to make the while loop slower (so that it appears animated in stead of just going through every iteration and showing the result).
I also can't figure out how to prevent the Arc from duplicating so that it creates a line that is permanent, instead of appearing to move from point a to point b.
Smooth animation here is really about determining how far to move your object for each iteration of the loop.
There is a little math involved here, but it's not too bad.
Velocity
Velocity in your case is just the speed at which your particles travel in any given direction over a period of time. If you want your particle to travel 200px over the course of 4 seconds, then the velocity would be 50px / second.
With this information, you can easily determine how many pixels to move (animate) a particle given some arbitrary length of time.
pixels = pixelsPerSecond * seconds
This is great to know how many pixels to move, but doesn't translate into individual X and Y coordinates. That's where vectors come in.
Vectors
A vector in mathematics is a measurement of both direction and magnitude. For our purposes, it's like combining our velocity with an angle (47°).
One of the great properties of vectors is it can be broken down into it's individual X and Y components (for 2-Dimensional space).
So if we wanted to move our particle at 50px / second at a 47° angle, we could calculate a vector for that like so:
function Vector(magnitude, angle){
var angleRadians = (angle * Math.PI) / 180;
this.magnitudeX = magnitude * Math.cos(angleRadians);
this.magnitudeY = magnitude * Math.sin(angleRadians);
}
var moveVector = new Vector(50, 47);
The wonderful thing about this is that these values can simply be added to any set of X and Y coordinates to move them based on your velocity calculation.
Mouse Move Vector
Modeling your objects in this way has the added benefit of making things nice and mathematically consistent. The distance between your particle and the mouse is just another vector.
We can back calculate both the distance and angle using a little bit more math. Remember that guy Pythagoras? Turns out he was pretty smart.
function distanceAndAngleBetweenTwoPoints(x1, y1, x2, y2){
var x = x2 - x1,
y = y2 - y1;
return {
// x^2 + y^2 = r^2
distance: Math.sqrt(x * x + y * y),
// convert from radians to degrees
angle: Math.atan2(y, x) * 180 / Math.PI
}
}
var mouseCoords = getMouseCoords();
var data = distanceAndAngleBetweenTwoPoints(particle.x, particle.y, mouse.x, mouse.y);
//Spread movement out over three seconds
var velocity = data.distance / 3;
var toMouseVector = new Vector(velocity, data.angle);
Smoothly Animating
Animating your stuff around the screen in a way that isn't jerky means doing the following:
Run your animation loop as fast as possible
Determine how much time has passed since last time
Move each item based on elapsed time.
Re-paint the screen
For the animation loop, I would use the requestAnimationFrame API instead of setInterval as it will have better overall performance.
Clearing The Screen
Also when you re-paint the screen, just draw a big rectangle over the entire thing in whatever background color you want before re-drawing your items.
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
Putting It All Together
Here is a Fiddle demonstrating all these techniques: https://jsfiddle.net/jwcarroll/2r69j1ok/3/

Calculating the coordinates of a point on circle, along a line between the center of the circle and another point outside it?

Here's an image to demonstrate the question:
Let's say I have Point A at [0,0], and Point B at [50, 30]. I want to find the coordinates of Point X, along a circle of radius 15, with an origin at Point A, which is also on a line between Point A and Point B.
Pointers on the best method to do this?
Since this has been tagged JavaScript, here's a simple implementation:
// disclaimer: code written in browser
function Point2D(x, y) {
this.x = x;
this.y = y;
}
function findCircleInteresction(center, radius, target) {
var vector = new Point2D(target.x - center.x, target.y - target.y);
var length = Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2));
var normal = new Point2D(vector.x / length, vector.y / length);
var result = new Point2D(center.x + (normal.x * radius), center.y + (normal.y * radius));
return result;
}
findCircleInteresction(new Point2D(0, 0), 15, new Point2D(50, 30));
Point2D is just a class to make objects with x and y properties.
findCircleInteresction takes three parameters:
- center the center of the circle
- radius the radius of the circle
- target a point outside the circle
In findCircleInteresction:
- calculate the vector between the center and the target
- get the length of the resulting vector
- compute the normal (normalized) of the vector
- find the point where the vector intersects with the circle by adding the center of the circle plus the normalized vector components multiplied by the radius of the circle
This code could be heavily optimized and it's untested but I think it illustrated the idea.
You would want to think of this as two overlapping triangles, one with sides Bx-Ax and By-Ay. What you want is to find the coordinates of X, which would specifically be a triangle with sides Xx-Ax and Xy-Ay but with known hypotenuse R, which is your radius of the circle. Notice that the angle for both triangles are equal in respect to the x-coordinates-axis.
So to get the angle of the triangle, take the arctan(By-Ay/Bx-Ax) Now with that angle, call it T, you can solve for the smaller legs with your know radius R.
To get the x coordinate you would take Rcos(T)
To get the y coordinate you would take Rsin(T)
Bringing it all together you have that Xx = Rcos(T) and Xy = Rsin(T)
If you are not willing to use a Math library, which this method would use, you can use ratio's (as Pointy commented)

How to set rotation of object 90 degrees along world x-axis?

Basically I want to set the rotation of an object along the world axis by an arbitrary angle. I'm currently using the rotateAroundWorldAxis function (found in another thread) to try to do that.
function render() {
// update mesh position
rotateAroundWorldAxis(mesh, new THREE.Vector3(1, 0, 0), angle);
renderer.render(scene, camera);
}
function rotateAroundWorldAxis(object, axis, radians) {
rotWorldMatrix = new THREE.Matrix4();
rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rotWorldMatrix.multiply(object.matrix); // pre-multiply
object.matrix = rotWorldMatrix;
object.rotation.setFromRotationMatrix(object.matrix);
}
The problem is I only want to set the angle (as in rotation.x = angle), not increment it (as in rotation.x += angle). Does anybody know how to adapt the above function (rotateAroundWorldAxis) to allow me to set the angle instead of incrementing it?
jsfiddle here
remove the third line in that function
rotWorldMatrix.multiply(object.matrix); // pre-multiply
This is the line that "adds" the two rotations together.

Creating an "Animated" Spiky Ball in Processing.js

For my class, I'm creating a project in which a level includes a cursor in the form of an ellipse that reacts to a mousePressed command by having spikes protrude from within the ellipse and then recede back into the ellipse.
The code for my cursor is right here:
class Cursor{
float r;
float x;
float y;
Cursor(float _r){
r = _r;
x = 0;
y = 0;
}
void setLocation (float _x, float _y) {
x = _x;
y = _y;
}
void display(){
noStroke();
fill(230, 242, 255);
ellipse(x, y, r, r);
}
My teacher suggested I use createShape (TRIANGLE) within the ellipse and animate one of the vertices from each spike coming out at the appropriate time, but I simply wasn't able to follow his instructions as well as I had needed to.
Any assistance on this matter would be greatly appreciated. I do hope to further use the animated vertices to "pop" a surrounding object later on, but I'm only mentioning that in the case that it's important for the initial creation and animation.
Thank you very much in advance!
Your teacher was probably talking about the beginShape(TRIANGLES) function. From the reference:
beginShape(TRIANGLES);
vertex(30, 75);
vertex(40, 20);
vertex(50, 75);
vertex(60, 20);
vertex(70, 75);
vertex(80, 20);
endShape();
(source: processing.org)
You could use this function to generate your spikes around your circle. You'll have to figure out the x and y positions of the triangles around the circle, but you can do that using an incrementing angle and the cos() and sin() functions.

Draw arc with increasing radius?

I am drawing an arc which increases gradually and turns in to a circle.On completion of animation(arc turning in to a circle) i want to draw another circle with increased radius with the previous circle persisting and the second animation continuing.
Arc to circle fiddle
After the circle is drawn,it gets washed out which is something that I dont want and continue the second animation.
Some unnecessary animation appears after the completion.
What should I do?
MyCode:
setInterval(function(){
context.save();
context.clearRect(0,0,500,400);
context.beginPath();
increase_end_angle=increase_end_angle+11/500;
dynamic_end_angle=end_angle+increase_end_angle;
context.arc(x,y,radius,start_angle,dynamic_end_angle,false);
context.lineWidth=6;
context.lineCap = "round";
context.stroke();
context.restore();
if(dynamic_end_angle>3.5*Math.PI){ //condition for if circle completion
draw(radius+10);//draw from same origin and increasd radius
}
},66);
window.onload=draw(30);
UPDATE:when should i clear the interval to save some cpu cycles and why does the animation slows down on third circle ??
First of all, about the flicker: you are using setInterval and not clearing it for the next draw(). So there’s that.
But I’d use a completely different approach; just check the time elapsed since the start, and draw an appropriate number of circles using a loop.
var start = new Date().getTime();
var timePerCircle = 2;
var x = 190, y = 140;
function draw() {
requestAnimationFrame(draw);
g.clearRect(0, 0, canvas.width, canvas.height);
var t = (new Date().getTime() - start) / 1000;
var circles = t / timePerCircle;
var r = 30;
do {
g.beginPath();
g.arc(x, y, r, 0, Math.PI * 2 * Math.min(circles, 1));
g.stroke();
r += 10;
circles--;
} while(circles > 0);
}
draw();
This snippet from your code has some flaw.
if(dynamic_end_angle>3.5*Math.PI){ //condition for if circle completion
draw(radius+10);//draw from same origin and increased radius
}
The recursive call to draw() will continue to run after the first circle was drawn completely. This is why the performance will be slow down immediately. You need to somehow block it.
I did a simple fix, you can polish it if you like. FIDDLE DEMO
My fix is to remove context.clearRect(0, 0, 500, 400); and change the new circle drawing logic to:
if (dynamic_end_angle > 3.5 * Math.PI) { //condition for if circle completion
increase_end_angle = 0; // this will prevent the draw() from triggering multiple times.
draw(radius + 10); //draw from same origin.
}
In this stackoverflow thread, it mentions how to make it more smooth. You'd better use some drawing framework since the optimization needs a lot of work.
When should I clear the interval to save some cpu cycles?
Better yet not use an interval at all for a couple of reasons:
Intervals are unable to sync to monitor's VBLANK gap so you will get jerks from time to time.
If you use setInterval you risk stacking calls (not high risk in this case though).
A much better approach is as you probably already know to use requestAnimationFrame. It's less CPU hungry, is able to sync to monitor and uses less resources in general even less if current tab/window is not active.
Why does the animation slows down on third circle ??
Your drawing calls are accumulating which slows everything down (setInterval is not cleared).
Here is a different approach to this. It's a simplified way and uses differential painting.
ONLINE DEMO
The main draw function here takes two arguments, circle index and current angle for that circle. The circles radius are stored in an array:
...,
sa = 0, // start angle
ea = 359, // end angle
angle = sa, // current angle
oldAngle = sa, // old angle
steps = 2, // number of degrees per step
current = 0, // current circle index
circles = [70, 80, 90], // the circle radius
numOfCircles = circles.length, ...
The function stores the old angle and only draws a new segment between old angle and new angle with 0.5 added to compensate for glitches due to anti-alias, rounding errors etc.
function drawCircle(circle, angle) {
angle *= deg2rad; // here: convert to radians
/// draw arc from old angle to new angle
ctx.beginPath();
ctx.arc(0, 0, circles[circle], oldAngle, angle + 0.5);
ctx.stroke();
/// store angle as old angle for next round
oldAngle = angle;
}
The loop increases the angle, if above or equal to end angle it will reset the angle and increase the current circle counter. When current counter has reach last circle the loop ends:
function loop() {
angle += steps;
/// check angle and reset, move to next circle
if (angle >= ea - steps) {
current++;
angle = sa;
oldAngle = angle;
}
drawCircle(current, angle);
if (current < numOfCircles)
requestAnimationFrame(loop);
}

Categories