Is it accurate to conclude the radius of a circle given 4 bazier curves in svg? - svg

I have used svg2paths2, and wanted to figure out what is the position and radius of a circle, I have noticed the circle is consructed by 4 CubicBezier, as follow:
Path(CubicBezier(start=(127.773+90.5469j), control1=(127.773+85.7656j), control2=(123.898+81.8906j), end=(119.121+81.8906j)),
CubicBezier(start=(119.121+81.8906j), control1=(114.34+81.8906j), control2=(110.465+85.7656j), end=(110.465+90.5469j)),
CubicBezier(start=(110.465+90.5469j), control1=(110.465+95.3281j), control2=(114.34+99.1992j), end=(119.121+99.1992j)),
CubicBezier(start=(119.121+99.1992j), control1=(123.898+99.1992j), control2=(127.773+95.3281j), end=(127.773+90.5469j)))
I have read the standard approach is to divide the circle into four equal sections, and fit each section to a cubic Bézier curve.
So I was wondering is it accurate to say the Radius of the circle is
(q1.start.real - q3.start.real)/2
or
(q2.start.imag - q4.start.imag)/2
And the center of the circle is:
c_x = (q1.start.real + q1.end.real) / 2
c_y = (q1.start.imag + q1.end.imag) / 2
Thank you!

I'm assuming you are using svg.path library in python, or svg2paths2 is related.
from svg.path import Path, Line, Arc, CubicBezier, QuadraticBezier, Close
path = Path(CubicBezier(start=(127.773+90.5469j), control1=(127.773+85.7656j), control2=(123.898+81.8906j), end=(119.121+81.8906j)),
CubicBezier(start=(119.121+81.8906j), control1=(114.34+81.8906j), control2=(110.465+85.7656j), end=(110.465+90.5469j)),
CubicBezier(start=(110.465+90.5469j), control1=(110.465+95.3281j), control2=(114.34+99.1992j), end=(119.121+99.1992j)),
CubicBezier(start=(119.121+99.1992j), control1=(123.898+99.1992j), control2=(127.773+95.3281j), end=(127.773+90.5469j)))
q1 = path[0]
q2 = path[1]
q3 = path[2]
q4 = path[3]
.real is the X coordinate
.imag is the Y coordinate
There's a very slight error in accuracy in the drawing program you are using and it's not at all an issue unless you want extreme accuracy.
(q1.start.real - q3.start.real) / 2 # 8.6539 is the radius in this case.
(q4.start.imag - q2.start.imag)/2 # 8.6543 is also the radius.
(q1.start.real - q1.end.real) # 8.6539 is again also the radius.
This accesses the same property, q1 of path and I' prefer it to the two above ways because it's accessing one property not two.
Below shown by green circle in diagram
c_x = (q1.start.real + q1.end.real) / 2 # 123.447 not center x
c_y = (q1.start.imag + q1.end.imag) / 2 # 86.21875 not center y
Below shown by red circle in diagram
c_x = q1.end.imag # 119.121 this is center x
c_y = q1.start.real # 90.5469 this is center y
To explain how serious the error in accuracy, the pink circle uses 8.6543 radius, below it is 8.6539 in green, perhaps viewable with an extreme zoom. But this does illustrate how important or not the decimal points can be.
Consider using numbers under 100 and as few decimal points as possible, especially understanding a new idea. Shorter text-length numbers vastly improves readability, understanding no end.
I often use just numbers below ten.
Note: you are drawing the circle counter-clockwise. Clockwise is the usual way.

Related

Calculate the number of circles that fit on the circumference of another circle

