packages feed

hgeometry-0.8.0.0: test/Data/Geometry/PlanarSubdivisionSpec.hs

{-# LANGUAGE PartialTypeSignatures #-}
module Data.Geometry.PlanarSubdivisionSpec where


import qualified Algorithms.Geometry.PolygonTriangulation.MakeMonotone as MM
import           Data.Bifunctor (second)
import           Data.Ext
import           Data.Foldable (toList, forM_)
import           Data.Geometry
import           Data.Geometry.PlanarSubdivision
import qualified Data.Geometry.PlanarSubdivision as PS
import           Data.Geometry.Polygon
import qualified Data.List.NonEmpty as NonEmpty
import           Data.PlanarGraph (FaceId(..),VertexId(..))
import qualified Data.PlaneGraph as PG
import           Test.Hspec
import qualified Data.Vector as V
import qualified Data.List as L

import qualified Algorithms.Geometry.PolygonTriangulation.TriangulateMonotone as TM
import qualified Algorithms.Geometry.PolygonTriangulation.Triangulate as TR

import           Control.Lens hiding (holesOf)
import           Data.Either (lefts)
import           Data.Geometry.Ipe
import           Data.Geometry.PlanarSubdivision.Draw
import           Data.Maybe (fromJust)
import           Data.PlaneGraph.Draw

import Data.Geometry.LineSegment
import Data.Geometry.Interval
import Data.Range
import Data.Ratio

data Test = Test
data Id a = Id a




simplePg  = fromSimplePolygon (Id Test) simplePg' Inside Outside
simplePg' = toCounterClockWiseOrder . fromPoints $ map ext $ [ Point2 160 736
                                                             , Point2 128 688
                                                             , Point2 176 672
                                                             , Point2 256 672
                                                             , Point2 272 608
                                                             , Point2 384 656
                                                             , Point2 336 768
                                                             , Point2 272 720
                                                             ]

triangle :: PlanarSubdivision Test () () PolygonFaceData Rational
triangle = (\pg -> fromSimplePolygon (Id Test) pg Inside Outside)
         $ trianglePG

trianglePG = fromPoints . map ext $ [origin, Point2 10 0, Point2 10 10]


toNonEmpty :: Foldable f => f a -> NonEmpty.NonEmpty a
toNonEmpty = NonEmpty.fromList . toList

spec :: Spec
spec = do
  describe "PlanarSubdivision" $ do
    it "outerFaceId = 0 " $
      outerFaceId triangle `shouldBe` (FaceId $ VertexId 0)
    it "outerFace tests" $
      let [d] = toList $ holesOf (outerFaceId triangle) triangle
      in leftFace d triangle `shouldBe` (outerFaceId triangle)
    noEmptyFacesSpec
    testSpec testPoly
    testSpec testPoly2
    testSpec testPoly3
    testSpec testPoly4

    -- describe "incidentDarts" $ do
    --   forM_ (darts' triangle) $ \d ->
    --     it "incidentDarts indiv"  $
    --       boundary' d triangle `shouldBe` (toNonEmpty $ edges' triangle)
    -- this last test is nonsense


sameAsConnectedPG      :: (Eq v, Eq e, Eq f, Eq r, Show v, Show e, Show f, Show r)
                       => PlaneGraph s v e f r -> PlanarSubdivision s v e f r
                       -> Spec
