\documentclass{article} %include lhs2TeX.fmt \begin{document} \begin{code} {-# LANGUAGE FlexibleContexts, UnboxedTuples, BangPatterns, NamedFieldPuns, RecordWildCards, MagicHash, CPP #-} -- | This module contains the basic functions for manipulating Bezier curves. It is heavily -- based on the book by N. M. Patrikalakis and T. Maekawa, Shape Interrogation for Computer -- Aided Design and Manufacturing. module Graphics.Typography.Geometry.Bezier ( Curve(..),line,bezier3, offset, inter, evalCurve,distance, left,bottom,right,top) where import Algebra.Polynomials.Bernstein import Algebra.Polynomials.Numerical import Graphics.Typography.Geometry import qualified Data.Vector as V import qualified Data.Vector.Unboxed as UV import Data.List (partition,sort) -- | The type for representing all types of curves. data Curve= Bezier { cx::Bernsteinp Int Double, cy::Bernsteinp Int Double, t0::Double, t1::Double } | Offset { cx::Bernsteinp Int Double, cy::Bernsteinp Int Double, t0::Double, t1::Double, matrix::Matrix2 Double } | Circle { cx0::Double, cy0::Double, t0::Double, t1::Double, matrix::Matrix2 Double } deriving (Show) -- | The basic constructor for lines : a line is a degree 1 Bezier curve line::Double->Double->Double->Double->Curve line px py px' py'=Bezier { cx=Bernsteinp 2 $ UV.fromList [px,px'], cy=Bernsteinp 2 $ UV.fromList [py,py'], t0=0,t1=1 } -- | A shortcut to define degree 3 Bezier curves from points. If the control -- points are @a,b,c,d@, the function should be called with -- @'bezier3' xa ya xb yb xc yc xd yd@. bezier3::Double->Double->Double->Double->Double->Double->Double->Double->Curve bezier3 px0 py0 px1 py1 px2 py2 px3 py3= Bezier { cx=Bernsteinp 4 $ UV.fromList [px0,px1,px2,px3], cy=Bernsteinp 4 $ UV.fromList [py0,py1,py2,py3], t0=0,t1=1 } instance Geometric Curve where translate x y cur@(Circle{cx0,cy0})= cur { cx0=cx0+x,cy0=cy0+y } translate x y cur= cur { cx=(cx cur) { coefs=UV.map (+x) $ coefs $ cx cur}, cy=(cy cur) { coefs=UV.map (+y) $ coefs $ cy cur} } apply m0@(Matrix2 a b c d) cir@(Circle{cx0,cy0,matrix})= cir { cx0=a*cx0+b*cy0, cy0=c*cx0+d*cy0, matrix=m0*matrix } apply (Matrix2 a b c d) cur= cur { cx=(scale a $ cx cur)+(scale b $ cy cur), cy=(scale c $ cx cur)+(scale d $ cy cur) } -- | Gives the point corresponding to the given value of the parameter evalCurve::Curve->Interval->(Interval,Interval) evalCurve (Offset{..}) t= let ix=intervalize cx iy=intervalize cy xt=eval ix t yt=eval iy t m@(Matrix2 a b c d)=intervalize matrix (Matrix2 a_ b_ c_ d_)=inverse m xt0'=eval (derivate ix) t yt0'=eval (derivate iy) t xt'=a_*xt0' + b_*yt0' yt'=c_*xt0' + d_*yt0' dd=sqrt $ xt'*xt' + yt'*yt' in (xt+(a*yt'-b*xt')/dd, yt+(c*yt'-d*xt')/dd) evalCurve (Circle{..}) alpha= let xx=cos alpha yy=sin alpha (Matrix2 a b c d)=intervalize matrix in (interval cx0+a*xx+b*yy, interval cy0+c*xx+d*yy) evalCurve (Bezier{..}) t= let ix=intervalize cx iy=intervalize cy xx=eval ix t yy=eval iy t in (xx,yy) data Topo=Dehors | SurLaLigne | Dedans deriving Eq -- | @'inter' c0 c1@ is a list of all possible points of intersection -- between curves @c0@ and @c1@ : if @(u,v,w,x)@ is returned by 'inter', -- then curve @c0@ may intersect with @c1@ between parameter values @u@ -- and @v@, which corresponds to parameter values between @w@ and @x@ for -- @c1@. The implementation guarantees that all actual solutions are found, -- but possibly false solutions may also be returned. inter::Curve->Curve->[((Double,Double,Double,Double))] inter op@(Offset { cx=bxp_,cy=byp_,matrix=mp,t0=t0a,t1=t1a }) (Offset { cx=bxq_,cy=byq_,matrix=mq,t0=t0b,t1=t1b })= -- Attention : verifier si c'est la meme generatrice let thrx=1e-5 solutions _ []=[] solutions thr boxes@(_:_)= let sol0=concatMap (solve thr (V.fromList [eq0,eq1,eq2,eq3])) boxes (correct,toRefine)=partition (\(u,v,_,_,_,_,_,_)-> let (xu,yu)=evalCurve op (Interval u u) (xv,yv)=evalCurve op (Interval v v) in (iup $ (xu-xv)^(2::Int)+(yu-yv)^(2::Int))<=thrx) sol0 in correct++(solutions (thr/2) toRefine) in map (\(u,v,w,x,_,_,_,_)->(u,v,w,x)) $ solutions 1e-2 $ [(t0a,t1a,t0b,t1b,0,1,0,1):: (Double,Double,Double,Double,Double,Double,Double,Double)] where imp@(Matrix2 ap bp cp dp)=intervalize mp imq@(Matrix2 aq bq cq dq)=intervalize mq (Matrix2 ap_ bp_ cp_ dp_)=inverse imp (Matrix2 aq_ bq_ cq_ dq_)=inverse imq bxp=intervalize bxp_ byp=intervalize byp_ bxq=intervalize bxq_ byq=intervalize byq_ bxp4=promote 1 bxp byp4=promote 1 byp bxq4=promote 2 bxq byq4=promote 2 byq bxp'=derivate bxp byp'=derivate byp bxq'=derivate bxq byq'=derivate byq bXp'=promote 1 $ (scale ap_ bxp')+(scale bp_ byp') bYp'=promote 1 $ (scale cp_ bxp')+(scale dp_ byp') bXq'=promote 2 $ (scale aq_ bxq')+(scale bq_ byq') bYq'=promote 2 $ (scale cq_ bxq')+(scale dq_ byq') bomp@(Bernsteinp _ omegap)=(bXp'*bXp')+(bYp'*bYp') :: Bernsteinp (Int,Int,Int,Int) Interval bomq@(Bernsteinp _ omegaq)=(bXq'*bXq')+(bYq'*bYq') :: Bernsteinp (Int,Int,Int,Int) Interval au= let au_=sqrt $ UV.minimum $ UV.map ilow omegap in max 0 $ fpred au_ bu= let bu_=sqrt $ UV.maximum $ UV.map iup omegap in fsucc bu_ av= let av_=sqrt $ UV.minimum $ UV.map ilow omegaq in max 0 $ fpred av_ bv= let bv_=sqrt $ UV.maximum $ UV.map iup omegaq in fsucc bv_ alphau=Bernsteinp (1,1,2,1) $ UV.fromList [Interval au au,Interval bu bu] alphav=Bernsteinp (1,1,1,2) $ UV.fromList [Interval av av,Interval bv bv] eq0= ((bxp4*alphau*alphav) + (scale ap $ bYp'*alphav) - (scale bp $ bXp'*alphav) -(bxq4*alphau*alphav) - (scale aq $ bYq'*alphau) + (scale bq $ bXq'*alphau)) eq1= ((byp4*alphau*alphav) + (scale cp $ bYp'*alphav) - (scale dp $ bXp'*alphav) -(byq4*alphau*alphav) - (scale cq $ bYq'*alphau) + (scale dq $ bXq'*alphau)) eq2=bomp-(alphau*alphau) eq3=bomq-(alphav*alphav) inter b@(Circle{}) a@(Offset{})= map (\(i,j,k,l)->(k,l,i,j)) $ inter a b inter o@(Offset { cx=bxp, cy=byp, matrix=mp }) cir@(Circle{cx0,cy0,matrix=mq})= let ix=intervalize bxp iy=intervalize byp m@(Matrix2 a b c d)=intervalize mp (Matrix2 a_ b_ c_ d_)=inverse m x'=derivate ix y'=derivate iy xx'=(scale a_ x')+(scale b_ y') yy'=(scale c_ x')+(scale d_ y') omega@(Bernsteinp _ omegap)=xx'*xx'+yy'*yy' au= let au_=sqrt $ UV.minimum $ UV.map ilow omegap in max 0 $ fpred au_ bu= let bu_=sqrt $ UV.maximum $ UV.map iup omegap in fsucc bu_ alphau=Bernsteinp (1,2) $ UV.fromList [Interval au au,Interval bu bu] lambda=(promote 1 omega) - alphau*alphau -- Avant multiplication par M_C^-1 xx0=(promote 1 $ ix-(intervalize $ constant cx0))*alphau +(promote 1 $ scale a yy'-scale b xx') yy0=(promote 1 $ iy-(intervalize $ constant cy0))*alphau +(promote 1 $ scale c yy'-scale d xx') :: Bernsteinp (Int,Int) Interval (Matrix2 ac_ bc_ cc_ dc_)=inverse $ intervalize mq xx1=(scale ac_ xx0)+(scale bc_ yy0) yy1=(scale cc_ xx0)+(scale dc_ yy0) eqc=xx1*xx1+yy1*yy1-alphau*alphau thrx=1e-5 solutions _ []=[] solutions thr boxes@(_:_)= let sol0=concatMap (solve thr (V.fromList [eqc,lambda])) boxes (correct,toRefine)=partition (\(u,v,_,_)-> let (xu,yu)=evalCurve o (Interval u u) (xv,yv)=evalCurve o (Interval v v) in (iup $ (xu-xv)^(2::Int)+(yu-yv)^(2::Int))<=thrx) sol0 in correct++(solutions (thr/2) toRefine) -- Removing false positives by computing the distance to the center of -- the circle (this is quite fast). removeFalse cl0 (h@(_,v,_,_):h'@(u',_,_,_):s)= let u''=(v+u')/2 (xu,yu)=evalCurve o (Interval u'' u'') Interval dl du=distance xu yu cir cl1 | du<1 = Dedans | dl>1 = Dehors | otherwise = SurLaLigne in if cl0/=cl1 then h:(removeFalse cl1 (h':s)) else removeFalse cl1 (h':s) removeFalse _ l=l initCl= let (x0,y0)=evalCurve o (Interval (t0 o) (t0 o)) Interval dl du=distance x0 y0 cir in if dl>1 then Dehors else if du<1 then Dedans else SurLaLigne in foldl (\l (u,v,_,_)-> let (Interval xl xu,Interval yl yu)=evalCurve o (Interval u v) in case angle (Interval xl xu) (Interval yl yu) cir of Just (Interval a0 a1)-> (u,v,a0,a1):l Nothing->l ) [] $ removeFalse initCl $ sort $ solutions 1e-2 [(t0 o,t1 o,0::Double,1::Double)] inter a@(Circle{cx0=x0a,cy0=y0a,matrix=ma}) b@(Circle{cx0=x0b,cy0=y0b,matrix=mb})= if (intervalize ma)`intersects`(intervalize mb) && x0a==x0b && y0a==y0b then let up ix@(Interval _ x_) tt0 tt1 | x_tt1 = down (ix-(2*interval pi)) tt0 tt1 | x__ case (up aa (t0 a) (t1 a), up ab (t0 a) (t1 a)) of (Just ba,Just bb) | ilow aa<=iup ab -> [(ilow ba, iup bb, ilow aa, iup ab)] | otherwise-> case (up (interval $ t0 b) (t0 a) (t1 a), up (interval $ t1 b) (t0 a) (t1 a)) of (Just b0,Just b1)-> [(ilow b0,iup bb, t0 b, iup ab), (ilow ba,iup b1, ilow aa, t1 b)] _->[] _->[] _->[] else let thr=1e-5 solutions=solve thr (V.fromList [eq0,eq1]) (fpred u0,fsucc v0, fpred w0,fsucc x0) in foldl (\l (u,v,w,x)-> let alpha=angle (Interval u v) (Interval w x) a beta=angle (Interval u v) (Interval w x) b in case alpha of Just (Interval a0l a0u)-> case beta of Just (Interval b0l b0u)->(a0l,a0u,b0l,b0u):l _->l _->l ) [] solutions where ima@(Matrix2 am bm cm dm)=intervalize ma maxa=max (iup $ abs am+abs bm) (iup $ abs cm+abs dm) (u0,v0,w0,x0)=(x0a-maxa,x0a+maxa,y0a-maxa,y0a+maxa) -- x-x0 xxa0=intervalize $ Bernsteinp (2,1) $ UV.fromList [-x0a,1-x0a] :: Bernsteinp (Int,Int) Interval yya0=intervalize $ Bernsteinp (1,2) $ UV.fromList [-y0a,1-y0a] :: Bernsteinp (Int,Int) Interval (Matrix2 aa_ ba_ ca_ da_)=inverse ima xxa=(scale aa_ xxa0)+(scale ba_ yya0)::Bernsteinp (Int,Int) Interval yya=(scale ca_ xxa0)+(scale da_ yya0) xxb0=intervalize $ Bernsteinp (2,1) $ UV.fromList [-x0b,1-x0b] yyb0=intervalize $ Bernsteinp (1,2) $ UV.fromList [-y0b,1-y0b] (Matrix2 ab_ bb_ cb_ db_)=inverse $ intervalize mb xxb=(scale ab_ xxb0)+(scale bb_ yyb0) yyb=(scale cb_ xxb0)+(scale db_ yyb0) c1=Bernsteinp (1,1) $ UV.singleton 1 eq0=xxa*xxa+yya*yya-c1 eq1=xxb*xxb+yyb*yyb-c1 inter op@(Bezier{cx=bxa,cy=bya,t0=t0a,t1=t1a}) (Bezier{cx=xb,cy=yb,t0=t0b,t1=t1b})= let p0=(promote 1 $ intervalize bxa)-(promote 2 $ intervalize xb) :: Bernsteinp (Int,Int) Interval p1=(promote 1 $ intervalize bya)-(promote 2 $ intervalize yb) :: Bernsteinp (Int,Int) Interval thrx=1e-2 solutions _ []=[] solutions thr boxes@(_:_)= let sol0=concatMap (solve thr (V.fromList [p0,p1])) boxes (correct,toRefine)=partition (\(u,v,_,_)-> let (xu,yu)=evalCurve op (Interval u u) (xv,yv)=evalCurve op (Interval v v) in (iup $ (xu-xv)^(2::Int)+(yu-yv)^(2::Int))<=thrx) sol0 in correct++(solutions (thr/2) toRefine) in solutions 1e-2 [(t0a,t1a,t0b,t1b)] inter cir@(Circle{}) bez@(Bezier{})=map (\(u,v,w,x)->(w,x,u,v)) $ inter bez cir inter bez@(Bezier{}) cir@(Circle{})= let xx=(intervalize $ cx bez)-(intervalize $ constant $ cx0 cir) yy=(intervalize $ cy bez)-(intervalize $ constant $ cy0 cir) (Matrix2 a b c d)=inverse $ intervalize $ matrix cir xx0=scale a xx+scale b yy yy0=scale c xx+scale d yy thrx=1e-5 solutions _ []=[] solutions thr boxes@(_:_)= let sol0=concatMap (solve thr (V.singleton (xx0*xx0+yy0*yy0-(constant 1)))) boxes (correct,toRefine)=partition (\(u,v)-> let (xu,yu)=evalCurve bez (Interval u u) (xv,yv)=evalCurve bez (Interval v v) in (iup $ (xu-xv)^(2::Int)+(yu-yv)^(2::Int))<=thrx) sol0 in correct++(solutions (thr/2) toRefine) in foldl (\l (u,v)-> let (Interval xl xu,Interval yl yu)=evalCurve bez (Interval u v) in case angle (Interval xl xu) (Interval yl yu) cir of Just (Interval a0 a1)-> (u,v,a0,a1):l Nothing->l ) [] $! solutions (1e-2) [(t0 bez,t1 bez)] inter bez@(Bezier{}) off@(Offset{})=map (\(u,v,w,x)->(w,x,u,v)) $ inter off bez inter off@(Offset{}) bez@(Bezier{})= let thr=1e-2 thrx=1e-5 solutions _ []=[] solutions thr boxes@(_:_)= let sol0=concatMap (solve thr (V.fromList [eq0,eq1,eq2])) boxes (correct,toRefine)=partition (\(u,v,_,_,_,_)-> let (xu,yu)=evalCurve off (Interval u u) (xv,yv)=evalCurve off (Interval v v) in (iup $ (xu-xv)^(2::Int)+(yu-yv)^(2::Int))<=thrx) sol0 in correct++(solutions (thr/2) toRefine) in map (\(a,b,c,d,_,_)->(a,b,c,d)) $ solutions 1e-2 $ [(0,1,0,1,0,1)::(Double,Double,Double,Double,Double,Double)] where bxp=intervalize $ cx off byp=intervalize $ cy off bxq=intervalize $ cx bez byq=intervalize $ cy bez bxp'=derivate bxp byp'=derivate byp bxp3=promote 1 bxp byp3=promote 1 byp bxq3=promote 2 bxq byq3=promote 2 byq mp@(Matrix2 ap bp cp dp)=intervalize $ matrix $ off (Matrix2 ap_ bp_ cp_ dp_)=inverse mp bXp'=promote 1 $ (scale ap_ bxp')+(scale bp_ byp') bYp'=promote 1 $ (scale cp_ bxp')+(scale dp_ byp') omp@(Bernsteinp _ omegap)=(bXp'*bXp')+(bYp'*bYp') au= let au_=sqrt $ UV.minimum $ UV.map ilow omegap in max 0 $ fpred au_ bu= let bu_=sqrt $ UV.maximum $ UV.map iup omegap in fsucc bu_ alphau=Bernsteinp (1,1,2) $ UV.fromList [Interval au au,Interval bu bu] eq0=bxp3*alphau + (scale ap bYp') - (scale bp bXp') - bxq3 eq1=byp3*alphau + (scale cp bYp') - (scale dp bXp') - byq3 eq2=alphau*alphau-omp angle::Interval->Interval->Curve->Maybe Interval angle x y (Circle { cx0,cy0,matrix,t0,t1 })= let vx=x-interval cx0 vy=y-interval cy0 Matrix2 a b c d=inverse $ intervalize matrix -- L'arithmetique d'intervalles fait un peu n'importe quoi -- quand le vecteur est trop long. On le raccourcit. alpha= let co@(Interval col cou)=a*vx+b*vy Interval sil siu=c*vx+d*vy co2= let (col2,cou2)=if col*col=0 then ac else Interval (negate $ min (abs acl) (abs acu)) (max (abs acl) (abs acu)) up ix | iup ixt1 = down $ ix-(2*interval pi) | iup ixInterval->Curve->Interval distance x0 y0 (Bezier{..})= distance x0 y0 (Offset{cx,cy,t0,t1,matrix=Matrix2 1 0 0 1}) distance x0 y0 (Offset{..})= let (Matrix2 a b c d)=inverse $ intervalize matrix vx_=intervalize cx-(constant x0) vy_=intervalize cy-(constant y0) vx=scale a vx_+scale b vy_ vy=scale c vx_+scale d vy_ dist=vx*vx+vy*vy in foldl (\di (u,v)->let di'=eval dist (Interval u v) in if iup diCurve->[Curve] offset m (Bezier{cx=x@(Bernsteinp nx bx),cy=y@(Bernsteinp ny by)})= if nx <=1 && ny <=1 then [Circle { cx0=UV.head bx,cy0=UV.head by,t0=ilow 0,t1=iup $ 2*pi,matrix=m }] else [ c0,c1,c2,c3 ] where im=intervalize m (Matrix2 a_ b_ c_ d_)=inverse im ibx=intervalize x iby=intervalize y lastCoef (Bernsteinp n c) | n>=1 = UV.last c | otherwise = 0 firstCoef (Bernsteinp n c) | n>=1 = UV.head c | otherwise = 0 -- Premiere courbe offset c0=Offset { cx=x, cy=y, t0=0,t1=1,matrix=m } -- Demi-cercle 1 ibx'=derivate ibx iby'=derivate iby -- Calcul du vecteur tangent au bout du premier alpha0= let xx0=lastCoef ibx' yy0=lastCoef iby' xx0'=a_*xx0+b_*yy0 yy0'=c_*xx0+d_*yy0 norm0=sqrt $ xx0'*xx0'+yy0'*yy0' xx'=xx0'/norm0 yy'=yy0'/norm0 in if ilow xx'>=0 then -(acos yy') else if iup xx'<=0 then acos yy' else let Interval u v=acos yy' in Interval (negate $ max (abs u) (abs v)) (max (abs u) (abs v)) alpha0'=alpha0+interval pi c1=Circle { cx0=lastCoef x, cy0=lastCoef y, t0=ilow alpha0, t1=iup alpha0', matrix=m } -- Deuxieme courbe offset c2=Offset { cx=reorient x, cy=reorient y, t0=0,t1=1, matrix=m } -- Deuxieme demi-cercle alpha1= let xx0=firstCoef ibx' yy0=firstCoef iby' xx0'=a_*xx0+b_*yy0 yy0'=c_*xx0+d_*yy0 norm0=sqrt $ xx0'*xx0'+yy0'*yy0' xx'=xx0'/norm0 yy'=yy0'/norm0 in if ilow xx'>=0 then -(acos yy') else if iup xx'<=0 then acos yy' else let Interval u v=acos yy' in Interval (negate $ max (abs u) (abs v)) (max (abs u) (abs v)) alpha1'=alpha1-pi c3=Circle { cx0=firstCoef x, cy0=firstCoef y, t0=ilow alpha1', t1=iup alpha1, matrix=m } offset _ _=error "offset : undefined yet for other than Bezier" rnd::Interval->Double rnd (Interval a b)=(a+b)/2 derivRoots::Double->Curve->([(Double,Double)],[(Double,Double)]) derivRoots thr (Bezier{..})= (solve thr (V.singleton $ derivate $ intervalize cx) (t0,t1), solve thr (V.singleton $ derivate $ intervalize cy) (t0,t1)) derivRoots thr (Offset{..})= let ix=intervalize cx iy=intervalize cy x'=derivate ix y'=derivate iy m@(Matrix2 a b c d)=intervalize matrix (Matrix2 a_ b_ c_ d_)=inverse m xx'=(scale a_ x')+(scale b_ y') yy'=(scale c_ x')+(scale d_ y') xx''=derivate xx' yy''=derivate yy' omega=xx'*xx'+yy'*yy' au= let au_=sqrt $ UV.minimum $ UV.map ilow $ coefs omega in max 0 $ fpred au_ bu= let bu_=sqrt $ UV.maximum $ UV.map iup $ coefs omega in fsucc bu_ alphau=Bernsteinp (1,2) $ UV.fromList [Interval au au,Interval bu bu] eqx0=(promote 1 yy'')*(alphau^(2::Int))-(promote 1 $ yy'*yy''+xx'*xx'') eqy0=(promote 1 $ yy'*yy''+xx'*xx'')-(promote 1 xx'')*(alphau^(2::Int)) eqx=(promote 1 x')*(alphau^(3::Int))+(scale a eqx0)+(scale b eqy0) :: Bernsteinp (Int,Int) Interval eqy=(promote 1 y')*(alphau^(3::Int))+(scale c eqx0)+(scale d eqy0) eq=(promote 1 omega)-alphau^(2::Int) :: Bernsteinp (Int,Int) Interval in (map (\(u,v,_,_)->(u,v)) $ solve thr (V.fromList [eqx,eq]) ((t0,t1,0,1)::(Double,Double,Double,Double)), map (\(u,v,_,_)->(u,v)) $ solve thr (V.fromList [eqy,eq]) ((t0,t1,0,1)::(Double,Double,Double,Double))) derivRoots _ (Circle{..})= let (Matrix2 a b c d)=intervalize matrix aa=sqrt $ a*a+b*b cc=sqrt $ c*c+d*d sx | ilow a>=0 = acos $ b/aa | otherwise = negate $ acos $ b/aa sy | ilow c>=0 = acos $ d/cc | otherwise = negate $ acos $ d/cc Interval ux vx=(-sx)-pi/2 Interval uy vy=(-sy)-pi/2 in ([(ux,vx)],[(uy,vy)]) -- | The leftmost point on a curve left::Curve->(Double,Double) left cur= let (x,y)= foldl (\m@(xx,_) (s,t)-> let m'@(xx',_)=evalCurve cur $ interval $ (s+t)/2 in if ilow xx(Double,Double) bottom cur= let (x,y)= foldl (\m@(_,yy) (s,t)-> let m'@(_,yy')=evalCurve cur $ interval $ (s+t)/2 in if ilow yy(Double,Double) right cur= let (x,y)= foldl (\m@(xx,_) (s,t)-> let m'@(xx',_)=evalCurve cur $ interval $ (s+t)/2 in if iup xx>iup xx' then m else m') (-1/0,-1/0) $ (t0 cur,t0 cur):(t1 cur,t1 cur):(fst $ derivRoots 1e-5 cur) in (rnd x,rnd y) -- | The topmost point on a curve top::Curve->(Double,Double) top cur= let (x,y)= foldl (\m@(_,yy) (s,t)-> let m'@(_,yy')=evalCurve cur $ interval $ (s+t)/2 in if iup yy>iup yy' then m else m') (-1/0,-1/0) $ (t0 cur,t0 cur):(t1 cur,t1 cur):(snd $ derivRoots 1e-5 cur) in (rnd x,rnd y) \end{code}