I'm looking for an algorithm (or pseudo code) that can calculate the maximum number of (smaller) circles with diameter "s" that can be squeezed into the circumference of another (larger) circle with radius "r" ...
Image: http://teasy.space/images/terracolony-squeezingcircles2.jpg
You can alternate between radius/diameter etc if you wish -- as these are the only 2 parameters (other than the center (large circle) coordinate) that i have, i.e. that are known ...
The outer circles may not overlap but can fit "snug" together ...
After various upgrades to my routine through the years, I'm currently using an algorithm that is not perfect (and it needs to be accurate or the galaxy breaks down lol)
which does a broad interpolation between small outside circle diameter and large inside circle circumference, to somewhat accurately plot the circle count in a polygon style fitting pattern, which causes problems (i.e. overlaps) when using larger outside circles ...
; try to fit a random number of circles
num_Circles = Rand( min,max )
; check if the number of circles exceed the maximum that can fit
If num_Circles * SmallCircle_Diameter > LargeCircle_Circumference
; adjust the amount accordingly
num_Circles = LargeCircle_Circumference / SmallCircle_Diameter
End If
Another assumption is that the size of the smaller outer circles never exceeds that of the larger inner circle ...
something less to worry about ;)
I'm using this algorithm for one of my projects called Terra Colony, based on Gravity Well, a 2D space/gravity realtime colonization simulation game with moons, planets, stars, black/white holes, etc
Image: http://teasy.space/images/terracolony-squeezingcircles1.jpg
This is an issue that has plagued this project for over a decade!
Hopefully you can point me in the right direction :D
I have previously done many experiments and wrote different programs to find a solution, and have traveled the internet looking for formulas and solutions which in the end are very close, but not close enough! :P
Thank you! <3
Teasy
P.S. I tried to add the tag "circumference" but it apparently requires "1500 reputation" (points i guess, maybe to prevent spam)
There is formula that establishes relation between radius of big circle R, radius of small circle r and number of (touching) small circles N
R = r / Sin(Pi/N)
So maximum number of small circles might be found as
Sin(Pi/N) = r / R
Pi / N = arcsin(r / R)
and finally
N = Pi / arcsin(r / R)
Example:
R=5
r=2.5
so
N = Pi / arcsin(1/2) =
Pi / (Pi/6) =
6
Given the diam. of the small circle 'd' and the number of them 'c'
then the dia. of the large circle 'D' is
D=d/sin(180/c)

Drawing a circle without using a function for it

So I was wondering how does a circle() function work, and how can I draw to circle without using it (wanted to do something related to it). Does anyone know this stuff?
A classic way of rasterizing a circle is using the Midpoint Circle Algorithm.
It works by tracking the pixels which are as close to the x2 + y2 = r2 isoline as possible. This can even be done with purely integer calculations, which is particularly suitable for low-computation power devices.
A circle is the set of points located at a constant distance from another point, called the center.
If you can draw lines defined by two points, you can draw the representation of a circle on a canvas, knowing its center, and its radius.
The approach is to determine a set of consecutive points located on the circumference, then join them with lines.
for instance, in python (which reads like pseudocode):
import math
def make_circle(center, radius, num_points=40):
"""returns a sequence of points on the circumference
"""
points = [center]
d_theta = 2 * math.pi / num_points
cx, cy = center
for idx in range(num_points + 1):
theta = idx * d_theta
points.append((cx + math.cos(theta) * radius, cy + math.sin(theta) * radius))
return points
And if you want to try it, here it is: circles codeskulptor.
You will see that for display purposes, 40 points on the circumference is enough to give an acceptable rendition.

How to know if a point belongs more or less to a circle?