sameAsConnectedPG g ps = describe "connected planarsubdiv, same as PlaneGraph" $ do
  it "same number of vertices" $
    PG.numVertices g `shouldBe` PS.numVertices ps
  it "same number of darts" $
    PG.numDarts g `shouldBe` PS.numDarts ps
  it "same number of edges" $
    PG.numEdges g `shouldBe` PS.numEdges ps
  it "same number of faces" $
    PG.numFaces g `shouldBe` PS.numFaces ps
  it "same vertices" $
    PG.vertices g `shouldBe` vertices ps
  it "same dart data" $
    (g^.PG.rawDartData) `shouldBe` ((^.dataVal) <$> ps^.rawDartData)
  -- it "same dart endpoints" $ do
  describe "same darts" $ do
    forM_ (darts' ps) $ \d ->
      it ("sameDarts: " ++ (show d)) $ endPoints d ps `shouldBe` PG.endPoints d g
  -- sameDarts g ps
  it "same edges" $
    (V.fromList . L.sortOn fst . toList $ PG.edgeSegments g) `shouldBe` edgeSegments ps
  it "same edges per vertex" $
    forM_ (PG.vertices' g) $ \v ->
      PG.incidentEdges v g `shouldBe` PS.incidentEdges v ps
  -- it "same face Id's" $
  --   PG.faces' g `shouldBe` faces' ps
  -- it "same outerface boundary" $
  --   (second (FaceData mempty)
  -- it "same faces" $
  --   (second (FaceData mempty) <$> PG.faces g) `shouldBe` faces ps


-- sameDart g ps d =

-- sameDarts          :: (Eq v, Eq e, Eq f, Eq r, Show v, Show e, Show f, Show r)
--                        => PlaneGraph s v e f r -> PlanarSubdivision s v e f r
--                        -> Spec
-- sameDarts g ps =
--     -- sameDart g ps


-- sort' = V.fromList . L.sortOn fst . toList




testSpec    :: (Ord r, Eq p, Fractional r, Show r, Show p)
            => SimplePolygon p r -> Spec
testSpec pg = do
  sameAsConnectedPG (PG.fromSimplePolygon (Id Test) pg Inside Outside)
                    (PS.fromSimplePolygon (Id Test) pg Inside Outside)
  -- sameAsConnectedPG (TM.triangulate' (Id Test) pg)
  --                   (TM.triangulate  (Id Test) pg)
  sameAsConnectedPG (TR.triangulate' (Id Test) pg)
                    (TR.triangulate  (Id Test) pg)



testPoly :: SimplePolygon () Rational
testPoly = toCounterClockWiseOrder . fromPoints $ map ext $ [
                                                              Point2 128 720
                                                            , Point2 192 752
                                                            , Point2 224 720
                                                            , Point2 240 672
                                                            , Point2 128 624
                                                            , Point2 176 672
                                                            ]


testPoly2 :: SimplePolygon () Rational
testPoly2 = toCounterClockWiseOrder . fromPoints $ map ext $ [ Point2 160 736
                                                             , Point2 128 688
                                                             , Point2 176 672
                                                             , Point2 256 672
                                                             , Point2 272 608
                                                             , Point2 384 656
                                                             , Point2 336 768
                                                             , Point2 272 720
                                                             ]



testPoly3 :: SimplePolygon () Rational
testPoly3 = toCounterClockWiseOrder . fromPoints $ map ext $ [ Point2 352 367
                                                             , Point2 128 176
                                                             , Point2 240 336
                                                             , Point2 80 272
                                                             , Point2 48 400
                                                             , Point2 96 384
                                                             , Point2 240 496
                                                             ]



testPoly4 :: SimplePolygon () Rational
testPoly4 = toCounterClockWiseOrder . fromPoints $ map ext $ [ Point2 64 544
                                                             , Point2 320 527
                                                             , Point2 208 496
                                                             , Point2 48 432
                                                             , Point2 16 560
                                                             ]

testPoly5 :: SimplePolygon () Rational
testPoly5 = toCounterClockWiseOrder . fromPoints $ map ext $ [ Point2 352 384
                                                             , Point2 128 176
                                                             , Point2 224 320
                                                             , Point2 48 400
                                                             , Point2 160 384
                                                             , Point2 240 496
                                                             ]


testPolyP  = fromSimplePolygon (Id Test) testPoly5 Inside Outside
testPolygPlaneG = fromJust $ testPolyP^?components.ix 0

monotonePs = MM.makeMonotone (Id Test) testPoly5
monotonePlaneG = fromJust $ monotonePs^?components.ix 0

test = TR.triangulate (Id Test) testPoly5
test' = TR.triangulate' (Id Test) testPoly5
-- test = asIpe drawPlaneGraph testPolygPlaneG mempty

printMP = mapM_ printAsIpeSelection
        . map (iO' . (^.core) . snd)
        . toList . rawFacePolygons $ monotonePs



printP = mapM_ printAsIpeSelection
       . map (iO' . (^.core) . snd)
       . toList . PG.rawFacePolygons $ test'


printPPX = mapM_ printAsIpeSelection
        . map (iO' . (^.core) . snd)
        . toList . rawFacePolygons

printPP = printPPX test

parts' = map (\pg -> fromSimplePolygon (Id Test) pg Inside Outside)
       . lefts . map ((^.core) . snd) . toList . rawFacePolygons $ monotonePs

parts'' = lefts . map ((^.core) . snd) . toList . rawFacePolygons $ monotonePs


--------------------------------------------------------------------------------

noEmptyFacesSpec :: Spec
noEmptyFacesSpec = describe "fromConnectedSegments, correct handling of high degree vertex" $ do
    it "ps1" $
      draw' testSegs `shouldBe` mempty
    it "ps5" $
      draw' testSegs2 `shouldBe` mempty
    it "ps3" $
      draw' testSegs3 `shouldBe` mempty
    -- segs4 <- runIO $ readFromIpeFile "test/Data/Geometry/connectedsegments_simple2.ipe"
    -- it "connected_simple2.ipe" $
    --   draw' segs4 `shouldBe` mempty
    -- segs2 <- runIO $ readFromIpeFile "test/Data/Geometry/connectedsegments_simple.ipe"
    -- it "connected_simple.ipe" $
    --   draw' segs2 `shouldBe` mempty
    -- segs3 <- runIO $ readFromIpeFile "test/Data/Geometry/connectedsegments.ipe"
    -- it "connectedsegments.ipe" $
    --   draw' segs3 `shouldBe` mempty
  where
    draw' = draw . fromConnectedSegments (Identity Test1)

readFromIpeFile    :: FilePath -> IO [LineSegment 2 () Rational :+ _]
readFromIpeFile fp = do Right page <- readSinglePageFile fp
                        pure $
                           page^..content.traverse._withAttrs _IpePath _asLineSegment

data Test1 = Test1

testX = do segs <- readFromIpeFile "test/Data/Geometry/connectedsegments_simple2.ipe"
           let ps = fromConnectedSegments (Identity Test1) segs
           print $ draw ps


draw = V.filter isEmpty . rawFacePolygons
  where
    isEmpty (_,Left  p :+ _) = (< 3) . length . polygonVertices $ p
    isEmpty (_,Right p :+ _) = (< 3) . length . polygonVertices $ p

testSegs = map (\(p,q) -> ClosedLineSegment (ext p) (ext q) :+ ())
                   [ (origin, Point2 10 10)
                   , (origin, Point2 12 10)
                   , (origin, Point2 20 5)
                   , (origin, Point2 13 20)
                   , (Point2 10 10, Point2 12 10)
                   , (Point2 10 10, Point2 13 20)
                   , (Point2 12 10, Point2 20 5)
                   ]

testSegs2 = map (\(p,q) -> ClosedLineSegment (ext p) (ext q) :+ ())
                   [ (Point2 160 192, Point2 80 112)
                   , (Point2 80 112, Point2 192 96)
                   , (Point2 192 96, Point2 160 192)
                   ]


testSegs3 = map (\(p,q) -> ClosedLineSegment (ext p) (ext q) :+ ())
                   [ (origin, Point2 10 0)
                   , (Point2 10 0, Point2 10 10)
                   , (origin, Point2 10 10)

                   , (origin, Point2 (-10) 0)
                   , (Point2 (-10) 0, Point2 (-10) (-10))
                   , (origin, Point2 (-10) (-10))
                   ]