\begin{code}
{-# OPTIONS -XRecordWildCards -XNamedFieldPuns #-}
module Graphics.Typography.Geometry.Approximation(approximate) where
import qualified Data.Vector.Unboxed as UV
import Graphics.Typography.Geometry.Bezier
import Graphics.Typography.Geometry
import Algebra.Polynomials.Bernstein
import Algebra.Polynomials.Numerical
rnd::Interval->Double
rnd (Interval a b)=(a+b)/2
approximate::[Curve]->[(Bernsteinp Int Double,Bernsteinp Int Double)]
approximate []=[]
approximate l0@(h0:_)=
let approx::Double->Double->[Curve]->[(Bernsteinp Int Double,Bernsteinp Int Double)]
approx _ _ []=[]
approx x0 y0 (cc@(Circle {..}):s)=
let theta=abs $ t1-t0 in
if theta <= pi/2 then
let x0_=cos $ theta/2
y0_=sin $ theta/2
x1_=(4-x0_)/3
y1_=(1-x0_)*(3-x0_)/(3*y0_)
c0=cos $! theta/2+t0
s0=sin $! theta/2+t0
px0=c0*x0_ - s0*y0_
py0=s0*x0_ + c0*y0_
px1=c0*x1_ - s0*y1_
py1=s0*x1_ + c0*y1_
px2=c0*x1_ + s0*y1_
py2=s0*x1_ - c0*y1_
x1=cx0+(a*px0+b*py0)
y1=cy0+(c*px0+d*py0)
(Matrix2 a b c d)=matrix
x=Bernsteinp 4 $ UV.fromList
[ x0,
cx0+(a*px2+b*py2),
cx0+(a*px1+b*py1),
x1]
y=Bernsteinp 4 $ UV.fromList
[ y0,
cy0+(c*px2+d*py2),
cy0+(c*px1+d*py1),
y1 ]
in
(x,y):(approx x1 y1 s)
else
let t1'=(t1+t0)/2 in
approx x0 y0 $ (cc { t1=t1' }):(cc { t0=t1' }):s
approx x0 y0 (off_:s)=
let bx=restriction (cx off_) (t0 off_) (t1 off_)
by=restriction (cy off_) (t0 off_) (t1 off_)
off=off_ { cx=bx,cy=by,t0=0,t1=1 }
ibx=elevate (bounds by-bounds bx) $ intervalize bx
iby=elevate (bounds bx-bounds by) $ intervalize by
points=
let np=10 in
map (\x->(x/np,x/np)) [0..np]
vx0=ibx?1-ibx?0
vy0=iby?1-iby?0
vx1=ibx?(bounds ibx-2)-ibx?(bounds ibx-1)
vy1=iby?(bounds iby-2)-iby?(bounds iby-1)
(wx0,wy0)=evalCurve off 0
(wx1,wy1)=evalCurve off 1
wx=Bernsteinp 4 $ UV.fromList [wx0,wx0,wx1,wx1] :: Bernsteinp Int Interval
wy=Bernsteinp 4 $ UV.fromList [wy0,wy0,wy1,wy1] :: Bernsteinp Int Interval
bern1=Bernsteinp 4 $ UV.fromList [0,1,0,0] :: Bernsteinp Int Interval
bern2=Bernsteinp 4 $ UV.fromList [0,0,1,0] :: Bernsteinp Int Interval
sumAll a b c d x1 y1 ((h1,h2):ss)=
let h=Interval h1 h2
(xi,yi)=evalCurve off h
b1=eval bern1 h
b2=eval bern2 h
a'=a + (vx0*vx0+vy0*vy0)*b1*b1
b'=b + (vx0*vx1 + vy0*vy1)*b1*b2
c'=c + (vx0*vx1 + vy0*vy1)*b1*b2
d'=d + (vx1*vx1+vy1*vy1)*b2*b2
dx=xi-(eval wx h)
dy=yi-(eval wy h)
x1'=x1 + (vx0*dx + vy0*dy)*b1
y1'=y1 + (vx1*dx + vy1*dy)*b2
in
sumAll a' b' c' d' x1' y1' ss
sumAll a b c d x1 y1 []=(a,b,c,d,x1,y1)
(ra,rb,rc,rd,rx1,ry1)=sumAll 0 0 0 0 0 0 points
(Matrix2 a_ b_ c_ d_)=inverse $ Matrix2 ra rb rc rd
lambda1=a_*rx1+b_*ry1
lambda2=c_*rx1+d_*ry1
xap=Bernsteinp 4 $ UV.fromList [wx0,
wx0+lambda1*vx0,
wx1+lambda2*vx1,
wx1]
yap=Bernsteinp 4 $ UV.fromList [wy0,
wy0+lambda1*vy0,
wy1+lambda2*vy1,
wy1]
(err,arg)=foldl (\m (h1,h2)->
let (xi,yi)=evalCurve off (Interval h1 h2)
xj=eval xap (Interval h1 h2)
yj=eval yap (Interval h1 h2)
in
max m (iup $ abs $ (xi-xj)*(xi-xj)+(yi-yj)*(yi-yj), (h1+h2)/2))
(0,0) points
in
if err<=0.01 then
(desintervalize xap,desintervalize yap):(approx (rnd wx1) (rnd wy1) s)
else
approx x0 y0 $
(off { cx=restriction (cx off) 0 arg,
cy=restriction (cy off) 0 arg }):
(off { cx=restriction (cx off) arg 1,
cy=restriction (cy off) arg 1 }):s
(x0h,y0h)=evalCurve h0 $ Interval (t0 h0) (t0 h0)
in
approx (rnd x0h) (rnd y0h) l0
desintervalize::(Bernsteinp a Interval)->(Bernsteinp a Double)
desintervalize b=b { coefs=UV.map rnd $ coefs b}
\end{code}