I know the formula to know if a point is inside, outside and on a circle : https://math.stackexchange.com/q/198769 This quote explains that we must compare d to r (please read the quote, it's only 5 lines).
But I just want to know if a point is ON a circle. Moreover, and that's the real problem : if a point is a bit inside/outside a circle, I want to consider it as ON the circle.
How could I do that ? I tried to delimit d-r (ie. : the comparison) in a range. Example :
if(d-r > -100 && d-r < 100) { point is on the circle }
It works, with -100 and 100, for circles with a little radius (ie. : ALL the points that are a bit outside/inside the circle are considered as being on the circle).
But for circles for a big radius, only SOME points are considered as being on the circle (ie. : only some of the points that are a bit outside/inside the circle are considered as being on the circle)...
So I would want that ALL the points that are a bit outside/inside the circle are considered as being on the circle, independently of the circle's radius. How ?
Your comparison for absolute difference might be written shorter as
if Abs(d - r) < delta (i.e. 100) ...
But seems you need relative difference depending on circle radius like this:
if Abs(d - r) / r < reldelta (i.e. 0.001) ...
From a probabilistic perspective, you could define a sort of distance map (as proposed by #Mbo) adopting the relative distance and use it to build a probability distribution on each point. The probability would represent a sort of likelihood of the point to belong to the circle. Intuitively, the closer the point, the more likely it is to be part of the circle. For example:
rel_d = (d-r)/r;
// P(x on the circle) = 1 - rel_d
if(rel_d < 1){
P_on_circle = 1 - rel_d;
}else{
P_on_circle = 0;
}

reconstructing circles from Bezier curves

I am trying to reconstruct original graphics primitives from Postscript/SVG paths. Thus an original circle is rendered (in SVG markup) as:
<path stroke-width="0.5" d="M159.679 141.309
C159.679 141.793 159.286 142.186 158.801 142.186
C158.318 142.186 157.925 141.793 157.925 141.309
C157.925 140.825 158.318 140.432 158.801 140.432
C159.286 140.432 159.679 140.825 159.679 141.309" />
This is an approximation using 4 Beziers curves to create a circle.In other places circular arcs are approximated by linked Bezier curves.
My question is whether there is an algorithm I can use to recognize this construct and reconstruct the "best" circle. I don't mind small errors - they will be second-order at worst.
UPDATE: Note that I don't know a priori that this is a circle or an arc - it could be anything. And there could be 2, 3 4 or possibly even more points on the curve. So I'd really like a function of the sort:
error = getCircleFromPath(path)
where error will give an early indication of whether this is likely to be a circle.
[I agree that if I know it's a circle it's an easier problem.]
UPDATE: #george goes some way towards answering my problem but I don't think it's the whole story.
After translation to the origin and normalization I appear to have the following four points on the curve:
point [0, 1] with control point at [+-d,1] // horizontal tangent
point [1, 0] with control point at [1,+-d] // vertical tangent
point [0, -1] with control point at [+-d,-1] // horizontal tangent
point [-1, 0] with control point at [-1,+-d] // vertical tangent
This guarantees that the tangent at each point is "parallel" to the path direction at the point. It also guarantees the symmetry (4-fold axis with reflection. But it does not guarantee a circle. For example a large value of d will give a rounded box and a small value a rounded diamond.
My value of d appears to be about 0.57. This might be 1/sqrt(3.) or it might be something else.It is this sort of relationship I am asking for.
#george gives midpoint of arc as;
{p1,(p1 + 3 (p2 + p3) + p4)/8,p4}
so in my example (for 1,0 to 0,1) this would be:
[[1,0]+3[1,d]+3[d,1]+[0,1]] / 8
i.e.
[0.5+3d/8, 3d/8+0.5]
and if d =0.57, this gives 0.71, so maybe d is
(sqrt(0.5)-0.5)*8./3.
This holds for a square diamond, but for circular arcs the formula must be more general and I'd be grateful if anyone has it. For example, I am not familiar with Bezier math, so #george's formula was new to me
enter code here
Without doing all the math for you.. this may help:
there are always 4 control points on a bezier.
Your curve is 4 beziers linked together with points 1-4 , 4-7 , 7-10 , and 10-13 the control points
for each part. Points 1 , 4 , 7 and 10 (&13==1) lie exactly on the curve. To see if you have a nice circle calculate:
center = ( p1+p7 )/2 =( {159.679, 141.309} + {157.925, 141.309} ) / 2
= {158.802, 141.309}
verify you get the same result using points 4+10 -> {158.801, 141.309}
Once you know the center you can sample points along the curve and see if you have a constant distance.
If you only have a single bezier arc with 4 points a useful formula is that the midpoint is at
(p1 + 3 (p2 + p3) + p4)/8. So you can find the circle passing through three points:
{p1,(p1 + 3 (p2 + p3) + p4)/8,p4}
and again sample other points on the curve to decide if you indeed have a near circular arc.
Edit
the bezier formula is this:
x=(1-t)^3 p1 + 3 (1-t)^2 t p2 + 3 (1-t) t^2 p3 + t^3 p4 with parameter 0 < t < 1
so for example at t=1/4 you have
x=( 27 p1 + 27 p2 + 9 p3 + 1 p4 ) / 64
so once you find the center you can readily check a few points and calculate their distance.
I suspect if you only want to detect nearly exact circular arcs then checking two extra points with a tight tolerance will do the job. If you want to detect things that are approximately circular I would compute a bunch of points and use the average error as a criteria.
If all your elements are circle-like then you can just get the dimensions through path.getBBox() and generate a circle from there. In this case I'm considering ellipses, but you can easily translate it to actual circle elements:
var path = document.getElementById("circle_path");
var bbox = path.getBBox();
var rx = bbox.width/2;
var ry = bbox.height/2;
var cx = bbox.x + rx;
var cy = bbox.y + ry;
var ellipse = document.createElementNS(xmlns, "ellipse");
ellipse.setAttribute("fill", "none");
ellipse.setAttribute("stroke", "red");
ellipse.setAttribute("stroke-width", 0.1);
ellipse.setAttribute("cx", cx);
ellipse.setAttribute("cy", cy);
ellipse.setAttribute("rx", rx);
ellipse.setAttribute("ry", ry);
svg.appendChild(ellipse);
You can see a demo here:
http://jsfiddle.net/nwHm6/
The endpoints of the Bézier curves are probably on the circle. If so, it's easy to reconstruct the original circle.
Another possibility is to take the barycenter of the control points as the center of the circle because the control points are probably laid out symmetrically around the center. From the center, you get the radius as the average distance of the four control points closest to the center.
One can define an ellipse as a unit circle centred on (0,0), translated (2 params), scaled (2 params), and rotated (1 param). So on each arc take five points (t=0 ¼ ½ ¾ 1) and solve for these five parameters. Next take the in-between four points (t=⅛ ⅜ ⅝ ⅞), and test whether these lie on the same transformed circle. If yes, whoopee!, this is (part of) a transformed circle.
Immediately before and after might be another arc or arcn. Are these the same ellipse? If yes, and the subtended angles touch, then join together your descriptions of the pieces.

Finding internal angles of polygon

I have some lines that their intersection describes a polygon, like this:
I know the order of the lines, and their equations.
To find the internal angles, I found each lines orientations. But I've got confused as subtracting two lines orientation would give two different angles, even if I do it in the order of polygon's sides.
For example, in the following image, if I just subtract the orientation of the lines, I would get any of the following angles:
What made me more confused, is when the polygon is not convex, I will have angles greater than 180, and using my approach I don't get the correct angle at all:
And I found out that this way of approaching the problem is wrong.
So, What is the best way of finding the internal angles using just the lines? I know for a convex polygon, I may find vectors and then find the angle between them, but even for P6 in my example the vector approach fails.
Anyway, I prefer a method that won't include a conditional case for solving that concavity problem.
Thanks.
With ordered lines it is possible to find points of intersection (polygon vertexes) in clockwise order. Then you can calculate internal angles:
Angle[i] = Pi + ArcTan2(V[i] x V[i+1], V[i] * V[i+1])
(crossproduct and dotproduct of incoming and outgoing vectors for every vertex)
or
Angle[i] = Pi + ArcTan2( dx_in*dy_out-dx_out*dy_in, dx_in*dx_out+dy_in*dy_out2 )
Note: change plus sign after Pi to minus for anti-clockwise direction.
Edit:
Note that crossproduct and dotproduct are scalars, not vectors.
Example for your data:
dx1 = 5; dy1 = -15; dx2 = -15; dy2 = 5
Angle = Pi + ArcTan2(5*5-15*15, -5*15-5*15) = Pi - 2.11 radians ~ 59 degrees
Example for vectors:
(0,-1) (1,0) (L-curve)
Angle = Pi + ArcTan2(1, 0) = 270 degrees

Resources