How to resolve the algorithm Bézier curves/Intersections step by step in the Julia programming language
How to resolve the algorithm Bézier curves/Intersections step by step in the Julia programming language
Table of Contents
Problem Statement
You are given two planar quadratic Bézier curves, having control points
( − 1 , 0 ) , ( 0 , 10 ) , ( 1 , 0 )
{\displaystyle (-1,0),(0,10),(1,0)}
and
( 2 , 1 ) , ( − 8 , 2 ) , ( 2 , 3 )
{\displaystyle (2,1),(-8,2),(2,3)}
, respectively. They are parabolas intersecting at four points, as shown in the following diagram:
The task is to write a program that finds all four intersection points and prints their
( x , y )
{\displaystyle (x,y)}
coordinates. You may use any algorithm you know of or can think of, including any of those that others have used.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Bézier curves/Intersections step by step in the Julia programming language
This code implements a method to find the intersections of two planar parametric quadratic Bézier curves.
The method is based on a recursive subdivision strategy, which progressively divides the curves into smaller segments until the segments are small enough to be considered as intersecting if their bounding boxes overlap.
The code first defines a Point
struct to represent points in the plane, then a QuadSpline
struct to represent quadratic splines, and finally a QuadCurve
struct to represent planar parametric quadratic Bézier curves.
The subdivide!
function subdivides a quadratic spline or curve into two new quadratic splines or curves, by applying de Casteljau's algorithm.
The rectsoverlap
function checks if two rectangles overlap, and the testintersect
function checks if two quadratic curves intersect by checking if their bounding boxes overlap and if the overlap is small enough.
The seemsduplicate
function checks if a point is already in a list of points, within a certain tolerance.
The findintersects
function finds the intersections of two quadratic curves by repeatedly subdividing the curves until they are small enough to be considered as intersecting, and then checking if their bounding boxes overlap. If the bounding boxes overlap, the function checks if the intersection point is already in the list of intersections, and if not, adds it to the list.
The testintersections
function tests the findintersects
function by finding the intersections of two specific quadratic curves.
Here are the steps of the algorithm in more detail:
- The algorithm starts with the two input curves,
p
andq
. - The algorithm repeatedly subdivides the curves into smaller segments until the segments are small enough to be considered as intersecting if their bounding boxes overlap.
- To subdivide a curve, the algorithm uses the
subdivide!
function. Thesubdivide!
function takes a curve and two new curves as input, and it subdivides the input curve into two new curves. - To check if two curves intersect, the algorithm uses the
testintersect
function. Thetestintersect
function takes two curves and a tolerance as input, and it returnstrue
if the curves intersect andfalse
otherwise. - If the curves intersect, the algorithm adds the intersection point to the list of intersections.
- The algorithm repeats steps 2-5 until all of the curves have been subdivided into segments that are small enough to be considered as intersecting.
The findintersects
function returns a list of the intersection points of the two input curves.
Source code in the julia programming language
#= The control points of a planar quadratic Bézier curve form a
triangle--called the "control polygon"--that completely contains
the curve. Furthermore, the rectangle formed by the minimum and
maximum x and y values of the control polygon completely contain
the polygon, and therefore also the curve.
Thus a simple method for narrowing down where intersections might
be is: subdivide both curves until you find "small enough" regions
where these rectangles overlap.
=#
mutable struct Point
x::Float64
y::Float64
Point() = new(0, 0)
end
mutable struct QuadSpline # Non-parametric spline.
c0::Float64
c1::Float64
c2::Float64
QuadSpline(a = 0.0, b = 0.0, c = 0.0) = new(a, b, c)
end
mutable struct QuadCurve # Planar parametric spline.
x::QuadSpline
y::QuadSpline
QuadCurve(x = QuadSpline(), y = QuadSpline()) = new(x, y)
end
const Workset = Tuple{QuadCurve, QuadCurve}
""" Subdivision by de Casteljau's algorithm. """
function subdivide!(q::QuadSpline, t::Float64, u::QuadSpline, v::QuadSpline)
s = 1.0 - t
c0 = q.c0
c1 = q.c1
c2 = q.c2
u.c0 = c0
v.c2 = c2
u.c1 = s*c0 + t*c1
v.c1 = s*c1 + t*c2
u.c2 = s*u.c1 + t*v.c1
v.c0 = u.c2
end
function subdivide!(q::QuadCurve, t::Float64, u::QuadCurve, v::QuadCurve)
subdivide!(q.x, t, u.x, v.x)
subdivide!(q.y, t, u.y, v.y)
end
""" It is assumed that xa0 <= xa1, ya0 <= ya1, xb0 <= xb1, and yb0 <= yb1. """
function rectsoverlap(xa0, ya0, xa1, ya1, xb0, yb0, xb1, yb1)
return xb0 <= xa1 && xa0 <= xb1 && yb0 <= ya1 && ya0 <= yb1
end
""" This accepts the point as an intersection if the boxes are small enough. """
function testintersect(p::QuadCurve, q::QuadCurve, tol::Float64, intersect::Point)
pxmin = min(p.x.c0, p.x.c1, p.x.c2)
pymin = min(p.y.c0, p.y.c1, p.y.c2)
pxmax = max(p.x.c0, p.x.c1, p.x.c2)
pymax = max(p.y.c0, p.y.c1, p.y.c2)
qxmin = min(q.x.c0, q.x.c1, q.x.c2)
qymin = min(q.y.c0, q.y.c1, q.y.c2)
qxmax = max(q.x.c0, q.x.c1, q.x.c2)
qymax = max(q.y.c0, q.y.c1, q.y.c2)
exclude = true
accept = false
if rectsoverlap(pxmin, pymin, pxmax, pymax, qxmin, qymin, qxmax, qymax)
exclude = false
xmin = max(pxmin, qxmin)
xmax = min(pxmax, pxmax)
@assert xmin < xmax "Assertion failure: $xmax < $xmin"
if xmax-xmin <= tol
ymin = max(pymin, qymin)
ymax = min(pymax, qymax)
@assert ymin < ymax "Assertion failure: $ymax < $ymin"
if ymax-ymin <= tol
accept = true
intersect.x = 0.5*xmin + 0.5*xmax
intersect.y = 0.5*ymin + 0.5*ymax
end
end
end
return exclude, accept
end
""" return true if there seems to be a duplicate of xy in the intersects `isects` """
seemsduplicate(isects, xy, sp) = any(p -> abs(p.x-xy.x) < sp && abs(p.y-xy.y) < sp, isects)
""" find intersections of p and q """
function findintersects(p, q, tol, spacing)
intersects = Point[]
workload = Workset[(p, q)]
while !isempty(workload)
work = popfirst!(workload)
intersect = Point()
exclude, accept = testintersect(first(work), last(work), tol, intersect)
if accept
# To avoid detecting the same intersection twice, require some
# space between intersections.
if !seemsduplicate(intersects, intersect, spacing)
pushfirst!(intersects, intersect)
end
elseif !exclude
p0, p1, q0, q1 = QuadCurve(), QuadCurve(), QuadCurve(), QuadCurve()
subdivide!(first(work), 0.5, p0, p1)
subdivide!(last(work), 0.5, q0, q1)
pushfirst!(workload, (p0, q0), (p0, q1), (p1, q0), (p1, q1))
end
end
return intersects
end
""" test the methods """
function testintersections()
p, q = QuadCurve(), QuadCurve()
p.x = QuadSpline(-1.0, 0.0, 1.0)
p.y = QuadSpline(0.0, 10.0, 0.0)
q.x = QuadSpline(2.0, -8.0, 2.0)
q.y = QuadSpline(1.0, 2.0, 3.0)
tol = 0.0000001
spacing = tol * 10
for intersect in findintersects(p, q, tol, spacing)
println("($(intersect.x) $(intersect.y))")
end
end
testintersections()
You may also check:How to resolve the algorithm Padovan sequence step by step in the jq programming language
You may also check:How to resolve the algorithm 99 bottles of beer step by step in the Raven programming language
You may also check:How to resolve the algorithm Sum of elements below main diagonal of matrix step by step in the Mathematica/Wolfram Language programming language
You may also check:How to resolve the algorithm Identity matrix step by step in the LFE programming language
You may also check:How to resolve the algorithm Inheritance/Single step by step in the Component Pascal programming language