How to calculate width, height and position of bezier curve - position

I have a bezier curve defined by start point, end point and 2 control points (parameters of this: http://www.w3schools.com/tags/canvas_beziercurveto.asp).
First, I need to calculate width and height of this curve. If I make rectangle around a curve, its width and height is what I need.
Then I need to start point (x,y of left top corner) of this rectangle.
How can I calculate that ? Thanks.

For true bounds, you need to compute the extremities of the curve's component functions, then plug those into the bezier function for the (x,y) coordinates for each extremity. I cover this over at http://pomax.github.io/bezierinfo/#extremities, which also explains how to do most of the steps required to get there in the text leading up to the extremities section. paragraphs 11 and 12/13 then cover bounding boxes (plain, which you're probably interested in, and tight, respectively)

I found approximate solution in some other topic (I don't remember which one) but here is simple JS function to calculate it:
function getCurveBoundary(ax, ay, bx, by, cx, cy, dx, dy) {
var tobx = bx - ax;
var toby = by - ay;
var tocx = cx - bx;
var tocy = cy - by;
var todx = dx - cx;
var tody = dy - cy;
var step = 1 / 40; // precission
var d, px, py, qx, qy, rx, ry, tx, ty, sx, sy, x, y, i, minx, miny, maxx, maxy;
function min(num1, num2) {
if (num1 > num2)
return num2;
if (num1 < num2)
return num1;
return num1;
}
function max(num1, num2) {
if (num1 > num2)
return num1;
if (num1 < num2)
return num2;
return num1;
}
for (var i = 0; i < 41; i++)
{
d = i * step;
px = ax + d * tobx;
py = ay + d * toby;
qx = bx + d * tocx;
qy = by + d * tocy;
rx = cx + d * todx;
ry = cy + d * tody;
toqx = qx - px;
toqy = qy - py;
torx = rx - qx;
tory = ry - qy;
sx = px + d * toqx;
sy = py + d * toqy;
tx = qx + d * torx;
ty = qy + d * tory;
totx = tx - sx;
toty = ty - sy;
x = sx + d * totx;
y = sy + d * toty;
if (i == 0)
{
minx = x;
miny = y;
maxx = x;
maxy = y;
}
else
{
minx = min(minx, x);
miny = min(miny, y);
maxx = max(maxx, x);
maxy = max(maxy, y);
}
}
return {x: Math.round(minx), y: Math.round(miny), width: Math.round(maxx - minx), height: Math.round(maxy - miny)};
}

If you're looking for an approximate solution, it's pretty easy to compute a solution that's always big enough to cover the curve, but might be too big.
Beziers satisfy the 'convex hull property' which means that you can take a bounding box of your control points and that will bound the curve itself.
If you're looking for something more accurate, then the simplest way is to evaluate a bunch of different points on the curve and take the bounding box of those points on the curve. You can vary the number of points you test in order to change the quality/speed tradeoff.
If you're looking for something that directly computes the exact answer then what you need is a root finder to look for extrema of the functions x(t) and y(t).

Related

Drawing markers on a quadratic curve

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.

Is there a better way to generate points on a circle than trying out points in the equation x^2 + y^2 = r^2?

I have seen many apps which draw circles e.g pygame for python, p5.js for javascript. But I cannot find a method to find out points on a circle efficiently. My current solution to the problem involves trying out all the numbers in the square in which the circle can be inscribed.
This can't be the most efficient method to do it. What is the method used at the industry level? Does it involve optimization or is it a whole new method?
A Midpoint Circle Algorithm could be used.
And an implementation e.g. in C from rosettacode.org:
#define plot(x, y) put_pixel_clip(img, x, y, r, g, b)
void raster_circle(
image img,
unsigned int x0,
unsigned int y0,
unsigned int radius,
color_component r,
color_component g,
color_component b )
{
int f = 1 - radius;
int ddF_x = 0;
int ddF_y = -2 * radius;
int x = 0;
int y = radius;
plot(x0, y0 + radius);
plot(x0, y0 - radius);
plot(x0 + radius, y0);
plot(x0 - radius, y0);
while(x < y)
{
if(f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x + 1;
plot(x0 + x, y0 + y);
plot(x0 - x, y0 + y);
plot(x0 + x, y0 - y);
plot(x0 - x, y0 - y);
plot(x0 + y, y0 + x);
plot(x0 - y, y0 + x);
plot(x0 + y, y0 - x);
plot(x0 - y, y0 - x);
}
}

Circle Rasterization Algorithm - center between pixels

I have a problem where I have to select all squares (think pixels) that are partially within a circle (even if the circle only cuts through a small corner of the square, but not if it goes through one of the corner vertices). The radius is an integer multiple of the pixel size.
The problem is that the center of the circle is between pixels, i.e. on the corner vertices of four pixels.
I want to visit each pixel only once.
For example, I would like to select all white pixels in the following images:
R = 8 px
R = 10 px
For a circle with the center in the center of a pixel, this wouldn't be a problem, and I could use the usual form of the Bresenham algorithm:
public void checkCircle(int x0, int y0, int radius) {
int x = radius;
int y = 0;
int err = -x;
while (x > 0) {
while (err <= 0) {
y++;
err += 2 * y + 1;
}
checkVLine(x0 + x, y0 - y, y0 + y);
checkVLine(x0 - x, y0 - y, y0 + y);
x--;
err -= 2 * x + 1;
}
checkVLine(x0, y0 - radius, y0 + radius);
}
public void checkVLine(int x, int y0, int y1) {
assert(y0 <= y1);
for (int y = y0; y <= y1; y++)
checkPixel(x, y);
}
Sadly, I don't see how to adapt it to support inter-pixel circles.
For the first quadrant - cell should be marked if its left bottom corner lies inside circle, so you can rasterize with simple loops
for dy = 0 to R-1
dx = 0
sq = R * R - dy * dy
while dx * dx < sq
mark (dx, dy)
mark (dx, -dy-1)
mark (-dx-1, dy)
mark (-dx-1, -dy-1)
To fill whole horizontal lines, you can calculate max value for dx
for dy = 0 to R-1
mdx = Floor(Sqrt(R * R - dy * dy))
fill line (-mdx-1,dy)-(mdx,dy)
fill line (-mdx-1,-dy-1)-(mdx,-dy-1)

Processing: Distance of intersection between line and circle

Now, I know similar questions have been asked. But none of the answers has helped me to find the result I need.
Following situation:
We have a line with a point-of-origin (PO), given as lx, ly. We also have an angle for the line in that it exits PO, where 0° means horizontally to the right, positive degrees mean clockwise. The angle is in [0;360[. Additionally we have the length of the line, since it is not infinitely long, as len.
There is also a circle with the given center-point (CP), given as cx, cy. The radius is given as cr.
I now need a function that takes these numbers as parameters and returns the distance of the closest intersection between line and circle to the PO, or -1 if no intersection occures.
My current approach is a follows:
float getDistance(float lx, float ly, float angle, float len, float cx, float cy, float cr) {
float nlx = lx - cx;
float nly = ly - cy;
float m = tan(angle);
float b = (-lx) * m;
// a = m^2 + 1
// b = 2 * m * b
// c = b^2 - cr^2
float[] x_12 = quadraticFormula(sq(m) + 1, 2*m*b, sq(b) - sq(cr));
// if no intersections
if (Float.isNaN(x_12[0]) && Float.isNaN(x_12[1]))
return -1;
float distance;
if (Float.isNaN(x_12[0])) {
distance = (x_12[1] - nlx) / cos(angle);
} else {
distance = (x_12[0] - nlx) / cos(angle);
}
if (distance <= len) {
return distance;
}
return -1;
}
// solves for x
float[] quadraticFormula(float a, float b, float c) {
float[] results = new float[2];
results[0] = (-b + sqrt(sq(b) - 4 * a * c)) / (2*a);
results[1] = (-b - sqrt(sq(b) - 4 * a * c)) / (2*a);
return results;
}
But the result is not as wished. Sometimes I do get a distance returned, but that is rarely correct, there often isn't even an intersection occuring. Most of the time no intersection is returned though, although there should be one.
Any help would be much appreciated.
EDIT:
I managed to find the solution thanks to MBo's answer. Here is the content of my finished getDistance(...)-function - maybe somebody can be helped by it:
float nlx = lx - cx;
float nly = ly - cy;
float dx = cos(angle);
float dy = sin(angle);
float[] results = quadraticFormula(1, 2*(nlx*dx + nly*dy), sq(nlx)+sq(nly)-sq(cr));
float dist = -1;
if (results[0] >= 0 && results[0] <= len)
dist = results[0];
if (results[1] >= 0 && results[1] <= len && results[1] < results[0])
dist = results[1];
return dist;
Using your nlx, nly, we can build parametric equation of line segment
dx = Cos(angle)
dy = Sin(Angle)
x = nlx + t * dx
y = nly + t * dy
Condition of intersection with circumference:
(nlx + t * dx)^2 + (nly + t * dy)^2 = cr^2
t^2 * (dx^2 + dy^2) + t * (2*nlx*dx + 2*nly*dy) + nlx^2+nly^2-cr^2 = 0
so we have quadratic equation for unknown parameter t with
a = 1
b = 2*(nlx*dx + nly*dy)
c = nlx^2+nly^2-cr^2
solve quadratic equation, find whether t lies in range 0..len.
// https://openprocessing.org/sketch/8009#
// by https://openprocessing.org/user/54?view=sketches
float circleX = 200;
float circleY = 200;
float circleRadius = 100;
float lineX1 = 350;
float lineY1 = 350;
float lineX2, lineY2;
void setup() {
size(400, 400);
ellipseMode(RADIUS);
smooth();
}
void draw() {
background(204);
lineX2 = mouseX;
lineY2 = mouseY;
if (circleLineIntersect(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleRadius) == true) {
noFill();
}
else {
fill(255);
}
ellipse(circleX, circleY, circleRadius, circleRadius);
line(lineX1, lineY1, lineX2, lineY2);
}
// Code adapted from Paul Bourke:
// http://local.wasp.uwa.edu.au/~pbourke/geometry/sphereline/raysphere.c
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;
//println(bb4ac);
if (bb4ac < 0) { // Not intersecting
return false;
}
else {
float mu = (-b + sqrt( b*b - 4*a*c )) / (2*a);
float ix1 = x1 + mu*(dx);
float iy1 = y1 + mu*(dy);
mu = (-b - sqrt(b*b - 4*a*c )) / (2*a);
float ix2 = x1 + mu*(dx);
float iy2 = y1 + mu*(dy);
// The intersection points
ellipse(ix1, iy1, 10, 10);
ellipse(ix2, iy2, 10, 10);
float testX;
float testY;
// Figure out which point is closer to the circle
if (dist(x1, y1, cx, cy) < dist(x2, y2, cx, cy)) {
testX = x2;
testY = y2;
} else {
testX = x1;
testY = y1;
}
if (dist(testX, testY, ix1, iy1) < dist(x1, y1, x2, y2) || dist(testX, testY, ix2, iy2) < dist(x1, y1, x2, y2)) {
return true;
} else {
return false;
}
}
}

Line Segment Circle Intersection

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

Resources