Bézier curves/Intersections: Difference between revisions

Content added Content deleted
(→‎{{header|Modula-2}}: Removed the stop after four intersections (it seems inadvisable, given there might be "near-intersections").)
(→‎{{header|ATS}}: Changes to make the program more robust against duplicates. Similar to what was done to the D and Modula-2. Also now the parameters are sorted.)
Line 228: Line 228:
error_squared / tol <= length_squared * tol
error_squared / tol <= length_squared * tol
end
end

(* One might be curious why "t@ype" instead of "type". The answer is:
the notation "type" is restricted to types that take up the same
space as a C void-pointer, which includes ATS pointers, "boxed"
types, etc. A "t@ype" can take up any amount of space, and so
includes any type there is (except for linear types, which is a
whole other subject). For instance, "int", "double", unboxed
records, unboxed tuples, and so on. *)
fun {a, b : t@ype} (* A polymorphic template function. *)
list_any (pred : (a, b) -<cloref1> bool,
obj : a,
lst : List0 b)
: bool =
(* Does pred(obj, item) return true for any list item? Here the
<cloref1> notation means that pred is a CLOSURE of the ordinary
garbage-collected kind, such as functions tend implicitly to be
in Lisps, MLs, Haskell, etc. *)
case+ lst of
| NIL => false
| hd :: tl =>
if pred (obj, hd) then
true
else
list_any (pred, obj, tl)


fun
fun
find_intersection_parameters
find_intersection_parameters
(px : @(double, double, double),
(px : @(double, double, double),
py : @(double, double, double),
py : @(double, double, double),
qx : @(double, double, double),
qx : @(double, double, double),
qy : @(double, double, double),
qy : @(double, double, double),
tol : double)
tol : double,
: [m : nat | m <= 4] list (double, m) =
spacing : double)
: List0 double =
let
let
val px = bernstein2spower_degree2 px
val px = bernstein2spower_degree2 px
Line 242: Line 267:


fun
fun
within_spacing (t_candidate : double,
loop {n : nat | n <= 4}
t_in_list : double)
:<cloref1> bool =
abs (t_candidate - t_in_list) < spacing

fun
loop {n : nat}
(params : list (double, n),
(params : list (double, n),
n : int n,
n : int n,
workload : List0 (@(double, double)))
workload : List0 (@(double, double)))
: [m : nat | m <= 4] list (double, m) =
: List0 double =
if n = 4 then
case+ workload of
params
| NIL => params
else
| hd :: tl =>
case+ workload of
let
| NIL => params
val portionx = spower_portion_degree2 (px, hd)
| hd :: tl =>
and portiony = spower_portion_degree2 (py, hd)
let
in
val portionx = spower_portion_degree2 (px, hd)
if flat_enough (portionx, portiony, tol) then
and portiony = spower_portion_degree2 (py, hd)
let
val @(portionx0, _, portionx2) = portionx
in
if flat_enough (portionx, portiony, tol) then
and @(portiony0, _, portiony2) = portiony
let
val @(root0, root1) =
val @(portionx0, _, portionx2) = portionx
solve_linear_quadratic (@(portionx0, portionx2),
and @(portiony0, _, portiony2) = portiony
@(portiony0, portiony2),
val @(root0, root1) =
qx, qy)
in
solve_linear_quadratic (@(portionx0, portionx2),
@(portiony0, portiony2),
if 0.0 <= root0 && root0 <= 1.0 &&
qx, qy)
~list_any (within_spacing, root0, params) then
in
begin
if 0.0 <= root0 && root0 <= 1.0 then
if 0.0 <= root1 && root1 <= 1.0 &&
begin
~list_any (within_spacing, root1, params) then
if (n < 3) * (0.0 <= root1 && root1 <= 1.0) then
loop (root0 :: root1 :: params, n + 2, tl)
loop (root0 :: root1 :: params, n + 2, tl)
else
else
loop (root0 :: params, n + 1, tl)
loop (root0 :: params, n + 1, tl)
end
end
else if 0.0 <= root1 && root1 <= 1.0 &&
else if 0.0 <= root1 && root1 <= 1.0 then
~list_any (within_spacing, root1, params) then
loop (root1 :: params, n + 1, tl)
loop (root1 :: params, n + 1, tl)
else
else
loop (params, n, tl)
loop (params, n, tl)
end
end
else
else
let
let
val @(t0, t1) = hd
val @(t0, t1) = hd
val tmiddle = (0.5 * t0) + (0.5 * t1)
val tmiddle = (0.5 * t0) + (0.5 * t1)
val job1 = @(t0, tmiddle)
val job1 = @(t0, tmiddle)
and job2 = @(tmiddle, t1)
and job2 = @(tmiddle, t1)
in
in
loop (params, n, job1 :: job2 :: tl)
loop (params, n, job1 :: job2 :: tl)
end
end
end
end
in
in
loop (NIL, 0, @(0.0, 1.0) :: NIL)
loop (NIL, 0, @(0.0, 1.0) :: NIL)
Line 300: Line 331:
val qy = @(1.0, 2.0, 3.0)
val qy = @(1.0, 2.0, 3.0)
val tol = 0.001 (* "Flatness ratio" *)
val tol = 0.001 (* "Flatness ratio" *)
val t_list = find_intersection_parameters (px, py, qx, qy, tol)
val spacing = 0.0000001 (* Min. spacing between parameters. *)
val t_list = find_intersection_parameters (px, py, qx, qy,
tol, spacing)

(* For no particular reason, sort the intersections so they go
from top to bottom. *)
val t_list = list_vt2t (list_vt_reverse (list_mergesort t_list))
val () = println! ("From top to bottom:")


fun
fun
Line 320: Line 358:


{{out}}
{{out}}
<pre>From top to bottom:
<pre>(0.881023, 1.118975)
(0.654983, 2.854983)
(0.654983, 2.854983)
(-0.681024, 2.681025)
(-0.681024, 2.681025)
(-0.854982, 1.345016)</pre>
(-0.854982, 1.345016)
(0.881023, 1.118975)</pre>


=={{header|D}}==
=={{header|D}}==