\begin{code}
{-# OPTIONS -XUnboxedTuples -cpp -XRecordWildCards -XNamedFieldPuns -XBangPatterns -XMagicHash -XScopedTypeVariables #-}
module Graphics.Typography.Geometry.Outlines (cutAll, intersections, contour, remerge, outlines) where
import Algebra.Polynomials.Bernstein
import Algebra.Polynomials.Numerical
import Graphics.Typography.Geometry.Bezier
import Graphics.Typography.Geometry
import Data.List (sort)
import qualified Data.Map.Strict as M
import qualified Data.Vector as V
import Control.Parallel
(!)::V.Vector a->Int->a
(!)=(V.!)
cutNoSelf::Curve->[Curve]
cutNoSelf c@(Circle{})=[c]
cutNoSelf bez@(Bezier{..})=
let ix=intervalize cx
dx=derivate ix
solutions=
sort $ filter (\(s,t)->(ilow $ eval ix (Interval s s))*
(iup $ eval ix (Interval t t)) <= 0) $
solve 1e-10 (V.singleton dx) (t0,t1)
roots lastU []=
if lastU>=t1 then
[]
else
[bez { t0=lastU }]
roots lastU (u:s)
| u<=lastU = roots lastU s
| otherwise =
(bez { t0=lastU, t1=u }):
(roots u s)
in
roots t0 $ map (\(s,t)->(s+t)/2) solutions
cutNoSelf off@(Offset{..})=
let thr=1e-2
ix=intervalize cx
iy=intervalize cy
x'=derivate ix
y'=derivate iy
(Matrix2 a b c d)=intervalize matrix
(Matrix2 a_ b_ c_ d_)=inverse $ intervalize matrix
xx'=(scale a_ x')+(scale b_ y')
yy'=(scale c_ x')+(scale d_ y')
xx''=derivate xx'
yy''=derivate yy'
evalC (t::Interval)=
let norm=sqrt $ (eval xx' t)*(eval xx' t)+(eval yy' t)*(eval yy' t)
derx=(eval yy'' t)/norm -
((eval yy' t)*((eval xx' t)*(eval xx'' t)+
(eval yy' t)*(eval yy'' t)))/(norm*norm*norm)
dery=(eval xx'' t)/norm -
((eval xx' t)*((eval xx' t)*(eval xx'' t)+
(eval yy' t)*(eval yy'' t)))/(norm*norm*norm)
in
((eval x' t)+(a*derx-b*dery), (eval y' t)+(c*derx-d*dery))
zerosx=
let verif t lastxx
| t>=t1 = []
| otherwise =
let (xx,_)=evalC (Interval t t) in
if (iup $ xx*lastxx)<=0 then
t:verif (t+thr) xx
else
verif (t+thr) xx
in
verif t0 $ fst $ evalC (Interval t0 t0)
roots lastU []=
if lastU>=t1 then
[]
else
[off { t0=lastU }]
roots lastU (u:s)
| u<=lastU = roots lastU s
| otherwise =
(off { t0=lastU, t1=u }):
(roots u s)
in
roots t0 zerosx
cutAll::[[Curve]]->V.Vector (V.Vector Curve)
cutAll l=V.fromList $ map (\c->V.fromList $ concatMap cutNoSelf c) l
data Topology=Dedans | SurLaLigne | Dehors deriving (Eq, Ord, Show)
minsert::Ord a=>a->b->M.Map a [b]->M.Map a [b]
minsert x y m=M.insertWith (++) x [y] m
munion::Ord a=>M.Map a [b]->M.Map a [b]->M.Map a [b]
munion=M.unionWith (++)
mdeleteFindMin::Ord a=>M.Map a [b]->(Maybe (a,b),M.Map a [b])
mdeleteFindMin m=
if M.null m then
(Nothing, m)
else
let ((a,b),m')=M.deleteFindMin m in
case b of
[]->mdeleteFindMin m'
(h:s)->(Just (a,h), if null s then m' else M.insert a s m')
intersections::V.Vector (V.Vector Curve)->
M.Map (Int,Int,Double) [(Int,Int,Double,Double)]
intersections curves=
let interAll ci cj
| ci>=V.length curves = M.empty
| cj>=V.length curves = interAll (ci+1) (ci+1)
| otherwise =
let next=interAll ci (cj+1)
inters
| ci==cj =
V.ifoldl'
(\s0 i curvei->
V.ifoldl'
(\s1 j curvej->
foldl (\s2 (ti,ti',tj,tj')->
minsert (ci,i,ti) (cj,j+i+1,tj,tj') $
minsert (cj,j+i+1,tj) (ci,i,ti,ti') $ s2) s1 $
inter curvei curvej
)
s0 $ V.drop (i+1) (curves!cj)
) M.empty $ V.take (V.length (curves!ci)-1) (curves!ci)
| otherwise =
V.ifoldl'
(\s0 i curvei->
V.ifoldl'
(\s1 j curvej->
foldl (\s2 (ti,ti',tj,tj')->
minsert (ci,i,ti) (cj,j,tj,tj') $
minsert (ci,i,ti') (cj,j,tj,tj') $
minsert (cj,j,tj) (ci,i,ti,ti') $
minsert (cj,j,tj') (ci,i,ti,ti') $ s2) s1 $
inter curvei curvej
)
s0 (curves!cj)
) M.empty $ V.take (V.length (curves!ci)-1) (curves!ci)
in
(next`par`inters)`seq`
(next`munion`inters)
in
interAll 0 0
contour::V.Vector (V.Vector Curve)->
M.Map (Int,Int,Double) [(Int,Int,Double,Double)]->
[[(Int,Int,Double,Double)]]
contour curves inters0=
let allPaths inters1 passages1=
let (first,inters2)=mdeleteFindMin inters1 in
case first of
Nothing->[]
Just ((ci0,i0,ti0),(cj0,j0,tj0a,tj0b))->
let walk ci i tia tib inters passages=
let (a,b)=M.split (ci,i,tib) inters
(next,b')=mdeleteFindMin b
in
case next of
Nothing->
([],a,passages)
Just ((ci',i',ti'),(cj,j,tja,tjb))
| ci==ci0 && i==i0 && (ci',i',ti')>=(ci,i,ti0)->
([(ci,i,tia,ti0)],a`munion`b',passages)
| ci==ci' && i==i' ->
let isVisible=
let tt=(tia+ti')/2
(xi,yi)=evalCurve (curves!ci!i) (Interval tt tt)
in
V.foldl (\vis cur->
vis &&
iup (distance xi yi $ (cur!0) {t0=0,t1=1})>=1)
True curves
in
if (not isVisible) then
([],a`munion`b',passages)
else
let alreadyPassed=
let (_,p1)=M.split (ci,i,ti') passages in
(not $ M.null p1) &&
(let ((ci_,i_,_),ti'_)=M.findMin p1 in
ci_==ci && i_==i && ti'_<=ti')
in
if alreadyPassed then
([],a`munion`b',passages)
else
let (nextPath,nextInters,nextPassages)=
walk cj j tja tjb (a`munion`b') $
M.insert (ci,i,ti') tia passages
in
if null nextPath then
walk ci i tia tib (a`munion`b') passages
else
((ci,i,tia,ti'):nextPath,
nextInters,
M.insert (ci,i,ti') tia nextPassages)
| otherwise ->
([],inters,passages)
(path,inters3,passages1')=walk cj0 j0 tj0a tj0b inters2 passages1
in
if null path then
allPaths inters3 passages1'
else
path:(allPaths inters3 passages1')
in
allPaths inters0 M.empty
remerge::V.Vector (V.Vector Curve)->[(Int,Int,Double,Double)]->[Curve]
remerge _ []=[]
remerge curves [(ci,i,ti0,ti1)]=[(curves!ci!i) { t0=ti0,t1=ti1 }]
remerge curves (l@((ci,i,ti0,_):s))=
let (cj,j,_,tj1)=last s in
if ci==cj && j+1==i && tj1==ti0 then
let takeFirsts []=(# [],[] #)
takeFirsts ((h@(ci',_,_,_)):ss)
| ci'==ci =
let (# u,v #)=takeFirsts ss in
(# h:u, v #)
| otherwise = (# [],h:ss #)
(# uu,vv #)=takeFirsts l
in
remerge_ $ vv++uu
else
remerge_ l
where
remerge_ []=[]
remerge_ [(cj,j,tj0,tj1)]=[(curves!cj!j) { t0=tj0,t1=tj1 }]
remerge_ ((cj,j,tj0,tj1):(cck@(ck,k,tk0,_)):ss)
| cj==ck && k==j+1 && tj1==tk0 =
let (h':s')=remerge_ $ cck:ss in
(h' { t0=tj0 }) : s'
| otherwise =
((curves!cj!j) { t0=tj0,t1=tj1 }) : (remerge_ $ cck:ss)
outlines::[[Curve]]->[[Curve]]
outlines curves=
let curves'=cutAll curves in
map (remerge curves') $ contour curves' $ intersections curves'
\end{code}