I am trying to place evenly-spaced markers/dots on a quadratic curve drawn with HTML Canvas API. Found a nice article explaining how the paths are calculated in the first place, at determining coordinates on canvas curve.
There is a formula, at the end, to calculate the angle:
function getQuadraticAngle(t, sx, sy, cp1x, cp1y, ex, ey) {
var dx = 2*(1-t)*(cp1x-sx) + 2*t*(ex-cp1x);
var dy = 2*(1-t)*(cp1y-sy) + 2*t*(ey-cp1y);
return Math.PI / 2 - Math.atan2(dx, dy);
}
The x/y pairs that we pass, are the current position, the control point and the end position of the curve - exactly what is needed to pass to the canvas context, and t is a value from 0 to 1. Unless I somehow misunderstood the referenced article.
I want to do something very similar - place my markers over the distance specified s, rather than use t. This means, unless I am mistaken, that I need to calculate the length of the "curved path" and from there, I could probably use the above formula.
I found a solution for the length in JavaScript at length of quadratic curve. The formula is similar to:
.
And added the below function:
function quadraticBezierLength(x1, y1, x2, y2, x3, y3) {
let a, b, c, d, e, u, a1, e1, c1, d1, u1, v1x, v1y;
v1x = x2 * 2;
v1y = y2 * 2;
d = x1 - v1x + x3;
d1 = y1 - v1y + y3;
e = v1x - 2 * x1;
e1 = v1y - 2 * y1;
c1 = a = 4 * (d * d + d1 * d1);
c1 += b = 4 * (d * e + d1 * e1);
c1 += c = e * e + e1 * e1;
c1 = 2 * Math.sqrt(c1);
a1 = 2 * a * (u = Math.sqrt(a));
u1 = b / u;
a = 4 * c * a - b * b;
c = 2 * Math.sqrt(c);
return (
(a1 * c1 + u * b * (c1 - c) + a * Math.log((2 * u + u1 + c1) / (u1 + c))) /
(4 * a1)
);
}
Now, I am trying to space markers evenly. I thought that making "entropy" smooth - dividing the total length by the step length would result in the n markers, so going using the 1/nth step over t would do the trick. However, this does not work. The correlation between t and distance on the curve is not linear.
How do I solve the equation "backwards" - knowing the control point, the start, and the length of the curved path, calculate the end-point?
Not sure I fully understand what you mean by "space markers evenly" but I do have some code that I did with curves and markers that maybe can help you ...
Run the code below, it should output a canvas like this:
function drawBezierCurve(p0, p1, p2, p3) {
distance = 0
last = null
for (let t = 0; t <= 1; t += 0.0001) {
const x = Math.pow(1 - t, 3) * p0[0] + 3 * Math.pow(1 - t, 2) * t * p1[0] + 3 * (1 - t) * Math.pow(t, 2) * p2[0] + Math.pow(t, 3) * p3[0];
const y = Math.pow(1 - t, 3) * p0[1] + 3 * Math.pow(1 - t, 2) * t * p1[1] + 3 * (1 - t) * Math.pow(t, 2) * p2[1] + Math.pow(t, 3) * p3[1];
ctx.lineTo(x, y);
if (last) {
distance += Math.sqrt((x - last[0]) ** 2 + (y - last[1]) ** 2)
if (distance >= 30) {
ctx.rect(x - 1, y - 1, 2, 2);
distance = 0
}
}
last = [x, y]
}
}
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
drawBezierCurve([0, 0], [40, 300], [200, -90], [300, 150]);
ctx.stroke();
<canvas id="canvas" width=300 height=150></canvas>
I created the drawBezierCurve function, there I'm using a parametric equation of a bezier curve and then I use lineTo to draw between points, and also we get a distance between points, the points are very close so my thinking is OK to use the Pythagorean theorem to calculate the distance, and the markers are just little rectangles.
Related
I am trying to calculate the length of a quadratic Bezier curve. Found a solution and a formula to calculate the length of the entire curve in a "closed form" - without having to do numeric methods. The solution is covered at: quadratic Bezier curve length. The function is below:
function quadraticBezierLength(x1, y1, x2, y2, x3, y3) {
let a, b, c, d, e, u, a1, e1, c1, d1, u1, v1x, v1y;
v1x = x2 * 2;
v1y = y2 * 2;
d = x1 - v1x + x3;
d1 = y1 - v1y + y3;
e = v1x - 2 * x1;
e1 = v1y - 2 * y1;
c1 = a = 4 * (d * d + d1 * d1);
c1 += b = 4 * (d * e + d1 * e1);
c1 += c = e * e + e1 * e1;
c1 = 2 * Math.sqrt(c1);
a1 = 2 * a * (u = Math.sqrt(a));
u1 = b / u;
a = 4 * c * a - b * b;
c = 2 * Math.sqrt(c);
return (
(a1 * c1 + u * b * (c1 - c) + a * Math.log((2 * u + u1 + c1) / (u1 + c))) /
(4 * a1)
);
}
It seems to be working correctly. I actually need L(t) rather than the length of the complete curve. It is another task. However, fortunately, someone solved it before me, and shared at length of a quadratic curve segment. I wrote a function based on this solution:
function quadraticBezierArcLength(t, x1, y1, x2, y2, x3, y3) {
let a, b, c, d, e, e1, d1, v1x, v1y;
v1x = x2 * 2;
v1y = y2 * 2;
d = x1 - v1x + x3;
d1 = y1 - v1y + y3;
e = v1x - 2 * x1;
e1 = v1y - 2 * y1;
a = 4 * (d * d + d1 * d1);
b = 4 * (d * e + d1 * e1);
c = e * e + e1 * e1;
a = 4 * c * a - b * b;
c = 2 * Math.sqrt(c);
const bt = b / (2 * a),
ct = c / a,
ut = t + bt,
k = ct - bt ** 2;
return (
(Math.sqrt(a) / 2) *
(ut * Math.sqrt(ut ** 2 + k) -
bt * Math.sqrt(bt ** 2 + k) +
k *
Math.log((ut + Math.sqrt(ut ** 2 + k)) / (bt + Math.sqrt(bt ** 2 + k))))
);
}
If I am not mistaken, the implementation in code is correct. Yet, the length values are definitely wrong. The only time it returns what I expect is when t equals 0.
Is there an error in my quadraticBezierArcLength function, or the formula wrong? I want the length along the curve of a segment from the point with coordinates {x1, x2} to the point C(t). I would expect that when t === 1, the results of both functions are the same, at the very least.
You seem to have added two unnecessary lines to the calculation. Here's a fiddle where I have commented out those two lines, along with the function copied from the original answer.
//a = 4 * c * a - b * b;
//c = 2 * Math.sqrt(c);
The original answer also used the absolute function, which you have removed.
In the attached image.
How can we find the position values of point Q1 and Q2.
this is my code but i am not able to get the points correctly.
How can we find the position values of point Q1 and Q2.
dim u as vertex
u = CVertex(P3.x - P4.x , P3.y - P4.y , 0.0)
dim v as vertex
v = CVertex( -u.y , u.x , v.z)
dim t1 as vertex
t1 = CVertex( (P1.x - P2.x) * v.x , (P1.y - P2.y) * v.y , 0.0)
dim t2 as vertex
t2 = CVertex( (P1.x - P3.x) * v.x , (P1.y - P3.y) * v.y , 0.0)
dim t as vertex
t = CVertex( t1.x / t2.x , t1.y / t2.y , 0.0)
dim Q1 as vertex
Q1.x = P1.x + t.x * (P3.x - P1.x)
Q1.y = P1.y + t.y * (P3.y - P1.y)
We make some vectors - for example, P31 = P3 - P1 and so on.
Points Q1, Q2 might be expressed as
Q1 = P1 + t * P31
Q2 = P1 + t * P41
where t is parameter in range 0..1 (for t=1 Q1 coincides with P3, Q2 coincides with P4).
Vectors q2-q1 and p2-q1 must be parallel, because Q1Q2 segment contains P2. So vector product of these vectors is zero. We express vector product using given vector components and solve simple linear equation to get t parameter that provides needed conditions.
Python code:
def segintri(p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y):
p21x = p2x - p1x
p21y = p2y - p1y
p31x = p3x - p1x
p31y = p3y - p1y
p41x = p4x - p1x
p41y = p4y - p1y
p43x = p4x - p3x
p43y = p4y - p3y
t = (p43x * p21y - p43y*p21x) / (p43x * p31y - p43y*p31x)
q1 = (p1x + p31x*t, p1y + p31y*t)
q2 = (p1x + p41x*t, p1y + p41y*t)
return (q1, q2)
print(segintri(1, 5, 2, 2, 0, 0, 6, 0))
>>>((0.4, 2.0), (4.0, 2.0))
I have two points in 2D space as we can see in the figure to move from the blue point to the red points we can use the equation (1). Where b is a constant used to limit the shape of the logarithmic spiral, l is a random number in [−1,1], which is used to
control the indentation effect of the movement, D indicates the distance between blue points and the current point
I need another movement that can move from blue points to the red points like in the figure
You can use sinusoidal model.
For start point (X0, Y0) and end point (X1,Y1) we have vector end-start, determine it's length - distance between points L, and angle of vector Fi (using atan2).
Then generate sinusoidal curve for some standard situation - for example, along OX axis, with magnitude A, N periods for distance 2 * Pi * N:
Scaled sinusoid in intermediate point with parameter t, where t is in range 0..1 (t=0 corresponds to start point (X0,Y0))
X(t) = t * L
Y(t) = A * Sin(2 * N * Pi * t)
Then shift and rotate sinusoid using X and Y calculated above
X_result = X0 + X * Cos(Fi) - Y * Sin(Fi)
Y_result = Y0 + X * Sin(Fi) + Y * Cos(Fi)
Example Python code:
import math
x0 = 100
y0 = 100
x1 = 400
y1 = 200
nperiods = 4
amp = 120
nsteps = 20
leng = math.hypot(x1 - x0, y1 - y0)
an = math.atan2(y1 - y0, x1 - x0)
arg = 2 * nperiods* math.pi
points = []
for t in range(1, nsteps + 1):
r = t / nsteps
xx = r * leng
yy = amp * math.sin(arg * r)
rx = x0 + xx * math.cos(an) - yy * math.sin(an)
ry = y0 + xx * math.sin(an) + yy * math.cos(an)
points.append([rx, ry])
print(points)
Draw points:
I have two circles with the same radius that intersect each other, I need to move one of them to some X and Y values so that they intersect only at one point (so that they do not lie on each other anymore).
How to calculate X and Y, which need to move one of the circles?
One approach is to move each circle away from the other when an overlap is detected:
While the distance between the two centers is less than the sum of the two radii, move each circle a little bit away from the other.
# r0, r1, epsilon, Scalars representing radii and a small value
# c0, c1, center Points
# ((c1 - c0) / |c1 - c0|) normalized c0c1 vector away from c1
# ((c0 - c1) / |c1 - c0|) normalized c0c1 vector away from c0
while (r0 + r1) > |c1 - c0|:
c0 = c0 + ((c1 - c0) / |c1 - c0|) * epsilon
c1 = c1 + ((c0 - c1) / |c1 - c0|) * epsilon
Edit: Example class Point (python) for convenient arithmetic:
class point:
def __init__(self, x, y):
self.x = x
self.y = y
def __sub__(self, other) -> Vector:
return Vector(self.x - other.x, self.y - other.y)
...
float r // = radius
float A[2] //= coordinates of the center A of the first circle
float B[2] //= coordinates of the center B of the second circle
float AB[2];
float length_AB;
float B_new[2];
AB[0] = B[0] - A[0];
AB[1] = B[1] - A[1];
length_AB = math.sqrt( AB[0]*AB[0] + AB[1]*AB[1] );
AB[0] = 2 * r * AB[0] / length_AB;
AB[1] = 2 * r * AB[1] / length_AB;
B_new[0] = A[0] + AB[0];
B_new[1] = A[1] + AB[1];
I am trying to determine the point at which a line segment intersect a circle. For example, given any point between P0 and P3 (And also assuming that you know the radius), what is the easiest method to determine P3?
Generally,
find the angle between P0 and P1
draw a line at that angle from P0 at a distance r, which will give you P3
In pseudocode,
theta = atan2(P1.y-P0.y, P1.x-P0.x)
P3.x = P0.x + r * cos(theta)
P3.y = P0.y + r * sin(theta)
From the center of the circle and the radius you can write the equation describing the circle.
From the two points P0 and P1 you can write the equation describing the line.
So you have 2 equations in 2 unknowns, which you can solved through substitution.
Let (x0,y0) = coordinates of the point P0
And (x1,y1) = coordinates of the point P1
And r = the radius of the circle.
The equation for the circle is:
(x-x0)^2 + (y-y0)^2 = r^2
The equation for the line is:
(y-y0) = M(x-x0) // where M = (y1-y0)/(x1-x0)
Plugging the 2nd equation into the first gives:
(x-x0)^2*(1 + M^2) = r^2
x - x0 = r/sqrt(1+M^2)
Similarly you can find that
y - y0 = r/sqrt(1+1/M^2)
The point (x,y) is the intersection point between the line and the circle, (x,y) is your answer.
P3 = (x0 + r/sqrt(1+M^2), y0 + r/sqrt(1+1/M^2))
Go for this code..its save the time
private boolean circleLineIntersect(float x1, float y1, float x2, float y2, float cx, float cy, float cr ) {
float dx = x2 - x1;
float dy = y2 - y1;
float a = dx * dx + dy * dy;
float b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
float c = cx * cx + cy * cy;
c += x1 * x1 + y1 * y1;
c -= 2 * (cx * x1 + cy * y1);
c -= cr * cr;
float bb4ac = b * b - 4 * a * c;
// return false No collision
// return true Collision
return bb4ac >= 0;
}
You have a system of equations. The circle is defined by: x^2 + y^2 = r^2. The line is defined by y = y0 + [(y1 - y0) / (x1 - x0)]·(x - x0). Substitute the second into the first, you get x^2 + (y0 + [(y1 - y0) / (x1 - x0)]·(x - x0))^2 = r^2. Solve this and you'll get 0-2 values for x. Plug them back into either equation to get your values for y.
MATLAB CODE
function [ flag] = circleLineSegmentIntersection2(Ax, Ay, Bx, By, Cx, Cy, R)
% A and B are two end points of a line segment and C is the center of
the circle, % R is the radius of the circle. THis function compute
the closest point fron C to the segment % If the distance to the
closest point > R return 0 else 1
Dx = Bx-Ax;
Dy = By-Ay;
LAB = (Dx^2 + Dy^2);
t = ((Cx - Ax) * Dx + (Cy - Ay) * Dy) / LAB;
if t > 1
t=1;
elseif t<0
t=0;
end;
nearestX = Ax + t * Dx;
nearestY = Ay + t * Dy;
dist = sqrt( (nearestX-Cx)^2 + (nearestY-Cy)^2 );
if (dist > R )
flag=0;
else
flag=1;
end
end