implicit-0.3.0.0: Graphics/Implicit/Export/Render/HandlePolylines.hs
-- Implicit CAD. Copyright (C) 2012, Christopher Olah (chris@colah.ca)
-- Copyright (C) 2016, Julia Longtin (julial@turinglace.com)
-- Released under the GNU AGPLV3+, see LICENSE
module Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs) where
import Prelude(Maybe(Just, Nothing), fmap, (.), (==), last, reverse, ($), (<>), (-), (/), abs, (<=), (||), (&&), (*), (>), otherwise, error)
import Graphics.Implicit.Definitions (minℝ, Polyline(Polyline))
cleanLoopsFromSegs :: [Polyline] -> [Polyline]
cleanLoopsFromSegs = fmap reducePolyline . joinSegs
-- | Join polylines that connect.
joinSegs :: [Polyline] -> [Polyline]
joinSegs (Polyline present:remaining) =
let
findNext :: [Polyline] -> (Maybe Polyline, [Polyline])
findNext (Polyline (p3:ps):segs)
| p3 == last present = (Just (Polyline (p3:ps)), segs)
| last ps == last present = (Just (Polyline $ reverse $ p3:ps), segs)
| otherwise = case findNext segs of (res1,res2) -> (res1,Polyline (p3:ps):res2)
findNext [] = (Nothing, [])
findNext (Polyline []:_) = (Nothing, [])
in
case findNext remaining of
(Nothing, _) -> Polyline present: joinSegs remaining
(Just (Polyline match), others) -> joinSegs $ Polyline (present <> match) : others
joinSegs [] = []
-- | Simplify and sort a polyline.
reducePolyline :: Polyline -> Polyline
reducePolyline (Polyline ((x1,y1):(x2,y2):(x3,y3):others))
-- Remove sequential duplicate points.
| (x1,y1) == (x2,y2) = reducePolyline (Polyline ((x2,y2):(x3,y3):others))
| abs ( (y2-y1)/(x2-x1) - (y3-y1)/(x3-x1) ) <= minℝ
|| ( (x2-x1) == 0 && (x3-x1) == 0 && (y2-y1)*(y3-y1) > 0) =
reducePolyline (Polyline ((x1,y1):(x3,y3):others))
| otherwise = Polyline ((x1,y1) : points (reducePolyline (Polyline ((x2,y2):(x3,y3):others))))
where
points (Polyline pts) = pts
-- | remove sequential duplicate points.
reducePolyline (Polyline ((x1,y1):(x2,y2):others)) =
if (x1,y1) == (x2,y2) then reducePolyline (Polyline ((x2,y2):others)) else Polyline ((x1,y1):(x2,y2):others)
-- | Return the last result.
reducePolyline l@(Polyline ((_:_))) = l
-- Should not happen.
reducePolyline (Polyline ([])) = error "empty polyline"
{-cleanLoopsFromSegs =
connectPolys
-- . joinSegs
. filter (not . degeneratePoly)
polylinesFromSegsOnGrid = undefined
degeneratePoly [] = True
degeneratePoly [a,b] = a == b
degeneratePoly _ = False
data SegOrPoly = Seg (ℝ2) ℝ ℝ2 -- Basis, shift, interval
| Poly [ℝ2]
isSeg (Seg _ _ _) = True
isSeg _ = False
toSegOrPoly :: Polyline -> SegOrPoly
toSegOrPoly [a, b] = Seg v (a⋅vp) (a⋅v, b⋅v)
where
v@(va, vb) = normalized (b ^-^ a)
vp = (-vb, va)
toSegOrPoly ps = Poly ps
fromSegOrPoly :: SegOrPoly -> Polyline
fromSegOrPoly (Seg v@(va,vb) s (a,b)) = [a*^v ^+^ t, b*^v ^+^ t]
where t = s*^(-vb, va)
fromSegOrPoly (Poly ps) = ps
joinSegs :: [Polyline] -> [Polyline]
joinSegs = fmap fromSegOrPoly . joinSegs' . fmap toSegOrPoly
joinSegs' :: [SegOrPoly] -> [SegOrPoly]
joinSegs' segsOrPolys = polys <> (foldMap joinAligned aligned) where
polys = filter (not.isSeg) segsOrPolys
segs = filter isSeg segsOrPolys
aligned = groupWith (\(Seg basis p _) -> (basis,p)) segs
joinAligned segs@((Seg b z _):_) = mergeAdjacent orderedSegs where
orderedSegs = sortBy (\(Seg _ _ (a1,_)) (Seg _ _ (b1,_)) -> compare a1 b1) segs
mergeAdjacent (pres@(Seg _ _ (x1a,x2a)) : next@(Seg _ _ (x1b,x2b)) : others) =
if x2a == x1b
then mergeAdjacent ((Seg b z (x1a,x2b)): others)
else pres : mergeAdjacent (next : others)
mergeAdjacent a = a
joinAligned [] = []
connectPolys :: [Polyline] -> [Polyline]
connectPolys [] = []
connectPolys (present:remaining) =
let
findNext (ps@(p:_):segs) =
if p == last present
then (Just ps, segs)
else (a, ps:b) where (a,b) = findNext segs
findNext [] = (Nothing, [])
in
case findNext remaining of
(Nothing, _) -> present:(connectPolys remaining)
(Just match, others) -> connectPolys $ (present <> tail match): others
-}