I have these conditions:
Point A, B and C are created.
Point A to Point B will create a line.
Parallel lines are created depending on the position of Point A, B and C (refer to the figure below).
If you move Point A, the lines will also move but Points B and C remain on their respective positions.
They can be moved at any position.
What I want is to create this:
Consider the figure 1 below (I'm sure you already know this basic 2D geometry but without this my answer would be incomplete):
Coordinates for points A and B are known and we want to find function that can be used to calculate y-coordinate whenever x-coordinate is known, in such a way that point (x,y) lies on the line. From the figure 1:
k = tan(alpha) = (y2 - y1) / (x2 - x1) - the slope of line
Putting coordinates of either A or B into well known line equation y = kx + m we can calculate m to make the equation complete. Having this equation, for any coordinate x we can calculate coordinate y using this equation. The good thing about it is that it doesn't depend on the position of point A and B or slop (angle) of the line - you will have to take care of the special case of vertical/horizontal lines where y/x will be infinite according to this equation.
Back to your question. Take a look at figure 2 below:
We have very similar situation here, there is a line between points A and C, and line between points B and D. I assumed that point A is at the center of the coordinate system! This generally won't be the case but this is really not a restriction as you can perform translation that will put A in the center, then make your calculations and then translate everything back.
Using the technique described at the beginning, you can find the line equation for the line that connects A and C points and for the line that connects B and D points (D coordinates can be easily calculated). Let's assume you did just that:
A-C: y = k1*x (m is zero as line goes through the center A)
B-D: y = k2*x + m2 (m2 is not zero as line doesn't go through the center A)
Finally the algorithm you could use to draw these parallel lines:
Choose a space with which you want to take x-coordinates between x1 and x3. For example, if you want 4 lines this space will be s = (x3 - x1) / 4 and so on.
Set value x_start = x1 + s (and later x_start += s), and calculate y-coordinate using the equation for A-C line y_end = k1*x_start. This will give you point that lies on the line A-C and this is the start of your line.
Similarly, calculate the end point that will lie on the line that connects B and D:
x_end = x2 + s (later x_end += s)
y_end = k2*x_end + m2
Using these equations calculate points (x_start,y_start) and (x_end,y_end) for all lines that you want to draw (there is |x3 - x1| / desired_num_of_lines of them).
You'll have to form new equations each time point A moves out of the current A-C line, as every time this happens the slop of the A-C (and B-D) line changes invalidating the current equations.
I'm not going to write any JS code, but having the logic behind the possible solution should give you more then enough information to move forward with you own implementation.
Always think, when using the Context2D, that using the transforms (translate, rotate, scale), can spare you some math.
With those transforms you can think of your drawing like you would do with a pen : where do you put the pen ? where do you move next (translate) ? do you rotate the page ? do you get closer or further from the page (scale) ?
Here you want to start at A, then move along AC.
Each step on the way, you want to draw the AB vector.
Here's how you could code it, as you see, just simple vector math here, so if you remember that AB vector has (B.x-A.x, B.y-A.y) coordinates, you know most of the math you'll need.
// boilerPlate
var ctx = document.getElementById('cv').getContext('2d');
ctx.strokeStyle = '#000';
// params : Points : {x,y}
var A, B, C;
A = { x: 20, y: 170 };
B = { x: 80, y: 60 };
C = { x: 140, y: 120 };
// param : number of lines to draw.
var stepCount = 5;
// ----------
// compute AB vector = B - A
var AB = { x: B.x - A.x, y: B.y - A.y };
// compute step : ( C - A ) / stepCount
var step = { x: (C.x - A.x) / stepCount, y: (C.y - A.y) / stepCount };
// -- start draw
ctx.save();
// Move pen to A
ctx.translate(A.x, A.y);
for (var i = 0; i <= stepCount; i++) {
// draw AB vector at current position
ctx.lineWidth= ( i==0 || i==stepCount ) ? 2 : 1 ;
drawVector(AB);
// move pen one step further
ctx.translate(step.x, step.y);
}
ctx.restore();
// --
// draws vector V at the current origin ((0,0)) of the context.
function drawVector(V) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(V.x, V.y);
ctx.stroke();
}
// ----------
// legend
drawPoint(A, 'A');
drawPoint(B, 'B');
drawPoint(C, 'C');
function drawPoint(P, name) {
ctx.beginPath();
ctx.arc(P.x, P.y, 3, 0, 6.28);
ctx.fill();
ctx.strokeText(name, P.x + 6, P.y + 6);
}
<canvas id='cv' width=300 height=200></canvas>
Džanan has it right, and in simple terms, you need the X and Y offsets between the starting points of the two lines, i'e' point A and point C. When drawing the line that starts at C, and assuming that it ends at D, you will need to add the same X and Y offsets, e.g., if you draw AB with starting coordinates (100, 150) as follows:
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
And if C has to start at (150, 200), the offset here would be
X: 50, Y:50
so CD would be drawn as
context.beginPath();
context.moveTo(150, 200);
context.lineTo((450+50), (50+50));
context.stroke();
Now this assumes that the length of both the lines are going to be same. If they are to differ, the equation will be slightly more complex.
Related
I am trying to fill circle with some line alike sprites.
I generate random position x and y within +- radius, but I olny want to draw sprite when it not intersects with circle bounds like:
Green lines I want to draw, and the red ones - I don't.
I am wondering on some ideas that can help to detect unwanted sprites fast. Is there anything I can use for this purposes?
I am using pixi.js and sprite`s height and width are always the same.
A simple idea could be the following one:
For segment (line?), defined by the two point P1 = (x0,y0) and P2 = (x1, y1).
The circle is defined by center R = (xc, yc) and a radius r.
Now check
distance P1 - C < r, also meaning point is IN the circle
distance P2 - C < r, (same)
If both true, green line ! Depending on the library you used, you'll probably find a method such pointIsInCircle that will do half of the job.
Here is a simple exemple from w3resource
function check_a_point(a, b, x, y, r) {
var dist_points = (a - x) * (a - x) + (b - y) * (b - y);
r *= r;
if (dist_points < r) {
return true;
}
return false;
}
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/
I want to make a Tetradecagon, a polygon with 14 sides, with Processing.JS.
(I want to make the Tetradecagon like the one shown in the Image below!)
Using the numbers given in the image, which I would like to replicate, I concluded that each piece (I don't know it's proper name), has an angle of 25.714285714°.....25 and 10/14 = 25 and 5/7 - 5/7 in decimal form = 0.714285714So, I arrived at 25.714285714°
Now, in Processing.JS, I was wanting to use a while loop:
var r = 0;
var draw = function() {
translate(200,200);
while(r < 361){
rotate(r);
r = r + 25.714285714;
line(200,0,200,200);
}
};
Here, I have set one variable, r. r will be the variable for the rotate() function. The while loop will keep going until r meets 360 - this will allow for the the change in r, the angle, to increase by 25.714285714°, while r < 361.
But, sadly, this is not happening. What I see on my canvas is the line being shot off the screen.
(edit) I added translate(200,200); just above the while() loop - this helped, but the lines are still not looking like the picture above.
The second point of the line is not staying in the center; the whole line is being shifted. I only want the first (top) point to be shifted by the given change in angles.
How do I change the code in order to achieve the goal that I am striving for? Any help would be appreciated - Thanks for your time!
P.S. This is my result with the current code -
Processing.js is just for running Processing code. This looks like a mix of Processing and Javascript code so my first advice would be "write real Processing code".
With that said, if you want to do this based on coordinate rotation, look at your 14-gon: it's 14 repeated triangles, so analyze one triangle and draw that 14 times. Any triangular slice is defined by a line from "the center" to "a vertex on the 14-gon" at a (necessary) distance r, the radius of the circumscribing circle. So, given a vertex (r,0) on the 14-gon where is the next vertex (nx,ny)?
Simple maths:
first vertex = (x, y) = (r,0)
next vertex = (nx,ny) = (r,0) rotated over (0,0) by (phi = tau/14)
(I'm using tau here because it's a far more convenient constant for programming purposes. It's simply equal to 2*pi, and as such represents an entire circle, rather than a half circle)
Now, computing that rotate coordinate using basic trigonometry:
nx = r * cos(phi) - 0 * sin(phi) = r * cos(phi)
ny = r * sin(phi) + 0 * cos(phi) = r * sin(phi)
Alright, done. And this nx,ny computation is clearly not specific to the number 14, it about arbitrary angles, so let's code the solution and make it work for any n-sided polygon:
void setup() {
size(400,400);
noLoop();
}
void draw() {
background(255);
// offset the coordinate system so that (0,0) is the sketch center
translate(width/2,height/2);
// then draw a polygon. In this case, radius width/2, and 14 sided
drawNgon(width/2, 14);
}
void drawNgon(float r, float n) {
// to draw (r,0)-(x',y') we need x' and y':
float phi = TAU/n;
float nx = r * cos(phi);
float ny = r * sin(phi);
// and then we just draw that line as many times as there are sides
for(int a=0; a<n; a++) {
// draw line...
line(r,0, nx,ny);
// rotate the entire coordinate system...
rotate(phi);
// repeat until done.
}
}
And now we can freely change both the polygon radius and the number of sides by changing the input to drawNgon(..., ...).
I am trying to calculate if a point resides above or below a line that is defined by two points. For clarification I only need to know if the point is on "this side" or "that side". I realize if the line is perfectly vertical there will be no "above" or "below". I have a line defined by two points (centerX, centerY) and (xprime, yprime). For simplicity centerX, centerY can be converted to (0,0). I want to determine if (mouseX, mouseY) is above or below that line.
I have tried to use this formula but it is not giving me the results I expected.
var position = Math.sin((xprime -centerX) * (mouseY - centerY) - (yprime - centerY) * (mouseX - prime));
The values for position seem to oscillate randomly from positive to negative as mouseX,mouseY values rotate around the line. I was under the impression the sign would change one time from positive to negative as the mouse position (mouseX,mouseY) crossed over the line. Values above the line would be positive, and values below would be negative.
I am using this code in conjunction with a formula to determine the angle of deflection from the original click. But I am not able to determine if the mouse is now above the initial click, or below. (again please excuse "above" and "below")
Simple solution exploiting cross product properties.
dx = xprime - centerX
dy = yprime - centerY
mx = mouseX - centerX
my = mouseY - centerY
cross = dx * my - dy * mx //zero means point on line
below = (cross > 0) //mouse is "at the right hand" of the directed line
if dx <> 0 then // check for vertical line
if dy/dx < 0 then //negative slope, invert result
below = not below
I'll try to give you general solution.
How to check if exact point is above or below line function:
Just imagine, we have line f(x) = 4x + 2. We need check, if point (x1, y1) below or above the line, we need to calculate f(x1) and compare it with y1.
if f(x1) > y1 it means, that (x1, y1) below the line.
if f(x1) < y1 means point (x1, y1) above the line.
if f(x1) = y1 - point on a line.
You can see on plot:
All line functions looks like f(x) = k * x + b, so, you need know k and b constants to know exact line function.
How to get line function by two points:
Imagine points A (x_a, y_a) and B (x_b, y_b) and we want to get line function, we need to solve system of two equations:
y_a = k * x_a + b
y_b = k * x_b + b
That is very easy. After you will know k and b and after you can check if point below or above AB line
Currently I have this fiddle from Blindman67 which draws Golden spiral figure 1(see image below).
function renderSpiral(pointA, pointB, turns){
var dx, dy, rad, i, ang, cx, cy, dist, a, c, angleStep, numberTurns, nTFPB, scale, styles;
// clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width,ctx.canvas.height)
// spiral stuff
a = 1; // the larger this number the larger the spiral
c = 1.358456; // constant See https://en.wikipedia.org/wiki/Golden_spiral
angleStep = Math.PI/20; // set the angular resultion for drawing
numberTurns = 6; // total half turns drawn
nTFPB = 2; // numberOfTurnsForPointB is the number of turns to point
// B should be integer and describes the number off
// turns made befor reaching point B
// get the ang from pointA to B
ang = Math.atan2(pointB.y-pointA.y,pointB.x-pointA.x);
// get the distance from A to B
dist = Math.sqrt(Math.pow(pointB.y-pointA.y,2)+Math.pow(pointB.x-pointA.x,2));
if(dist === 0){
return; // this makes no sense so exit as nothing to draw
}
// get the spiral radius at point B
rad = Math.pow(c,ang + nTFPB * 2 * Math.PI); // spiral radius at point2
// now just need to get the correct scale so the spiral fist to the
// constraints requiered.
scale = dist / rad;
// ajust the number of turns so that the spiral fills the canvas
while(Math.pow(c,Math.PI*numberTurns)*scale < ctx.canvas.width){
numberTurns += 2;
}
// set the scale, and origin to centre
ctx.setTransform(scale, 0, 0, scale, pointA.x, pointA.y)
// make it look nice create some line styles
// first just draw the line A-B
ctx.strokeStyle = "black";
ctx.lineWidth = 2 * ( 1 / scale); // because it is scaled invert the scale
// can calculate the width requiered
// ready to draw
ctx.beginPath();
ctx.moveTo(0, 0) // start at center
ctx.lineTo((pointB.x-pointA.x)*(1/scale),(pointB.y-pointA.y)*(1/scale) ); // add line
ctx.stroke(); // draw it all
// Now draw the sporal. draw it for each style
styles.forEach( function(style) {
ctx.strokeStyle = style.colour;
ctx.lineWidth = style.width * ( 1 / scale); // because it is scaled invert the scale
// can calculate the width requiered
// ready to draw
ctx.beginPath();
for( i = 0; i <= Math.PI *numberTurns; i+= angleStep){
dx = Math.cos(i); // get the vector for angle i
dy = Math.sin(i);
var rad = Math.pow(c, i); // calculate the radius
if(i === 0) {
ctx.moveTo(0, 0) // start at center
}else{
ctx.lineTo(dx * rad, dy * rad ); // add line
}
}
ctx.stroke(); // draw it all
});
ctx.setTransform(1,0,0,1,0,0); // reset tranfrom to default;
}
What I want to obtain is figure 2 (see image below).
Q1. How can I change mine spiral so line AB will fit between first and second screw while A is the start of spiral?
You can also refer to my earlier question for better understanding of my problem.
To achieve the properties you need you need to adjust your spiral like following:
choose the right angular position of the line AB
I choose 1.5*M_PI [rad] for A and 3.5*M_PI [rad] for B (on unrotated spiral)
rotate your spiral by angle of your AB line
that is easy just add the angle to the final polar -> cartesian coordinates conversion and that will rotate entire spiral so computed angular positions of A,B on spiral will match the real points AB direction
rescale your spiral to match the AB size
So compute the radiuses for angular points A,B positons on spiral and then compute the scale=|AB|-(r(b)-r(a)). Now just multiply this to compute radius of each rendered point ...
I played a bit with the golden ratio and spiral a bit and here is the result
Yellow spiral is approximation by quarter circle arcs
Aqua is the Golden spiral
As you can see they do not match so much (this is with ratio*0.75 to make them more similar but it should be just ratio) Either I have a bug somewhere, or the origin of spiral is shifted (but does not look like it) or I have wrong ratio constant ratio = 0.3063489 or the Golden rectangles are introducing higher floating round errors then I taught or I am missing something stupid.
Here the C++ source code so you can extract what you need:
//---------------------------------------------------------------------------
#include <Math.h>
//---------------------------------------------------------------------------
bool _redraw=false; // just signal to repaint window after spiral change
double Ax,Ay,Bx,By; // mouse eddited points
double gr=0.75; // golden spiral ratio scale should be 1 !!!
void GoldenSpiral_draw(TCanvas *can) // GDI draw
{
double a0,a,b,l,x,y,r=5,ratio;
// draw AB line
can->Pen->Color=clWhite;
can->MoveTo(Ax,Ay);
can->LineTo(Bx,By);
// draw A,B points
can->Pen->Color=clBlue;
can->Brush->Color=clAqua;
can->Ellipse(Ax-r,Ay-r,Ax+r,Ay+r);
can->Ellipse(Bx-r,By-r,Bx+r,By+r);
// draw golden ratio rectangles
can->Pen->Color=clDkGray;
can->Brush->Style=bsClear;
ratio=1.6180339887;
a=5.0; b=a/ratio; x=Ax; y=Ay;
y-=0.5*b; x-=0.5*b; // bias to match real golden spiral
can->Rectangle(x,y,x+a,y+b); y-=a;
for (int i=0;i<5;i++)
{
can->Rectangle(x,y,x+a,y+a); b=a; a*=ratio; x-=a;
can->Rectangle(x,y,x+a,y+a); y+=a; b=a; a*=ratio;
can->Rectangle(x,y,x+a,y+a); x+=a; y-=b; b=a; a*=ratio;
can->Rectangle(x,y,x+a,y+a); x-=b; b=a; a*=ratio; y-=a;
}
// draw circle arc approximation of golden spiral
ratio=1.6180339887;
a=5.0; b=a/ratio; x=Ax; y=Ay; r=10000; y-=a;
y-=0.5*b; x-=0.5*b; // bias to match real golden spiral
can->Pen->Color=clYellow;
for (int i=0;i<5;i++)
{
can->Arc(x-a,y,x+a,y+a+a,+r, 0, 0,-r); b=a; a*=ratio; x-=a;
can->Arc(x,y,x+a+a,y+a+a, 0,-r,-r, 0); y+=a; b=a; a*=ratio;
can->Arc(x,y-a,x+a+a,y+a,-r, 0, 0,+r); x+=a; y-=b; b=a; a*=ratio;
can->Arc(x-a,y-a,x+a,y+a, 0,+r,+r, 0); x-=b; b=a; a*=ratio; y-=a;
}
can->Brush->Style=bsSolid;
// compute golden spiral parameters
ratio=0.3063489*gr;
x=Bx-Ax;
y=By-Ay;
l=sqrt(x*x+y*y); // l=|AB|
if (l<1.0) return; // prevent domain errors
a0=atan2(-y,x); // a=atan2(AB)
a0+=0.5*M_PI; // offset so direction of AB matches the normal
a=1.5*M_PI; r=a*exp(ratio*a); b=r;
a+=2.0*M_PI; r=a*exp(ratio*a); b=r-b;
b=l/r; // b=zoom of spiral to match AB screw distance
// draw golden spiral
can->Pen->Color=clAqua;
can->MoveTo(Ax,Ay);
for (a=0.0;a<100.0*M_PI;a+=0.001)
{
r=a*b*exp(ratio*a); if (r>512.0) break;
x=Ax+r*cos(a0+a);
y=Ay-r*sin(a0+a);
can->LineTo(x,y);
}
}
//---------------------------------------------------------------------------
You can ignore the golden ratio rectangles and circular arcs ...
change the drawings based on can-> to your gfx API. It is just GDI Canvas
Hard to say if your spiral is correct ... you can check with the golden ratio rectangles (as I did). If you got correct spiral then just apply the bullets #1,#2,#3 to it and you should be fine.
Here's a fiddle which I believe gives you the output your looking for.
https://jsfiddle.net/8a7fdg3d/4/
The main problem was starting the spiral from 0 results in the initial straight line.
Starting the spiral from 1 removes this part of the graph and then you just had to adjust the starting point of your black |AB| line.
This was done by adjusting
for( i = 0; i <= Math.PI *numberTurns; i+= angleStep)
to
for( i = 1; i <= Math.PI *numberTurns; i+= angleStep)
to change the starting point of the spiral, then changing
// ready to draw
ctx.beginPath();
ctx.moveTo(0, 0) // start at center
to
// ready to draw
ctx.beginPath();
dx = Math.cos(1); // get the vector for angle i
dy = Math.sin(1);
var rad = Math.pow(c, 1); // calculate the radius
ctx.moveTo(dx * rad, dy * rad ) // start at center
to make your |AB| line match up.