From 77e087bbc354dc023b00d4e8706ecf6a95cea786 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 27 May 2017 04:48:11 -0400 Subject: [PATCH 001/227] handle earlier haskell version. --- Graphics/Implicit/Export/TriangleMeshFormats.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index ec9325a..6d05a07 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.Export.TriangleMeshFormats where -import Prelude (Real, Float, Int, ($), (+), map, (.), mconcat, realToFrac, toEnum, length, zip, return) +import Prelude (Real, Float, Int, ($), (+), map, (.), realToFrac, toEnum, length, zip, return) import Graphics.Implicit.Definitions (Triangle, TriangleMesh, ℝ3) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildInt) @@ -18,6 +18,9 @@ import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<> import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) import qualified Data.ByteString.Builder.Internal as BI (Builder) +-- note: moved to prelude in newer version +import Data.Monoid(mconcat) + import Data.ByteString (replicate) import Data.ByteString.Lazy (ByteString) import Data.Storable.Endian (LittleEndian(LE)) From d34f3ca1a847693986b0ad044266471a80e7168e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 27 May 2017 05:48:31 -0400 Subject: [PATCH 002/227] use the same math for determining resolution as extopenscad. --- programs/implicitsnap.hs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 4f3c5c0..cc32db7 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -25,11 +25,18 @@ import Graphics.Implicit.ExtOpenScad.Definitions (OVal (ONum)) -- Functions for finding a box around an object, so we can define the area we need to raytrace inside of. import Graphics.Implicit.ObjectUtil (getBox2, getBox3) +-- Definitions of the datatypes used for 2D objects, 3D objects, and for defining the resolution to raytrace at. import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3, ℝ) +-- Use default values when a Maybe is Nothing. +import Data.Maybe (fromMaybe) + import Graphics.Implicit.Export.TriangleMeshFormats (jsTHREE, stl) import Graphics.Implicit.Export.PolylineFormats (svg, hacklabLaserGCode) +-- Operator to subtract two points. Used when defining the resolution of a 2d object. +import Data.AffineSpace ((.-.)) + -- class DiscreteApprox import Graphics.Implicit.Export.DiscreteAproxable (discreteAprox) @@ -71,7 +78,33 @@ renderHandler = method GET $ withCompression $ do (Just $ BS.Char.unpack format) (_, _, _) -> writeBS "must provide source and callback as 1 GET variable each" +-- Find the resolution to raytrace at. getRes :: forall k. (Data.String.IsString k, Ord k) => (Map k OVal, [SymbolicObj2], [SymbolicObj3]) -> ℝ + +-- First, use a resolution specified by a variable in the input file. +getRes (Map.lookup "$res" -> Just (ONum res), _, _) = res +-- Use a resolution chosen for 3D objects. +-- FIXME: magic numbers. +getRes (varlookup, _, obj:_) = + let + ((x1,y1,z1),(x2,y2,z2)) = getBox3 obj + (x,y,z) = (x2-x1, y2-y1, z2-z1) + in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of + ONum qual | qual > 0 -> min (minimum [x,y,z]/2) ((x*y*z/qual)**(1/3) / 22) + _ -> min (minimum [x,y,z]/2) ((x*y*z)**(1/3) / 22) +-- Use a resolution chosen for 2D objects. +-- FIXME: magic numbers. +getRes (varlookup, obj:_, _) = + let + (p1,p2) = getBox2 obj + (x,y) = p2 .-. p1 + in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of + ONum qual | qual > 0 -> min (min x y/2) ((x*y/qual)**0.5 / 30) + _ -> min (min x y/2) ((x*y)**0.5 / 30) +-- fallthrough value. +getRes _ = 1 + +{- getRes (varlookup, obj2s, obj3s) = let qual = case Map.lookup "$quality" varlookup of @@ -98,7 +131,7 @@ getRes (varlookup, obj2s, obj3s) = if qual <= 30 then qualRes else -1 - +-} getWidth :: forall t. (t, [SymbolicObj2], [SymbolicObj3]) -> ℝ getWidth (_, _, obj:_) = maximum [x2-x1, y2-y1, z2-z1] From d7713e82a54f4bf79a8ea52c88d4351b05c239f8 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 30 May 2017 11:45:31 +0100 Subject: [PATCH 003/227] remove unneeded do and $, use sqrt() instead of **0.5 --- programs/implicitsnap.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 4f3c5c0..b7d4851 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -5,7 +5,7 @@ -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} -{-# LANGUAGE OverloadedStrings, ViewPatterns #-} +{-# LANGUAGE OverloadedStrings #-} -- A Snap(HTTP) server providing an ImplicitCAD REST API. @@ -59,12 +59,12 @@ renderHandler = method GET $ withCompression $ do modifyResponse $ setContentType "application/x-javascript" request <- getRequest case (rqParam "source" request, rqParam "callback" request, rqParam "format" request) of - (Just [source], Just [callback], Nothing) -> do + (Just [source], Just [callback], Nothing) -> writeBS $ BS.Char.pack $ executeAndExport (BS.Char.unpack source) (BS.Char.unpack callback) Nothing - (Just [source], Just [callback], Just [format]) -> do + (Just [source], Just [callback], Just [format]) -> writeBS $ BS.Char.pack $ executeAndExport (BS.Char.unpack source) (BS.Char.unpack callback) @@ -83,8 +83,8 @@ getRes (varlookup, obj2s, obj3s) = where ((x1,y1,z1),(x2,y2,z2)) = getBox3 obj (x,y,z) = (x2-x1, y2-y1, z2-z1) - (obj:_, _) -> ( min (min x y/2) ((x*y )**0.5 / 30) - , min (min x y/2) ((x*y/qual)**0.5 / 30) ) + (obj:_, _) -> ( min (min x y/2) (sqrt(x*y ) / 30) + , min (min x y/2) (sqrt(x*y/qual) / 30) ) where ((x1,y1),(x2,y2)) = getBox2 obj (x,y) = (x2-x1, y2-y1) @@ -130,7 +130,7 @@ executeAndExport content callback maybeFormat = msgs = showErrorMessages' $ errorMessages err in callbackF False False 1 $ (\s-> "error (" ++ show line ++ "):" ++ s) msgs Right openscadProgram -> unsafePerformIO $ do - (msgs,s) <- capture $ openscadProgram + (msgs,s) <- capture openscadProgram let res = getRes s w = getWidth s From b1bc8f1e2dffb46f204beaf364609b8b4c2cf60e Mon Sep 17 00:00:00 2001 From: Junji Hashimoto Date: Wed, 7 Jun 2017 05:56:56 +0900 Subject: [PATCH 004/227] Fix openscad-export of cylinder --- Graphics/Implicit/Export/SymbolicFormats.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index 3ef0d35..ff362a0 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -55,7 +55,7 @@ buildS3 (Rect3R r (x1,y1,z1) (x2,y2,z2)) | r == 0 = call "translate" [bf x1, bf buildS3 (Sphere r) = callNaked "sphere" ["r = " <> bf r] [] -buildS3 (Cylinder h r1 r2) = call "cylinder" [ +buildS3 (Cylinder h r1 r2) = callNaked "cylinder" [ "r1 = " <> bf r1 ,"r2 = " <> bf r2 , bf h From 0b0f88fca898c187b60aad5678b13e1000dd1e80 Mon Sep 17 00:00:00 2001 From: Junji Hashimoto Date: Thu, 8 Jun 2017 05:41:04 +0900 Subject: [PATCH 005/227] Fix function-definition-test --- tests/ParserSpec/Statement.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 2726d05..049018c 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -34,9 +34,7 @@ assignmentSpec = do it "handles pattern matching" $ "[x, y] = [1, 2];" `parsesAs` (single $ ListP [Name "x", Name "y"] := (ListE [num 1, num 2])) - it "handles function definitions" $ - "foo (x, y) = x * y;" `parsesAs` single fooFunction - it "handles the function keyword" $ + it "handles the function keyword and definitions" $ "function foo(x, y) = x * y;" `parsesAs` single fooFunction it "nested indexing" $ "x = [y[0] - z * 2];" `parsesAs` From 4f9555b9f22d2f4d70d56df53ee63041c50f8915 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 9 Sep 2017 14:41:01 +0100 Subject: [PATCH 006/227] remove unneeded LANGUAGE pragmas --- Graphics/Implicit.hs | 2 +- .../Export/Symbolic/CoerceSymbolic2.hs | 2 +- .../Export/Symbolic/CoerceSymbolic3.hs | 2 +- Graphics/Implicit/Export/SymbolicObj2.hs | 2 +- Graphics/Implicit/Export/Util.hs | 2 +- .../Implicit/ExtOpenScad/Eval/Statement.hs | 30 +++++++++---------- {bench => programs}/ParserBench.hs | 0 7 files changed, 19 insertions(+), 21 deletions(-) rename {bench => programs}/ParserBench.hs (100%) diff --git a/Graphics/Implicit.hs b/Graphics/Implicit.hs index 92b8253..0d65907 100644 --- a/Graphics/Implicit.hs +++ b/Graphics/Implicit.hs @@ -3,7 +3,7 @@ -- Released under the GNU AGPLV3+, see LICENSE -- FIXME: Required. why? -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} {- The purpose of this file is to pass on the functionality we want to be accessible to an end user who is compiling objects using diff --git a/Graphics/Implicit/Export/Symbolic/CoerceSymbolic2.hs b/Graphics/Implicit/Export/Symbolic/CoerceSymbolic2.hs index 1f4f08c..b707de5 100644 --- a/Graphics/Implicit/Export/Symbolic/CoerceSymbolic2.hs +++ b/Graphics/Implicit/Export/Symbolic/CoerceSymbolic2.hs @@ -2,7 +2,7 @@ -- Copyright (C) 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} module Graphics.Implicit.Export.Symbolic.CoerceSymbolic2 (coerceSymbolic2) where diff --git a/Graphics/Implicit/Export/Symbolic/CoerceSymbolic3.hs b/Graphics/Implicit/Export/Symbolic/CoerceSymbolic3.hs index 63bc45f..af30ee9 100644 --- a/Graphics/Implicit/Export/Symbolic/CoerceSymbolic3.hs +++ b/Graphics/Implicit/Export/Symbolic/CoerceSymbolic3.hs @@ -2,7 +2,7 @@ -- Copyright (C) 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} -- We just want to export the instance... module Graphics.Implicit.Export.Symbolic.CoerceSymbolic3 (coerceSymbolic3) where diff --git a/Graphics/Implicit/Export/SymbolicObj2.hs b/Graphics/Implicit/Export/SymbolicObj2.hs index 9e518c1..624776b 100644 --- a/Graphics/Implicit/Export/SymbolicObj2.hs +++ b/Graphics/Implicit/Export/SymbolicObj2.hs @@ -3,7 +3,7 @@ -- Released under the GNU AGPLV3+, see LICENSE -- FIXME: why is all of this needed? -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} -- This file symbolicaly renders contours and contour fillings. -- If it can't, it passes the puck to a marching-squares-like diff --git a/Graphics/Implicit/Export/Util.hs b/Graphics/Implicit/Export/Util.hs index 36e339a..8da81f5 100644 --- a/Graphics/Implicit/Export/Util.hs +++ b/Graphics/Implicit/Export/Util.hs @@ -3,7 +3,7 @@ -- Released under the GNU AGPLV3+, see LICENSE -- FIXME: why are these needed? -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} -- Functions to make meshes/polylines finer. diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs index 3a4107c..116839d 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs @@ -2,7 +2,7 @@ -- Copyright 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE -{-# LANGUAGE ViewPatterns, ScopedTypeVariables #-} +{-# LANGUAGE ScopedTypeVariables #-} module Graphics.Implicit.ExtOpenScad.Eval.Statement where @@ -23,8 +23,10 @@ import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, errorC, modifyVarLooku import Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, matchPat) import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) +import Data.Maybe(fromMaybe) + import qualified Data.Map as Map -import qualified Control.Monad as Monad +import Control.Monad (forM_, forM, mapM_) import Control.Monad.State (get, liftIO, mapM, runStateT, (>>)) import qualified System.FilePath as FilePath @@ -52,7 +54,7 @@ runStatementI (StatementI lineN (For pat expr loopContent)) = do val <- evalExpr expr case (getErrors val, val) of (Just err, _) -> errorC lineN err - (_, OList vals) -> Monad.forM_ vals $ \v -> + (_, OList vals) -> forM_ vals $ \v -> case matchPat pat v of Just match -> do modifyVarLookup $ Map.union match @@ -69,13 +71,13 @@ runStatementI (StatementI lineN (If expr a b)) = do _ -> return () runStatementI (StatementI lineN (NewModule name argTemplate suite)) = do - argTemplate' <- Monad.forM argTemplate $ \(name', defexpr) -> do + argTemplate' <- forM argTemplate $ \(name', defexpr) -> do defval <- mapMaybeM evalExpr defexpr return (name', defval) (varlookup, _, path, _, _) <- get -- FIXME: \_? really? - runStatementI $ StatementI lineN $ (Name name :=) $ LitE $ OModule $ \_ -> do - newNameVals <- Monad.forM argTemplate' $ \(name', maybeDef) -> do + runStatementI . StatementI lineN $ (Name name :=) $ LitE $ OModule $ \_ -> do + newNameVals <- forM argTemplate' $ \(name', maybeDef) -> do val <- case maybeDef of Just def -> argument name' `defaultTo` def Nothing -> argument name' @@ -105,19 +107,17 @@ runStatementI (StatementI lineN (ModuleCall name argsExpr suite)) = do maybeMod <- lookupVar name (varlookup, _, path, _, _) <- get childVals <- fmap reverse . liftIO $ runSuiteCapture varlookup path suite - argsVal <- Monad.forM argsExpr $ \(posName, expr) -> do + argsVal <- forM argsExpr $ \(posName, expr) -> do val <- evalExpr expr return (posName, val) newVals <- case maybeMod of Just (OModule mod') -> liftIO ioNewVals where argparser = mod' childVals - ioNewVals = case fst $ argMap argsVal argparser of - Just iovals -> iovals - Nothing -> return [] + ioNewVals = fromMaybe (return []) (fst $ argMap argsVal argparser) Just foo -> do case getErrors foo of Just err -> errorC lineN err - Nothing -> errorC lineN $ "Object called not module!" + Nothing -> errorC lineN "Object called not module!" return [] Nothing -> do errorC lineN $ "Module " ++ name ++ " not in scope." @@ -127,7 +127,7 @@ runStatementI (StatementI lineN (ModuleCall name argsExpr suite)) = do runStatementI (StatementI _ (Include name injectVals)) = do name' <- getRelPath name content <- liftIO $ readFile name' - case parseProgram name content of + case parseProgram content of Left e -> liftIO $ putStrLn $ "Error parsing " ++ name ++ ":" ++ show e Right sts -> withPathShiftedBy (FilePath.takeDirectory name) $ do vals <- getVals @@ -136,12 +136,10 @@ runStatementI (StatementI _ (Include name injectVals)) = do vals' <- getVals if injectVals then putVals (vals' ++ vals) else putVals vals - -runStatementI (StatementI _ DoNothing) = do - liftIO $ putStrLn $ "Do Nothing?" +runStatementI (StatementI _ DoNothing) = liftIO $ putStrLn "Do Nothing?" runSuite :: [StatementI] -> StateC () -runSuite stmts = Monad.mapM_ runStatementI stmts +runSuite = mapM_ runStatementI runSuiteCapture :: VarLookup -> FilePath -> [StatementI] -> IO [OVal] runSuiteCapture varlookup path suite = do diff --git a/bench/ParserBench.hs b/programs/ParserBench.hs similarity index 100% rename from bench/ParserBench.hs rename to programs/ParserBench.hs From eeebc5d06349dd85b5a08a6cd0e85715ae4adccb Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 9 Sep 2017 15:21:33 +0100 Subject: [PATCH 007/227] spacing and comment changes only. --- Graphics/Implicit/Definitions.hs | 2 +- Graphics/Implicit/Export.hs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index c4a306d..0c6dc20 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -98,7 +98,7 @@ type ℕ = Integer (⋅) = (<.>) --- handle additional instances of Show. +-- add aditional instances to Show, for when we dump the intermediate form of an object. instance Show (ℝ -> ℝ) where show _ = "" diff --git a/Graphics/Implicit/Export.hs b/Graphics/Implicit/Export.hs index 8aeacda..9e84ae9 100644 --- a/Graphics/Implicit/Export.hs +++ b/Graphics/Implicit/Export.hs @@ -16,7 +16,7 @@ import Prelude (FilePath, IO, (.), ($)) -- The types of our objects (before rendering), and the type of the resolution to render with. import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3, ℝ, Polyline, TriangleMesh, Triangle, NormedTriangle) --- The functions for writing our output, as well as a type used. +-- functions for outputing a file, and one of the types. import Data.Text.Lazy (Text) import qualified Data.Text.Lazy.IO as LT (writeFile) import qualified Data.ByteString.Lazy as LBS (writeFile) @@ -39,7 +39,8 @@ writeObject :: (DiscreteAproxable obj aprox) -> obj -- ^ Object to render -> IO () -- ^ Writing Action! writeObject res format filename obj = - let aprox = formatObject res format obj + let + aprox = formatObject res format obj in LT.writeFile filename aprox -- Write an object using the given format writer. @@ -50,7 +51,9 @@ writeObject' :: (DiscreteAproxable obj aprox) -> obj -- ^ Object to render -> IO () -- ^ Writing Action! writeObject' res formatWriter filename obj = - let aprox = discreteAprox res obj + let +-- aprox :: (DiscreteAproxable aprox) => aprox + aprox = discreteAprox res obj in formatWriter filename aprox formatObject :: (DiscreteAproxable obj aprox) From f58fb29cb90e046edca3664b7162ca9d2d11dfac Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 9 Sep 2017 16:31:40 +0100 Subject: [PATCH 008/227] add kpe to the contributors list. --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 6d7363b..96402a9 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -20,6 +20,7 @@ chicagoduane -- Duane Johnson -- Duane.Johnson@gmail.com l29ah -- Sergey Alirzaev -- zl29ah@gmail.com firegurafiku -- Pavel Kretov -- firegurafiku@gmail.com gambogi -- Matthew Gambogi -- m@gambogi.com +kpe -- ?? -- ?? Thanks as well, to raghuugare. Due to not being contactable, his code has been removed during the license update. From b5af52a5269332af19dbf78ef2d93a17ee7f4bc3 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 02:06:09 +0100 Subject: [PATCH 009/227] perform simple changes. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 8 +++---- .../Implicit/Export/Render/HandleSquares.hs | 2 +- .../Implicit/Export/Render/TesselateLoops.hs | 8 +++---- Graphics/Implicit/Export/Symbolic/Rebound2.hs | 2 +- Graphics/Implicit/Export/Symbolic/Rebound3.hs | 2 +- Graphics/Implicit/ExtOpenScad/Parser/Util.hs | 24 +++++++++++-------- Graphics/Implicit/ExtOpenScad/Util/StateC.hs | 9 +++---- Graphics/Implicit/ObjectUtil/GetBox3.hs | 19 ++++++++------- 8 files changed, 41 insertions(+), 33 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index bab64df..3fe7ffe 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -35,7 +35,7 @@ class DiscreteAproxable obj aprox where discreteAprox :: ℝ -> obj -> aprox instance DiscreteAproxable SymbolicObj3 TriangleMesh where - discreteAprox res obj = symbolicGetMesh res obj + discreteAprox = symbolicGetMesh instance DiscreteAproxable SymbolicObj3 NormedTriangleMesh where discreteAprox res obj = map (normTriangle res (getImplicit3 obj)) $ symbolicGetMesh res obj @@ -60,7 +60,7 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where ((fromIntegral a :: ℝ)/w - (0.5::ℝ)) ((fromIntegral b :: ℝ)/h - (0.5 ::ℝ)) renderScreen :: ℝ -> ℝ -> Color renderScreen a b = - average $ [ + average [ traceRay (cameraRay camera ((a,b) ^+^ ( 0.25/w, 0.25/h))) 2 box scene, @@ -76,7 +76,7 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where ] instance DiscreteAproxable SymbolicObj2 [Polyline] where - discreteAprox res obj = symbolicGetContour res obj + discreteAprox = symbolicGetContour instance DiscreteAproxable SymbolicObj2 DynamicImage where discreteAprox _ symbObj = dynamicImage $ generateImage pixelRenderer (round w) (round h) @@ -91,7 +91,7 @@ instance DiscreteAproxable SymbolicObj2 DynamicImage where where xy a b = ((x1,y2) .-^ (dxy-dx, dy-dxy)^/2) .+^ dxy*^(a/w, -b/h) s = 0.25 :: ℝ - (a', b') = (realToFrac mya, realToFrac myb) :: (ℝ2) + (a', b') = (realToFrac mya, realToFrac myb) :: ℝ2 mycolor = average [objColor $ xy a' b', objColor $ xy a' b', objColor $ xy (a'+s) (b'+s), objColor $ xy (a'-s) (b'-s), diff --git a/Graphics/Implicit/Export/Render/HandleSquares.hs b/Graphics/Implicit/Export/Render/HandleSquares.hs index 843c695..4d2f961 100644 --- a/Graphics/Implicit/Export/Render/HandleSquares.hs +++ b/Graphics/Implicit/Export/Render/HandleSquares.hs @@ -66,7 +66,7 @@ mergedSquareTris sqTris = triTriangles = [tri | Tris tris <- sqTris, tri <- tris ] --concat $ map (\(Tris a) -> a) $ filter isTris sqTris -- We actually want to work on the quads, so we find those - squaresFromTris = [ (Sq x y z q) | Sq x y z q <- sqTris ] + squaresFromTris = [ Sq x y z q | Sq x y z q <- sqTris ] {- -- Collect ones that are on the same plane. planeAligned = groupWith (\(Sq basis z _ _) -> (basis,z)) squares diff --git a/Graphics/Implicit/Export/Render/TesselateLoops.hs b/Graphics/Implicit/Export/Render/TesselateLoops.hs index 0418c4f..9ed50a2 100644 --- a/Graphics/Implicit/Export/Render/TesselateLoops.hs +++ b/Graphics/Implicit/Export/Render/TesselateLoops.hs @@ -27,12 +27,12 @@ tesselateLoop _ _ [[a,b],[_,c],[_,_]] = return $ Tris [(a,b,c)] -} tesselateLoop res obj [[_,_], as@(_:_:_:_),[_,_], bs@(_:_:_:_)] | length as == length bs = - concatMap (tesselateLoop res obj) $ + concatMap (tesselateLoop res obj) [[[a1,b1],[b1,b2],[b2,a2],[a2,a1]] | ((a1,b1),(a2,b2)) <- zip (init pairs) (tail pairs)] where pairs = zip (reverse as) bs tesselateLoop res obj [as@(_:_:_:_),[_,_], bs@(_:_:_:_), [_,_] ] | length as == length bs = - concatMap (tesselateLoop res obj) $ + concatMap (tesselateLoop res obj) [[[a1,b1],[b1,b2],[b2,a2],[a2,a1]] | ((a1,b1),(a2,b2)) <- zip (init pairs) (tail pairs)] where pairs = zip (reverse as) bs @@ -58,7 +58,7 @@ tesselateLoop _ _ [[a,_],[b,_],[c,_],[d,_]] | centroid [a,c] == centroid [b,d] = -} tesselateLoop res obj [[a,_],[b,_],[c,_],[d,_]] | obj (centroid [a,c]) < res/30 = - return $ Tris $ [(a,b,c),(a,c,d)] + return $ Tris [(a,b,c),(a,c,d)] -- Fallback case: make fans @@ -71,7 +71,7 @@ tesselateLoop res obj pathSides = return $ Tris $ else let mid@(_,_,_) = centroid path midval = obj mid - preNormal = foldl1 (^+^) $ + preNormal = foldl1 (^+^) [ a `cross3` b | (a,b) <- zip path (tail path ++ [head path]) ] preNormalNorm = magnitude preNormal normal = preNormal ^/ preNormalNorm diff --git a/Graphics/Implicit/Export/Symbolic/Rebound2.hs b/Graphics/Implicit/Export/Symbolic/Rebound2.hs index 5b0214e..698e1ad 100644 --- a/Graphics/Implicit/Export/Symbolic/Rebound2.hs +++ b/Graphics/Implicit/Export/Symbolic/Rebound2.hs @@ -16,4 +16,4 @@ rebound2 (obj, (a,b)) = d :: ℝ2 d = (b ^-^ a) ^/ 10 in - (obj, ((a ^-^ d), (b ^+^ d))) + (obj, (a ^-^ d, b ^+^ d)) diff --git a/Graphics/Implicit/Export/Symbolic/Rebound3.hs b/Graphics/Implicit/Export/Symbolic/Rebound3.hs index 36fd8be..c5050bb 100644 --- a/Graphics/Implicit/Export/Symbolic/Rebound3.hs +++ b/Graphics/Implicit/Export/Symbolic/Rebound3.hs @@ -16,5 +16,5 @@ rebound3 (obj, (a,b)) = d :: ℝ3 d = (b ^-^ a) ^/ 10 in - (obj, ((a ^-^ d), (b ^+^ d))) + (obj, (a ^-^ d, b ^+^ d)) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs index 7e231d7..c05775a 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs @@ -13,26 +13,29 @@ module Graphics.Implicit.ExtOpenScad.Parser.Util (genSpace, pad, (*<|>), (?:), s import Prelude (String, Char, ($), (++), foldl1, map, (>>), (.), return) import Text.ParserCombinators.Parsec (GenParser, many, oneOf, noneOf, (<|>), try, string, manyTill, anyChar, (), char, many1, sepBy) + import Text.Parsec.Prim (ParsecT, Stream) + import Data.Functor.Identity (Identity) + import Graphics.Implicit.ExtOpenScad.Definitions (Pattern(Wild, Name, ListP)) -- white space, including tabs, newlines and comments -genSpace :: ParsecT [Char] u Identity [Char] +genSpace :: ParsecT String u Identity String genSpace = many $ oneOf " \t\n\r" - <|> (try $ do + <|> try ( do _ <- string "//" _ <- many ( noneOf "\n") - _ <- string "\n" return ' ' - ) <|> (try $ do + ) <|> try ( do _ <- string "/*" _ <- manyTill anyChar (try $ string "*/") return ' ' ) -pad :: forall b u. ParsecT [Char] u Identity b -> ParsecT [Char] u Identity b +-- a padded ... parser? +pad :: ParsecT String u Identity b -> ParsecT String u Identity b pad parser = do _ <- genSpace a <- parser @@ -47,7 +50,7 @@ infixr 2 ?: (?:) :: forall s u (m :: * -> *) a. String -> ParsecT s u m a -> ParsecT s u m a l ?: p = p l -stringGS :: [Char] -> ParsecT [Char] u Identity [Char] +stringGS :: String -> ParsecT String u Identity String stringGS (' ':xs) = do x' <- genSpace xs' <- stringGS xs @@ -58,7 +61,8 @@ stringGS (x:xs) = do return (x' : xs') stringGS "" = return "" -padString :: String -> ParsecT [Char] u Identity String +-- a padded string +padString :: String -> ParsecT String u Identity String padString s = do _ <- genSpace s' <- string s @@ -66,9 +70,9 @@ padString s = do return s' tryMany :: forall u a tok. [GenParser tok u a] -> ParsecT [tok] u Identity a -tryMany = (foldl1 (<|>)) . (map try) +tryMany = foldl1 (<|>) . map try -variableSymb :: forall s u (m :: * -> *). Stream s m Char => ParsecT s u m [Char] +variableSymb :: forall s u (m :: * -> *). Stream s m Char => ParsecT s u m String variableSymb = many1 (noneOf " ,|[]{}()+-*&^%#@!~`'\"\\/;:.,<>?=") "variable" patternMatcher :: GenParser Char st Pattern @@ -88,7 +92,7 @@ patternMatcher = ) <|> ( do _ <- char '[' _ <- genSpace - components <- patternMatcher `sepBy` (try $ genSpace >> char ',' >> genSpace) + components <- patternMatcher `sepBy` try (genSpace >> char ',' >> genSpace) _ <- genSpace _ <- char ']' return $ ListP components diff --git a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs index 2bab1e1..ce8c07f 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs @@ -7,11 +7,11 @@ -- FIXME: required. why? {-# LANGUAGE KindSignatures, FlexibleContexts #-} -{-# LANGUAGE ViewPatterns, RankNTypes, ScopedTypeVariables #-} +{-# LANGUAGE RankNTypes, ScopedTypeVariables #-} module Graphics.Implicit.ExtOpenScad.Util.StateC (getVarLookup, modifyVarLookup, lookupVar, pushVals, getVals, putVals, withPathShiftedBy, getPath, getRelPath, errorC, mapMaybeM, StateC) where -import Prelude(FilePath, IO, String, Maybe(Just, Nothing), Show, Char, Monad, fmap, (.), ($), (++), return, putStrLn, show) +import Prelude(FilePath, IO, String, Maybe(Just, Nothing), Show, Monad, fmap, (.), ($), (++), return, putStrLn, show) import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup, OVal) @@ -20,6 +20,7 @@ import Control.Monad.State (StateT, get, put, modify, liftIO) import System.FilePath(()) import Control.Monad.IO.Class (MonadIO) +-- This is the state machine. It contains the variables, their values, the path, and... ? type CompState = (VarLookup, [OVal], FilePath, (), ()) type StateC = StateT CompState IO @@ -50,7 +51,7 @@ putVals vals = do withPathShiftedBy :: FilePath -> StateC a -> StateC a withPathShiftedBy pathShift s = do (a,b,path,d,e) <- get - put (a,b, path pathShift, d, e) + put (a, b, path pathShift, d, e) x <- s (a',b',_,d',e') <- get put (a', b', path, d', e') @@ -66,7 +67,7 @@ getRelPath relPath = do path <- getPath return $ path relPath -errorC :: forall (m :: * -> *) a. (Show a, MonadIO m) => a -> [Char] -> m () +errorC :: forall (m :: * -> *) a. (Show a, MonadIO m) => a -> String -> m () errorC lineN err = liftIO $ putStrLn $ "At " ++ show lineN ++ ": " ++ err mapMaybeM :: forall t (m :: * -> *) a. Monad m => (t -> m a) -> Maybe t -> m (Maybe a) diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index c4a255f..6575a82 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -4,11 +4,11 @@ -- Released under the GNU AGPLV3+, see LICENSE -- FIXME: required. why? -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} module Graphics.Implicit.ObjectUtil.GetBox3 (getBox3) where -import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), Maybe(Nothing, Just), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, sqrt, (>), (&&), head, (*), (<), abs, either, error, const) +import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), Maybe(Nothing, Just), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, sqrt, (>), (&&), head, (*), (<), abs, either, error, const, otherwise) import Graphics.Implicit.Definitions (ℝ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), (⋯*)) import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) @@ -29,7 +29,7 @@ outsetBox r (a,b) = getBox3 :: SymbolicObj3 -> Box3 -- Primitives getBox3 (Rect3R _ a b) = (a,b) -getBox3 (Sphere r ) = ((-r, -r, -r), (r,r,r)) +getBox3 (Sphere r) = ((-r, -r, -r), (r,r,r)) getBox3 (Cylinder h r1 r2) = ( (-r,-r,0), (r,r,h) ) where r = max r1 r2 -- (Rounded) CSG getBox3 (Complement3 _) = @@ -84,7 +84,7 @@ getBox3 (Scale3 s symbObj) = getBox3 (Rotate3 _ symbObj) = ( (-d, -d, -d), (d, d, d) ) where ((x1,y1, z1), (x2,y2, z2)) = getBox3 symbObj - d = (sqrt 3 *) $ maximum $ map abs [x1, x2, y1, y2, z1, z2] + d = (sqrt 3 *) . maximum $ map abs [x1, x2, y1, y2, z1, z2] getBox3 (Rotate3V _ v symbObj) = getBox3 (Rotate3 v symbObj) -- Boundary mods getBox3 (Shell3 w symbObj) = @@ -110,7 +110,7 @@ getBox3 (ExtrudeRM _ twist scale translate symbObj eitherh) = range = [0, 0.1 .. 1.0] ((x1,y1),(x2,y2)) = getBox2 symbObj (dx,dy) = (x2 - x1, y2 - y1) - (xrange, yrange) = (map (\s -> x1+s*dx) $ range, map (\s -> y1+s*dy) $ range ) + (xrange, yrange) = (map (\s -> x1+s*dx) range, map (\s -> y1+s*dy) range ) h = case eitherh of Left h' -> h' @@ -118,7 +118,7 @@ getBox3 (ExtrudeRM _ twist scale translate symbObj eitherh) = where hs = [hf (x,y) | x <- xrange, y <- yrange] (hmin, hmax) = (minimum hs, maximum hs) - hrange = map (h*) $ range + hrange = map (h*) range sval = case scale of Nothing -> 1 Just scale' -> maximum $ map (abs . scale') hrange @@ -143,6 +143,7 @@ getBox3 (RotateExtrude _ _ (Left (xshift,yshift)) _ symbObj) = r = max x2 (x2 + xshift) in ((-r, -r, min y1 (y1 + yshift)),(r, r, max y2 (y2 + yshift))) +-- FIXME: magic numbers getBox3 (RotateExtrude rot _ (Right f) rotate symbObj) = let ((x1,y1),(x2,y2)) = getBox2 symbObj @@ -150,7 +151,9 @@ getBox3 (RotateExtrude rot _ (Right f) rotate symbObj) = xmax = maximum xshifts ymax = maximum yshifts ymin = minimum yshifts - xmax' = if xmax > 0 then xmax * 1.1 else if xmax < - x1 then 0 else xmax + xmax' | xmax > 0 = xmax * 1.1 + | xmax < - x1 = 0 + | otherwise = xmax ymax' = ymax + 0.1 * (ymax - ymin) ymin' = ymin - 0.1 * (ymax - ymin) (r, _, _) = if either (==0) (const False) rotate @@ -161,4 +164,4 @@ getBox3 (RotateExtrude rot _ (Right f) rotate symbObj) = in ((-r, -r, y1 + ymin'),(r, r, y2 + ymax')) -- FIXME: add case for ExtrudeRotateR! -getBox3(ExtrudeRotateR _ _ _ _ ) = error "ExtrudeRotateR implementation incomplete!" +getBox3 ExtrudeRotateR{} = error "ExtrudeRotateR implementation incomplete!" From 0b9f8112f7453a6105f0edf01a9640f2175d2fc2 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 02:44:14 +0100 Subject: [PATCH 010/227] add and use *** from control.arrow, remove more warnings, do some formatting changes, and remove unneeded parenthesis. --- Graphics/Implicit/Export/MarchingSquares.hs | 79 +++++++++------------ 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index 0a07d28..3b91428 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -2,33 +2,35 @@ -- Copyright 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE +-- Allow us to use explicit foralls when writing function type declarations. +{-# LANGUAGE ExplicitForAll #-} + module Graphics.Implicit.Export.MarchingSquares (getContour) where -import Prelude(Int, Bool(True, False), ceiling, fromIntegral, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), (.), splitAt, div, unzip, length, (++), (<), (++), head, concat, not, null, (||), Eq, Int, fst, snd) +import Prelude(Int, Bool(True, False), ceiling, fromIntegral, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), (.), splitAt, div, unzip, length, (++), (<), (++), head, concat, not, null, (||), Eq, Int) import Graphics.Implicit.Export.Render.HandlePolylines (reducePolyline) -import Graphics.Implicit.Definitions (ℝ2, Polyline, Obj2, (⋯/), (⋯*)) +import Graphics.Implicit.Definitions (ℝ, ℝ2, Polyline, Obj2, (⋯/), (⋯*)) -- FIXME: commented out for now, parallelism is not properly implemented. -- import Control.Parallel.Strategies (using, parList, rdeepseq) + import Data.VectorSpace ((^-^), (^+^)) -both :: (a -> b) -> (a,a) -> (b,b) +import Control.Arrow((***)) + +-- we are explicit here, so GHC knows what types n is made up of in getContour. +both :: (ℝ -> Int) -> ℝ2 -> (Int, Int) both f (x,y) = (f x, f y) --- | getContour gets a polyline describe the edge of your 2D --- object. It's really the only function in this file you need --- to care about from an external perspective. - +-- getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polyline] getContour p1 p2 d obj = let -- How many steps will we take on each axis? n :: (Int, Int) - n = (ceiling) `both` ((p2 ^-^ p1) ⋯/ d) - nx = fst n - ny = snd n + n@(nx, ny) = ceiling `both` ((p2 ^-^ p1) ⋯/ d) -- Divide it up and compute the polylines gridPos :: (Int,Int) -> (Int,Int) -> ℝ2 gridPos (nx',ny') (mx,my) = @@ -39,25 +41,16 @@ getContour p1 p2 d obj = in p1 ^+^ (p2 ^-^ p1) ⋯* p linesOnGrid :: [[[Polyline]]] - linesOnGrid = [[getSquareLineSegs - (gridPos n (mx,my)) - (gridPos n (mx+1,my+1)) - obj - | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] + linesOnGrid = [[getSquareLineSegs (gridPos n (mx,my)) (gridPos n (mx+1,my+1)) obj + | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] + in -- Cleanup, cleanup, everybody cleanup! -- (We connect multilines, delete redundant vertices on them, etc) - multilines = (filter polylineNotNull) $ (map reducePolyline) $ orderLinesDC $ linesOnGrid - in - multilines + filter polylineNotNull . map reducePolyline $ orderLinesDC linesOnGrid -- FIXME: Commented out, not used? {- -getContour2 :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polyline] -getContour2 p1@(x1, y1) p2@(x2, y2) d obj = - let - -- How many steps will we take on each axis? - n@(nx,ny) = (fromIntegral . ceiling) `both` ((p2 ^-^ p1) ⋯/ d) - -- Grid mapping funcs + -- alternate Grid mapping funcs fromGrid (mx, my) = let p = (mx/nx, my/ny) in (p1 ^+^ (p2 ^-^ p1) ⋯/ p) toGrid (x,y) = (floor $ nx*(x-x1)/(x2-x1), floor $ ny*(y-y1)/(y2-y1)) @@ -71,15 +64,10 @@ getContour2 p1@(x1, y1) p2@(x2, y2) d obj = linesOnGrid :: [[[Polyline]]] linesOnGrid = [[getSquareLineSegs (fromGrid (mx, my)) (fromGrid (mx+1, my+1)) preEvaledObj | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] - -- Cleanup, cleanup, everybody cleanup! - -- (We connect multilines, delete redundant vertices on them, etc) - multilines = (filter polylineNotNull) $ (map reducePolyline) $ orderLinesDC $ linesOnGrid - in - multilines -} -- | This function gives line segments to divide negative interior --- regions and positive exterior ones inside a square, based on its +-- regions and positive exterior ones inside a square, based on the -- values at its vertices. -- It is based on the linearly-interpolated marching squares algorithm. @@ -88,15 +76,14 @@ getSquareLineSegs (x1, y1) (x2, y2) obj = let (x,y) = (x1, y1) - -- Let's evlauate obj at a few points... + -- Let's evlauate obj at four corners... x1y1 = obj (x1, y1) x2y1 = obj (x2, y1) x1y2 = obj (x1, y2) x2y2 = obj (x2, y2) - c = obj ((x1+x2)/2, (y1+y2)/2) - dx = x2 - x1 - dy = y2 - y1 + -- And the center point.. + c = obj ((x1+x2)/2, (y1+y2)/2) -- linearly interpolated midpoints on the relevant axis -- midy2 @@ -111,17 +98,22 @@ getSquareLineSegs (x1, y1) (x2, y2) obj = -- ---------*---------- -- midy1 + + dx = x2 - x1 + dy = y2 - y1 + midx1 = (x, y + dy*x1y1/(x1y1-x1y2)) midx2 = (x + dx, y + dy*x2y1/(x2y1-x2y2)) midy1 = (x + dx*x1y1/(x1y1-x2y1), y ) midy2 = (x + dx*x1y2/(x1y2-x2y2), y + dy) + notPointLine :: Eq a => [a] -> Bool - notPointLine (p1:p2:[]) = p1 /= p2 - notPointLine ([]) = False - notPointLine ([_]) = False - notPointLine (_ : (_ : (_ : _))) = False - in filter (notPointLine) $ case (x1y2 <= 0, x2y2 <= 0, - x1y1 <= 0, x2y1 <= 0) of + notPointLine (start:stop:xs) = start /= stop || notPointLine [stop:xs] + notPointLine [_] = False + notPointLine [] = False + + in filter notPointLine $ case (x1y2 <= 0, x2y2 <= 0, + x1y1 <= 0, x2y1 <= 0) of -- Yes, there's some symetries that could reduce the amount of code... -- But I don't think they're worth exploiting... (True, True, @@ -162,8 +154,7 @@ getSquareLineSegs (x1, y1) (x2, y2) obj = else [[midx1, midy1], [midx2, midy2]] - --- $ Functions for cleaning up the polylines +-- Functions for cleaning up the polylines -- Many have multiple implementations as efficiency experiments. -- At some point, we'll get rid of the redundant ones.... @@ -187,10 +178,10 @@ orderLinesDC segs = let halve :: [a] -> ([a], [a]) halve l = splitAt (div (length l) 2) l - splitOrder segs' = case (\(x,y) -> (halve x, halve y)) . unzip . map (halve) $ segs' of + splitOrder segs' = case (halve *** halve) . unzip . map halve $ segs' of ((a,b),(c,d)) -> orderLinesDC a ++ orderLinesDC b ++ orderLinesDC c ++ orderLinesDC d in - if (length segs < 5 || length (head segs) < 5 ) then concat $ concat segs else + if length segs < 5 || length (head segs) < 5 then concat $ concat segs else splitOrder segs {- orderLinesP :: [[[Polyline]]] -> [Polyline] From 70fefd0ebbb0b8194cca8e039ac8d143904652f2 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 03:16:54 +0100 Subject: [PATCH 011/227] import less, clear up some type definitions, and drop unneeded function arguments. --- Graphics/Implicit/Export/PolylineFormats.hs | 31 +++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index 157ef74..71233a1 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -9,9 +9,9 @@ module Graphics.Implicit.Export.PolylineFormats where -import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max, Ord, Num) +import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max) -import Graphics.Implicit.Definitions (Polyline, ℝ2) +import Graphics.Implicit.Definitions (Polyline, ℝ, ℝ2) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, mempty, toLazyText, mconcat, bf, (<>), buildTruncFloat) @@ -20,21 +20,22 @@ import Text.Blaze.Svg11 ((!),docTypeSvg,g,polyline,toValue) import Text.Blaze.Internal (stringValue) import qualified Text.Blaze.Svg11.Attributes as A -import qualified Data.List as List +import Data.List (sortBy) svg :: [Polyline] -> Text svg plines = renderSvg . svg11 . svg' $ plines where + strokeWidth :: ℝ strokeWidth = 1.0 - (xmin, xmax, ymin, ymax) = ((minimum xs) - margin, (maximum xs) + margin, (minimum ys) - margin, (maximum ys) + margin) + (xmin, xmax, ymin, ymax) = (minimum xs - margin, maximum xs + margin, minimum ys - margin, maximum ys + margin) where margin = strokeWidth / 2 (xs,ys) = unzip (concat plines) - svg11 content = docTypeSvg ! A.version "1.1" - ! A.width (stringValue $ show (xmax-xmin) ++ "mm") - ! A.height (stringValue $ show (ymax-ymin) ++ "mm") - ! A.viewbox (stringValue $ unwords . map show $ [0,0,xmax-xmin,ymax-ymin]) - $ content + svg11 = docTypeSvg ! A.version "1.1" + ! A.width (stringValue $ show (xmax-xmin) ++ "mm") + ! A.height (stringValue $ show (ymax-ymin) ++ "mm") + ! A.viewbox (stringValue $ unwords . map show $ [0,0,xmax-xmin,ymax-ymin]) + -- The reason this isn't totally straightforwards is that svg has different coordinate system -- and we need to compute the requisite translation. svg' [] = mempty @@ -49,17 +50,18 @@ svg plines = renderSvg . svg11 . svg' $ plines hacklabLaserGCode :: [Polyline] -> Text hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpretPolyline orderedPolylines) <> gcodeFooter - where + where + orderedPolylines :: [Polyline] orderedPolylines = - snd . unzip - . List.sortBy (\(a,_) (b, _) -> compare a b) + map snd + . sortBy (\(a,_) (b, _) -> compare a b) . map (\x -> (polylineRadius x, x)) $ polylines - polylineRadius :: forall t. (Ord t, Num t) => [(t, t)] -> t + polylineRadius :: [(ℝ, ℝ)] -> ℝ polylineRadius [] = 0 polylineRadius polyline' = max (xmax' - xmin') (ymax' - ymin') where ((xmin', xmax'), (ymin', ymax')) = polylineRadius' polyline' - polylineRadius' :: forall a a1. (Ord a1, Ord a, Num a1, Num a) => [(a, a1)] -> ((a, a), (a1, a1)) + polylineRadius' :: [(ℝ, ℝ)] -> ((ℝ, ℝ), (ℝ, ℝ)) polylineRadius' [] = ((0,0),(0,0)) polylineRadius' [(x,y)] = ((x,x),(y,y)) polylineRadius' ((x,y):ps) = ((min x xmin,max x xmax),(min y ymin, max y ymax)) @@ -79,7 +81,6 @@ hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpret ,"M2 (end)"] gcodeXY :: ℝ2 -> Builder gcodeXY (x,y) = mconcat ["X", buildTruncFloat x, " Y", buildTruncFloat y] - interpretPolyline (start:others) = mconcat [ "G00 ", gcodeXY start ,"\nM62 P0 (laser on)\n" From adec5bdfd80dccffb5a895018bdb733ea1b099f6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 03:18:53 +0100 Subject: [PATCH 012/227] import less, import and use ***, remove extra ()s, clear up some type definitions, and drop unneeded function arguments. --- Graphics/Implicit/Export/RayTrace.hs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index 32317ae..b25681e 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -10,11 +10,12 @@ module Graphics.Implicit.Export.RayTrace where -import Prelude(Show, RealFrac, Maybe(Just, Nothing), Int, Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, (++), not, abs, floor, fromIntegral, toRational) +import Prelude(Show, RealFrac, Maybe(Just, Nothing), Int, Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, fromIntegral, toRational, otherwise) import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, (⋅), Obj3) import Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) import Control.Monad (guard, return) +import Control.Arrow ((***)) import Data.VectorSpace (Scalar, magnitude, (^+^), (*^), normalized, (^-^), InnerSpace) import Data.Cross (cross3) @@ -34,7 +35,7 @@ data Scene = Scene Obj3 Color [Light] Color type Color = PixelRGBA8 color :: Pixel8 -> Pixel8 -> Pixel8 -> Pixel8 -> PixelRGBA8 -color r g b a = PixelRGBA8 r g b a +color = PixelRGBA8 dynamicImage :: Image PixelRGBA8 -> DynamicImage dynamicImage = ImageRGBA8 @@ -55,7 +56,7 @@ s `colorMult` (PixelRGBA8 a b c d) = color (s `mult` a) (s `mult` b) (s `mult` c average :: [Color] -> Color average l = let - ((rs, gs), (bs, as)) = (\(a'',b'') -> (unzip a'', unzip b'')) $ unzip $ map + ((rs, gs), (bs, as)) = (unzip *** unzip) . unzip $ map (\(PixelRGBA8 r g b a) -> ((fromIntegral r, fromIntegral g), (fromIntegral b, fromIntegral a))) l :: (([ℝ], [ℝ]), ([ℝ],[ℝ])) n = fromIntegral $ length l :: ℝ @@ -96,10 +97,9 @@ rayBounds ray box = intersection :: Ray -> ((ℝ,ℝ), ℝ) -> ℝ -> Obj3 -> Maybe ℝ3 intersection r@(Ray p v) ((a, aval),b) res obj = let - step = - if aval/(4::ℝ) > res then res - else if aval/(2::ℝ) > res then res/(2 :: ℝ) - else res/(10 :: ℝ) + step | aval/(4::ℝ) > res = res + | aval/(2::ℝ) > res = res/(2 :: ℝ) + | otherwise = res/(10 :: ℝ) a' = a + step a'val = obj (p ^+^ a'*^v) in if a'val < 0 @@ -143,7 +143,7 @@ traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultCo let (a,b) = rayBounds ray box in case intersection ray ((a, obj (cameraP ^+^ a*^cameraV)), b) step obj of - Just p -> flip colorMult objColor $ floor (sum $ [0.2] ++ do + Just p -> flip colorMult objColor $ floor (sum $ 0.2 : do Light lightPos lightIntensity <- lights let ray'@(Ray _ v) = rayFromTo p lightPos @@ -154,19 +154,19 @@ traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultCo dirDeriv :: ℝ3 -> ℝ dirDeriv v'' = (obj (p ^+^ step*^v'') ^-^ pval)/step deriv = (dirDeriv (1,0,0), dirDeriv (0,1,0), dirDeriv (0,0,1)) - normal = normalized $ deriv - unitV = normalized $ v' + normal = normalized deriv + unitV = normalized v' proj :: forall v. InnerSpace v => v -> v -> v proj a' b' = (a'⋅b')*^b' dist = vectorDistance p lightPos - illumination = (max 0 (normal ⋅ unitV)) * lightIntensity * (25 /dist) + illumination = max 0 (normal ⋅ unitV) * lightIntensity * (25 /dist) rV = let normalComponent = proj v' normal parComponent = v' - normalComponent in normalComponent - parComponent - return $ illumination*(3 + 0.3*(abs $ rV ⋅ cameraV)*(abs $ rV ⋅ cameraV)) + return $ illumination*(3 + 0.3*abs(rV ⋅ cameraV)*abs(rV ⋅ cameraV)) ) Nothing -> defaultColor From f96a4e9b1866725b3495766d8eda95a12f4d3579 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 03:24:00 +0100 Subject: [PATCH 013/227] remove unnecessary ()s, use function concatenation more, and make the app functions do the work instead of returning a function. --- Graphics/Implicit/Export/Render.hs | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 67ba655..cedd529 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -97,7 +97,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = (\n -> y1 + ry*fromInteger (my+n)) my (\n -> z1 + rz*fromInteger (mz+n)) mz | mx <- [0..lenx] ] | my <- [0..leny] ] | mz <- [0..lenz] ] - `using` (parBuffer (max 1 . fromInteger $ div lenz 32) rdeepseq) + `using` parBuffer (max 1 . fromInteger $ div lenz 32) rdeepseq -- Evaluate obj to avoid waste in mids, segs, later. objV = par3DList (nx+2) (ny+2) (nz+2) $ \x _ y _ z _ -> obj (x 0, y 0, z 0) @@ -108,21 +108,21 @@ getMesh p1@(x1,y1,z1) p2 res obj = | x0 <- pXs | objX0Y0Z0 <- objY0Z0 | objX0Y0Z1 <- objY0Z1 ]| y0 <- pYs | objY0Z0 <- objZ0 | objY0Z1 <- objZ1 ]| z0 <- pZs | z1' <- tail pZs | objZ0 <- objV | objZ1 <- tail objV - ] `using` (parBuffer (max 1 . fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 . fromInteger $ div nz 32) rdeepseq midsY = [[[ interpolate (y0, objX0Y0Z0) (y1', objX0Y1Z0) (appAC obj x0 z0) res | x0 <- pXs | objX0Y0Z0 <- objY0Z0 | objX0Y1Z0 <- objY1Z0 ]| y0 <- pYs | y1' <- tail pYs | objY0Z0 <- objZ0 | objY1Z0 <- tail objZ0 ]| z0 <- pZs | objZ0 <- objV - ] `using` (parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq midsX = [[[ interpolate (x0, objX0Y0Z0) (x1', objX1Y0Z0) (appBC obj y0 z0) res | x0 <- pXs | x1' <- tail pXs | objX0Y0Z0 <- objY0Z0 | objX1Y0Z0 <- tail objY0Z0 ]| y0 <- pYs | objY0Z0 <- objZ0 ]| z0 <- pZs | objZ0 <- objV - ] `using` (parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq -- Calculate segments for each side segsZ = [[[ @@ -135,7 +135,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <- objZ0 | objY1Z0 <- tail objZ0 ]|z0<-pZs |mX' <-midsX| mY' <-midsY |objZ0 <- objV - ] `using` (parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq segsY = [[[ map2 (inj2 y0) $ getSegs (x0,z0) (x1',z1') (obj *$* y0) @@ -147,7 +147,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <- objZ0 | objY0Z1 <- objZ1 ]|z0<-pZs|z1'<-tail pZs|mB' <-midsX|mBT <-tail midsX|mA' <-midsZ |objZ0 <- objV | objZ1 <- tail objV - ] `using` (parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq segsX = [[[ map2 (inj1 x0) $ getSegs (y0,z0) (y1',z1') (obj $** x0) @@ -159,7 +159,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <-objZ0 |objY1Z0 <-tail objZ0 |objY0Z1 <-objZ1 |objY1Z1 <-tail objZ1 ]|z0<-pZs|z1'<-tail pZs|mB' <-midsY|mBT <-tail midsY|mA' <-midsZ |objZ0 <- objV | objZ1 <- tail objV - ] `using` (parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq -- (3) & (4) : get and tesselate loops sqTris = [[[ @@ -183,9 +183,9 @@ getMesh p1@(x1,y1,z1) p2 res obj = ]| segZ' <- segsZ | segZT <- tail segsZ | segY' <- segsY | segX' <- segsX - ] `using` (parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq) + ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq - in cleanupTris $ mergedSquareTris $ concat $ concat $ concat sqTris -- (5) merge squares, etc + in cleanupTris . mergedSquareTris . concat . concat $ concat sqTris -- (5) merge squares, etc -- Removes triangles that are empty, when converting their positions to Float resolution. -- NOTE: this will need to be disabled for AMF, and other triangle formats that can handle Double. @@ -225,7 +225,7 @@ getContour p1@(x1, y1) p2 res obj = (\n -> x1 + rx*fromIntegral (mx+n)) mx (\n -> y1 + ry*fromIntegral (my+n)) my | mx <- [0..lenx] ] | my <- [0..leny] ] - `using` (parBuffer (max 1 $ fromInteger $ div leny 32) rdeepseq) + `using` parBuffer (max 1 . fromInteger $ div leny 32) rdeepseq -- Evaluate obj to avoid waste in mids, segs, later. @@ -238,13 +238,13 @@ getContour p1@(x1, y1) p2 res obj = interpolate (y0, objX0Y0) (y1', objX0Y1) (obj $* x0) res | x0 <- pXs | objX0Y0 <- objY0 | objX0Y1 <- objY1 ]| y0 <- pYs | y1' <- tail pYs | objY0 <- objV | objY1 <- tail objV - ] `using` (parBuffer (max 1 $ fromInteger $ div ny 32) rdeepseq) + ] `using` parBuffer (max 1 . fromInteger $ div ny 32) rdeepseq midsX = [[ interpolate (x0, objX0Y0) (x1', objX1Y0) (obj *$ y0) res | x0 <- pXs | x1' <- tail pXs | objX0Y0 <- objY0 | objX1Y0 <- tail objY0 ]| y0 <- pYs | objY0 <- objV - ] `using` (parBuffer (max 1 $ fromInteger $ div ny 32) rdeepseq) + ] `using` parBuffer (max 1 . fromInteger $ div ny 32) rdeepseq -- Calculate segments for each side @@ -256,9 +256,9 @@ getContour p1@(x1, y1) p2 res obj = |objX0Y0<-objY0|objX1Y0<-tail objY0|objX0Y1<-objY1|objX1Y1<-tail objY1 ]|y0<-pYs|y1'<-tail pYs|mX'' <-midsX|mX'T <-tail midsX|mY'' <-midsY |objY0 <- objV | objY1 <- tail objV - ] `using` (parBuffer (max 1 $ fromInteger $ div ny 32) rdeepseq) + ] `using` parBuffer (max 1 . fromInteger $ div ny 32) rdeepseq - in cleanLoopsFromSegs $ concat $ concat $ segs -- (5) merge squares, etc + in cleanLoopsFromSegs . concat $ concat segs -- (5) merge squares, etc @@ -291,11 +291,11 @@ f *$* b = \(a,c) -> f (a,b,c) f **$ c = \(a,b) -> f (a,b,c) appAB :: forall t t1 t2 t3. ((t1, t2, t3) -> t) -> t1 -> t2 -> t3 -> t -appAB f a b = \c -> f (a,b,c) +appAB f a b c = f (a,b,c) appBC :: forall t t1 t2 t3. ((t1, t2, t3) -> t) -> t2 -> t3 -> t1 -> t -appBC f b c = \a -> f (a,b,c) +appBC f b c a = f (a,b,c) appAC :: forall t t1 t2 t3. ((t1, t2, t3) -> t) -> t1 -> t3 -> t2 -> t -appAC f a c = \b -> f (a,b,c) +appAC f a c b = f (a,b,c) map2 :: forall a b. (a -> b) -> [[a]] -> [[b]] map2 f = map (map f) From 99494edcfee10e8b93c778cf486d7312d289353a Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 03:26:42 +0100 Subject: [PATCH 014/227] import and use partition, and comment cleanups. --- Graphics/Implicit/Export/Render/GetLoops.hs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/Export/Render/GetLoops.hs b/Graphics/Implicit/Export/Render/GetLoops.hs index 4faeffb..1c16eba 100644 --- a/Graphics/Implicit/Export/Render/GetLoops.hs +++ b/Graphics/Implicit/Export/Render/GetLoops.hs @@ -8,8 +8,9 @@ module Graphics.Implicit.Export.Render.GetLoops (getLoops) where -- Explicitly include what we want from Prelude. -import Prelude (Eq, head, last, tail, (==), Bool(False), filter, not, (.), null, error, (++)) +import Prelude (Eq, head, last, tail, (==), Bool(False), (.), null, error, (++)) +import Data.List (partition) -- The goal of getLoops is to extract loops from a list of segments. -- The input is a list of segments. @@ -51,7 +52,7 @@ getLoops' [] [] = [] getLoops' (x:xs) [] = getLoops' xs [x] -- A loop is finished if its start and end are the same. --- In this case, we return it and empty the building loop. +-- In this case, we return it and start searching for another loop. getLoops' segs workingLoop | head (head workingLoop) == last (last workingLoop) = workingLoop : getLoops' segs [] @@ -60,16 +61,14 @@ getLoops' segs workingLoop | head (head workingLoop) == last (last workingLoop) -- and stick one on if we find it. -- Otherwise... something is really screwed up. --- FIXME: connects should be used with a singleton. - getLoops' segs workingLoop = let presEnd :: forall c. [[c]] -> c presEnd = last . last connects (x:_) = x == presEnd workingLoop connects [] = False -- Handle the empty case. - possibleConts = filter connects segs - nonConts = filter (not . connects) segs + -- divide our set into sequences that connect, and sequences that don't. + (possibleConts,nonConts) = partition connects segs (next, unused) = if null possibleConts then error "unclosed loop in paths given" else (head possibleConts, tail possibleConts ++ nonConts) From 2f8945094852426b8d46ba030423f7c1ebcc08d3 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 03:57:22 +0100 Subject: [PATCH 015/227] remove unneeded ()s. --- Graphics/Implicit/Export/Render/GetSegs.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/Render/GetSegs.hs b/Graphics/Implicit/Export/Render/GetSegs.hs index e2b9138..ffb18be 100644 --- a/Graphics/Implicit/Export/Render/GetSegs.hs +++ b/Graphics/Implicit/Export/Render/GetSegs.hs @@ -74,7 +74,7 @@ getSegs p1 p2 obj (x1y1, x2y1, x1y2, x2y2) (midx1V,midx2V,midy1V,midy2V) = midy2 = (midy2V, y + dy) notPointLine :: Eq a => [a] -> Bool - notPointLine (np1:np2:[]) = np1 /= np2 + notPointLine [np1, np2] = np1 /= np2 notPointLine [] = False notPointLine [_] = False notPointLine (_ : (_ : (_ : _))) = False @@ -82,8 +82,8 @@ getSegs p1 p2 obj (x1y1, x2y1, x1y2, x2y2) (midx1V,midx2V,midy1V,midy2V) = -- takes straight lines between mid points and subdivides them to -- account for sharp corners, etc. - in map (refine res obj) . filter (notPointLine) $ case (x1y2 <= 0, x2y2 <= 0, - x1y1 <= 0, x2y1 <= 0) of + in map (refine res obj) . filter notPointLine $ case (x1y2 <= 0, x2y2 <= 0, + x1y1 <= 0, x2y1 <= 0) of -- An important point here is orientation. If you imagine going along a -- generated segment, the interior should be on the left-hand side. From cd898826e66807f2630e295d0e7b5e0819c156b1 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 04:32:29 +0100 Subject: [PATCH 016/227] use | syntax, and remove unneeded ()s. --- .../Implicit/Export/Render/HandlePolylines.hs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/Export/Render/HandlePolylines.hs b/Graphics/Implicit/Export/Render/HandlePolylines.hs index db1f081..97ec367 100644 --- a/Graphics/Implicit/Export/Render/HandlePolylines.hs +++ b/Graphics/Implicit/Export/Render/HandlePolylines.hs @@ -7,7 +7,7 @@ module Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs, reducePolyline) where -import Prelude(Bool(False), Maybe(Just, Nothing), map, (.), filter, (==), last, reverse, ($), (++), tail, (-), (/), abs, (<=), (||), (&&), (*), (>), not, null) +import Prelude(Bool(False), Maybe(Just, Nothing), map, (.), filter, (==), last, reverse, ($), (++), tail, (-), (/), abs, (<=), (||), (&&), (*), (>), not, null, otherwise) import Graphics.Implicit.Definitions (minℝ, Polyline, ℝ) @@ -21,23 +21,24 @@ joinSegs :: [Polyline] -> [Polyline] joinSegs [] = [] joinSegs (present:remaining) = let - findNext ((p3:ps):segs) = if p3 == last present then (Just (p3:ps), segs) else - if last ps == last present then (Just (reverse $ p3:ps), segs) else - case findNext segs of (res1,res2) -> (res1,(p3:ps):res2) + findNext ((p3:ps):segs) + | p3 == last present = (Just (p3:ps), segs) + | last ps == last present = (Just (reverse $ p3:ps), segs) + | otherwise = case findNext segs of (res1,res2) -> (res1,(p3:ps):res2) findNext [] = (Nothing, []) - findNext (([]):_) = (Nothing, []) + findNext ([]:_) = (Nothing, []) in case findNext remaining of - (Nothing, _) -> present:(joinSegs remaining) + (Nothing, _) -> present: joinSegs remaining (Just match, others) -> joinSegs $ (present ++ tail match): others reducePolyline :: [(ℝ, ℝ)] -> [(ℝ, ℝ)] -reducePolyline ((x1,y1):(x2,y2):(x3,y3):others) = - if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):(x3,y3):others) else - if abs ( (y2-y1)/(x2-x1) - (y3-y1)/(x3-x1) ) <= minℝ - || ( (x2-x1) == 0 && (x3-x1) == 0 && (y2-y1)*(y3-y1) > 0) - then reducePolyline ((x1,y1):(x3,y3):others) - else (x1,y1) : reducePolyline ((x2,y2):(x3,y3):others) +reducePolyline ((x1,y1):(x2,y2):(x3,y3):others) + | (x1,y1) == (x2,y2) = reducePolyline ((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 ((x1,y1):(x3,y3):others) + | otherwise = (x1,y1) : reducePolyline ((x2,y2):(x3,y3):others) reducePolyline ((x1,y1):(x2,y2):others) = if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):others) else (x1,y1):(x2,y2):others reducePolyline l = l From d3608b2a3a81d5495c1fd353d51e07f9e700284d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 05:14:51 +0100 Subject: [PATCH 017/227] rename interpolate_bin to interpolateBin, and rename interpolate_lin to InterpolateLin. --- .../Implicit/Export/Render/Interpolate.hs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Graphics/Implicit/Export/Render/Interpolate.hs b/Graphics/Implicit/Export/Render/Interpolate.hs index 21c3b65..21423c8 100644 --- a/Graphics/Implicit/Export/Render/Interpolate.hs +++ b/Graphics/Implicit/Export/Render/Interpolate.hs @@ -89,38 +89,38 @@ interpolate _ (b, 0) _ _ = b -- The best case is that it crosses between a and a' if aval*a'val < 0 then - interpolate_bin 0 (a,aval) (a',a'val) f + interpolateBin 0 (a,aval) (a',a'val) f -- Or between b' and b else if bval*b'val < 0 - then interpolate_bin 0 (b',b'val) (b,bval) f + then interpolateBin 0 (b',b'val) (b,bval) f -- But in the worst case, we get to shrink to (a',b') :) - else interpolate_bin 0 (a',a'val) (b',b'val) f + else interpolateBin 0 (a',a'val) (b',b'val) f -- Otherwise, we use our friend, linear interpolation! else -- again... -- The best case is that it crosses between a and a' if aval*a'val < 0 then - interpolate_lin 0 (a,aval) (a',a'val) f + interpolateLin 0 (a,aval) (a',a'val) f -- Or between b' and b else if bval*b'val < 0 - then interpolate_lin 0 (b',b'val) (b,bval) f + then interpolateLin 0 (b',b'val) (b,bval) f -- But in the worst case, we get to shrink to (a',b') :) - else interpolate_lin 0 (a',a'val) (b',b'val) f + else interpolateLin 0 (a',a'val) (b',b'val) f -} interpolate (a,aval) (b,bval) f _ = - -- Make sure aval > bval, then pass to interpolate_lin + -- Make sure aval > bval, then pass to interpolateLin if aval > bval - then interpolate_lin 0 (a,aval) (b,bval) f - else interpolate_lin 0 (b,bval) (a,aval) f + then interpolateLin 0 (a,aval) (b,bval) f + else interpolateLin 0 (b,bval) (a,aval) f -- Yay, linear interpolation! -- Try the answer linear interpolation gives us... -- (n is to cut us off if recursion goes too deep) -interpolate_lin :: Integer -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ -interpolate_lin n (a, aval) (b, bval) obj | aval /= bval= +interpolateLin :: Integer -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ +interpolateLin n (a, aval) (b, bval) obj | aval /= bval= let -- Interpolate and evaluate mid :: ℝ @@ -144,32 +144,32 @@ interpolate_lin n (a, aval) (b, bval) obj | aval /= bval= -- to zero than the previous one. in if improveRatio < 0.3 && n < 4 -- And we continue on. - then interpolate_lin (n+1) (a', a'val) (b', b'val) obj + then interpolateLin (n+1) (a', a'val) (b', b'val) obj -- But if not, we switch to binary interpolate, which is -- immune to this problem - else interpolate_bin (n+1) (a', a'val) (b', b'val) obj + else interpolateBin (n+1) (a', a'val) (b', b'val) obj -- And a fallback: -interpolate_lin _ (a, _) _ _ = a +interpolateLin _ (a, _) _ _ = a -- Now for binary searching! -interpolate_bin :: Integer -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ +interpolateBin :: Integer -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ -- The termination case: -interpolate_bin 5 (a,aval) (b,bval) _ = +interpolateBin 5 (a,aval) (b,bval) _ = if abs aval < abs bval then a else b -- Otherwise, have fun with mid! -interpolate_bin n (a,aval) (b,bval) f = +interpolateBin n (a,aval) (b,bval) f = let mid :: ℝ mid = (a+b)/2 midval = f mid in if midval > 0 - then interpolate_bin (n+1) (mid,midval) (b,bval) f - else interpolate_bin (n+1) (a,aval) (mid,midval) f + then interpolateBin (n+1) (mid,midval) (b,bval) f + else interpolateBin (n+1) (a,aval) (mid,midval) f From 733599bb076d9b94e2925d7299174de2ef39cf0e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 05:29:15 +0100 Subject: [PATCH 018/227] remove extra ()s, shorten some functions that generate only errors, and remove unnecessary $s. --- Graphics/Implicit/Export/SymbolicFormats.hs | 29 ++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index 3ef0d35..d030022 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -38,7 +38,7 @@ callToken cs name args objs = do buildArgs :: (Text, Text) -> [Builder] -> Builder buildArgs _ [] = "()" -buildArgs (c1, c2) args = "(" <> (fromLazyText c1) <> mconcat (intersperse "," args) <> (fromLazyText c2) <> ")" +buildArgs (c1, c2) args = "(" <> fromLazyText c1 <> mconcat (intersperse "," args) <> fromLazyText c2 <> ")" call :: Builder -> [Builder] -> [Reader a Builder] -> Reader a Builder call = callToken ("[", "]") @@ -75,8 +75,7 @@ buildS3 (Scale3 (x,y,z) obj) = call "scale" [bf x, bf y, bf z] [buildS3 obj] buildS3 (Rotate3 (x,y,z) obj) = call "rotate" [bf (rad2deg x), bf (rad2deg y), bf (rad2deg z)] [buildS3 obj] --- FIXME: where is Rotate3V? -buildS3 (Rotate3V _ _ _) = error "Rotate3V not implemented." +buildS3 Rotate3V{} = error "Rotate3V not implemented." buildS3 (Outset3 r obj) | r == 0 = call "outset" [] [buildS3 obj] @@ -100,17 +99,17 @@ buildS3 (ExtrudeRM r (Just twist) Nothing Nothing obj (Left height)) | r == 0 = -- FIXME: where are RotateExtrude, ExtrudeOnEdgeOf? -buildS3(Rect3R _ _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." +buildS3 Rect3R{} = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(UnionR3 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(IntersectR3 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(DifferenceR3 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(Outset3 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(Shell3 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." -buildS3(ExtrudeR _ _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." -buildS3(ExtrudeRotateR _ _ _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." -buildS3(ExtrudeRM _ _ _ _ _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." +buildS3 ExtrudeR{} = error "cannot provide roundness when exporting openscad; unsupported in target format." +buildS3 ExtrudeRotateR {} = error "cannot provide roundness when exporting openscad; unsupported in target format." +buildS3 ExtrudeRM{} = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(EmbedBoxedObj3 _) = error "cannot provide roundness when exporting openscad; unsupported in target format." -buildS3(RotateExtrude _ _ _ _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." +buildS3 RotateExtrude{} = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS3(ExtrudeOnEdgeOf _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." -- Now the 2D objects/transforms. @@ -126,7 +125,7 @@ buildS2 (Circle r) = call "circle" [bf r] [] buildS2 (PolygonR r points) | r == 0 = call "polygon" [buildVector [x,y] | (x,y) <- points] [] where buildVector comps = "[" <> mconcat (intersperse "," $ map bf comps) <> "]" -buildS2 (Complement2 obj) = call "complement" [] $ [buildS2 obj] +buildS2 (Complement2 obj) = call "complement" [] [buildS2 obj] buildS2 (UnionR2 r objs) | r == 0 = call "union" [] $ map buildS2 objs @@ -134,18 +133,18 @@ buildS2 (DifferenceR2 r objs) | r == 0 = call "difference" [] $ map buildS2 objs buildS2 (IntersectR2 r objs) | r == 0 = call "intersection" [] $ map buildS2 objs -buildS2 (Translate2 (x,y) obj) = call "translate" [bf x, bf y] $ [buildS2 obj] +buildS2 (Translate2 (x,y) obj) = call "translate" [bf x, bf y] [buildS2 obj] -buildS2 (Scale2 (x,y) obj) = call "scale" [bf x, bf y] $ [buildS2 obj] +buildS2 (Scale2 (x,y) obj) = call "scale" [bf x, bf y] [buildS2 obj] -buildS2 (Rotate2 (r) obj) = call "rotate" [bf (rad2deg r)] $ [buildS2 obj] +buildS2 (Rotate2 r obj) = call "rotate" [bf (rad2deg r)] [buildS2 obj] -buildS2 (Outset2 r obj) | r == 0 = call "outset" [] $ [buildS2 obj] +buildS2 (Outset2 r obj) | r == 0 = call "outset" [] [buildS2 obj] -buildS2 (Shell2 r obj) | r == 0 = call "shell" [] $ [buildS2 obj] +buildS2 (Shell2 r obj) | r == 0 = call "shell" [] [buildS2 obj] -- Generate errors for rounding requests. OpenSCAD does not support rounding. -buildS2 (RectR _ _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." +buildS2 RectR{} = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS2 (PolygonR _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS2 (UnionR2 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." buildS2 (DifferenceR2 _ _) = error "cannot provide roundness when exporting openscad; unsupported in target format." From 308fda353e518e725ba97164504c18b64f7b0926 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 05:38:18 +0100 Subject: [PATCH 019/227] remove unneeded LANGUAGE pragmas, and import and use first and second. --- Graphics/Implicit/Export/SymbolicObj3.hs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicObj3.hs b/Graphics/Implicit/Export/SymbolicObj3.hs index 7f037d2..720ba6a 100644 --- a/Graphics/Implicit/Export/SymbolicObj3.hs +++ b/Graphics/Implicit/Export/SymbolicObj3.hs @@ -6,14 +6,14 @@ {-# LANGUAGE ExplicitForAll #-} -- FIXME: why are these needed? -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} -- The purpose of this function is to symbolicaly compute triangle meshes using the symbolic system where possible. -- Otherwise we coerce it into an implicit function and apply our modified marching cubes algorithm. module Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) where -import Prelude(map, zip, length, filter, (>), ($), null, concat, (++), concatMap) +import Prelude(map, zip, length, filter, (>), ($), null, (++), concatMap) import Graphics.Implicit.Definitions (ℝ, ℝ3, SymbolicObj3(UnionR3)) import Graphics.Implicit.Export.Render (getMesh) @@ -21,6 +21,8 @@ import Graphics.Implicit.ObjectUtil (getBox3, getImplicit3) import Graphics.Implicit.MathUtil(box3sWithin) import Graphics.Implicit.Export.Symbolic.Rebound3 (rebound3) +import Control.Arrow(first, second) + symbolicGetMesh :: ℝ -> SymbolicObj3 -> [(ℝ3, ℝ3, ℝ3)] {-- @@ -203,8 +205,8 @@ symbolicGetMesh res inputObj@(UnionR3 r objs) = sepFree :: forall a. [((ℝ3, ℝ3), a)] -> ([a], [a]) sepFree ((box,obj):others) = if length (filter (box3sWithin r box) boxes) > 1 - then (\(a,b) -> (obj:a,b)) $ sepFree others - else (\(a,b) -> (a,obj:b)) $ sepFree others + then first ((:) obj) $ sepFree others + else second ((:) obj) $ sepFree others sepFree [] = ([],[]) (dependants, independents) = sepFree boxedObjs @@ -214,7 +216,7 @@ symbolicGetMesh res inputObj@(UnionR3 r objs) = else if null dependants then concatMap (symbolicGetMesh res) independents else concatMap (symbolicGetMesh res) independents - ++ concat [symbolicGetMesh res (UnionR3 r dependants)] + ++ symbolicGetMesh res (UnionR3 r dependants) -- If all that fails, coerce and apply marching cubes :( -- (rebound is for being safe about the bounding box -- From 8eb61cd1d4af441cfc7ae2838b9b348b4f9a44f2 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 05:49:09 +0100 Subject: [PATCH 020/227] import and use second, use String instead of [Char], move some $ to ., and remove unneeded ()s. --- Graphics/Implicit/ExtOpenScad/Default.hs | 40 +++++++++++------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 0e06ada..e7902d6 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -10,13 +10,14 @@ module Graphics.Implicit.ExtOpenScad.Default where -import Prelude (Char, String, Bool(True, False), Maybe(Just, Nothing), Int, ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), (!!), length, otherwise, fromIntegral) +import Prelude (String, Bool(True, False), Maybe(Just, Nothing), Int, ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), (!!), length, otherwise, fromIntegral) import Graphics.Implicit.Definitions (ℝ) import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup, OVal(OList, ONum, OString, OUndefined, OError, OModule, OFunc)) import Graphics.Implicit.ExtOpenScad.Util.OVal (toOObj, oTypeStr) import Graphics.Implicit.ExtOpenScad.Primitives (primitives) import Data.Map (fromList) +import Control.Arrow (second) defaultObjects :: VarLookup -- = Map String OVal defaultObjects = fromList $ @@ -30,11 +31,11 @@ defaultObjects = fromList $ -- Missing standard ones: -- rand, lookup, -defaultConstants :: [([Char], OVal)] +defaultConstants :: [(String, OVal)] defaultConstants = map (\(a,b) -> (a, toOObj (b::ℝ) )) [("pi", pi)] -defaultFunctions :: [([Char], OVal)] +defaultFunctions :: [(String, OVal)] defaultFunctions = map (\(a,b) -> (a, toOObj ( b :: ℝ -> ℝ))) [ ("sin", sin), @@ -58,7 +59,7 @@ defaultFunctions = map (\(a,b) -> (a, toOObj ( b :: ℝ -> ℝ))) ("sqrt", sqrt) ] -defaultFunctions2 :: [([Char], OVal)] +defaultFunctions2 :: [(String, OVal)] defaultFunctions2 = map (\(a,b) -> (a, toOObj (b :: ℝ -> ℝ -> ℝ) )) [ ("max", max), @@ -67,25 +68,22 @@ defaultFunctions2 = map (\(a,b) -> (a, toOObj (b :: ℝ -> ℝ -> ℝ) )) ("pow", (**)) ] -defaultFunctionsSpecial :: [([Char], OVal)] +defaultFunctionsSpecial :: [(String, OVal)] defaultFunctionsSpecial = [ - ("map", toOObj $ flip $ + ("map", toOObj $ flip (map :: (OVal -> OVal) -> [OVal] -> [OVal] ) ) ] - defaultModules :: [(String, OVal)] defaultModules = - map (\(a,b) -> (a, OModule b)) primitives - - + map (second OModule) primitives -- more complicated ones: -defaultPolymorphicFunctions :: [([Char], OVal)] +defaultPolymorphicFunctions :: [(String, OVal)] defaultPolymorphicFunctions = [ ("+", sumtotal), @@ -138,7 +136,7 @@ defaultPolymorphicFunctions = div' (OList a) (ONum b) = OList (map (\x -> div' x (ONum b)) a) div' a b = errorAsAppropriate "divide" a b - omod (ONum a) (ONum b) = ONum $ fromInteger $ mod (floor a) (floor b) + omod (ONum a) (ONum b) = ONum . fromInteger $ mod (floor a) (floor b) omod a b = errorAsAppropriate "modulo" a b append (OList a) (OList b) = OList $ a++b @@ -190,17 +188,17 @@ defaultPolymorphicFunctions = OList $ splice list (floor a) (floor b) osplice (OString str) (ONum a) ( ONum b ) = OString $ splice str (floor a) (floor b) - osplice (OList list) (OUndefined) (ONum b ) = + osplice (OList list) OUndefined (ONum b ) = OList $ splice list 0 (floor b) - osplice (OString str) (OUndefined) (ONum b ) = + osplice (OString str) OUndefined (ONum b ) = OString $ splice str 0 (floor b) - osplice (OList list) (ONum a) ( OUndefined) = + osplice (OList list) (ONum a) OUndefined = OList $ splice list (floor a) (length list + 1) - osplice (OString str) (ONum a) ( OUndefined) = + osplice (OString str) (ONum a) OUndefined = OString $ splice str (floor a) (length str + 1) - osplice (OList list) (OUndefined) (OUndefined) = + osplice (OList list) OUndefined OUndefined = OList $ splice list 0 (length list + 1) - osplice (OString str) (OUndefined) (OUndefined) = + osplice (OString str) OUndefined OUndefined = OString $ splice str 0 (length str + 1) osplice _ _ _ = OUndefined @@ -210,7 +208,7 @@ defaultPolymorphicFunctions = | a < 0 = splice l (a+n) b | b < 0 = splice l a (b+n) | a > 0 = splice xs (a-1) (b-1) - | b > 0 = x:(splice xs a (b-1) ) + | b > 0 = x: splice xs a (b-1) | otherwise = [] where n = length l @@ -237,7 +235,7 @@ defaultPolymorphicFunctions = ternary True a _ = a ternary False _ b = b - olength (OString s) = ONum $ fromIntegral $ length s - olength (OList s) = ONum $ fromIntegral $ length s + olength (OString s) = ONum . fromIntegral $ length s + olength (OList s) = ONum . fromIntegral $ length s olength a = OError ["Can't take length of a " ++ oTypeStr a ++ "."] From ede4c3f1809ef02d834d9ffe88bcd8a788472a2e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 06:04:13 +0100 Subject: [PATCH 021/227] import and use second, and switch from elemIndex to findIndex. --- Graphics/Implicit/ExtOpenScad/Eval/Expr.hs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Expr.hs b/Graphics/Implicit/ExtOpenScad/Eval/Expr.hs index 5cb3fe3..5483172 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Expr.hs @@ -4,7 +4,7 @@ module Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, matchPat) where -import Prelude (String, Maybe(Just, Nothing), IO, concat, ($), map, return, zip, (==), (!!), const, (++), foldr, concatMap) +import Prelude (String, Maybe(Just, Nothing), IO, concat, ($), map, return, zip, (!!), const, (++), foldr, concatMap) import Graphics.Implicit.ExtOpenScad.Definitions ( Pattern(Name, ListP, Wild), @@ -15,11 +15,12 @@ import Graphics.Implicit.ExtOpenScad.Definitions ( import Graphics.Implicit.ExtOpenScad.Util.OVal (oTypeStr, getErrors) import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, getVarLookup) -import Data.List (findIndex) +import Data.List (elemIndex) import Data.Map (fromList, lookup) import Control.Monad (zipWithM, mapM, forM) import Control.Monad.State (StateT, get, modify, liftIO, runStateT) +import Control.Arrow (second) patVars :: Pattern -> [String] patVars (Name name) = [name] @@ -40,22 +41,19 @@ matchPat pat val = do vals <- patMatch pat val return $ fromList $ zip vars vals - evalExpr :: Expr -> StateC OVal evalExpr expr = do varlookup <- getVarLookup (valf, _) <- liftIO $ runStateT (evalExpr' expr) (varlookup, []) return $ valf [] - - evalExpr' :: Expr -> StateT (VarLookup, [String]) IO ([OVal] -> OVal) evalExpr' (Var name ) = do (varlookup, namestack) <- get return $ - case (lookup name varlookup, findIndex (==name) namestack) of - (_, Just pos) -> \s -> s !! pos + case (lookup name varlookup, elemIndex name namestack) of + (_, Just pos) -> (!! pos) (Just val, _) -> const val _ -> const $ OError ["Variable " ++ name ++ " not in scope" ] @@ -80,7 +78,7 @@ evalExpr' (fexpr :$ argExprs) = do evalExpr' (LamE pats fexpr) = do fparts <- forM pats $ \pat -> do - modify (\(vl, names) -> (vl, patVars pat ++ names)) + modify (second (patVars pat ++)) return $ \f xss -> OFunc $ \val -> case patMatch pat val of Just xs -> f (xs ++ xss) Nothing -> OError ["Pattern match failed"] From d8c711460b62cf77c3ce8d7be9d51b4e84e60e76 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 06:08:16 +0100 Subject: [PATCH 022/227] implement scientific notion support, convert $ to ., and remove unneeded $ and ()s. --- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 126 +++++++++++++------ 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index ee5771d..0108308 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -4,7 +4,7 @@ module Graphics.Implicit.ExtOpenScad.Parser.Expr where -import Prelude (Char, Maybe(Nothing, Just), fmap, ($), (>>), return, Bool(True, False), read, (++), id, foldl, map, foldl1, unzip, tail, zipWith3) +import Prelude (Char, Maybe(Nothing, Just), fmap, ($), (.), (>>), return, Bool(True, False), read, (++), (*), (**), (/), id, foldl, map, foldl1, unzip, tail, zipWith3) -- the datatype representing the graininess of our world. import Graphics.Implicit.Definitions (ℝ) @@ -13,6 +13,7 @@ import Graphics.Implicit.Definitions (ℝ) import Text.ParserCombinators.Parsec (GenParser, string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try) import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, LitE, ListE, (:$)), OVal(ONum, OString, OBool, OUndefined), collector) + import Graphics.Implicit.ExtOpenScad.Parser.Util (variableSymb, (?:), (*<|>), genSpace, padString) variable :: GenParser Char st Expr @@ -23,27 +24,66 @@ literal = ("literal" ?:) $ "boolean" ?: do b <- (string "true" >> return True ) *<|> (string "false" >> return False) - return $ LitE $ OBool b + return . LitE $ OBool b + -- FIXME: this is a hack, implement something like exprN to replace this? *<|> "number" ?: ( - do + do + a <- many1 digit + _ <- char 'e' + b <- many1 digit + return . LitE $ ONum (((read a) * (10 ** (read b))) :: ℝ) + *<|> do a <- many1 digit _ <- char '.' b <- many digit - return $ LitE $ ONum (read (a ++ "." ++ b) :: ℝ) + _ <- char 'e' + c <- many1 digit + return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** (read c))) :: ℝ) *<|> do a <- many1 digit - return $ LitE $ ONum (read a :: ℝ) + _ <- char '.' + b <- many digit + _ <- char 'e' + _ <- char '+' + c <- many1 digit + return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** (read c))) :: ℝ) + *<|> do + a <- many1 digit + _ <- char '.' + b <- many digit + _ <- char 'e' + _ <- char '-' + c <- many1 digit + return . LitE $ ONum ((read (a ++ "." ++ b) / (10 ** (read c))) :: ℝ) + *<|> do + a <- many1 digit + _ <- char 'e' + _ <- char '-' + b <- many1 digit + return . LitE $ ONum (((read a) / (10 ** (read b))) :: ℝ) + *<|> do + a <- many1 digit + _ <- char '.' + b <- many digit + return . LitE $ ONum (read (a ++ "." ++ b) :: ℝ) + *<|> do + a <- many1 digit + return . LitE $ ONum (read a :: ℝ) ) - *<|> "string" ?: do + *<|> "string" ?: do _ <- string "\"" strlit <- many $ (string "\\\"" >> return '\"') *<|> (string "\\n" >> return '\n') - *<|> ( noneOf "\"\n") + *<|> (string "\\r" >> return '\r') + *<|> (string "\\t" >> return '\t') + *<|> (string "\\\\" >> return '\\') + -- FIXME: no \u unicode support? + *<|> noneOf "\"\n" _ <- string "\"" - return $ LitE $ OString strlit + return . LitE $ OString strlit -- We represent the priority or 'fixity' of different types of expressions --- by the Int argument +-- by the ExprIdx argument, with A0 as the highest. expr0 :: GenParser Char st Expr expr0 = exprN A0 @@ -85,7 +125,7 @@ exprN A12 = exprN A11 = do - obj <- exprN $ A12 + obj <- exprN A12 _ <- genSpace mods <- many1 ( "function application" ?: do @@ -111,67 +151,73 @@ exprN A11 = (Just s, Just e ) -> \l -> Var "splice" :$ [l, s, e] ) return $ foldl (\a b -> b a) obj mods - *<|> (exprN $ A12 ) + *<|> exprN A12 +-- match a leading (+) or (-) operator. exprN A10 = "negation" ?: do _ <- padString "-" - expr <- exprN $ A11 + expr <- exprN A11 return $ Var "negate" :$ [expr] *<|> do _ <- padString "+" - expr <- exprN $ A11 - return expr - *<|> exprN (A11) + exprN A11 + *<|> exprN A11 +-- match power-of (^) operator. exprN A9 = "exponentiation" ?: do - a <- exprN $ A10 + a <- exprN A10 _ <- padString "^" b <- exprN A9 return $ Var "^" :$ [a,b] - *<|> exprN (A10) + *<|> exprN A10 +-- match sequences of multiplication and division. exprN A8 = "multiplication/division" ?: do -- outer list is multiplication, inner division. -- eg. "1*2*3/4/5*6*7/8" -- [[1],[2],[3,4,5],[6],[7,8]] exprs <- sepBy1 - (sepBy1 (exprN $ A9) (try $ padString "/" )) + (sepBy1 (exprN A9) (try $ padString "/" )) (try $ padString "*" ) let div' a b = Var "/" :$ [a, b] - return $ collector "*" $ map (foldl1 div') exprs - *<|> exprN (A9) + return . collector "*" $ map (foldl1 div') exprs + *<|> exprN A9 +-- match remainder (%) operator. exprN A7 = "modulo" ?: do - exprs <- sepBy1 (exprN $ A8) (try $ padString "%") + exprs <- sepBy1 (exprN A8) (try $ padString "%") let mod' a b = Var "%" :$ [a, b] return $ foldl1 mod' exprs - *<|> exprN (A8) + *<|> exprN A8 +-- match string addition (++) operator. exprN A6 = "append" ?: do - exprs <- sepBy1 (exprN $ A7) (try $ padString "++") + exprs <- sepBy1 (exprN A7) (try $ padString "++") return $ collector "++" exprs - *<|> exprN (A7) + *<|> exprN A7 +-- match sequences of addition and subtraction. exprN A5 = "addition/subtraction" ?: do -- Similar to multiply & divide -- eg. "1+2+3-4-5+6-7" -- [[1],[2],[3,4,5],[6,7]] exprs <- sepBy1 - (sepBy1 (exprN $ A6) (try $ padString "-" )) + (sepBy1 (exprN A6) (try $ padString "-" )) (try $ padString "+" ) let sub a b = Var "-" :$ [a, b] - return $ collector "+" $ map (foldl1 sub) exprs - *<|> exprN (A6) + return . collector "+" $ map (foldl1 sub) exprs + *<|> exprN A6 +-- match comparison operators. exprN A4 = do - firstExpr <- exprN $ A5 + firstExpr <- exprN A5 otherComparisonsExpr <- many $ do comparisonSymb <- padString "==" @@ -180,7 +226,7 @@ exprN A4 = *<|> padString "<=" *<|> padString ">" *<|> padString "<" - expr <- exprN $ A5 + expr <- exprN A5 return (Var comparisonSymb, expr) let (comparisons, otherExprs) = unzip otherComparisonsExpr @@ -189,39 +235,43 @@ exprN A4 = [] -> firstExpr [x] -> x :$ exprs _ -> collector "all" $ zipWith3 (\c e1 e2 -> c :$ [e1,e2]) comparisons exprs (tail exprs) - *<|> exprN (A5) + *<|> exprN A5 +-- match the logical negation operator. exprN A3 = "logical-not" ?: do _ <- padString "!" - a <- exprN $ A4 + a <- exprN A4 return $ Var "!" :$ [a] - *<|> exprN (A4) + *<|> exprN A4 +-- match the logical And and Or (&&,||) operators. exprN A2 = "logical and/or" ?: do - a <- exprN $ A3 + a <- exprN A3 symb <- padString "&&" *<|> padString "||" b <- exprN A2 return $ Var symb :$ [a,b] - *<|> exprN (A3) + *<|> exprN A3 +-- match the ternary (1?2:3) operator. exprN A1 = "ternary" ?: do - a <- exprN $ A2 + a <- exprN A2 _ <- padString "?" b <- exprN A1 _ <- padString ":" c <- exprN A1 return $ Var "?" :$ [a,b,c] - *<|> exprN (A2) + *<|> exprN A2 +-- Match and throw away any white space around an expression. exprN A0 = do _ <- genSpace - expr <- exprN $ A1 + expr <- exprN A1 _ <- genSpace return expr - *<|> exprN (A1) + *<|> exprN A1 From 97d2622c9eed02c18550a08ca9569d2c6092b652 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 08:10:33 +0100 Subject: [PATCH 023/227] use String instead of [Char], remove unnecessary ()s, minor spacing changes, and change unitName to name. --- Graphics/Implicit/ExtOpenScad/Primitives.hs | 63 +++++++++++---------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 9b98185..9d87861 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -12,7 +12,7 @@ -- Export one set containing all of the primitive object's patern matches. module Graphics.Implicit.ExtOpenScad.Primitives (primitives) where -import Prelude(String, IO, Char, Either(Left, Right), Bool(False), Maybe(Just, Nothing), Fractional, ($), return, either, id, (-), (==), (&&), (<), fromIntegral, (*), cos, sin, pi, (/), (>), const, uncurry, realToFrac, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) +import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), Fractional, ($), return, either, id, (-), (==), (&&), (<), fromIntegral, (*), cos, sin, pi, (/), (>), const, uncurry, realToFrac, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, ℕ, SymbolicObj2, SymbolicObj3) @@ -37,7 +37,7 @@ primitives = [ sphere, cube, square, cylinder, circle, polygon, union, differenc -- sphere is a module without a suite. -- this means that the parser will look for this like -- sphere(args...); -sphere :: ([Char], [OVal] -> ArgParser (IO [OVal])) +sphere :: (String, [OVal] -> ArgParser (IO [OVal])) sphere = moduleWithoutSuite "sphere" $ do example "sphere(3);" example "sphere(r=5);" @@ -52,7 +52,7 @@ sphere = moduleWithoutSuite "sphere" $ do -- (Graphics.Implicit.Primitives) addObj3 $ Prim.sphere r -cube :: ([Char], [OVal] -> ArgParser (IO [OVal])) +cube :: (String, [OVal] -> ArgParser (IO [OVal])) cube = moduleWithoutSuite "cube" $ do -- examples @@ -98,7 +98,7 @@ cube = moduleWithoutSuite "cube" $ do addObj3 $ Prim.rect3R r (x1, y1, z1) (x2, y2, z2) -square :: ([Char], [OVal] -> ArgParser (IO [OVal])) +square :: (String, [OVal] -> ArgParser (IO [OVal])) square = moduleWithoutSuite "square" $ do -- examples @@ -142,7 +142,7 @@ square = moduleWithoutSuite "square" $ do addObj2 $ Prim.rectR r (x1, y1) (x2, y2) -cylinder :: ([Char], [OVal] -> ArgParser (IO [OVal])) +cylinder :: (String, [OVal] -> ArgParser (IO [OVal])) cylinder = moduleWithoutSuite "cylinder" $ do example "cylinder(r=10, h=30, center=true);" @@ -197,7 +197,7 @@ cylinder = moduleWithoutSuite "cylinder" $ do in shift obj3 else shift $ Prim.cylinder2 r1 r2 dh -circle :: ([Char], [OVal] -> ArgParser (IO [OVal])) +circle :: (String, [OVal] -> ArgParser (IO [OVal])) circle = moduleWithoutSuite "circle" $ do example "circle(r=10); // circle" @@ -221,14 +221,14 @@ circle = moduleWithoutSuite "circle" $ do sides = fromIntegral fn in [(r*cos θ, r*sin θ )| θ <- [2*pi*n/sides | n <- [0.0 .. sides - 1.0]]] -polygon :: ([Char], [OVal] -> ArgParser (IO [OVal])) +polygon :: (String, [OVal] -> ArgParser (IO [OVal])) polygon = moduleWithoutSuite "polygon" $ do example "polygon ([(0,0), (0,10), (10,0)]);" - points :: [ℝ2] <- argument "points" + points :: [ℝ2] <- argument "points" `doc` "vertices of the polygon" - paths :: [ℕ ] <- argument "paths" + paths :: [ℕ] <- argument "paths" `doc` "order to go through vertices; ignored for now" `defaultTo` [] r :: ℝ <- argument "r" @@ -239,7 +239,7 @@ polygon = moduleWithoutSuite "polygon" $ do _ -> return $ return [] -union :: ([Char], [OVal] -> ArgParser (IO [OVal])) +union :: (String, [OVal] -> ArgParser (IO [OVal])) union = moduleWithSuite "union" $ \children -> do r :: ℝ <- argument "r" `defaultTo` 0.0 @@ -248,7 +248,7 @@ union = moduleWithSuite "union" $ \children -> do then objReduce (Prim.unionR r) (Prim.unionR r) children else objReduce Prim.union Prim.union children -intersect :: ([Char], [OVal] -> ArgParser (IO [OVal])) +intersect :: (String, [OVal] -> ArgParser (IO [OVal])) intersect = moduleWithSuite "intersection" $ \children -> do r :: ℝ <- argument "r" `defaultTo` 0.0 @@ -257,7 +257,7 @@ intersect = moduleWithSuite "intersection" $ \children -> do then objReduce (Prim.intersectR r) (Prim.intersectR r) children else objReduce Prim.intersect Prim.intersect children -difference :: ([Char], [OVal] -> ArgParser (IO [OVal])) +difference :: (String, [OVal] -> ArgParser (IO [OVal])) difference = moduleWithSuite "difference" $ \children -> do r :: ℝ <- argument "r" `defaultTo` 0.0 @@ -266,7 +266,7 @@ difference = moduleWithSuite "difference" $ \children -> do then objReduce (Prim.differenceR r) (Prim.differenceR r) children else objReduce Prim.difference Prim.difference children -translate :: ([Char], [OVal] -> ArgParser (IO [OVal])) +translate :: (String, [OVal] -> ArgParser (IO [OVal])) translate = moduleWithSuite "translate" $ \children -> do example "translate ([2,3]) circle (4);" @@ -297,11 +297,12 @@ deg2rad :: ℝ -> ℝ deg2rad x = x / 180.0 * pi -- This is mostly insane -rotate :: ([Char], [OVal] -> ArgParser (IO [OVal])) +rotate :: (String, [OVal] -> ArgParser (IO [OVal])) rotate = moduleWithSuite "rotate" $ \children -> do a <- argument "a" `doc` "value to rotate by; angle or list of angles" - v <- argument "v" `defaultTo` (0, 0, 1) + v <- argument "v" + `defaultTo` (0, 0, 1) `doc` "Vector to rotate around if a is a single angle" -- caseOType matches depending on whether size can be coerced into @@ -317,14 +318,14 @@ rotate = moduleWithSuite "rotate" $ \children -> do objMap id (Prim.rotate3 (deg2rad yz, deg2rad zx, 0)) children ) <||> const [] -scale :: ([Char], [OVal] -> ArgParser (IO [OVal])) +scale :: (String, [OVal] -> ArgParser (IO [OVal])) scale = moduleWithSuite "scale" $ \children -> do example "scale(2) square(5);" example "scale([2,3]) square(5);" example "scale([2,3,4]) cube(5);" - v :: Either ℝ (Either ℝ2 ℝ3) <- argument "v" + v <- argument "v" `doc` "vector or scalar to scale by" let @@ -336,11 +337,11 @@ scale = moduleWithSuite "scale" $ \children -> do Right (Left (x,y)) -> scaleObjs (x,y) (x,y,1) Right (Right (x,y,z)) -> scaleObjs (x,y) (x,y,z) -extrude :: ([Char], [OVal] -> ArgParser (IO [OVal])) +extrude :: (String, [OVal] -> ArgParser (IO [OVal])) extrude = moduleWithSuite "linear_extrude" $ \children -> do example "linear_extrude(10) square(5);" - height :: Either ℝ (ℝ -> ℝ -> ℝ) <- argument "height" `defaultTo` (Left 1) + height :: Either ℝ (ℝ -> ℝ -> ℝ) <- argument "height" `defaultTo` Left 1 `doc` "height to extrude to..." center :: Bool <- argument "center" `defaultTo` False `doc` "center? (the z component)" @@ -384,12 +385,12 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do shiftAsNeeded $ Prim.extrudeRM r twist' scale' translate' obj height' ) children -rotateExtrude :: ([Char], [OVal] -> ArgParser (IO [OVal])) +rotateExtrude :: (String, [OVal] -> ArgParser (IO [OVal])) rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do example "rotate_extrude() translate(20) circle(10);" - totalRot :: ℝ <- argument "a" `defaultTo` 360 - `doc` "angle to sweep" + totalRot :: ℝ <- argument "a" `defaultTo` 360 + `doc` "angle to sweep" r :: ℝ <- argument "r" `defaultTo` 0 translateArg :: Either ℝ2 (ℝ -> ℝ2) <- argument "translate" `defaultTo` Left (0,0) rotateArg :: Either ℝ (ℝ -> ℝ ) <- argument "rotate" `defaultTo` Left 0 @@ -398,8 +399,8 @@ rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do is360m :: RealFrac a => a -> Bool is360m n = 360 * fromInteger (round $ n / 360) /= n cap = is360m totalRot - || (either ( /= (0,0)) (\f -> f 0 /= f totalRot) ) translateArg - || (either (is360m) (\f -> is360m (f 0 - f totalRot)) ) rotateArg + || either ( /= (0,0)) (\f -> f 0 /= f totalRot) translateArg + || either is360m (\f -> is360m (f 0 - f totalRot)) rotateArg capM = if cap then Just r else Nothing return $ return $ obj2UpMap (Prim.rotateExtrude totalRot capM translateArg rotateArg) children @@ -414,7 +415,7 @@ rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do getAndModUpObj2s suite (\obj -> extrudeRMod r (\θ (x,y) -> (x*cos(θ)+y*sin(θ), y*cos(θ)-x*sin(θ)) ) obj h) -} -shell :: ([Char], [OVal] -> ArgParser (IO [OVal])) +shell :: (String, [OVal] -> ArgParser (IO [OVal])) shell = moduleWithSuite "shell" $ \children-> do w :: ℝ <- argument "w" `doc` "width of the shell..." @@ -422,7 +423,7 @@ shell = moduleWithSuite "shell" $ \children-> do return $ return $ objMap (Prim.shell w) (Prim.shell w) children -- Not a perenant solution! Breaks if can't pack. -pack :: ([Char], [OVal] -> ArgParser (IO [OVal])) +pack :: (String, [OVal] -> ArgParser (IO [OVal])) pack = moduleWithSuite "pack" $ \children -> do example "pack ([45,45], sep=2) { circle(10); circle(10); circle(10); circle(10); }" @@ -448,17 +449,17 @@ pack = moduleWithSuite "pack" $ \children -> do putStrLn "Can't pack given objects in given box with present algorithm" return children -unit :: ([Char], [OVal] -> ArgParser (IO [OVal])) +unit :: (String, [OVal] -> ArgParser (IO [OVal])) unit = moduleWithSuite "unit" $ \children -> do example "unit(\"inch\") {..}" -- arguments - unitName :: String <- argument "unit" + name :: String <- argument "unit" `doc` "the unit you wish to work in" let - mmRatio :: Fractional a => [Char] -> Maybe a + mmRatio :: Fractional a => String -> Maybe a mmRatio "inch" = Just 25.4 mmRatio "in" = mmRatio "inch" mmRatio "foot" = Just 304.8 @@ -476,9 +477,9 @@ unit = moduleWithSuite "unit" $ \children -> do mmRatio _ = Nothing -- The actual work... - return $ case mmRatio unitName of + return $ case mmRatio name of Nothing -> do - putStrLn $ "unrecognized unit " ++ unitName + putStrLn $ "unrecognized unit " ++ name return children Just r -> return $ objMap (Prim.scale (r,r)) (Prim.scale (r,r,r)) children From 4b796686b928256eee96a5c2df0d8b6823c7ad82 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 08:13:53 +0100 Subject: [PATCH 024/227] remove empty ()s, import and use first, and use list filtering for case handling. --- Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs index 4fce99d..1c3672a 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs @@ -18,6 +18,8 @@ import Graphics.Implicit.ExtOpenScad.Util.OVal (fromOObj, toOObj, OTypeMirror) import qualified Data.Map as Map import Data.Maybe (isNothing, fromJust, isJust) +import Control.Arrow(first) + -- * ArgParser building functions -- ** argument and combinators @@ -56,7 +58,7 @@ test str = APTest str [] (return ()) eulerCharacteristic :: ArgParser a -> Int -> ArgParser a eulerCharacteristic (APTest str tests child) χ = - APTest str ((EulerCharacteristic χ) : tests) child + APTest str (EulerCharacteristic χ : tests) child eulerCharacteristic _ _ = error "Impossible!" -- * Tools for handeling ArgParsers @@ -70,7 +72,7 @@ argMap :: argMap args = argMap2 unnamedArgs (Map.fromList namedArgs) where unnamedArgs = map snd $ filter (isNothing . fst) args - namedArgs = map (\(a,b) -> (fromJust a, b)) $ filter (isJust . fst) args + namedArgs = map (first fromJust) $ filter (isJust . fst) args argMap2 :: [OVal] -> Map.Map String OVal -> ArgParser a -> (Maybe a, [String]) @@ -97,11 +99,7 @@ argMap2 unnamedArgs namedArgs (AP name fallback _ f) = Nothing -> (Nothing, ["No value and no default for argument " ++ name]) argMap2 a b (APTerminator val) = - (Just val, - if not (null a && Map.null b) - then ["unused arguments"] - else [] - ) + (Just val, ["unused arguments" | not (null a && Map.null b)]) argMap2 a b (APFailIf testval err child) = if testval From b67c801989ddf9caa440251d09cc637c7daff2d0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 20:27:13 +0100 Subject: [PATCH 025/227] remove overlapping instances, be more pedantic about what we provide, remove unnecessary variables, remove unnecessary ()s, simplify type declarations, and add more comments. --- Graphics/Implicit/ExtOpenScad/Util/OVal.hs | 103 ++++++++++----------- 1 file changed, 49 insertions(+), 54 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index ca70cd9..9182227 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -3,103 +3,98 @@ -- Released under the GNU AGPLV3+, see LICENSE -- FIXME: required. why? -{-# LANGUAGE ViewPatterns, RankNTypes, ScopedTypeVariables, TypeSynonymInstances, FlexibleInstances #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeSynonymInstances #-} -{-# LANGUAGE CPP #-} -#if __GLASGOW_HASKELL__ < 710 -{-# LANGUAGE OverlappingInstances #-} -#endif +module Graphics.Implicit.ExtOpenScad.Util.OVal(OTypeMirror, (<||>), fromOObj, toOObj, divideObjs, caseOType, oTypeStr, getErrors) where -module Graphics.Implicit.ExtOpenScad.Util.OVal where - -import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left,Right), String, Char, (==), fromInteger, floor, ($), (.), map, error, (++), show, fromIntegral, head, flip, filter, not, return) +import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left,Right), Char, String, (==), fromInteger, floor, ($), (.), map, error, (++), show, fromIntegral, head, flip, filter, not, return, head) import Graphics.Implicit.Definitions(ℝ, ℕ, SymbolicObj2, SymbolicObj3) + import Graphics.Implicit.ExtOpenScad.Definitions (OVal(ONum, OBool, OString, OList, OFunc, OUndefined, OModule, OError, OObj2, OObj3)) -import qualified Control.Monad as Monad -import Data.Maybe (fromJust, isJust) + +import Control.Monad (mapM, msum) + +import Data.Maybe (fromMaybe, maybe) -- for some minimal paralellism. import Control.Parallel.Strategies(runEval, rpar, rseq) --- | We'd like to be able to turn OVals into a given Haskell type +-- Convert OVals (and Lists of OVals) into a given Haskell type class OTypeMirror a where fromOObj :: OVal -> Maybe a + fromOObjList :: OVal -> Maybe [a] + fromOObjList (OList list) = mapM fromOObj list + fromOObjList _ = Nothing toOObj :: a -> OVal instance OTypeMirror OVal where - fromOObj a = Just a + fromOObj = Just toOObj a = a instance OTypeMirror ℝ where fromOObj (ONum n) = Just n fromOObj _ = Nothing - toOObj n = ONum n + toOObj = ONum instance OTypeMirror ℕ where fromOObj (ONum n) = if n == fromInteger (floor n) then Just (floor n) else Nothing fromOObj _ = Nothing - toOObj n = ONum $ fromIntegral n + toOObj a = ONum $ fromIntegral a instance OTypeMirror Bool where fromOObj (OBool b) = Just b fromOObj _ = Nothing - toOObj b = OBool b + toOObj = OBool -#if __GLASGOW_HASKELL__ >= 710 -instance {-# Overlapping #-} OTypeMirror String where -#else -instance OTypeMirror String where -#endif - fromOObj (OString str) = Just str +-- We don't actually use single chars, this is to compile lists of chars (AKA strings) after passing through OTypeMirror [a]'s fromOObj. +-- This lets us handle strings without overlapping the [a] case. +instance OTypeMirror Char where + fromOObj (OString str) = Just $ head str fromOObj _ = Nothing - toOObj str = OString str + fromOObjList (OString str) = Just str + fromOObjList _ = Nothing + toOObj a = OString [a] -instance forall a. (OTypeMirror a) => OTypeMirror (Maybe a) where +instance (OTypeMirror a) => OTypeMirror [a] where + fromOObj = fromOObjList + toOObj list = OList $ map toOObj list + +instance (OTypeMirror a) => OTypeMirror (Maybe a) where fromOObj a = Just $ fromOObj a toOObj (Just a) = toOObj a toOObj Nothing = OUndefined -#if __GLASGOW_HASKELL__ >= 710 -instance {-# Overlappable #-} forall a. (OTypeMirror a) => OTypeMirror [a] where -#else -instance forall a. (OTypeMirror a) => OTypeMirror [a] where -#endif - fromOObj (OList list) = Monad.sequence . map fromOObj $ list - fromOObj _ = Nothing - toOObj list = OList $ map toOObj list - -instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (a,b) where - fromOObj (OList ((fromOObj -> Just a):(fromOObj -> Just b):[])) = Just (a,b) +instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (a,b) where + fromOObj (OList [fromOObj -> Just a,fromOObj -> Just b]) = Just (a,b) fromOObj _ = Nothing toOObj (a,b) = OList [toOObj a, toOObj b] - -instance forall a b c. (OTypeMirror a, OTypeMirror b, OTypeMirror c) => OTypeMirror (a,b,c) where - fromOObj (OList ((fromOObj -> Just a):(fromOObj -> Just b):(fromOObj -> Just c):[])) = +instance (OTypeMirror a, OTypeMirror b, OTypeMirror c) => OTypeMirror (a,b,c) where + fromOObj (OList [fromOObj -> Just a,fromOObj -> Just b,fromOObj -> Just c]) = Just (a,b,c) fromOObj _ = Nothing toOObj (a,b,c) = OList [toOObj a, toOObj b, toOObj c] -instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (a -> b) where +instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (a -> b) where fromOObj (OFunc f) = Just $ \input -> let oInput = toOObj input oOutput = f oInput output :: Maybe b output = fromOObj oOutput - in case output of - Just out -> out - Nothing -> error $ "coercing OVal to a -> b isn't always safe; use a -> Maybe b" - ++ " (trace: " ++ show oInput ++ " -> " ++ show oOutput ++ " )" + in + fromMaybe (error $ "coercing OVal to a -> b isn't always safe; use a -> Maybe b" + ++ " (trace: " ++ show oInput ++ " -> " ++ show oOutput ++ " )") output fromOObj _ = Nothing toOObj f = OFunc $ \oObj -> case fromOObj oObj :: Maybe a of Nothing -> OError ["bad input type"] Just obj -> toOObj $ f obj - -instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (Either a b) where +instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (Either a b) where fromOObj (fromOObj -> Just (x :: a)) = Just $ Left x fromOObj (fromOObj -> Just (x :: b)) = Just $ Right x fromOObj _ = Nothing @@ -107,8 +102,9 @@ instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (Either a b) toOObj (Right x) = toOObj x toOObj (Left x) = toOObj x -oTypeStr :: OVal -> [Char] -oTypeStr (OUndefined) = "Undefined" +-- A string representing each type. +oTypeStr :: OVal -> String +oTypeStr OUndefined = "Undefined" oTypeStr (OBool _ ) = "Bool" oTypeStr (ONum _ ) = "Number" oTypeStr (OList _ ) = "List" @@ -121,7 +117,7 @@ oTypeStr (OObj3 _ ) = "3D Object" getErrors :: OVal -> Maybe String getErrors (OError er) = Just $ head er -getErrors (OList l) = Monad.msum $ map getErrors l +getErrors (OList l) = msum $ map getErrors l getErrors _ = Nothing caseOType :: forall c a. a -> (a -> c) -> c @@ -132,21 +128,20 @@ infixr 2 <||> => (desiredType -> out) -> (OVal -> out) -> (OVal -> out) -(<||>) f g = \input -> +(<||>) f g input = let coerceAttempt :: Maybe desiredType coerceAttempt = fromOObj input in - if isJust coerceAttempt -- ≅ (/= Nothing) but no Eq req - then f $ fromJust coerceAttempt - else g input + maybe (g input) f coerceAttempt +-- separate 2d and 3d objects from a set of OVals. divideObjs :: [OVal] -> ([SymbolicObj2], [SymbolicObj3], [OVal]) divideObjs children = runEval $ do - obj2s <- rseq ([ x | OObj2 x <- children ]) - obj3s <- rseq ([ x | OObj3 x <- children ]) - objs <- rpar (filter (not . isOObj) $ children ) + obj2s <- rseq [ x | OObj2 x <- children ] + obj3s <- rseq [ x | OObj3 x <- children ] + objs <- rpar (filter (not . isOObj) children) return (obj2s, obj3s, objs) where isOObj (OObj2 _) = True From 6fcbf5e7eaf0a660e2db48bdf6e8b461330a224a Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 20:56:08 +0100 Subject: [PATCH 026/227] use distance instead of .-., add better comments, remove unneeded ()s, simplify type declarations, and use sortBy. --- Graphics/Implicit/MathUtil.hs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index 243f8ed..7311e8b 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -9,21 +9,25 @@ module Graphics.Implicit.MathUtil (rmax, rmaximum, rminimum, distFromLineSeg, pack, box3sWithin) where -- Explicitly include what we need from Prelude. -import Prelude (Bool, Num, Ord, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), sin, asin, pi, max, sqrt, min, compare, (<=), fst, snd, (++)) +import Prelude (Bool, Num, Ord, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), sin, asin, pi, max, sqrt, min, compare, (<=), fst, snd, (++), head, flip) import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Box2, (⋅)) -import Data.List (sort, sortBy, reverse, (!!)) +import Data.List (sort, sortBy, (!!)) + import Data.VectorSpace (magnitude, normalized, (^-^), (^+^), (*^)) -import Data.AffineSpace ((.-.)) + +-- get the distance between two points. +import Data.AffineSpace (distance) -- | The distance a point p is from a line segment (a,b) distFromLineSeg :: ℝ2 -> (ℝ2, ℝ2) -> ℝ -distFromLineSeg p (a,b) = magnitude (closest .-. p) +distFromLineSeg p (a,b) = distance p closest where ab = b ^-^ a ap = p ^-^ a d = normalized ab ⋅ ap + -- the closest point to p on the line segment. closest | d < 0 = a | d > magnitude ab = b @@ -70,26 +74,26 @@ rmaximum :: ℝ -- ^ radius -> [ℝ] -- ^ numbers to take round maximum -> ℝ -- ^ resulting number -rmaximum _ (a:[]) = a -rmaximum r (a:b:[]) = rmax r a b +rmaximum _ [a] = a +rmaximum r [a,b] = rmax r a b rmaximum r l = let - tops = reverse $ sort l + tops = sortBy (flip compare) l in - rmax r (tops !! 0) (tops !! 1) + rmax r (head tops) (tops !! 1) -- | Like rmin but on a list. rminimum :: ℝ -- ^ radius -> [ℝ] -- ^ numbers to take round minimum -> ℝ -- ^ resulting number -rminimum _ (a:[]) = a -rminimum r (a:b:[]) = rmin r a b +rminimum _ [a] = a +rminimum r [a,b] = rmin r a b rminimum r l = let tops = sort l in - rmin r (tops !! 0) (tops !! 1) + rmin r (head tops) (tops !! 1) -- | Pack the given objects in a box the given size. pack :: @@ -107,9 +111,9 @@ pack (dx, dy) sep objs = packSome sortedObjs (dx, dy) (\(boxa, _) (boxb, _) -> compareBoxesByY boxa boxb ) objs - tmap1 :: forall t t1 t2. (t2 -> t) -> (t2, t1) -> (t, t1) + tmap1 :: (t2 -> t) -> (t2, t1) -> (t, t1) tmap1 f (a,b) = (f a, b) - tmap2 :: forall t t1 t2. (t2 -> t1) -> (t, t2) -> (t, t1) + tmap2 :: (t2 -> t1) -> (t, t2) -> (t, t1) tmap2 f (a,b) = (a, f b) packSome :: [(Box2,a)] -> Box2 -> ([(ℝ2,a)], [(Box2,a)]) @@ -121,7 +125,7 @@ pack (dx, dy) sep objs = packSome sortedObjs (dx, dy) packSome otherBoxedObjs ((bx1+x2-x1+sep, by1), (bx2, by1 + y2-y1)) rowAndUp = if abs (by2-by1) - abs (y2-y1) > sep - then tmap1 ((fst row) ++ ) $ + then tmap1 (fst row ++ ) $ packSome (snd row) ((bx1, by1 + y2-y1+sep), (bx2, by2)) else row in From 1f9fe28c27f94b1cfcccf8a042bd73086202ec0e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 21:00:26 +0100 Subject: [PATCH 027/227] remove unneeded LANGUAGE pragmas, replace the getbox2 implementation for PolygonR, remove unnecessary ()s, and add more implementations of getDist2. --- Graphics/Implicit/ObjectUtil/GetBox2.hs | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 9174967..9418d1a 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -2,7 +2,7 @@ -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} module Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) where @@ -45,8 +45,7 @@ getBox2 :: SymbolicObj2 -> Box2 -- Primitives getBox2 (RectR _ a b) = (a,b) getBox2 (Circle r ) = ((-r, -r), (r,r)) -getBox2 (PolygonR _ points) = ((minimum xs, minimum ys), (maximum xs, maximum ys)) - where (xs, ys) = unzip points +getBox2 (PolygonR _ points) = pointsBox points -- (Rounded) CSG getBox2 (Complement2 _) = ((-infty, -infty), (infty, infty)) @@ -86,14 +85,13 @@ getBox2 (Scale2 s symbObj) = getBox2 (Rotate2 θ symbObj) = let ((x1,y1), (x2,y2)) = getBox2 symbObj - rotate (x,y) = (cos(θ)*x - sin(θ)*y, sin(θ)*x + cos(θ)*y) + rotate (x,y) = (x*cos θ - y*sin θ, x*sin θ + y*cos θ) in pointsBox [ rotate (x1, y1) , rotate (x1, y2) , rotate (x2, y1) , rotate (x2, y2) ] - -- Boundary mods getBox2 (Shell2 w symbObj) = outsetBox (w/2) $ getBox2 symbObj @@ -105,20 +103,26 @@ getBox2 (EmbedBoxedObj2 (_,box)) = box -- Get the maximum distance (read upper bound) an object is from a point. -- Sort of a circular getDist2 :: ℝ2 -> SymbolicObj2 -> ℝ +-- Real implementations +getDist2 p (Circle r) = magnitude p + r +getDist2 p (PolygonR r points) = r + maximum [magnitude (p ^-^ p') | p' <- points] +-- Transform implementations getDist2 p (UnionR2 r objs) = r + maximum [getDist2 p obj | obj <- objs ] +getDist2 p (DifferenceR2 r objs) = r + (getDist2 p $ head objs) +getDist2 p (IntersectR2 r objs) = r + maximum [getDist2 p obj | obj <- objs ] +-- FIXME: isn't this wrong? should we be returning distance inside of the object? +getDist2 _ (Complement2 _) = 1/0 getDist2 p (Translate2 v obj) = getDist2 (p ^+^ v) obj -getDist2 p (Circle r) = magnitude p + r -getDist2 p (PolygonR r points) = - r + maximum [magnitude (p ^-^ p') | p' <- points] -- FIXME: write optimized functions for the rest of the SymbObjs. +-- Fallthrough: use getBox2 to check the distance a box is from the point. getDist2 (x,y) symbObj = let ((x1,y1), (x2,y2)) = getBox2 symbObj in sqrt ( - (max (abs (x1 - x)) (abs (x2 - x))) * - (max (abs (x1 - x)) (abs (x2 - x))) + - (max (abs (y1 - y)) (abs (y2 - y))) * - (max (abs (y1 - y)) (abs (y2 - y))) + max (abs (x1 - x)) (abs (x2 - x)) * + max (abs (x1 - x)) (abs (x2 - x)) + + max (abs (y1 - y)) (abs (y2 - y)) * + max (abs (y1 - y)) (abs (y2 - y)) ) From 2e468545cc1069e5e44b3d79d0e6a55d35e93e8d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 21:03:23 +0100 Subject: [PATCH 028/227] normalize the form of the implementations of getImplicit2, and small spacing and redundant () changes. --- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 65 +++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index 3d51a85..178188e 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) where -import Prelude(Int, Num, abs, (-), (/), sqrt, (*), (+), (!!), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, cos, sin, head, tail) +import Prelude(Int, Num, abs, (-), (/), sqrt, (*), (+), (!!), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, cos, sin, head, tail, (.)) import Graphics.Implicit.Definitions (SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2), Obj2, ℝ, ℝ2, (⋯/)) import Graphics.Implicit.MathUtil (rminimum, rmaximum, distFromLineSeg) @@ -20,84 +20,89 @@ import Data.List (nub) getImplicit2 :: SymbolicObj2 -> Obj2 -- Primitives -getImplicit2 (RectR r (x1,y1) (x2,y2)) = \(x,y) -> rmaximum r - [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2] - where (dx, dy) = (x2-x1, y2-y1) -getImplicit2 (Circle r ) = +getImplicit2 (RectR r (x1,y1) (x2,y2)) = + \(x,y) -> let + (dx, dy) = (x2-x1, y2-y1) + in + if r == 0 + then maximum [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2] + else rmaximum r [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2] +getImplicit2 (Circle r) = \(x,y) -> sqrt (x * x + y * y) - r getImplicit2 (PolygonR _ points) = \p -> let pair :: Int -> (ℝ2,ℝ2) - pair n = (points !! n, points !! (mod (n + 1) (length points) ) ) - pairs = [ pair n | n <- [0 .. (length points) - 1] ] + pair n = (points !! n, points !! mod (n + 1) (length points) ) + pairs = [ pair n | n <- [0 .. length points - 1] ] relativePairs = map (\(a,b) -> (a ^-^ p, b ^-^ p) ) pairs crossing_points = [x2 ^-^ y2*(x2-x1)/(y2-y1) | ((x1,y1), (x2,y2)) <-relativePairs, ( (y2 <= 0) && (y1 >= 0) ) || ( (y2 >= 0) && (y1 <= 0) ) ] - seemsInRight = odd $ length $ filter (>0) $ nub crossing_points - seemsInLeft = odd $ length $ filter (<0) $ nub crossing_points + -- FIXME: use partition instead? + seemsInRight = odd . length . filter (>0) $ nub crossing_points + seemsInLeft = odd . length . filter (<0) $ nub crossing_points isIn = seemsInRight && seemsInLeft dists = map (distFromLineSeg p) pairs :: [ℝ] in minimum dists * if isIn then -1 else 1 -- (Rounded) CSG getImplicit2 (Complement2 symbObj) = - let + \p -> let obj = getImplicit2 symbObj in - \p -> - obj p + - obj p getImplicit2 (UnionR2 r symbObjs) = - let + \p -> let objs = map getImplicit2 symbObjs in if r == 0 - then \p -> minimum $ map ($p) objs - else \p -> rminimum r $ map ($p) objs + then minimum $ map ($p) objs + else rminimum r $ map ($p) objs getImplicit2 (DifferenceR2 r symbObjs) = let objs = map getImplicit2 symbObjs obj = head objs complement :: forall a t. Num a => (t -> a) -> t -> a - complement obj' = \p -> - obj' p + complement obj' p = - obj' p in if r == 0 - then \p -> maximum $ map ($p) $ obj:(map complement $ tail objs) - else \p -> rmaximum r $ map ($p) $ obj:(map complement $ tail objs) + then \p -> maximum . map ($p) $ obj:map complement (tail objs) + else \p -> rmaximum r . map ($p) $ obj:map complement (tail objs) getImplicit2 (IntersectR2 r symbObjs) = - let + \p -> let objs = map getImplicit2 symbObjs in if r == 0 - then \p -> maximum $ map ($p) objs - else \p -> rmaximum r $ map ($p) objs + then maximum $ map ($p) objs + else rmaximum r $ map ($p) objs -- Simple transforms getImplicit2 (Translate2 v symbObj) = - let + \p -> let obj = getImplicit2 symbObj in - \p -> obj (p ^-^ v) + obj (p ^-^ v) getImplicit2 (Scale2 s@(sx,sy) symbObj) = - let + \p -> let obj = getImplicit2 symbObj k = abs(max sx sy) in - \p -> k * obj (p ⋯/ s) + k * obj (p ⋯/ s) getImplicit2 (Rotate2 θ symbObj) = - let + \(x,y) -> let obj = getImplicit2 symbObj in - \(x,y) -> obj ( cos(θ)*x + sin(θ)*y, cos(θ)*y - sin(θ)*x) + obj ( x*cos θ + y*sin θ, y*cos θ - x*sin θ) -- Boundary mods getImplicit2 (Shell2 w symbObj) = - let + \p -> let obj = getImplicit2 symbObj in - \p -> abs (obj p) - w/2 + abs (obj p) - w/2 getImplicit2 (Outset2 d symbObj) = - let + \p -> let obj = getImplicit2 symbObj in - \p -> obj p - d + obj p - d -- Misc getImplicit2 (EmbedBoxedObj2 (obj,_)) = obj From e65e362820100cc96a739ff4aa0b6344f20f7d31 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 21:08:58 +0100 Subject: [PATCH 029/227] remove redundant $ and ()s, and normalize implementation forms. --- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 4b87cef..c8ca958 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -59,11 +59,11 @@ getImplicit3 (DifferenceR3 r symbObjs) = objs = map getImplicit3 symbObjs obj = head objs complement :: forall a t. Num a => (t -> a) -> t -> a - complement obj' = \p -> - obj' p + complement obj' p = - obj' p in if r == 0 - then \p -> maximum $ map ($p) $ obj:(map complement $ tail objs) - else \p -> rmaximum r $ map ($p) $ obj:(map complement $ tail objs) + then \p -> maximum $ map ($p) $ obj:map complement (tail objs) + else \p -> rmaximum r $ map ($p) $ obj:map complement (tail objs) -- Simple transforms getImplicit3 (Translate3 v symbObj) = let @@ -80,13 +80,13 @@ getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = let obj = getImplicit3 symbObj rotateYZ :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) - rotateYZ θ obj' = \(x,y,z) -> obj' ( x, cos(θ)*y + sin(θ)*z, cos(θ)*z - sin(θ)*y) + rotateYZ θ obj' (x,y,z) = obj' ( x, y*cos θ + z*sin θ, z*cos θ - y*sin θ) rotateZX :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) - rotateZX θ obj' = \(x,y,z) -> obj' ( cos(θ)*x - sin(θ)*z, y, cos(θ)*z + sin(θ)*x) + rotateZX θ obj' (x,y,z) = obj' ( x*cos θ - z*sin θ, y, z*cos θ + x*sin θ) rotateXY :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) - rotateXY θ obj' = \(x,y,z) -> obj' ( cos(θ)*x + sin(θ)*y, cos(θ)*y - sin(θ)*x, z) + rotateXY θ obj' (x,y,z) = obj' ( x*cos θ + y*sin θ, y*cos θ - x*sin θ, z) in - rotateYZ yz $ rotateZX zx $ rotateXY xy $ obj + rotateYZ yz . rotateZX zx $ rotateXY xy obj getImplicit3 (Rotate3V θ axis symbObj) = let axis' = normalized axis @@ -98,9 +98,9 @@ getImplicit3 (Rotate3V θ axis symbObj) = , ax * by - ay * bx ) in \v -> obj $ - v ^* cos(θ) - ^-^ (axis' `cross3` v) ^* sin(θ) - ^+^ (axis' ^* (axis' <.> (v ^* (1 - cos(θ))))) + v ^* cos θ + ^-^ (axis' `cross3` v) ^* sin θ + ^+^ (axis' ^* (axis' <.> (v ^* (1 - cos θ)))) -- Boundary mods getImplicit3 (Shell3 w symbObj) = let @@ -130,9 +130,9 @@ getImplicit3 (ExtrudeRM r twist scale translate symbObj height) = Left n -> n Right f -> f (x,y) scaleVec :: ℝ -> ℝ2 -> ℝ2 - scaleVec s = \(x,y) -> (x/s, y/s) + scaleVec s (x,y) = (x/s, y/s) rotateVec :: ℝ -> ℝ2 -> ℝ2 - rotateVec θ (x,y) = (x*cos(θ)+y*sin(θ), y*cos(θ)-x*sin(θ)) + rotateVec θ (x,y) = (x*cos θ + y*sin θ, y*cos θ - x*sin θ) k = (pi :: ℝ)/(180:: ℝ) in \(x,y,z) -> let h = height' (x,y) in @@ -157,12 +157,12 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = round' = Maybe.fromMaybe 0 round translate' :: ℝ -> ℝ2 translate' = Either.either - (\(a,b) -> \θ -> (a*θ/totalRotation', b*θ/totalRotation')) + (\(a,b) θ -> (a*θ/totalRotation', b*θ/totalRotation')) (. (/k)) translate rotate' :: ℝ -> ℝ rotate' = Either.either - (\t -> \θ -> t*θ/totalRotation' ) + (\t θ -> t*θ/totalRotation' ) (. (/k)) rotate twists = case rotate of @@ -178,9 +178,9 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = ns = if capped then -- we will cap a different way, but want leeway to keep the function cont - [-1 .. (ceiling (totalRotation' / tau)) + 1] + [-1 .. ceiling (totalRotation' / tau) + 1] else - [0 .. floor $ (totalRotation' - θ) /tau] + [0 .. floor $ (totalRotation' - θ) / tau] n <- ns let θvirt = fromIntegral n * tau + θ @@ -201,4 +201,4 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = else obj rz_pos -- FIXME: implement this, or implement a fallthrough function. --getImplicit3 (ExtrudeRotateR) = -getImplicit3 (ExtrudeRotateR _ _ _ _) = error "ExtrudeRotateR unimplimented!" +getImplicit3 ExtrudeRotateR{} = error "ExtrudeRotateR unimplimented!" From 2fbbb5b15bcd6707ecb9f853f2d36c14c15ffcb7 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 21:23:50 +0100 Subject: [PATCH 030/227] upgrade dependencies more often, and remove more intemediary files. --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c36f406..459b76d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ RTSOPTS=+RTS -N -RESOPTS=-r 10 +RESOPTS=-r 50 #uncomment for profiling support. #PROFILING= --enable-library-profiling --enable-executable-profiling @@ -26,8 +26,11 @@ clean: Setup rm -f Examples/*.ps rm -f Examples/*.png rm -f Examples/example[0-9][0-9] + rm -f Examples/*.hi + rm -f Examples/*.o rm -f tests/*.stl rm -f Setup Setup.hi Setup.o + rm -rf dist/* distclean: clean rm -f `find ./ -name *~` @@ -57,8 +60,8 @@ dist/build/extopenscad/extopenscad: Setup dist/setup-config cabal build dist/setup-config: Setup implicit.cabal - cabal install --only-dependencies - cabal configure $(PROFILING) + cabal install --only-dependencies --upgrade-dependencies + cabal configure --enable-tests $(PROFILING) Setup: Setup.*hs ghc -O2 -Wall --make Setup From 240fc1811e7462803a798d487f6d65ae89e58319 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 21:24:25 +0100 Subject: [PATCH 031/227] more warnings, and a minor optimization. --- implicit.cabal | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 5269e8d..cf77d5a 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -46,7 +46,8 @@ library ghc-options: -Wall -- for debugging only. --- -Weverything + -Wextra + -Weverything -O2 -optc-O3 -- cannot use, we use infinity in some calculations. @@ -121,8 +122,11 @@ executable extopenscad implicit ghc-options: -threaded + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing -rtsopts -Wall + -Weverything -O2 -optc-O3 -optc-ffast-math @@ -191,6 +195,7 @@ executable implicitsnap -threaded -rtsopts -Wall + -Weverything -O2 -optc-O3 -optc-ffast-math @@ -223,6 +228,7 @@ executable Benchmark -threaded -rtsopts -Wall + -Weverything -O2 -optc-O3 -optc-ffast-math @@ -232,15 +238,21 @@ test-suite test-implicit build-depends: base, mtl, containers, hspec, parsec, implicit main-is: Main.hs hs-source-dirs: tests + ghc-options: + -Wall + -Weverything + -O2 + -optc-O3 benchmark parser-bench type: exitcode-stdio-1.0 - hs-source-dirs: bench - main-is: ParserBench.hs build-depends: base, criterion, random, parsec, implicit + main-is: ParserBench.hs ghc-options: -Wall - -O2 -optc-O3 + -Weverything + -O2 + -optc-O3 source-repository head type: git From d792ea4167a6e446d54f3b6eee95a6da7077318b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 21:53:15 +0100 Subject: [PATCH 032/227] replace [Char] with String, do not pass an unused program name into runOpenscad, improve comments, remove unneede ()s, and rename variables for clarity. --- Graphics/Implicit/ExtOpenScad.hs | 8 +-- .../Implicit/ExtOpenScad/Parser/Statement.hs | 71 +++++++++---------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad.hs b/Graphics/Implicit/ExtOpenScad.hs index 7bdfc45..0e0e8dc 100644 --- a/Graphics/Implicit/ExtOpenScad.hs +++ b/Graphics/Implicit/ExtOpenScad.hs @@ -9,7 +9,7 @@ module Graphics.Implicit.ExtOpenScad (runOpenscad) where -import Prelude(Char, Either(Left, Right), IO, ($), fmap) +import Prelude(String, Either(Left, Right), IO, ($), fmap) import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3) import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup, OVal) @@ -24,14 +24,14 @@ import qualified Control.Monad.State as State (runStateT) import qualified System.Directory as Dir (getCurrentDirectory) -- Small wrapper to handle parse errors, etc. -runOpenscad :: [Char] -> Either Parsec.ParseError (IO (VarLookup, [SymbolicObj2], [SymbolicObj3])) -runOpenscad s = +runOpenscad :: String -> Either Parsec.ParseError (IO (VarLookup, [SymbolicObj2], [SymbolicObj3])) +runOpenscad source = let initial = defaultObjects rearrange :: forall t t1 t2 t3 t4. (t, (t4, [OVal], t1, t2, t3)) -> (t4, [SymbolicObj2], [SymbolicObj3]) rearrange (_, (varlookup, ovals, _ , _ , _)) = (varlookup, obj2s, obj3s) where (obj2s, obj3s, _ ) = divideObjs ovals - in case parseProgram "" s of + in case parseProgram source of Left e -> Left e Right sts -> Right $ fmap rearrange diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index 92b0df9..ded2755 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -8,7 +8,8 @@ -- FIXME: required. why? {-# LANGUAGE KindSignatures #-} -module Graphics.Implicit.ExtOpenScad.Parser.Statement where +-- The entry point for parsing an ExtOpenScad program. +module Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) where import Prelude(Char, Either, String, Maybe(Just, Nothing), Monad, return, fmap, ($), (>>), Bool(False, True), map) @@ -20,17 +21,19 @@ import Data.Functor.Identity(Identity) import Graphics.Implicit.ExtOpenScad.Definitions (Pattern(Name), Statement(DoNothing, NewModule, Include, Echo, If, For, ModuleCall,(:=)),Expr(LamE), StatementI(StatementI)) import Graphics.Implicit.ExtOpenScad.Parser.Util (genSpace, tryMany, stringGS, (*<|>), (?:), patternMatcher, variableSymb) + +-- the top level of the expression parser. import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) -parseProgram :: SourceName -> [Char] -> Either ParseError [StatementI] -parseProgram name s = parse program name s where - program :: ParsecT [Char] u Identity [StatementI] +parseProgram :: String -> Either ParseError [StatementI] +parseProgram = parse program "" where -- "" is our program name. + program :: ParsecT String u Identity [StatementI] program = do sts <- many1 computation eof return sts --- | A in our programming openscad-like programming language. +-- | A computable block of code in our openscad-like programming language. computation :: GenParser Char st StatementI computation = do -- suite statements: no semicolon... @@ -39,30 +42,21 @@ computation = ifStatementI, forStatementI, throwAway, - userModuleDeclaration{-, - unimplemented "mirror", - unimplemented "multmatrix", - unimplemented "color", - unimplemented "render", - unimplemented "surface", - unimplemented "projection", - unimplemented "import_stl"-} - -- rotateExtrude + userModuleDeclaration ] _ <- genSpace return s - *<|> do -- Non suite s. Semicolon needed... + *<|> do -- Non suite statements. Semicolon needed... _ <- genSpace s <- tryMany [ echo, - include, + include, -- also handles use function, - assignment--, - --use + assignment ] _ <- stringGS " ; " return s - *<|> do -- Modules + *<|> do -- Modules. no semicolon... _ <- genSpace s <- userModule _ <- genSpace @@ -81,9 +75,8 @@ computation = -- -- union() sphere(3); -- --- We consider it to be a list of s which +-- We consider it to be a list of computables which -- are in turn StatementI s. --- So this parses them. -} suite :: GenParser Char st [StatementI] suite = (fmap return computation <|> do @@ -95,7 +88,7 @@ suite = (fmap return computation <|> do return stmts ) " suite" - +-- commenting out a comuptation: use % or * before the statement, and it will not be run. throwAway :: GenParser Char st StatementI throwAway = do line <- lineNumber @@ -105,7 +98,7 @@ throwAway = do _ <- computation return $ StatementI line DoNothing --- An included ! Basically, inject another openscad file here... +-- An include! Basically, inject another openscad file here... include :: GenParser Char st StatementI include = (do line <- lineNumber @@ -117,34 +110,34 @@ include = (do return $ StatementI line $ Include filename injectVals ) "include " --- | An assignment (parser) +-- | An assignment (parser) assignment :: GenParser Char st StatementI assignment = ("assignment " ?:) $ do line <- lineNumber - pattern <- patternMatcher + lvalue <- patternMatcher _ <- stringGS " = " valExpr <- expr0 - return $ StatementI line$ pattern := valExpr + return $ StatementI line $ lvalue := valExpr -- | A function declaration (parser) function :: GenParser Char st StatementI function = ("function " ?:) $ do line <- lineNumber - varSymb <- (string "function" >> space >> genSpace >> variableSymb) + varSymb <- string "function" >> space >> genSpace >> variableSymb _ <- stringGS " ( " argVars <- sepBy patternMatcher (stringGS " , ") _ <- stringGS " ) = " valExpr <- expr0 return $ StatementI line $ Name varSymb := LamE argVars valExpr --- | An echo (parser) +-- | An echo (parser) echo :: GenParser Char st StatementI echo = do line <- lineNumber _ <- stringGS "echo ( " - exprs <- expr0 `sepBy` (stringGS " , ") + exprs <- expr0 `sepBy` stringGS " , " _ <- stringGS " ) " return $ StatementI line $ Echo exprs @@ -157,7 +150,7 @@ ifStatementI = _ <- stringGS " ) " sTrueCase <- suite _ <- genSpace - sFalseCase <- (stringGS "else " >> suite ) *<|> (return []) + sFalseCase <- (stringGS "else " >> suite ) *<|> return [] return $ StatementI line $ If bexpr sTrueCase sFalseCase forStatementI :: GenParser Char st StatementI @@ -169,13 +162,14 @@ forStatementI = -- eg. for ( a = [1,2,3] ) {echo(a); echo "lol";} -- eg. for ( [a,b] = [[1,2]] ) {echo(a+b); echo "lol";} _ <- stringGS "for ( " - pattern <- patternMatcher + lvalue <- patternMatcher _ <- stringGS " = " vexpr <- expr0 _ <- stringGS " ) " loopContent <- suite - return $ StatementI line $ For pattern vexpr loopContent + return $ StatementI line $ For lvalue vexpr loopContent +-- parse a call to a module. userModule :: GenParser Char st StatementI userModule = do line <- lineNumber @@ -186,6 +180,7 @@ userModule = do s <- suite *<|> (stringGS " ; " >> return []) return $ StatementI line $ ModuleCall name args s +-- declare a module. userModuleDeclaration :: GenParser Char st StatementI userModuleDeclaration = do line <- lineNumber @@ -197,8 +192,7 @@ userModuleDeclaration = do s <- suite return $ StatementI line $ NewModule newModuleName args s ----------------------- - +-- parse the arguments passed to a module. moduleArgsUnit :: GenParser Char st [(Maybe String, Expr)] moduleArgsUnit = do _ <- stringGS " ( " @@ -208,7 +202,7 @@ moduleArgsUnit = do symb <- variableSymb _ <- stringGS " = " expr <- expr0 - return $ (Just symb, expr) + return (Just symb, expr) *<|> do -- eg. a(x,y) = 12 symb <- variableSymb @@ -216,7 +210,7 @@ moduleArgsUnit = do argVars <- sepBy variableSymb (try $ stringGS " , ") _ <- stringGS " ) = " expr <- expr0 - return $ (Just symb, LamE (map Name argVars) expr) + return (Just symb, LamE (map Name argVars) expr) *<|> do -- eg. 12 expr <- expr0 @@ -225,6 +219,7 @@ moduleArgsUnit = do _ <- stringGS " ) " return args +-- parse the arguments in the module declaration. moduleArgsUnitDecl :: GenParser Char st [(String, Maybe Expr)] moduleArgsUnitDecl = do _ <- stringGS " ( " @@ -241,8 +236,6 @@ moduleArgsUnitDecl = do _ <- sepBy variableSymb (try $ stringGS " , ") _ <- stringGS " ) = " expr <- expr0 --- FIXME: this line looks right, but.. what does this change? --- return $ (Just symb, LamE (map Name argVars) expr) return (symb, Just expr) *<|> do symb <- variableSymb @@ -251,12 +244,14 @@ moduleArgsUnitDecl = do _ <- stringGS " ) " return argTemplate +-- find the line number. used when generating errors. lineNumber :: forall s u (m :: * -> *). Monad m => ParsecT s u m Line lineNumber = fmap sourceLine getPosition --FIXME: use the below function to improve error reporting. {- +-- find the column number. SHOULD be used when generating errors. columnNumber :: forall s u (m :: * -> *). Monad m => ParsecT s u m Column columnNumber = fmap sourceColumn getPosition From 8d29a8de17e7e95f5ff82820bc211df392ad3b56 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 22:29:45 +0100 Subject: [PATCH 033/227] remove unnecessary variables, use >=> instead of >>=, improve comments, and switch a data to a newtype. --- Graphics/Implicit/ExtOpenScad/Definitions.hs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Definitions.hs b/Graphics/Implicit/ExtOpenScad/Definitions.hs index 6c9bb1f..aa89c00 100644 --- a/Graphics/Implicit/ExtOpenScad/Definitions.hs +++ b/Graphics/Implicit/ExtOpenScad/Definitions.hs @@ -14,12 +14,12 @@ module Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch TestInvariant(EulerCharacteristic), collector) where -import Prelude(Eq, Show, String, Maybe, Bool(True, False), Int, IO, (==), show, map, ($), (++), undefined, all, id, zipWith, foldl1) +import Prelude(Eq, Show, String, Maybe, Bool(True, False), Int, IO, (==), show, map, ($), (++), undefined, and, zipWith, foldl1) import Graphics.Implicit.Definitions (ℝ, SymbolicObj2, SymbolicObj3) import Control.Applicative (Applicative, Alternative((<|>), empty), pure, (<*>)) -import Control.Monad (Functor, Monad, fmap, (>>=), mzero, mplus, MonadPlus, liftM, ap, return) +import Control.Monad (Functor, Monad, fmap, (>>=), mzero, mplus, MonadPlus, liftM, ap, return, (>=>)) import Data.Map (Map) ----------------------------------------------------------------- @@ -45,14 +45,14 @@ instance Functor ArgParser where fmap = liftM instance Applicative ArgParser where - pure a = APTerminator a + pure = APTerminator (<*>) = ap instance Monad ArgParser where -- We need to describe how (>>=) works. -- Let's get the hard ones out of the way first. -- ArgParser actually - (AP str fallback d f) >>= g = AP str fallback d (\a -> (f a) >>= g) + (AP str fallback d f) >>= g = AP str fallback d (f >=> g) (APFailIf b errmsg child) >>= g = APFailIf b errmsg (child >>= g) -- These next to is easy, they just pass the work along to their child (APExample str child) >>= g = APExample str (child >>= g) @@ -60,13 +60,13 @@ instance Monad ArgParser where -- And an ArgParserTerminator happily gives away the value it contains (APTerminator a) >>= g = g a (APBranch bs) >>= g = APBranch $ map (>>= g) bs - return g = APTerminator g + return = pure instance MonadPlus ArgParser where mzero = APFailIf True "" undefined mplus (APBranch as) (APBranch bs) = APBranch ( as ++ bs ) mplus (APBranch as) b = APBranch ( as ++ [b] ) - mplus a (APBranch bs) = APBranch ( [a] ++ bs ) + mplus a (APBranch bs) = APBranch ( a : bs ) mplus a b = APBranch [ a , b ] instance Alternative ArgParser where @@ -88,6 +88,7 @@ data Expr = Var Symbol | Expr :$ [Expr] deriving (Show, Eq) +-- a statement, along with the line number it is found on. data StatementI = StatementI Int (Statement StatementI) deriving (Show, Eq) @@ -118,7 +119,7 @@ data OVal = OUndefined instance Eq OVal where (OBool a) == (OBool b) = a == b (ONum a) == (ONum b) = a == b - (OList a) == (OList b) = all id $ zipWith (==) a b + (OList a) == (OList b) = and $ zipWith (==) a b (OString a) == (OString b) = a == b _ == _ = False @@ -141,6 +142,6 @@ collector :: Symbol -> [Expr] -> Expr collector _ [x] = x collector s l = Var s :$ [ListE l] -data TestInvariant = EulerCharacteristic Int +newtype TestInvariant = EulerCharacteristic Int deriving (Show) From 2191fdbf5fdf5093b3787634e5dcf406fccef3ca Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 22:43:01 +0100 Subject: [PATCH 034/227] be more explicit about our imports, add more function types, and add another item to benchmark. --- programs/Benchmark.hs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index 4e45310..8449d7c 100644 --- a/programs/Benchmark.hs +++ b/programs/Benchmark.hs @@ -6,18 +6,23 @@ -- Let's be explicit about where things come from :) +import Prelude (($), (*), (/), String, IO, cos, pi, map, zip3, Maybe(Just, Nothing), Either(Left)) + -- Use criterion for benchmarking. see -import Criterion.Main +import Criterion.Main (Benchmark, bgroup, bench, nf, defaultMain) -- The parts of ImplicitCAD we know how to benchmark (in theory). -import Graphics.Implicit (union, circle, writeSVG, writePNG2, writePNG3, writeSTL, SymbolicObj2, SymbolicObj3) +import Graphics.Implicit (union, circle, SymbolicObj2, SymbolicObj3) import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) import Graphics.Implicit.Primitives (translate, difference, extrudeRM, rect3R) +-- The variable defining distance in our world. +import Graphics.Implicit.Definitions (ℝ) + -- Haskell representations of objects to benchmark. --- FIXME: move each of these objects into seperate compilable files. +-- FIXME: move each of these objects into seperate compilable files. obj2d_1 :: SymbolicObj2 obj2d_1 = @@ -31,7 +36,9 @@ obj2d_1 = object1 :: SymbolicObj3 object1 = extrudeRM 0 (Just twist) Nothing Nothing obj2d_1 (Left 40) - where twist h = 35*cos(h*2*pi/60) + where + twist :: ℝ -> ℝ + twist h = 35*cos(h*2*pi/60) object2 :: SymbolicObj3 object2 = squarePipe (10,10,10) 1 100 @@ -71,11 +78,14 @@ obj3Benchmarks name obj = bench "Get mesh" $ nf (symbolicGetMesh 1) obj ] +benchmarks :: [Benchmark] benchmarks = [ obj3Benchmarks "Object 1" object1 , obj3Benchmarks "Object 2" object2 , obj3Benchmarks "Object 3" object3 + , obj2Benchmarks "Object 2d 1" obj2d_1 ] +main :: IO () main = defaultMain benchmarks From 9cf296fdd858aeff35de24dc4c515e8bb1fc520b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 23:09:17 +0100 Subject: [PATCH 035/227] be more explicit about imports, use fromMaybe, spacing changes, add type declarations, andremove unnecessary $s, and move from [Char] to String. --- programs/extopenscad.hs | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index f131f50..510b9b5 100644 --- a/programs/extopenscad.hs +++ b/programs/extopenscad.hs @@ -11,6 +11,8 @@ -- Let's be explicit about what we're getting from where :) +import Prelude (Read(readsPrec), Maybe(Just, Nothing), Either(Left, Right), IO, FilePath, Show, Eq, Ord, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, print, (>>=), lookup) + -- Our Extended OpenScad interpreter, and functions to write out files in designated formats. import Graphics.Implicit (runOpenscad, writeSVG, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeGCodeHacklabLaser, writePNG2, writePNG3) @@ -37,7 +39,7 @@ import Graphics.Implicit.ExtOpenScad.Definitions (OVal (ONum)) -- Operator to subtract two points. Used when defining the resolution of a 2d object. import Data.AffineSpace ((.-.)) -import Data.Monoid (Monoid, mappend, mconcat) +import Data.Monoid (Monoid, mappend) import Control.Applicative ((<$>), (<*>)) @@ -88,7 +90,7 @@ formatExtensions = -- Lookup an output format for a given output file. Throw an error if one cannot be found. guessOutputFormat :: FilePath -> OutputFormat guessOutputFormat fileName = - maybe (error $ "Unrecognized output format: "<>ext) id + fromMaybe (error $ "Unrecognized output format: "<>ext) $ readOutputFormat $ tail ext where (_,ext) = splitExtension fileName @@ -134,14 +136,16 @@ readOutputFormat ext = lookup (map toLower ext) formatExtensions instance Read OutputFormat where readsPrec _ myvalue = tryParse formatExtensions - where tryParse [] = [] -- If there is nothing left to try, fail - tryParse ((attempt, result):xs) = - if (take (length attempt) myvalue) == attempt - then [(result, drop (length attempt) myvalue)] - else tryParse xs + where + tryParse :: [(String, OutputFormat)] -> [(OutputFormat, String)] + tryParse [] = [] -- If there is nothing left to try, fail + tryParse ((attempt, result):xs) = + if take (length attempt) myvalue == attempt + then [(result, drop (length attempt) myvalue)] + else tryParse xs -- Find the resolution to raytrace at. -getRes :: (Map.Map [Char] OVal, [SymbolicObj2], [SymbolicObj3]) -> ℝ +getRes :: (Map.Map String OVal, [SymbolicObj2], [SymbolicObj3]) -> ℝ -- First, use a resolution specified by a variable in the input file. getRes (Map.lookup "$res" -> Just (ONum res), _, _) = res -- Use a resolution chosen for 3D objects. @@ -160,8 +164,8 @@ getRes (varlookup, obj:_, _) = (p1,p2) = getBox2 obj (x,y) = p2 .-. p1 in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of - ONum qual | qual > 0 -> min (min x y/2) ((x*y/qual)**0.5 / 30) - _ -> min (min x y/2) ((x*y)**0.5 / 30) + ONum qual | qual > 0 -> min (min x y/2) (sqrt(x*y/qual) / 30) + _ -> min (min x y/2) (sqrt(x*y) / 30) -- fallthrough value. getRes _ = 1 @@ -191,21 +195,21 @@ export2 posFmt res output obj = run :: ExtOpenScadOpts -> IO() run args = do - putStrLn $ "Loading File." + putStrLn "Loading File." content <- readFile (inputFile args) let format = case () of - _ | Just fmt <- outputFormat args -> Just $ fmt + _ | Just fmt <- outputFormat args -> Just fmt _ | Just file <- outputFile args -> Just $ guessOutputFormat file _ -> Nothing - putStrLn $ "Processing File." + putStrLn "Processing File." case runOpenscad content of - Left err -> putStrLn $ show $ err + Left err -> print err Right openscadProgram -> do s@(_, obj2s, obj3s) <- openscadProgram - let res = maybe (getRes s) id (resolution args) + let res = fromMaybe (getRes s) (resolution args) let basename = fst (splitExtension $ inputFile args) let posDefExt = case format of Just f -> Prelude.lookup f (map swap formatExtensions) @@ -218,7 +222,7 @@ run args = do putStrLn $ "Rendering 3D object to " ++ output putStrLn $ "With resolution " ++ show res putStrLn $ "In box " ++ show (getBox3 obj) - putStrLn $ show obj + print obj export3 format res output obj ([obj], []) -> do let output = fromMaybe @@ -227,7 +231,7 @@ run args = do putStrLn $ "Rendering 2D object to " ++ output putStrLn $ "With resolution " ++ show res putStrLn $ "In box " ++ show (getBox2 obj) - putStrLn $ show obj + print obj export2 format res output obj ([], []) -> putStrLn "No objects to render." _ -> putStrLn "Multiple/No objects, what do you want to render?" From 08ecc36b876678c57a580993daf63877acc2758b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 23:17:06 +0100 Subject: [PATCH 036/227] add longer timeout for requests, be more specific about imports, move $ to . in some cases, improve comments, formatting changes, and handle an unexpected case. --- programs/implicitsnap.hs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index cee75f3..186983b 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -7,13 +7,17 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ViewPatterns #-} + -- A Snap(HTTP) server providing an ImplicitCAD REST API. -- Let's be explicit about what we're getting from where :) +import Prelude (IO, Maybe(Just, Nothing), Ord, String, Bool(True, False), Either(Left, Right), Show, Char, ($), (++), (>), (.), (-), (/), (*), (**), sqrt, min, max, minimum, maximum, show, return) + import Control.Applicative ((<|>)) -import Snap.Core (Snap, route, writeBS, method, Method(GET), modifyResponse, setContentType, getRequest, rqParam) +import Snap.Core (Snap, route, writeBS, method, Method(GET), modifyResponse, setContentType, setTimeout, getRequest, rqParam) import Snap.Http.Server (quickHttpServe) import Snap.Util.GZip (withCompression) @@ -64,15 +68,16 @@ site = route renderHandler :: Snap () renderHandler = method GET $ withCompression $ do modifyResponse $ setContentType "application/x-javascript" + setTimeout 600 request <- getRequest case (rqParam "source" request, rqParam "callback" request, rqParam "format" request) of (Just [source], Just [callback], Nothing) -> - writeBS $ BS.Char.pack $ executeAndExport + writeBS . BS.Char.pack $ executeAndExport (BS.Char.unpack source) (BS.Char.unpack callback) Nothing (Just [source], Just [callback], Just [format]) -> - writeBS $ BS.Char.pack $ executeAndExport + writeBS . BS.Char.pack $ executeAndExport (BS.Char.unpack source) (BS.Char.unpack callback) (Just $ BS.Char.unpack format) @@ -83,7 +88,8 @@ getRes :: forall k. (Data.String.IsString k, Ord k) => (Map k OVal, [SymbolicObj -- First, use a resolution specified by a variable in the input file. getRes (Map.lookup "$res" -> Just (ONum res), _, _) = res --- Use a resolution chosen for 3D objects. + +-- If there was no resolution specified, use a resolution chosen for 3D objects. -- FIXME: magic numbers. getRes (varlookup, _, obj:_) = let @@ -91,7 +97,7 @@ getRes (varlookup, _, obj:_) = (x,y,z) = (x2-x1, y2-y1, z2-z1) in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of ONum qual | qual > 0 -> min (minimum [x,y,z]/2) ((x*y*z/qual)**(1/3) / 22) - _ -> min (minimum [x,y,z]/2) ((x*y*z)**(1/3) / 22) + _ -> min (minimum [x,y,z]/2) ((x*y*z )**(1/3) / 22) -- Use a resolution chosen for 2D objects. -- FIXME: magic numbers. getRes (varlookup, obj:_, _) = @@ -99,8 +105,8 @@ getRes (varlookup, obj:_, _) = (p1,p2) = getBox2 obj (x,y) = p2 .-. p1 in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of - ONum qual | qual > 0 -> min (min x y/2) ((x*y/qual)**0.5 / 30) - _ -> min (min x y/2) ((x*y)**0.5 / 30) + ONum qual | qual > 0 -> min (min x y/2) (sqrt(x*y/qual) / 30) + _ -> min (min x y/2) (sqrt(x*y ) / 30) -- fallthrough value. getRes _ = 1 @@ -145,6 +151,7 @@ getWidth (_, [], []) = 0 executeAndExport :: String -> String -> Maybe String -> String executeAndExport content callback maybeFormat = let + showB :: IsString t => Bool -> t showB True = "true" showB False = "false" callbackF :: Bool -> Bool -> ℝ -> String -> String @@ -152,6 +159,7 @@ executeAndExport content callback maybeFormat = callback ++ "([null," ++ show msg ++ "," ++ showB is2D ++ "," ++ show w ++ "]);" callbackF True is2D w msg = callback ++ "([new Shape()," ++ show msg ++ "," ++ showB is2D ++ "," ++ show w ++ "]);" + callbackS :: (Show a1, Show a) => a -> a1 -> [Char] callbackS str msg = callback ++ "([" ++ show str ++ "," ++ show msg ++ ",null,null]);" in case runOpenscad content of Left err -> @@ -195,6 +203,7 @@ executeAndExport content callback maybeFormat = callbackS (TL.unpack (svg (discreteAprox res obj))) msgs (Right (Just obj, _), Just "gcode/hacklab-laser") -> callbackS (TL.unpack (hacklabLaserGCode (discreteAprox res obj))) msgs - + (Right (_ , _), _) -> + callbackF False False 1 "unexpected case" From f8131dbde918d87fd7014af027d501cc18b3e477 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 23:18:25 +0100 Subject: [PATCH 037/227] remove unneeded $s. --- tests/Main.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Main.hs b/tests/Main.hs index 04d67fc..081c437 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -4,5 +4,5 @@ import ParserSpec.Expr main :: IO () main = hspec $ do - describe "expressions" $ exprSpec - describe "statements" $ statementSpec + describe "expressions" exprSpec + describe "statements" statementSpec From 0bb5e9692bae0e066897a6586d4ca299e987be04 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 23:29:20 +0100 Subject: [PATCH 038/227] remove unneeded ()s, remove unneeded dos. --- tests/ParserSpec/Expr.hs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index cca7a0c..b8360d3 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -1,3 +1,7 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright (C) 2014-2017, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + module ParserSpec.Expr (exprSpec) where import Test.Hspec @@ -11,19 +15,19 @@ import Data.Either infixr 1 --> (-->) :: String -> Expr -> Expectation (-->) source expr = - (parseExpr source) `shouldBe` Right expr + parseExpr source `shouldBe` Right expr infixr 1 -->+ (-->+) :: String -> (Expr, String) -> Expectation (-->+) source (result, leftover) = - (parseWithLeftOver expr0 source) `shouldBe` (Right (result, leftover)) + parseWithLeftOver expr0 source `shouldBe` Right (result, leftover) ternaryIssue :: Expectation -> Expectation ternaryIssue _ = pendingWith "parser doesn't handle ternary operator correctly" logicalSpec :: Spec logicalSpec = do - it "handles not" $ "!foo" --> (app' "!" [Var "foo"]) + it "handles not" $ "!foo" --> app' "!" [Var "foo"] it "handles and/or" $ do "foo && bar" --> app' "&&" [Var "foo", Var "bar"] "foo || bar" --> app' "||" [Var "foo", Var "bar"] @@ -40,9 +44,9 @@ logicalSpec = do literalSpec :: Spec literalSpec = do - it "handles integers" $ do + it "handles integers" $ "12356" --> num 12356 - it "handles floats" $ do + it "handles floats" $ "23.42" --> num 23.42 describe "booleans" $ do it "accepts true" $ "true" --> bool True @@ -51,24 +55,23 @@ literalSpec = do exprSpec :: Spec exprSpec = do describe "literals" literalSpec - describe "identifiers" $ do + describe "identifiers" $ it "accepts valid variable names" $ do "foo" --> Var "foo" "foo_bar" --> Var "foo_bar" - describe "literals" $ literalSpec + describe "literals" literalSpec describe "grouping" $ do - it "allows parens" $ do + it "allows parens" $ "( false )" --> bool False - it "handles vectors" $ do + it "handles vectors" $ "[ 1, 2, 3 ]" --> ListE [num 1, num 2, num 3] - it "handles lists" $ do + it "handles lists" $ "( 1, 2, 3 )" --> ListE [num 1, num 2, num 3] it "handles generators" $ "[ a : 1 : b + 10 ]" --> - (app "list_gen" [Var "a", num 1, app "+" [Var "b", num 10]]) + app "list_gen" [Var "a", num 1, app "+" [Var "b", num 10]] it "handles indexing" $ "foo[23]" --> Var "index" :$ [Var "foo", num 23] - describe "arithmetic" $ do it "handles unary +/-" $ do "-42" --> num (-42) @@ -97,7 +100,7 @@ exprSpec = do it "handles precedence" $ parseExpr "1 + 2 / 3 * 5" `shouldBe` (Right $ app "+" [num 1, app "*" [app' "/" [num 2, num 3], num 5]]) - it "handles append" $ - parseExpr "foo ++ bar ++ baz" `shouldBe` - (Right $ app "++" [Var "foo", Var "bar", Var "baz"]) + it "handles append" $ + parseExpr "foo ++ bar ++ baz" `shouldBe` + (Right $ app "++" [Var "foo", Var "bar", Var "baz"]) describe "logical operators" logicalSpec From 4fe0fb84f263a9ba4c303c9e780f320d05f882e5 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 16 Sep 2017 23:29:58 +0100 Subject: [PATCH 039/227] remove unneeded $s, and ()s. --- programs/ParserBench.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/ParserBench.hs b/programs/ParserBench.hs index a865962..ca02619 100644 --- a/programs/ParserBench.hs +++ b/programs/ParserBench.hs @@ -24,7 +24,7 @@ assignments :: Int -> String assignments n = concat ["x = (foo + bar);\n" | _ <- [1..n]] intList :: Int -> String -intList n = "[" ++ concat [(show i) ++ "," | i <- [1..n]] ++ "0]" +intList n = "[" ++ concat [show i ++ "," | i <- [1..n]] ++ "0]" parseExpr :: String -> Expr parseExpr s = case parse expr0 "src" s of @@ -45,12 +45,12 @@ deepArithmetic n run :: String -> (String -> a) -> String -> Benchmark run name func input = - env (return $ input) $ \s -> + env (return input) $ \s -> bench name $ whnf func s main :: IO () main = - defaultMain $ + defaultMain [ bgroup "comments" [ run "line" parseStatements (lineComments 5000) , run "block" parseStatements (blockComments 10 500) From 8063f1a8b45094adeb99dbb6a82100a4b1d7b468 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 17 Sep 2017 00:26:04 +0100 Subject: [PATCH 040/227] be more explicit about where we import things from, add an operator instead of parsesAs, remove unnecessary $s and ()s, and remove unneeded do statements. --- tests/ParserSpec/Statement.hs | 97 +++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 2726d05..56de979 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -1,19 +1,34 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright (C) 2014-2017, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + +-- statement related hspec tests. module ParserSpec.Statement (statementSpec) where -import Test.Hspec -import Text.ParserCombinators.Parsec hiding (State) -import ParserSpec.Util -import Graphics.Implicit.ExtOpenScad.Definitions -import Graphics.Implicit.ExtOpenScad.Parser.Statement -import Data.Either +import Prelude (String, Maybe(Just), Bool(True), ($)) -parsesAs :: String -> [StatementI] -> Expectation -parsesAs source stmts = - (parseProgram "src" source) `shouldBe` Right stmts +import Test.Hspec (Spec, Expectation, shouldBe, shouldSatisfy, it, pendingWith, describe) +-- import Text.ParserCombinators.Parsec () + +import ParserSpec.Util (bool, num, app, app') + +import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) + +-- Parse an ExtOpenScad program. +import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) + +import Data.Either (Either(Left, Right), isLeft) + +-- an expectation that a string become a statement. +infixr 1 --> +(-->) :: String -> [StatementI] -> Expectation +(-->) source stmts = + parseProgram source `shouldBe` Right stmts + +-- an expectation that a string generates an error. parsesAsError :: String -> Expectation -parsesAsError source = - (parseProgram "src" source) `shouldSatisfy` isLeft +parsesAsError source = parseProgram source `shouldSatisfy` isLeft single :: Statement StatementI -> [StatementI] single st = [StatementI 1 st] @@ -22,61 +37,55 @@ call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI call name args stmts = StatementI 1 (ModuleCall name args stmts) ifSpec :: Spec -ifSpec = do - it "parses" $ - "if (true) { a(); } else { b(); }" `parsesAs` ( - single $ If (bool True) [call "a" [] []] [call "b" [] []]) +ifSpec = it "parses" $ + "if (true) { a(); } else { b(); }" --> + single ( If (bool True) [call "a" [] []] [call "b" [] []]) assignmentSpec :: Spec assignmentSpec = do it "parses correctly" $ - "y = -5;" `parsesAs` (single $ Name "y" := (num (-5))) + "y = -5;" --> single ( Name "y" := num (-5)) it "handles pattern matching" $ - "[x, y] = [1, 2];" `parsesAs` - (single $ ListP [Name "x", Name "y"] := (ListE [num 1, num 2])) + "[x, y] = [1, 2];" --> single (ListP [Name "x", Name "y"] := ListE [num 1, num 2]) it "handles function definitions" $ - "foo (x, y) = x * y;" `parsesAs` single fooFunction + "foo (x, y) = x * y;" --> single fooFunction it "handles the function keyword" $ - "function foo(x, y) = x * y;" `parsesAs` single fooFunction + "function foo(x, y) = x * y;" --> single fooFunction it "nested indexing" $ - "x = [y[0] - z * 2];" `parsesAs` - (single $ Name "x" := ListE [app' "-" [app' "index" [Var "y", num 0], + "x = [y[0] - z * 2];" --> + single ( Name "x" := ListE [app' "-" [app' "index" [Var "y", num 0], app "*" [Var "z", num 2]]]) where - fooFunction = Name "foo" := (LamE [Name "x", Name "y"] - (app "*" [Var "x", Var "y"])) + fooFunction = Name "foo" := LamE [Name "x", Name "y"] + (app "*" [Var "x", Var "y"]) emptyFileIssue :: Expectation -> Expectation emptyFileIssue _ = pendingWith "parser should probably allow empty files" statementSpec :: Spec statementSpec = do - describe "empty file" $ do + describe "assignment" $ assignmentSpec + describe "if" $ ifSpec + describe "empty file" $ it "returns an empty list" $ - emptyFileIssue $ "" `parsesAs` [] - - describe "line comment" $ do - it "parses as empty" $ emptyFileIssue $ "// foish bar\n" `parsesAs` [] - - describe "module call" $ do - it "parses" $ - "foo();" `parsesAs` (single $ ModuleCall "foo" [] []) - describe "difference of two cylinders" $ do + emptyFileIssue $ "" --> [] + describe "line comment" $ + it "parses as empty" $ emptyFileIssue $ "// foish bar\n" --> [] + describe "module call" $ + it "parses" $ "foo();" --> single (ModuleCall "foo" [] []) + describe "difference of two cylinders" $ it "parses correctly" $ "difference(){ cylinder(r=5,h=20); cylinder(r=2,h=20); }" - `parsesAs` single ( + --> single ( ModuleCall "difference" [] [ - (call "cylinder" [(Just "r", num 5.0), + call "cylinder" [(Just "r", num 5.0), (Just "h", num 20.0)] - []), - (call "cylinder" [(Just "r", num 2.0), + [], + call "cylinder" [(Just "r", num 2.0), (Just "h", num 20.0)] - [])]) - - describe "empty module definition" $ do + []]) + describe "empty module definition" $ it "parses correctly" $ - "module foo_bar() {}" `parsesAs` (single $ NewModule "foo_bar" [] []) + "module foo_bar() {}" --> single (NewModule "foo_bar" [] []) - describe "assignment" assignmentSpec - describe "if" ifSpec From 319c60ff82c7cf654a6726d9d64022b6904dea92 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 17 Sep 2017 00:26:31 +0100 Subject: [PATCH 041/227] spacing change. --- tests/ParserSpec/Util.hs | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/tests/ParserSpec/Util.hs b/tests/ParserSpec/Util.hs index fe98dfc..2180d1e 100644 --- a/tests/ParserSpec/Util.hs +++ b/tests/ParserSpec/Util.hs @@ -1,3 +1,12 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright 2014 2015 2016, Julia Longtin (julial@turinglace.com) +-- Copyright 2015 2016, Mike MacHenry (mike.machenry@gmail.com) +-- Released under the GNU AGPLV3+, see LICENSE + +-- Allow us to use explicit foralls when writing function type declarations. +{-# LANGUAGE ExplicitForAll #-} + +-- Utilities module ParserSpec.Util ( num , bool @@ -8,18 +17,25 @@ module ParserSpec.Util , parseExpr ) where -import Graphics.Implicit.Definitions -import Graphics.Implicit.ExtOpenScad -import Graphics.Implicit.ExtOpenScad.Definitions -import Graphics.Implicit.ExtOpenScad.Parser.Expr -import Text.Parsec.String -import Text.Parsec.Error -import Text.ParserCombinators.Parsec hiding (State) -import Control.Applicative ((<$>), (<*>), (<*), (*>)) +-- be explicit about where we get things from. +import Prelude (Bool, String, Either, (<), ($), (.), otherwise) + +-- The datatype of positions in our world. +import Graphics.Implicit.Definitions (ℝ) + +-- The datatype of expressions, symbols, and values in the OpenScad language. +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE), Symbol, OVal(ONum, OBool)) + +-- the entry point of the expression parser. +import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) + +import Text.ParserCombinators.Parsec (Parser, ParseError, parse, manyTill, anyChar, eof) + +import Control.Applicative ((<$>), (<*>), (<*)) num :: ℝ -> Expr num x - -- note that the parser should handle negative number literals + -- FIXME: the parser should handle negative number literals -- directly, we abstract that deficiency away here | x < 0 = app' "negate" [LitE $ ONum (-x)] | otherwise = LitE $ ONum x @@ -36,7 +52,9 @@ app' name args = Var name :$ args parseWithLeftOver :: Parser a -> String -> Either ParseError (a, String) parseWithLeftOver p = parse ((,) <$> p <*> leftOver) "" - where leftOver = manyTill anyToken eof + where + leftOver :: Parser String + leftOver = manyTill anyChar eof parseWithEof :: Parser a -> String -> String -> Either ParseError a parseWithEof p = parse (p <* eof) From 5959777dc2c0a8da99eda826d4bd3dc1a3c062aa Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 17 Sep 2017 00:27:46 +0100 Subject: [PATCH 042/227] add copyright, make imports much more explicit, and add another function type. --- tests/ParserSpec/Expr.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index b8360d3..bec9bac 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -65,7 +65,7 @@ exprSpec = do "( false )" --> bool False it "handles vectors" $ "[ 1, 2, 3 ]" --> ListE [num 1, num 2, num 3] - it "handles lists" $ + it "handles lists" $ "( 1, 2, 3 )" --> ListE [num 1, num 2, num 3] it "handles generators" $ "[ a : 1 : b + 10 ]" --> From c673d75332f63c296469bfbee57a526b2a6cd6a8 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 17 Sep 2017 01:01:11 +0100 Subject: [PATCH 043/227] bump version number --- implicit.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implicit.cabal b/implicit.cabal index cf77d5a..32e7c6f 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -1,5 +1,5 @@ name: implicit -version: 0.1.0 +version: 0.1.1 cabal-version: >= 1.8 synopsis: Math-inspired programmatic 2&3D CAD: CSG, bevels, and shells; gcode export.. description: A math-inspired programmatic CAD library in haskell. From 8181fb2189b7ee276dced8ed04ccc12088d1eeb4 Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Thu, 12 Oct 2017 12:11:17 +0200 Subject: [PATCH 044/227] Update stack LTS Use stack LTS 9.8 for GHC 8.0.2 --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 013de2f..230dc05 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,7 +1,7 @@ # For more information, see: https://github.com/commercialhaskell/stack/blob/master/doc/yaml_configuration.md # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) -resolver: lts-3.12 +resolver: lts-9.8 # Local packages, usually specified by relative directory name packages: From 2a1929caaeb14f48ebab357172a857c5ead38bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Gillig?= Date: Tue, 13 Mar 2018 21:08:07 +0000 Subject: [PATCH 045/227] Remove 'download' from dependencies. This fixes a setup issue when using `cabal install` on Windows where it can't find a C compiler for `old-time` (via `feed` via `download`). --- implicit.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/implicit.cabal b/implicit.cabal index 5269e8d..aede0b7 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -20,7 +20,6 @@ library base >= 3 && < 5, filepath, directory, - download, parsec, unordered-containers, parallel, From 4ebeeb1e338ad9d79335fc2839e43487081c3991 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:20:20 +0100 Subject: [PATCH 046/227] add polytri and fast int types. --- Graphics/Implicit/Definitions.hs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 0c6dc20..a4eb767 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -1,5 +1,5 @@ -- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) --- Copyright 2014 2015 2016, Julia Longtin (julial@turinglace.com) +-- Copyright 2014 2015 2016, 2017, 2018, Julia Longtin (julial@turinglace.com) -- Copyright 2015 2016, Mike MacHenry (mike.machenry@gmail.com) -- Released under the GNU AGPLV3+, see LICENSE @@ -17,10 +17,12 @@ module Graphics.Implicit.Definitions ( ℝ3, minℝ, ℕ, + Fastℕ, (⋅), (⋯*), (⋯/), Polyline, + Polytri, Triangle, NormedTriangle, TriangleMesh, @@ -72,7 +74,7 @@ module Graphics.Implicit.Definitions ( ) where -import Prelude (Show, Double, Integer, Maybe, Either, show, (*), (/)) +import Prelude (Show, Double, Integer, Int, Maybe, Either, show, (*), (/)) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) @@ -91,13 +93,16 @@ minℝ :: ℝ -- for Doubles. minℝ = 0.0000000000000002 +-- Arbitrary precision integers. type ℕ = Integer +-- System integers. +type Fastℕ = Int + -- TODO: Find a better place for this (⋅) :: InnerSpace a => a -> a -> Scalar a (⋅) = (<.>) - -- add aditional instances to Show, for when we dump the intermediate form of an object. instance Show (ℝ -> ℝ) where show _ = "" @@ -132,17 +137,20 @@ instance ComponentWiseMultable ℝ3 where -- eg. [(0,0), (0.5,1), (1,0)] ---> /\ type Polyline = [ℝ2] --- | A triangle (a,b,c) = a triangle with vertices a, b and c +-- | A triangle in 2D space (a,b,c). +type Polytri = (ℝ2, ℝ2, ℝ2) + +-- | A triangle in 3D space (a,b,c) = a triangle with vertices a, b and c type Triangle = (ℝ3, ℝ3, ℝ3) -- | A triangle ((v1,n1),(v2,n2),(v3,n3)) has vertices v1, v2, v3 -- with corresponding normals n1, n2, and n3 type NormedTriangle = ((ℝ3, ℝ3), (ℝ3, ℝ3), (ℝ3, ℝ3)) --- | A triangle mesh is a bunch of triangles :) +-- | A triangle mesh is a bunch of triangles, attempting to be a surface. type TriangleMesh = [Triangle] --- | A normed triangle mesh is a bunch of normed trianlges!! +-- | A normed triangle mesh is a mesh of normed trianlges. type NormedTriangleMesh = [NormedTriangle] -- | A 2D object @@ -237,6 +245,6 @@ data SymbolicObj3 = -- | Rectilinear 2D set type Rectilinear2 = [Box2] --- | Rectilinear 2D set +-- | Rectilinear 3D set type Rectilinear3 = [Box3] From 88858bb3f941f909420a1add76b163b24244ffb6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:23:46 +0100 Subject: [PATCH 047/227] remove warning, and use mesh types better. --- Graphics/Implicit/Export.hs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Graphics/Implicit/Export.hs b/Graphics/Implicit/Export.hs index 9e84ae9..a2cf92b 100644 --- a/Graphics/Implicit/Export.hs +++ b/Graphics/Implicit/Export.hs @@ -14,7 +14,7 @@ module Graphics.Implicit.Export (writeObject, formatObject, writeSVG, writeSTL, import Prelude (FilePath, IO, (.), ($)) -- The types of our objects (before rendering), and the type of the resolution to render with. -import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3, ℝ, Polyline, TriangleMesh, Triangle, NormedTriangle) +import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3, ℝ, Polyline, TriangleMesh, NormedTriangleMesh) -- functions for outputing a file, and one of the types. import Data.Text.Lazy (Text) @@ -28,7 +28,7 @@ import Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAp import qualified Graphics.Implicit.Export.PolylineFormats as PolylineFormats (svg, hacklabLaserGCode) import qualified Graphics.Implicit.Export.TriangleMeshFormats as TriangleMeshFormats (stl, binaryStl, jsTHREE) import qualified Graphics.Implicit.Export.NormedTriangleMeshFormats as NormedTriangleMeshFormats (obj) -import qualified Graphics.Implicit.Export.SymbolicFormats as SymbolicFormats (scad3, scad2) +import qualified Graphics.Implicit.Export.SymbolicFormats as SymbolicFormats (scad2, scad3) import qualified Codec.Picture as ImageFormatCodecs (DynamicImage, savePngImage) -- Write an object using the given format function. @@ -51,10 +51,7 @@ writeObject' :: (DiscreteAproxable obj aprox) -> obj -- ^ Object to render -> IO () -- ^ Writing Action! writeObject' res formatWriter filename obj = - let --- aprox :: (DiscreteAproxable aprox) => aprox - aprox = discreteAprox res obj - in formatWriter filename aprox + formatWriter filename (discreteAprox res obj) formatObject :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution @@ -66,13 +63,13 @@ formatObject res format = format . discreteAprox res writeSVG :: forall obj. DiscreteAproxable obj [Polyline] => ℝ -> FilePath -> obj -> IO () writeSVG res = writeObject res PolylineFormats.svg -writeSTL :: forall obj. DiscreteAproxable obj [Triangle] => ℝ -> FilePath -> obj -> IO () +writeSTL :: forall obj. DiscreteAproxable obj TriangleMesh => ℝ -> FilePath -> obj -> IO () writeSTL res = writeObject res TriangleMeshFormats.stl -writeBinSTL :: forall obj. DiscreteAproxable obj [Triangle] => ℝ -> FilePath -> obj -> IO () +writeBinSTL :: forall obj. DiscreteAproxable obj TriangleMesh => ℝ -> FilePath -> obj -> IO () writeBinSTL res file obj = LBS.writeFile file $ TriangleMeshFormats.binaryStl $ discreteAprox res obj -writeOBJ :: forall obj. DiscreteAproxable obj [NormedTriangle] => ℝ -> FilePath -> obj -> IO () +writeOBJ :: forall obj. DiscreteAproxable obj NormedTriangleMesh => ℝ -> FilePath -> obj -> IO () writeOBJ res = writeObject res NormedTriangleMeshFormats.obj writeTHREEJS :: forall obj. DiscreteAproxable obj TriangleMesh => ℝ -> FilePath -> obj -> IO () From 66674da64a2758e5938b1504aeb7e99ef763af6a Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:24:58 +0100 Subject: [PATCH 048/227] be explicit about what we export, and use fastint type. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index 3fe7ffe..eb6b26f 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -8,11 +8,11 @@ -- FIXME: why is this here? {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} -module Graphics.Implicit.Export.DiscreteAproxable where +module Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAprox) where -import Prelude(Int, (-), (/), ($), (<), map, round, (+), maximum, abs, (*), fromIntegral, max, realToFrac) +import Prelude((-), (/), ($), (<), map, round, (+), maximum, abs, (*), fromIntegral, max, realToFrac) -import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj3, SymbolicObj2, Polyline, TriangleMesh, NormedTriangleMesh) +import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, SymbolicObj2, SymbolicObj3, Polyline, TriangleMesh, NormedTriangleMesh) import Graphics.Implicit.ObjectUtil (getImplicit3, getImplicit2, getBox3, getBox2) @@ -55,7 +55,7 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where camera = Camera (x1-deviation*(2.2::ℝ), avY, avZ) (0, -1, 0) (0,0, -1) 1.0 lights = [Light (x1-deviation*(1.5::ℝ), y1 - (0.4::ℝ)*(y2-y1), avZ) ((0.03::ℝ)*deviation) ] scene = Scene obj (PixelRGBA8 200 200 230 255) lights (PixelRGBA8 255 255 255 0) - pixelRenderer :: Int -> Int -> Color + pixelRenderer :: Fastℕ -> Fastℕ -> Color pixelRenderer a b = renderScreen ((fromIntegral a :: ℝ)/w - (0.5::ℝ)) ((fromIntegral b :: ℝ)/h - (0.5 ::ℝ)) renderScreen :: ℝ -> ℝ -> Color @@ -86,7 +86,7 @@ instance DiscreteAproxable SymbolicObj2 DynamicImage where (p1@(x1,_), p2@(_,y2)) = getBox2 symbObj (dx, dy) = p2 ^-^ p1 dxy = max dx dy - pixelRenderer :: Int -> Int -> Color + pixelRenderer :: Fastℕ -> Fastℕ -> Color pixelRenderer mya myb = mycolor where xy a b = ((x1,y2) .-^ (dxy-dx, dy-dxy)^/2) .+^ dxy*^(a/w, -b/h) From e917e4a05842dec29325824a64259095a3bbf45c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:27:06 +0100 Subject: [PATCH 049/227] parallelize, and use integers more, instead of floats. --- Graphics/Implicit/Export/MarchingSquares.hs | 62 +++++++++++---------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index 3b91428..e93b92a 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -5,49 +5,53 @@ -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} +-- export getContour, which returns as array of polylines describing the edge of a 2D object. module Graphics.Implicit.Export.MarchingSquares (getContour) where -import Prelude(Int, Bool(True, False), ceiling, fromIntegral, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), (.), splitAt, div, unzip, length, (++), (<), (++), head, concat, not, null, (||), Eq, Int) +import Prelude(Bool(True, False), ceiling, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), splitAt, div, unzip, length, (++), (<), (++), head, ceiling, concat, div, max, not, null, (||), Eq, fromIntegral) -import Graphics.Implicit.Export.Render.HandlePolylines (reducePolyline) - -import Graphics.Implicit.Definitions (ℝ, ℝ2, Polyline, Obj2, (⋯/), (⋯*)) - --- FIXME: commented out for now, parallelism is not properly implemented. --- import Control.Parallel.Strategies (using, parList, rdeepseq) +import Graphics.Implicit.Definitions (ℕ, ℝ2, Polyline, Obj2, (⋯/), (⋯*)) import Data.VectorSpace ((^-^), (^+^)) import Control.Arrow((***)) --- we are explicit here, so GHC knows what types n is made up of in getContour. -both :: (ℝ -> Int) -> ℝ2 -> (Int, Int) +-- import a helper, to clean up the result we return. +import Graphics.Implicit.Export.Render.HandlePolylines (reducePolyline) + +-- Each step on the Y axis is done in parallel using Control.Parallel.Strategies +import Control.Parallel.Strategies (using, rdeepseq, parBuffer) + +-- apply a function to both items in the provided tuple. +both :: forall t b. (t -> b) -> (t, t) -> (b, b) both f (x,y) = (f x, f y) -- getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polyline] -getContour p1 p2 d obj = +getContour p1 p2 res obj = let + -- How much space are we rendering? + d = p2 ^-^ p1 + -- How many steps will we take on each axis? - n :: (Int, Int) - n@(nx, ny) = ceiling `both` ((p2 ^-^ p1) ⋯/ d) - -- Divide it up and compute the polylines - gridPos :: (Int,Int) -> (Int,Int) -> ℝ2 - gridPos (nx',ny') (mx,my) = - let - p :: ℝ2 - p = ( fromIntegral mx / fromIntegral nx' - , fromIntegral my / fromIntegral ny') - in - p1 ^+^ (p2 ^-^ p1) ⋯* p + nx :: ℕ + ny :: ℕ + n@(nx,ny) = (ceiling) `both` (d ⋯/ res) + + -- a helper for calculating a position inside of the space. + gridPos :: (ℕ,ℕ) -> (ℕ,ℕ) -> ℝ2 + gridPos n' m = p1 ^+^ d ⋯* ((fromIntegral `both` m) ⋯/ (fromIntegral `both` n')) + + -- compute the polylines linesOnGrid :: [[[Polyline]]] linesOnGrid = [[getSquareLineSegs (gridPos n (mx,my)) (gridPos n (mx+1,my+1)) obj - | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] - in + | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq + -- Cleanup, cleanup, everybody cleanup! -- (We connect multilines, delete redundant vertices on them, etc) - filter polylineNotNull . map reducePolyline $ orderLinesDC linesOnGrid - + lines = filter polylineNotNull $ map reducePolyline $ orderLinesDC linesOnGrid + in + lines -- FIXME: Commented out, not used? {- -- alternate Grid mapping funcs @@ -85,6 +89,9 @@ getSquareLineSegs (x1, y1) (x2, y2) obj = -- And the center point.. c = obj ((x1+x2)/2, (y1+y2)/2) + dx = x2 - x1 + dy = y2 - y1 + -- linearly interpolated midpoints on the relevant axis -- midy2 -- _________*__________ @@ -99,9 +106,6 @@ getSquareLineSegs (x1, y1) (x2, y2) obj = -- midy1 - dx = x2 - x1 - dy = y2 - y1 - midx1 = (x, y + dy*x1y1/(x1y1-x1y2)) midx2 = (x + dx, y + dy*x2y1/(x2y1-x2y2)) midy1 = (x + dx*x1y1/(x1y1-x2y1), y ) @@ -178,7 +182,7 @@ orderLinesDC segs = let halve :: [a] -> ([a], [a]) halve l = splitAt (div (length l) 2) l - splitOrder segs' = case (halve *** halve) . unzip . map halve $ segs' of + splitOrder segs' = case (halve *** halve) $ unzip $ map halve $ segs' of ((a,b),(c,d)) -> orderLinesDC a ++ orderLinesDC b ++ orderLinesDC c ++ orderLinesDC d in if length segs < 5 || length (head segs) < 5 then concat $ concat segs else From 4c8843c36b6c48059eafad5130ec51725419ad72 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:27:55 +0100 Subject: [PATCH 050/227] parallelize, and use integers more, instead of floats. --- .../Implicit/Export/MarchingSquaresFill.hs | 74 +++++++++++-------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/Graphics/Implicit/Export/MarchingSquaresFill.hs b/Graphics/Implicit/Export/MarchingSquaresFill.hs index f7e548f..becb76e 100644 --- a/Graphics/Implicit/Export/MarchingSquaresFill.hs +++ b/Graphics/Implicit/Export/MarchingSquaresFill.hs @@ -5,31 +5,41 @@ -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} --- define getContour, which gets a polyline describe the edge of your 2D object. +-- export getContourMesh, which returns an array of triangles describing the interior of a 2D object. module Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) where -import Prelude(Bool(True, False), fromInteger, ($), (-), (+), (/), (*), (<=), (>), ceiling, concat) +import Prelude(Bool(True, False), fromIntegral, ($), (-), (+), (/), (*), (<=), (>), ceiling, concat, max, div) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2) +import Graphics.Implicit.Definitions (ℕ, ℝ2, Polytri, Obj2, (⋯/), (⋯*)) --- FIXME: commented out, test how to apply.. --- import Control.Parallel (par, pseq) +import Data.VectorSpace ((^-^),(^+^)) -getContourMesh :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [(ℝ2,ℝ2,ℝ2)] -getContourMesh (x1, y1) (x2, y2) (dx, dy) obj = +-- Each step on the Y axis is done in parallel using Control.Parallel.Strategies +import Control.Parallel.Strategies (using, rdeepseq, parBuffer) + +-- apply a function to both items in the provided tuple. +both :: forall t b. (t -> b) -> (t, t) -> (b, b) +both f (x,y) = (f x, f y) + +getContourMesh :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polytri] +getContourMesh p1 p2 res obj = let + -- How much space are we rendering? + d = p2 ^-^ p1 + -- How many steps will we take on each axis? - nx :: ℝ - nx = fromInteger $ ceiling $ (x2 - x1) / dx - ny :: ℝ - ny = fromInteger $ ceiling $ (y2 - y1) / dy - -- Divide it up and compute the polylines - trisOnGrid :: [[[(ℝ2,ℝ2,ℝ2)]]] - trisOnGrid = [[getSquareTriangles - (x1 + (x2 - x1)*mx/nx, y1 + (y2 - y1)*my/ny) - (x1 + (x2 - x1)*(mx+1)/nx, y1 + (y2 - y1)*(my+1)/ny) - obj - | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] + nx :: ℕ + ny :: ℕ + n@(nx,ny) = (ceiling) `both` (d ⋯/ res) + + -- a helper for calculating a position inside of the space. + gridPos :: (ℕ,ℕ) -> (ℕ,ℕ) -> ℝ2 + gridPos n' m = p1 ^+^ d ⋯* ((fromIntegral `both` m) ⋯/ (fromIntegral `both` n')) + + -- compute the triangles. + trisOnGrid :: [[[Polytri]]] + trisOnGrid = [[getSquareTriangles (gridPos n (mx,my)) (gridPos n (mx+1,my+1)) obj + | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq triangles = concat $ concat trisOnGrid in triangles @@ -39,16 +49,18 @@ getContourMesh (x1, y1) (x2, y2) (dx, dy) obj = -- values at its vertices. -- It is based on the linearly-interpolated marching squares algorithm. -getSquareTriangles :: ℝ2 -> ℝ2 -> Obj2 -> [(ℝ2,ℝ2,ℝ2)] +getSquareTriangles :: ℝ2 -> ℝ2 -> Obj2 -> [Polytri] getSquareTriangles (x1, y1) (x2, y2) obj = let (x,y) = (x1, y1) - -- Let's evlauate obj at a few points... + -- Let's evaluate obj at four corners... x1y1 = obj (x1, y1) x2y1 = obj (x2, y1) x1y2 = obj (x1, y2) x2y2 = obj (x2, y2) + + -- And the center point.. c = obj ((x1+x2)/2, (y1+y2)/2) dx = x2 - x1 @@ -56,16 +68,16 @@ getSquareTriangles (x1, y1) (x2, y2) obj = -- linearly interpolated midpoints on the relevant axis -- midy2 - -- _________*__________ - -- | | - -- | | - -- | | - --midx1* * midx2 - -- | | - -- | | - -- | | - -- -----------*---------- - -- midy1 + -- _________*_________ + -- | | + -- | | + -- | | + --midx1* * midx2 + -- | | + -- | | + -- | | + -- ---------*--------- + -- midy1 midx1 = (x, y + dy*x1y1/(x1y1-x1y2)) midx2 = (x + dx, y + dy*x2y1/(x2y1-x2y2)) @@ -114,7 +126,7 @@ getSquareTriangles (x1, y1) (x2, y2) obj = False, False) -> [(midx2, (x2,y2), midy2)] (True, False, False, True) -> if c > 0 - then [((x1,y2), midx1, midy2), ((x2,y1), midy1, midx2)] + then [((x1,y2), midx1, midy2), ((x2,y1), midy1, midx2)] --[[midx1, midy2], [midx2, midy1]] else [] --[[midx1, midy1], [midx2, midy2]] (False, True, True, False) -> if c <= 0 From ce80a1b5e32be54a73cfc20422c3098d0b8ac50f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:28:50 +0100 Subject: [PATCH 051/227] be explicit about exports. --- Graphics/Implicit/Export/PolylineFormats.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index 71233a1..d838f04 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -7,7 +7,7 @@ {-# LANGUAGE OverloadedStrings #-} -module Graphics.Implicit.Export.PolylineFormats where +module Graphics.Implicit.Export.PolylineFormats (svg, hacklabLaserGCode) where import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max) From 1caabb93e6b5a67efe9e72f6bc5ee9b989c7c145 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:31:00 +0100 Subject: [PATCH 052/227] be explicit about what we export, and use new fastint type. --- Graphics/Implicit/Export/RayTrace.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index b25681e..ee9b761 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -8,11 +8,11 @@ -- FIXME: why are these needed? {-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, FlexibleContexts #-} -module Graphics.Implicit.Export.RayTrace where +module Graphics.Implicit.Export.RayTrace( dynamicImage, Color, average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) where -import Prelude(Show, RealFrac, Maybe(Just, Nothing), Int, Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, fromIntegral, toRational, otherwise) +import Prelude(Show, RealFrac, Maybe(Just, Nothing), Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, fromIntegral, toRational, otherwise) -import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, (⋅), Obj3) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋅), Obj3) import Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) import Control.Monad (guard, return) import Control.Arrow ((***)) @@ -118,7 +118,7 @@ refine (a, b) obj = then refine' 10 (a, b) (aval, bval) obj else refine' 10 (b, a) (aval, bval) obj -refine' :: Int -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ +refine' :: ℕ -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ refine' 0 (a, _) _ _ = a refine' n (a, b) (aval, bval) obj = let From 4a2a895c7fce5f483300e930bfce35070ad0083d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:34:54 +0100 Subject: [PATCH 053/227] parallize better, shorten forms of space searching, and use fromIntegral in place of fromInteger. --- Graphics/Implicit/Export/Render.hs | 109 ++++++++++++++++------------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index cedd529..be0cba8 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -8,11 +8,12 @@ -- Allow us to use the tearser parallel list comprehension syntax, to avoid having to call zip in the complicated comprehensions below. {-# LANGUAGE ParallelListComp #-} -module Graphics.Implicit.Export.Render where +-- export getContour and getMesh, which returns the edge of a 2D object, or the surface of a 3D object, respectively. +module Graphics.Implicit.Export.Render (getMesh, getContour) where -import Prelude(Float, Bool, ceiling, ($), (/), fromIntegral, (+), (*), fromInteger, max, div, tail, map, concat, realToFrac, (==), (||), filter, not, reverse, (.), Integral, Eq, Integer, concatMap) +import Prelude(Float, Bool, ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, realToFrac, (==), (||), filter, not, reverse, (.), Eq, concatMap) -import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Obj2, Obj3, TriangleMesh, Triangle, Polyline) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/)) import Data.VectorSpace ((^-^)) @@ -41,7 +42,7 @@ import Graphics.Implicit.Export.Render.HandleSquares (mergedSquareTris) -- Success: This is our mesh. --- Each step is done in parallel using Control.Parallel.Strategies +-- Each step on the Z axis is done in parallel using Control.Parallel.Strategies import Control.Parallel.Strategies (using, rdeepseq, parBuffer) import Control.DeepSeq (NFData) @@ -66,38 +67,39 @@ import Control.DeepSeq (NFData) -- For the 2D case, we need one last thing, cleanLoopsFromSegs: import Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs) +-- apply a function to all three items in the provided tuple. +allthree :: forall t b. (t -> b) -> (t, t, t) -> (b, b, b) +allthree f (x,y,z) = (f x, f y, f z) + +-- FIXME: res should be ℝ3, not ℝ. getMesh :: ℝ3 -> ℝ3 -> ℝ -> Obj3 -> TriangleMesh getMesh p1@(x1,y1,z1) p2 res obj = let -- How much space are we rendering? - (dx,dy,dz) = p2 ^-^ p1 + d = p2 ^-^ p1 -- How many steps will we take on each axis? - nx :: Integral a => a - nx = ceiling $ dx / res - ny :: Integral a => a - ny = ceiling $ dy / res - nz :: Integral a => a - nz = ceiling $ dz / res + nx :: ℕ + ny :: ℕ + nz :: ℕ + (nx,ny,nz) = ceiling `allthree` ( d ⋯/ (res,res,res)) -- How big are the steps? - rx = dx / fromInteger nx - ry = dy / fromInteger ny - rz = dz / fromInteger nz + (rx,ry,rz) = d ⋯/ (fromIntegral `allthree` (nx,ny,nz)) -- The positions we're rendering. - pXs = [ x1 + rx*n | n <- [0.. fromInteger nx] ] - pYs = [ y1 + ry*n | n <- [0.. fromInteger ny] ] - pZs = [ z1 + rz*n | n <- [0.. fromInteger nz] ] + pXs = [ x1 + rx*n | n <- [0.. fromIntegral nx] ] + pYs = [ y1 + ry*n | n <- [0.. fromIntegral ny] ] + pZs = [ z1 + rz*n | n <- [0.. fromIntegral nz] ] - par3DList :: forall t. NFData t => Integer -> Integer -> Integer -> ((Integer -> ℝ) -> Integer -> (Integer -> ℝ) -> Integer -> (Integer -> ℝ) -> Integer -> t) -> [[[t]]] + par3DList :: forall t. NFData t => ℕ -> ℕ -> ℕ -> ((ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> t) -> [[[t]]] par3DList lenx leny lenz f = [[[f - (\n -> x1 + rx*fromInteger (mx+n)) mx - (\n -> y1 + ry*fromInteger (my+n)) my - (\n -> z1 + rz*fromInteger (mz+n)) mz + (\n -> x1 + rx*fromIntegral (mx+n)) mx + (\n -> y1 + ry*fromIntegral (my+n)) my + (\n -> z1 + rz*fromIntegral (mz+n)) mz | mx <- [0..lenx] ] | my <- [0..leny] ] | mz <- [0..lenz] ] - `using` parBuffer (max 1 . fromInteger $ div lenz 32) rdeepseq + `using` parBuffer (max 1 . fromIntegral $ div lenz 32) rdeepseq -- Evaluate obj to avoid waste in mids, segs, later. objV = par3DList (nx+2) (ny+2) (nz+2) $ \x _ y _ z _ -> obj (x 0, y 0, z 0) @@ -108,21 +110,21 @@ getMesh p1@(x1,y1,z1) p2 res obj = | x0 <- pXs | objX0Y0Z0 <- objY0Z0 | objX0Y0Z1 <- objY0Z1 ]| y0 <- pYs | objY0Z0 <- objZ0 | objY0Z1 <- objZ1 ]| z0 <- pZs | z1' <- tail pZs | objZ0 <- objV | objZ1 <- tail objV - ] `using` parBuffer (max 1 . fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 . fromIntegral $ div nz 32) rdeepseq midsY = [[[ interpolate (y0, objX0Y0Z0) (y1', objX0Y1Z0) (appAC obj x0 z0) res | x0 <- pXs | objX0Y0Z0 <- objY0Z0 | objX0Y1Z0 <- objY1Z0 ]| y0 <- pYs | y1' <- tail pYs | objY0Z0 <- objZ0 | objY1Z0 <- tail objZ0 ]| z0 <- pZs | objZ0 <- objV - ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq midsX = [[[ interpolate (x0, objX0Y0Z0) (x1', objX1Y0Z0) (appBC obj y0 z0) res | x0 <- pXs | x1' <- tail pXs | objX0Y0Z0 <- objY0Z0 | objX1Y0Z0 <- tail objY0Z0 ]| y0 <- pYs | objY0Z0 <- objZ0 ]| z0 <- pZs | objZ0 <- objV - ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 $ fromIntegral $ div nx 32) rdeepseq -- Calculate segments for each side segsZ = [[[ @@ -135,7 +137,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <- objZ0 | objY1Z0 <- tail objZ0 ]|z0<-pZs |mX' <-midsX| mY' <-midsY |objZ0 <- objV - ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 $ fromIntegral $ div nz 32) rdeepseq segsY = [[[ map2 (inj2 y0) $ getSegs (x0,z0) (x1',z1') (obj *$* y0) @@ -147,7 +149,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <- objZ0 | objY0Z1 <- objZ1 ]|z0<-pZs|z1'<-tail pZs|mB' <-midsX|mBT <-tail midsX|mA' <-midsZ |objZ0 <- objV | objZ1 <- tail objV - ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq segsX = [[[ map2 (inj1 x0) $ getSegs (y0,z0) (y1',z1') (obj $** x0) @@ -159,7 +161,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <-objZ0 |objY1Z0 <-tail objZ0 |objY0Z1 <-objZ1 |objY1Z1 <-tail objZ1 ]|z0<-pZs|z1'<-tail pZs|mB' <-midsY|mBT <-tail midsY|mA' <-midsZ |objZ0 <- objV | objZ1 <- tail objV - ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 $ fromIntegral $ div nx 32) rdeepseq -- (3) & (4) : get and tesselate loops sqTris = [[[ @@ -183,9 +185,11 @@ getMesh p1@(x1,y1,z1) p2 res obj = ]| segZ' <- segsZ | segZT <- tail segsZ | segY' <- segsY | segX' <- segsX - ] `using` parBuffer (max 1 $ fromInteger $ div nz 32) rdeepseq + ] `using` parBuffer (max 1 $ fromIntegral $ div nz 32) rdeepseq - in cleanupTris . mergedSquareTris . concat . concat $ concat sqTris -- (5) merge squares, etc + in + -- (5) merge squares, etc + cleanupTris . mergedSquareTris . concat . concat $ concat sqTris -- Removes triangles that are empty, when converting their positions to Float resolution. -- NOTE: this will need to be disabled for AMF, and other triangle formats that can handle Double. @@ -202,30 +206,36 @@ cleanupTris tris = isDegenerateTri (a, b, c) = isDegenerateTriFloat (floatPoint a, floatPoint b, floatPoint c) in filter (not . isDegenerateTri) tris +-- apply a function to both items in the provided tuple. +both :: forall t b. (t -> b) -> (t, t) -> (b, b) +both f (x,y) = (f x, f y) + +-- getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ -> Obj2 -> [Polyline] getContour p1@(x1, y1) p2 res obj = let - (dx,dy) = p2 ^-^ p1 + -- the size of the region we're being asked to search. + d = p2 ^-^ p1 -- How many steps will we take on each axis? - nx :: Integral a => a - nx = ceiling $ dx / res - ny :: Integral a => a - ny = ceiling $ dy / res + nx :: ℕ + ny :: ℕ + (nx,ny) = (ceiling) `both` (d ⋯/ (res,res)) - rx = dx/fromInteger nx - ry = dy/fromInteger ny + -- How big are the steps? + (rx,ry) = d ⋯/ (fromIntegral `both` (nx,ny)) - pYs = [ y1 + ry*n | n <- [0.. fromInteger ny] ] - pXs = [ x1 + rx*n | n <- [0.. fromInteger nx] ] + -- the points inside of the region. + pYs = [ y1 + ry*(fromIntegral p) | p <- [0.. ny] ] + pXs = [ x1 + rx*(fromIntegral p) | p <- [0.. nx] ] - par2DList :: forall t. NFData t => Integer -> Integer -> ((Integer -> ℝ) -> Integer -> (Integer -> ℝ) -> Integer -> t) -> [[t]] + par2DList :: forall t. NFData t => ℕ -> ℕ -> ((ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> t) -> [[t]] par2DList lenx leny f = [[ f (\n -> x1 + rx*fromIntegral (mx+n)) mx (\n -> y1 + ry*fromIntegral (my+n)) my | mx <- [0..lenx] ] | my <- [0..leny] ] - `using` parBuffer (max 1 . fromInteger $ div leny 32) rdeepseq + `using` parBuffer (max 1 . fromIntegral $ div leny 32) rdeepseq -- Evaluate obj to avoid waste in mids, segs, later. @@ -238,13 +248,13 @@ getContour p1@(x1, y1) p2 res obj = interpolate (y0, objX0Y0) (y1', objX0Y1) (obj $* x0) res | x0 <- pXs | objX0Y0 <- objY0 | objX0Y1 <- objY1 ]| y0 <- pYs | y1' <- tail pYs | objY0 <- objV | objY1 <- tail objV - ] `using` parBuffer (max 1 . fromInteger $ div ny 32) rdeepseq + ] `using` parBuffer (max 1 . fromIntegral $ div ny 32) rdeepseq midsX = [[ interpolate (x0, objX0Y0) (x1', objX1Y0) (obj *$ y0) res | x0 <- pXs | x1' <- tail pXs | objX0Y0 <- objY0 | objX1Y0 <- tail objY0 ]| y0 <- pYs | objY0 <- objV - ] `using` parBuffer (max 1 . fromInteger $ div ny 32) rdeepseq + ] `using` parBuffer (max 1 . fromIntegral $ div nx 32) rdeepseq -- Calculate segments for each side @@ -256,12 +266,10 @@ getContour p1@(x1, y1) p2 res obj = |objX0Y0<-objY0|objX1Y0<-tail objY0|objX0Y1<-objY1|objX1Y1<-tail objY1 ]|y0<-pYs|y1'<-tail pYs|mX'' <-midsX|mX'T <-tail midsX|mY'' <-midsY |objY0 <- objV | objY1 <- tail objV - ] `using` parBuffer (max 1 . fromInteger $ div ny 32) rdeepseq - - in cleanLoopsFromSegs . concat $ concat segs -- (5) merge squares, etc - - + ] `using` parBuffer (max 1 . fromIntegral $ div ny 32) rdeepseq + in + cleanLoopsFromSegs . concat $ concat segs -- (5) merge squares, etc -- utility functions @@ -299,8 +307,9 @@ appAC f a c b = f (a,b,c) map2 :: forall a b. (a -> b) -> [[a]] -> [[b]] map2 f = map (map f) -map2R :: forall a a1. (a1 -> a) -> [[a1]] -> [[a]] -map2R f = map (reverse . map f) +-- FIXME: not used? +--map2R :: forall a a1. (a1 -> a) -> [[a1]] -> [[a]] +--map2R f = map (reverse . map f) mapR :: forall a. [[a]] -> [[a]] mapR = map reverse From 1d09ea1fc6212b4992e73015f89e13483be3b313 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:36:29 +0100 Subject: [PATCH 054/227] use mesh type better. --- Graphics/Implicit/Export/Render/Definitions.hs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Graphics/Implicit/Export/Render/Definitions.hs b/Graphics/Implicit/Export/Render/Definitions.hs index fc55164..0f1080b 100644 --- a/Graphics/Implicit/Export/Render/Definitions.hs +++ b/Graphics/Implicit/Export/Render/Definitions.hs @@ -1,25 +1,20 @@ -- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) -- Released under the GNU AGPLV3+, see LICENSE +-- We want a type that can represent squares/quads and triangles. module Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) where import Prelude() -import Graphics.Implicit.Definitions(ℝ, ℝ2, ℝ3, Triangle) +import Graphics.Implicit.Definitions(ℝ, ℝ2, ℝ3, TriangleMesh) import Control.DeepSeq (NFData, rnf) --- We want a format that can represent squares/quads and triangles. --- So that we can merge squares and thereby reduces triangles. - --- Regarding Sq: Sq Basis@(b1,b2,b3) (Height on b3) --- (b1 pos 1, b2 pos 1) (b1 pos 2, b2 pos 2) - data TriSquare = Sq (ℝ3,ℝ3,ℝ3) ℝ ℝ2 ℝ2 - | Tris [Triangle] + | Tris TriangleMesh --- For use with Parallel.Strategies later +-- FIXME: For use with Parallel.Strategies later instance NFData TriSquare where rnf (Sq b z xS yS) = rnf (b,z,xS,yS) From 1c801d6648ea4699569d2f361fb242fdb3497074 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:36:49 +0100 Subject: [PATCH 055/227] use mesh type better. --- Graphics/Implicit/Export/Render/HandleSquares.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/Render/HandleSquares.hs b/Graphics/Implicit/Export/Render/HandleSquares.hs index 4d2f961..2eaa8ff 100644 --- a/Graphics/Implicit/Export/Render/HandleSquares.hs +++ b/Graphics/Implicit/Export/Render/HandleSquares.hs @@ -6,7 +6,7 @@ module Graphics.Implicit.Export.Render.HandleSquares (mergedSquareTris) where import Prelude(concatMap, (++)) -import Graphics.Implicit.Definitions (Triangle) +import Graphics.Implicit.Definitions (TriangleMesh) import Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) import Data.VectorSpace ((^*), (*^), (^+^)) @@ -57,7 +57,7 @@ import Data.VectorSpace ((^*), (*^), (^+^)) -} -mergedSquareTris :: [TriSquare] -> [Triangle] +mergedSquareTris :: [TriSquare] -> TriangleMesh mergedSquareTris sqTris = let -- We don't need to do any work on triangles. They'll just be part of @@ -126,7 +126,7 @@ joinYaligned [] = [] -} -- Reconstruct a triangle -squareToTri :: TriSquare -> [Triangle] +squareToTri :: TriSquare -> TriangleMesh squareToTri (Sq (b1,b2,b3) z (x1,x2) (y1,y2)) = let zV = b3 ^* z From 3fbca664c303274b0b4cc8f0180a5861c64d11d0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:37:30 +0100 Subject: [PATCH 056/227] use integer type better. --- Graphics/Implicit/Export/Render/Interpolate.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/Render/Interpolate.hs b/Graphics/Implicit/Export/Render/Interpolate.hs index 21423c8..99f974f 100644 --- a/Graphics/Implicit/Export/Render/Interpolate.hs +++ b/Graphics/Implicit/Export/Render/Interpolate.hs @@ -7,9 +7,9 @@ module Graphics.Implicit.Export.Render.Interpolate (interpolate) where -import Prelude(Integer, (*), (>), (<), (/=), (+), (-), (/), (==), (&&), abs) +import Prelude((*), (>), (<), (/=), (+), (-), (/), (==), (&&), abs) -import Graphics.Implicit.Definitions (ℝ, ℝ2) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2) -- Consider a function f(x): @@ -43,6 +43,8 @@ import Graphics.Implicit.Definitions (ℝ, ℝ2) -- If it doesn't cross zero, we don't actually care what answer we give, -- just that it's cheap. +-- FIXME: accept resolution on multiple axises. + interpolate :: ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ -> ℝ interpolate (a,aval) (_,bval) _ _ | aval*bval > 0 = a @@ -119,7 +121,7 @@ interpolate (a,aval) (b,bval) f _ = -- Try the answer linear interpolation gives us... -- (n is to cut us off if recursion goes too deep) -interpolateLin :: Integer -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ +interpolateLin :: ℕ -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ interpolateLin n (a, aval) (b, bval) obj | aval /= bval= let -- Interpolate and evaluate @@ -153,7 +155,7 @@ interpolateLin n (a, aval) (b, bval) obj | aval /= bval= interpolateLin _ (a, _) _ _ = a -- Now for binary searching! -interpolateBin :: Integer -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ +interpolateBin :: ℕ -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ -- The termination case: From 2e7cc400128f24596ca199334b071654a16a4cb9 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:37:56 +0100 Subject: [PATCH 057/227] use integer type better. --- Graphics/Implicit/Export/Render/RefineSegs.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/Render/RefineSegs.hs b/Graphics/Implicit/Export/Render/RefineSegs.hs index 9b2f25a..f5b3e62 100644 --- a/Graphics/Implicit/Export/Render/RefineSegs.hs +++ b/Graphics/Implicit/Export/Render/RefineSegs.hs @@ -2,11 +2,12 @@ -- Copyright (C) 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE +-- export one function, which refines polylines. module Graphics.Implicit.Export.Render.RefineSegs (refine) where -import Prelude(Int, (<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, tail, sqrt, (<=)) +import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, tail, sqrt, (<=)) -import Graphics.Implicit.Definitions (ℝ, ℝ2, minℝ, Obj2, (⋅)) +import Graphics.Implicit.Definitions (ℝ, ℝ2, minℝ, ℕ, Obj2, (⋅)) import Graphics.Implicit.Export.Util (centroid) import Data.VectorSpace (normalized, magnitude, (^-^), (^*), (^+^)) @@ -30,7 +31,7 @@ detail' _ _ a = a -- detail adds new points to a polyline to add more detail. -detail :: Int -> ℝ -> (ℝ2 -> ℝ) -> [ℝ2] -> [ℝ2] +detail :: ℕ -> ℝ -> (ℝ2 -> ℝ) -> [ℝ2] -> [ℝ2] detail n res obj [p1, p2] | n < 2 = let mid = centroid [p1,p2] From fe389de82bbc17835815d90e1e91c682e4552903 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:38:27 +0100 Subject: [PATCH 058/227] use integer type better. --- Graphics/Implicit/Export/Render/TesselateLoops.hs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/Render/TesselateLoops.hs b/Graphics/Implicit/Export/Render/TesselateLoops.hs index 9ed50a2..21a6f09 100644 --- a/Graphics/Implicit/Export/Render/TesselateLoops.hs +++ b/Graphics/Implicit/Export/Render/TesselateLoops.hs @@ -4,11 +4,16 @@ module Graphics.Implicit.Export.Render.TesselateLoops (tesselateLoop) where -import Prelude(Int, return, ($), length, (==), zip, init, tail, reverse, (<), (/), null, foldl1, (++), head, (*), abs, (>), (&&), (+), concatMap) -import Graphics.Implicit.Definitions (ℝ, Obj3, ℝ3, Triangle, (⋅)) +import Prelude(return, ($), length, (==), zip, init, tail, reverse, (<), (/), null, foldl1, (++), head, (*), abs, (>), (&&), (+), concatMap) + +import Graphics.Implicit.Definitions (ℝ, Fastℕ, Obj3, ℝ3, TriangleMesh, (⋅)) + import Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) + import Graphics.Implicit.Export.Util (centroid) + import Data.VectorSpace (normalized, (^-^), (^+^), magnitude, (^/), (^*)) + import Data.Cross (cross3) tesselateLoop :: ℝ -> Obj3 -> [[ℝ3]] -> [TriSquare] @@ -83,7 +88,7 @@ tesselateLoop res obj pathSides = return $ Tris $ else early_tris ++ [(a,b,mid) | (a,b) <- zip path (tail path ++ [head path]) ] -shrinkLoop :: Int -> [ℝ3] -> ℝ -> Obj3 -> ([Triangle], [ℝ3]) +shrinkLoop :: Fastℕ -> [ℝ3] -> ℝ -> Obj3 -> (TriangleMesh, [ℝ3]) shrinkLoop _ path@[a,b,c] res obj = if abs (obj $ centroid [a,b,c]) < res/50 From 25bbc731ca4650bd68ffb3107c0ad93ad5cec470 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:39:15 +0100 Subject: [PATCH 059/227] be explicit about what we export, and use polytri. --- Graphics/Implicit/Export/SymbolicObj2.hs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicObj2.hs b/Graphics/Implicit/Export/SymbolicObj2.hs index 624776b..73b49db 100644 --- a/Graphics/Implicit/Export/SymbolicObj2.hs +++ b/Graphics/Implicit/Export/SymbolicObj2.hs @@ -9,11 +9,11 @@ -- If it can't, it passes the puck to a marching-squares-like -- algorithm... -module Graphics.Implicit.Export.SymbolicObj2 where +module Graphics.Implicit.Export.SymbolicObj2 (symbolicGetOrientedContour, symbolicGetContour, symbolicGetContourMesh) where import Prelude(map, ($), (-), (/), (+), (>), (*), (.), reverse, cos, pi, sin, max, fromInteger, ceiling) -import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline, (⋯*)) +import Graphics.Implicit.Definitions (ℝ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline, Polytri, (⋯*)) import Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) @@ -52,7 +52,7 @@ symbolicGetContour res obj = case rebound2 (getImplicit2 obj, getBox2 obj) of (obj', (a,b)) -> Render.getContour a b res obj' -symbolicGetContourMesh :: ℝ -> SymbolicObj2 -> [(ℝ2,ℝ2,ℝ2)] +symbolicGetContourMesh :: ℝ -> SymbolicObj2 -> [Polytri] symbolicGetContourMesh res (Translate2 v obj) = map (\(a,b,c) -> (a + v, b + v, c + v) ) $ symbolicGetContourMesh res obj symbolicGetContourMesh res (Scale2 s@(a,b) obj) = map (\(c,d,e) -> (c ⋯* s, d ⋯* s, e ⋯* s) ) $ @@ -68,5 +68,3 @@ symbolicGetContourMesh res (Circle r) = n = max 5 (fromInteger . ceiling $ 2*pi*r/res) symbolicGetContourMesh res obj = case rebound2 (getImplicit2 obj, getBox2 obj) of (obj', (a,b)) -> getContourMesh a b (res,res) obj' - - From 9d6398a0b6bb1f3c72101b17e0cf571c7895c130 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:40:06 +0100 Subject: [PATCH 060/227] use fastint. --- Graphics/Implicit/Export/TextBuilderUtils.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/TextBuilderUtils.hs b/Graphics/Implicit/Export/TextBuilderUtils.hs index e90af95..1ef1f8b 100644 --- a/Graphics/Implicit/Export/TextBuilderUtils.hs +++ b/Graphics/Implicit/Export/TextBuilderUtils.hs @@ -5,8 +5,7 @@ -- This module exists to re-export a coherent set of functions to define -- Data.Text.Lazy builders with. -module Graphics.Implicit.Export.TextBuilderUtils - ( +module Graphics.Implicit.Export.TextBuilderUtils ( -- Values from Data.Text.Lazy Text, pack, @@ -25,8 +24,9 @@ module Graphics.Implicit.Export.TextBuilderUtils mempty ) where -import Prelude (Int, Maybe(Nothing, Just), ($)) +import Prelude (Maybe(Nothing, Just), ($)) +import Graphics.Implicit.Definitions(Fastℕ) import Data.Text.Lazy (Text, pack) -- We manually redefine this operator to avoid a dependency on base >= 4.5 -- This will become unnecessary later. @@ -50,7 +50,7 @@ bf = formatRealFloat Exponent Nothing buildTruncFloat = formatRealFloat Fixed $ Just 4 -buildInt :: Int -> Builder +buildInt :: Fastℕ -> Builder buildInt = decimal -- This is directly copied from base 4.5.1.0 From 7b34c488c494e4375f46ef2a3e57cafa1432c7ca Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:42:03 +0100 Subject: [PATCH 061/227] be explicit about what we export, and use mesh and fastint types --- Graphics/Implicit/Export/TriangleMeshFormats.hs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 6d05a07..71de08f 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -8,11 +8,12 @@ -- Make string litearls more polymorphic, so we can use them with Builder. {-# LANGUAGE OverloadedStrings #-} -module Graphics.Implicit.Export.TriangleMeshFormats where +-- This module exposes three functions, which convert a triangle mesh to an output file. +module Graphics.Implicit.Export.TriangleMeshFormats (stl, binaryStl, jsTHREE) where -import Prelude (Real, Float, Int, ($), (+), map, (.), realToFrac, toEnum, length, zip, return) +import Prelude (Real, Float, ($), (+), map, (.), realToFrac, toEnum, length, zip, return) -import Graphics.Implicit.Definitions (Triangle, TriangleMesh, ℝ3) +import Graphics.Implicit.Definitions (Triangle, TriangleMesh, Fastℕ, ℝ3) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildInt) import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) @@ -32,7 +33,7 @@ normal :: (ℝ3,ℝ3,ℝ3) -> ℝ3 normal (a,b,c) = normalized $ (b + negateV a) `cross3` (c + negateV a) -stl :: [Triangle] -> Text +stl :: TriangleMesh -> Text stl triangles = toLazyText $ stlHeader <> mconcat (map triangle triangles) <> stlFooter where stlHeader :: Builder @@ -62,7 +63,7 @@ toFloat = realToFrac :: (Real a) => a -> Float float32LE :: Float -> Write float32LE = writeStorable . LE -binaryStl :: [Triangle] -> ByteString +binaryStl :: TriangleMesh -> ByteString binaryStl triangles = toLazyByteString $ header <> lengthField <> mconcat (map triangle triangles) where header = fromByteString $ replicate 80 0 lengthField = fromWord32le $ toEnum $ length triangles @@ -95,7 +96,7 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer v :: ℝ3 -> Builder v (x,y,z) = "v(" <> bf x <> "," <> bf y <> "," <> bf z <> ");\n" -- A face line - f :: Int -> Int -> Int -> Builder + f :: Fastℕ -> Fastℕ -> Fastℕ -> Builder f posa posb posc = "f(" <> buildInt posa <> "," <> buildInt posb <> "," <> buildInt posc <> ");" verts = do @@ -108,5 +109,5 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer facecode = mconcat $ do (n,_) <- zip [0, 3 ..] triangles let - (posa, posb, posc) = (n, n+1, n+2) :: (Int, Int, Int) + (posa, posb, posc) = (n, n+1, n+2) :: (Fastℕ, Fastℕ, Fastℕ) return $ f posa posb posc From 37e7df7c2b6823df79db2694fecbfa284182be23 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:48:10 +0100 Subject: [PATCH 062/227] be explicit about what we export, and use fastint. --- Graphics/Implicit/ExtOpenScad/Default.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index e7902d6..2b3f26a 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -7,12 +7,12 @@ -- We'd like to parse openscad code, with some improvements, for backwards compatability. -module Graphics.Implicit.ExtOpenScad.Default where +module Graphics.Implicit.ExtOpenScad.Default (defaultObjects) where -import Prelude (String, Bool(True, False), Maybe(Just, Nothing), Int, ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), (!!), length, otherwise, fromIntegral) +import Prelude (String, Bool(True, False), Maybe(Just, Nothing), ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), (!!), length, otherwise, fromIntegral) -import Graphics.Implicit.Definitions (ℝ) +import Graphics.Implicit.Definitions (ℝ, Fastℕ) import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup, OVal(OList, ONum, OString, OUndefined, OError, OModule, OFunc)) import Graphics.Implicit.ExtOpenScad.Util.OVal (toOObj, oTypeStr) import Graphics.Implicit.ExtOpenScad.Primitives (primitives) @@ -173,13 +173,13 @@ defaultPolymorphicFunctions = index (OList l) (ONum ind) = let - n :: Int + n :: Fastℕ n = floor ind in if n < length l then l !! n else OError ["List accessd out of bounds"] index (OString s) (ONum ind) = let - n :: Int + n :: Fastℕ n = floor ind in if n < length s then OString [s !! n] else OError ["List accessd out of bounds"] index a b = errorAsAppropriate "index" a b @@ -202,7 +202,7 @@ defaultPolymorphicFunctions = OString $ splice str 0 (length str + 1) osplice _ _ _ = OUndefined - splice :: [a] -> Int -> Int -> [a] + splice :: [a] -> Fastℕ -> Fastℕ -> [a] splice [] _ _ = [] splice (l@(x:xs)) a b | a < 0 = splice l (a+n) b From c802ac49e6b58d5024f68f45fe2f33c9deb18350 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:49:05 +0100 Subject: [PATCH 063/227] be more explicit about what e import, and use integer and fastint. --- Graphics/Implicit/ExtOpenScad/Definitions.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Definitions.hs b/Graphics/Implicit/ExtOpenScad/Definitions.hs index aa89c00..fbba186 100644 --- a/Graphics/Implicit/ExtOpenScad/Definitions.hs +++ b/Graphics/Implicit/ExtOpenScad/Definitions.hs @@ -14,9 +14,10 @@ module Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch TestInvariant(EulerCharacteristic), collector) where -import Prelude(Eq, Show, String, Maybe, Bool(True, False), Int, IO, (==), show, map, ($), (++), undefined, and, zipWith, foldl1) +import Prelude(Eq, Show, String, Maybe, Bool(True, False), IO, (==), show, map, ($), (++), undefined, and, zipWith, foldl1) -import Graphics.Implicit.Definitions (ℝ, SymbolicObj2, SymbolicObj3) +-- Resolution of the world, Integer operator, and symbolic languages for 2D and 3D objects. +import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℕ, SymbolicObj2, SymbolicObj3) import Control.Applicative (Applicative, Alternative((<|>), empty), pure, (<*>)) import Control.Monad (Functor, Monad, fmap, (>>=), mzero, mplus, MonadPlus, liftM, ap, return, (>=>)) @@ -89,7 +90,7 @@ data Expr = Var Symbol deriving (Show, Eq) -- a statement, along with the line number it is found on. -data StatementI = StatementI Int (Statement StatementI) +data StatementI = StatementI Fastℕ (Statement StatementI) deriving (Show, Eq) data Statement st = Include String Bool @@ -142,6 +143,6 @@ collector :: Symbol -> [Expr] -> Expr collector _ [x] = x collector s l = Var s :$ [ListE l] -newtype TestInvariant = EulerCharacteristic Int +newtype TestInvariant = EulerCharacteristic ℕ deriving (Show) From d7e3964a428386f08f98d88b2774acd931c5cacc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:51:42 +0100 Subject: [PATCH 064/227] be explicit about what we export. --- Graphics/Implicit/ExtOpenScad/Eval/Statement.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs index 116839d..f7a4681 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs @@ -4,7 +4,7 @@ {-# LANGUAGE ScopedTypeVariables #-} -module Graphics.Implicit.ExtOpenScad.Eval.Statement where +module Graphics.Implicit.ExtOpenScad.Eval.Statement (runStatementI) where import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left, Right), FilePath, IO, (.), ($), show, putStrLn, concatMap, return, (++), fmap, reverse, fst, readFile) From 7db8ae9386fe91e3ac6324117fb0df44f1810050 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:52:16 +0100 Subject: [PATCH 065/227] be explicit about what we export. --- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 0108308..0e17160 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -2,7 +2,8 @@ -- Copyright 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE -module Graphics.Implicit.ExtOpenScad.Parser.Expr where +-- a parser for a numeric expression. +module Graphics.Implicit.ExtOpenScad.Parser.Expr(expr0) where import Prelude (Char, Maybe(Nothing, Just), fmap, ($), (.), (>>), return, Bool(True, False), read, (++), (*), (**), (/), id, foldl, map, foldl1, unzip, tail, zipWith3) From b2ad4bf91fecaa5ae7ceab05cd3da9eb314fcf41 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:55:05 +0100 Subject: [PATCH 066/227] be explicit about what we export, and use integer type. --- Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs index 1c3672a..5bd170c 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs @@ -8,13 +8,15 @@ -- FIXME: why is this required? {-# LANGUAGE ScopedTypeVariables #-} -module Graphics.Implicit.ExtOpenScad.Util.ArgParser where +module Graphics.Implicit.ExtOpenScad.Util.ArgParser (argument, doc, defaultTo, example, test, eulerCharacteristic, argMap) where -import Prelude(String, Maybe(Just, Nothing), Int, ($), (++), concat, show, error, return, map, snd, filter, (.), fst, foldl1, not, null, (&&)) +import Prelude(String, Maybe(Just, Nothing), ($), (++), concat, show, error, return, map, snd, filter, (.), fst, foldl1, not, null, (&&)) import Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch, APTerminator, APFailIf, APExample), OVal (OError), TestInvariant(EulerCharacteristic)) import Graphics.Implicit.ExtOpenScad.Util.OVal (fromOObj, toOObj, OTypeMirror) +import Graphics.Implicit.Definitions(ℕ) + import qualified Data.Map as Map import Data.Maybe (isNothing, fromJust, isJust) @@ -56,7 +58,7 @@ example str = APExample str (return ()) test :: String -> ArgParser () test str = APTest str [] (return ()) -eulerCharacteristic :: ArgParser a -> Int -> ArgParser a +eulerCharacteristic :: ArgParser a -> ℕ -> ArgParser a eulerCharacteristic (APTest str tests child) χ = APTest str (EulerCharacteristic χ : tests) child eulerCharacteristic _ _ = error "Impossible!" From ea2ebc4e2ac08e8399efb52f0a6db17785d653d8 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:56:02 +0100 Subject: [PATCH 067/227] use fastint --- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index 178188e..836036b 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -10,9 +10,10 @@ module Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) where -import Prelude(Int, Num, abs, (-), (/), sqrt, (*), (+), (!!), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, cos, sin, head, tail, (.)) +import Prelude(Num, abs, (-), (/), sqrt, (*), (+), (!!), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, cos, sin, head, tail, (.)) + +import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2)) -import Graphics.Implicit.Definitions (SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2), Obj2, ℝ, ℝ2, (⋯/)) import Graphics.Implicit.MathUtil (rminimum, rmaximum, distFromLineSeg) import Data.VectorSpace ((^-^)) @@ -31,7 +32,7 @@ getImplicit2 (Circle r) = \(x,y) -> sqrt (x * x + y * y) - r getImplicit2 (PolygonR _ points) = \p -> let - pair :: Int -> (ℝ2,ℝ2) + pair :: Fastℕ -> (ℝ2,ℝ2) pair n = (points !! n, points !! mod (n + 1) (length points) ) pairs = [ pair n | n <- [0 .. length points - 1] ] relativePairs = map (\(a,b) -> (a ^-^ p, b ^-^ p) ) pairs From f2bafa9efa3dc95b1fc037fd9512124d4886381d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 15 May 2018 02:56:59 +0100 Subject: [PATCH 068/227] use fastint --- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index c8ca958..c50db8f 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -8,9 +8,9 @@ module Graphics.Implicit.ObjectUtil.GetImplicit3 (getImplicit3) where -import Prelude (Either(Left, Right), Int, abs, (-), (/), (*), sqrt, (+), atan2, max, cos, map, (==), minimum, ($), maximum, (**), sin, const, pi, (.), Bool(True, False), ceiling, floor, fromIntegral, return, error, head, tail, Num) +import Prelude (Either(Left, Right), abs, (-), (/), (*), sqrt, (+), atan2, max, cos, map, (==), minimum, ($), maximum, (**), sin, const, pi, (.), Bool(True, False), ceiling, floor, fromIntegral, return, error, head, tail, Num) -import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, (⋯/), Obj3, +import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, ℝ3, (⋯/), Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR)) @@ -19,13 +19,15 @@ import qualified Data.Maybe as Maybe import qualified Data.Either as Either import Data.VectorSpace ((^-^), (^+^), (^*), (<.>), normalized) +-- Use getImplicit2 for handling extrusion of 2D shapes to 3D. import Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) getImplicit3 :: SymbolicObj3 -> Obj3 -- Primitives -getImplicit3 (Rect3R r (x1,y1,z1) (x2,y2,z2)) = \(x,y,z) -> rmaximum r - [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2, abs (z-dz/2-z1) - dz/2] - where (dx, dy, dz) = (x2-x1, y2-y1, z2-z1) +getImplicit3 (Rect3R r (x1,y1,z1) (x2,y2,z2)) = + \(x,y,z) -> let (dx, dy, dz) = (x2-x1, y2-y1, z2-z1) + in + rmaximum r [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2, abs (z-dz/2-z1) - dz/2] getImplicit3 (Sphere r ) = \(x,y,z) -> sqrt (x*x + y*y + z*z) - r getImplicit3 (Cylinder h r1 r2) = \(x,y,z) -> @@ -86,7 +88,7 @@ getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = rotateXY :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) rotateXY θ obj' (x,y,z) = obj' ( x*cos θ + y*sin θ, y*cos θ - x*sin θ, z) in - rotateYZ yz . rotateZX zx $ rotateXY xy obj + rotateYZ yz . rotateZX zx $ rotateXY xy obj getImplicit3 (Rotate3V θ axis symbObj) = let axis' = normalized axis @@ -174,7 +176,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = let r = sqrt (x*x + y*y) θ = atan2 y x - ns :: [Int] + ns :: [Fastℕ] ns = if capped then -- we will cap a different way, but want leeway to keep the function cont From 6a5a4cabe72a5e5b0b0add8f7bd31b11c5b78fe1 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 02:23:21 +0100 Subject: [PATCH 069/227] support the test suite better, and add cachegrind to the example runs. --- Makefile | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 459b76d..c12491b 100644 --- a/Makefile +++ b/Makefile @@ -14,9 +14,11 @@ stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py convert=convert EXTOPENSCAD=dist/build/extopenscad/extopenscad +TESTSUITE=dist/build/test-implicit/test-implicit +TARGETS=$(EXTOPENSCAD) $(TESTSUITE) # FIXME: this used to be ./Setup install. what's going on? -install: $(EXTOPENSCAD) +install: $(TARGETS) cabal install clean: Setup @@ -36,30 +38,38 @@ distclean: clean rm -f `find ./ -name *~` rm -f `find ./ -name \#*\#` +nukeclean: distclean + rm -rf ~/.cabal/ ~/.ghc/ -docs: $(EXTOPENSCAD) + +docs: $(TARGETS) ./Setup haddock -dist: $(EXTOPENSCAD) +dist: $(TARGETS) ./Setup sdist -test: $(EXTOPENSCAD) - ./Setup test +#test: $(TARGETS) +# ./Setup test -examples: $(EXTOPENSCAD) - cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { time ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done +examples: $(TARGETS) + cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; ghc $$filename.hs -o $$filename; $$filename; } done images: cd Examples && for each in `find ./ -name '*.stl' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; if [ -e $$filename.transform ] ; then echo ${stl2ps} $$each $$filename.ps `cat $$filename.transform`; else ${stl2ps} $$each $$filename.ps; fi; ${convert} $$filename.ps $$filename.png; } done -tests: $(EXTOPENSCAD) - cd tests && for each in `find ./ -name '*scad' -type f | sort`; do { time ../$(EXTOPENSCAD) $$each ${RESOPTS} ${RTSOPTS}; } done +tests: $(TARGETS) +# cd tests && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each ${RESOPTS} ${RTSOPTS}; } done + ./dist/build/test-implicit/test-implicit dist/build/extopenscad/extopenscad: Setup dist/setup-config cabal build +dist/build/test-implicit/test-implicit: Setup dist/setup-config + cabal build + dist/setup-config: Setup implicit.cabal + cabal update cabal install --only-dependencies --upgrade-dependencies cabal configure --enable-tests $(PROFILING) From 8ccdc3e6f48452c9a8dc718a742b10557fd76a7e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 02:26:42 +0100 Subject: [PATCH 070/227] depend on the test suite requirements, and speed up the benchmark program. --- implicit.cabal | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 32e7c6f..b8dcbb0 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -20,7 +20,6 @@ library base >= 3 && < 5, filepath, directory, - download, parsec, unordered-containers, parallel, @@ -41,7 +40,8 @@ library snap-core, snap-server, silently, - transformers + transformers, + hspec ghc-options: -Wall @@ -165,7 +165,6 @@ executable extopenscad -- -fspec-constr-count=10 executable implicitsnap - main-is: implicitsnap.hs hs-source-dirs: programs build-depends: @@ -201,7 +200,6 @@ executable implicitsnap -optc-ffast-math executable Benchmark - main-is: Benchmark.hs hs-source-dirs: programs build-depends: @@ -253,6 +251,7 @@ benchmark parser-bench -Weverything -O2 -optc-O3 + -optc-ffast-math source-repository head type: git From 5b7070e2fe84b014a1349873bb55558de64224c9 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 02:27:21 +0100 Subject: [PATCH 071/227] comment the code, and be more explicit about imports. --- tests/Main.hs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/Main.hs b/tests/Main.hs index 081c437..7f27805 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -1,8 +1,22 @@ -import Test.Hspec -import ParserSpec.Statement -import ParserSpec.Expr +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright (C) 2018, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + +-- be explicit about what we import. +import Prelude (($), IO) + +-- our testing engine. +import Test.Hspec(hspec, describe) + +-- the test forstatements. +import ParserSpec.Statement(statementSpec) + +-- the test for expressions. +import ParserSpec.Expr(exprSpec) main :: IO () main = hspec $ do + -- run tests against the expression engine. describe "expressions" exprSpec + -- and now, against the statement engine. describe "statements" statementSpec From 556b4510db93b4e91b6a6e4e3703c10dc49d89ff Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 02:27:56 +0100 Subject: [PATCH 072/227] remove warning. --- Setup.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Setup.hs b/Setup.hs index 9a994af..b55cb16 100644 --- a/Setup.hs +++ b/Setup.hs @@ -1,2 +1,3 @@ import Distribution.Simple +main :: IO () main = defaultMain From 2ff0313ced5b7066f96187d4bf7e64fa1d71e96f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 02:30:23 +0100 Subject: [PATCH 073/227] use abstraction to make operators easier to use, add more tests, and add some comments. --- tests/ParserSpec/Expr.hs | 95 ++++++++++++++++++++++------------- tests/ParserSpec/Statement.hs | 16 +++--- tests/ParserSpec/Util.hs | 51 +++++++++++++------ 3 files changed, 103 insertions(+), 59 deletions(-) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index bec9bac..e08771b 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -4,19 +4,31 @@ module ParserSpec.Expr (exprSpec) where -import Test.Hspec -import Graphics.Implicit.ExtOpenScad.Definitions -import Graphics.Implicit.ExtOpenScad.Parser.Expr -import Graphics.Implicit.ExtOpenScad.Parser.Statement -import ParserSpec.Util -import Text.ParserCombinators.Parsec hiding (State) -import Data.Either +-- Be explicit about what we import. +import Prelude (String, Bool(True, False), ($), (<*), ) +-- Hspec, for writing specs. +import Test.Hspec (describe, Expectation, Spec, it, shouldBe, pendingWith, specify) + +-- parsed expression components. +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$)) ) + +-- the expression parser entry point. +import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) + +import ParserSpec.Util (fapp, num, bool, plus, minus, mult, modulo, power, divide, negate, and, or, gt, lt, ternary, append, index, parseWithLeftOver) + +import Data.Either (Either(Right)) + +import Text.ParserCombinators.Parsec (parse, eof) + +-- An operator for expressions for "the left side should parse to the right side." infixr 1 --> (-->) :: String -> Expr -> Expectation (-->) source expr = - parseExpr source `shouldBe` Right expr + parse (expr0 <* eof) "" source `shouldBe` Right expr +-- An operator for expressions for "the left side should parse to the right side, and some should be left over. infixr 1 -->+ (-->+) :: String -> (Expr, String) -> Expectation (-->+) source (result, leftover) = @@ -25,22 +37,28 @@ infixr 1 -->+ ternaryIssue :: Expectation -> Expectation ternaryIssue _ = pendingWith "parser doesn't handle ternary operator correctly" +negationIssue :: Expectation -> Expectation +negationIssue _ = pendingWith "parser doesn't handle negation operator correctly" + logicalSpec :: Spec logicalSpec = do - it "handles not" $ "!foo" --> app' "!" [Var "foo"] + describe "not" $ do + specify "single" $ "!foo" --> negate [Var "foo"] + specify "multiple" $ + negationIssue $ "!!!foo" --> negate [negate [negate [Var "foo"]]] it "handles and/or" $ do - "foo && bar" --> app' "&&" [Var "foo", Var "bar"] - "foo || bar" --> app' "||" [Var "foo", Var "bar"] + "foo && bar" --> and [Var "foo", Var "bar"] + "foo || bar" --> or [Var "foo", Var "bar"] describe "ternary operator" $ do specify "with primitive expressions" $ - "x ? 2 : 3" --> app' "?" [Var "x", num 2, num 3] + "x ? 2 : 3" --> ternary [Var "x", num 2, num 3] specify "with parenthesized comparison" $ - "(1 > 0) ? 5 : -5" --> app' "?" [app' ">" [num 1, num 0], num 5, num (-5)] + "(1 > 0) ? 5 : -5" --> ternary [gt [num 1, num 0], num 5, num (-5)] specify "with comparison in head position" $ - ternaryIssue $ "1 > 0 ? 5 : -5" --> app' "?" [app' ">" [num 1, num 0], num 5, num (-5)] + ternaryIssue $ "1 > 0 ? 5 : -5" --> ternary [gt [num 1, num 0], num 5, num (-5)] specify "with comparison in head position, and addition in tail" $ ternaryIssue $ "1 > 0 ? 5 : 1 + 2" --> - app' "?" [app' ">" [num 1, num 0], num 5, app "+" [num 1, num 2]] + ternary [gt [num 1, num 0], num 5, plus [num 1, num 2]] literalSpec :: Spec literalSpec = do @@ -59,7 +77,6 @@ exprSpec = do it "accepts valid variable names" $ do "foo" --> Var "foo" "foo_bar" --> Var "foo_bar" - describe "literals" literalSpec describe "grouping" $ do it "allows parens" $ "( false )" --> bool False @@ -69,38 +86,44 @@ exprSpec = do "( 1, 2, 3 )" --> ListE [num 1, num 2, num 3] it "handles generators" $ "[ a : 1 : b + 10 ]" --> - app "list_gen" [Var "a", num 1, app "+" [Var "b", num 10]] + fapp "list_gen" [Var "a", num 1, plus [Var "b", num 10]] it "handles indexing" $ - "foo[23]" --> Var "index" :$ [Var "foo", num 23] + "foo[23]" --> index [Var "foo", num 23] describe "arithmetic" $ do it "handles unary +/-" $ do "-42" --> num (-42) "+42" --> num 42 it "handles +" $ do - "1 + 2" --> app "+" [num 1, num 2] - "1 + 2 + 3" --> app "+" [num 1, num 2, num 3] + "1 + 2" --> plus [num 1, num 2] + "1 + 2 + 3" --> plus [num 1, num 2, num 3] it "handles -" $ do - "1 - 2" --> app' "-" [num 1, num 2] - "1 - 2 - 3" --> app' "-" [app' "-" [num 1, num 2], num 3] + "1 - 2" --> minus [num 1, num 2] + "1 - 2 - 3" --> minus [minus [num 1, num 2], num 3] it "handles +/- in combination" $ do - "1 + 2 - 3" --> app "+" [num 1, app' "-" [num 2, num 3]] - "2 - 3 + 4" --> app "+" [app' "-" [num 2, num 3], num 4] - "1 + 2 - 3 + 4" --> app "+" [num 1, app' "-" [num 2, num 3], num 4] - "1 + 2 - 3 + 4 - 5 - 6" --> app "+" [num 1, - app' "-" [num 2, num 3], - app' "-" [app' "-" [num 4, num 5], + "1 + 2 - 3" --> plus [num 1, minus [num 2, num 3]] + "2 - 3 + 4" --> plus [minus [num 2, num 3], num 4] + "1 + 2 - 3 + 4" --> plus [num 1, minus [num 2, num 3], num 4] + "1 + 2 - 3 + 4 - 5 - 6" --> plus [num 1, + minus [num 2, num 3], + minus [minus [num 4, num 5], num 6]] it "handles exponentiation" $ - "x ^ y" --> app' "^" [Var "x", Var "y"] + "x ^ y" --> power [Var "x", Var "y"] it "handles *" $ do - "3 * 4" --> app "*" [num 3, num 4] - "3 * 4 * 5" --> app "*" [num 3, num 4, num 5] + "3 * 4" --> mult [num 3, num 4] + "3 * 4 * 5" --> mult [num 3, num 4, num 5] it "handles /" $ - "4.2 / 2.3" --> app' "/" [num 4.2, num 2.3] + "4.2 / 2.3" --> divide [num 4.2, num 2.3] it "handles precedence" $ - parseExpr "1 + 2 / 3 * 5" `shouldBe` - (Right $ app "+" [num 1, app "*" [app' "/" [num 2, num 3], num 5]]) + "1 + 2 / 3 * 5" --> plus [num 1, mult [divide [num 2, num 3], num 5]] it "handles append" $ - parseExpr "foo ++ bar ++ baz" `shouldBe` - (Right $ app "++" [Var "foo", Var "bar", Var "baz"]) + "foo ++ bar ++ baz" --> append [Var "foo", Var "bar", Var "baz"] describe "logical operators" logicalSpec + describe "application" $ do + specify "base case" $ "foo(x)" --> Var "foo" :$ [Var "x"] + specify "multiple arguments" $ + "foo(x, 1, 2)" --> Var "foo" :$ [Var "x", num 1, num 2] + specify "multiple" $ + "foo(x, 1, 2)(5)(y)" --> ((Var "foo" :$ [Var "x", num 1, num 2]) :$ [num 5]) :$ [Var "y"] + specify "multiple, with indexing" $ + "foo(x)[0](y)" --> ((index [(Var "foo" :$ [Var "x"]), num 0]) :$ [Var "y"]) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 56de979..709c337 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -11,7 +11,7 @@ import Test.Hspec (Spec, Expectation, shouldBe, shouldSatisfy, it, pendingWith, -- import Text.ParserCombinators.Parsec () -import ParserSpec.Util (bool, num, app, app') +import ParserSpec.Util (bool, num, minus, mult, index) import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) @@ -36,32 +36,34 @@ single st = [StatementI 1 st] call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI call name args stmts = StatementI 1 (ModuleCall name args stmts) +-- test a simple if block. ifSpec :: Spec ifSpec = it "parses" $ "if (true) { a(); } else { b(); }" --> single ( If (bool True) [call "a" [] []] [call "b" [] []]) +-- test assignments. assignmentSpec :: Spec assignmentSpec = do it "parses correctly" $ "y = -5;" --> single ( Name "y" := num (-5)) it "handles pattern matching" $ "[x, y] = [1, 2];" --> single (ListP [Name "x", Name "y"] := ListE [num 1, num 2]) - it "handles function definitions" $ - "foo (x, y) = x * y;" --> single fooFunction - it "handles the function keyword" $ + it "handles the function keyword and definitions" $ "function foo(x, y) = x * y;" --> single fooFunction it "nested indexing" $ "x = [y[0] - z * 2];" --> - single ( Name "x" := ListE [app' "-" [app' "index" [Var "y", num 0], - app "*" [Var "z", num 2]]]) + single ( Name "x" := ListE [minus [index [Var "y", num 0], + mult [Var "z", num 2]]]) where + fooFunction :: Statement st fooFunction = Name "foo" := LamE [Name "x", Name "y"] - (app "*" [Var "x", Var "y"]) + (mult [Var "x", Var "y"]) emptyFileIssue :: Expectation -> Expectation emptyFileIssue _ = pendingWith "parser should probably allow empty files" + statementSpec :: Spec statementSpec = do describe "assignment" $ assignmentSpec diff --git a/tests/ParserSpec/Util.hs b/tests/ParserSpec/Util.hs index 2180d1e..9c92d0d 100644 --- a/tests/ParserSpec/Util.hs +++ b/tests/ParserSpec/Util.hs @@ -10,11 +10,22 @@ module ParserSpec.Util ( num , bool - , app - , app' - , parseWithEof + , fapp + , plus + , minus + , mult + , modulo + , power + , divide + , negate + , and + , or + , gt + , lt + , ternary + , append + , index , parseWithLeftOver - , parseExpr ) where -- be explicit about where we get things from. @@ -37,27 +48,35 @@ num :: ℝ -> Expr num x -- FIXME: the parser should handle negative number literals -- directly, we abstract that deficiency away here - | x < 0 = app' "negate" [LitE $ ONum (-x)] + | x < 0 = oapp "negate" [LitE $ ONum (-x)] | otherwise = LitE $ ONum x bool :: Bool -> Expr bool = LitE . OBool --- Operators and functions need two different kinds of applications -app :: String -> [Expr] -> Expr -app name args = Var name :$ [ListE args] +plus,minus,mult,modulo,power,divide,negate,and,or,gt,lt,ternary,append,index :: [Expr] -> Expr +minus = oapp "-" +modulo = oapp "%" +power = oapp "^" +divide = oapp "/" +and = oapp "&&" +or = oapp "||" +gt = oapp ">" +lt = oapp "<" +ternary = oapp "?" +negate = oapp "!" +index = oapp "index" +plus = fapp "+" +mult = fapp "*" +append = fapp "++" -app' :: Symbol -> [Expr] -> Expr -app' name args = Var name :$ args +-- we need two different kinds of application functions +oapp,fapp :: String -> [Expr] -> Expr +oapp name args = Var name :$ args +fapp name args = Var name :$ [ListE args] parseWithLeftOver :: Parser a -> String -> Either ParseError (a, String) parseWithLeftOver p = parse ((,) <$> p <*> leftOver) "" where leftOver :: Parser String leftOver = manyTill anyChar eof - -parseWithEof :: Parser a -> String -> String -> Either ParseError a -parseWithEof p = parse (p <* eof) - -parseExpr :: String -> Either ParseError Expr -parseExpr = parseWithEof expr0 "expr" From c1f8d17764384dd76b819005610a88fa25d4a6f2 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 03:18:26 +0100 Subject: [PATCH 074/227] date stamp cachegrind files. --- Graphics/Implicit/Export/PolylineFormats.hs | 4 ++-- Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index d838f04..79314f0 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -57,11 +57,11 @@ hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpret . sortBy (\(a,_) (b, _) -> compare a b) . map (\x -> (polylineRadius x, x)) $ polylines - polylineRadius :: [(ℝ, ℝ)] -> ℝ + polylineRadius :: [ℝ2] -> ℝ polylineRadius [] = 0 polylineRadius polyline' = max (xmax' - xmin') (ymax' - ymin') where ((xmin', xmax'), (ymin', ymax')) = polylineRadius' polyline' - polylineRadius' :: [(ℝ, ℝ)] -> ((ℝ, ℝ), (ℝ, ℝ)) + polylineRadius' :: [ℝ2] -> (ℝ2, ℝ2) polylineRadius' [] = ((0,0),(0,0)) polylineRadius' [(x,y)] = ((x,x),(y,y)) polylineRadius' ((x,y):ps) = ((min x xmin,max x xmax),(min y ymin, max y ymax)) diff --git a/Makefile b/Makefile index c12491b..eee967c 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ dist: $(TARGETS) # ./Setup test examples: $(TARGETS) - cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done + cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; ghc $$filename.hs -o $$filename; $$filename; } done images: From 889a93d7584d48bf11372db8d43bf9b0fc9945cc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 03:18:58 +0100 Subject: [PATCH 075/227] remove unused imports. --- tests/ParserSpec/Statement.hs | 2 +- tests/ParserSpec/Util.hs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 709c337..1fe01fb 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -18,7 +18,7 @@ import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol -- Parse an ExtOpenScad program. import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) -import Data.Either (Either(Left, Right), isLeft) +import Data.Either (Either(Right), isLeft) -- an expectation that a string become a statement. infixr 1 --> diff --git a/tests/ParserSpec/Util.hs b/tests/ParserSpec/Util.hs index 9c92d0d..55b8c26 100644 --- a/tests/ParserSpec/Util.hs +++ b/tests/ParserSpec/Util.hs @@ -35,14 +35,11 @@ import Prelude (Bool, String, Either, (<), ($), (.), otherwise) import Graphics.Implicit.Definitions (ℝ) -- The datatype of expressions, symbols, and values in the OpenScad language. -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE), Symbol, OVal(ONum, OBool)) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE), OVal(ONum, OBool)) --- the entry point of the expression parser. -import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) - import Text.ParserCombinators.Parsec (Parser, ParseError, parse, manyTill, anyChar, eof) -import Control.Applicative ((<$>), (<*>), (<*)) +import Control.Applicative ((<$>), (<*>)) num :: ℝ -> Expr num x From c597ee39074120a969b99404ba7ff56b598c6d4f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 03:21:41 +0100 Subject: [PATCH 076/227] api changes, new version! --- implicit.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implicit.cabal b/implicit.cabal index b8dcbb0..22bf1d6 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -1,5 +1,5 @@ name: implicit -version: 0.1.1 +version: 0.2.0 cabal-version: >= 1.8 synopsis: Math-inspired programmatic 2&3D CAD: CSG, bevels, and shells; gcode export.. description: A math-inspired programmatic CAD library in haskell. From dd14de3f95e5e23452c6e799511ab66db92fae44 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 18:51:25 +0100 Subject: [PATCH 077/227] remove -O2 --- implicit.cabal | 6 ------ 1 file changed, 6 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 22bf1d6..d51cb9a 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -48,7 +48,6 @@ library -- for debugging only. -Wextra -Weverything - -O2 -optc-O3 -- cannot use, we use infinity in some calculations. -- -optc-ffast-math @@ -127,7 +126,6 @@ executable extopenscad -rtsopts -Wall -Weverything - -O2 -optc-O3 -optc-ffast-math @@ -195,7 +193,6 @@ executable implicitsnap -rtsopts -Wall -Weverything - -O2 -optc-O3 -optc-ffast-math @@ -227,7 +224,6 @@ executable Benchmark -rtsopts -Wall -Weverything - -O2 -optc-O3 -optc-ffast-math @@ -239,7 +235,6 @@ test-suite test-implicit ghc-options: -Wall -Weverything - -O2 -optc-O3 benchmark parser-bench @@ -249,7 +244,6 @@ benchmark parser-bench ghc-options: -Wall -Weverything - -O2 -optc-O3 -optc-ffast-math From 4d27710f6dd9d97e755620ed570d223843aa32c6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 18:51:45 +0100 Subject: [PATCH 078/227] fix typo. --- Graphics/Implicit/Definitions.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index a4eb767..fb9fa57 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -150,7 +150,7 @@ type NormedTriangle = ((ℝ3, ℝ3), (ℝ3, ℝ3), (ℝ3, ℝ3)) -- | A triangle mesh is a bunch of triangles, attempting to be a surface. type TriangleMesh = [Triangle] --- | A normed triangle mesh is a mesh of normed trianlges. +-- | A normed triangle mesh is a mesh of normed triangles. type NormedTriangleMesh = [NormedTriangle] -- | A 2D object From 9cf8541d6d16a33c9990b7b691c4209f1a71eb8a Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 18:52:03 +0100 Subject: [PATCH 079/227] fix a comment. --- tests/ParserSpec/Statement.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 1fe01fb..7246f79 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -20,7 +20,7 @@ import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Data.Either (Either(Right), isLeft) --- an expectation that a string become a statement. +-- an expectation that a string is equivalent to a statement. infixr 1 --> (-->) :: String -> [StatementI] -> Expectation (-->) source stmts = @@ -63,7 +63,6 @@ assignmentSpec = do emptyFileIssue :: Expectation -> Expectation emptyFileIssue _ = pendingWith "parser should probably allow empty files" - statementSpec :: Spec statementSpec = do describe "assignment" $ assignmentSpec From 2ecac90d0cde8e17ee13669c4dd72cd23d466c04 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 19 May 2018 21:51:27 +0100 Subject: [PATCH 080/227] pre-evaluate our object before processing it, for the 2d cases. --- Graphics/Implicit/Export/MarchingSquares.hs | 44 ++++++++++--------- .../Implicit/Export/MarchingSquaresFill.hs | 21 +++++++-- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index e93b92a..76d8d56 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -8,19 +8,21 @@ -- export getContour, which returns as array of polylines describing the edge of a 2D object. module Graphics.Implicit.Export.MarchingSquares (getContour) where -import Prelude(Bool(True, False), ceiling, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), splitAt, div, unzip, length, (++), (<), (++), head, ceiling, concat, div, max, not, null, (||), Eq, fromIntegral) +import Prelude(Bool(True, False), ceiling, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), splitAt, div, unzip, length, (++), (<), (++), head, ceiling, concat, div, max, not, null, (||), Eq, fromIntegral, floor) -import Graphics.Implicit.Definitions (ℕ, ℝ2, Polyline, Obj2, (⋯/), (⋯*)) +import Graphics.Implicit.Definitions (ℕ, ℝ, ℝ2, Polyline, Obj2, (⋯/), (⋯*)) import Data.VectorSpace ((^-^), (^+^)) +import Data.List(genericIndex) + import Control.Arrow((***)) -- import a helper, to clean up the result we return. import Graphics.Implicit.Export.Render.HandlePolylines (reducePolyline) -- Each step on the Y axis is done in parallel using Control.Parallel.Strategies -import Control.Parallel.Strategies (using, rdeepseq, parBuffer) +import Control.Parallel.Strategies (using, rdeepseq, parBuffer, parList) -- apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) @@ -42,10 +44,26 @@ getContour p1 p2 res obj = gridPos :: (ℕ,ℕ) -> (ℕ,ℕ) -> ℝ2 gridPos n' m = p1 ^+^ d ⋯* ((fromIntegral `both` m) ⋯/ (fromIntegral `both` n')) + -- alternate Grid mapping funcs + toGrid :: ℝ2 -> (ℕ,ℕ) + toGrid f = floor `both` ((fromIntegral `both` n) ⋯* (f ^-^ p1) ⋯/ d) + + -- Evaluate obj on a grid, in parallel. + valsOnGrid :: [[ℝ]] + valsOnGrid = [[ obj $ gridPos n (mx, my) | mx <- [0..nx-1] ] | my <- [0..ny-1] ] `using` parList rdeepseq + + -- A faster version of the obj. Sort of like memoization, but done in advance, in parallel. + preEvaledObj p = valsOnGrid `genericIndex` my `genericIndex` mx where (mx,my) = toGrid p + -- compute the polylines linesOnGrid :: [[[Polyline]]] - linesOnGrid = [[getSquareLineSegs (gridPos n (mx,my)) (gridPos n (mx+1,my+1)) obj - | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq + linesOnGrid = [[getSquareLineSegs (gridPos n (mx, my)) (gridPos n (mx+1, my+1)) preEvaledObj + | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq + +{- + linesOnGrid = [[getSquareLineSegs (gridPos n (mx, my)) (gridPos n (mx+1, my+1)) obj + | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq +-} -- Cleanup, cleanup, everybody cleanup! -- (We connect multilines, delete redundant vertices on them, etc) @@ -53,22 +71,6 @@ getContour p1 p2 res obj = in lines -- FIXME: Commented out, not used? -{- - -- alternate Grid mapping funcs - fromGrid (mx, my) = let p = (mx/nx, my/ny) - in (p1 ^+^ (p2 ^-^ p1) ⋯/ p) - toGrid (x,y) = (floor $ nx*(x-x1)/(x2-x1), floor $ ny*(y-y1)/(y2-y1)) - -- Evaluate obj on a grid, in parallel. - valsOnGrid :: [[ℝ]] - valsOnGrid = [[ obj (fromGrid (mx, my)) | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] - `using` parList rdeepseq - -- A faster version of the obj. Sort of like memoization, but done in advance, in parallel. - preEvaledObj p = valsOnGrid !! my !! mx where (mx,my) = toGrid p - -- Divide it up and compute the polylines - linesOnGrid :: [[[Polyline]]] - linesOnGrid = [[getSquareLineSegs (fromGrid (mx, my)) (fromGrid (mx+1, my+1)) preEvaledObj - | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] --} -- | This function gives line segments to divide negative interior -- regions and positive exterior ones inside a square, based on the diff --git a/Graphics/Implicit/Export/MarchingSquaresFill.hs b/Graphics/Implicit/Export/MarchingSquaresFill.hs index becb76e..fa00645 100644 --- a/Graphics/Implicit/Export/MarchingSquaresFill.hs +++ b/Graphics/Implicit/Export/MarchingSquaresFill.hs @@ -8,14 +8,16 @@ -- export getContourMesh, which returns an array of triangles describing the interior of a 2D object. module Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) where -import Prelude(Bool(True, False), fromIntegral, ($), (-), (+), (/), (*), (<=), (>), ceiling, concat, max, div) +import Prelude(Bool(True, False), fromIntegral, ($), (-), (+), (/), (*), (<=), (>), ceiling, concat, max, div, floor) -import Graphics.Implicit.Definitions (ℕ, ℝ2, Polytri, Obj2, (⋯/), (⋯*)) +import Graphics.Implicit.Definitions (ℕ, ℝ, ℝ2, Polytri, Obj2, (⋯/), (⋯*)) import Data.VectorSpace ((^-^),(^+^)) +import Data.List(genericIndex) + -- Each step on the Y axis is done in parallel using Control.Parallel.Strategies -import Control.Parallel.Strategies (using, rdeepseq, parBuffer) +import Control.Parallel.Strategies (using, rdeepseq, parBuffer, parList) -- apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) @@ -36,9 +38,20 @@ getContourMesh p1 p2 res obj = gridPos :: (ℕ,ℕ) -> (ℕ,ℕ) -> ℝ2 gridPos n' m = p1 ^+^ d ⋯* ((fromIntegral `both` m) ⋯/ (fromIntegral `both` n')) + -- alternate Grid mapping funcs + toGrid :: ℝ2 -> (ℕ,ℕ) + toGrid f = floor `both` ((fromIntegral `both` n) ⋯* (f ^-^ p1) ⋯/ d) + + -- Evaluate obj on a grid, in parallel. + valsOnGrid :: [[ℝ]] + valsOnGrid = [[ obj $ gridPos n (mx, my) | mx <- [0..nx-1] ] | my <- [0..ny-1] ] `using` parList rdeepseq + + -- A faster version of the obj. Sort of like memoization, but done in advance, in parallel. + preEvaledObj p = valsOnGrid `genericIndex` my `genericIndex` mx where (mx,my) = toGrid p + -- compute the triangles. trisOnGrid :: [[[Polytri]]] - trisOnGrid = [[getSquareTriangles (gridPos n (mx,my)) (gridPos n (mx+1,my+1)) obj + trisOnGrid = [[getSquareTriangles (gridPos n (mx,my)) (gridPos n (mx+1,my+1)) preEvaledObj | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq triangles = concat $ concat trisOnGrid in From 0815222b98db28d5fec9a71e8583c2edaa4e465b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 20 May 2018 05:01:26 +0100 Subject: [PATCH 081/227] spacing and comment changes. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 6 +++--- Graphics/Implicit/Export/MarchingSquares.hs | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index eb6b26f..f6390e7 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -29,7 +29,7 @@ import Data.AffineSpace ((.-^), (.+^)) -- | There is a discrete way to aproximate this object. --- eg. Aproximating a 3D object with a tirangle mesh +-- eg. Aproximating a 3D object with a triangle mesh -- would be DiscreteApproxable Obj3 TriangleMesh class DiscreteAproxable obj aprox where discreteAprox :: ℝ -> obj -> aprox @@ -40,7 +40,7 @@ instance DiscreteAproxable SymbolicObj3 TriangleMesh where instance DiscreteAproxable SymbolicObj3 NormedTriangleMesh where discreteAprox res obj = map (normTriangle res (getImplicit3 obj)) $ symbolicGetMesh res obj --- FIXME: magic numbers. +-- FIXME: way too many magic numbers. instance DiscreteAproxable SymbolicObj3 DynamicImage where discreteAprox _ symbObj = dynamicImage $ generateImage pixelRenderer (round w) (round h) where @@ -68,7 +68,7 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where (cameraRay camera ((a,b) ^+^ (-0.25/w, 0.25/h))) 0.5 box scene, traceRay - (cameraRay camera ((a,b) ^+^ (0.25/w, -0.25/h))) + (cameraRay camera ((a,b) ^+^ ( 0.25/w,-0.25/h))) 0.5 box scene, traceRay (cameraRay camera ((a,b) ^+^ (-0.25/w,-0.25/h))) diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index 76d8d56..cc29723 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -60,17 +60,11 @@ getContour p1 p2 res obj = linesOnGrid = [[getSquareLineSegs (gridPos n (mx, my)) (gridPos n (mx+1, my+1)) preEvaledObj | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq -{- - linesOnGrid = [[getSquareLineSegs (gridPos n (mx, my)) (gridPos n (mx+1, my+1)) obj - | mx <- [0.. nx-1] ] | my <- [0..ny-1] ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq --} - -- Cleanup, cleanup, everybody cleanup! -- (We connect multilines, delete redundant vertices on them, etc) lines = filter polylineNotNull $ map reducePolyline $ orderLinesDC linesOnGrid in lines --- FIXME: Commented out, not used? -- | This function gives line segments to divide negative interior -- regions and positive exterior ones inside a square, based on the From 482feb8ce3d99a2017134f6b76d8ab1d7dd8a824 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 20 May 2018 05:04:53 +0100 Subject: [PATCH 082/227] =?UTF-8?q?move=20from=20Fast=E2=84=95=20to=20?= =?UTF-8?q?=E2=84=95,=20to=20increase=20precision=20and=20maximum=20array?= =?UTF-8?q?=20sizes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Implicit/Export/Render/TesselateLoops.hs | 8 +++-- Graphics/Implicit/Export/TextBuilderUtils.hs | 6 +++- .../Implicit/Export/TriangleMeshFormats.hs | 10 +++--- Graphics/Implicit/ExtOpenScad/Default.hs | 31 ++++++++++--------- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 12 +++---- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 4 +-- 6 files changed, 40 insertions(+), 31 deletions(-) diff --git a/Graphics/Implicit/Export/Render/TesselateLoops.hs b/Graphics/Implicit/Export/Render/TesselateLoops.hs index 21a6f09..e533522 100644 --- a/Graphics/Implicit/Export/Render/TesselateLoops.hs +++ b/Graphics/Implicit/Export/Render/TesselateLoops.hs @@ -6,7 +6,7 @@ module Graphics.Implicit.Export.Render.TesselateLoops (tesselateLoop) where import Prelude(return, ($), length, (==), zip, init, tail, reverse, (<), (/), null, foldl1, (++), head, (*), abs, (>), (&&), (+), concatMap) -import Graphics.Implicit.Definitions (ℝ, Fastℕ, Obj3, ℝ3, TriangleMesh, (⋅)) +import Graphics.Implicit.Definitions (ℝ, ℕ, Obj3, ℝ3, TriangleMesh, (⋅)) import Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) @@ -14,6 +14,8 @@ import Graphics.Implicit.Export.Util (centroid) import Data.VectorSpace (normalized, (^-^), (^+^), magnitude, (^/), (^*)) +import Data.List (genericLength) + import Data.Cross (cross3) tesselateLoop :: ℝ -> Obj3 -> [[ℝ3]] -> [TriSquare] @@ -88,7 +90,7 @@ tesselateLoop res obj pathSides = return $ Tris $ else early_tris ++ [(a,b,mid) | (a,b) <- zip path (tail path ++ [head path]) ] -shrinkLoop :: Fastℕ -> [ℝ3] -> ℝ -> Obj3 -> (TriangleMesh, [ℝ3]) +shrinkLoop :: ℕ -> [ℝ3] -> ℝ -> Obj3 -> (TriangleMesh, [ℝ3]) shrinkLoop _ path@[a,b,c] res obj = if abs (obj $ centroid [a,b,c]) < res/50 @@ -97,7 +99,7 @@ shrinkLoop _ path@[a,b,c] res obj = else ([], path) -shrinkLoop n path@(a:b:c:xs) res obj | n < length path = +shrinkLoop n path@(a:b:c:xs) res obj | n < genericLength path = if abs (obj (centroid [a,c])) < res/50 then let (tris,remainder) = shrinkLoop 0 (a:c:xs) res obj diff --git a/Graphics/Implicit/Export/TextBuilderUtils.hs b/Graphics/Implicit/Export/TextBuilderUtils.hs index 1ef1f8b..9eaf18a 100644 --- a/Graphics/Implicit/Export/TextBuilderUtils.hs +++ b/Graphics/Implicit/Export/TextBuilderUtils.hs @@ -14,6 +14,7 @@ module Graphics.Implicit.Export.TextBuilderUtils ( toLazyText, fromLazyText, buildInt, + buildℕ, -- Serialize a float in full precision bf, -- Serialize a float with four decimal places @@ -37,7 +38,7 @@ import Data.Text.Lazy.Builder (Builder, toLazyTextWith, fromLazyText) import Data.Text.Lazy.Builder.RealFloat (formatRealFloat, FPFormat(Exponent, Fixed)) import Data.Text.Lazy.Builder.Int (decimal) -import Graphics.Implicit.Definitions (ℝ) +import Graphics.Implicit.Definitions (ℝ, ℕ) -- The chunk size for toLazyText is very small (128 bytes), so we export -- a version with a much larger size (~16 K) @@ -50,6 +51,9 @@ bf = formatRealFloat Exponent Nothing buildTruncFloat = formatRealFloat Fixed $ Just 4 +buildℕ :: ℕ -> Builder +buildℕ = decimal + buildInt :: Fastℕ -> Builder buildInt = decimal diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 71de08f..f33e9e7 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -13,8 +13,8 @@ module Graphics.Implicit.Export.TriangleMeshFormats (stl, binaryStl, jsTHREE) wh import Prelude (Real, Float, ($), (+), map, (.), realToFrac, toEnum, length, zip, return) -import Graphics.Implicit.Definitions (Triangle, TriangleMesh, Fastℕ, ℝ3) -import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildInt) +import Graphics.Implicit.Definitions (TriangleMesh, ℕ, ℝ3) +import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildℕ) import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) import qualified Data.ByteString.Builder.Internal as BI (Builder) @@ -96,9 +96,9 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer v :: ℝ3 -> Builder v (x,y,z) = "v(" <> bf x <> "," <> bf y <> "," <> bf z <> ");\n" -- A face line - f :: Fastℕ -> Fastℕ -> Fastℕ -> Builder + f :: ℕ -> ℕ -> ℕ -> Builder f posa posb posc = - "f(" <> buildInt posa <> "," <> buildInt posb <> "," <> buildInt posc <> ");" + "f(" <> buildℕ posa <> "," <> buildℕ posb <> "," <> buildℕ posc <> ");" verts = do -- extract the vertices for each triangle -- recall that a normed triangle is of the form ((vert, norm), ...) @@ -109,5 +109,5 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer facecode = mconcat $ do (n,_) <- zip [0, 3 ..] triangles let - (posa, posb, posc) = (n, n+1, n+2) :: (Fastℕ, Fastℕ, Fastℕ) + (posa, posb, posc) = (n, n+1, n+2) :: (ℕ, ℕ, ℕ) return $ f posa posb posc diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 2b3f26a..9a0a7c7 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -10,13 +10,14 @@ module Graphics.Implicit.ExtOpenScad.Default (defaultObjects) where -import Prelude (String, Bool(True, False), Maybe(Just, Nothing), ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), (!!), length, otherwise, fromIntegral) +import Prelude (String, Bool(True, False), Maybe(Just, Nothing), ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), otherwise) -import Graphics.Implicit.Definitions (ℝ, Fastℕ) +import Graphics.Implicit.Definitions (ℝ, ℕ) import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup, OVal(OList, ONum, OString, OUndefined, OError, OModule, OFunc)) import Graphics.Implicit.ExtOpenScad.Util.OVal (toOObj, oTypeStr) import Graphics.Implicit.ExtOpenScad.Primitives (primitives) import Data.Map (fromList) +import Data.List (genericIndex, genericLength) import Control.Arrow (second) defaultObjects :: VarLookup -- = Map String OVal @@ -173,15 +174,15 @@ defaultPolymorphicFunctions = index (OList l) (ONum ind) = let - n :: Fastℕ + n :: ℕ n = floor ind in - if n < length l then l !! n else OError ["List accessd out of bounds"] + if n < genericLength l then l `genericIndex` n else OError ["List accessd out of bounds"] index (OString s) (ONum ind) = let - n :: Fastℕ + n :: ℕ n = floor ind - in if n < length s then OString [s !! n] else OError ["List accessd out of bounds"] + in if n < genericLength s then OString [s `genericIndex` n] else OError ["List accessd out of bounds"] index a b = errorAsAppropriate "index" a b osplice (OList list) (ONum a) ( ONum b ) = @@ -193,16 +194,16 @@ defaultPolymorphicFunctions = osplice (OString str) OUndefined (ONum b ) = OString $ splice str 0 (floor b) osplice (OList list) (ONum a) OUndefined = - OList $ splice list (floor a) (length list + 1) + OList $ splice list (floor a) (genericLength list + 1) osplice (OString str) (ONum a) OUndefined = - OString $ splice str (floor a) (length str + 1) + OString $ splice str (floor a) (genericLength str + 1) osplice (OList list) OUndefined OUndefined = - OList $ splice list 0 (length list + 1) + OList $ splice list 0 (genericLength list + 1) osplice (OString str) OUndefined OUndefined = - OString $ splice str 0 (length str + 1) + OString $ splice str 0 (genericLength str + 1) osplice _ _ _ = OUndefined - splice :: [a] -> Fastℕ -> Fastℕ -> [a] + splice :: [a] -> ℕ -> ℕ -> [a] splice [] _ _ = [] splice (l@(x:xs)) a b | a < 0 = splice l (a+n) b @@ -210,7 +211,9 @@ defaultPolymorphicFunctions = | a > 0 = splice xs (a-1) (b-1) | b > 0 = x: splice xs a (b-1) | otherwise = [] - where n = length l + where + n :: ℕ + n = genericLength l errorAsAppropriate _ err@(OError _) _ = err errorAsAppropriate _ _ err@(OError _) = err @@ -235,7 +238,7 @@ defaultPolymorphicFunctions = ternary True a _ = a ternary False _ b = b - olength (OString s) = ONum . fromIntegral $ length s - olength (OList s) = ONum . fromIntegral $ length s + olength (OString s) = ONum $ genericLength s + olength (OList s) = ONum $ genericLength s olength a = OError ["Can't take length of a " ++ oTypeStr a ++ "."] diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index 836036b..e8866a2 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -10,14 +10,14 @@ module Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) where -import Prelude(Num, abs, (-), (/), sqrt, (*), (+), (!!), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, cos, sin, head, tail, (.)) +import Prelude(Num, abs, (-), (/), sqrt, (*), (+), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, cos, sin, head, tail, (.)) -import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2)) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2)) import Graphics.Implicit.MathUtil (rminimum, rmaximum, distFromLineSeg) import Data.VectorSpace ((^-^)) -import Data.List (nub) +import Data.List (nub, genericIndex, genericLength) getImplicit2 :: SymbolicObj2 -> Obj2 -- Primitives @@ -32,9 +32,9 @@ getImplicit2 (Circle r) = \(x,y) -> sqrt (x * x + y * y) - r getImplicit2 (PolygonR _ points) = \p -> let - pair :: Fastℕ -> (ℝ2,ℝ2) - pair n = (points !! n, points !! mod (n + 1) (length points) ) - pairs = [ pair n | n <- [0 .. length points - 1] ] + pair :: ℕ -> (ℝ2,ℝ2) + pair n = (points `genericIndex` n, points `genericIndex` mod (n + 1) (genericLength points) ) + pairs = [ pair n | n <- [0 .. genericLength points - 1] ] relativePairs = map (\(a,b) -> (a ^-^ p, b ^-^ p) ) pairs crossing_points = [x2 ^-^ y2*(x2-x1)/(y2-y1) | ((x1,y1), (x2,y2)) <-relativePairs, diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index c50db8f..97c6dae 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.ObjectUtil.GetImplicit3 (getImplicit3) where import Prelude (Either(Left, Right), abs, (-), (/), (*), sqrt, (+), atan2, max, cos, map, (==), minimum, ($), maximum, (**), sin, const, pi, (.), Bool(True, False), ceiling, floor, fromIntegral, return, error, head, tail, Num) -import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, ℝ3, (⋯/), Obj3, +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR)) @@ -176,7 +176,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = let r = sqrt (x*x + y*y) θ = atan2 y x - ns :: [Fastℕ] + ns :: [ℕ] ns = if capped then -- we will cap a different way, but want leeway to keep the function cont From 7f8bec0dc5b6069a74d489652e08bd2357276840 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 20 May 2018 05:07:03 +0100 Subject: [PATCH 083/227] display column number in error messages. --- Graphics/Implicit/ExtOpenScad/Definitions.hs | 4 +-- .../Implicit/ExtOpenScad/Eval/Statement.hs | 34 +++++++++---------- .../Implicit/ExtOpenScad/Parser/Statement.hs | 33 +++++++++++------- Graphics/Implicit/ExtOpenScad/Primitives.hs | 1 - Graphics/Implicit/ExtOpenScad/Util/StateC.hs | 4 +-- 5 files changed, 42 insertions(+), 34 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Definitions.hs b/Graphics/Implicit/ExtOpenScad/Definitions.hs index fbba186..8c7aa82 100644 --- a/Graphics/Implicit/ExtOpenScad/Definitions.hs +++ b/Graphics/Implicit/ExtOpenScad/Definitions.hs @@ -89,8 +89,8 @@ data Expr = Var Symbol | Expr :$ [Expr] deriving (Show, Eq) --- a statement, along with the line number it is found on. -data StatementI = StatementI Fastℕ (Statement StatementI) +-- a statement, along with the line and column number it is found on. +data StatementI = StatementI Fastℕ Fastℕ (Statement StatementI) deriving (Show, Eq) data Statement st = Include String Bool diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs index f7a4681..92bd690 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs @@ -33,27 +33,27 @@ import qualified System.FilePath as FilePath -- Run statements out of the OpenScad file. runStatementI :: StatementI -> StateC () -runStatementI (StatementI lineN (pat := expr)) = do +runStatementI (StatementI lineN columnN (pat := expr)) = do val <- evalExpr expr let posMatch = matchPat pat val case (getErrors val, posMatch) of - (Just err, _ ) -> errorC lineN err + (Just err, _ ) -> errorC lineN columnN err (_, Just match) -> modifyVarLookup $ Map.union match - (_, Nothing ) -> errorC lineN "pattern match failed in assignment" + (_, Nothing ) -> errorC lineN columnN "pattern match failed in assignment" -runStatementI (StatementI lineN (Echo exprs)) = do +runStatementI (StatementI lineN columnN (Echo exprs)) = do let show2 (OString s) = s show2 x = show x vals <- mapM evalExpr exprs case getErrors (OList vals) of Nothing -> liftIO . putStrLn $ concatMap show2 vals - Just err -> errorC lineN err + Just err -> errorC lineN columnN err -runStatementI (StatementI lineN (For pat expr loopContent)) = do +runStatementI (StatementI lineN columnN (For pat expr loopContent)) = do val <- evalExpr expr case (getErrors val, val) of - (Just err, _) -> errorC lineN err + (Just err, _) -> errorC lineN columnN err (_, OList vals) -> forM_ vals $ \v -> case matchPat pat v of Just match -> do @@ -62,21 +62,21 @@ runStatementI (StatementI lineN (For pat expr loopContent)) = do Nothing -> return () _ -> return () -runStatementI (StatementI lineN (If expr a b)) = do +runStatementI (StatementI lineN columnN (If expr a b)) = do val <- evalExpr expr case (getErrors val, val) of - (Just err, _ ) -> errorC lineN ("In conditional expression of if statement: " ++ err) + (Just err, _ ) -> errorC lineN columnN ("In conditional expression of if statement: " ++ err) (_, OBool True ) -> runSuite a (_, OBool False) -> runSuite b _ -> return () -runStatementI (StatementI lineN (NewModule name argTemplate suite)) = do +runStatementI (StatementI lineN columnN (NewModule name argTemplate suite)) = do argTemplate' <- forM argTemplate $ \(name', defexpr) -> do defval <- mapMaybeM evalExpr defexpr return (name', defval) (varlookup, _, path, _, _) <- get -- FIXME: \_? really? - runStatementI . StatementI lineN $ (Name name :=) $ LitE $ OModule $ \_ -> do + runStatementI . StatementI lineN columnN $ (Name name :=) $ LitE $ OModule $ \_ -> do newNameVals <- forM argTemplate' $ \(name', maybeDef) -> do val <- case maybeDef of Just def -> argument name' `defaultTo` def @@ -103,7 +103,7 @@ runStatementI (StatementI lineN (NewModule name argTemplate suite)) = do suiteVals = runSuiteCapture varlookup' path suite return suiteVals -runStatementI (StatementI lineN (ModuleCall name argsExpr suite)) = do +runStatementI (StatementI lineN columnN (ModuleCall name argsExpr suite)) = do maybeMod <- lookupVar name (varlookup, _, path, _, _) <- get childVals <- fmap reverse . liftIO $ runSuiteCapture varlookup path suite @@ -116,15 +116,15 @@ runStatementI (StatementI lineN (ModuleCall name argsExpr suite)) = do ioNewVals = fromMaybe (return []) (fst $ argMap argsVal argparser) Just foo -> do case getErrors foo of - Just err -> errorC lineN err - Nothing -> errorC lineN "Object called not module!" + Just err -> errorC lineN columnN err + Nothing -> errorC lineN columnN "Object called not module!" return [] Nothing -> do - errorC lineN $ "Module " ++ name ++ " not in scope." + errorC lineN columnN $ "Module " ++ name ++ " not in scope." return [] pushVals newVals -runStatementI (StatementI _ (Include name injectVals)) = do +runStatementI (StatementI _ _ (Include name injectVals)) = do name' <- getRelPath name content <- liftIO $ readFile name' case parseProgram content of @@ -136,7 +136,7 @@ runStatementI (StatementI _ (Include name injectVals)) = do vals' <- getVals if injectVals then putVals (vals' ++ vals) else putVals vals -runStatementI (StatementI _ DoNothing) = liftIO $ putStrLn "Do Nothing?" +runStatementI (StatementI _ _ DoNothing) = liftIO $ putStrLn "Do Nothing?" runSuite :: [StatementI] -> StateC () runSuite = mapM_ runStatementI diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index ded2755..7ea82d3 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) where import Prelude(Char, Either, String, Maybe(Just, Nothing), Monad, return, fmap, ($), (>>), Bool(False, True), map) -import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, SourceName, ParseError, many, noneOf, Line, (<|>), ()) +import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, sourceColumn, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, SourceName, ParseError, many, noneOf, Line, Column, (<|>), ()) import Text.Parsec.Prim (ParsecT) @@ -92,22 +92,24 @@ suite = (fmap return computation <|> do throwAway :: GenParser Char st StatementI throwAway = do line <- lineNumber + column <- columnNumber _ <- genSpace _ <- oneOf "%*" _ <- genSpace _ <- computation - return $ StatementI line DoNothing + return $ StatementI line column DoNothing -- An include! Basically, inject another openscad file here... include :: GenParser Char st StatementI include = (do line <- lineNumber + column <- columnNumber injectVals <- (string "include" >> return True ) <|> (string "use" >> return False) _ <- stringGS " < " filename <- many (noneOf "<> ") _ <- stringGS " > " - return $ StatementI line $ Include filename injectVals + return $ StatementI line column $ Include filename injectVals ) "include " -- | An assignment (parser) @@ -115,48 +117,53 @@ assignment :: GenParser Char st StatementI assignment = ("assignment " ?:) $ do line <- lineNumber + column <- columnNumber lvalue <- patternMatcher _ <- stringGS " = " valExpr <- expr0 - return $ StatementI line $ lvalue := valExpr + return $ StatementI line column $ lvalue := valExpr -- | A function declaration (parser) function :: GenParser Char st StatementI function = ("function " ?:) $ do line <- lineNumber + column <- columnNumber varSymb <- string "function" >> space >> genSpace >> variableSymb _ <- stringGS " ( " argVars <- sepBy patternMatcher (stringGS " , ") _ <- stringGS " ) = " valExpr <- expr0 - return $ StatementI line $ Name varSymb := LamE argVars valExpr + return $ StatementI line column $ Name varSymb := LamE argVars valExpr -- | An echo (parser) echo :: GenParser Char st StatementI echo = do line <- lineNumber + column <- columnNumber _ <- stringGS "echo ( " exprs <- expr0 `sepBy` stringGS " , " _ <- stringGS " ) " - return $ StatementI line $ Echo exprs + return $ StatementI line column $ Echo exprs ifStatementI :: GenParser Char st StatementI ifStatementI = "if " ?: do line <- lineNumber + column <- columnNumber _ <- stringGS "if ( " bexpr <- expr0 _ <- stringGS " ) " sTrueCase <- suite _ <- genSpace sFalseCase <- (stringGS "else " >> suite ) *<|> return [] - return $ StatementI line $ If bexpr sTrueCase sFalseCase + return $ StatementI line column $ If bexpr sTrueCase sFalseCase forStatementI :: GenParser Char st StatementI forStatementI = "for " ?: do line <- lineNumber + column <- columnNumber -- a for loop is of the form: -- for ( vsymb = vexpr ) loops -- eg. for ( a = [1,2,3] ) {echo(a); echo "lol";} @@ -167,30 +174,32 @@ forStatementI = vexpr <- expr0 _ <- stringGS " ) " loopContent <- suite - return $ StatementI line $ For lvalue vexpr loopContent + return $ StatementI line column $ For lvalue vexpr loopContent -- parse a call to a module. userModule :: GenParser Char st StatementI userModule = do line <- lineNumber + column <- columnNumber name <- variableSymb _ <- genSpace args <- moduleArgsUnit _ <- genSpace s <- suite *<|> (stringGS " ; " >> return []) - return $ StatementI line $ ModuleCall name args s + return $ StatementI line column $ ModuleCall name args s -- declare a module. userModuleDeclaration :: GenParser Char st StatementI userModuleDeclaration = do line <- lineNumber + column <- columnNumber _ <- stringGS "module " newModuleName <- variableSymb _ <- genSpace args <- moduleArgsUnitDecl _ <- genSpace s <- suite - return $ StatementI line $ NewModule newModuleName args s + return $ StatementI line column $ NewModule newModuleName args s -- parse the arguments passed to a module. moduleArgsUnit :: GenParser Char st [(Maybe String, Expr)] @@ -250,9 +259,9 @@ lineNumber :: forall s u (m :: * -> *). lineNumber = fmap sourceLine getPosition --FIXME: use the below function to improve error reporting. -{- + -- find the column number. SHOULD be used when generating errors. columnNumber :: forall s u (m :: * -> *). Monad m => ParsecT s u m Column columnNumber = fmap sourceColumn getPosition --} + diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 9d87861..fd9463d 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -324,7 +324,6 @@ scale = moduleWithSuite "scale" $ \children -> do example "scale(2) square(5);" example "scale([2,3]) square(5);" example "scale([2,3,4]) cube(5);" - v <- argument "v" `doc` "vector or scalar to scale by" diff --git a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs index ce8c07f..e077789 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs @@ -67,8 +67,8 @@ getRelPath relPath = do path <- getPath return $ path relPath -errorC :: forall (m :: * -> *) a. (Show a, MonadIO m) => a -> String -> m () -errorC lineN err = liftIO $ putStrLn $ "At " ++ show lineN ++ ": " ++ err +errorC :: forall (m :: * -> *) a. (Show a, MonadIO m) => a -> a -> String -> m () +errorC lineN columnN err = liftIO $ putStrLn $ "On line " ++ show lineN ++ ", column " ++ show columnN ++ ": " ++ err mapMaybeM :: forall t (m :: * -> *) a. Monad m => (t -> m a) -> Maybe t -> m (Maybe a) mapMaybeM f (Just a) = do From 7ff740ca20ccd348c88078fc0f4ecf3fb9d71825 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 20 May 2018 05:13:54 +0100 Subject: [PATCH 084/227] remove unneeded import (sourceName). --- Graphics/Implicit/ExtOpenScad/Parser/Statement.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index 7ea82d3..f7e9dca 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) where import Prelude(Char, Either, String, Maybe(Just, Nothing), Monad, return, fmap, ($), (>>), Bool(False, True), map) -import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, sourceColumn, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, SourceName, ParseError, many, noneOf, Line, Column, (<|>), ()) +import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, sourceColumn, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, ParseError, many, noneOf, Line, Column, (<|>), ()) import Text.Parsec.Prim (ParsecT) From 57959310d76dfb5f938c6bb31229a76d66d2689b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 20 May 2018 05:14:39 +0100 Subject: [PATCH 085/227] add column numbers to statement interpreter calls. --- tests/ParserSpec/Statement.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 7246f79..aa00d5a 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -31,10 +31,10 @@ parsesAsError :: String -> Expectation parsesAsError source = parseProgram source `shouldSatisfy` isLeft single :: Statement StatementI -> [StatementI] -single st = [StatementI 1 st] +single st = [StatementI 1 1 st] call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI -call name args stmts = StatementI 1 (ModuleCall name args stmts) +call name args stmts = StatementI 1 1 (ModuleCall name args stmts) -- test a simple if block. ifSpec :: Spec From 85b7204a2548ed270e3df6ed9c181c8bac744235 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 24 May 2018 03:49:23 +0100 Subject: [PATCH 086/227] add comment. --- Graphics/Implicit/ExtOpenScad/Parser/Statement.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index f7e9dca..3fad7aa 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -13,8 +13,8 @@ module Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) where import Prelude(Char, Either, String, Maybe(Just, Nothing), Monad, return, fmap, ($), (>>), Bool(False, True), map) +-- We use parsec to parse. import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, sourceColumn, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, ParseError, many, noneOf, Line, Column, (<|>), ()) - import Text.Parsec.Prim (ParsecT) import Data.Functor.Identity(Identity) From 6133a5cd2b2ee2f540ddb2d14b20bff8733ceebc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 24 May 2018 03:49:59 +0100 Subject: [PATCH 087/227] use function composition to use pointfree style. --- Graphics/Implicit/ExtOpenScad/Util/OVal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index 9182227..023880b 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -42,7 +42,7 @@ instance OTypeMirror ℝ where instance OTypeMirror ℕ where fromOObj (ONum n) = if n == fromInteger (floor n) then Just (floor n) else Nothing fromOObj _ = Nothing - toOObj a = ONum $ fromIntegral a + toOObj = ONum . fromIntegral instance OTypeMirror Bool where fromOObj (OBool b) = Just b From 5b1dc8c2185bdb97847644dc080a5c0a20b82d7d Mon Sep 17 00:00:00 2001 From: Sergey Alirzaev Date: Fri, 25 May 2018 02:53:24 +0000 Subject: [PATCH 088/227] README: openscad supports variable assignment already --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04582a4..9baf3cf 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ linear_extrude (height = 40, center=true, twist=90, r=5){ ![A rounded twisted extrusion](http://faikvm.com/ImplicitCAD/example7.png) -ImplicitCAD also provides full programmatic functionality, like variable assignment in loops, which are sadly absent in OpenSCAD. For example, the trivial program: +ImplicitCAD also provides full programmatic functionality, like variable assignment in loops. For example, the trivial program: ```c // Example8.escad -- variable assignment in loops. From 1a15b6fcd942c4a83ff905b39bf6e80be60e3e66 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 9 Jun 2018 18:29:46 +0100 Subject: [PATCH 089/227] better variable names, better comments, less qualified imports, and more specific imports. --- Graphics/Implicit/Export.hs | 17 +++++++------- Graphics/Implicit/Export/PolylineFormats.hs | 2 +- Graphics/Implicit/ExtOpenScad.hs | 19 ++++++++-------- Graphics/Implicit/ExtOpenScad/Default.hs | 4 ++-- .../Implicit/ExtOpenScad/Eval/Statement.hs | 16 +++++++++----- Graphics/Implicit/ExtOpenScad/Primitives.hs | 22 ++++++++++++------- .../Implicit/ExtOpenScad/Util/ArgParser.hs | 19 ++++++++++------ Graphics/Implicit/ExtOpenScad/Util/StateC.hs | 4 ++-- Graphics/Implicit/ObjectUtil.hs | 3 ++- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 14 ++++++------ 10 files changed, 68 insertions(+), 52 deletions(-) diff --git a/Graphics/Implicit/Export.hs b/Graphics/Implicit/Export.hs index a2cf92b..166fae1 100644 --- a/Graphics/Implicit/Export.hs +++ b/Graphics/Implicit/Export.hs @@ -24,26 +24,26 @@ import qualified Data.ByteString.Lazy as LBS (writeFile) -- Import instances of DiscreteApproxable... import Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAprox) --- Object formats +-- Output file formats. import qualified Graphics.Implicit.Export.PolylineFormats as PolylineFormats (svg, hacklabLaserGCode) import qualified Graphics.Implicit.Export.TriangleMeshFormats as TriangleMeshFormats (stl, binaryStl, jsTHREE) import qualified Graphics.Implicit.Export.NormedTriangleMeshFormats as NormedTriangleMeshFormats (obj) import qualified Graphics.Implicit.Export.SymbolicFormats as SymbolicFormats (scad2, scad3) import qualified Codec.Picture as ImageFormatCodecs (DynamicImage, savePngImage) --- Write an object using the given format function. +-- Write an object to a file with LazyText IO, using the given format writer function. writeObject :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution - -> (aprox -> Text) -- ^ File Format (Function that formats) + -> (aprox -> Text) -- ^ File Format Writer (Function that formats) -> FilePath -- ^ File Name -> obj -- ^ Object to render -> IO () -- ^ Writing Action! -writeObject res format filename obj = +writeObject res formatWriter filename obj = let - aprox = formatObject res format obj + aprox = formatObject res formatWriter obj in LT.writeFile filename aprox --- Write an object using the given format writer. +-- Serialize an object using the given format writer, which takes the filename and writes to it.. writeObject' :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution -> (FilePath -> aprox -> IO ()) -- ^ File Format writer @@ -53,12 +53,13 @@ writeObject' :: (DiscreteAproxable obj aprox) writeObject' res formatWriter filename obj = formatWriter filename (discreteAprox res obj) +-- Serialize an object using the given format writer. no file target implied. formatObject :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution - -> (aprox -> Text) -- ^ File Format (Function that formats) + -> (aprox -> Text) -- ^ File Format Writer (Function that formats) -> obj -- ^ Object to render -> Text -- ^ Resulting lazy ByteString -formatObject res format = format . discreteAprox res +formatObject res formatWriter = formatWriter . discreteAprox res writeSVG :: forall obj. DiscreteAproxable obj [Polyline] => ℝ -> FilePath -> obj -> IO () writeSVG res = writeObject res PolylineFormats.svg diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index 79314f0..226f770 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -18,7 +18,7 @@ import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, mempty, toLazyT import Text.Blaze.Svg.Renderer.Text (renderSvg) import Text.Blaze.Svg11 ((!),docTypeSvg,g,polyline,toValue) import Text.Blaze.Internal (stringValue) -import qualified Text.Blaze.Svg11.Attributes as A +import qualified Text.Blaze.Svg11.Attributes as A (version, width, height, viewbox, points, stroke, strokeWidth, fill) import Data.List (sortBy) diff --git a/Graphics/Implicit/ExtOpenScad.hs b/Graphics/Implicit/ExtOpenScad.hs index 0e0e8dc..7a60cf9 100644 --- a/Graphics/Implicit/ExtOpenScad.hs +++ b/Graphics/Implicit/ExtOpenScad.hs @@ -5,8 +5,7 @@ -- FIXME: why are these required? {-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-} --- We'd like to parse openscad code, with some improvements, for backwards compatability. - +-- An executor, which parses openscad code, and executes it. module Graphics.Implicit.ExtOpenScad (runOpenscad) where import Prelude(String, Either(Left, Right), IO, ($), fmap) @@ -18,13 +17,13 @@ import Graphics.Implicit.ExtOpenScad.Eval.Statement (runStatementI) import Graphics.Implicit.ExtOpenScad.Default (defaultObjects) import Graphics.Implicit.ExtOpenScad.Util.OVal (divideObjs) -import qualified Text.Parsec.Error as Parsec (ParseError) -import qualified Control.Monad as Monad (mapM_) -import qualified Control.Monad.State as State (runStateT) -import qualified System.Directory as Dir (getCurrentDirectory) +import Text.Parsec.Error (ParseError) +import Control.Monad (mapM_) +import Control.Monad.State (runStateT) +import System.Directory (getCurrentDirectory) -- Small wrapper to handle parse errors, etc. -runOpenscad :: String -> Either Parsec.ParseError (IO (VarLookup, [SymbolicObj2], [SymbolicObj3])) +runOpenscad :: String -> Either ParseError (IO (VarLookup, [SymbolicObj2], [SymbolicObj3])) runOpenscad source = let initial = defaultObjects @@ -36,7 +35,7 @@ runOpenscad source = Right sts -> Right $ fmap rearrange $ (\sts' -> do - path <- Dir.getCurrentDirectory - State.runStateT sts' (initial, [], path, (), () ) + path <- getCurrentDirectory + runStateT sts' (initial, [], path, (), () ) ) - $ Monad.mapM_ runStatementI sts + $ mapM_ runStatementI sts diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 9a0a7c7..172ad3d 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -5,11 +5,11 @@ -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} --- We'd like to parse openscad code, with some improvements, for backwards compatability. - +-- We'd like to parse openscad-ish code, with some improvements, for backwards compatability. module Graphics.Implicit.ExtOpenScad.Default (defaultObjects) where +-- be explicit about where we pull things in from. import Prelude (String, Bool(True, False), Maybe(Just, Nothing), ($), (++), map, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, abs, signum, fromInteger, (.), floor, ceiling, round, exp, log, sqrt, max, min, atan2, (**), flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), otherwise) import Graphics.Implicit.Definitions (ℝ, ℕ) diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs index 92bd690..7e40e5f 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs @@ -2,6 +2,7 @@ -- Copyright 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE +-- FIXME: why is this required? {-# LANGUAGE ScopedTypeVariables #-} module Graphics.Implicit.ExtOpenScad.Eval.Statement (runStatementI) where @@ -25,10 +26,13 @@ import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Data.Maybe(fromMaybe) -import qualified Data.Map as Map +import Data.Map (union, fromList) + import Control.Monad (forM_, forM, mapM_) + import Control.Monad.State (get, liftIO, mapM, runStateT, (>>)) -import qualified System.FilePath as FilePath + +import System.FilePath (takeDirectory) -- Run statements out of the OpenScad file. runStatementI :: StatementI -> StateC () @@ -38,7 +42,7 @@ runStatementI (StatementI lineN columnN (pat := expr)) = do let posMatch = matchPat pat val case (getErrors val, posMatch) of (Just err, _ ) -> errorC lineN columnN err - (_, Just match) -> modifyVarLookup $ Map.union match + (_, Just match) -> modifyVarLookup $ union match (_, Nothing ) -> errorC lineN columnN "pattern match failed in assignment" runStatementI (StatementI lineN columnN (Echo exprs)) = do @@ -57,7 +61,7 @@ runStatementI (StatementI lineN columnN (For pat expr loopContent)) = do (_, OList vals) -> forM_ vals $ \v -> case matchPat pat v of Just match -> do - modifyVarLookup $ Map.union match + modifyVarLookup $ union match runSuite loopContent Nothing -> return () _ -> return () @@ -99,7 +103,7 @@ runStatementI (StatementI lineN columnN (NewModule name argTemplate suite)) = do _ -> OUndefined newNameVals' = newNameVals ++ [("children", children),("child", child), ("childBox", childBox)] -} - varlookup' = Map.union (Map.fromList newNameVals) varlookup + varlookup' = union (fromList newNameVals) varlookup suiteVals = runSuiteCapture varlookup' path suite return suiteVals @@ -129,7 +133,7 @@ runStatementI (StatementI _ _ (Include name injectVals)) = do content <- liftIO $ readFile name' case parseProgram content of Left e -> liftIO $ putStrLn $ "Error parsing " ++ name ++ ":" ++ show e - Right sts -> withPathShiftedBy (FilePath.takeDirectory name) $ do + Right sts -> withPathShiftedBy (takeDirectory name) $ do vals <- getVals putVals [] runSuite sts diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index fd9463d..d3f2b58 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -22,15 +22,17 @@ import Graphics.Implicit.ExtOpenScad.Util.ArgParser (doc, defaultTo, argument, e import Graphics.Implicit.ExtOpenScad.Util.OVal (caseOType, divideObjs, (<||>)) +-- note the use of a qualified import, so we don't have the functions in this file conflict with what we're importing. import qualified Graphics.Implicit.Primitives as Prim (sphere, rect3R, rectR, translate, circle, polygonR, extrudeR, cylinder2, union, unionR, intersect, intersectR, difference, differenceR, rotate, rotate3V, rotate3, scale, extrudeR, extrudeRM, rotateExtrude, shell, pack3, pack2) import Data.Maybe (isNothing) -import qualified Control.Monad as Monad +import Control.Monad (mplus) import Data.VectorSpace (VectorSpace, Scalar, (*^)) import GHC.Real (RealFrac) +-- The only thing exported here. basically, an array of ... ? primitives :: [(String, [OVal] -> ArgParser (IO [OVal]) )] primitives = [ sphere, cube, square, cylinder, circle, polygon, union, difference, intersect, translate, scale, rotate, extrude, pack, shell, rotateExtrude, unit ] @@ -406,16 +408,20 @@ rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do -{-rotateExtrudeStatement = moduleWithSuite "rotate_extrude" $ \suite -> do - h <- realArgument "h" - center <- boolArgumentWithDefault "center" False - twist <- realArgumentWithDefault 0.0 - r <- realArgumentWithDefault "r" 0.0 +{- +rotateExtrudeStatement :: (String, [OVal] -> ArgParser (IO [OVal])) +rotateExtrudeStatement = moduleWithSuite "rotate_extrude" $ \suite -> do + -- arguments + h :: ℝ <- realArgument "h" + center :: Bool <- boolArgumentWithDefault "center" False + twist :: ℝ <- realArgumentWithDefault 0.0 + r :: ℝ <- realArgumentWithDefault "r" 0.0 + getAndModUpObj2s suite (\obj -> extrudeRMod r (\θ (x,y) -> (x*cos(θ)+y*sin(θ), y*cos(θ)-x*sin(θ)) ) obj h) -} shell :: (String, [OVal] -> ArgParser (IO [OVal])) -shell = moduleWithSuite "shell" $ \children-> do +shell = moduleWithSuite "shell" $ \children -> do w :: ℝ <- argument "w" `doc` "width of the shell..." @@ -487,7 +493,7 @@ unit = moduleWithSuite "unit" $ \children -> do --------------- (<|>) :: ArgParser a -> ArgParser a -> ArgParser a -(<|>) = Monad.mplus +(<|>) = mplus moduleWithSuite :: t -> t1 -> (t, t1) moduleWithSuite name modArgMapper = (name, modArgMapper) diff --git a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs index 5bd170c..88b2b00 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs @@ -10,14 +10,19 @@ module Graphics.Implicit.ExtOpenScad.Util.ArgParser (argument, doc, defaultTo, example, test, eulerCharacteristic, argMap) where -import Prelude(String, Maybe(Just, Nothing), ($), (++), concat, show, error, return, map, snd, filter, (.), fst, foldl1, not, null, (&&)) +import Prelude(String, Maybe(Just, Nothing), ($), (++), concat, show, error, return, map, snd, filter, (.), fst, foldl1, not, (&&)) +import qualified Prelude as Prelude (null) + import Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch, APTerminator, APFailIf, APExample), OVal (OError), TestInvariant(EulerCharacteristic)) import Graphics.Implicit.ExtOpenScad.Util.OVal (fromOObj, toOObj, OTypeMirror) import Graphics.Implicit.Definitions(ℕ) -import qualified Data.Map as Map +-- imported twice, once qualified. null from Data.Map conflicts with null from Prelude. +import Data.Map (fromList, Map, lookup, delete) +import qualified Data.Map as Map (null) + import Data.Maybe (isNothing, fromJust, isJust) import Control.Arrow(first) @@ -72,12 +77,12 @@ argMap :: -> ArgParser a -- ^ ArgParser to apply them to -> (Maybe a, [String]) -- ^ (result, error messages) -argMap args = argMap2 unnamedArgs (Map.fromList namedArgs) where +argMap args = argMap2 unnamedArgs (fromList namedArgs) where unnamedArgs = map snd $ filter (isNothing . fst) args namedArgs = map (first fromJust) $ filter (isJust . fst) args -argMap2 :: [OVal] -> Map.Map String OVal -> ArgParser a -> (Maybe a, [String]) +argMap2 :: [OVal] -> Map String OVal -> ArgParser a -> (Maybe a, [String]) argMap2 uArgs nArgs (APBranch branches) = foldl1 merge solutions where @@ -89,10 +94,10 @@ argMap2 uArgs nArgs (APBranch branches) = merge (Nothing, _) a = a argMap2 unnamedArgs namedArgs (AP name fallback _ f) = - case Map.lookup name namedArgs of + case lookup name namedArgs of Just a -> argMap2 unnamedArgs - (Map.delete name namedArgs) + (delete name namedArgs) (f a) Nothing -> case unnamedArgs of x:xs -> argMap2 xs namedArgs (f x) @@ -101,7 +106,7 @@ argMap2 unnamedArgs namedArgs (AP name fallback _ f) = Nothing -> (Nothing, ["No value and no default for argument " ++ name]) argMap2 a b (APTerminator val) = - (Just val, ["unused arguments" | not (null a && Map.null b)]) + (Just val, ["unused arguments" | not (Prelude.null a && Map.null b)]) argMap2 a b (APFailIf testval err child) = if testval diff --git a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs index e077789..f8d2ac6 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs @@ -15,7 +15,7 @@ import Prelude(FilePath, IO, String, Maybe(Just, Nothing), Show, Monad, fmap, (. import Graphics.Implicit.ExtOpenScad.Definitions(VarLookup, OVal) -import qualified Data.Map as Map +import Data.Map (lookup) import Control.Monad.State (StateT, get, put, modify, liftIO) import System.FilePath(()) import Control.Monad.IO.Class (MonadIO) @@ -33,7 +33,7 @@ modifyVarLookup = modify . (\f (a,b,c,d,e) -> (f a, b, c, d, e)) lookupVar :: String -> StateC (Maybe OVal) lookupVar name = do varlookup <- getVarLookup - return $ Map.lookup name varlookup + return $ lookup name varlookup pushVals :: [OVal] -> StateC () pushVals vals = modify (\(a,b,c,d,e) -> (a, vals ++ b,c,d,e)) diff --git a/Graphics/Implicit/ObjectUtil.hs b/Graphics/Implicit/ObjectUtil.hs index 42a68df..fac4e2d 100644 --- a/Graphics/Implicit/ObjectUtil.hs +++ b/Graphics/Implicit/ObjectUtil.hs @@ -2,10 +2,11 @@ -- Copyright (C) 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --- create a module that is just wrapping up these functions. +-- create a module that just wraps the functions in the ObjectUtil directory. module Graphics.Implicit.ObjectUtil(getImplicit3, getImplicit2, getBox3, getBox2) where +-- as there is no real content here, we need no content from the prelude. import Prelude() import Graphics.Implicit.ObjectUtil.GetImplicit3 (getImplicit3) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 97c6dae..3950ec9 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -15,8 +15,8 @@ import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR)) import Graphics.Implicit.MathUtil (rmaximum, rminimum, rmax) -import qualified Data.Maybe as Maybe -import qualified Data.Either as Either +import Data.Maybe (fromMaybe, isJust) +import qualified Data.Either as Either (either) import Data.VectorSpace ((^-^), (^+^), (^*), (<.>), normalized) -- Use getImplicit2 for handling extrusion of 2D shapes to 3D. @@ -125,9 +125,9 @@ getImplicit3 (ExtrudeR r symbObj h) = getImplicit3 (ExtrudeRM r twist scale translate symbObj height) = let obj = getImplicit2 symbObj - twist' = Maybe.fromMaybe (const 0) twist - scale' = Maybe.fromMaybe (const 1) scale - translate' = Maybe.fromMaybe (const (0,0)) translate + twist' = fromMaybe (const 0) twist + scale' = fromMaybe (const 1) scale + translate' = fromMaybe (const (0,0)) translate height' (x,y) = case height of Left n -> n Right f -> f (x,y) @@ -155,8 +155,8 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = k = tau / 360 totalRotation' = totalRotation*k obj = getImplicit2 symbObj - capped = Maybe.isJust round - round' = Maybe.fromMaybe 0 round + capped = isJust round + round' = fromMaybe 0 round translate' :: ℝ -> ℝ2 translate' = Either.either (\(a,b) θ -> (a*θ/totalRotation', b*θ/totalRotation')) From 48c888c2a7f83ddcdc896160efc5f8bcfcd59be3 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 9 Jun 2018 18:36:47 +0100 Subject: [PATCH 090/227] fromInteger -> fromIntegral --- Graphics/Implicit/ExtOpenScad/Util/OVal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index 023880b..e0302e3 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -40,7 +40,7 @@ instance OTypeMirror ℝ where toOObj = ONum instance OTypeMirror ℕ where - fromOObj (ONum n) = if n == fromInteger (floor n) then Just (floor n) else Nothing + fromOObj (ONum n) = if n == fromIntegral (floor n) then Just (floor n) else Nothing fromOObj _ = Nothing toOObj = ONum . fromIntegral From 3b4b669bef2ba7e1c0ea5d4afa8900f22471065b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 9 Jun 2018 18:38:21 +0100 Subject: [PATCH 091/227] better option handling, use uppercase instead of lowercase, and better comments. --- implicit.cabal | 149 +++++++++++++++++++++++++++---------------------- 1 file changed, 81 insertions(+), 68 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index d51cb9a..edaadd4 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -1,22 +1,23 @@ -name: implicit -version: 0.2.0 -cabal-version: >= 1.8 -synopsis: Math-inspired programmatic 2&3D CAD: CSG, bevels, and shells; gcode export.. -description: A math-inspired programmatic CAD library in haskell. +Name: implicit +Version: 0.2.0 +Cabal-version: >= 1.8 +Tested-with: GHC >= 8.2 +Build-type: Simple +Synopsis: A Math-inspired programmatic 2&3D CAD system: CSG, bevels, and shells; gcode export.. +Description: A math-inspired programmatic CAD library in haskell. Build objects with constructive solid geometry, bevels, shells and more in 2D & 3D. Then export to SVGs, STLs, or produce gcode directly! -license: AGPL-3 -license-file: LICENSE -author: Christopher Olah -maintainer: Julia Longtin -homepage: http://kalli1.faikvm.com/ImplicitCAD/Stable -build-type: Simple -category: Graphics +License: AGPL-3 +License-file: LICENSE +Author: Julia Longtin +Maintainer: Julia Longtin +Homepage: http://implicitcad.org/ +Category: Graphics -library +Library - build-depends: + Build-depends: base >= 3 && < 5, filepath, directory, @@ -43,17 +44,17 @@ library transformers, hspec - ghc-options: + Ghc-options: -Wall --- for debugging only. + -optc-O3 + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. -Wextra -Weverything - -optc-O3 --- cannot use, we use infinity in some calculations. --- -optc-ffast-math - extensions: - exposed-modules: + Extensions: + Exposed-modules: Graphics.Implicit Graphics.Implicit.Definitions Graphics.Implicit.Primitives @@ -61,21 +62,19 @@ library Graphics.Implicit.MathUtil Graphics.Implicit.ExtOpenScad Graphics.Implicit.ObjectUtil - -- Note that these modules are only temporarily exposed, to - -- allow coding the unit tests against the current parser - -- interface. + -- these modules are exposed for the unit tests against the parser interface. Graphics.Implicit.ExtOpenScad.Parser.Statement Graphics.Implicit.ExtOpenScad.Parser.Expr Graphics.Implicit.ExtOpenScad.Definitions - -- these are exported for Benchmark. + -- these are exposed for Benchmark. Graphics.Implicit.Export.SymbolicObj2 Graphics.Implicit.Export.SymbolicObj3 - -- these are exported for implicitsnap. + -- these are exposed for implicitsnap. Graphics.Implicit.Export.TriangleMeshFormats Graphics.Implicit.Export.PolylineFormats Graphics.Implicit.Export.DiscreteAproxable - other-modules: + Other-modules: Graphics.Implicit.ObjectUtil.GetBox2 Graphics.Implicit.ObjectUtil.GetBox3 Graphics.Implicit.ObjectUtil.GetImplicit2 @@ -107,11 +106,11 @@ library Graphics.Implicit.Export.Render.TesselateLoops Graphics.Implicit.Export.Render.HandlePolylines -executable extopenscad +Executable extopenscad - main-is: extopenscad.hs - hs-source-dirs: programs - build-depends: + Main-is: extopenscad.hs + Hs-source-dirs: programs + Build-depends: base, containers, vector-space, @@ -119,15 +118,16 @@ executable extopenscad parallel, optparse-applicative >= 0.10.0, implicit - ghc-options: + Ghc-options: -threaded - -- see GHC manual 8.2.1 section 6.5.1. - -feager-blackholing -rtsopts -Wall - -Weverything -optc-O3 - -optc-ffast-math + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. + -Wextra + -Weverything -- FIXME: does not compile. --Executable docgen @@ -161,11 +161,11 @@ executable extopenscad -- -rtsopts -- -funfolding-use-threshold=16 -- -fspec-constr-count=10 - -executable implicitsnap - main-is: implicitsnap.hs - hs-source-dirs: programs - build-depends: + +Executable implicitsnap + Main-is: implicitsnap.hs + Hs-source-dirs: programs + Build-depends: base, vector-space, text, @@ -188,18 +188,21 @@ executable implicitsnap silently, transformers, implicit - ghc-options: + Ghc-options: -threaded -rtsopts -Wall - -Weverything -optc-O3 - -optc-ffast-math + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. + -Wextra + -Weverything -executable Benchmark - main-is: Benchmark.hs - hs-source-dirs: programs - build-depends: +Executable Benchmark + Main-is: Benchmark.hs + Hs-source-dirs: programs + Build-depends: base, text, JuicyPixels, @@ -219,34 +222,44 @@ executable Benchmark criterion, transformers, implicit - ghc-options: + Ghc-options: -threaded -rtsopts -Wall - -Weverything -optc-O3 - -optc-ffast-math + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing +-- for debugging. + -Wextra + -Weverything -test-suite test-implicit - type: exitcode-stdio-1.0 - build-depends: base, mtl, containers, hspec, parsec, implicit - main-is: Main.hs - hs-source-dirs: tests - ghc-options: +Test-suite test-implicit + Type: exitcode-stdio-1.0 + Build-depends: base, mtl, containers, hspec, parsec, implicit + Main-is: Main.hs + Hs-source-dirs: tests + Ghc-options: -Wall - -Weverything -optc-O3 + -- for debugging. + -Wextra + -Weverything -benchmark parser-bench - type: exitcode-stdio-1.0 - build-depends: base, criterion, random, parsec, implicit - main-is: ParserBench.hs - ghc-options: +Benchmark parser-bench + Type: exitcode-stdio-1.0 + Build-depends: base, criterion, random, parsec, implicit + Main-is: ParserBench.hs + Ghc-options: + -threaded + -rtsopts -Wall - -Weverything -optc-O3 - -optc-ffast-math + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. + -Wextra + -Weverything -source-repository head - type: git - location: https://github.com/colah/ImplicitCAD.git +Source-repository head + Type: git + Location: https://github.com/colah/ImplicitCAD.git From e7d35e81c5875b2d3a662536c47063ea17001900 Mon Sep 17 00:00:00 2001 From: elimohl Date: Mon, 11 Jun 2018 22:33:06 +0300 Subject: [PATCH 092/227] Better bounding box calculation. Fix #202 --- Graphics/Implicit/ObjectUtil/GetBox3.hs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index 6575a82..0afb111 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.ObjectUtil.GetBox3 (getBox3) where import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), Maybe(Nothing, Just), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, sqrt, (>), (&&), head, (*), (<), abs, either, error, const, otherwise) -import Graphics.Implicit.Definitions (ℝ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), (⋯*)) +import Graphics.Implicit.Definitions (ℝ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), (⋯*)) import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) import Data.Maybe (fromMaybe) @@ -81,10 +81,17 @@ getBox3 (Scale3 s symbObj) = (sbx,sby,sbz) = s ⋯* b in ((min sax sbx, min say sby, min saz sbz), (max sax sbx, max say sby, max saz sbz)) -getBox3 (Rotate3 _ symbObj) = ( (-d, -d, -d), (d, d, d) ) - where - ((x1,y1, z1), (x2,y2, z2)) = getBox3 symbObj - d = (sqrt 3 *) . maximum $ map abs [x1, x2, y1, y2, z1, z2] +getBox3 (Rotate3 (a, b, c) symbObj) = + let + ((x1, y1, z1), (x2, y2, z2)) = getBox3 symbObj + rotate v1 w1 v2 w2 angle = getBox2(Rotate2 angle $ RectR 0 (v1, w1) (v2, w2)) + ((y1', z1'), (y2', z2')) = rotate y1 z1 y2 z2 a + ((z1'', x1'), (z2'', x2')) = rotate z1' x1 z2' x2 b + ((x1'', y1''), (x2'', y2'')) = rotate x1' y1' x2' y2' c + (xs, ys, zs) = ([x1'', x2''], [y1'', y2''], [z1'', z2'']) + in + ((minimum xs, minimum ys, minimum zs), (maximum xs, maximum ys, maximum zs)) + getBox3 (Rotate3V _ v symbObj) = getBox3 (Rotate3 v symbObj) -- Boundary mods getBox3 (Shell3 w symbObj) = From 77780f6f73454ce5660f99e6ac57cb1497af8cfd Mon Sep 17 00:00:00 2001 From: elimohl Date: Sun, 24 Jun 2018 23:29:25 +0300 Subject: [PATCH 093/227] Change rotation order to be compatible with OpenSCAD. Fix #208 --- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index c50db8f..38ae210 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -88,7 +88,7 @@ getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = rotateXY :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) rotateXY θ obj' (x,y,z) = obj' ( x*cos θ + y*sin θ, y*cos θ - x*sin θ, z) in - rotateYZ yz . rotateZX zx $ rotateXY xy obj + rotateXY xy $ rotateZX zx $ rotateYZ yz obj getImplicit3 (Rotate3V θ axis symbObj) = let axis' = normalized axis From ed9e3d3d203be8a28fb24ab6f20593688d927fd5 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 27 Jun 2018 05:20:28 +0100 Subject: [PATCH 094/227] make docgen.hs compile again. --- docgen.hs | 16 ++++++++----- implicit.cabal | 65 ++++++++++++++++++++++++++------------------------ 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/docgen.hs b/docgen.hs index b716c83..901db7a 100644 --- a/docgen.hs +++ b/docgen.hs @@ -6,8 +6,10 @@ -- FIXME: this doesn't work. looks like it broke badly when ArgParser became a Monad. import Graphics.Implicit.ExtOpenScad.Primitives (primitives) -import Graphics.Implicit.ExtOpenScad.Util.ArgParser +-- import Graphics.Implicit.ExtOpenScad.Util.ArgParser() +import Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP,APFailIf,APExample,APTest,APTerminator,APBranch)) +import qualified Control.Exception as Ex (catch, SomeException) import Control.Monad isExample (ExampleDoc _ ) = True @@ -67,12 +69,14 @@ getArgParserDocs :: (ArgParser a) -- ^ ArgParser -> IO [DocPart] -- ^ Docs (sadly IO wrapped) -getArgParserDocs (ArgParser name fallback doc fnext) = +getArgParserDocs (AP name fallback doc fnext) = do otherDocs <- Ex.catch (getArgParserDocs $ fnext undefined) (\(e :: Ex.SomeException) -> return []) return $ (ArgumentDoc name (fmap show fallback) doc):otherDocs -getArgParserDocs (ArgParserExample str child) = +getArgParserDocs (APFailIf _ _ child) = getArgParserDocs child + +getArgParserDocs (APExample str child) = do childResults <- getArgParserDocs child return $ (ExampleDoc str) : childResults @@ -80,9 +84,9 @@ getArgParserDocs (ArgParserExample str child) = -- We try to look at as little as possible, to avoid the risk of triggering an error. -- Yay laziness! -getArgParserDocs (ArgParserTest _ _ child ) = getArgParserDocs child -getArgParserDocs (ArgParserFailIf _ _ child ) = getArgParserDocs child +getArgParserDocs (APTest _ _ child) = getArgParserDocs child -- To look at this one would almost certainly be death (exception) -getArgParserDocs (ArgParserTerminator _ ) = return [] +getArgParserDocs (APTerminator _) = return [] +getArgParserDocs (APBranch children) = return []-- mapM getArgParserDocs children diff --git a/implicit.cabal b/implicit.cabal index edaadd4..64dc06f 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -130,37 +130,40 @@ Executable extopenscad -Weverything -- FIXME: does not compile. ---Executable docgen +Executable docgen --- main-is: docgen.hs --- build-depends: --- base, --- vector-space, --- text, --- JuicyPixels, --- blaze-builder, --- blaze-svg, --- blaze-markup, --- parallel, --- deepseq, --- vector-space, --- monads-tf, --- bytestring, --- storable-endian, --- parsec, --- directory, --- containers, --- filepath, --- snap-core, --- snap-server, --- silently, --- transformers --- ghc-options: --- -optc-O3 --- -threaded --- -rtsopts --- -funfolding-use-threshold=16 --- -fspec-constr-count=10 + main-is: docgen.hs + build-depends: + base, + vector-space, + text, + JuicyPixels, + blaze-builder, + blaze-svg, + blaze-markup, + parallel, + deepseq, + vector-space, + monads-tf, + bytestring, + storable-endian, + parsec, + directory, + containers, + filepath, + snap-core, + snap-server, + silently, + transformers + ghc-options: + -Wall + -Weverything + -Wextra + -optc-O3 + -threaded + -rtsopts + -funfolding-use-threshold=16 + -fspec-constr-count=10 Executable implicitsnap Main-is: implicitsnap.hs @@ -229,7 +232,7 @@ Executable Benchmark -optc-O3 -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing --- for debugging. + -- for debugging. -Wextra -Weverything From 349ddf7afa3856b7645bf32ad61299c3f4bf1952 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 8 Jul 2018 20:52:24 +0100 Subject: [PATCH 095/227] spacing changes. --- Graphics/Implicit/ExtOpenScad/Definitions.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Definitions.hs b/Graphics/Implicit/ExtOpenScad/Definitions.hs index 8c7aa82..5b0c19f 100644 --- a/Graphics/Implicit/ExtOpenScad/Definitions.hs +++ b/Graphics/Implicit/ExtOpenScad/Definitions.hs @@ -76,8 +76,8 @@ instance Alternative ArgParser where type Symbol = String -data Pattern = Name Symbol - | ListP [Pattern] +data Pattern = Name Symbol + | ListP [Pattern] | Wild | Symbol :@ Pattern deriving (Show, Eq) From dae1bd8b042fb942cc100a94355a95cc3b3e5ba7 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 8 Jul 2018 20:54:46 +0100 Subject: [PATCH 096/227] change argument definition order for easier parsing by docgen. --- Graphics/Implicit/ExtOpenScad/Primitives.hs | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index d3f2b58..ed20011 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -32,7 +32,7 @@ import Control.Monad (mplus) import Data.VectorSpace (VectorSpace, Scalar, (*^)) import GHC.Real (RealFrac) --- The only thing exported here. basically, an array of ... ? +-- The only thing exported here. basically, a list of ... ? primitives :: [(String, [OVal] -> ArgParser (IO [OVal]) )] primitives = [ sphere, cube, square, cylinder, circle, polygon, union, difference, intersect, translate, scale, rotate, extrude, pack, shell, rotateExtrude, unit ] @@ -61,7 +61,12 @@ cube = moduleWithoutSuite "cube" $ do example "cube(size = [2,3,4], center = true, r = 0.5);" example "cube(4);" - -- arguments + -- arguments shared between forms + r :: ℝ <- argument "r" + `doc` "radius of rounding" + `defaultTo` 0 + + -- arguments (two forms) ((x1,x2), (y1,y2), (z1,z2)) <- do x :: Either ℝ ℝ2 <- argument "x" @@ -88,10 +93,6 @@ cube = moduleWithoutSuite "cube" $ do let (x,y, z) = either (\w -> (w,w,w)) id size return (toInterval center x, toInterval center y, toInterval center z) - r :: ℝ <- argument "r" - `doc` "radius of rounding" - `defaultTo` 0 - -- Tests test "cube(4);" `eulerCharacteristic` 2 @@ -108,7 +109,12 @@ square = moduleWithoutSuite "square" $ do example "square(size = [3,4], center = true, r = 0.5);" example "square(4);" - -- arguments + -- arguments shared between forms + r :: ℝ <- argument "r" + `doc` "radius of rounding" + `defaultTo` 0 + + -- arguments (two forms) ((x1,x2), (y1,y2)) <- do x :: Either ℝ ℝ2 <- argument "x" @@ -132,10 +138,6 @@ square = moduleWithoutSuite "square" $ do let (x,y) = either (\w -> (w,w)) id size return (toInterval center x, toInterval center y) - r :: ℝ <- argument "r" - `doc` "radius of rounding" - `defaultTo` 0 - -- Tests test "square(2);" `eulerCharacteristic` 0 @@ -427,7 +429,7 @@ shell = moduleWithSuite "shell" $ \children -> do return $ return $ objMap (Prim.shell w) (Prim.shell w) children --- Not a perenant solution! Breaks if can't pack. +-- Not a permanent solution! Breaks if can't pack. pack :: (String, [OVal] -> ArgParser (IO [OVal])) pack = moduleWithSuite "pack" $ \children -> do From 2b47de787c6278be3e4c1060740df09317a32789 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 8 Jul 2018 20:58:30 +0100 Subject: [PATCH 097/227] almost perfect. --- docgen.hs | 163 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 46 deletions(-) diff --git a/docgen.hs b/docgen.hs index 901db7a..81520d2 100644 --- a/docgen.hs +++ b/docgen.hs @@ -3,14 +3,13 @@ {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances, ScopedTypeVariables #-} --- FIXME: this doesn't work. looks like it broke badly when ArgParser became a Monad. +import Prelude(IO, Show, String, Int, Maybe(Just,Nothing), Eq, return, ($), show, fmap, (++), putStrLn, filter, zip, null, map, undefined, const, Bool(True,False), fst, snd, sequence, (.), concat, head, tail, sequence, length, (>), (/=), (+)) import Graphics.Implicit.ExtOpenScad.Primitives (primitives) --- import Graphics.Implicit.ExtOpenScad.Util.ArgParser() import Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP,APFailIf,APExample,APTest,APTerminator,APBranch)) import qualified Control.Exception as Ex (catch, SomeException) -import Control.Monad +import Control.Monad (forM_, mapM) isExample (ExampleDoc _ ) = True isExample _ = False @@ -18,35 +17,86 @@ isExample _ = False isArgument (ArgumentDoc _ _ _) = True isArgument _ = False -main = do - let names = map fst primitives - docs <- sequence $ map (getArgParserDocs.($ []).snd) primitives +isBranch (Branch _) = True +isBranch _ = False - forM_ (zip names docs) $ \(moduleName, moduleDocList) -> do - let - examples = filter isExample moduleDocList - arguments = filter isArgument moduleDocList - putStrLn moduleName - putStrLn (map (const '-') moduleName) - putStrLn "" - if not $ null examples then putStrLn "**Examples:**\n" else return () - forM_ examples $ \(ExampleDoc example) -> do - putStrLn $ " * `" ++ example ++ "`" - putStrLn "" - putStrLn "**Arguments:**\n" - forM_ arguments $ \(ArgumentDoc name posfallback description) -> - case (posfallback, description) of - (Nothing, "") -> do - putStrLn $ " * `" ++ name ++ "`" - (Just fallback, "") -> do - putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" - (Nothing, _) -> do - putStrLn $ " * `" ++ name ++ "`" - putStrLn $ " " ++ description - (Just fallback, _) -> do - putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" - putStrLn $ " " ++ description - putStrLn "" +dumpPrimitive :: String -> [DocPart] -> Int -> IO () +dumpPrimitive moduleName moduleDocList level = do + let + examples = filter isExample moduleDocList + arguments = filter isArgument moduleDocList + syntaxes = filter isBranch moduleDocList + moduleLabel = moduleName + + if level /= 0 + then + do + putStrLn $ "#" ++ moduleLabel + else + do + putStrLn moduleLabel + putStrLn (map (const '-') moduleLabel) + putStrLn "" + + if null examples + then + return () + else + do + putStrLn "#Examples:\n" + forM_ examples $ \(ExampleDoc example) -> do + putStrLn $ " * `" ++ example ++ "`" + putStrLn "" + + if null arguments + then + return () + else + do + if level /= 0 + then + putStrLn "##Arguments:\n" + else + if null syntaxes + then + putStrLn "#Arguments:\n" + else + putStrLn "#Shared Arguments:\n" + forM_ arguments $ \(ArgumentDoc name posfallback description) -> + case (posfallback, description) of + (Nothing, "") -> do + putStrLn $ " * `" ++ name ++ "`" + (Just fallback, "") -> do + putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" + (Nothing, _) -> do + putStrLn $ " * `" ++ name ++ "`" + putStrLn $ " " ++ description + (Just fallback, _) -> do + putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" + putStrLn $ " " ++ description + putStrLn "" + + if null syntaxes + then + return () + else + forM_ syntaxes $ \(Branch syntax) -> do + dumpPrimitive ("Syntax " ++ (show $ level+1)) syntax (level+1) + +main :: IO () +main = do + docs <- mapM (getArgParserDocs.($ []).snd) primitives + let + names = map fst primitives + docname = "ImplicitCAD Primitives" + + putStrLn (map (const '=') docname) + putStrLn docname + putStrLn (map (const '=') docname) + putStrLn "" + putStrLn "" + forM_ (zip names docs) $ \(moduleName, moduleDocList) -> do + dumpPrimitive moduleName moduleDocList 0 -- | We need a format to extract documentation into data Doc = Doc String [DocPart] @@ -54,11 +104,13 @@ data Doc = Doc String [DocPart] data DocPart = ExampleDoc String | ArgumentDoc String (Maybe String) String - deriving (Show) + | Empty + | Branch [DocPart] + deriving (Show,Eq) -- Here there be dragons! --- Because we made this a Monad instead of applicative functor, there's now sane way to do this. +-- Because we made this a Monad instead of applicative functor, there's no sane way to do this. -- We give undefined (= an error) and let laziness prevent if from ever being touched. -- We're using IO so that we can catch an error if this backfires. -- If so, we *back off*. @@ -66,27 +118,46 @@ data DocPart = ExampleDoc String -- | Extract Documentation from an ArgParser getArgParserDocs :: - (ArgParser a) -- ^ ArgParser + (ArgParser a) -- ^ ArgParser(s) -> IO [DocPart] -- ^ Docs (sadly IO wrapped) -getArgParserDocs (AP name fallback doc fnext) = - do - otherDocs <- Ex.catch (getArgParserDocs $ fnext undefined) (\(e :: Ex.SomeException) -> return []) - return $ (ArgumentDoc name (fmap show fallback) doc):otherDocs +getArgParserDocs (AP name fallback doc fnext) = do + otherDocs <- Ex.catch (getArgParserDocs $ fnext undefined) (\(e :: Ex.SomeException) -> return []) + if (otherDocs /= [Empty]) + then + do + return $ [(ArgumentDoc name (fmap show fallback) doc)] ++ (otherDocs) + else + do + return $ [(ArgumentDoc name (fmap show fallback) doc)] -getArgParserDocs (APFailIf _ _ child) = getArgParserDocs child +getArgParserDocs (APFailIf _ _ child) = do + childResults <- getArgParserDocs child + return $ childResults -getArgParserDocs (APExample str child) = - do - childResults <- getArgParserDocs child - return $ (ExampleDoc str) : childResults +getArgParserDocs (APExample str child) = do + childResults <- getArgParserDocs child + return $ (ExampleDoc str):(childResults) -- We try to look at as little as possible, to avoid the risk of triggering an error. -- Yay laziness! -getArgParserDocs (APTest _ _ child) = getArgParserDocs child +getArgParserDocs (APTest _ _ child) = do + childResults <- getArgParserDocs child + return $ childResults -- To look at this one would almost certainly be death (exception) -getArgParserDocs (APTerminator _) = return [] +getArgParserDocs (APTerminator _) = return $ [(Empty)] -getArgParserDocs (APBranch children) = return []-- mapM getArgParserDocs children +-- This one confuses me. +getArgParserDocs (APBranch children) = do + putStrLn $ show $ length children + otherDocs <- Ex.catch (getArgParserDocs (APBranch $ tail children)) (\(e :: Ex.SomeException) -> return []) + aResults <- getArgParserDocs $ head children + if (otherDocs /= [(Empty)]) + then + do + return $ [Branch ((aResults)++(otherDocs))] + else + do + return aResults From 9c1bfc77c4da0fcf525cd72f797130eea3f3ed89 Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 17:25:59 +0200 Subject: [PATCH 098/227] Whitespace cleanup --- Examples/example11.hs | 2 +- Examples/example12.hs | 2 +- Graphics/Implicit/Definitions.hs | 2 +- Graphics/Implicit/Export/DiscreteAproxable.hs | 16 +++--- Graphics/Implicit/Export/PolylineFormats.hs | 8 +-- Graphics/Implicit/Export/RayTrace.hs | 14 ++--- Graphics/Implicit/Export/Render.hs | 2 +- Graphics/Implicit/Export/SymbolicFormats.hs | 6 +-- Graphics/Implicit/Export/SymbolicObj3.hs | 8 +-- .../Implicit/Export/TriangleMeshFormats.hs | 2 +- Graphics/Implicit/ExtOpenScad/Default.hs | 1 - .../Implicit/ExtOpenScad/Eval/Statement.hs | 3 +- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 2 +- Graphics/Implicit/ExtOpenScad/Primitives.hs | 18 +++---- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 2 +- Graphics/Implicit/Primitives.hs | 8 +-- docgen.hs | 54 +++++++++---------- programs/extopenscad.hs | 2 +- tests/ParserSpec/Statement.hs | 6 +-- 19 files changed, 78 insertions(+), 80 deletions(-) diff --git a/Examples/example11.hs b/Examples/example11.hs index 9573d95..bd4a660 100644 --- a/Examples/example11.hs +++ b/Examples/example11.hs @@ -6,4 +6,4 @@ out = union [ translate (40,40) (circle 30) ] main = writeSVG 2 "example11.svg" out - + diff --git a/Examples/example12.hs b/Examples/example12.hs index c451867..09d63c3 100644 --- a/Examples/example12.hs +++ b/Examples/example12.hs @@ -4,5 +4,5 @@ import Graphics.Implicit out = unionR 14 [ rectR 0 (-40,-40) (40,40), translate (40,40) (circle 30) ] - + main = writeSVG 2 "example12.svg" out diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index a4eb767..74ae7e8 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -203,7 +203,7 @@ data SymbolicObj2 = deriving Show -- | A symbolic 3D format! -data SymbolicObj3 = +data SymbolicObj3 = -- Primitives Rect3R ℝ ℝ3 ℝ3 | Sphere ℝ diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index eb6b26f..8463238 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -18,8 +18,8 @@ import Graphics.Implicit.ObjectUtil (getImplicit3, getImplicit2, getBox3, getBox import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) -import Graphics.Implicit.Export.Util (normTriangle) - +import Graphics.Implicit.Export.Util (normTriangle) + import Graphics.Implicit.Export.RayTrace (dynamicImage, Color, average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) import Codec.Picture (DynamicImage, generateImage, PixelRGBA8(PixelRGBA8)) @@ -56,21 +56,21 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where lights = [Light (x1-deviation*(1.5::ℝ), y1 - (0.4::ℝ)*(y2-y1), avZ) ((0.03::ℝ)*deviation) ] scene = Scene obj (PixelRGBA8 200 200 230 255) lights (PixelRGBA8 255 255 255 0) pixelRenderer :: Fastℕ -> Fastℕ -> Color - pixelRenderer a b = renderScreen + pixelRenderer a b = renderScreen ((fromIntegral a :: ℝ)/w - (0.5::ℝ)) ((fromIntegral b :: ℝ)/h - (0.5 ::ℝ)) renderScreen :: ℝ -> ℝ -> Color renderScreen a b = average [ - traceRay + traceRay (cameraRay camera ((a,b) ^+^ ( 0.25/w, 0.25/h))) 2 box scene, - traceRay + traceRay (cameraRay camera ((a,b) ^+^ (-0.25/w, 0.25/h))) 0.5 box scene, - traceRay + traceRay (cameraRay camera ((a,b) ^+^ (0.25/w, -0.25/h))) 0.5 box scene, - traceRay + traceRay (cameraRay camera ((a,b) ^+^ (-0.25/w,-0.25/h))) 0.5 box scene ] @@ -102,4 +102,4 @@ instance DiscreteAproxable SymbolicObj2 DynamicImage where - + diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index 79314f0..fa29a19 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -30,7 +30,7 @@ svg plines = renderSvg . svg11 . svg' $ plines (xmin, xmax, ymin, ymax) = (minimum xs - margin, maximum xs + margin, minimum ys - margin, maximum ys + margin) where margin = strokeWidth / 2 (xs,ys) = unzip (concat plines) - + svg11 = docTypeSvg ! A.version "1.1" ! A.width (stringValue $ show (xmax-xmin) ++ "mm") ! A.height (stringValue $ show (ymax-ymin) ++ "mm") @@ -38,11 +38,11 @@ svg plines = renderSvg . svg11 . svg' $ plines -- The reason this isn't totally straightforwards is that svg has different coordinate system -- and we need to compute the requisite translation. - svg' [] = mempty + svg' [] = mempty -- When we have a known point, we can compute said transformation: svg' polylines = thinBlueGroup $ mapM_ poly polylines - poly line = polyline ! A.points pointList + poly line = polyline ! A.points pointList where pointList = toValue $ toLazyText $ mconcat [bf (x-xmin) <> "," <> bf (ymax - y) <> " " | (x,y) <- line] -- Instead of setting styles on every polyline, we wrap the lines in a group element and set the styles on it: @@ -87,4 +87,4 @@ hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpret ,mconcat [ "G01 " <> gcodeXY point <> "\n" | point <- others] ,"M63 P0 (laser off)\n\n" ] - interpretPolyline [] = mempty + interpretPolyline [] = mempty diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index ee9b761..05d2077 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -54,8 +54,8 @@ s `colorMult` (PixelRGBA8 a b c d) = color (s `mult` a) (s `mult` b) (s `mult` c mult x y = round . bound . toRational $ x * y average :: [Color] -> Color -average l = - let +average l = + let ((rs, gs), (bs, as)) = (unzip *** unzip) . unzip $ map (\(PixelRGBA8 r g b a) -> ((fromIntegral r, fromIntegral g), (fromIntegral b, fromIntegral a))) l :: (([ℝ], [ℝ]), ([ℝ],[ℝ])) @@ -103,7 +103,7 @@ intersection r@(Ray p v) ((a, aval),b) res obj = a' = a + step a'val = obj (p ^+^ a'*^v) in if a'val < 0 - then + then let a'' = refine (a,a') (\s -> obj (p ^+^ s*^v)) in Just (p ^+^ a''*^v) else if a' < b @@ -111,7 +111,7 @@ intersection r@(Ray p v) ((a, aval),b) res obj = else Nothing refine :: ℝ2 -> (ℝ -> ℝ) -> ℝ -refine (a, b) obj = +refine (a, b) obj = let (aval, bval) = (obj a, obj b) in if bval < aval @@ -120,7 +120,7 @@ refine (a, b) obj = refine' :: ℕ -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ refine' 0 (a, _) _ _ = a -refine' n (a, b) (aval, bval) obj = +refine' n (a, b) (aval, bval) obj = let mid = (a+b)/(2::ℝ) midval = obj mid @@ -160,12 +160,12 @@ traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultCo proj a' b' = (a'⋅b')*^b' dist = vectorDistance p lightPos illumination = max 0 (normal ⋅ unitV) * lightIntensity * (25 /dist) - rV = + rV = let normalComponent = proj v' normal parComponent = v' - normalComponent in - normalComponent - parComponent + normalComponent - parComponent return $ illumination*(3 + 0.3*abs(rV ⋅ cameraV)*abs(rV ⋅ cameraV)) ) Nothing -> defaultColor diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index be0cba8..ab65e72 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -189,7 +189,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = in -- (5) merge squares, etc - cleanupTris . mergedSquareTris . concat . concat $ concat sqTris + cleanupTris . mergedSquareTris . concat . concat $ concat sqTris -- Removes triangles that are empty, when converting their positions to Float resolution. -- NOTE: this will need to be disabled for AMF, and other triangle formats that can handle Double. diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index 4558522..e619e41 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -17,10 +17,10 @@ import Control.Monad.Reader (Reader, runReader, return, fmap, sequence, ask) import Data.List (intersperse) -scad2 :: ℝ -> SymbolicObj2 -> Text +scad2 :: ℝ -> SymbolicObj2 -> Text scad2 res obj = toLazyText $ runReader (buildS2 obj) res -scad3 :: ℝ -> SymbolicObj3 -> Text +scad3 :: ℝ -> SymbolicObj3 -> Text scad3 res obj = toLazyText $ runReader (buildS3 obj) res -- used by rotate2 and rotate3 @@ -93,7 +93,7 @@ buildS3 (ExtrudeRM r (Just twist) Nothing Nothing obj (Left height)) | r == 0 = call "rotate" ["0","0", bf $ twist h] [ callNaked "linear_extrude" ["height = " <> bf res, "twist = " <> bf (twist (h+res) - twist h)][ buildS2 obj - ] + ] ] | h <- init [0, res .. height] ] diff --git a/Graphics/Implicit/Export/SymbolicObj3.hs b/Graphics/Implicit/Export/SymbolicObj3.hs index 720ba6a..47ed179 100644 --- a/Graphics/Implicit/Export/SymbolicObj3.hs +++ b/Graphics/Implicit/Export/SymbolicObj3.hs @@ -197,13 +197,13 @@ symbolicGetMesh res (ExtrudeRM r twist scale translate obj2 h) = map transformTriangle (side_tris ++ bottom_tris ++ top_tris) -} -symbolicGetMesh res inputObj@(UnionR3 r objs) = +symbolicGetMesh res inputObj@(UnionR3 r objs) = let boxes = map getBox3 objs boxedObjs = zip boxes objs - + sepFree :: forall a. [((ℝ3, ℝ3), a)] -> ([a], [a]) - sepFree ((box,obj):others) = + sepFree ((box,obj):others) = if length (filter (box3sWithin r box) boxes) > 1 then first ((:) obj) $ sepFree others else second ((:) obj) $ sepFree others @@ -212,7 +212,7 @@ symbolicGetMesh res inputObj@(UnionR3 r objs) = (dependants, independents) = sepFree boxedObjs in if null independents then case rebound3 (getImplicit3 inputObj, getBox3 inputObj) of - (obj, (a,b)) -> getMesh a b res obj + (obj, (a,b)) -> getMesh a b res obj else if null dependants then concatMap (symbolicGetMesh res) independents else concatMap (symbolicGetMesh res) independents diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 71de08f..a3c337d 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -97,7 +97,7 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer v (x,y,z) = "v(" <> bf x <> "," <> bf y <> "," <> bf z <> ");\n" -- A face line f :: Fastℕ -> Fastℕ -> Fastℕ -> Builder - f posa posb posc = + f posa posb posc = "f(" <> buildInt posa <> "," <> buildInt posb <> "," <> buildInt posc <> ");" verts = do -- extract the vertices for each triangle diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 2b3f26a..8929a72 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -74,7 +74,6 @@ defaultFunctionsSpecial = ("map", toOObj $ flip (map :: (OVal -> OVal) -> [OVal] -> [OVal] ) ) - ] defaultModules :: [(String, OVal)] diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs index f7a4681..aef3d1c 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs @@ -24,9 +24,8 @@ import Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, matchPat) import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Data.Maybe(fromMaybe) - import qualified Data.Map as Map -import Control.Monad (forM_, forM, mapM_) +import Control.Monad (forM_, forM, mapM_) import Control.Monad.State (get, liftIO, mapM, runStateT, (>>)) import qualified System.FilePath as FilePath diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 0e17160..504894b 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -13,7 +13,7 @@ import Graphics.Implicit.Definitions (ℝ) -- The parsec parsing library. import Text.ParserCombinators.Parsec (GenParser, string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try) -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, LitE, ListE, (:$)), OVal(ONum, OString, OBool, OUndefined), collector) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, LitE, ListE, (:$)), OVal(ONum, OString, OBool, OUndefined), collector) import Graphics.Implicit.ExtOpenScad.Parser.Util (variableSymb, (?:), (*<|>), genSpace, padString) diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 9d87861..474b9b5 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -199,7 +199,7 @@ cylinder = moduleWithoutSuite "cylinder" $ do circle :: (String, [OVal] -> ArgParser (IO [OVal])) circle = moduleWithoutSuite "circle" $ do - + example "circle(r=10); // circle" example "circle(r=5, $fn=6); //hexagon" @@ -223,9 +223,9 @@ circle = moduleWithoutSuite "circle" $ do polygon :: (String, [OVal] -> ArgParser (IO [OVal])) polygon = moduleWithoutSuite "polygon" $ do - + example "polygon ([(0,0), (0,10), (10,0)]);" - + points :: [ℝ2] <- argument "points" `doc` "vertices of the polygon" paths :: [ℕ] <- argument "paths" @@ -289,7 +289,7 @@ translate = moduleWithSuite "translate" $ \children -> do Left x -> (x,0,0) Right (Left (x,y) ) -> (x,y,0) Right (Right (x,y,z)) -> (x,y,z) - + return $ return $ objMap (Prim.translate (x,y)) (Prim.translate (x,y,z)) children @@ -327,11 +327,11 @@ scale = moduleWithSuite "scale" $ \children -> do v <- argument "v" `doc` "vector or scalar to scale by" - + let scaleObjs stretch2 stretch3 = objMap (Prim.scale stretch2) (Prim.scale stretch3) children - + return $ return $ case v of Left x -> scaleObjs (x,1) (x,1,1) Right (Left (x,y)) -> scaleObjs (x,y) (x,y,1) @@ -353,7 +353,7 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do `doc` "translate according to this funciton as we extrude..." r :: ℝ <- argument "r" `defaultTo` 0 `doc` "round the top?" - + let heightn = case height of Left h -> h @@ -372,11 +372,11 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do funcify :: (VectorSpace a, Fractional (Scalar a)) => Either a (ℝ -> a) -> ℝ -> a funcify (Left val) h = realToFrac (h/heightn) *^ val funcify (Right f ) h = f h - + twist' = fmap funcify twist scale' = fmap funcify scaleArg translate' = fmap funcify translateArg - + return $ return $ obj2UpMap ( \obj -> case height of Left constHeight | isNothing twist && isNothing scaleArg && isNothing translateArg -> diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 38ae210..501d1f8 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -172,7 +172,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = _ -> False in \(x,y,z) -> minimum $ do - + let r = sqrt (x*x + y*y) θ = atan2 y x diff --git a/Graphics/Implicit/Primitives.hs b/Graphics/Implicit/Primitives.hs index d248173..3e7c323 100644 --- a/Graphics/Implicit/Primitives.hs +++ b/Graphics/Implicit/Primitives.hs @@ -138,12 +138,12 @@ polygonR = PolygonR -- $ Shared Operations class Object obj vec | obj -> vec where - + -- | Complement an Object complement :: obj -- ^ Object to complement -> obj -- ^ Result - + -- | Rounded union unionR :: ℝ -- ^ The radius of rounding @@ -161,7 +161,7 @@ class Object obj vec | obj -> vec where ℝ -- ^ The radius of rounding -> [obj] -- ^ Objects to intersect -> obj -- ^ Resulting object - + -- | Translate an object by a vector of appropriate dimension. translate :: vec -- ^ Vector to translate by (Also: a is a vector, blah, blah) @@ -200,7 +200,7 @@ class Object obj vec | obj -> vec where (vec -> ℝ) -- ^ Implicit function -> (vec, vec) -- ^ Bounding box -> obj -- ^ Resulting object - + instance Object SymbolicObj2 ℝ2 where translate = Translate2 diff --git a/docgen.hs b/docgen.hs index b716c83..1e94f42 100644 --- a/docgen.hs +++ b/docgen.hs @@ -17,34 +17,34 @@ isArgument (ArgumentDoc _ _ _) = True isArgument _ = False main = do - let names = map fst primitives - docs <- sequence $ map (getArgParserDocs.($ []).snd) primitives + let names = map fst primitives + docs <- sequence $ map (getArgParserDocs.($ []).snd) primitives - forM_ (zip names docs) $ \(moduleName, moduleDocList) -> do - let - examples = filter isExample moduleDocList - arguments = filter isArgument moduleDocList - putStrLn moduleName - putStrLn (map (const '-') moduleName) - putStrLn "" - if not $ null examples then putStrLn "**Examples:**\n" else return () - forM_ examples $ \(ExampleDoc example) -> do - putStrLn $ " * `" ++ example ++ "`" - putStrLn "" - putStrLn "**Arguments:**\n" - forM_ arguments $ \(ArgumentDoc name posfallback description) -> - case (posfallback, description) of - (Nothing, "") -> do - putStrLn $ " * `" ++ name ++ "`" - (Just fallback, "") -> do - putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" - (Nothing, _) -> do - putStrLn $ " * `" ++ name ++ "`" - putStrLn $ " " ++ description - (Just fallback, _) -> do - putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" - putStrLn $ " " ++ description - putStrLn "" + forM_ (zip names docs) $ \(moduleName, moduleDocList) -> do + let + examples = filter isExample moduleDocList + arguments = filter isArgument moduleDocList + putStrLn moduleName + putStrLn (map (const '-') moduleName) + putStrLn "" + if not $ null examples then putStrLn "**Examples:**\n" else return () + forM_ examples $ \(ExampleDoc example) -> do + putStrLn $ " * `" ++ example ++ "`" + putStrLn "" + putStrLn "**Arguments:**\n" + forM_ arguments $ \(ArgumentDoc name posfallback description) -> + case (posfallback, description) of + (Nothing, "") -> do + putStrLn $ " * `" ++ name ++ "`" + (Just fallback, "") -> do + putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" + (Nothing, _) -> do + putStrLn $ " * `" ++ name ++ "`" + putStrLn $ " " ++ description + (Just fallback, _) -> do + putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" + putStrLn $ " " ++ description + putStrLn "" -- | We need a format to extract documentation into data Doc = Doc String [DocPart] diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index 510b9b5..b96fc30 100644 --- a/programs/extopenscad.hs +++ b/programs/extopenscad.hs @@ -242,6 +242,6 @@ main = execParser opts >>= run where opts= info (helper <*> extOpenScadOpts) ( fullDesc - <> progDesc "ImplicitCAD: Extended OpenSCAD interpreter." + <> progDesc "ImplicitCAD: Extended OpenSCAD interpreter." <> header "extopenscad - Extended OpenSCAD" ) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 1fe01fb..d876f08 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -39,7 +39,7 @@ call name args stmts = StatementI 1 (ModuleCall name args stmts) -- test a simple if block. ifSpec :: Spec ifSpec = it "parses" $ - "if (true) { a(); } else { b(); }" --> + "if (true) { a(); } else { b(); }" --> single ( If (bool True) [call "a" [] []] [call "b" [] []]) -- test assignments. @@ -71,9 +71,9 @@ statementSpec = do describe "empty file" $ it "returns an empty list" $ emptyFileIssue $ "" --> [] - describe "line comment" $ + describe "line comment" $ it "parses as empty" $ emptyFileIssue $ "// foish bar\n" --> [] - describe "module call" $ + describe "module call" $ it "parses" $ "foo();" --> single (ModuleCall "foo" [] []) describe "difference of two cylinders" $ it "parses correctly" $ From 53b6a4b1088e4e2d74694fcc783dd44e95cdb9d8 Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 17:35:18 +0200 Subject: [PATCH 099/227] hlint: Redundant do --- docgen.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docgen.hs b/docgen.hs index 1e94f42..608277f 100644 --- a/docgen.hs +++ b/docgen.hs @@ -28,15 +28,15 @@ main = do putStrLn (map (const '-') moduleName) putStrLn "" if not $ null examples then putStrLn "**Examples:**\n" else return () - forM_ examples $ \(ExampleDoc example) -> do + forM_ examples $ \(ExampleDoc example) -> putStrLn $ " * `" ++ example ++ "`" putStrLn "" putStrLn "**Arguments:**\n" forM_ arguments $ \(ArgumentDoc name posfallback description) -> case (posfallback, description) of - (Nothing, "") -> do + (Nothing, "") -> putStrLn $ " * `" ++ name ++ "`" - (Just fallback, "") -> do + (Just fallback, "") -> putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" (Nothing, _) -> do putStrLn $ " * `" ++ name ++ "`" From 42be1a214b46b56554c0b037cb35b4d1f4760fd4 Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 17:41:20 +0200 Subject: [PATCH 100/227] hlint: Redundant $ --- tests/ParserSpec/Statement.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index d876f08..c40938c 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -66,8 +66,8 @@ emptyFileIssue _ = pendingWith "parser should probably allow empty files" statementSpec :: Spec statementSpec = do - describe "assignment" $ assignmentSpec - describe "if" $ ifSpec + describe "assignment" assignmentSpec + describe "if" ifSpec describe "empty file" $ it "returns an empty list" $ emptyFileIssue $ "" --> [] From ef3c4df7a46b11a50c935ca2e2b9228a5b65c3c5 Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 17:46:10 +0200 Subject: [PATCH 101/227] hlint: Redundant bracket --- Graphics/Implicit/Export/MarchingSquares.hs | 2 +- Graphics/Implicit/Export/MarchingSquaresFill.hs | 2 +- Graphics/Implicit/Export/Render.hs | 6 +++--- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 10 +++++----- docgen.hs | 6 +++--- tests/ParserSpec/Expr.hs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index e93b92a..c41cc66 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -36,7 +36,7 @@ getContour p1 p2 res obj = -- How many steps will we take on each axis? nx :: ℕ ny :: ℕ - n@(nx,ny) = (ceiling) `both` (d ⋯/ res) + n@(nx,ny) = ceiling `both` (d ⋯/ res) -- a helper for calculating a position inside of the space. gridPos :: (ℕ,ℕ) -> (ℕ,ℕ) -> ℝ2 diff --git a/Graphics/Implicit/Export/MarchingSquaresFill.hs b/Graphics/Implicit/Export/MarchingSquaresFill.hs index becb76e..f2a02af 100644 --- a/Graphics/Implicit/Export/MarchingSquaresFill.hs +++ b/Graphics/Implicit/Export/MarchingSquaresFill.hs @@ -30,7 +30,7 @@ getContourMesh p1 p2 res obj = -- How many steps will we take on each axis? nx :: ℕ ny :: ℕ - n@(nx,ny) = (ceiling) `both` (d ⋯/ res) + n@(nx,ny) = ceiling `both` (d ⋯/ res) -- a helper for calculating a position inside of the space. gridPos :: (ℕ,ℕ) -> (ℕ,ℕ) -> ℝ2 diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index ab65e72..5e4edc0 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -220,14 +220,14 @@ getContour p1@(x1, y1) p2 res obj = -- How many steps will we take on each axis? nx :: ℕ ny :: ℕ - (nx,ny) = (ceiling) `both` (d ⋯/ (res,res)) + (nx,ny) = ceiling `both` (d ⋯/ (res,res)) -- How big are the steps? (rx,ry) = d ⋯/ (fromIntegral `both` (nx,ny)) -- the points inside of the region. - pYs = [ y1 + ry*(fromIntegral p) | p <- [0.. ny] ] - pXs = [ x1 + rx*(fromIntegral p) | p <- [0.. nx] ] + pYs = [ y1 + ry*fromIntegral p | p <- [0.. ny] ] + pXs = [ x1 + rx*fromIntegral p | p <- [0.. nx] ] par2DList :: forall t. NFData t => ℕ -> ℕ -> ((ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> t) -> [[t]] par2DList lenx leny f = diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 504894b..eee1aab 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -32,14 +32,14 @@ literal = ("literal" ?:) $ a <- many1 digit _ <- char 'e' b <- many1 digit - return . LitE $ ONum (((read a) * (10 ** (read b))) :: ℝ) + return . LitE $ ONum ((read a * (10 ** read b)) :: ℝ) *<|> do a <- many1 digit _ <- char '.' b <- many digit _ <- char 'e' c <- many1 digit - return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** (read c))) :: ℝ) + return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** read c)) :: ℝ) *<|> do a <- many1 digit _ <- char '.' @@ -47,7 +47,7 @@ literal = ("literal" ?:) $ _ <- char 'e' _ <- char '+' c <- many1 digit - return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** (read c))) :: ℝ) + return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** read c)) :: ℝ) *<|> do a <- many1 digit _ <- char '.' @@ -55,13 +55,13 @@ literal = ("literal" ?:) $ _ <- char 'e' _ <- char '-' c <- many1 digit - return . LitE $ ONum ((read (a ++ "." ++ b) / (10 ** (read c))) :: ℝ) + return . LitE $ ONum ((read (a ++ "." ++ b) / (10 ** read c)) :: ℝ) *<|> do a <- many1 digit _ <- char 'e' _ <- char '-' b <- many1 digit - return . LitE $ ONum (((read a) / (10 ** (read b))) :: ℝ) + return . LitE $ ONum ((read a / (10 ** read b)) :: ℝ) *<|> do a <- many1 digit _ <- char '.' diff --git a/docgen.hs b/docgen.hs index 608277f..8311e8a 100644 --- a/docgen.hs +++ b/docgen.hs @@ -64,18 +64,18 @@ data DocPart = ExampleDoc String -- | Extract Documentation from an ArgParser getArgParserDocs :: - (ArgParser a) -- ^ ArgParser + ArgParser a -- ^ ArgParser -> IO [DocPart] -- ^ Docs (sadly IO wrapped) getArgParserDocs (ArgParser name fallback doc fnext) = do otherDocs <- Ex.catch (getArgParserDocs $ fnext undefined) (\(e :: Ex.SomeException) -> return []) - return $ (ArgumentDoc name (fmap show fallback) doc):otherDocs + return $ ArgumentDoc name (fmap show fallback) doc:otherDocs getArgParserDocs (ArgParserExample str child) = do childResults <- getArgParserDocs child - return $ (ExampleDoc str) : childResults + return $ ExampleDoc str : childResults -- We try to look at as little as possible, to avoid the risk of triggering an error. -- Yay laziness! diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index e08771b..43e62b0 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -126,4 +126,4 @@ exprSpec = do specify "multiple" $ "foo(x, 1, 2)(5)(y)" --> ((Var "foo" :$ [Var "x", num 1, num 2]) :$ [num 5]) :$ [Var "y"] specify "multiple, with indexing" $ - "foo(x)[0](y)" --> ((index [(Var "foo" :$ [Var "x"]), num 0]) :$ [Var "y"]) + "foo(x)[0](y)" --> (index [Var "foo" :$ [Var "x"], num 0] :$ [Var "y"]) From 6f799d3b3b7f46fc9888d7933de038613fe89444 Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 17:56:25 +0200 Subject: [PATCH 102/227] hlint: Use fewer imports --- Graphics/Implicit/Export/TextBuilderUtils.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/TextBuilderUtils.hs b/Graphics/Implicit/Export/TextBuilderUtils.hs index 1ef1f8b..7c65641 100644 --- a/Graphics/Implicit/Export/TextBuilderUtils.hs +++ b/Graphics/Implicit/Export/TextBuilderUtils.hs @@ -26,7 +26,7 @@ module Graphics.Implicit.Export.TextBuilderUtils ( import Prelude (Maybe(Nothing, Just), ($)) -import Graphics.Implicit.Definitions(Fastℕ) +import Graphics.Implicit.Definitions (Fastℕ, ℝ) import Data.Text.Lazy (Text, pack) -- We manually redefine this operator to avoid a dependency on base >= 4.5 -- This will become unnecessary later. @@ -37,8 +37,6 @@ import Data.Text.Lazy.Builder (Builder, toLazyTextWith, fromLazyText) import Data.Text.Lazy.Builder.RealFloat (formatRealFloat, FPFormat(Exponent, Fixed)) import Data.Text.Lazy.Builder.Int (decimal) -import Graphics.Implicit.Definitions (ℝ) - -- The chunk size for toLazyText is very small (128 bytes), so we export -- a version with a much larger size (~16 K) toLazyText :: Builder -> Text From 26b5dc9ccd17f46398374eda86c5364cec46286c Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 18:02:15 +0200 Subject: [PATCH 103/227] hlint: Move brackets to avoid $ --- Graphics/Implicit/ObjectUtil/GetBox2.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 9418d1a..d4504b2 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -108,7 +108,7 @@ getDist2 p (Circle r) = magnitude p + r getDist2 p (PolygonR r points) = r + maximum [magnitude (p ^-^ p') | p' <- points] -- Transform implementations getDist2 p (UnionR2 r objs) = r + maximum [getDist2 p obj | obj <- objs ] -getDist2 p (DifferenceR2 r objs) = r + (getDist2 p $ head objs) +getDist2 p (DifferenceR2 r objs) = r + getDist2 p (head objs) getDist2 p (IntersectR2 r objs) = r + maximum [getDist2 p obj | obj <- objs ] -- FIXME: isn't this wrong? should we be returning distance inside of the object? getDist2 _ (Complement2 _) = 1/0 From 6e03d13135df40d160acccec3ffee82d71cf687f Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 18:03:12 +0200 Subject: [PATCH 104/227] hlint: Unused LANGUAGE pragma --- docgen.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docgen.hs b/docgen.hs index 8311e8a..c0e83f4 100644 --- a/docgen.hs +++ b/docgen.hs @@ -1,7 +1,9 @@ -- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) -- Released under the GNU GPL, see LICENSE -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances, ScopedTypeVariables #-} +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, + FlexibleContexts, TypeSynonymInstances, UndecidableInstances, + ScopedTypeVariables #-} -- FIXME: this doesn't work. looks like it broke badly when ArgParser became a Monad. From 323af6987a69d5c3b1178dbf35672e8a04611ffa Mon Sep 17 00:00:00 2001 From: Jonas Claeson Date: Tue, 7 Aug 2018 18:04:32 +0200 Subject: [PATCH 105/227] hlint: Use String --- programs/implicitsnap.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 186983b..621179a 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -159,7 +159,7 @@ executeAndExport content callback maybeFormat = callback ++ "([null," ++ show msg ++ "," ++ showB is2D ++ "," ++ show w ++ "]);" callbackF True is2D w msg = callback ++ "([new Shape()," ++ show msg ++ "," ++ showB is2D ++ "," ++ show w ++ "]);" - callbackS :: (Show a1, Show a) => a -> a1 -> [Char] + callbackS :: (Show a1, Show a) => a -> a1 -> String callbackS str msg = callback ++ "([" ++ show str ++ "," ++ show msg ++ ",null,null]);" in case runOpenscad content of Left err -> From 800822c34a3df566fc5930ac08c34c7bf019bc8f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 20 Feb 2019 15:06:03 +0000 Subject: [PATCH 106/227] move docgen to live with the rest of the programs, and create a docs/ directory. --- hacking.md => docs/hacking.md | 0 implicit.cabal | 1 + docgen.hs => programs/docgen.hs | 0 3 files changed, 1 insertion(+) rename hacking.md => docs/hacking.md (100%) rename docgen.hs => programs/docgen.hs (100%) diff --git a/hacking.md b/docs/hacking.md similarity index 100% rename from hacking.md rename to docs/hacking.md diff --git a/implicit.cabal b/implicit.cabal index b4166c1..3d01ada 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -132,6 +132,7 @@ Executable extopenscad Executable docgen main-is: docgen.hs + Hs-source-dirs: programs build-depends: base, vector-space, diff --git a/docgen.hs b/programs/docgen.hs similarity index 100% rename from docgen.hs rename to programs/docgen.hs From af975ba7069fae2a46c3acf50d2222e0d814ad2c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 20 Feb 2019 17:22:46 +0000 Subject: [PATCH 107/227] build documentation, many comments in Makefile, and make docgen build correctly. --- .gitignore | 1 + Makefile | 69 +++++++++++++++++++++++++++++++------------------- implicit.cabal | 6 +++-- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 88bf9ec..da54a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ *.stl dist/ Setup +docs/iscad.md \ No newline at end of file diff --git a/Makefile b/Makefile index eee967c..53d8349 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,42 @@ -.PHONY: build install clean docs dist test examples tests +# ImplicitCAD Makefile. Build and test Implicitcad. + +## Locations of binaries used when running tests, or generating the images to go along with our README.md. +# the location of stl2ps, from stltools, available from https://github.com/rsmith-nl/stltools/tree/develop +stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py +# the location of convert, from imagemagick +convert=convert +# the location of GHC, used to compile .hs examples. +GHC=ghc +# the location of the created extopenscad binary, for running shell based test cases. +EXTOPENSCAD=dist/build/extopenscad/extopenscad +# the location of the created test binary, for running haskell test cases. +TESTSUITE=dist/build/test-implicit/test-implicit +# the location of the documentation generator. for documenting (some of) the extopenscad languagi. +DOCGEN=dist/build/docgen/docgen + +## options used when calling ImplicitCAD. for testing, and for image generation. +# enable multiple CPU usage. RTSOPTS=+RTS -N - +# the resolution to generate objects at. FIXME: what does this mean in human terms? RESOPTS=-r 50 -#uncomment for profiling support. +#uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. #PROFILING= --enable-library-profiling --enable-executable-profiling -# stl2ps, from stltools, available from https://github.com/rsmith-nl/stltools/tree/develop -stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py +TARGETS=$(EXTOPENSCAD) $(TESTSUITE) $(DOCGEN) -# convert, from imagemagick -convert=convert +# mark the below fake targets as unrean, so make will not get choked up if a file with one of these names is created. +.PHONY: build install clean distclean nukeclean docs dist examples tests -EXTOPENSCAD=dist/build/extopenscad/extopenscad -TESTSUITE=dist/build/test-implicit/test-implicit -TARGETS=$(EXTOPENSCAD) $(TESTSUITE) +# build implicitcad binaries. +build: $(TARGETS) -# FIXME: this used to be ./Setup install. what's going on? -install: $(TARGETS) +# install implicitcad. +install: build cabal install +# cleanup from using the rules in this file. clean: Setup ./Setup clean rm -f Examples/*.stl @@ -33,40 +49,41 @@ clean: Setup rm -f tests/*.stl rm -f Setup Setup.hi Setup.o rm -rf dist/* + rm -rf docs/parser.md +# clean up before making a release. distclean: clean rm -f `find ./ -name *~` rm -f `find ./ -name \#*\#` +# destroy the current user's cabal/ghc environment. nukeclean: distclean rm -rf ~/.cabal/ ~/.ghc/ - -docs: $(TARGETS) +# Generate documentation. +docs: $(DOCGEN) ./Setup haddock + $(DOCGEN) > docs/iscad.md +# dist: $(TARGETS) ./Setup sdist -#test: $(TARGETS) -# ./Setup test - -examples: $(TARGETS) +# generate examples. +examples: $(EXTOPENSCAD) cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done - cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; ghc $$filename.hs -o $$filename; $$filename; } done + cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; $(GHC) $$filename.hs -o $$filename; $$filename; } done -images: +images: examples cd Examples && for each in `find ./ -name '*.stl' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; if [ -e $$filename.transform ] ; then echo ${stl2ps} $$each $$filename.ps `cat $$filename.transform`; else ${stl2ps} $$each $$filename.ps; fi; ${convert} $$filename.ps $$filename.png; } done -tests: $(TARGETS) +tests: $(TESTSUITE) # cd tests && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each ${RESOPTS} ${RTSOPTS}; } done ./dist/build/test-implicit/test-implicit -dist/build/extopenscad/extopenscad: Setup dist/setup-config - cabal build - -dist/build/test-implicit/test-implicit: Setup dist/setup-config - cabal build +# actually build a given binary. +dist/build/%: Setup dist/setup-config + cabal build $(word 2,$(subst /, ,$*)) dist/setup-config: Setup implicit.cabal cabal update diff --git a/implicit.cabal b/implicit.cabal index 3d01ada..3738c5e 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -73,6 +73,8 @@ Library Graphics.Implicit.Export.TriangleMeshFormats Graphics.Implicit.Export.PolylineFormats Graphics.Implicit.Export.DiscreteAproxable + -- These are exposed for docgen. + Graphics.Implicit.ExtOpenScad.Primitives Other-modules: Graphics.Implicit.ObjectUtil.GetBox2 @@ -81,7 +83,6 @@ Library Graphics.Implicit.ObjectUtil.GetImplicit3 Graphics.Implicit.ExtOpenScad.Default Graphics.Implicit.ExtOpenScad.Parser.Util - Graphics.Implicit.ExtOpenScad.Primitives Graphics.Implicit.ExtOpenScad.Eval.Statement Graphics.Implicit.ExtOpenScad.Eval.Expr Graphics.Implicit.ExtOpenScad.Util.StateC @@ -154,7 +155,8 @@ Executable docgen snap-core, snap-server, silently, - transformers + transformers, + implicit ghc-options: -Wall -Weverything From 2a2c0ab2c24a5cd6917f40b8180aa4908f36ee18 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 4 Mar 2019 11:56:59 +0000 Subject: [PATCH 108/227] minor import changes for clarity, version bump, and support for autocad R12 DXF export. --- Graphics/Implicit.hs | 6 +- Graphics/Implicit/Definitions.hs | 6 +- Graphics/Implicit/Export.hs | 7 +- Graphics/Implicit/Export/PolylineFormats.hs | 103 ++++++++++++++---- .../Implicit/ExtOpenScad/Parser/Statement.hs | 8 +- Graphics/Implicit/ObjectUtil/GetBox3.hs | 4 +- implicit.cabal | 3 +- programs/extopenscad.hs | 9 +- 8 files changed, 113 insertions(+), 33 deletions(-) diff --git a/Graphics/Implicit.hs b/Graphics/Implicit.hs index 0d65907..967d325 100644 --- a/Graphics/Implicit.hs +++ b/Graphics/Implicit.hs @@ -38,6 +38,7 @@ module Graphics.Implicit( pack2, -- Export writeSVG, + writeDXF2, writeSTL, writeBinSTL, writeOBJ, @@ -62,7 +63,7 @@ import Graphics.Implicit.Primitives (translate, scale, complement, union, inters import Graphics.Implicit.ExtOpenScad (runOpenscad) -- Functions for writing files based on the result of operations on primitives. -import qualified Graphics.Implicit.Export as Export (writeSVG, writeSTL, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeTHREEJS, writeGCodeHacklabLaser, writePNG) +import qualified Graphics.Implicit.Export as Export (writeSVG, writeDXF2, writeSTL, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeTHREEJS, writeGCodeHacklabLaser, writePNG) -- Datatypes/classes defining the world, or part of the world. import Graphics.Implicit.Definitions (ℝ, SymbolicObj2, SymbolicObj3) @@ -73,6 +74,9 @@ import Graphics.Implicit.Definitions (ℝ, SymbolicObj2, SymbolicObj3) writeSVG :: ℝ -> FilePath -> SymbolicObj2 -> IO () writeSVG = Export.writeSVG +writeDXF2 :: ℝ -> FilePath -> SymbolicObj2 -> IO () +writeDXF2 = Export.writeDXF2 + writeSTL :: ℝ -> FilePath -> SymbolicObj3 -> IO () writeSTL = Export.writeSTL diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index ea4db92..1d12429 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -70,11 +70,13 @@ module Graphics.Implicit.Definitions ( ExtrudeOnEdgeOf, RotateExtrude), Rectilinear2, - Rectilinear3, + Rectilinear3 ) where -import Prelude (Show, Double, Integer, Int, Maybe, Either, show, (*), (/)) +import Prelude (Show, Double, Integer, Int, Either, show, (*), (/)) + +import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) diff --git a/Graphics/Implicit/Export.hs b/Graphics/Implicit/Export.hs index 166fae1..637a366 100644 --- a/Graphics/Implicit/Export.hs +++ b/Graphics/Implicit/Export.hs @@ -9,7 +9,7 @@ -- FIXME: Required. why? {-# LANGUAGE FlexibleContexts #-} -module Graphics.Implicit.Export (writeObject, formatObject, writeSVG, writeSTL, writeBinSTL, writeOBJ, writeTHREEJS, writeGCodeHacklabLaser, writeSCAD3, writeSCAD2, writePNG) where +module Graphics.Implicit.Export (writeObject, formatObject, writeSVG, writeSTL, writeBinSTL, writeOBJ, writeTHREEJS, writeGCodeHacklabLaser, writeDXF2, writeSCAD2, writeSCAD3, writePNG) where import Prelude (FilePath, IO, (.), ($)) @@ -25,7 +25,7 @@ import qualified Data.ByteString.Lazy as LBS (writeFile) import Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAprox) -- Output file formats. -import qualified Graphics.Implicit.Export.PolylineFormats as PolylineFormats (svg, hacklabLaserGCode) +import qualified Graphics.Implicit.Export.PolylineFormats as PolylineFormats (svg, hacklabLaserGCode, dxf2) import qualified Graphics.Implicit.Export.TriangleMeshFormats as TriangleMeshFormats (stl, binaryStl, jsTHREE) import qualified Graphics.Implicit.Export.NormedTriangleMeshFormats as NormedTriangleMeshFormats (obj) import qualified Graphics.Implicit.Export.SymbolicFormats as SymbolicFormats (scad2, scad3) @@ -64,6 +64,9 @@ formatObject res formatWriter = formatWriter . discreteAprox res writeSVG :: forall obj. DiscreteAproxable obj [Polyline] => ℝ -> FilePath -> obj -> IO () writeSVG res = writeObject res PolylineFormats.svg +writeDXF2 :: forall obj. DiscreteAproxable obj [Polyline] => ℝ -> FilePath -> obj -> IO () +writeDXF2 res = writeObject res PolylineFormats.dxf2 + writeSTL :: forall obj. DiscreteAproxable obj TriangleMesh => ℝ -> FilePath -> obj -> IO () writeSTL res = writeObject res TriangleMeshFormats.stl diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index 46ef9ea..a353118 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -7,13 +7,13 @@ {-# LANGUAGE OverloadedStrings #-} -module Graphics.Implicit.Export.PolylineFormats (svg, hacklabLaserGCode) where +module Graphics.Implicit.Export.PolylineFormats (svg, hacklabLaserGCode, dxf2) where -import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max) +import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max, length) import Graphics.Implicit.Definitions (Polyline, ℝ, ℝ2) -import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, mempty, toLazyText, mconcat, bf, (<>), buildTruncFloat) +import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, mempty, toLazyText, mconcat, bf, (<>), buildInt, buildTruncFloat) import Text.Blaze.Svg.Renderer.Text (renderSvg) import Text.Blaze.Svg11 ((!),docTypeSvg,g,polyline,toValue) @@ -48,24 +48,89 @@ svg plines = renderSvg . svg11 . svg' $ plines -- Instead of setting styles on every polyline, we wrap the lines in a group element and set the styles on it: thinBlueGroup = g ! A.stroke "rgb(0,0,255)" ! A.strokeWidth (stringValue $ show strokeWidth) ! A.fill "none" -- obj +-- DXF2 export in 2D. conforming to AutoCAD R12/13. +dxf2 :: [Polyline] -> Text +dxf2 plines = toLazyText $ dxf2Header <> dxf2Tables <> dxf2Blocks <> dxf2Entities + where + dxf2Header :: Builder + dxf2Header = mconcat [ + " 0\n", "SECTION\n", + " 2\n", "HEADER\n", + " 9\n", "$ACADVER\n", + " 1\n", "AC1009\n", + " 9\n", "$LIMMIN\n", + " 10\n", buildTruncFloat dxfxmin, "\n", + " 20\n", buildTruncFloat dxfymin, "\n", + " 9\n", "$LIMMAX\n", + " 10\n", buildTruncFloat dxfxmax, "\n", + " 20\n", buildTruncFloat dxfymax, "\n", + " 9\n", "$LUPREC\n", + " 70\n", "4\n", + " 0\n", "ENDSEC\n" + ] + dxf2Tables :: Builder + dxf2Tables = mconcat [ + " 0\n", "SECTION\n", + " 2\n", "TABLES\n", + " 0\n", "ENDSEC\n" + ] + dxf2Blocks :: Builder + dxf2Blocks = mconcat [ + " 0\n", "SECTION\n", + " 2\n", "BLOCKS\n", + " 0\n", "ENDSEC\n" + ] + dxf2Entities :: Builder + dxf2Entities = mconcat [ + " 0\n", "SECTION\n", + " 2\n", "ENTITIES\n", + mconcat [ buildPolyline orderedPolyline | orderedPolyline <- (orderPolylines plines)], + " 0\n", "ENDSEC\n" + ] + buildPolyline :: [ℝ2] -> Builder + buildPolyline singlePolyline = + mconcat [ + " 0\n", "POLYLINE\n", + " 8\n", "0\n", + " 6\n", "CONTINUOUS\n", + " 66\n", "1\n", + " 62\n", buildInt $ length singlePolyline,"\n", + " 10\n", "0.0\n", + " 20\n", "0.0\n", + " 30\n", "0.0000\n", + mconcat [ buildVertex vertex | vertex <- singlePolyline ], + " 0\n", "SEQEND\n" + ] + buildVertex :: (ℝ2) -> Builder + buildVertex (x1,y1) = + mconcat [ + " 0\n", "VERTEX\n", + " 8\n", "0\n", + " 10\n", buildTruncFloat x1, "\n", + " 20\n", buildTruncFloat y1, "\n" + ] + (dxfxmin, dxfxmax, dxfymin, dxfymax) = (minimum xs, maximum xs, minimum ys, maximum ys) + (xs, ys) = unzip (concat plines) + +orderPolylines :: [Polyline] -> [Polyline] +orderPolylines plines = + map snd . sortBy (\(a,_) (b, _) -> compare a b) . map (\x -> (polylineRadius x, x)) $ plines + where + polylineRadius :: [ℝ2] -> ℝ + polylineRadius [] = 0 + polylineRadius polyline' = max (xmax' - xmin') (ymax' - ymin') + where + ((xmin', xmax'), (ymin', ymax')) = polylineRadius' polyline' + polylineRadius' :: [ℝ2] -> (ℝ2, ℝ2) + polylineRadius' [] = ((0,0),(0,0)) + polylineRadius' [(x,y)] = ((x,x),(y,y)) + polylineRadius' ((x,y):ps) = ((min x xmin,max x xmax),(min y ymin, max y ymax)) + where ((xmin, xmax), (ymin, ymax)) = polylineRadius' ps + +-- Gcode generation for the laser cutter in HackLab. Complies with https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=823374 hacklabLaserGCode :: [Polyline] -> Text -hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpretPolyline orderedPolylines) <> gcodeFooter +hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpretPolyline (orderPolylines polylines)) <> gcodeFooter where - orderedPolylines :: [Polyline] - orderedPolylines = - map snd - . sortBy (\(a,_) (b, _) -> compare a b) - . map (\x -> (polylineRadius x, x)) - $ polylines - polylineRadius :: [ℝ2] -> ℝ - polylineRadius [] = 0 - polylineRadius polyline' = max (xmax' - xmin') (ymax' - ymin') where - ((xmin', xmax'), (ymin', ymax')) = polylineRadius' polyline' - polylineRadius' :: [ℝ2] -> (ℝ2, ℝ2) - polylineRadius' [] = ((0,0),(0,0)) - polylineRadius' [(x,y)] = ((x,x),(y,y)) - polylineRadius' ((x,y):ps) = ((min x xmin,max x xmax),(min y ymin, max y ymax)) - where ((xmin, xmax), (ymin, ymax)) = polylineRadius' ps gcodeHeader :: Builder gcodeHeader = mconcat [ "(generated by ImplicitCAD, based of hacklab wiki example)\n" diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index 3fad7aa..4757528 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -11,14 +11,16 @@ -- The entry point for parsing an ExtOpenScad program. module Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) where -import Prelude(Char, Either, String, Maybe(Just, Nothing), Monad, return, fmap, ($), (>>), Bool(False, True), map) +import Prelude(Char, Either, String, Monad, return, fmap, ($), (>>), Bool(False, True), map) + +import Data.Maybe(Maybe(Just, Nothing)) + +import Data.Functor.Identity(Identity) -- We use parsec to parse. import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, sourceColumn, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, ParseError, many, noneOf, Line, Column, (<|>), ()) import Text.Parsec.Prim (ParsecT) -import Data.Functor.Identity(Identity) - import Graphics.Implicit.ExtOpenScad.Definitions (Pattern(Name), Statement(DoNothing, NewModule, Include, Echo, If, For, ModuleCall,(:=)),Expr(LamE), StatementI(StatementI)) import Graphics.Implicit.ExtOpenScad.Parser.Util (genSpace, tryMany, stringGS, (*<|>), (?:), patternMatcher, variableSymb) diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index fd2f813..6bc0cbc 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -8,7 +8,9 @@ module Graphics.Implicit.ObjectUtil.GetBox3 (getBox3) where -import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), Maybe(Nothing, Just), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise) +import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise) + +import Data.Maybe(Maybe(Nothing, Just)) import Graphics.Implicit.Definitions (ℝ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), (⋯*)) import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) diff --git a/implicit.cabal b/implicit.cabal index 3738c5e..bac3343 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -1,5 +1,5 @@ Name: implicit -Version: 0.2.0 +Version: 0.2.1 Cabal-version: >= 1.8 Tested-with: GHC >= 8.2 Build-type: Simple @@ -53,7 +53,6 @@ Library -Wextra -Weverything - Extensions: Exposed-modules: Graphics.Implicit Graphics.Implicit.Definitions diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index b96fc30..0496021 100644 --- a/programs/extopenscad.hs +++ b/programs/extopenscad.hs @@ -14,7 +14,7 @@ import Prelude (Read(readsPrec), Maybe(Just, Nothing), Either(Left, Right), IO, FilePath, Show, Eq, Ord, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, print, (>>=), lookup) -- Our Extended OpenScad interpreter, and functions to write out files in designated formats. -import Graphics.Implicit (runOpenscad, writeSVG, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeGCodeHacklabLaser, writePNG2, writePNG3) +import Graphics.Implicit (runOpenscad, writeSVG, writeDXF2, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeGCodeHacklabLaser, writePNG2, writePNG3) -- Functions for finding a box around an object, so we can define the area we need to raytrace inside of. import Graphics.Implicit.ObjectUtil (getBox2, getBox3) @@ -71,7 +71,8 @@ data OutputFormat | GCode | STL | OBJ --- | AMF +-- | 3MF + | DXF deriving (Show, Eq, Ord) -- A list mapping file extensions to output formats. @@ -84,7 +85,8 @@ formatExtensions = , ("gcode", GCode) , ("stl", STL) , ("obj", OBJ) --- , ("amf", AMF) +-- , ("3mf", 3MF) + , ("dxf", DXF) ] -- Lookup an output format for a given output file. Throw an error if one cannot be found. @@ -185,6 +187,7 @@ export2 :: Maybe OutputFormat -> ℝ -> FilePath -> SymbolicObj2 -> IO () export2 posFmt res output obj = case posFmt of Just SVG -> writeSVG res output obj + Just DXF -> writeDXF2 res output obj Just SCAD -> writeSCAD2 res output obj Just PNG -> writePNG2 res output obj Just GCode -> writeGCodeHacklabLaser res output obj From ce12aa250a1ceb01eef8c3a9576a4ecc54ff12d9 Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Wed, 13 Mar 2019 11:43:45 -0400 Subject: [PATCH 109/227] update to stackage lts 13.12 --- stack.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stack.yaml b/stack.yaml index 230dc05..ef45380 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,14 +1,15 @@ # For more information, see: https://github.com/commercialhaskell/stack/blob/master/doc/yaml_configuration.md # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) -resolver: lts-9.8 +resolver: lts-13.12 # Local packages, usually specified by relative directory name packages: - '.' # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3) -extra-deps: [] +extra-deps: +- storable-endian-0.2.6 # Override default flag values for local packages and extra-deps flags: {} From d421991daa5f99a77164d80191ddbdaec64673fa Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Wed, 13 Mar 2019 11:43:55 -0400 Subject: [PATCH 110/227] gitignore .stack-work --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index da54a8f..95c9f6d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ *.stl dist/ Setup -docs/iscad.md \ No newline at end of file +docs/iscad.md +.stack-work/ From 0a4ef5070b3dc0ef10755acc6ac9ce983fc0371d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 23 Apr 2019 18:50:23 +0000 Subject: [PATCH 111/227] use module in module to reduce duplication. --- Graphics/Implicit.hs | 69 ++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/Graphics/Implicit.hs b/Graphics/Implicit.hs index 967d325..3b02792 100644 --- a/Graphics/Implicit.hs +++ b/Graphics/Implicit.hs @@ -9,65 +9,38 @@ to be accessible to an end user who is compiling objects using this haskell library. -} --- MAYBEFIXME: impliment slice operation , regularPolygon and zsurface primitives. - -module Graphics.Implicit( - -- Operations - translate, - scale, - complement, union, intersect, difference, - unionR, intersectR, differenceR, - shell, - extrudeR, - extrudeRotateR, - extrudeRM, - extrudeOnEdgeOf, - -- Primitives - sphere, - rect3R, - circle, - cylinder, - cylinder2, - rectR, - polygonR, - rotateExtrude, - rotate3, - rotate3V, - pack3, - rotate, - pack2, - -- Export - writeSVG, - writeDXF2, - writeSTL, - writeBinSTL, - writeOBJ, - writeTHREEJS, - writeSCAD2, - writeSCAD3, - writeGCodeHacklabLaser, - writePNG2, - writePNG3, - runOpenscad, - implicit, - SymbolicObj2, - SymbolicObj3 +module Graphics.Implicit ( + module P, + module E, + module D, + writeSVG, + writeDXF2, + writeSTL, + writeBinSTL, + writeOBJ, + writeTHREEJS, + writeSCAD2, + writeSCAD3, + writeGCodeHacklabLaser, + writePNG2, + writePNG3 ) where import Prelude(FilePath, IO) -- The primitive objects, and functions for manipulating them. -import Graphics.Implicit.Primitives (translate, scale, complement, union, intersect, difference, unionR, intersectR, differenceR, shell, extrudeR, extrudeRM, extrudeRotateR, extrudeOnEdgeOf, sphere, rect3R, circle, cylinder, cylinder2, rectR, polygonR, rotateExtrude, rotate3, rotate3V, pack3, rotate, pack2, implicit) +-- MAYBEFIXME: impliment slice operation, regularPolygon and zsurface primitives. +import Graphics.Implicit.Primitives as P (translate, scale, complement, union, intersect, difference, unionR, intersectR, differenceR, shell, extrudeR, extrudeRM, extrudeRotateR, extrudeOnEdgeOf, sphere, rect3R, circle, cylinder, cylinder2, rectR, polygonR, rotateExtrude, rotate3, rotate3V, pack3, rotate, pack2, implicit) -- The Extended OpenScad interpreter. -import Graphics.Implicit.ExtOpenScad (runOpenscad) +import Graphics.Implicit.ExtOpenScad as E (runOpenscad) + +-- typesclasses and types defining the world, or part of the world. +import Graphics.Implicit.Definitions as D (ℝ, SymbolicObj2, SymbolicObj3) -- Functions for writing files based on the result of operations on primitives. import qualified Graphics.Implicit.Export as Export (writeSVG, writeDXF2, writeSTL, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeTHREEJS, writeGCodeHacklabLaser, writePNG) --- Datatypes/classes defining the world, or part of the world. -import Graphics.Implicit.Definitions (ℝ, SymbolicObj2, SymbolicObj3) - -- We want Export to be a bit less polymorphic -- (so that types will collapse nicely) From 0fa6ebaa79acb9f91a453c08a1093aa06eefc345 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 23 Apr 2019 18:51:43 +0000 Subject: [PATCH 112/227] ordering and comment changes. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index 94d5b30..0cc8ca9 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -8,18 +8,21 @@ -- FIXME: why is this here? {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} +-- | A module for retrieving approximate represententations of objects. module Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAprox) where import Prelude((-), (/), ($), (<), map, round, (+), maximum, abs, (*), fromIntegral, max, realToFrac) +-- Definitions for our number system, objects, and the things we can use to approximately represent objects. import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, SymbolicObj2, SymbolicObj3, Polyline, TriangleMesh, NormedTriangleMesh) -import Graphics.Implicit.ObjectUtil (getImplicit3, getImplicit2, getBox3, getBox2) +import Graphics.Implicit.ObjectUtil (getImplicit2, getImplicit3, getBox2, getBox3) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.Util (normTriangle) +-- We are the only ones that use this. import Graphics.Implicit.Export.RayTrace (dynamicImage, Color, average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) import Codec.Picture (DynamicImage, generateImage, PixelRGBA8(PixelRGBA8)) @@ -27,7 +30,6 @@ import Codec.Picture (DynamicImage, generateImage, PixelRGBA8(PixelRGBA8)) import Data.VectorSpace ((^+^), (^/), (*^), (^-^)) import Data.AffineSpace ((.-^), (.+^)) - -- | There is a discrete way to aproximate this object. -- eg. Aproximating a 3D object with a triangle mesh -- would be DiscreteApproxable Obj3 TriangleMesh From 7f7ce7d29e6a3a8e992fd2c319cedec70988e7ef Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 23 Apr 2019 18:54:39 +0000 Subject: [PATCH 113/227] remove incorrect comment. --- Graphics/Implicit/Export/Render/Definitions.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Graphics/Implicit/Export/Render/Definitions.hs b/Graphics/Implicit/Export/Render/Definitions.hs index 0f1080b..2225ee8 100644 --- a/Graphics/Implicit/Export/Render/Definitions.hs +++ b/Graphics/Implicit/Export/Render/Definitions.hs @@ -14,8 +14,6 @@ data TriSquare = Sq (ℝ3,ℝ3,ℝ3) ℝ ℝ2 ℝ2 | Tris TriangleMesh --- FIXME: For use with Parallel.Strategies later - instance NFData TriSquare where rnf (Sq b z xS yS) = rnf (b,z,xS,yS) rnf (Tris tris) = rnf tris From 41da3e42cd47bc5945c65f53904f8e4e2b618fe0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 23 Apr 2019 18:56:41 +0000 Subject: [PATCH 114/227] update comment. --- Graphics/Implicit/ExtOpenScad.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad.hs b/Graphics/Implicit/ExtOpenScad.hs index 7a60cf9..92eaac1 100644 --- a/Graphics/Implicit/ExtOpenScad.hs +++ b/Graphics/Implicit/ExtOpenScad.hs @@ -22,7 +22,7 @@ import Control.Monad (mapM_) import Control.Monad.State (runStateT) import System.Directory (getCurrentDirectory) --- Small wrapper to handle parse errors, etc. +-- | Small wrapper of our parser to handle parse errors, etc. runOpenscad :: String -> Either ParseError (IO (VarLookup, [SymbolicObj2], [SymbolicObj3])) runOpenscad source = let From e118565de600a6331b4264c271eb9e48ab72fa68 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 23 Apr 2019 19:00:36 +0000 Subject: [PATCH 115/227] update comments. --- .../Implicit/ExtOpenScad/Parser/Statement.hs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index 4757528..9141dd0 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -90,7 +90,7 @@ suite = (fmap return computation <|> do return stmts ) " suite" --- commenting out a comuptation: use % or * before the statement, and it will not be run. +-- | commenting out a comuptation: use % or * before the statement, and it will not be run. throwAway :: GenParser Char st StatementI throwAway = do line <- lineNumber @@ -101,7 +101,7 @@ throwAway = do _ <- computation return $ StatementI line column DoNothing --- An include! Basically, inject another openscad file here... +-- | An include! Basically, inject another extopenscad file here... include :: GenParser Char st StatementI include = (do line <- lineNumber @@ -178,7 +178,7 @@ forStatementI = loopContent <- suite return $ StatementI line column $ For lvalue vexpr loopContent --- parse a call to a module. +-- | parse a call to a module. userModule :: GenParser Char st StatementI userModule = do line <- lineNumber @@ -190,7 +190,7 @@ userModule = do s <- suite *<|> (stringGS " ; " >> return []) return $ StatementI line column $ ModuleCall name args s --- declare a module. +-- | declare a module. userModuleDeclaration :: GenParser Char st StatementI userModuleDeclaration = do line <- lineNumber @@ -203,7 +203,7 @@ userModuleDeclaration = do s <- suite return $ StatementI line column $ NewModule newModuleName args s --- parse the arguments passed to a module. +-- | parse the arguments passed to a module. moduleArgsUnit :: GenParser Char st [(Maybe String, Expr)] moduleArgsUnit = do _ <- stringGS " ( " @@ -230,7 +230,7 @@ moduleArgsUnit = do _ <- stringGS " ) " return args --- parse the arguments in the module declaration. +-- | parse the arguments in the module declaration. moduleArgsUnitDecl :: GenParser Char st [(String, Maybe Expr)] moduleArgsUnitDecl = do _ <- stringGS " ( " @@ -255,14 +255,12 @@ moduleArgsUnitDecl = do _ <- stringGS " ) " return argTemplate --- find the line number. used when generating errors. +-- | Find the line number. Used when generating errors. lineNumber :: forall s u (m :: * -> *). Monad m => ParsecT s u m Line lineNumber = fmap sourceLine getPosition ---FIXME: use the below function to improve error reporting. - --- find the column number. SHOULD be used when generating errors. +-- | Find the column number. Used when generating errors. columnNumber :: forall s u (m :: * -> *). Monad m => ParsecT s u m Column columnNumber = fmap sourceColumn getPosition From 807accf38040f63f0bf32a021b55373405543f39 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 26 Apr 2019 20:04:45 +0000 Subject: [PATCH 116/227] comment changes, and minor ordering changes. --- tests/ParserSpec/Statement.hs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index f0f64a6..c76de3a 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -2,7 +2,7 @@ -- Copyright (C) 2014-2017, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --- statement related hspec tests. +-- | Statement related hspec tests. module ParserSpec.Statement (statementSpec) where import Prelude (String, Maybe(Just), Bool(True), ($)) @@ -36,13 +36,13 @@ single st = [StatementI 1 1 st] call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI call name args stmts = StatementI 1 1 (ModuleCall name args stmts) --- test a simple if block. +-- | Test a simple if block. ifSpec :: Spec ifSpec = it "parses" $ "if (true) { a(); } else { b(); }" --> single ( If (bool True) [call "a" [] []] [call "b" [] []]) --- test assignments. +-- | Test assignments. assignmentSpec :: Spec assignmentSpec = do it "parses correctly" $ @@ -51,7 +51,7 @@ assignmentSpec = do "[x, y] = [1, 2];" --> single (ListP [Name "x", Name "y"] := ListE [num 1, num 2]) it "handles the function keyword and definitions" $ "function foo(x, y) = x * y;" --> single fooFunction - it "nested indexing" $ + it "handles nested indexing" $ "x = [y[0] - z * 2];" --> single ( Name "x" := ListE [minus [index [Var "y", num 0], mult [Var "z", num 2]]]) @@ -60,20 +60,22 @@ assignmentSpec = do fooFunction = Name "foo" := LamE [Name "x", Name "y"] (mult [Var "x", Var "y"]) +-- | the parser fails on as empty file. This can't be right. emptyFileIssue :: Expectation -> Expectation emptyFileIssue _ = pendingWith "parser should probably allow empty files" +-- | Our entry points. Test all of the statements. statementSpec :: Spec statementSpec = do - describe "assignment" assignmentSpec - describe "if" ifSpec describe "empty file" $ it "returns an empty list" $ emptyFileIssue $ "" --> [] + describe "assignment" assignmentSpec + describe "if" ifSpec describe "line comment" $ it "parses as empty" $ emptyFileIssue $ "// foish bar\n" --> [] describe "module call" $ - it "parses" $ "foo();" --> single (ModuleCall "foo" [] []) + it "parses" $ "foo();" --> single (ModuleCall "foo" [] []) describe "difference of two cylinders" $ it "parses correctly" $ "difference(){ cylinder(r=5,h=20); cylinder(r=2,h=20); }" From c71b5cdbe825c7c71e7af68f36c3542f377a3176 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 28 Apr 2019 09:33:16 +0000 Subject: [PATCH 117/227] take column into account when testing statements. --- tests/ParserSpec/Statement.hs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index c76de3a..2ae5057 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -9,8 +9,6 @@ import Prelude (String, Maybe(Just), Bool(True), ($)) import Test.Hspec (Spec, Expectation, shouldBe, shouldSatisfy, it, pendingWith, describe) --- import Text.ParserCombinators.Parsec () - import ParserSpec.Util (bool, num, minus, mult, index) import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) @@ -20,27 +18,31 @@ import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Data.Either (Either(Right), isLeft) --- an expectation that a string is equivalent to a statement. +import Text.ParserCombinators.Parsec (Line, Column) + +-- | an expectation that a string is equivalent to a statement. infixr 1 --> (-->) :: String -> [StatementI] -> Expectation (-->) source stmts = parseProgram source `shouldBe` Right stmts --- an expectation that a string generates an error. +-- | an expectation that a string generates an error. parsesAsError :: String -> Expectation parsesAsError source = parseProgram source `shouldSatisfy` isLeft +-- | A single statement. single :: Statement StatementI -> [StatementI] single st = [StatementI 1 1 st] -call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI -call name args stmts = StatementI 1 1 (ModuleCall name args stmts) +-- | A function call. +call :: Symbol -> Column -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI +call name position args stmts = StatementI 1 position (ModuleCall name args stmts) -- | Test a simple if block. ifSpec :: Spec ifSpec = it "parses" $ "if (true) { a(); } else { b(); }" --> - single ( If (bool True) [call "a" [] []] [call "b" [] []]) + single ( If (bool True) [call "a" 13 [] []] [call "b" 27 [] []]) -- | Test assignments. assignmentSpec :: Spec @@ -81,10 +83,10 @@ statementSpec = do "difference(){ cylinder(r=5,h=20); cylinder(r=2,h=20); }" --> single ( ModuleCall "difference" [] [ - call "cylinder" [(Just "r", num 5.0), + call "cylinder" 15 [(Just "r", num 5.0), (Just "h", num 20.0)] [], - call "cylinder" [(Just "r", num 2.0), + call "cylinder" 35 [(Just "r", num 2.0), (Just "h", num 20.0)] []]) describe "empty module definition" $ From 8303ff44f7db3e50ab8f6d71056cf94bc16a32c9 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 28 Apr 2019 09:33:16 +0000 Subject: [PATCH 118/227] take column into account when testing statements. --- tests/ParserSpec/Statement.hs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index f0f64a6..92b1e82 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -9,8 +9,6 @@ import Prelude (String, Maybe(Just), Bool(True), ($)) import Test.Hspec (Spec, Expectation, shouldBe, shouldSatisfy, it, pendingWith, describe) --- import Text.ParserCombinators.Parsec () - import ParserSpec.Util (bool, num, minus, mult, index) import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) @@ -20,27 +18,31 @@ import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Data.Either (Either(Right), isLeft) --- an expectation that a string is equivalent to a statement. +import Text.ParserCombinators.Parsec (Line, Column) + +-- | an expectation that a string is equivalent to a statement. infixr 1 --> (-->) :: String -> [StatementI] -> Expectation (-->) source stmts = parseProgram source `shouldBe` Right stmts --- an expectation that a string generates an error. +-- | an expectation that a string generates an error. parsesAsError :: String -> Expectation parsesAsError source = parseProgram source `shouldSatisfy` isLeft +-- | A single statement. single :: Statement StatementI -> [StatementI] single st = [StatementI 1 1 st] -call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI -call name args stmts = StatementI 1 1 (ModuleCall name args stmts) +-- | A function call. +call :: Symbol -> Column -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI +call name position args stmts = StatementI 1 position (ModuleCall name args stmts) -- test a simple if block. ifSpec :: Spec ifSpec = it "parses" $ "if (true) { a(); } else { b(); }" --> - single ( If (bool True) [call "a" [] []] [call "b" [] []]) + single ( If (bool True) [call "a" 13 [] []] [call "b" 27 [] []]) -- test assignments. assignmentSpec :: Spec @@ -79,10 +81,10 @@ statementSpec = do "difference(){ cylinder(r=5,h=20); cylinder(r=2,h=20); }" --> single ( ModuleCall "difference" [] [ - call "cylinder" [(Just "r", num 5.0), + call "cylinder" 15 [(Just "r", num 5.0), (Just "h", num 20.0)] [], - call "cylinder" [(Just "r", num 2.0), + call "cylinder" 35 [(Just "r", num 2.0), (Just "h", num 20.0)] []]) describe "empty module definition" $ From 596ca5aca39bd49b07c325848ca648036aa620cd Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 28 Apr 2019 09:33:16 +0000 Subject: [PATCH 119/227] take column into account when testing statements. --- tests/ParserSpec/Statement.hs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index f0f64a6..92b1e82 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -9,8 +9,6 @@ import Prelude (String, Maybe(Just), Bool(True), ($)) import Test.Hspec (Spec, Expectation, shouldBe, shouldSatisfy, it, pendingWith, describe) --- import Text.ParserCombinators.Parsec () - import ParserSpec.Util (bool, num, minus, mult, index) import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) @@ -20,27 +18,31 @@ import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Data.Either (Either(Right), isLeft) --- an expectation that a string is equivalent to a statement. +import Text.ParserCombinators.Parsec (Line, Column) + +-- | an expectation that a string is equivalent to a statement. infixr 1 --> (-->) :: String -> [StatementI] -> Expectation (-->) source stmts = parseProgram source `shouldBe` Right stmts --- an expectation that a string generates an error. +-- | an expectation that a string generates an error. parsesAsError :: String -> Expectation parsesAsError source = parseProgram source `shouldSatisfy` isLeft +-- | A single statement. single :: Statement StatementI -> [StatementI] single st = [StatementI 1 1 st] -call :: Symbol -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI -call name args stmts = StatementI 1 1 (ModuleCall name args stmts) +-- | A function call. +call :: Symbol -> Column -> [(Maybe Symbol, Expr)] -> [StatementI] -> StatementI +call name position args stmts = StatementI 1 position (ModuleCall name args stmts) -- test a simple if block. ifSpec :: Spec ifSpec = it "parses" $ "if (true) { a(); } else { b(); }" --> - single ( If (bool True) [call "a" [] []] [call "b" [] []]) + single ( If (bool True) [call "a" 13 [] []] [call "b" 27 [] []]) -- test assignments. assignmentSpec :: Spec @@ -79,10 +81,10 @@ statementSpec = do "difference(){ cylinder(r=5,h=20); cylinder(r=2,h=20); }" --> single ( ModuleCall "difference" [] [ - call "cylinder" [(Just "r", num 5.0), + call "cylinder" 15 [(Just "r", num 5.0), (Just "h", num 20.0)] [], - call "cylinder" [(Just "r", num 2.0), + call "cylinder" 35 [(Just "r", num 2.0), (Just "h", num 20.0)] []]) describe "empty module definition" $ From 070ad28f68886cf385b01e93366e0431cc29a1bc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 05:45:12 +0100 Subject: [PATCH 120/227] chose a better letter. --- Graphics/Implicit.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit.hs b/Graphics/Implicit.hs index 3b02792..5756fee 100644 --- a/Graphics/Implicit.hs +++ b/Graphics/Implicit.hs @@ -12,7 +12,7 @@ module Graphics.Implicit ( module P, module E, - module D, + module W, writeSVG, writeDXF2, writeSTL, @@ -36,7 +36,7 @@ import Graphics.Implicit.Primitives as P (translate, scale, complement, union, i import Graphics.Implicit.ExtOpenScad as E (runOpenscad) -- typesclasses and types defining the world, or part of the world. -import Graphics.Implicit.Definitions as D (ℝ, SymbolicObj2, SymbolicObj3) +import Graphics.Implicit.Definitions as W (ℝ, SymbolicObj2, SymbolicObj3) -- Functions for writing files based on the result of operations on primitives. import qualified Graphics.Implicit.Export as Export (writeSVG, writeDXF2, writeSTL, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeTHREEJS, writeGCodeHacklabLaser, writePNG) From 2ed29166570f712f136cf13fca31747f32ae8b4a Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 06:52:09 +0100 Subject: [PATCH 121/227] Use more correct types. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index 0cc8ca9..4d9af36 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -11,10 +11,10 @@ -- | A module for retrieving approximate represententations of objects. module Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAprox) where -import Prelude((-), (/), ($), (<), map, round, (+), maximum, abs, (*), fromIntegral, max, realToFrac) +import Prelude((-), (/), ($), (<), map, round, (+), maximum, abs, (*), fromIntegral, max, realToFrac, Int) -- Definitions for our number system, objects, and the things we can use to approximately represent objects. -import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, SymbolicObj2, SymbolicObj3, Polyline, TriangleMesh, NormedTriangleMesh) +import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj2, SymbolicObj3, Polyline, TriangleMesh, NormedTriangleMesh) import Graphics.Implicit.ObjectUtil (getImplicit2, getImplicit3, getBox2, getBox3) @@ -57,7 +57,7 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where camera = Camera (x1-deviation*(2.2::ℝ), avY, avZ) (0, -1, 0) (0,0, -1) 1.0 lights = [Light (x1-deviation*(1.5::ℝ), y1 - (0.4::ℝ)*(y2-y1), avZ) ((0.03::ℝ)*deviation) ] scene = Scene obj (PixelRGBA8 200 200 230 255) lights (PixelRGBA8 255 255 255 0) - pixelRenderer :: Fastℕ -> Fastℕ -> Color + pixelRenderer :: Int -> Int -> Color pixelRenderer a b = renderScreen ((fromIntegral a :: ℝ)/w - (0.5::ℝ)) ((fromIntegral b :: ℝ)/h - (0.5 ::ℝ)) renderScreen :: ℝ -> ℝ -> Color @@ -88,7 +88,7 @@ instance DiscreteAproxable SymbolicObj2 DynamicImage where (p1@(x1,_), p2@(_,y2)) = getBox2 symbObj (dx, dy) = p2 ^-^ p1 dxy = max dx dy - pixelRenderer :: Fastℕ -> Fastℕ -> Color + pixelRenderer :: Int -> Int -> Color pixelRenderer mya myb = mycolor where xy a b = ((x1,y2) .-^ (dxy-dx, dy-dxy)^/2) .+^ dxy*^(a/w, -b/h) From fa281cc52880e575f260bd89b3bbf510f55ca53f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 07:00:22 +0100 Subject: [PATCH 122/227] remove unneeded forall. --- Graphics/Implicit/Export/RayTrace.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index 05d2077..a892535 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -2,9 +2,6 @@ -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --- Allow us to use explicit foralls when writing function type declarations. -{-# LANGUAGE ExplicitForAll #-} - -- FIXME: why are these needed? {-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, FlexibleContexts #-} @@ -58,7 +55,7 @@ average l = let ((rs, gs), (bs, as)) = (unzip *** unzip) . unzip $ map (\(PixelRGBA8 r g b a) -> ((fromIntegral r, fromIntegral g), (fromIntegral b, fromIntegral a))) - l :: (([ℝ], [ℝ]), ([ℝ],[ℝ])) + l :: (([ℝ], [ℝ]), ([ℝ], [ℝ])) n = fromIntegral $ length l :: ℝ (r', g', b', a') = (sum rs/n, sum gs/n, sum bs/n, sum as/n) in PixelRGBA8 @@ -156,7 +153,7 @@ traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultCo deriv = (dirDeriv (1,0,0), dirDeriv (0,1,0), dirDeriv (0,0,1)) normal = normalized deriv unitV = normalized v' - proj :: forall v. InnerSpace v => v -> v -> v + proj :: InnerSpace v => v -> v -> v proj a' b' = (a'⋅b')*^b' dist = vectorDistance p lightPos illumination = max 0 (normal ⋅ unitV) * lightIntensity * (25 /dist) From d2e1cade87a6d7cf49706b2b33b13f5642f9e18f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 20:23:42 +0100 Subject: [PATCH 123/227] use both and allthree from Definitions. --- Graphics/Implicit/Export/Render.hs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 5e4edc0..74a37e1 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.Export.Render (getMesh, getContour) where import Prelude(Float, Bool, ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, realToFrac, (==), (||), filter, not, reverse, (.), Eq, concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/)) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), both, allthree) import Data.VectorSpace ((^-^)) @@ -67,10 +67,6 @@ import Control.DeepSeq (NFData) -- For the 2D case, we need one last thing, cleanLoopsFromSegs: import Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs) --- apply a function to all three items in the provided tuple. -allthree :: forall t b. (t -> b) -> (t, t, t) -> (b, b, b) -allthree f (x,y,z) = (f x, f y, f z) - -- FIXME: res should be ℝ3, not ℝ. getMesh :: ℝ3 -> ℝ3 -> ℝ -> Obj3 -> TriangleMesh getMesh p1@(x1,y1,z1) p2 res obj = @@ -206,10 +202,6 @@ cleanupTris tris = isDegenerateTri (a, b, c) = isDegenerateTriFloat (floatPoint a, floatPoint b, floatPoint c) in filter (not . isDegenerateTri) tris --- apply a function to both items in the provided tuple. -both :: forall t b. (t -> b) -> (t, t) -> (b, b) -both f (x,y) = (f x, f y) - -- getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ -> Obj2 -> [Polyline] getContour p1@(x1, y1) p2 res obj = From 8d9f7400a3b9d15cee94a7b64615086b4575c075 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 20:37:57 +0100 Subject: [PATCH 124/227] inline things ghc thinks should be inlined. --- Graphics/Implicit/Export/Util.hs | 1 + Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs | 1 + Graphics/Implicit/ExtOpenScad/Util/StateC.hs | 1 + 3 files changed, 3 insertions(+) diff --git a/Graphics/Implicit/Export/Util.hs b/Graphics/Implicit/Export/Util.hs index 8da81f5..73468b5 100644 --- a/Graphics/Implicit/Export/Util.hs +++ b/Graphics/Implicit/Export/Util.hs @@ -46,6 +46,7 @@ centroid pts = where norm :: Fractional a => a norm = recip $ realToFrac $ length pts +{-# INLINABLE centroid #-} {--- If we need to make a 2D mesh finer... divideMesh2To :: ℝ -> [(ℝ2, ℝ2, ℝ2)] -> [(ℝ2, ℝ2, ℝ2)] diff --git a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs index 0a8c1f7..56d7814 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs @@ -43,6 +43,7 @@ argument name = _ -> "arg " ++ show oObjVal ++ " not compatible with " ++ name -- Using /= Nothing would require Eq desiredType APFailIf (isNothing val) errmsg $ APTerminator $ fromJust val +{-# INLINABLE argument #-} doc :: forall a. ArgParser a -> String -> ArgParser a doc (AP name defMaybeVal _ next) newDoc = AP name defMaybeVal newDoc next diff --git a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs index f8d2ac6..06302e9 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs @@ -69,6 +69,7 @@ getRelPath relPath = do errorC :: forall (m :: * -> *) a. (Show a, MonadIO m) => a -> a -> String -> m () errorC lineN columnN err = liftIO $ putStrLn $ "On line " ++ show lineN ++ ", column " ++ show columnN ++ ": " ++ err +{-# INLINABLE errorC #-} mapMaybeM :: forall t (m :: * -> *) a. Monad m => (t -> m a) -> Maybe t -> m (Maybe a) mapMaybeM f (Just a) = do From 3328d2bbcb332ff6bb255a94d869d0800d43f56d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 20:47:07 +0100 Subject: [PATCH 125/227] move both and allthree to Definitions. --- Graphics/Implicit/Definitions.hs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 1d12429..4700cc4 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -6,6 +6,9 @@ -- This module deliberately declares orphan instances of Show. {-# OPTIONS_GHC -fno-warn-orphans #-} +-- Allow us to use explicit foralls when writing function type declarations. +{-# LANGUAGE ExplicitForAll #-} + -- Required. FIXME: why? {-# LANGUAGE FlexibleInstances #-} @@ -18,6 +21,8 @@ module Graphics.Implicit.Definitions ( minℝ, ℕ, Fastℕ, + both, + allthree, (⋅), (⋯*), (⋯/), @@ -101,6 +106,15 @@ type ℕ = Integer -- System integers. type Fastℕ = Int +-- | apply a function to both items in the provided tuple. +both :: forall t b. (t -> b) -> (t, t) -> (b, b) +both f (x,y) = (f x, f y) + +-- | apply a function to all three items in the provided tuple. +allthree :: forall t b. (t -> b) -> (t, t, t) -> (b, b, b) +allthree f (x,y,z) = (f x, f y, f z) + + -- TODO: Find a better place for this (⋅) :: InnerSpace a => a -> a -> Scalar a (⋅) = (<.>) From be0d5393d6258f1cc253dc85c4ad85853bb62b35 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 29 Apr 2019 21:31:49 +0100 Subject: [PATCH 126/227] enable offline building, and better dependency tracking. --- Makefile | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 53d8349..7fe88bd 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,10 @@ convert=convert GHC=ghc # the location of the created extopenscad binary, for running shell based test cases. EXTOPENSCAD=dist/build/extopenscad/extopenscad +# the location of the benchmark binary, for benchmarking some implicitcad internals. +BENCHMARK=dist/build/Benchmark/Benchmark # the location of the created test binary, for running haskell test cases. +TESTFILES=$(shell find tests/ParserSpec -name '*.hs') TESTSUITE=dist/build/test-implicit/test-implicit # the location of the documentation generator. for documenting (some of) the extopenscad languagi. DOCGEN=dist/build/docgen/docgen @@ -24,7 +27,11 @@ RESOPTS=-r 50 #uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. #PROFILING= --enable-library-profiling --enable-executable-profiling -TARGETS=$(EXTOPENSCAD) $(TESTSUITE) $(DOCGEN) +LIBFILES=$(shell find Graphics -name '*.hs') +LIBTARGET=dist/build/Graphics/Implicit.o + +EXECTARGETS=$(EXTOPENSCAD) $(BENCHMARK) $(TESTSUITE) $(DOCGEN) +TARGETS=$(EXECTARGETS) $(LIBTARGET) # mark the below fake targets as unrean, so make will not get choked up if a file with one of these names is created. .PHONY: build install clean distclean nukeclean docs dist examples tests @@ -47,12 +54,15 @@ clean: Setup rm -f Examples/*.hi rm -f Examples/*.o rm -f tests/*.stl - rm -f Setup Setup.hi Setup.o - rm -rf dist/* rm -rf docs/parser.md + rm -f $(TARGETS) + rm -rf dist/build/Graphics + rm -f dist/build/libHS* # clean up before making a release. distclean: clean + rm -f Setup Setup.hi Setup.o + rm -rf dist/ rm -f `find ./ -name *~` rm -f `find ./ -name \#*\#` @@ -63,9 +73,9 @@ nukeclean: distclean # Generate documentation. docs: $(DOCGEN) ./Setup haddock - $(DOCGEN) > docs/iscad.md + $(DOCGEN) > docs/escad.md -# +# Upload to hackage? dist: $(TARGETS) ./Setup sdist @@ -74,22 +84,30 @@ examples: $(EXTOPENSCAD) cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; $(GHC) $$filename.hs -o $$filename; $$filename; } done +# generate images from the examples, so we can upload the images to our website. images: examples cd Examples && for each in `find ./ -name '*.stl' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; if [ -e $$filename.transform ] ; then echo ${stl2ps} $$each $$filename.ps `cat $$filename.transform`; else ${stl2ps} $$each $$filename.ps; fi; ${convert} $$filename.ps $$filename.png; } done -tests: $(TESTSUITE) +# hspec parser tests. +tests: $(TESTSUITE) $(TESTFILES) # cd tests && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each ${RESOPTS} ${RTSOPTS}; } done - ./dist/build/test-implicit/test-implicit + $(TESTSUITE) -# actually build a given binary. -dist/build/%: Setup dist/setup-config +# The ImplicitCAD library. +$(LIBTARGET): $(LIBFILES) + cabal build implicit + +# build a binary target with cabal. +dist/build/%: Setup dist/setup-config $(LIBTARGET) $(LIBFILES) cabal build $(word 2,$(subst /, ,$*)) +# prepare to build. dist/setup-config: Setup implicit.cabal cabal update cabal install --only-dependencies --upgrade-dependencies - cabal configure --enable-tests $(PROFILING) + cabal configure --enable-tests --enable-benchmarks $(PROFILING) +# the setup command, used to perform administrative tasks (haddock, upload to hackage, clean, etc...). Setup: Setup.*hs ghc -O2 -Wall --make Setup From 9f37cded879d1a28baaf8aba73681e79a32958d3 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 30 Apr 2019 06:43:50 +0100 Subject: [PATCH 127/227] spacing. --- tests/ParserSpec/Expr.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index 43e62b0..73f48bf 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -5,13 +5,13 @@ module ParserSpec.Expr (exprSpec) where -- Be explicit about what we import. -import Prelude (String, Bool(True, False), ($), (<*), ) +import Prelude (String, Bool(True, False), ($), (<*)) -- Hspec, for writing specs. import Test.Hspec (describe, Expectation, Spec, it, shouldBe, pendingWith, specify) -- parsed expression components. -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$)) ) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$))) -- the expression parser entry point. import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) From 4cbeae5aafda74959bffac5d137384fab7ba4f5e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 30 Apr 2019 07:47:43 +0100 Subject: [PATCH 128/227] update comments, and use types slightly better. --- programs/implicitsnap.hs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 0fa5690..381d43e 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -56,15 +56,18 @@ import System.IO.Silently (capture) import qualified Data.ByteString.Char8 as BS.Char (pack, unpack) import qualified Data.Text.Lazy as TL (unpack) +-- | The entry point. uses snap to serve a website. main :: IO () main = quickHttpServe site +-- | Our site definition. Renders requests to "render/", discards all else. site :: Snap () site = route [ ("render/", renderHandler) ] <|> writeBS "fall through" +-- | Our render/ handler. Uses source, callback, and opitional format to render an object. renderHandler :: Snap () renderHandler = method GET $ withCompression $ do modifyResponse $ setContentType "application/x-javascript" @@ -83,14 +86,12 @@ renderHandler = method GET $ withCompression $ do (Just $ BS.Char.unpack format) (_, _, _) -> writeBS "must provide source and callback as 1 GET variable each" --- Find the resolution to raytrace at. +-- | Find the resolution to raytrace at. getRes :: forall k. (Data.String.IsString k, Ord k) => (Map k OVal, [SymbolicObj2], [SymbolicObj3]) -> ℝ - --- First, use a resolution specified by a variable in the input file. +-- | If a resolution was specified in the input file, just use it. getRes (Map.lookup "$res" -> Just (ONum res), _, _) = res - --- If there was no resolution specified, use a resolution chosen for 3D objects. --- FIXME: magic numbers. +-- | If there was no resolution specified, use a resolution chosen for 3D objects. +-- FIXME: magic numbers. getRes (varlookup, _, obj:_) = let ((x1,y1,z1),(x2,y2,z2)) = getBox3 obj @@ -98,8 +99,8 @@ getRes (varlookup, _, obj:_) = in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of ONum qual | qual > 0 -> min (minimum [x,y,z]/2) ((x*y*z/qual)**(1/3) / 22) _ -> min (minimum [x,y,z]/2) ((x*y*z )**(1/3) / 22) --- Use a resolution chosen for 2D objects. --- FIXME: magic numbers. +-- | ... Or use a resolution chosen for 2D objects. +-- FIXME: magic numbers. getRes (varlookup, obj:_, _) = let (p1,p2) = getBox2 obj @@ -107,7 +108,7 @@ getRes (varlookup, obj:_, _) = in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of ONum qual | qual > 0 -> min (min x y/2) (sqrt(x*y/qual) / 30) _ -> min (min x y/2) (sqrt(x*y ) / 30) --- fallthrough value. +-- | fallthrough value. getRes _ = 1 {- @@ -139,12 +140,14 @@ getRes (varlookup, obj2s, obj3s) = else -1 -} -getWidth :: forall t. (t, [SymbolicObj2], [SymbolicObj3]) -> ℝ +-- | get the maximum dimension of the object being rendered. +-- FIXME: shouldn't this get the diagonal across the box? +getWidth :: (String, [SymbolicObj2], [SymbolicObj3]) -> ℝ getWidth (_, _, obj:_) = maximum [x2-x1, y2-y1, z2-z1] where ((x1,y1,z1),(x2,y2,z2)) = getBox3 obj getWidth (_, obj:_, _) = max (x2-x1) (y2-y1) where ((x1,y1),(x2,y2)) = getBox2 obj -getWidth (_, [], []) = 0 +getWidth (_, [], []) = 0 -- | Give an openscad object to run and the basename of -- the target to write to... write an object! From 712f80382568d0b6a3a020700c4d2896c8810f95 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 30 Apr 2019 23:18:40 +0100 Subject: [PATCH 129/227] comment changes, and minimal message changes. --- programs/extopenscad.hs | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index 0496021..a90774a 100644 --- a/programs/extopenscad.hs +++ b/programs/extopenscad.hs @@ -3,11 +3,11 @@ -- Copyright (C) 2014 2016, Mike MacHenry (mike.machenry@gmail.com) -- Released under the GNU GPL, see LICENSE --- FIXME: add support for AMF. --- An interpreter to run extended OpenScad code, outputing STL, OBJ, SVG, SCAD, PNG, or GCODE. +-- An interpreter to run extended OpenScad code. outputs STL, OBJ, SVG, SCAD, PNG, DXF, or GCODE. -- Enable additional syntax to make our code more readable. -{-# LANGUAGE ViewPatterns , PatternGuards #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PatternGuards #-} -- Let's be explicit about what we're getting from where :) @@ -49,13 +49,13 @@ import Options.Applicative (fullDesc, progDesc, header, auto, info, helper, help -- For handling input/output files. import System.FilePath (splitExtension) --- The following is needed to ensure backwards/forwards compatibility --- Backwards compatibility with old versions of Data.Monoid: +-- | The following is needed to ensure backwards/forwards compatibility +-- | with old versions of Data.Monoid: infixr 6 <> (<>) :: Monoid a => a -> a -> a (<>) = mappend --- A datatype for containing our command line options. +-- | Our command line options. data ExtOpenScadOpts = ExtOpenScadOpts { outputFile :: Maybe FilePath , outputFormat :: Maybe OutputFormat @@ -63,7 +63,7 @@ data ExtOpenScadOpts = ExtOpenScadOpts , inputFile :: FilePath } --- A datatype enumerating our output file formats types. +-- | An enumeration of our output file formats types. data OutputFormat = SVG | SCAD @@ -75,7 +75,7 @@ data OutputFormat | DXF deriving (Show, Eq, Ord) --- A list mapping file extensions to output formats. +-- | A list mapping file extensions to output formats. formatExtensions :: [(String, OutputFormat)] formatExtensions = [ ("svg", SVG) @@ -89,15 +89,15 @@ formatExtensions = , ("dxf", DXF) ] --- Lookup an output format for a given output file. Throw an error if one cannot be found. +-- | Lookup an output format for a given output file. Throw an error if one cannot be found. guessOutputFormat :: FilePath -> OutputFormat guessOutputFormat fileName = - fromMaybe (error $ "Unrecognized output format: "<>ext) + fromMaybe (error $ "Unrecognized output format: " <> ext) $ readOutputFormat $ tail ext where (_,ext) = splitExtension fileName --- The parser for our command line arguments. +-- | The parser for our command line arguments. extOpenScadOpts :: Parser ExtOpenScadOpts extOpenScadOpts = ExtOpenScadOpts <$> optional ( @@ -129,12 +129,12 @@ extOpenScadOpts = ExtOpenScadOpts <> help "Input extended OpenSCAD file" ) --- Try to look up an output format from a supplied extension. +-- | Try to look up an output format from a supplied extension. readOutputFormat :: String -> Maybe OutputFormat readOutputFormat ext = lookup (map toLower ext) formatExtensions --- A Read instance for our output format. Used by 'auto' in our command line parser. --- Reads a string, and evaluates to the appropriate OutputFormat. +-- | A Read instance for our output format. Used by 'auto' in our command line parser. +-- Reads a string, and evaluates to the appropriate OutputFormat. instance Read OutputFormat where readsPrec _ myvalue = tryParse formatExtensions @@ -146,11 +146,11 @@ instance Read OutputFormat where then [(result, drop (length attempt) myvalue)] else tryParse xs --- Find the resolution to raytrace at. +-- | Find the resolution to raytrace at. getRes :: (Map.Map String OVal, [SymbolicObj2], [SymbolicObj3]) -> ℝ --- First, use a resolution specified by a variable in the input file. +-- | First, use a resolution specified by a variable in the input file. getRes (Map.lookup "$res" -> Just (ONum res), _, _) = res --- Use a resolution chosen for 3D objects. +-- | Use a resolution chosen for 3D objects. -- FIXME: magic numbers. getRes (varlookup, _, obj:_) = let @@ -159,7 +159,7 @@ getRes (varlookup, _, obj:_) = in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of ONum qual | qual > 0 -> min (minimum [x,y,z]/2) ((x*y*z/qual)**(1/3) / 22) _ -> min (minimum [x,y,z]/2) ((x*y*z)**(1/3) / 22) --- Use a resolution chosen for 2D objects. +-- | Use a resolution chosen for 2D objects. -- FIXME: magic numbers. getRes (varlookup, obj:_, _) = let @@ -168,10 +168,10 @@ getRes (varlookup, obj:_, _) = in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of ONum qual | qual > 0 -> min (min x y/2) (sqrt(x*y/qual) / 30) _ -> min (min x y/2) (sqrt(x*y) / 30) --- fallthrough value. +-- | fallthrough value. getRes _ = 1 --- Output a file containing a 3D object. +-- | Output a file containing a 3D object. export3 :: Maybe OutputFormat -> ℝ -> FilePath -> SymbolicObj3 -> IO () export3 posFmt res output obj = case posFmt of @@ -182,7 +182,7 @@ export3 posFmt res output obj = Nothing -> writeBinSTL res output obj Just fmt -> putStrLn $ "Unrecognized 3D format: "<>show fmt --- Output a file containing a 2D object. +-- | Output a file containing a 2D object. export2 :: Maybe OutputFormat -> ℝ -> FilePath -> SymbolicObj2 -> IO () export2 posFmt res output obj = case posFmt of @@ -194,8 +194,8 @@ export2 posFmt res output obj = Nothing -> writeSVG res output obj Just fmt -> putStrLn $ "Unrecognized 2D format: "<>show fmt --- Interpret arguments, and render the object defined in the supplied input file. -run :: ExtOpenScadOpts -> IO() +-- | Interpret arguments, and render the object defined in the supplied input file. +run :: ExtOpenScadOpts -> IO () run args = do putStrLn "Loading File." @@ -237,10 +237,10 @@ run args = do print obj export2 format res output obj ([], []) -> putStrLn "No objects to render." - _ -> putStrLn "Multiple/No objects, what do you want to render?" + _ -> putStrLn "A mixture of 2D and 3d objects, what do you want to render?" --- The entry point. Use the option parser then run the extended OpenScad code. -main :: IO() +-- | The entry point. Use the option parser then run the extended OpenScad code. +main :: IO () main = execParser opts >>= run where opts= info (helper <*> extOpenScadOpts) From 3eb4d85cf990895d1a97fed2d5c6d14b262a0a92 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 30 Apr 2019 23:19:26 +0100 Subject: [PATCH 130/227] add some fixmes. --- programs/implicitsnap.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 381d43e..d4dfbc7 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -5,12 +5,14 @@ -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} +-- FIXME: what are these for? {-# LANGUAGE OverloadedStrings #-} - {-# LANGUAGE ViewPatterns #-} -- A Snap(HTTP) server providing an ImplicitCAD REST API. +-- FIXME: we need AuthN/AuthZ for https://github.com/kliment/explicitcad to be useful. + -- Let's be explicit about what we're getting from where :) import Prelude (IO, Maybe(Just, Nothing), Ord, String, Bool(True, False), Either(Left, Right), Show, ($), (++), (>), (.), (-), (/), (*), (**), sqrt, min, max, minimum, maximum, show, return) From a330898a78bfbf1ed3e0525ea3d6a240f92a90f2 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 09:22:03 +0100 Subject: [PATCH 131/227] 3d -> 3D --- programs/extopenscad.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index a90774a..4e98c42 100644 --- a/programs/extopenscad.hs +++ b/programs/extopenscad.hs @@ -237,7 +237,7 @@ run args = do print obj export2 format res output obj ([], []) -> putStrLn "No objects to render." - _ -> putStrLn "A mixture of 2D and 3d objects, what do you want to render?" + _ -> putStrLn "A mixture of 2D and 3D objects, what do you want to render?" -- | The entry point. Use the option parser then run the extended OpenScad code. main :: IO () From 0e57ac76d42e6a878a56c12f7c44c101fe36ec9f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 09:22:27 +0100 Subject: [PATCH 132/227] make precident in 2d resolution generation clearer. --- programs/implicitsnap.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index d4dfbc7..50b78c1 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -108,8 +108,8 @@ getRes (varlookup, obj:_, _) = (p1,p2) = getBox2 obj (x,y) = p2 .-. p1 in case fromMaybe (ONum 1) $ Map.lookup "$quality" varlookup of - ONum qual | qual > 0 -> min (min x y/2) (sqrt(x*y/qual) / 30) - _ -> min (min x y/2) (sqrt(x*y ) / 30) + ONum qual | qual > 0 -> min ((min x y)/2) (sqrt(x*y/qual) / 30) + _ -> min ((min x y)/2) (sqrt(x*y ) / 30) -- | fallthrough value. getRes _ = 1 From fba996ac50d3bd514e9b7ec697a6a16914ed960e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 10:18:14 +0100 Subject: [PATCH 133/227] make our parser benchmark compile again. --- implicit.cabal | 3 ++- programs/{ParserBench.hs => parser-bench.hs} | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) rename programs/{ParserBench.hs => parser-bench.hs} (97%) diff --git a/implicit.cabal b/implicit.cabal index bac3343..7ee116c 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -252,7 +252,8 @@ Test-suite test-implicit Benchmark parser-bench Type: exitcode-stdio-1.0 Build-depends: base, criterion, random, parsec, implicit - Main-is: ParserBench.hs + Main-is: parser-bench.hs + Hs-source-dirs: programs Ghc-options: -threaded -rtsopts diff --git a/programs/ParserBench.hs b/programs/parser-bench.hs similarity index 97% rename from programs/ParserBench.hs rename to programs/parser-bench.hs index ca02619..196ec6f 100644 --- a/programs/ParserBench.hs +++ b/programs/parser-bench.hs @@ -32,7 +32,7 @@ parseExpr s = case parse expr0 "src" s of Right e -> e parseStatements :: String -> [StatementI] -parseStatements s = case parseProgram "src" s of +parseStatements s = case parseProgram s of Left err -> error (show err) Right e -> e From 034e9d9e33f4df8c9fbfd41a811371781931951b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 10:47:01 +0100 Subject: [PATCH 134/227] make developing and rebuilding while offline easier, improve dependency checking, make debugging output cleaner, and build the parser-bench parser benchmarking tool. --- Makefile | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 7fe88bd..6e52f05 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,12 @@ GHC=ghc EXTOPENSCAD=dist/build/extopenscad/extopenscad # the location of the benchmark binary, for benchmarking some implicitcad internals. BENCHMARK=dist/build/Benchmark/Benchmark +# the location of the parser benchmark binary, specifically for benchmarking implicitcad's parser. +PARSERBENCH=dist/build/parser-bench/parser-bench # the location of the created test binary, for running haskell test cases. -TESTFILES=$(shell find tests/ParserSpec -name '*.hs') TESTSUITE=dist/build/test-implicit/test-implicit +# the location of it's source. +TESTFILES=$(shell find tests/ParserSpec -name '*.hs') # the location of the documentation generator. for documenting (some of) the extopenscad languagi. DOCGEN=dist/build/docgen/docgen @@ -30,12 +33,21 @@ RESOPTS=-r 50 LIBFILES=$(shell find Graphics -name '*.hs') LIBTARGET=dist/build/Graphics/Implicit.o -EXECTARGETS=$(EXTOPENSCAD) $(BENCHMARK) $(TESTSUITE) $(DOCGEN) +EXECTARGETS=$(EXTOPENSCAD) $(BENCHMARK) $(TESTSUITE) $(PARSERBENCH) $(DOCGEN) TARGETS=$(EXECTARGETS) $(LIBTARGET) # mark the below fake targets as unrean, so make will not get choked up if a file with one of these names is created. .PHONY: build install clean distclean nukeclean docs dist examples tests +# empty out the default suffix list, to make debugging output cleaner. +.SUFFIXES: + +# allow for us to (ab)use $$* in dependencies of rules. +.SECONDEXPANSION: + +# disable make's default builtin rules, to make debugging output cleaner. +MAKEFLAGS += --no-builtin-rules + # build implicitcad binaries. build: $(TARGETS) @@ -45,7 +57,6 @@ install: build # cleanup from using the rules in this file. clean: Setup - ./Setup clean rm -f Examples/*.stl rm -f Examples/*.svg rm -f Examples/*.ps @@ -60,7 +71,8 @@ clean: Setup rm -f dist/build/libHS* # clean up before making a release. -distclean: clean +distclean: clean Setup + ./Setup clean rm -f Setup Setup.hi Setup.o rm -rf dist/ rm -f `find ./ -name *~` @@ -97,8 +109,13 @@ tests: $(TESTSUITE) $(TESTFILES) $(LIBTARGET): $(LIBFILES) cabal build implicit +# the test suite, since it's source is stored in a different location than the other binaries we build: +dist/build/test-implicit/test-implicit: $(TESTFILES) Setup dist/setup-config $(LIBTARGET) $(LIBFILES) + cabal build test-implicit + + # build a binary target with cabal. -dist/build/%: Setup dist/setup-config $(LIBTARGET) $(LIBFILES) +dist/build/%: programs/$$(word 2,$$(subst /, ,%)).hs Setup dist/setup-config $(LIBTARGET) $(LIBFILES) cabal build $(word 2,$(subst /, ,$*)) # prepare to build. From fa84f9544ebf43a46379f55d7c5b702d5bdcb346 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 13:02:55 +0100 Subject: [PATCH 135/227] clean up dependencies, stop building a historic module, and spacing changes. --- implicit.cabal | 123 +++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 87 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 7ee116c..da66031 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -18,31 +18,24 @@ Category: Graphics Library Build-depends: - base >= 3 && < 5, - filepath, - directory, - parsec, - unordered-containers, - parallel, - containers, - deepseq, - vector-space, - text, - monads-tf, - bytestring, - bytestring-builder, - blaze-builder, - blaze-markup, - blaze-svg, - storable-endian, - JuicyPixels, - NumInstances, - criterion, - snap-core, - snap-server, - silently, - transformers, - hspec + base >= 3 && < 5, + filepath, + directory, + parsec, + parallel, + containers, + deepseq, + hspec, + vector-space, + text, + monads-tf, + bytestring, + blaze-builder, + blaze-markup, + blaze-svg, + storable-endian, + JuicyPixels, + transformers Ghc-options: -Wall @@ -87,7 +80,6 @@ Library Graphics.Implicit.ExtOpenScad.Util.StateC Graphics.Implicit.ExtOpenScad.Util.ArgParser Graphics.Implicit.ExtOpenScad.Util.OVal - Graphics.Implicit.Export.MarchingSquares Graphics.Implicit.Export.MarchingSquaresFill Graphics.Implicit.Export.RayTrace Graphics.Implicit.Export.NormedTriangleMeshFormats @@ -135,63 +127,32 @@ Executable docgen Hs-source-dirs: programs build-depends: base, - vector-space, - text, - JuicyPixels, - blaze-builder, - blaze-svg, - blaze-markup, - parallel, - deepseq, - vector-space, - monads-tf, - bytestring, - storable-endian, - parsec, - directory, - containers, - filepath, - snap-core, - snap-server, - silently, - transformers, implicit ghc-options: -Wall -Weverything -Wextra - -optc-O3 - -threaded - -rtsopts - -funfolding-use-threshold=16 - -fspec-constr-count=10 + -optc-O3 + -threaded + -rtsopts + -funfolding-use-threshold=16 + -fspec-constr-count=10 Executable implicitsnap Main-is: implicitsnap.hs Hs-source-dirs: programs Build-depends: base, - vector-space, - text, - JuicyPixels, - blaze-builder, - blaze-svg, - blaze-markup, - parallel, - deepseq, - vector-space, - monads-tf, bytestring, - storable-endian, - parsec, - directory, containers, - filepath, + implicit, + parallel, + parsec, + silently, snap-core, snap-server, - silently, - transformers, - implicit + text, + vector-space Ghc-options: -threaded -rtsopts @@ -208,38 +169,26 @@ Executable Benchmark Hs-source-dirs: programs Build-depends: base, - text, - JuicyPixels, - blaze-svg, - blaze-markup, - parallel, - deepseq, - vector-space, - monads-tf, - blaze-builder, - bytestring, - storable-endian, - parsec, - directory, - containers, - filepath, criterion, - transformers, implicit Ghc-options: -threaded -rtsopts -Wall -optc-O3 - -- see GHC manual 8.2.1 section 6.5.1. - -feager-blackholing -- for debugging. -Wextra -Weverything Test-suite test-implicit Type: exitcode-stdio-1.0 - Build-depends: base, mtl, containers, hspec, parsec, implicit + Build-depends: + base, + containers, + hspec, + implicit, + mtl, + parsec Main-is: Main.hs Hs-source-dirs: tests Ghc-options: From 39e0868dd5fb89f44984b2547637d81a7fa6989d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 14:29:38 +0100 Subject: [PATCH 136/227] formatting changes, and minor formatting changes on the output. --- programs/docgen.hs | 132 ++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/programs/docgen.hs b/programs/docgen.hs index 81520d2..dd7acff 100644 --- a/programs/docgen.hs +++ b/programs/docgen.hs @@ -1,8 +1,14 @@ -- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) -- Released under the GNU GPL, see LICENSE -{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances, ScopedTypeVariables #-} - +-- FIXME: document why we need each of these. +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE ScopedTypeVariables #-} import Prelude(IO, Show, String, Int, Maybe(Just,Nothing), Eq, return, ($), show, fmap, (++), putStrLn, filter, zip, null, map, undefined, const, Bool(True,False), fst, snd, sequence, (.), concat, head, tail, sequence, length, (>), (/=), (+)) import Graphics.Implicit.ExtOpenScad.Primitives (primitives) @@ -11,77 +17,80 @@ import Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP,APFailIf,APExampl import qualified Control.Exception as Ex (catch, SomeException) import Control.Monad (forM_, mapM) +-- | Return true if the argument is of type ExampleDoc. isExample (ExampleDoc _ ) = True isExample _ = False +-- | Return true if the argument is of type ArgumentDoc. isArgument (ArgumentDoc _ _ _) = True isArgument _ = False +-- | Return true if the argument is of type Branch. isBranch (Branch _) = True isBranch _ = False dumpPrimitive :: String -> [DocPart] -> Int -> IO () dumpPrimitive moduleName moduleDocList level = do - let - examples = filter isExample moduleDocList - arguments = filter isArgument moduleDocList - syntaxes = filter isBranch moduleDocList - moduleLabel = moduleName + let + examples = filter isExample moduleDocList + arguments = filter isArgument moduleDocList + syntaxes = filter isBranch moduleDocList + moduleLabel = moduleName - if level /= 0 - then - do - putStrLn $ "#" ++ moduleLabel - else - do - putStrLn moduleLabel - putStrLn (map (const '-') moduleLabel) - putStrLn "" + if level /= 0 + then + do + putStrLn $ "#" ++ moduleLabel + else + do + putStrLn moduleLabel + putStrLn (map (const '-') moduleLabel) + putStrLn "" - if null examples - then - return () - else - do - putStrLn "#Examples:\n" - forM_ examples $ \(ExampleDoc example) -> do - putStrLn $ " * `" ++ example ++ "`" - putStrLn "" + if null examples + then + return () + else + do + putStrLn "#Examples:\n" + forM_ examples $ \(ExampleDoc example) -> do + putStrLn $ " * `" ++ example ++ "`" + putStrLn "" - if null arguments - then - return () - else - do - if level /= 0 - then - putStrLn "##Arguments:\n" - else - if null syntaxes - then - putStrLn "#Arguments:\n" - else - putStrLn "#Shared Arguments:\n" - forM_ arguments $ \(ArgumentDoc name posfallback description) -> - case (posfallback, description) of - (Nothing, "") -> do - putStrLn $ " * `" ++ name ++ "`" - (Just fallback, "") -> do - putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" - (Nothing, _) -> do - putStrLn $ " * `" ++ name ++ "`" - putStrLn $ " " ++ description - (Just fallback, _) -> do - putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" - putStrLn $ " " ++ description - putStrLn "" + if null arguments + then + return () + else + do + if level /= 0 + then + putStrLn "##Arguments:\n" + else + if null syntaxes + then + putStrLn "#Arguments:\n" + else + putStrLn "#Shared Arguments:\n" + forM_ arguments $ \(ArgumentDoc name posfallback description) -> + case (posfallback, description) of + (Nothing, "") -> do + putStrLn $ " * `" ++ name ++ "`" + (Just fallback, "") -> do + putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" + (Nothing, _) -> do + putStrLn $ " * `" ++ name ++ "`" + putStrLn $ " " ++ description + (Just fallback, _) -> do + putStrLn $ " * `" ++ name ++ " = " ++ fallback ++ "`" + putStrLn $ " " ++ description + putStrLn "" - if null syntaxes - then - return () - else - forM_ syntaxes $ \(Branch syntax) -> do - dumpPrimitive ("Syntax " ++ (show $ level+1)) syntax (level+1) + if null syntaxes + then + return () + else + forM_ syntaxes $ \(Branch syntax) -> do + dumpPrimitive ("Syntax " ++ (show $ level+1)) syntax (level+1) main :: IO () main = do @@ -98,16 +107,15 @@ main = do forM_ (zip names docs) $ \(moduleName, moduleDocList) -> do dumpPrimitive moduleName moduleDocList 0 --- | We need a format to extract documentation into +-- | the format we extract documentation into data Doc = Doc String [DocPart] deriving (Show) data DocPart = ExampleDoc String | ArgumentDoc String (Maybe String) String - | Empty | Branch [DocPart] - deriving (Show,Eq) - + | Empty + deriving (Show, Eq) -- Here there be dragons! -- Because we made this a Monad instead of applicative functor, there's no sane way to do this. From ed4f6b236451e1fbf38f96b0f332e9637e91db57 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 14:29:50 +0100 Subject: [PATCH 137/227] typo. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6e52f05..7d08617 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ PARSERBENCH=dist/build/parser-bench/parser-bench TESTSUITE=dist/build/test-implicit/test-implicit # the location of it's source. TESTFILES=$(shell find tests/ParserSpec -name '*.hs') -# the location of the documentation generator. for documenting (some of) the extopenscad languagi. +# the location of the documentation generator. for documenting (some of) the extopenscad language. DOCGEN=dist/build/docgen/docgen ## options used when calling ImplicitCAD. for testing, and for image generation. From 53509bbdc5bda2bea7c6905daadbef9de15c473e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 15:16:44 +0100 Subject: [PATCH 138/227] add comments. --- programs/Benchmark.hs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index 8449d7c..8982baa 100644 --- a/programs/Benchmark.hs +++ b/programs/Benchmark.hs @@ -24,6 +24,7 @@ import Graphics.Implicit.Definitions (ℝ) -- FIXME: move each of these objects into seperate compilable files. +-- | A 2D object, for benchmarking. obj2d_1 :: SymbolicObj2 obj2d_1 = union @@ -34,12 +35,14 @@ obj2d_1 = , translate (0,-22) $ circle 10 ] +-- | a 3D object, for benchmarking. extruded from our 2D object. object1 :: SymbolicObj3 object1 = extrudeRM 0 (Just twist) Nothing Nothing obj2d_1 (Left 40) where twist :: ℝ -> ℝ twist h = 35*cos(h*2*pi/60) +-- | another 3D object, for benchmarking. object2 :: SymbolicObj3 object2 = squarePipe (10,10,10) 1 100 where squarePipe (x,y,z) diameter precision = @@ -51,6 +54,7 @@ object2 = squarePipe (10,10,10) 1 100 (map (\n->(n/precision)*y) [0..precision]) (map (\n->(n/precision)*z) [0..precision]) +-- | A third 3d object to benchmark. object3 :: SymbolicObj3 object3 = difference @@ -58,6 +62,7 @@ object3 = , rect3R 1 (0,0,0) (2,2,2) ] +-- | Benchmark a 2D object. obj2Benchmarks :: String -> SymbolicObj2 -> Benchmark obj2Benchmarks name obj = bgroup name @@ -68,6 +73,7 @@ obj2Benchmarks name obj = bench "Get contour" $ nf (symbolicGetContour 1) obj ] +-- | Benchmark a 3D object. obj3Benchmarks :: String -> SymbolicObj3 -> Benchmark obj3Benchmarks name obj = bgroup name @@ -78,6 +84,7 @@ obj3Benchmarks name obj = bench "Get mesh" $ nf (symbolicGetMesh 1) obj ] +-- | Benchmark all of our objects. benchmarks :: [Benchmark] benchmarks = [ obj3Benchmarks "Object 1" object1 @@ -86,6 +93,7 @@ benchmarks = , obj2Benchmarks "Object 2d 1" obj2d_1 ] +-- | Our entrypoint. Runs all benchmarks. main :: IO () main = defaultMain benchmarks From 1323840b15133634cb498a4922e1062a00551527 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 16:27:57 +0100 Subject: [PATCH 139/227] remove unneeded typeclass from OutputFormat, and comment/include updates. --- programs/extopenscad.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index 4e98c42..4b85aa1 100644 --- a/programs/extopenscad.hs +++ b/programs/extopenscad.hs @@ -11,7 +11,7 @@ -- Let's be explicit about what we're getting from where :) -import Prelude (Read(readsPrec), Maybe(Just, Nothing), Either(Left, Right), IO, FilePath, Show, Eq, Ord, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, print, (>>=), lookup) +import Prelude (Read(readsPrec), Maybe(Just, Nothing), Either(Left, Right), IO, FilePath, Show, Eq, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, print, (>>=), lookup) -- Our Extended OpenScad interpreter, and functions to write out files in designated formats. import Graphics.Implicit (runOpenscad, writeSVG, writeDXF2, writeBinSTL, writeOBJ, writeSCAD2, writeSCAD3, writeGCodeHacklabLaser, writePNG2, writePNG3) @@ -63,7 +63,7 @@ data ExtOpenScadOpts = ExtOpenScadOpts , inputFile :: FilePath } --- | An enumeration of our output file formats types. +-- | A type serving to enumerate our output formats. data OutputFormat = SVG | SCAD @@ -73,7 +73,7 @@ data OutputFormat | OBJ -- | 3MF | DXF - deriving (Show, Eq, Ord) + deriving (Show, Eq) -- | A list mapping file extensions to output formats. formatExtensions :: [(String, OutputFormat)] From cb6e9e30ceef6c6d078d919ba1761f81367391e9 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 16:34:34 +0100 Subject: [PATCH 140/227] comment change. --- programs/docgen.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/programs/docgen.hs b/programs/docgen.hs index dd7acff..583f9b3 100644 --- a/programs/docgen.hs +++ b/programs/docgen.hs @@ -92,6 +92,7 @@ dumpPrimitive moduleName moduleDocList level = do forM_ syntaxes $ \(Branch syntax) -> do dumpPrimitive ("Syntax " ++ (show $ level+1)) syntax (level+1) +-- | Our entrypoint. Generate one document describing all of our primitives. main :: IO () main = do docs <- mapM (getArgParserDocs.($ []).snd) primitives From 619a5f89b16bd72bc84d3013adbeafe9127ca737 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 1 May 2019 18:52:52 +0100 Subject: [PATCH 141/227] use an integral for counting. --- programs/Benchmark.hs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index 8982baa..06a60bd 100644 --- a/programs/Benchmark.hs +++ b/programs/Benchmark.hs @@ -6,7 +6,7 @@ -- Let's be explicit about where things come from :) -import Prelude (($), (*), (/), String, IO, cos, pi, map, zip3, Maybe(Just, Nothing), Either(Left)) +import Prelude (($), (*), (/), String, IO, cos, pi, map, zip3, Maybe(Just, Nothing), Either(Left), fromIntegral) -- Use criterion for benchmarking. see import Criterion.Main (Benchmark, bgroup, bench, nf, defaultMain) @@ -17,8 +17,8 @@ import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) import Graphics.Implicit.Primitives (translate, difference, extrudeRM, rect3R) --- The variable defining distance in our world. -import Graphics.Implicit.Definitions (ℝ) +-- The variables defining distance and counting in our world. +import Graphics.Implicit.Definitions (ℝ, Fastℕ) -- Haskell representations of objects to benchmark. @@ -45,14 +45,16 @@ object1 = extrudeRM 0 (Just twist) Nothing Nothing obj2d_1 (Left 40) -- | another 3D object, for benchmarking. object2 :: SymbolicObj3 object2 = squarePipe (10,10,10) 1 100 - where squarePipe (x,y,z) diameter precision = + where + squarePipe :: (ℝ,ℝ,ℝ) -> ℝ -> ℝ -> SymbolicObj3 + squarePipe (x,y,z) diameter precision = union $ map (\start-> translate start $ rect3R 0 (0,0,0) (diameter,diameter,diameter) ) - $ zip3 (map (\n->(n/precision)*x) [0..precision]) - (map (\n->(n/precision)*y) [0..precision]) - (map (\n->(n/precision)*z) [0..precision]) + $ zip3 (map (\n->((fromIntegral n)/precision)*x) [0..100::Fastℕ]) + (map (\n->((fromIntegral n)/precision)*y) [0..100::Fastℕ]) + (map (\n->((fromIntegral n)/precision)*z) [0..100::Fastℕ]) -- | A third 3d object to benchmark. object3 :: SymbolicObj3 From 31155626b913826916a467515b2f34456d5515da Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 06:39:50 +0100 Subject: [PATCH 142/227] clean up formatting of ghc options, reorder, and specify -O2 --- implicit.cabal | 100 ++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index da66031..5b2a800 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -38,13 +38,14 @@ Library transformers Ghc-options: - -Wall - -optc-O3 - -- see GHC manual 8.2.1 section 6.5.1. - -feager-blackholing - -- for debugging. - -Wextra - -Weverything + -O2 + -optc-O3 + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. + -Wextra + -Weverything + -Wall Exposed-modules: Graphics.Implicit @@ -111,15 +112,16 @@ Executable extopenscad optparse-applicative >= 0.10.0, implicit Ghc-options: - -threaded - -rtsopts - -Wall - -optc-O3 - -- see GHC manual 8.2.1 section 6.5.1. - -feager-blackholing - -- for debugging. - -Wextra - -Weverything + -O2 + -optc-O3 + -threaded + -rtsopts + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. + -Wall + -Wextra + -Weverything Executable docgen @@ -129,14 +131,10 @@ Executable docgen base, implicit ghc-options: + -- for debugging. -Wall - -Weverything -Wextra - -optc-O3 - -threaded - -rtsopts - -funfolding-use-threshold=16 - -fspec-constr-count=10 + -Weverything Executable implicitsnap Main-is: implicitsnap.hs @@ -154,15 +152,16 @@ Executable implicitsnap text, vector-space Ghc-options: - -threaded - -rtsopts - -Wall - -optc-O3 - -- see GHC manual 8.2.1 section 6.5.1. - -feager-blackholing - -- for debugging. - -Wextra - -Weverything + -threaded + -rtsopts + -O2 + -optc-O3 + -- see GHC manual 8.2.1 section 6.5.1. + -feager-blackholing + -- for debugging. + -Wall + -Wextra + -Weverything Executable Benchmark Main-is: Benchmark.hs @@ -172,13 +171,12 @@ Executable Benchmark criterion, implicit Ghc-options: - -threaded - -rtsopts - -Wall - -optc-O3 - -- for debugging. - -Wextra - -Weverything + -O2 + -optc-O3 + -- for debugging. + -Wall + -Wextra + -Weverything Test-suite test-implicit Type: exitcode-stdio-1.0 @@ -192,11 +190,12 @@ Test-suite test-implicit Main-is: Main.hs Hs-source-dirs: tests Ghc-options: - -Wall - -optc-O3 - -- for debugging. - -Wextra - -Weverything + -O2 + -optc-O3 + -- for debugging. + -Wall + -Wextra + -Weverything Benchmark parser-bench Type: exitcode-stdio-1.0 @@ -204,15 +203,12 @@ Benchmark parser-bench Main-is: parser-bench.hs Hs-source-dirs: programs Ghc-options: - -threaded - -rtsopts - -Wall - -optc-O3 - -- see GHC manual 8.2.1 section 6.5.1. - -feager-blackholing - -- for debugging. - -Wextra - -Weverything + -O2 + -optc-O3 + -- for debugging. + -Wall + -Wextra + -Weverything Source-repository head Type: git From d671ff7b2c0b11b10aa14c9eb460bcd676863540 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 07:18:47 +0100 Subject: [PATCH 143/227] formatting, and comment about MarchingSquares. --- implicit.cabal | 58 ++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 5b2a800..5f82bde 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -70,34 +70,36 @@ Library Graphics.Implicit.ExtOpenScad.Primitives Other-modules: - Graphics.Implicit.ObjectUtil.GetBox2 - Graphics.Implicit.ObjectUtil.GetBox3 - Graphics.Implicit.ObjectUtil.GetImplicit2 - Graphics.Implicit.ObjectUtil.GetImplicit3 - Graphics.Implicit.ExtOpenScad.Default - Graphics.Implicit.ExtOpenScad.Parser.Util - Graphics.Implicit.ExtOpenScad.Eval.Statement - Graphics.Implicit.ExtOpenScad.Eval.Expr - Graphics.Implicit.ExtOpenScad.Util.StateC - Graphics.Implicit.ExtOpenScad.Util.ArgParser - Graphics.Implicit.ExtOpenScad.Util.OVal - Graphics.Implicit.Export.MarchingSquaresFill - Graphics.Implicit.Export.RayTrace - Graphics.Implicit.Export.NormedTriangleMeshFormats - Graphics.Implicit.Export.SymbolicFormats - Graphics.Implicit.Export.Util - Graphics.Implicit.Export.TextBuilderUtils - Graphics.Implicit.Export.Symbolic.Rebound2 - Graphics.Implicit.Export.Symbolic.Rebound3 - Graphics.Implicit.Export.Render - Graphics.Implicit.Export.Render.Definitions - Graphics.Implicit.Export.Render.GetLoops - Graphics.Implicit.Export.Render.GetSegs - Graphics.Implicit.Export.Render.HandleSquares - Graphics.Implicit.Export.Render.Interpolate - Graphics.Implicit.Export.Render.RefineSegs - Graphics.Implicit.Export.Render.TesselateLoops - Graphics.Implicit.Export.Render.HandlePolylines + Graphics.Implicit.ObjectUtil.GetBox2 + Graphics.Implicit.ObjectUtil.GetBox3 + Graphics.Implicit.ObjectUtil.GetImplicit2 + Graphics.Implicit.ObjectUtil.GetImplicit3 + Graphics.Implicit.ExtOpenScad.Default + Graphics.Implicit.ExtOpenScad.Parser.Util + Graphics.Implicit.ExtOpenScad.Eval.Statement + Graphics.Implicit.ExtOpenScad.Eval.Expr + Graphics.Implicit.ExtOpenScad.Util.StateC + Graphics.Implicit.ExtOpenScad.Util.ArgParser + Graphics.Implicit.ExtOpenScad.Util.OVal + -- Historic, but functional. Should be merged into MarchingSquaresFill. + -- Graphics.Implicit.Export.MarchingSquares + Graphics.Implicit.Export.MarchingSquaresFill + Graphics.Implicit.Export.RayTrace + Graphics.Implicit.Export.NormedTriangleMeshFormats + Graphics.Implicit.Export.SymbolicFormats + Graphics.Implicit.Export.Util + Graphics.Implicit.Export.TextBuilderUtils + Graphics.Implicit.Export.Symbolic.Rebound2 + Graphics.Implicit.Export.Symbolic.Rebound3 + Graphics.Implicit.Export.Render + Graphics.Implicit.Export.Render.Definitions + Graphics.Implicit.Export.Render.GetLoops + Graphics.Implicit.Export.Render.GetSegs + Graphics.Implicit.Export.Render.HandleSquares + Graphics.Implicit.Export.Render.Interpolate + Graphics.Implicit.Export.Render.RefineSegs + Graphics.Implicit.Export.Render.TesselateLoops + Graphics.Implicit.Export.Render.HandlePolylines Executable extopenscad From 43439de45aad12fff137416cef4ebcd0a01a6700 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 07:21:36 +0100 Subject: [PATCH 144/227] spacing changes, and capitalization. --- implicit.cabal | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 5f82bde..c23fdc0 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -48,26 +48,26 @@ Library -Wall Exposed-modules: - Graphics.Implicit - Graphics.Implicit.Definitions - Graphics.Implicit.Primitives - Graphics.Implicit.Export - Graphics.Implicit.MathUtil - Graphics.Implicit.ExtOpenScad - Graphics.Implicit.ObjectUtil - -- these modules are exposed for the unit tests against the parser interface. - Graphics.Implicit.ExtOpenScad.Parser.Statement - Graphics.Implicit.ExtOpenScad.Parser.Expr - Graphics.Implicit.ExtOpenScad.Definitions - -- these are exposed for Benchmark. - Graphics.Implicit.Export.SymbolicObj2 - Graphics.Implicit.Export.SymbolicObj3 - -- these are exposed for implicitsnap. - Graphics.Implicit.Export.TriangleMeshFormats - Graphics.Implicit.Export.PolylineFormats - Graphics.Implicit.Export.DiscreteAproxable - -- These are exposed for docgen. - Graphics.Implicit.ExtOpenScad.Primitives + Graphics.Implicit + Graphics.Implicit.Definitions + Graphics.Implicit.Primitives + Graphics.Implicit.Export + Graphics.Implicit.MathUtil + Graphics.Implicit.ExtOpenScad + Graphics.Implicit.ObjectUtil + -- These modules are exposed for the unit tests against the parser interface. + Graphics.Implicit.ExtOpenScad.Parser.Statement + Graphics.Implicit.ExtOpenScad.Parser.Expr + Graphics.Implicit.ExtOpenScad.Definitions + -- These are exposed for Benchmark. + Graphics.Implicit.Export.SymbolicObj2 + Graphics.Implicit.Export.SymbolicObj3 + -- These are exposed for implicitsnap. + Graphics.Implicit.Export.TriangleMeshFormats + Graphics.Implicit.Export.PolylineFormats + Graphics.Implicit.Export.DiscreteAproxable + -- These are exposed for docgen. + Graphics.Implicit.ExtOpenScad.Primitives Other-modules: Graphics.Implicit.ObjectUtil.GetBox2 From a56fb683ea3cf3375ac6f50411d1a5dd8217363f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 07:29:22 +0100 Subject: [PATCH 145/227] build and use dynamic libraries, by default. --- implicit.cabal | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index c23fdc0..65f46cb 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -40,6 +40,7 @@ Library Ghc-options: -O2 -optc-O3 + -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -118,6 +119,7 @@ Executable extopenscad -optc-O3 -threaded -rtsopts + -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -133,6 +135,7 @@ Executable docgen base, implicit ghc-options: + -dynamic -- for debugging. -Wall -Wextra @@ -154,6 +157,7 @@ Executable implicitsnap text, vector-space Ghc-options: + -dynamic -threaded -rtsopts -O2 @@ -175,6 +179,7 @@ Executable Benchmark Ghc-options: -O2 -optc-O3 + -dynamic -- for debugging. -Wall -Wextra @@ -192,8 +197,9 @@ Test-suite test-implicit Main-is: Main.hs Hs-source-dirs: tests Ghc-options: - -O2 + -O2 -optc-O3 + -dynamic -- for debugging. -Wall -Wextra @@ -205,8 +211,9 @@ Benchmark parser-bench Main-is: parser-bench.hs Hs-source-dirs: programs Ghc-options: - -O2 + -O2 -optc-O3 + -dynamic -- for debugging. -Wall -Wextra From 060b3c97922e8d4137039ed7b61407fa31597f0e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 07:36:46 +0100 Subject: [PATCH 146/227] list the additional modules for our parser test suite. --- implicit.cabal | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/implicit.cabal b/implicit.cabal index 65f46cb..c3f17df 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -204,6 +204,10 @@ Test-suite test-implicit -Wall -Wextra -Weverything + Other-Modules: + ParserSpec.Expr + ParserSpec.Statement + ParserSpec.Util Benchmark parser-bench Type: exitcode-stdio-1.0 From b719ff029635bb5613311bbcd292e28d9282ff07 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 07:38:26 +0100 Subject: [PATCH 147/227] reorder --- implicit.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implicit.cabal b/implicit.cabal index c3f17df..feb615f 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -157,11 +157,11 @@ Executable implicitsnap text, vector-space Ghc-options: - -dynamic -threaded -rtsopts -O2 -optc-O3 + -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. From 4df47faeca30836ad36d6a11d187781aaaa0a785 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 18:27:47 +0100 Subject: [PATCH 148/227] spacing. --- implicit.cabal | 2 -- 1 file changed, 2 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index feb615f..c97f879 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -103,7 +103,6 @@ Library Graphics.Implicit.Export.Render.HandlePolylines Executable extopenscad - Main-is: extopenscad.hs Hs-source-dirs: programs Build-depends: @@ -128,7 +127,6 @@ Executable extopenscad -Weverything Executable docgen - main-is: docgen.hs Hs-source-dirs: programs build-depends: From cff5199a319e775fb93870a20c1f7b13c28132f7 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 19:13:15 +0100 Subject: [PATCH 149/227] use GHC variable consistently. --- Makefile | 60 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 7d08617..7d05bbe 100644 --- a/Makefile +++ b/Makefile @@ -2,32 +2,32 @@ ## Locations of binaries used when running tests, or generating the images to go along with our README.md. -# the location of stl2ps, from stltools, available from https://github.com/rsmith-nl/stltools/tree/develop +# The location of stl2ps, from stltools, available from https://github.com/rsmith-nl/stltools/tree/develop stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py -# the location of convert, from imagemagick +# The location of convert, from imagemagick convert=convert -# the location of GHC, used to compile .hs examples. +# The location of GHC, used to compile .hs examples. GHC=ghc # the location of the created extopenscad binary, for running shell based test cases. EXTOPENSCAD=dist/build/extopenscad/extopenscad -# the location of the benchmark binary, for benchmarking some implicitcad internals. +# The location of the benchmark binary, for benchmarking some implicitcad internals. BENCHMARK=dist/build/Benchmark/Benchmark -# the location of the parser benchmark binary, specifically for benchmarking implicitcad's parser. +# The location of the parser benchmark binary, specifically for benchmarking implicitcad's parser. PARSERBENCH=dist/build/parser-bench/parser-bench -# the location of the created test binary, for running haskell test cases. +# The location of the created test binary, for running haskell test cases. TESTSUITE=dist/build/test-implicit/test-implicit -# the location of it's source. +# The location of it's source. TESTFILES=$(shell find tests/ParserSpec -name '*.hs') -# the location of the documentation generator. for documenting (some of) the extopenscad language. +# The location of the documentation generator. for documenting (some of) the extopenscad language. DOCGEN=dist/build/docgen/docgen -## options used when calling ImplicitCAD. for testing, and for image generation. -# enable multiple CPU usage. +## Options used when calling ImplicitCAD. for testing, and for image generation. +# Enable multiple CPU usage. RTSOPTS=+RTS -N -# the resolution to generate objects at. FIXME: what does this mean in human terms? +# The resolution to generate objects at. FIXME: what does this mean in human terms? RESOPTS=-r 50 -#uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. +# Uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. #PROFILING= --enable-library-profiling --enable-executable-profiling LIBFILES=$(shell find Graphics -name '*.hs') @@ -36,26 +36,26 @@ LIBTARGET=dist/build/Graphics/Implicit.o EXECTARGETS=$(EXTOPENSCAD) $(BENCHMARK) $(TESTSUITE) $(PARSERBENCH) $(DOCGEN) TARGETS=$(EXECTARGETS) $(LIBTARGET) -# mark the below fake targets as unrean, so make will not get choked up if a file with one of these names is created. +# Mark the below fake targets as unrean, so make will not get choked up if a file with one of these names is created. .PHONY: build install clean distclean nukeclean docs dist examples tests -# empty out the default suffix list, to make debugging output cleaner. +# Empty out the default suffix list, to make debugging output cleaner. .SUFFIXES: -# allow for us to (ab)use $$* in dependencies of rules. +# Allow for us to (ab)use $$* in dependencies of rules. .SECONDEXPANSION: -# disable make's default builtin rules, to make debugging output cleaner. +# Disable make's default builtin rules, to make debugging output cleaner. MAKEFLAGS += --no-builtin-rules -# build implicitcad binaries. +# Build implicitcad binaries. build: $(TARGETS) -# install implicitcad. +# Install implicitcad. install: build cabal install -# cleanup from using the rules in this file. +# Cleanup from using the rules in this file. clean: Setup rm -f Examples/*.stl rm -f Examples/*.svg @@ -70,7 +70,7 @@ clean: Setup rm -rf dist/build/Graphics rm -f dist/build/libHS* -# clean up before making a release. +# Clean up before making a release. distclean: clean Setup ./Setup clean rm -f Setup Setup.hi Setup.o @@ -78,7 +78,7 @@ distclean: clean Setup rm -f `find ./ -name *~` rm -f `find ./ -name \#*\#` -# destroy the current user's cabal/ghc environment. +# Destroy the current user's cabal/ghc environment. nukeclean: distclean rm -rf ~/.cabal/ ~/.ghc/ @@ -91,16 +91,16 @@ docs: $(DOCGEN) dist: $(TARGETS) ./Setup sdist -# generate examples. +# Generate examples. examples: $(EXTOPENSCAD) - cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each ${RTSOPTS}; } done + cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; $(GHC) $$filename.hs -o $$filename; $$filename; } done -# generate images from the examples, so we can upload the images to our website. +# Generate images from the examples, so we can upload the images to our website. images: examples cd Examples && for each in `find ./ -name '*.stl' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; if [ -e $$filename.transform ] ; then echo ${stl2ps} $$each $$filename.ps `cat $$filename.transform`; else ${stl2ps} $$each $$filename.ps; fi; ${convert} $$filename.ps $$filename.png; } done -# hspec parser tests. +# Hspec parser tests. tests: $(TESTSUITE) $(TESTFILES) # cd tests && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each ${RESOPTS} ${RTSOPTS}; } done $(TESTSUITE) @@ -109,22 +109,22 @@ tests: $(TESTSUITE) $(TESTFILES) $(LIBTARGET): $(LIBFILES) cabal build implicit -# the test suite, since it's source is stored in a different location than the other binaries we build: +# The parser test suite, since it's source is stored in a different location than the other binaries we build: dist/build/test-implicit/test-implicit: $(TESTFILES) Setup dist/setup-config $(LIBTARGET) $(LIBFILES) cabal build test-implicit -# build a binary target with cabal. +# Build a binary target with cabal. dist/build/%: programs/$$(word 2,$$(subst /, ,%)).hs Setup dist/setup-config $(LIBTARGET) $(LIBFILES) cabal build $(word 2,$(subst /, ,$*)) -# prepare to build. +# Prepare to build. dist/setup-config: Setup implicit.cabal cabal update cabal install --only-dependencies --upgrade-dependencies cabal configure --enable-tests --enable-benchmarks $(PROFILING) -# the setup command, used to perform administrative tasks (haddock, upload to hackage, clean, etc...). +# The setup command, used to perform administrative tasks (haddock, upload to hackage, clean, etc...). Setup: Setup.*hs - ghc -O2 -Wall --make Setup + $(GHC) -O2 -Wall --make Setup From 907b8b87f763a588d050850ebd5fb566135b9a5e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 2 May 2019 19:14:49 +0100 Subject: [PATCH 150/227] typos. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7d05bbe..30a4e23 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py convert=convert # The location of GHC, used to compile .hs examples. GHC=ghc -# the location of the created extopenscad binary, for running shell based test cases. +# The location of the created extopenscad binary, for running shell based test cases. EXTOPENSCAD=dist/build/extopenscad/extopenscad # The location of the benchmark binary, for benchmarking some implicitcad internals. BENCHMARK=dist/build/Benchmark/Benchmark @@ -36,7 +36,7 @@ LIBTARGET=dist/build/Graphics/Implicit.o EXECTARGETS=$(EXTOPENSCAD) $(BENCHMARK) $(TESTSUITE) $(PARSERBENCH) $(DOCGEN) TARGETS=$(EXECTARGETS) $(LIBTARGET) -# Mark the below fake targets as unrean, so make will not get choked up if a file with one of these names is created. +# Mark the below fake targets as unreal, so make will not get choked up if a file with one of these names is created. .PHONY: build install clean distclean nukeclean docs dist examples tests # Empty out the default suffix list, to make debugging output cleaner. From 54e82f225eb930d357d0f1b639c8945226e07bcc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 4 May 2019 12:01:18 +0100 Subject: [PATCH 151/227] reformat a little less c style, spacing changes, and trust ghc to infer types more. --- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 29 ++++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index b39fb27..61d3419 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -14,9 +14,13 @@ import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR)) + import Graphics.Implicit.MathUtil (rmaximum, rminimum, rmax) + import Data.Maybe (fromMaybe, isJust) + import qualified Data.Either as Either (either) + import Data.VectorSpace ((^-^), (^+^), (^*), (<.>), normalized) -- Use getImplicit2 for handling extrusion of 2D shapes to 3D. @@ -27,15 +31,15 @@ getImplicit3 :: SymbolicObj3 -> Obj3 getImplicit3 (Rect3R r (x1,y1,z1) (x2,y2,z2)) = \(x,y,z) -> let (dx, dy, dz) = (x2-x1, y2-y1, z2-z1) in - rmaximum r [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2, abs (z-dz/2-z1) - dz/2] + rmaximum r [(abs $ x-dx/2-x1) - dx/2, (abs $ y-dy/2-y1) - dy/2, (abs $ z-dz/2-z1) - dz/2] getImplicit3 (Sphere r ) = - \(x,y,z) -> sqrt (x*x + y*y + z*z) - r + \(x,y,z) -> (sqrt $ x*x + y*y + z*z) - r getImplicit3 (Cylinder h r1 r2) = \(x,y,z) -> let - d = sqrt(x*x + y*y) - ((r2-r1)/h*z+r1) + d = (sqrt $ x*x + y*y) - ((r2-r1)/h*z+r1) θ = atan2 (r2-r1) h in - max (d * cos θ) (abs(z-h/(2::ℝ)) - h/(2::ℝ)) + max (d * cos θ) ((abs $ z-h/2) - (h/2)) -- (Rounded) CSG getImplicit3 (Complement3 symbObj) = let @@ -75,7 +79,7 @@ getImplicit3 (Translate3 v symbObj) = getImplicit3 (Scale3 s@(sx,sy,sz) symbObj) = let obj = getImplicit3 symbObj - k = abs(sx*sy*sz)**(1/3) + k = (abs $ sx*sy*sz)**(1/3) in \p -> k * obj (p ⋯/ s) getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = @@ -108,7 +112,7 @@ getImplicit3 (Shell3 w symbObj) = let obj = getImplicit3 symbObj in - \p -> abs (obj p) - w/2 + \p -> (abs $ obj p) - w/2 getImplicit3 (Outset3 d symbObj) = let obj = getImplicit3 symbObj @@ -121,7 +125,7 @@ getImplicit3 (ExtrudeR r symbObj h) = let obj = getImplicit2 symbObj in - \(x,y,z) -> rmax r (obj (x,y)) (abs (z - h/2) - h/2) + \(x,y,z) -> rmax r (obj (x,y)) ((abs $ z - h/2) - h/2) getImplicit3 (ExtrudeRM r twist scale translate symbObj height) = let obj = getImplicit2 symbObj @@ -135,12 +139,13 @@ getImplicit3 (ExtrudeRM r twist scale translate symbObj height) = scaleVec s (x,y) = (x/s, y/s) rotateVec :: ℝ -> ℝ2 -> ℝ2 rotateVec θ (x,y) = (x*cos θ + y*sin θ, y*cos θ - x*sin θ) - k = (pi :: ℝ)/(180:: ℝ) + k :: ℝ + k = pi/180 in \(x,y,z) -> let h = height' (x,y) in rmax r (obj . rotateVec (-k*twist' z) . scaleVec (scale' z) . (\a -> a ^-^ translate' z) $ (x,y)) - (abs (z - h/2) - h/2) + ((abs $ z - h/2) - h/2) getImplicit3 (ExtrudeOnEdgeOf symbObj1 symbObj2) = let obj1 = getImplicit2 symbObj1 @@ -174,7 +179,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = \(x,y,z) -> minimum $ do let - r = sqrt (x*x + y*y) + r = sqrt $ x*x + y*y θ = atan2 y x ns :: [ℕ] ns = @@ -190,7 +195,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = twist = rotate' θvirt rz_pos = if twists then let - (c,s) = (cos(twist*k), sin(twist*k)) + (c,s) = ((cos $ twist*k), (sin $ twist*k)) (r',z') = (r-rshift, z-zshift) in (c*r' - s*z', c*z' + s*r') @@ -198,7 +203,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = return $ if capped then rmax round' - (abs (θvirt - (totalRotation' / 2)) - (totalRotation' / 2)) + ((abs $ θvirt - (totalRotation' / 2)) - (totalRotation' / 2)) (obj rz_pos) else obj rz_pos -- FIXME: implement this, or implement a fallthrough function. From 01b19843fa66a5b969c7e643064ad4986d17b1d0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 4 May 2019 13:30:28 +0100 Subject: [PATCH 152/227] more minor syntax changes, and use cronn3 from data.cross, instead of copying it. --- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 61d3419..9ca628e 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -23,6 +23,8 @@ import qualified Data.Either as Either (either) import Data.VectorSpace ((^-^), (^+^), (^*), (<.>), normalized) +import Data.Cross (cross3) + -- Use getImplicit2 for handling extrusion of 2D shapes to 3D. import Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) @@ -97,16 +99,11 @@ getImplicit3 (Rotate3V θ axis symbObj) = let axis' = normalized axis obj = getImplicit3 symbObj - -- Note: this is ripped from data.cross. - cross3 :: forall t. Num t => (t, t, t) -> (t, t, t) -> (t, t, t) - cross3 (ax,ay,az) (bx,by,bz) = ( ay * bz - az * by - , az * bx - ax * bz - , ax * by - ay * bx ) in \v -> obj $ - v ^* cos θ - ^-^ (axis' `cross3` v) ^* sin θ - ^+^ (axis' ^* (axis' <.> (v ^* (1 - cos θ)))) + v ^* (cos θ) + ^-^ (axis' `cross3` v) ^* (sin θ) + ^+^ (axis' ^* (axis' <.> (v ^* (1 - (cos θ))))) -- Boundary mods getImplicit3 (Shell3 w symbObj) = let From e028f517f92a830ae4b1ae4504eda95bcb234e2c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 4 May 2019 13:48:36 +0100 Subject: [PATCH 153/227] a bit more formatting changes. --- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 9ca628e..93d9c81 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -41,7 +41,7 @@ getImplicit3 (Cylinder h r1 r2) = \(x,y,z) -> d = (sqrt $ x*x + y*y) - ((r2-r1)/h*z+r1) θ = atan2 (r2-r1) h in - max (d * cos θ) ((abs $ z-h/2) - (h/2)) + max (d * (cos θ)) ((abs $ z-h/2) - (h/2)) -- (Rounded) CSG getImplicit3 (Complement3 symbObj) = let @@ -88,11 +88,11 @@ getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = let obj = getImplicit3 symbObj rotateYZ :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) - rotateYZ θ obj' (x,y,z) = obj' ( x, y*cos θ + z*sin θ, z*cos θ - y*sin θ) + rotateYZ θ obj' (x,y,z) = obj' ( x, y*(cos θ) + z*(sin θ), z*(cos θ) - y*(sin θ)) rotateZX :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) - rotateZX θ obj' (x,y,z) = obj' ( x*cos θ - z*sin θ, y, z*cos θ + x*sin θ) + rotateZX θ obj' (x,y,z) = obj' ( x*(cos θ) - z*(sin θ), y, z*(cos θ) + x*(sin θ)) rotateXY :: ℝ -> (ℝ3 -> ℝ) -> (ℝ3 -> ℝ) - rotateXY θ obj' (x,y,z) = obj' ( x*cos θ + y*sin θ, y*cos θ - x*sin θ, z) + rotateXY θ obj' (x,y,z) = obj' ( x*(cos θ) + y*(sin θ), y*(cos θ) - x*(sin θ), z) in rotateXY xy $ rotateZX zx $ rotateYZ yz obj getImplicit3 (Rotate3V θ axis symbObj) = @@ -135,7 +135,7 @@ getImplicit3 (ExtrudeRM r twist scale translate symbObj height) = scaleVec :: ℝ -> ℝ2 -> ℝ2 scaleVec s (x,y) = (x/s, y/s) rotateVec :: ℝ -> ℝ2 -> ℝ2 - rotateVec θ (x,y) = (x*cos θ + y*sin θ, y*cos θ - x*sin θ) + rotateVec θ (x,y) = (x*(cos θ) + y*(sin θ), y*(cos θ) - x*(sin θ)) k :: ℝ k = pi/180 in From 3f4d7046377715ccb1606d73f98e3a39a7cce7cb Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 4 May 2019 14:05:02 +0100 Subject: [PATCH 154/227] clean up function types, and use abs more haskell-like, less c-like. --- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index e8866a2..e882297 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -26,14 +26,15 @@ getImplicit2 (RectR r (x1,y1) (x2,y2)) = (dx, dy) = (x2-x1, y2-y1) in if r == 0 - then maximum [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2] - else rmaximum r [abs (x-dx/2-x1) - dx/2, abs (y-dy/2-y1) - dy/2] + then maximum [(abs $ x-dx/2-x1) - dx/2, (abs $ y-dy/2-y1) - dy/2] + else rmaximum r [(abs $ x-dx/2-x1) - dx/2, (abs $ y-dy/2-y1) - dy/2] getImplicit2 (Circle r) = - \(x,y) -> sqrt (x * x + y * y) - r + \(x,y) -> (sqrt $ x * x + y * y) - r getImplicit2 (PolygonR _ points) = \p -> let pair :: ℕ -> (ℝ2,ℝ2) pair n = (points `genericIndex` n, points `genericIndex` mod (n + 1) (genericLength points) ) + pairs :: [(ℝ2,ℝ2)] pairs = [ pair n | n <- [0 .. genericLength points - 1] ] relativePairs = map (\(a,b) -> (a ^-^ p, b ^-^ p) ) pairs crossing_points = @@ -43,7 +44,8 @@ getImplicit2 (PolygonR _ points) = seemsInRight = odd . length . filter (>0) $ nub crossing_points seemsInLeft = odd . length . filter (<0) $ nub crossing_points isIn = seemsInRight && seemsInLeft - dists = map (distFromLineSeg p) pairs :: [ℝ] + dists :: [ℝ] + dists = map (distFromLineSeg p) pairs in minimum dists * if isIn then -1 else 1 -- (Rounded) CSG @@ -85,7 +87,7 @@ getImplicit2 (Translate2 v symbObj) = getImplicit2 (Scale2 s@(sx,sy) symbObj) = \p -> let obj = getImplicit2 symbObj - k = abs(max sx sy) + k = abs $ max sx sy in k * obj (p ⋯/ s) getImplicit2 (Rotate2 θ symbObj) = @@ -98,7 +100,7 @@ getImplicit2 (Shell2 w symbObj) = \p -> let obj = getImplicit2 symbObj in - abs (obj p) - w/2 + (abs $ obj p) - w/2 getImplicit2 (Outset2 d symbObj) = \p -> let obj = getImplicit2 symbObj From 133b978913137b8fe4c36d90c4d6407588780c01 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 4 May 2019 14:17:24 +0100 Subject: [PATCH 155/227] make more readable. --- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index e882297..56a04ee 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -94,7 +94,7 @@ getImplicit2 (Rotate2 θ symbObj) = \(x,y) -> let obj = getImplicit2 symbObj in - obj ( x*cos θ + y*sin θ, y*cos θ - x*sin θ) + obj ( x*(cos θ) + y*(sin θ), y*(cos θ) - x*(sin θ)) -- Boundary mods getImplicit2 (Shell2 w symbObj) = \p -> let From ce6ce19d233ce4cdc5ae7a09ad6da99345154785 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 5 May 2019 21:58:59 +0100 Subject: [PATCH 156/227] wrap fromIntegral. --- Graphics/Implicit/Definitions.hs | 11 ++++++++--- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 4700cc4..b53ed7b 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -75,11 +75,12 @@ module Graphics.Implicit.Definitions ( ExtrudeOnEdgeOf, RotateExtrude), Rectilinear2, - Rectilinear3 + Rectilinear3, + fromℕtoℝ ) where -import Prelude (Show, Double, Integer, Int, Either, show, (*), (/)) +import Prelude (Show, Double, Integer, Int, Either, show, (*), (/), fromIntegral) import Data.Maybe (Maybe) @@ -114,11 +115,15 @@ both f (x,y) = (f x, f y) allthree :: forall t b. (t -> b) -> (t, t, t) -> (b, b, b) allthree f (x,y,z) = (f x, f y, f z) - -- TODO: Find a better place for this (⋅) :: InnerSpace a => a -> a -> Scalar a (⋅) = (<.>) +-- Wrap the functions that convert datatypes. + +fromℕtoℝ :: ℕ -> ℝ +fromℕtoℝ = fromIntegral + -- add aditional instances to Show, for when we dump the intermediate form of an object. instance Show (ℝ -> ℝ) where show _ = "" diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 93d9c81..72f4a1d 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -8,12 +8,12 @@ module Graphics.Implicit.ObjectUtil.GetImplicit3 (getImplicit3) where -import Prelude (Either(Left, Right), abs, (-), (/), (*), sqrt, (+), atan2, max, cos, map, (==), minimum, ($), maximum, (**), sin, const, pi, (.), Bool(True, False), ceiling, floor, fromIntegral, return, error, head, tail, Num) +import Prelude (Either(Left, Right), abs, (-), (/), (*), sqrt, (+), atan2, max, cos, map, (==), minimum, ($), maximum, (**), sin, const, pi, (.), Bool(True, False), ceiling, floor, return, error, head, tail, Num) import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, - ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR)) + ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR), fromℕtoℝ) import Graphics.Implicit.MathUtil (rmaximum, rminimum, rmax) @@ -187,7 +187,7 @@ getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = [0 .. floor $ (totalRotation' - θ) / tau] n <- ns let - θvirt = fromIntegral n * tau + θ + θvirt = (fromℕtoℝ n) * tau + θ (rshift, zshift) = translate' θvirt twist = rotate' θvirt rz_pos = if twists From 741123e1dd57b8f12901341d6cf43f26ad6a9252 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 6 May 2019 22:29:24 +0100 Subject: [PATCH 157/227] use integer values for counting, and add more type conversion wrappers. --- Graphics/Implicit/Definitions.hs | 10 +++++++- Graphics/Implicit/ObjectUtil/GetBox3.hs | 31 ++++++++++++++++--------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index b53ed7b..df16aae 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -76,7 +76,9 @@ module Graphics.Implicit.Definitions ( RotateExtrude), Rectilinear2, Rectilinear3, - fromℕtoℝ + fromℕtoℝ, + fromFastℕtoℝ, + fromFastℕ, ) where @@ -124,6 +126,12 @@ allthree f (x,y,z) = (f x, f y, f z) fromℕtoℝ :: ℕ -> ℝ fromℕtoℝ = fromIntegral +fromFastℕtoℝ :: Fastℕ -> ℝ +fromFastℕtoℝ = fromIntegral + +fromFastℕ :: Fastℕ -> Int +fromFastℕ a = a + -- add aditional instances to Show, for when we dump the intermediate form of an object. instance Show (ℝ -> ℝ) where show _ = "" diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index 6bc0cbc..c5afab7 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -8,11 +8,11 @@ module Graphics.Implicit.ObjectUtil.GetBox3 (getBox3) where -import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise) +import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), (==), (||), max, (/), (-), (+), map, unzip, ($), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise, take) import Data.Maybe(Maybe(Nothing, Just)) -import Graphics.Implicit.Definitions (ℝ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), (⋯*)) +import Graphics.Implicit.Definitions (ℝ, Fastℕ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), (⋯*), fromFastℕtoℝ, fromFastℕ) import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) import Data.Maybe (fromMaybe) @@ -112,7 +112,7 @@ getBox3 (ExtrudeOnEdgeOf symbObj1 symbObj2) = ((bx1,by1),(bx2,by2)) = getBox2 symbObj2 in ((bx1+ax1, by1+ax1, ay1), (bx2+ax2, by2+ax2, ay2)) --- FIXME: magic numbers in range. +-- FIXME: magic numbers. getBox3 (ExtrudeRM _ twist scale translate symbObj eitherh) = let range :: [ℝ] @@ -152,19 +152,28 @@ getBox3 (RotateExtrude _ _ (Left (xshift,yshift)) _ symbObj) = r = max x2 (x2 + xshift) in ((-r, -r, min y1 (y1 + yshift)),(r, r, max y2 (y2 + yshift))) --- FIXME: magic numbers +-- FIXME: magic numbers. getBox3 (RotateExtrude rot _ (Right f) rotate symbObj) = let + samples :: Fastℕ + samples=11 + xfuzz :: ℝ + xfuzz=1.1 + yfuzz :: ℝ + yfuzz=0.1 + range :: [Fastℕ] + range = [0, 1 .. (samples-1)] + step = rot/(fromFastℕtoℝ $ samples-1) ((x1,y1),(x2,y2)) = getBox2 symbObj - (xshifts, yshifts) = unzip [f θ | θ <- [0 , rot / 10 .. rot] ] - xmax = maximum xshifts - ymax = maximum yshifts - ymin = minimum yshifts - xmax' | xmax > 0 = xmax * 1.1 + (xrange, yrange) = unzip $ take (fromFastℕ samples) $ map f $ map (step*) $ map fromFastℕtoℝ range + xmax = maximum xrange + ymax = maximum yrange + ymin = minimum yrange + xmax' | xmax > 0 = xmax * xfuzz | xmax < - x1 = 0 | otherwise = xmax - ymax' = ymax + 0.1 * (ymax - ymin) - ymin' = ymin - 0.1 * (ymax - ymin) + ymax' = ymax + yfuzz * (ymax - ymin) + ymin' = ymin - yfuzz * (ymax - ymin) (r, _, _) = if either (==0) (const False) rotate then let s = maximum $ map abs [x2, y1, y2] From 973b78995b8edf9b901757582f59c75213d44536 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 6 May 2019 23:05:33 +0100 Subject: [PATCH 158/227] switch another spot to counting with integers. --- Graphics/Implicit/ObjectUtil/GetBox3.hs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index c5afab7..3a98ee3 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -115,19 +115,22 @@ getBox3 (ExtrudeOnEdgeOf symbObj1 symbObj2) = -- FIXME: magic numbers. getBox3 (ExtrudeRM _ twist scale translate symbObj eitherh) = let - range :: [ℝ] - range = [0, 0.1 .. 1.0] + samples :: Fastℕ + samples=11 + hfuzz :: ℝ + hfuzz = 0.2 + range :: [Fastℕ] + range = [0, 1 .. (samples-1)] ((x1,y1),(x2,y2)) = getBox2 symbObj (dx,dy) = (x2 - x1, y2 - y1) - (xrange, yrange) = (map (\s -> x1+s*dx) range, map (\s -> y1+s*dy) range ) - + (xrange, yrange) = ( map (\s -> x1+s*dx/(fromFastℕtoℝ $ samples-1)) $ map fromFastℕtoℝ range, map (\s -> y1+s*dy/(fromFastℕtoℝ $ samples-1)) $ map fromFastℕtoℝ range ) h = case eitherh of Left h' -> h' - Right hf -> hmax + 0.2*(hmax-hmin) + Right hf -> hmax + hfuzz*(hmax-hmin) where hs = [hf (x,y) | x <- xrange, y <- yrange] (hmin, hmax) = (minimum hs, maximum hs) - hrange = map (h*) range + hrange = map (/(fromFastℕtoℝ $ samples-1)) $ map (h*) $ map fromFastℕtoℝ range sval = case scale of Nothing -> 1 Just scale' -> maximum $ map (abs . scale') hrange @@ -156,9 +159,9 @@ getBox3 (RotateExtrude _ _ (Left (xshift,yshift)) _ symbObj) = getBox3 (RotateExtrude rot _ (Right f) rotate symbObj) = let samples :: Fastℕ - samples=11 + samples = 11 xfuzz :: ℝ - xfuzz=1.1 + xfuzz = 1.1 yfuzz :: ℝ yfuzz=0.1 range :: [Fastℕ] From 42c4fabcc324492e648677488042506185c9d871 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 04:50:30 +0100 Subject: [PATCH 159/227] spacing/comments, and simplify an unneeded function composition. --- Graphics/Implicit/ObjectUtil/GetBox3.hs | 6 ++++-- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 1 + Makefile | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index 3a98ee3..aae74cc 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -18,7 +18,9 @@ import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) import Data.Maybe (fromMaybe) import Data.VectorSpace ((^-^), (^+^)) --- test to see whether a Box3 has area. +-- FIXME: many variables are being ignored here. no rounding for intersect, or difference.. etc. + +-- Test to see whether a Box3 has area. isEmpty :: (Eq a2, Eq a1, Eq a) => ((a, a1, a2), (a, a1, a2)) -> Bool isEmpty ((a,b,c),(d,e,f)) = a==d || b==e || c==f @@ -142,7 +144,7 @@ getBox3 (ExtrudeRM _ twist scale translate symbObj eitherh) = Just _ -> (-d, -d, d, d) where d = sval * getDist2 (0,0) symbObj translate' = fromMaybe (const (0,0)) translate - (tvalsx, tvalsy) = unzip . map (translate' . (h*)) $ hrange + (tvalsx, tvalsy) = unzip $ map (translate' . (h*)) hrange (tminx, tminy) = (minimum tvalsx, minimum tvalsy) (tmaxx, tmaxy) = (maximum tvalsx, maximum tvalsy) in diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 72f4a1d..dccb3a6 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -28,6 +28,7 @@ import Data.Cross (cross3) -- Use getImplicit2 for handling extrusion of 2D shapes to 3D. import Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) +-- Get a function that describes the surface of the object. getImplicit3 :: SymbolicObj3 -> Obj3 -- Primitives getImplicit3 (Rect3R r (x1,y1,z1) (x2,y2,z2)) = diff --git a/Makefile b/Makefile index 30a4e23..5df4737 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ # ImplicitCAD Makefile. Build and test Implicitcad. - ## Locations of binaries used when running tests, or generating the images to go along with our README.md. # The location of stl2ps, from stltools, available from https://github.com/rsmith-nl/stltools/tree/develop stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py From 2a8b7011568e8e56922fc3d10c18c47e418e91f0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 05:03:23 +0100 Subject: [PATCH 160/227] comment, spacing, and minor clarity changes. --- Graphics/Implicit/ObjectUtil/GetBox2.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index d4504b2..905f617 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -14,12 +14,12 @@ import Graphics.Implicit.Definitions (ℝ, ℝ2, Box2, (⋯*), import Data.VectorSpace (magnitude, (^-^), (^+^)) --- Is a Box2 empty? --- Really, this checks if it is one dimensional, which is good enough. +-- | Is a Box2 empty? +-- | Really, this checks if it is one dimensional, which is good enough. isEmpty :: Box2 -> Bool isEmpty ((a, b), (c, d)) = a==c || b==d --- Define a Box2 around all of the given points. +-- | Define a Box2 around all of the given points. pointsBox :: [ℝ2] -> Box2 pointsBox points = let @@ -27,6 +27,7 @@ pointsBox points = in ((minimum xs, minimum ys), (maximum xs, maximum ys)) +-- | Define a box that fits around the given boxes. unionBoxes :: [Box2] -> Box2 unionBoxes boxes = let @@ -44,7 +45,7 @@ outsetBox r (a,b) = getBox2 :: SymbolicObj2 -> Box2 -- Primitives getBox2 (RectR _ a b) = (a,b) -getBox2 (Circle r ) = ((-r, -r), (r,r)) +getBox2 (Circle r) = ((-r, -r), (r,r)) getBox2 (PolygonR _ points) = pointsBox points -- (Rounded) CSG getBox2 (Complement2 _) = @@ -85,7 +86,7 @@ getBox2 (Scale2 s symbObj) = getBox2 (Rotate2 θ symbObj) = let ((x1,y1), (x2,y2)) = getBox2 symbObj - rotate (x,y) = (x*cos θ - y*sin θ, x*sin θ + y*cos θ) + rotate (x,y) = (x*(cos θ) - y*(sin θ), x*(sin θ) + y*(cos θ)) in pointsBox [ rotate (x1, y1) , rotate (x1, y2) From 7a0fc5271f316b435af228f74c2c2a7d7e99a801 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 05:07:02 +0100 Subject: [PATCH 161/227] use Type to eliminate a warning. --- Graphics/Implicit/ExtOpenScad/Util/StateC.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs index 06302e9..9c993ff 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs @@ -19,6 +19,7 @@ import Data.Map (lookup) import Control.Monad.State (StateT, get, put, modify, liftIO) import System.FilePath(()) import Control.Monad.IO.Class (MonadIO) +import Data.Kind (Type) -- This is the state machine. It contains the variables, their values, the path, and... ? type CompState = (VarLookup, [OVal], FilePath, (), ()) @@ -67,11 +68,11 @@ getRelPath relPath = do path <- getPath return $ path relPath -errorC :: forall (m :: * -> *) a. (Show a, MonadIO m) => a -> a -> String -> m () +errorC :: forall (m :: Type -> Type) a. (Show a, MonadIO m) => a -> a -> String -> m () errorC lineN columnN err = liftIO $ putStrLn $ "On line " ++ show lineN ++ ", column " ++ show columnN ++ ": " ++ err {-# INLINABLE errorC #-} -mapMaybeM :: forall t (m :: * -> *) a. Monad m => (t -> m a) -> Maybe t -> m (Maybe a) +mapMaybeM :: forall t (m :: Type -> Type) a. Monad m => (t -> m a) -> Maybe t -> m (Maybe a) mapMaybeM f (Just a) = do b <- f a return (Just b) From 2a159909db5a5c63705342b7afc17aee8f7a877c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 05:09:39 +0100 Subject: [PATCH 162/227] use Type to eliminate a warning. --- Graphics/Implicit/ExtOpenScad/Parser/Util.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs index c05775a..50c8a54 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs @@ -20,6 +20,8 @@ import Data.Functor.Identity (Identity) import Graphics.Implicit.ExtOpenScad.Definitions (Pattern(Wild, Name, ListP)) +import Data.Kind (Type) + -- white space, including tabs, newlines and comments genSpace :: ParsecT String u Identity String genSpace = many $ @@ -47,7 +49,7 @@ infixr 1 *<|> a *<|> b = try a <|> b infixr 2 ?: -(?:) :: forall s u (m :: * -> *) a. String -> ParsecT s u m a -> ParsecT s u m a +(?:) :: forall s u (m :: Type -> Type) a. String -> ParsecT s u m a -> ParsecT s u m a l ?: p = p l stringGS :: String -> ParsecT String u Identity String @@ -72,7 +74,7 @@ padString s = do tryMany :: forall u a tok. [GenParser tok u a] -> ParsecT [tok] u Identity a tryMany = foldl1 (<|>) . map try -variableSymb :: forall s u (m :: * -> *). Stream s m Char => ParsecT s u m String +variableSymb :: forall s u (m :: Type -> Type). Stream s m Char => ParsecT s u m String variableSymb = many1 (noneOf " ,|[]{}()+-*&^%#@!~`'\"\\/;:.,<>?=") "variable" patternMatcher :: GenParser Char st Pattern From 1f98d8c570bee5f78293df1ea59ce948e9dd2549 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 05:13:03 +0100 Subject: [PATCH 163/227] use Type to eliminate a warning. --- Graphics/Implicit/ExtOpenScad/Parser/Statement.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs index 9141dd0..3b17603 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Statement.hs @@ -17,6 +17,8 @@ import Data.Maybe(Maybe(Just, Nothing)) import Data.Functor.Identity(Identity) +import Data.Kind (Type) + -- We use parsec to parse. import Text.ParserCombinators.Parsec (try, sepBy, sourceLine, sourceColumn, GenParser, oneOf, space, char, getPosition, parse, many1, eof, string, ParseError, many, noneOf, Line, Column, (<|>), ()) import Text.Parsec.Prim (ParsecT) @@ -256,12 +258,12 @@ moduleArgsUnitDecl = do return argTemplate -- | Find the line number. Used when generating errors. -lineNumber :: forall s u (m :: * -> *). +lineNumber :: forall s u (m :: Type -> Type). Monad m => ParsecT s u m Line lineNumber = fmap sourceLine getPosition -- | Find the column number. Used when generating errors. -columnNumber :: forall s u (m :: * -> *). +columnNumber :: forall s u (m :: Type -> Type). Monad m => ParsecT s u m Column columnNumber = fmap sourceColumn getPosition From 10a4398c58f073e32a385a13b68aca75f33242eb Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 05:41:38 +0100 Subject: [PATCH 164/227] allow for inlining, and specialization on functions GHC wants to inline. also, remove a warning. --- Graphics/Implicit/ExtOpenScad/Util/OVal.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index e0302e3..5134a11 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -28,6 +28,7 @@ class OTypeMirror a where fromOObjList :: OVal -> Maybe [a] fromOObjList (OList list) = mapM fromOObj list fromOObjList _ = Nothing + {-# INLINABLE fromOObjList #-} toOObj :: a -> OVal instance OTypeMirror OVal where @@ -37,16 +38,19 @@ instance OTypeMirror OVal where instance OTypeMirror ℝ where fromOObj (ONum n) = Just n fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj = ONum instance OTypeMirror ℕ where - fromOObj (ONum n) = if n == fromIntegral (floor n) then Just (floor n) else Nothing + fromOObj (ONum n) = if n == fromInteger (floor n) then Just (floor n) else Nothing fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj = ONum . fromIntegral instance OTypeMirror Bool where fromOObj (OBool b) = Just b fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj = OBool -- We don't actually use single chars, this is to compile lists of chars (AKA strings) after passing through OTypeMirror [a]'s fromOObj. @@ -54,28 +58,33 @@ instance OTypeMirror Bool where instance OTypeMirror Char where fromOObj (OString str) = Just $ head str fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} fromOObjList (OString str) = Just str fromOObjList _ = Nothing toOObj a = OString [a] instance (OTypeMirror a) => OTypeMirror [a] where fromOObj = fromOObjList + {-# INLINABLE fromOObj #-} toOObj list = OList $ map toOObj list instance (OTypeMirror a) => OTypeMirror (Maybe a) where fromOObj a = Just $ fromOObj a + {-# INLINABLE fromOObj #-} toOObj (Just a) = toOObj a toOObj Nothing = OUndefined instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (a,b) where fromOObj (OList [fromOObj -> Just a,fromOObj -> Just b]) = Just (a,b) fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj (a,b) = OList [toOObj a, toOObj b] instance (OTypeMirror a, OTypeMirror b, OTypeMirror c) => OTypeMirror (a,b,c) where fromOObj (OList [fromOObj -> Just a,fromOObj -> Just b,fromOObj -> Just c]) = Just (a,b,c) fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj (a,b,c) = OList [toOObj a, toOObj b, toOObj c] instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (a -> b) where @@ -89,6 +98,7 @@ instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (a -> b) where fromMaybe (error $ "coercing OVal to a -> b isn't always safe; use a -> Maybe b" ++ " (trace: " ++ show oInput ++ " -> " ++ show oOutput ++ " )") output fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj f = OFunc $ \oObj -> case fromOObj oObj :: Maybe a of Nothing -> OError ["bad input type"] @@ -98,6 +108,7 @@ instance (OTypeMirror a, OTypeMirror b) => OTypeMirror (Either a b) where fromOObj (fromOObj -> Just (x :: a)) = Just $ Left x fromOObj (fromOObj -> Just (x :: b)) = Just $ Right x fromOObj _ = Nothing + {-# INLINABLE fromOObj #-} toOObj (Right x) = toOObj x toOObj (Left x) = toOObj x From ecdaf356210575239bcc8eb2ffc747e069dafbcc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 05:42:05 +0100 Subject: [PATCH 165/227] be explicit about where things come from. --- programs/parser-bench.hs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/programs/parser-bench.hs b/programs/parser-bench.hs index 196ec6f..43ba53c 100644 --- a/programs/parser-bench.hs +++ b/programs/parser-bench.hs @@ -1,9 +1,10 @@ -import Criterion.Main -import Graphics.Implicit.ExtOpenScad.Definitions -import Graphics.Implicit.ExtOpenScad.Parser.Expr -import Graphics.Implicit.ExtOpenScad.Parser.Statement -import Text.ParserCombinators.Parsec hiding (State) -import Text.Printf +import Prelude (IO, String, Int, Either(Left, Right), return, show, ($), otherwise, (==), (-), (++), concat, error) +import Criterion.Main (Benchmark, bgroup, defaultMain, bench, env, whnf) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr, StatementI) +import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) +import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) +import Text.ParserCombinators.Parsec (parse) +import Text.Printf (printf) lineComment :: Int -> String lineComment width = "//" ++ ['x' | _ <- [1..width]] ++ "\n" From 604041dd225e4dd0713c49d594fbb857c95e05d0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 17:27:39 +0100 Subject: [PATCH 166/227] spacing and comment changes. --- Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs | 4 +++- Graphics/Implicit/ObjectUtil/GetBox2.hs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs index 56d7814..5510ba1 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/ArgParser.hs @@ -15,6 +15,7 @@ import Prelude(String, Maybe(Just, Nothing), ($), (++), concat, show, error, ret import qualified Prelude as Prelude (null) import Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch, APTerminator, APFailIf, APExample), OVal (OError), TestInvariant(EulerCharacteristic)) + import Graphics.Implicit.ExtOpenScad.Util.OVal (fromOObj, toOObj, OTypeMirror) import Graphics.Implicit.Definitions(ℕ) @@ -31,6 +32,7 @@ import Control.Arrow(first) -- ** argument and combinators +-- | Builds an argparser for the type that is expected from it. argument :: forall desiredType. (OTypeMirror desiredType) => String -> ArgParser desiredType argument name = AP name Nothing "" $ \oObjVal -> do @@ -74,7 +76,7 @@ eulerCharacteristic _ _ = error "Impossible!" -- | Apply arguments to an ArgParser argMap :: - [(Maybe String, OVal)] -- ^ arguments + [(Maybe String, OVal)] -- ^ arguments -> ArgParser a -- ^ ArgParser to apply them to -> (Maybe a, [String]) -- ^ (result, error messages) diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 905f617..29df518 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -45,7 +45,7 @@ outsetBox r (a,b) = getBox2 :: SymbolicObj2 -> Box2 -- Primitives getBox2 (RectR _ a b) = (a,b) -getBox2 (Circle r) = ((-r, -r), (r,r)) +getBox2 (Circle r) = ((-r, -r), (r,r)) getBox2 (PolygonR _ points) = pointsBox points -- (Rounded) CSG getBox2 (Complement2 _) = From 085248ff786769a80fc5676720a2a7ebf5874247 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 18:42:52 +0100 Subject: [PATCH 167/227] comments and spacing changes, and use an integral for counting sides of a polygon, instead of doubles. also, add a tester for polygons. --- Examples/example14.escad | 10 +++ Graphics/Implicit/ExtOpenScad/Primitives.hs | 97 +++++---------------- 2 files changed, 31 insertions(+), 76 deletions(-) create mode 100644 Examples/example14.escad diff --git a/Examples/example14.escad b/Examples/example14.escad new file mode 100644 index 0000000..2764e55 --- /dev/null +++ b/Examples/example14.escad @@ -0,0 +1,10 @@ +// example7.escad -- A twisted rounded extrusion of the rounded union of 5 hexagonical solids. +linear_extrude (height = 40, center=true, twist=90, r=5){ + union ( r = 8) { + circle (10,$fn=6); + translate ([22,0]) circle (10,$fn=6); + translate ([0,22]) circle (10,$fn=6); + translate ([-22,0]) circle (10,$fn=6); + translate ([0,-22]) circle (10,$fn=6); + } +} diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index c42058d..20e40a5 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -2,7 +2,7 @@ -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --- Idealy, we'd like to parse openscad code, with some improvements, for backwards compatability. +-- Idealy, we'd like to parse a superset of openscad code, with some improvements. -- This file provides primitive objects for the openscad parser. @@ -12,9 +12,9 @@ -- Export one set containing all of the primitive object's patern matches. module Graphics.Implicit.ExtOpenScad.Primitives (primitives) where -import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), Fractional, ($), return, either, id, (-), (==), (&&), (<), fromIntegral, (*), cos, sin, pi, (/), (>), const, uncurry, realToFrac, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) +import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), Fractional, ($), return, either, id, (-), (==), (&&), (<), (*), cos, sin, pi, (/), (>), const, uncurry, realToFrac, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) -import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, ℕ, SymbolicObj2, SymbolicObj3) +import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, ℕ, SymbolicObj2, SymbolicObj3, fromℕtoℝ) import Graphics.Implicit.ExtOpenScad.Definitions (OVal (OObj2, OObj3), ArgParser) @@ -22,7 +22,7 @@ import Graphics.Implicit.ExtOpenScad.Util.ArgParser (doc, defaultTo, argument, e import Graphics.Implicit.ExtOpenScad.Util.OVal (caseOType, divideObjs, (<||>)) --- note the use of a qualified import, so we don't have the functions in this file conflict with what we're importing. +-- Note the use of a qualified import, so we don't have the functions in this file conflict with what we're importing. import qualified Graphics.Implicit.Primitives as Prim (sphere, rect3R, rectR, translate, circle, polygonR, extrudeR, cylinder2, union, unionR, intersect, intersectR, difference, differenceR, rotate, rotate3V, rotate3, scale, extrudeR, extrudeRM, rotateExtrude, shell, pack3, pack2) import Data.Maybe (isNothing) @@ -32,13 +32,13 @@ import Control.Monad (mplus) import Data.VectorSpace (VectorSpace, Scalar, (*^)) import GHC.Real (RealFrac) --- The only thing exported here. basically, a list of ... ? -primitives :: [(String, [OVal] -> ArgParser (IO [OVal]) )] +-- | The only thing exported here. basically, a list of functions, which accept OVal arguments and retrun an ArgParser ? +primitives :: [(String, [OVal] -> ArgParser (IO [OVal]))] primitives = [ sphere, cube, square, cylinder, circle, polygon, union, difference, intersect, translate, scale, rotate, extrude, pack, shell, rotateExtrude, unit ] --- sphere is a module without a suite. --- this means that the parser will look for this like --- sphere(args...); +-- | sphere is a module without a suite. +-- this means that the parser will look for this like +-- sphere(args...); sphere :: (String, [OVal] -> ArgParser (IO [OVal])) sphere = moduleWithoutSuite "sphere" $ do example "sphere(3);" @@ -56,16 +56,13 @@ sphere = moduleWithoutSuite "sphere" $ do cube :: (String, [OVal] -> ArgParser (IO [OVal])) cube = moduleWithoutSuite "cube" $ do - -- examples example "cube(size = [2,3,4], center = true, r = 0.5);" example "cube(4);" - -- arguments shared between forms r :: ℝ <- argument "r" `doc` "radius of rounding" `defaultTo` 0 - -- arguments (two forms) ((x1,x2), (y1,y2), (z1,z2)) <- do @@ -79,7 +76,7 @@ cube = moduleWithoutSuite "cube" $ do `doc` "should center? (non-intervals)" `defaultTo` False let - toInterval' :: Fractional t => t -> (t, t) + toInterval' :: ℝ -> (ℝ, ℝ) toInterval' = toInterval center return (either toInterval' id x, either toInterval' id y, @@ -92,28 +89,23 @@ cube = moduleWithoutSuite "cube" $ do `defaultTo` False let (x,y, z) = either (\w -> (w,w,w)) id size return (toInterval center x, toInterval center y, toInterval center z) - -- Tests test "cube(4);" `eulerCharacteristic` 2 test "cube(size=[2,3,4]);" `eulerCharacteristic` 2 - addObj3 $ Prim.rect3R r (x1, y1, z1) (x2, y2, z2) square :: (String, [OVal] -> ArgParser (IO [OVal])) square = moduleWithoutSuite "square" $ do - -- examples example "square(x=[-2,2], y=[-1,5]);" example "square(size = [3,4], center = true, r = 0.5);" example "square(4);" - -- arguments shared between forms r :: ℝ <- argument "r" `doc` "radius of rounding" `defaultTo` 0 - -- arguments (two forms) ((x1,x2), (y1,y2)) <- do @@ -125,7 +117,7 @@ square = moduleWithoutSuite "square" $ do `doc` "should center? (non-intervals)" `defaultTo` False let - toInterval' :: Fractional t => t -> (t, t) + toInterval' :: ℝ -> (ℝ, ℝ) toInterval' = toInterval center return (either toInterval' id x, either toInterval' id y) @@ -137,22 +129,18 @@ square = moduleWithoutSuite "square" $ do `defaultTo` False let (x,y) = either (\w -> (w,w)) id size return (toInterval center x, toInterval center y) - -- Tests test "square(2);" `eulerCharacteristic` 0 test "square(size=[2,3]);" `eulerCharacteristic` 0 - addObj2 $ Prim.rectR r (x1, y1) (x2, y2) cylinder :: (String, [OVal] -> ArgParser (IO [OVal])) cylinder = moduleWithoutSuite "cylinder" $ do - example "cylinder(r=10, h=30, center=true);" example "cylinder(r1=4, r2=6, h=10);" example "cylinder(r=5, h=10, $fn = 6);" - -- arguments r :: ℝ <- argument "r" `defaultTo` 1 @@ -166,19 +154,17 @@ cylinder = moduleWithoutSuite "cylinder" $ do r2 :: ℝ <- argument "r2" `defaultTo` 1 `doc` "top radius; overrides r" - fn :: ℕ <- argument "$fn" + sides :: ℕ <- argument "$fn" `defaultTo` (-1) `doc` "number of sides, for making prisms" center :: Bool <- argument "center" `defaultTo` False `doc` "center cylinder with respect to z?" - -- Tests test "cylinder(r=10, h=30, center=true);" `eulerCharacteristic` 0 test "cylinder(r=5, h=10, $fn = 6);" `eulerCharacteristic` 0 - let (h1, h2) = either (toInterval center) id h dh = h2 - h1 @@ -187,49 +173,36 @@ cylinder = moduleWithoutSuite "cylinder" $ do if h1 == 0 then id else Prim.translate (0,0,h1) - -- The result is a computation state modifier that adds a 3D object, -- based on the args. addObj3 $ if r1 == 1 && r2 == 1 then let - obj2 = if fn < 0 then Prim.circle r else Prim.polygonR 0 $ - let - sides :: ℝ - sides = fromIntegral fn - in [(r*cos θ, r*sin θ )| θ <- [2*pi*n/sides | n <- [0.0 .. sides - 1.0]]] + obj2 = if sides < 0 then Prim.circle r else Prim.polygonR 0 $ + [(r*cos θ, r*sin θ )| θ <- [2*pi*(fromℕtoℝ n)/(fromℕtoℝ sides) | n <- [0 .. sides - 1]]] obj3 = Prim.extrudeR 0 obj2 dh in shift obj3 else shift $ Prim.cylinder2 r1 r2 dh circle :: (String, [OVal] -> ArgParser (IO [OVal])) circle = moduleWithoutSuite "circle" $ do - example "circle(r=10); // circle" example "circle(r=5, $fn=6); //hexagon" - -- Arguments - r :: ℝ <- argument "r" - `doc` "radius of the circle" - fn :: ℕ <- argument "$fn" - `doc` "if defined, makes a regular polygon with n sides instead of a circle" - `defaultTo` (-1) - + r :: ℝ <- argument "r" + `doc` "radius of the circle" + sides :: ℕ <- argument "$fn" + `doc` "if defined, makes a regular polygon with n sides instead of a circle" + `defaultTo` (-1) test "circle(r=10);" `eulerCharacteristic` 0 - - addObj2 $ if fn < 3 + addObj2 $ if sides < 3 then Prim.circle r else Prim.polygonR 0 $ - let - sides :: ℝ - sides = fromIntegral fn - in [(r*cos θ, r*sin θ )| θ <- [2*pi*n/sides | n <- [0.0 .. sides - 1.0]]] + [(r*cos θ, r*sin θ )| θ <- [2*pi*(fromℕtoℝ n)/(fromℕtoℝ sides) | n <- [0 .. sides - 1]]] polygon :: (String, [OVal] -> ArgParser (IO [OVal])) polygon = moduleWithoutSuite "polygon" $ do - example "polygon ([(0,0), (0,10), (10,0)]);" - points :: [ℝ2] <- argument "points" `doc` "vertices of the polygon" paths :: [ℕ] <- argument "paths" @@ -242,7 +215,6 @@ polygon = moduleWithoutSuite "polygon" $ do [] -> addObj2 $ Prim.polygonR r points _ -> return $ return [] - union :: (String, [OVal] -> ArgParser (IO [OVal])) union = moduleWithSuite "union" $ \children -> do r :: ℝ <- argument "r" @@ -272,10 +244,8 @@ difference = moduleWithSuite "difference" $ \children -> do translate :: (String, [OVal] -> ArgParser (IO [OVal])) translate = moduleWithSuite "translate" $ \children -> do - example "translate ([2,3]) circle (4);" example "translate ([5,6,7]) sphere(5);" - (x,y,z) <- do x :: ℝ <- argument "x" @@ -293,7 +263,6 @@ translate = moduleWithSuite "translate" $ \children -> do Left x -> (x,0,0) Right (Left (x,y) ) -> (x,y,0) Right (Right (x,y,z)) -> (x,y,z) - return $ return $ objMap (Prim.translate (x,y)) (Prim.translate (x,y,z)) children @@ -308,7 +277,6 @@ rotate = moduleWithSuite "rotate" $ \children -> do v <- argument "v" `defaultTo` (0, 0, 1) `doc` "Vector to rotate around if a is a single angle" - -- caseOType matches depending on whether size can be coerced into -- the right object. See Graphics.Implicit.ExtOpenScad.Util -- Entries must be joined with the operator <||> @@ -324,17 +292,14 @@ rotate = moduleWithSuite "rotate" $ \children -> do scale :: (String, [OVal] -> ArgParser (IO [OVal])) scale = moduleWithSuite "scale" $ \children -> do - example "scale(2) square(5);" example "scale([2,3]) square(5);" example "scale([2,3,4]) cube(5);" v <- argument "v" `doc` "vector or scalar to scale by" - let scaleObjs stretch2 stretch3 = objMap (Prim.scale stretch2) (Prim.scale stretch3) children - return $ return $ case v of Left x -> scaleObjs (x,1) (x,1,1) Right (Left (x,y)) -> scaleObjs (x,y) (x,y,1) @@ -343,7 +308,6 @@ scale = moduleWithSuite "scale" $ \children -> do extrude :: (String, [OVal] -> ArgParser (IO [OVal])) extrude = moduleWithSuite "linear_extrude" $ \children -> do example "linear_extrude(10) square(5);" - height :: Either ℝ (ℝ -> ℝ -> ℝ) <- argument "height" `defaultTo` Left 1 `doc` "height to extrude to..." center :: Bool <- argument "center" `defaultTo` False @@ -356,7 +320,6 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do `doc` "translate according to this funciton as we extrude..." r :: ℝ <- argument "r" `defaultTo` 0 `doc` "round the top?" - let heightn = case height of Left h -> h @@ -365,21 +328,17 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do height' = case height of Right f -> Right $ uncurry f Left a -> Left a - shiftAsNeeded :: SymbolicObj3 -> SymbolicObj3 shiftAsNeeded = if center then Prim.translate (0,0,-heightn/2.0) else id - funcify :: (VectorSpace a, Fractional (Scalar a)) => Either a (ℝ -> a) -> ℝ -> a funcify (Left val) h = realToFrac (h/heightn) *^ val funcify (Right f ) h = f h - twist' = fmap funcify twist scale' = fmap funcify scaleArg translate' = fmap funcify translateArg - return $ return $ obj2UpMap ( \obj -> case height of Left constHeight | isNothing twist && isNothing scaleArg && isNothing translateArg -> @@ -391,13 +350,11 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do rotateExtrude :: (String, [OVal] -> ArgParser (IO [OVal])) rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do example "rotate_extrude() translate(20) circle(10);" - totalRot :: ℝ <- argument "a" `defaultTo` 360 `doc` "angle to sweep" r :: ℝ <- argument "r" `defaultTo` 0 translateArg :: Either ℝ2 (ℝ -> ℝ2) <- argument "translate" `defaultTo` Left (0,0) rotateArg :: Either ℝ (ℝ -> ℝ ) <- argument "rotate" `defaultTo` Left 0 - let is360m :: RealFrac a => a -> Bool is360m n = 360 * fromInteger (round $ n / 360) /= n @@ -405,11 +362,8 @@ rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do || either ( /= (0,0)) (\f -> f 0 /= f totalRot) translateArg || either is360m (\f -> is360m (f 0 - f totalRot)) rotateArg capM = if cap then Just r else Nothing - return $ return $ obj2UpMap (Prim.rotateExtrude totalRot capM translateArg rotateArg) children - - {- rotateExtrudeStatement :: (String, [OVal] -> ArgParser (IO [OVal])) rotateExtrudeStatement = moduleWithSuite "rotate_extrude" $ \suite -> do @@ -426,21 +380,17 @@ shell :: (String, [OVal] -> ArgParser (IO [OVal])) shell = moduleWithSuite "shell" $ \children -> do w :: ℝ <- argument "w" `doc` "width of the shell..." - return $ return $ objMap (Prim.shell w) (Prim.shell w) children -- Not a permanent solution! Breaks if can't pack. pack :: (String, [OVal] -> ArgParser (IO [OVal])) pack = moduleWithSuite "pack" $ \children -> do - example "pack ([45,45], sep=2) { circle(10); circle(10); circle(10); circle(10); }" - -- arguments size :: ℝ2 <- argument "size" `doc` "size of 2D box to pack objects within" sep :: ℝ <- argument "sep" `doc` "mandetory space between objects" - -- The actual work... return $ let (obj2s, obj3s, others) = divideObjs children @@ -458,13 +408,10 @@ pack = moduleWithSuite "pack" $ \children -> do unit :: (String, [OVal] -> ArgParser (IO [OVal])) unit = moduleWithSuite "unit" $ \children -> do - example "unit(\"inch\") {..}" - -- arguments name :: String <- argument "unit" `doc` "the unit you wish to work in" - let mmRatio :: Fractional a => String -> Maybe a mmRatio "inch" = Just 25.4 @@ -482,7 +429,6 @@ unit = moduleWithSuite "unit" $ \children -> do mmRatio "um" = mmRatio "µm" mmRatio "nm" = Just 0.0000001 mmRatio _ = Nothing - -- The actual work... return $ case mmRatio name of Nothing -> do @@ -491,7 +437,6 @@ unit = moduleWithSuite "unit" $ \children -> do Just r -> return $ objMap (Prim.scale (r,r)) (Prim.scale (r,r,r)) children - --------------- (<|>) :: ArgParser a -> ArgParser a -> ArgParser a From 8d64bc858c58f963c960bf94dab106da05b77a2f Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 19:17:53 +0100 Subject: [PATCH 168/227] make types more specific. --- Graphics/Implicit/ExtOpenScad/Primitives.hs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 20e40a5..3f7dcc0 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -9,10 +9,13 @@ -- FIXME: why are these required? {-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-} +-- For the type arithmatic involved in calling VectorSpace. +{-# LANGUAGE TypeFamilies #-} + -- Export one set containing all of the primitive object's patern matches. module Graphics.Implicit.ExtOpenScad.Primitives (primitives) where -import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), Fractional, ($), return, either, id, (-), (==), (&&), (<), (*), cos, sin, pi, (/), (>), const, uncurry, realToFrac, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) +import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), ($), return, either, id, (-), (==), (&&), (<), (*), cos, sin, pi, (/), (>), const, uncurry, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, ℕ, SymbolicObj2, SymbolicObj3, fromℕtoℝ) @@ -30,7 +33,6 @@ import Data.Maybe (isNothing) import Control.Monad (mplus) import Data.VectorSpace (VectorSpace, Scalar, (*^)) -import GHC.Real (RealFrac) -- | The only thing exported here. basically, a list of functions, which accept OVal arguments and retrun an ArgParser ? primitives :: [(String, [OVal] -> ArgParser (IO [OVal]))] @@ -333,8 +335,8 @@ extrude = moduleWithSuite "linear_extrude" $ \children -> do if center then Prim.translate (0,0,-heightn/2.0) else id - funcify :: (VectorSpace a, Fractional (Scalar a)) => Either a (ℝ -> a) -> ℝ -> a - funcify (Left val) h = realToFrac (h/heightn) *^ val + funcify :: (VectorSpace a, s ~ (Scalar a), s ~ ℝ) => Either a (ℝ -> a) -> ℝ -> a + funcify (Left val) h = (h/heightn) *^ val funcify (Right f ) h = f h twist' = fmap funcify twist scale' = fmap funcify scaleArg @@ -356,7 +358,7 @@ rotateExtrude = moduleWithSuite "rotate_extrude" $ \children -> do translateArg :: Either ℝ2 (ℝ -> ℝ2) <- argument "translate" `defaultTo` Left (0,0) rotateArg :: Either ℝ (ℝ -> ℝ ) <- argument "rotate" `defaultTo` Left 0 let - is360m :: RealFrac a => a -> Bool + is360m :: ℝ -> Bool is360m n = 360 * fromInteger (round $ n / 360) /= n cap = is360m totalRot || either ( /= (0,0)) (\f -> f 0 /= f totalRot) translateArg @@ -413,7 +415,7 @@ unit = moduleWithSuite "unit" $ \children -> do name :: String <- argument "unit" `doc` "the unit you wish to work in" let - mmRatio :: Fractional a => String -> Maybe a + mmRatio :: String -> Maybe ℝ mmRatio "inch" = Just 25.4 mmRatio "in" = mmRatio "inch" mmRatio "foot" = Just 304.8 @@ -473,7 +475,7 @@ obj2UpMap obj2upmod (x:xs) = case x of a -> a : obj2UpMap obj2upmod xs obj2UpMap _ [] = [] -toInterval :: Fractional t => Bool -> t -> (t, t) +toInterval :: Bool -> ℝ -> (ℝ, ℝ) toInterval center h = if center then (-h/2, h/2) From 73780c1d76e907cf46df868cdb0e03cfc3db9bee Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 21:10:49 +0100 Subject: [PATCH 169/227] use type conversion wrapper --- Graphics/Implicit/ExtOpenScad/Util/OVal.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index 5134a11..8af19a2 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -9,9 +9,9 @@ module Graphics.Implicit.ExtOpenScad.Util.OVal(OTypeMirror, (<||>), fromOObj, toOObj, divideObjs, caseOType, oTypeStr, getErrors) where -import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left,Right), Char, String, (==), fromInteger, floor, ($), (.), map, error, (++), show, fromIntegral, head, flip, filter, not, return, head) +import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left,Right), Char, String, (==), fromInteger, floor, ($), (.), map, error, (++), show, head, flip, filter, not, return, head) -import Graphics.Implicit.Definitions(ℝ, ℕ, SymbolicObj2, SymbolicObj3) +import Graphics.Implicit.Definitions(ℝ, ℕ, SymbolicObj2, SymbolicObj3, fromℕtoℝ) import Graphics.Implicit.ExtOpenScad.Definitions (OVal(ONum, OBool, OString, OList, OFunc, OUndefined, OModule, OError, OObj2, OObj3)) @@ -45,7 +45,7 @@ instance OTypeMirror ℕ where fromOObj (ONum n) = if n == fromInteger (floor n) then Just (floor n) else Nothing fromOObj _ = Nothing {-# INLINABLE fromOObj #-} - toOObj = ONum . fromIntegral + toOObj = ONum . fromℕtoℝ instance OTypeMirror Bool where fromOObj (OBool b) = Just b From d3c8d77781baf3567211b274176df7442281c584 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 21:11:10 +0100 Subject: [PATCH 170/227] be more explicit about types. --- Graphics/Implicit/MathUtil.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index 7311e8b..53be028 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -26,8 +26,10 @@ distFromLineSeg p (a,b) = distance p closest where ab = b ^-^ a ap = p ^-^ a + d :: ℝ d = normalized ab ⋅ ap -- the closest point to p on the line segment. + closest :: ℝ2 closest | d < 0 = a | d > magnitude ab = b From db8d28f48df602c158c5abc2971c1855a67b9726 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 7 May 2019 21:11:40 +0100 Subject: [PATCH 171/227] seperate language pragmas. --- Graphics/Implicit/ObjectUtil/GetBox2.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 29df518..d162210 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -2,7 +2,12 @@ -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE -{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} +-- FIXME: Document what these are for. +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE UndecidableInstances #-} module Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) where From 473ab4817d6ac6e9ada853fc4b60b7e8d3ed7830 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 8 May 2019 07:51:09 +0100 Subject: [PATCH 172/227] let ghc infer types better when reading numbers. --- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index eee1aab..1c1b4e3 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -7,9 +7,6 @@ module Graphics.Implicit.ExtOpenScad.Parser.Expr(expr0) where import Prelude (Char, Maybe(Nothing, Just), fmap, ($), (.), (>>), return, Bool(True, False), read, (++), (*), (**), (/), id, foldl, map, foldl1, unzip, tail, zipWith3) --- the datatype representing the graininess of our world. -import Graphics.Implicit.Definitions (ℝ) - -- The parsec parsing library. import Text.ParserCombinators.Parsec (GenParser, string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try) @@ -32,14 +29,14 @@ literal = ("literal" ?:) $ a <- many1 digit _ <- char 'e' b <- many1 digit - return . LitE $ ONum ((read a * (10 ** read b)) :: ℝ) + return . LitE $ ONum $ read a * (10 ** read b) *<|> do a <- many1 digit _ <- char '.' b <- many digit _ <- char 'e' c <- many1 digit - return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** read c)) :: ℝ) + return . LitE $ ONum $ read (a ++ "." ++ b) * (10 ** read c) *<|> do a <- many1 digit _ <- char '.' @@ -47,7 +44,7 @@ literal = ("literal" ?:) $ _ <- char 'e' _ <- char '+' c <- many1 digit - return . LitE $ ONum ((read (a ++ "." ++ b) * (10 ** read c)) :: ℝ) + return . LitE $ ONum $ read (a ++ "." ++ b) * (10 ** read c) *<|> do a <- many1 digit _ <- char '.' @@ -55,21 +52,21 @@ literal = ("literal" ?:) $ _ <- char 'e' _ <- char '-' c <- many1 digit - return . LitE $ ONum ((read (a ++ "." ++ b) / (10 ** read c)) :: ℝ) + return . LitE $ ONum $ read (a ++ "." ++ b) / (10 ** read c) *<|> do a <- many1 digit _ <- char 'e' _ <- char '-' b <- many1 digit - return . LitE $ ONum ((read a / (10 ** read b)) :: ℝ) + return . LitE $ ONum $ read a / (10 ** read b) *<|> do a <- many1 digit _ <- char '.' b <- many digit - return . LitE $ ONum (read (a ++ "." ++ b) :: ℝ) + return . LitE $ ONum $ read (a ++ "." ++ b) *<|> do a <- many1 digit - return . LitE $ ONum (read a :: ℝ) + return . LitE $ ONum $ read a ) *<|> "string" ?: do _ <- string "\"" From 09a4b6953df21173ee41f4f22ae80cee202302ab Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 8 May 2019 08:48:38 +0100 Subject: [PATCH 173/227] allow ghc to inline variableSymb. --- Graphics/Implicit/ExtOpenScad/Parser/Util.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs index 50c8a54..5292820 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs @@ -76,6 +76,7 @@ tryMany = foldl1 (<|>) . map try variableSymb :: forall s u (m :: Type -> Type). Stream s m Char => ParsecT s u m String variableSymb = many1 (noneOf " ,|[]{}()+-*&^%#@!~`'\"\\/;:.,<>?=") "variable" +{-# INLINABLE variableSymb #-} patternMatcher :: GenParser Char st Pattern patternMatcher = From 5b3ce3ca2d57bad0a67aa3a9a8ea766ba6f918f7 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 06:43:37 +0100 Subject: [PATCH 174/227] reorder. --- Graphics/Implicit/ExtOpenScad/Primitives.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 3f7dcc0..6298e1c 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -449,12 +449,12 @@ moduleWithSuite name modArgMapper = (name, modArgMapper) moduleWithoutSuite :: t -> a -> (t, b -> a) moduleWithoutSuite name modArgMapper = (name, const modArgMapper) -addObj3 :: SymbolicObj3 -> ArgParser (IO [OVal]) -addObj3 x = return $ return [OObj3 x] - addObj2 :: SymbolicObj2 -> ArgParser (IO [OVal]) addObj2 x = return $ return [OObj2 x] +addObj3 :: SymbolicObj3 -> ArgParser (IO [OVal]) +addObj3 x = return $ return [OObj3 x] + objMap :: (SymbolicObj2 -> SymbolicObj2) -> (SymbolicObj3 -> SymbolicObj3) -> [OVal] -> [OVal] objMap obj2mod obj3mod (x:xs) = case x of OObj2 obj2 -> OObj2 (obj2mod obj2) : objMap obj2mod obj3mod xs From 890bf7c0d8023d81cc0d4626ef092206d3a61e9e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 06:53:57 +0100 Subject: [PATCH 175/227] use line and column type from parsec when parsing extopenscad. --- Graphics/Implicit/ExtOpenScad/Definitions.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Definitions.hs b/Graphics/Implicit/ExtOpenScad/Definitions.hs index 5b0c19f..73187ab 100644 --- a/Graphics/Implicit/ExtOpenScad/Definitions.hs +++ b/Graphics/Implicit/ExtOpenScad/Definitions.hs @@ -16,13 +16,16 @@ module Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch import Prelude(Eq, Show, String, Maybe, Bool(True, False), IO, (==), show, map, ($), (++), undefined, and, zipWith, foldl1) --- Resolution of the world, Integer operator, and symbolic languages for 2D and 3D objects. -import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℕ, SymbolicObj2, SymbolicObj3) +-- Resolution of the world, Integer type, and symbolic languages for 2D and 3D objects. +import Graphics.Implicit.Definitions (ℝ, ℕ, SymbolicObj2, SymbolicObj3) import Control.Applicative (Applicative, Alternative((<|>), empty), pure, (<*>)) import Control.Monad (Functor, Monad, fmap, (>>=), mzero, mplus, MonadPlus, liftM, ap, return, (>=>)) import Data.Map (Map) +-- for keeping track of the line and column number we are on in our extopenscad file. +import Text.ParserCombinators.Parsec (Line, Column) + ----------------------------------------------------------------- -- | Handles parsing arguments to modules data ArgParser a @@ -90,7 +93,7 @@ data Expr = Var Symbol deriving (Show, Eq) -- a statement, along with the line and column number it is found on. -data StatementI = StatementI Fastℕ Fastℕ (Statement StatementI) +data StatementI = StatementI Line Column (Statement StatementI) deriving (Show, Eq) data Statement st = Include String Bool From 565c34750d70d49ed67a5e08a9c6d110b604c032 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 07:05:53 +0100 Subject: [PATCH 176/227] use map to add clarity. --- Graphics/Implicit/ExtOpenScad/Default.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 6643ea0..5543160 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -220,17 +220,17 @@ defaultPolymorphicFunctions = ["Can't " ++ name ++ " objects of types " ++ oTypeStr a ++ " and " ++ oTypeStr b ++ "."] list_gen :: [ℝ] -> Maybe [ℝ] - list_gen [a,b] = Just [fromInteger (ceiling a).. fromInteger (floor b)] + list_gen [a, b] = Just $ map fromInteger $ [(ceiling a).. (floor b)] list_gen [a, b, c] = let nr = (c-a)/b n :: ℝ n = fromInteger (floor nr) in if nr - n > 0 - then Just - [fromInteger (ceiling a), fromInteger (ceiling (a+b)).. fromInteger (floor (c - b*(nr -n)))] - else Just - [fromInteger (ceiling a), fromInteger (ceiling (a+b)).. fromInteger (floor c)] + then Just $ map fromInteger $ + [(ceiling a), (ceiling (a+b)).. (floor (c - b*(nr -n)))] + else Just $ map fromInteger $ + [(ceiling a), (ceiling (a+b)).. (floor c)] list_gen _ = Nothing ternary :: forall t. Bool -> t -> t -> t From e2646f6ca29d8eb2fce4aa6af358b83750813c38 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 08:01:41 +0100 Subject: [PATCH 177/227] use types more accurately, move comments around, and make the include section shorter. --- Graphics/Implicit/Export/TextBuilderUtils.hs | 33 ++++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/Graphics/Implicit/Export/TextBuilderUtils.hs b/Graphics/Implicit/Export/TextBuilderUtils.hs index 87ff53f..1a6d0b8 100644 --- a/Graphics/Implicit/Export/TextBuilderUtils.hs +++ b/Graphics/Implicit/Export/TextBuilderUtils.hs @@ -6,35 +6,32 @@ -- Data.Text.Lazy builders with. module Graphics.Implicit.Export.TextBuilderUtils ( - -- Values from Data.Text.Lazy - Text, - pack, - -- Values from Data.Text.Lazy.Builder, as well as some special builders - Builder, + -- From Data.Text.Lazy + module DTL, + -- From Data.Text.Lazy.Builder + module DTLB, toLazyText, - fromLazyText, - buildInt, - buildℕ, - -- Serialize a float in full precision + -- some special case Builders. bf, - -- Serialize a float with four decimal places buildTruncFloat, + buildℕ, + buildInt, -- Values from Data.Monoid (<>), mconcat, mempty ) where -import Prelude (Maybe(Nothing, Just), ($)) +import Prelude (Maybe(Nothing, Just), Int, ($)) -import Graphics.Implicit.Definitions (Fastℕ, ℝ, ℕ) -import Data.Text.Lazy (Text, pack) +import Graphics.Implicit.Definitions (ℝ, ℕ) +import Data.Text.Lazy as DTL (Text, pack) -- We manually redefine this operator to avoid a dependency on base >= 4.5 -- This will become unnecessary later. import Data.Monoid (Monoid, mappend, mconcat, mempty) import Data.Text.Internal.Lazy (defaultChunkSize) -import Data.Text.Lazy.Builder (Builder, toLazyTextWith, fromLazyText) +import Data.Text.Lazy.Builder as DTLB (Builder, toLazyTextWith, fromLazyText) import Data.Text.Lazy.Builder.RealFloat (formatRealFloat, FPFormat(Exponent, Fixed)) import Data.Text.Lazy.Builder.Int (decimal) @@ -43,16 +40,18 @@ import Data.Text.Lazy.Builder.Int (decimal) toLazyText :: Builder -> Text toLazyText = toLazyTextWith defaultChunkSize -bf, buildTruncFloat :: ℝ -> Builder - +-- | Serialize a float in full precision +bf :: ℝ -> Builder bf = formatRealFloat Exponent Nothing +-- | Serialize a float with four decimal places +buildTruncFloat :: ℝ -> Builder buildTruncFloat = formatRealFloat Fixed $ Just 4 buildℕ :: ℕ -> Builder buildℕ = decimal -buildInt :: Fastℕ -> Builder +buildInt :: Int -> Builder buildInt = decimal -- This is directly copied from base 4.5.1.0 From 8477ea1c2797b8eb75470d4ca0d259141242cf51 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 20:38:37 +0100 Subject: [PATCH 178/227] cleanup comments. --- Graphics/Implicit/Definitions.hs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 4700cc4..976a4a6 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -169,22 +169,22 @@ type TriangleMesh = [Triangle] -- | A normed triangle mesh is a mesh of normed triangles. type NormedTriangleMesh = [NormedTriangle] --- | A 2D object +-- | A 2D object. type Obj2 = (ℝ2 -> ℝ) --- | A 3D object +-- | A 3D object. type Obj3 = (ℝ3 -> ℝ) --- | A 2D box +-- | A 2D box. type Box2 = (ℝ2, ℝ2) --- | A 3D box +-- | A 3D box. type Box3 = (ℝ3, ℝ3) --- | A Box for containing a 2D object +-- | A Box containing a 2D object. type Boxed2 a = (a, Box2) --- | A Box for containing a 3D object +-- | A Box containing a 3D object. type Boxed3 a = (a, Box3) -- | A Boxed 2D object @@ -194,7 +194,7 @@ type BoxedObj2 = Boxed2 Obj2 type BoxedObj3 = Boxed3 Obj3 -- | A symbolic 2D object format. --- We want to have a symbolic object so that we can +-- We want to have symbolic objects so that we can -- accelerate rendering & give ideal meshes for simple -- cases. data SymbolicObj2 = @@ -221,9 +221,9 @@ data SymbolicObj2 = -- | A symbolic 3D format! data SymbolicObj3 = -- Primitives - Rect3R ℝ ℝ3 ℝ3 - | Sphere ℝ - | Cylinder ℝ ℝ ℝ + Rect3R ℝ ℝ3 ℝ3 -- rounding, start, stop. + | Sphere ℝ -- radius + | Cylinder ℝ ℝ ℝ -- -- (Rounded) CSG | Complement3 SymbolicObj3 | UnionR3 ℝ [SymbolicObj3] @@ -243,7 +243,7 @@ data SymbolicObj3 = | ExtrudeR ℝ SymbolicObj2 ℝ | ExtrudeRotateR ℝ ℝ SymbolicObj2 ℝ | ExtrudeRM - ℝ -- rounding radius + ℝ -- rounding radius (ignored) (Maybe (ℝ -> ℝ)) -- twist (Maybe (ℝ -> ℝ)) -- scale (Maybe (ℝ -> ℝ2)) -- translate @@ -251,7 +251,7 @@ data SymbolicObj3 = (Either ℝ (ℝ2 -> ℝ)) -- height to extrude to | RotateExtrude ℝ -- Angle to sweep to - (Maybe ℝ) -- Loop or path (rounded corner) + (Maybe ℝ) -- Loop or path (rounded corner) (ignored) (Either ℝ2 (ℝ -> ℝ2)) -- translate function (Either ℝ (ℝ -> ℝ )) -- rotate function SymbolicObj2 -- object to extrude From e4d690bc1f96e76b1e4778a69bfad118ff735d52 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 20:44:54 +0100 Subject: [PATCH 179/227] clean up cachegrind output, and spacing changes. --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 30a4e23..e51fd9b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ # ImplicitCAD Makefile. Build and test Implicitcad. - ## Locations of binaries used when running tests, or generating the images to go along with our README.md. # The location of stl2ps, from stltools, available from https://github.com/rsmith-nl/stltools/tree/develop stl2ps=/disk4/faikvm.com/stltools/stltools/stl2ps.py @@ -69,6 +68,7 @@ clean: Setup rm -f $(TARGETS) rm -rf dist/build/Graphics rm -f dist/build/libHS* + rm -f Examples/example*.cachegrind.* # Clean up before making a release. distclean: clean Setup @@ -113,7 +113,6 @@ $(LIBTARGET): $(LIBFILES) dist/build/test-implicit/test-implicit: $(TESTFILES) Setup dist/setup-config $(LIBTARGET) $(LIBFILES) cabal build test-implicit - # Build a binary target with cabal. dist/build/%: programs/$$(word 2,$$(subst /, ,%)).hs Setup dist/setup-config $(LIBTARGET) $(LIBFILES) cabal build $(word 2,$(subst /, ,$*)) From 39bf037196a848ca88f6306179e2e3e1253330de Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 12:08:49 +0100 Subject: [PATCH 180/227] add kelvin cookshaw, for the parser work. --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 96402a9..8b8b1ea 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -20,6 +20,7 @@ chicagoduane -- Duane Johnson -- Duane.Johnson@gmail.com l29ah -- Sergey Alirzaev -- zl29ah@gmail.com firegurafiku -- Pavel Kretov -- firegurafiku@gmail.com gambogi -- Matthew Gambogi -- m@gambogi.com +cookshak -- Kelvin Cookshaw -- kelvin@cookshaw.com kpe -- ?? -- ?? Thanks as well, to raghuugare. Due to not being contactable, From 17cd48756595643df292fd47b4b8bc5352b01bee Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 12:28:59 +0100 Subject: [PATCH 181/227] add more tests, move some helper functions from Expr to Util, and add a negate that is separate from not. --- tests/ParserSpec/Expr.hs | 78 ++++++++++++++++++++++++++-------------- tests/ParserSpec/Util.hs | 39 ++++++++++++++++---- 2 files changed, 85 insertions(+), 32 deletions(-) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index 73f48bf..c7f53f2 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -5,34 +5,15 @@ module ParserSpec.Expr (exprSpec) where -- Be explicit about what we import. -import Prelude (String, Bool(True, False), ($), (<*)) +import Prelude (Bool(True, False), ($)) -- Hspec, for writing specs. -import Test.Hspec (describe, Expectation, Spec, it, shouldBe, pendingWith, specify) +import Test.Hspec (describe, Expectation, Spec, it, pendingWith, specify) -- parsed expression components. import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$))) --- the expression parser entry point. -import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) - -import ParserSpec.Util (fapp, num, bool, plus, minus, mult, modulo, power, divide, negate, and, or, gt, lt, ternary, append, index, parseWithLeftOver) - -import Data.Either (Either(Right)) - -import Text.ParserCombinators.Parsec (parse, eof) - --- An operator for expressions for "the left side should parse to the right side." -infixr 1 --> -(-->) :: String -> Expr -> Expectation -(-->) source expr = - parse (expr0 <* eof) "" source `shouldBe` Right expr - --- An operator for expressions for "the left side should parse to the right side, and some should be left over. -infixr 1 -->+ -(-->+) :: String -> (Expr, String) -> Expectation -(-->+) source (result, leftover) = - parseWithLeftOver expr0 source `shouldBe` Right (result, leftover) +import ParserSpec.Util ((-->), fapp, num, bool, stringLiteral, plus, minus, mult, modulo, power, divide, negate, and, or, not, gt, lt, ternary, append, index, parseWithLeftOver) ternaryIssue :: Expectation -> Expectation ternaryIssue _ = pendingWith "parser doesn't handle ternary operator correctly" @@ -43,9 +24,9 @@ negationIssue _ = pendingWith "parser doesn't handle negation operator correctly logicalSpec :: Spec logicalSpec = do describe "not" $ do - specify "single" $ "!foo" --> negate [Var "foo"] + specify "single" $ "!foo" --> not [Var "foo"] specify "multiple" $ - negationIssue $ "!!!foo" --> negate [negate [negate [Var "foo"]]] + negationIssue $ "!!!foo" --> not [not [not [Var "foo"]]] it "handles and/or" $ do "foo && bar" --> and [Var "foo", Var "bar"] "foo || bar" --> or [Var "foo", Var "bar"] @@ -64,6 +45,12 @@ literalSpec :: Spec literalSpec = do it "handles integers" $ "12356" --> num 12356 + it "handles positive leading zero integers" $ do + "000012356" --> num 12356 + it "handles zero integer" $ do + "0" --> num 0 + it "handles leading zero integer" $ do + "0000" --> num 0 it "handles floats" $ "23.42" --> num 23.42 describe "booleans" $ do @@ -82,17 +69,54 @@ exprSpec = do "( false )" --> bool False it "handles vectors" $ "[ 1, 2, 3 ]" --> ListE [num 1, num 2, num 3] + it "handles empty vectors" $ do + "[]" --> ListE [] + it "handles single element vectors" $ do + "[a]" --> ListE [Var "a"] + it "handles nested vectors" $ do + "[ 1, [2, 7], [3, 4, 5, 6] ]" --> ListE [num 1, ListE [num 2, num 7], ListE [num 3, num 4, num 5, num 6]] it "handles lists" $ "( 1, 2, 3 )" --> ListE [num 1, num 2, num 3] it "handles generators" $ - "[ a : 1 : b + 10 ]" --> - fapp "list_gen" [Var "a", num 1, plus [Var "b", num 10]] + "[ a : b ]" --> + fapp "list_gen" [Var "a", Var "b"] + it "handles generators with expression" $ + "[ a : b + 10 ]" --> + fapp "list_gen" [Var "a", plus [Var "b", num 10]] + it "handles increment generators" $ + "[ a : 3 : b + 10 ]" --> + fapp "list_gen" [Var "a", num 3, plus [Var "b", num 10]] it "handles indexing" $ "foo[23]" --> index [Var "foo", num 23] + it "handles multiple indexes" $ + "foo[23][12]" --> Var "index" :$ [Var "index" :$ [Var "foo", num 23], num 12] + it "handles single function call with single argument" $ + "foo(1)" --> Var "foo" :$ [num 1] + it "handles single function call with multiple arguments" $ + "foo(1, 2, 3)" --> Var "foo" :$ [num 1, num 2, num 3] + it "handles multiple function calls" $ + "foo(1)(2)(3)" --> ((Var "foo" :$ [num 1]) :$ [num 2]) :$ [num 3] + describe "arithmetic" $ do it "handles unary +/-" $ do "-42" --> num (-42) "+42" --> num 42 + it "handles unary - with extra spaces" $ do + "- 42" --> num (-42) + it "handles unary + with extra spaces" $ do + "+ 42" --> num 42 + it "handles unary - with parentheses" $ do + "-(4 - 3)" --> negate [ minus [num 4, num 3]] + it "handles unary + with parentheses" $ do + "+(4 - 1)" --> minus [num 4, num 1] + it "handles unary - with identifier" $ do + "-foo" --> negate [Var "foo"] + it "handles unary + with identifier" $ do + "+foo" --> Var "foo" + it "handles unary - with string literal" $ do + "-\"foo\"" --> negate [stringLiteral "foo"] + it "handles unary + with string literal" $ do + "+\"foo\"" --> stringLiteral "foo" it "handles +" $ do "1 + 2" --> plus [num 1, num 2] "1 + 2 + 3" --> plus [num 1, num 2, num 3] @@ -109,6 +133,8 @@ exprSpec = do num 6]] it "handles exponentiation" $ "x ^ y" --> power [Var "x", Var "y"] + it "handles multiple exponentiations" $ + "x ^ y ^ z" --> power [Var "x", power [Var "y", Var "z"]] it "handles *" $ do "3 * 4" --> mult [num 3, num 4] "3 * 4 * 5" --> mult [num 3, num 4, num 5] diff --git a/tests/ParserSpec/Util.hs b/tests/ParserSpec/Util.hs index 55b8c26..5666abb 100644 --- a/tests/ParserSpec/Util.hs +++ b/tests/ParserSpec/Util.hs @@ -8,8 +8,11 @@ -- Utilities module ParserSpec.Util - ( num + ( (-->) + , (-->+) + , num , bool + , stringLiteral , fapp , plus , minus @@ -17,11 +20,12 @@ module ParserSpec.Util , modulo , power , divide - , negate + , not , and , or , gt , lt + , negate , ternary , append , index @@ -29,18 +33,37 @@ module ParserSpec.Util ) where -- be explicit about where we get things from. -import Prelude (Bool, String, Either, (<), ($), (.), otherwise) +import Prelude (Bool, String, Either, (<), ($), (.), (<*), otherwise) -- The datatype of positions in our world. import Graphics.Implicit.Definitions (ℝ) -- The datatype of expressions, symbols, and values in the OpenScad language. -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE), OVal(ONum, OBool)) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE), OVal(ONum, OBool, OString)) import Text.ParserCombinators.Parsec (Parser, ParseError, parse, manyTill, anyChar, eof) import Control.Applicative ((<$>), (<*>)) +import Test.Hspec (Expectation, shouldBe) + +import Data.Either (Either(Right)) + +-- the expression parser entry point. +import Graphics.Implicit.ExtOpenScad.Parser.Expr (expr0) + +-- An operator for expressions for "the left side should parse to the right side." +infixr 1 --> +(-->) :: String -> Expr -> Expectation +(-->) source expr = + parse (expr0 <* eof) "" source `shouldBe` Right expr + +-- An operator for expressions for "the left side should parse to the right side, and some should be left over". +infixr 1 -->+ +(-->+) :: String -> (Expr, String) -> Expectation +(-->+) source (result, leftover) = + parseWithLeftOver expr0 source `shouldBe` Right (result, leftover) + num :: ℝ -> Expr num x -- FIXME: the parser should handle negative number literals @@ -51,17 +74,21 @@ num x bool :: Bool -> Expr bool = LitE . OBool -plus,minus,mult,modulo,power,divide,negate,and,or,gt,lt,ternary,append,index :: [Expr] -> Expr +stringLiteral :: String -> Expr +stringLiteral = LitE . OString + +plus,minus,mult,modulo,power,divide,negate,and,or,not,gt,lt,ternary,append,index :: [Expr] -> Expr minus = oapp "-" modulo = oapp "%" power = oapp "^" divide = oapp "/" and = oapp "&&" or = oapp "||" +not = oapp "!" gt = oapp ">" lt = oapp "<" ternary = oapp "?" -negate = oapp "!" +negate = oapp "negate" index = oapp "index" plus = fapp "+" mult = fapp "*" From afcee1b5944701799b346a520c7c6926b6608bc6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 12:51:12 +0100 Subject: [PATCH 182/227] add a release process, and add a list of the tools i use when developing. --- Release.md | 34 ++++++++++++++++++++++++++++++++++ Tools.md | 16 ++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 Release.md create mode 100644 Tools.md diff --git a/Release.md b/Release.md new file mode 100644 index 0000000..c872960 --- /dev/null +++ b/Release.md @@ -0,0 +1,34 @@ +# Release Processes: + +Purpose of this document: to make sure i follow a consistent patern, when making changes to ImplicitCAD. + +## "no point" releases: + +### Comment / Format / Messages + +These changes don't improve anything but the code quality, messages output, or build system. they can add features to the parser, but cannot remove them. they may not change the md5 of the generated STL files. + +1. make sure test-implicit is all green. +2. make sure parser-bench hasn't gone all out of control. +3. make sure docgen hasn't changed it's output too much. + +push to master. + +### Math / Types +These releases change the math engine, but only in a direction that is provably better, and shows in our examples. + +1. do all of the above. +2. check 'make examples' output. look at the times that valgrind measures. +3. check the md5sum of the .stl files output. + +If the md5sums of the last release and this one differ, run admesh on both, and examine the output. if the output is conclusively better for all changed examples, then proceed to push. + +push to master. + +## point releases: + +These releases change the quality of the output significantly enough that poking it with admesh is indeterminate, or they include changes to the parser such that old code would not work. + + + +## major releases: \ No newline at end of file diff --git a/Tools.md b/Tools.md new file mode 100644 index 0000000..a8a91fc --- /dev/null +++ b/Tools.md @@ -0,0 +1,16 @@ +# Purpose of this document: + +List the external tools i've found useful with this codebase. + +# Tools: + +## Workflow: + +My workflow consists of: + +### admesh + +### meshlab + +## Code Checking +'weeder' is useful. From 02fb2d8607f18ec1b0fb576c7cceac3afb86185c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 14:42:08 +0100 Subject: [PATCH 183/227] be explicit about numbers being the proper ImplicitCAD type, and comment things a bit more. --- tests/ParserSpec/Expr.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index c7f53f2..5ea186f 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -10,11 +10,18 @@ import Prelude (Bool(True, False), ($)) -- Hspec, for writing specs. import Test.Hspec (describe, Expectation, Spec, it, pendingWith, specify) --- parsed expression components. +-- Parsed expression components. import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$))) +-- The type used for variables, in ImplicitCAD. +import Graphics.Implicit.Definitions (ℝ) + +-- Our utility library, for making these tests easier to read. import ParserSpec.Util ((-->), fapp, num, bool, stringLiteral, plus, minus, mult, modulo, power, divide, negate, and, or, not, gt, lt, ternary, append, index, parseWithLeftOver) +-- Default all numbers in this file to being of the type ImplicitCAD uses for values. +default (ℝ) + ternaryIssue :: Expectation -> Expectation ternaryIssue _ = pendingWith "parser doesn't handle ternary operator correctly" From 0f04a409fa3245aa18fe8764b8a5ef9846b33d23 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 15:21:24 +0100 Subject: [PATCH 184/227] =?UTF-8?q?move=20type=20conversion=20from=20?= =?UTF-8?q?=E2=84=9D=20to=20Float=20into=20Definitions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Graphics/Implicit/Definitions.hs | 6 +++++- Graphics/Implicit/Export/Render.hs | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 82c792a..4fb769d 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -79,10 +79,11 @@ module Graphics.Implicit.Definitions ( fromℕtoℝ, fromFastℕtoℝ, fromFastℕ, + fromℝtoFloat ) where -import Prelude (Show, Double, Integer, Int, Either, show, (*), (/), fromIntegral) +import Prelude (Show, Double, Integer, Int, Either, show, (*), (/), fromIntegral, Float, realToFrac) import Data.Maybe (Maybe) @@ -132,6 +133,9 @@ fromFastℕtoℝ = fromIntegral fromFastℕ :: Fastℕ -> Int fromFastℕ a = a +fromℝtoFloat :: ℝ -> Float +fromℝtoFloat = realToFrac + -- add aditional instances to Show, for when we dump the intermediate form of an object. instance Show (ℝ -> ℝ) where show _ = "" diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 74a37e1..089d0c2 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -11,9 +11,9 @@ -- export getContour and getMesh, which returns the edge of a 2D object, or the surface of a 3D object, respectively. module Graphics.Implicit.Export.Render (getMesh, getContour) where -import Prelude(Float, Bool, ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, realToFrac, (==), (||), filter, not, reverse, (.), Eq, concatMap) +import Prelude(Float, Bool, ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, (==), (||), filter, not, reverse, (.), Eq, concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), both, allthree) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), both, allthree, fromℝtoFloat) import Data.VectorSpace ((^-^)) @@ -193,7 +193,7 @@ cleanupTris :: TriangleMesh -> TriangleMesh cleanupTris tris = let toFloat :: ℝ -> Float - toFloat = realToFrac + toFloat = fromℝtoFloat floatPoint :: (ℝ, ℝ, ℝ) -> (Float, Float, Float) floatPoint (a,b,c) = (toFloat a, toFloat b, toFloat c) isDegenerateTriFloat :: Eq t => (t,t,t) -> Bool From ad5b39e54795f3fe6d4ae5627c37a55ac19d9649 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 15:24:07 +0100 Subject: [PATCH 185/227] remove duplicate import of fromIntegral. --- Graphics/Implicit/Export/RayTrace.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index a892535..f71a4fe 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -7,7 +7,7 @@ module Graphics.Implicit.Export.RayTrace( dynamicImage, Color, average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) where -import Prelude(Show, RealFrac, Maybe(Just, Nothing), Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, fromIntegral, toRational, otherwise) +import Prelude(Show, RealFrac, Maybe(Just, Nothing), Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, toRational, otherwise) import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋅), Obj3) import Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) From bd3a4daad745100c0f7f8e6231d677e41a83d75e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 15:24:51 +0100 Subject: [PATCH 186/227] ignore cachegrind files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 95c9f6d..d5c61b2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ dist/ Setup docs/iscad.md .stack-work/ +Examples/*cachegrind* \ No newline at end of file From aded0809acb3e6b5c3f42fff90764f2a4f5dd5c4 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 22:58:13 +0100 Subject: [PATCH 187/227] add let() statements to the parser, and test suite. --- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 26 ++++++++++++++++++-- tests/ParserSpec/Expr.hs | 16 ++++++++++-- tests/ParserSpec/Statement.hs | 9 ++++--- tests/ParserSpec/Util.hs | 12 +++++++-- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 1c1b4e3..39d7a49 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -5,12 +5,12 @@ -- a parser for a numeric expression. module Graphics.Implicit.ExtOpenScad.Parser.Expr(expr0) where -import Prelude (Char, Maybe(Nothing, Just), fmap, ($), (.), (>>), return, Bool(True, False), read, (++), (*), (**), (/), id, foldl, map, foldl1, unzip, tail, zipWith3) +import Prelude (Char, Maybe(Nothing, Just), fmap, ($), (.), (>>), return, Bool(True, False), read, (++), (*), (**), (/), id, foldl, map, foldl1, unzip, tail, zipWith3, foldr) -- The parsec parsing library. import Text.ParserCombinators.Parsec (GenParser, string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try) -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, LitE, ListE, (:$)), OVal(ONum, OString, OBool, OUndefined), collector) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, LamE, LitE, ListE, (:$)), OVal(ONum, OString, OBool, OUndefined), collector, Pattern(Name)) import Graphics.Implicit.ExtOpenScad.Parser.Util (variableSymb, (?:), (*<|>), genSpace, padString) @@ -80,6 +80,27 @@ literal = ("literal" ?:) $ _ <- string "\"" return . LitE $ OString strlit +letExpr :: GenParser Char st Expr +letExpr = "let expression" ?: do + _ <- string "let" + _ <- genSpace + _ <- string "(" + _ <- genSpace + bindingPairs <- sepBy ( do + _ <- genSpace + boundName <- variableSymb + _ <- genSpace + _ <- string "=" + _ <- genSpace + boundExpr <- expr0 + return $ ListE [Var boundName, boundExpr]) + (char ',') + _ <- string ")" + expr <- expr0 + let bindLets (ListE [Var boundName, boundExpr]) nestedExpr = (LamE [Name boundName] nestedExpr) :$ [boundExpr] + bindLets _ e = e + return $ foldr bindLets expr bindingPairs + -- We represent the priority or 'fixity' of different types of expressions -- by the ExprIdx argument, with A0 as the highest. @@ -93,6 +114,7 @@ exprN :: ExprIdx -> GenParser Char st Expr exprN A12 = literal + *<|> letExpr *<|> variable *<|> "bracketed expression" ?: do -- eg. ( 1 + 5 ) diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index 5ea186f..031ee73 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -11,13 +11,13 @@ import Prelude (Bool(True, False), ($)) import Test.Hspec (describe, Expectation, Spec, it, pendingWith, specify) -- Parsed expression components. -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$))) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(Var, ListE, (:$)), Pattern(Name)) -- The type used for variables, in ImplicitCAD. import Graphics.Implicit.Definitions (ℝ) -- Our utility library, for making these tests easier to read. -import ParserSpec.Util ((-->), fapp, num, bool, stringLiteral, plus, minus, mult, modulo, power, divide, negate, and, or, not, gt, lt, ternary, append, index, parseWithLeftOver) +import ParserSpec.Util ((-->), fapp, num, bool, stringLiteral, plus, minus, mult, modulo, power, divide, negate, and, or, not, gt, lt, ternary, append, index, lambda, parseWithLeftOver) -- Default all numbers in this file to being of the type ImplicitCAD uses for values. default (ℝ) @@ -64,6 +64,17 @@ literalSpec = do it "accepts true" $ "true" --> bool True it "accepts false" $ "false" --> bool False +letBindingSpec :: Spec +letBindingSpec = do + it "handles let with integer binding and spaces" $ do + "let ( a = 1 ) a" --> lambda [Name "a"] (Var "a") [num 1] + it "handles multiple variable let" $ do + "let (a = x, b = y) a + b" --> lambda [Name "a"] ((lambda [Name "b"] (plus [Var "a", Var "b"])) [Var "y"]) [Var "x"] + it "handles empty let" $ do + "let () a" --> (Var "a") + it "handles nested let" $ do + "let(a=x) let(b = y) a + b" --> lambda [Name "a"] ((lambda [Name "b"] (plus [Var "a", Var "b"])) [Var "y"]) [Var "x"] + exprSpec :: Spec exprSpec = do describe "literals" literalSpec @@ -152,6 +163,7 @@ exprSpec = do it "handles append" $ "foo ++ bar ++ baz" --> append [Var "foo", Var "bar", Var "baz"] describe "logical operators" logicalSpec + describe "let expressions" letBindingSpec describe "application" $ do specify "base case" $ "foo(x)" --> Var "foo" :$ [Var "x"] specify "multiple arguments" $ diff --git a/tests/ParserSpec/Statement.hs b/tests/ParserSpec/Statement.hs index 2ae5057..526deea 100644 --- a/tests/ParserSpec/Statement.hs +++ b/tests/ParserSpec/Statement.hs @@ -9,9 +9,9 @@ import Prelude (String, Maybe(Just), Bool(True), ($)) import Test.Hspec (Spec, Expectation, shouldBe, shouldSatisfy, it, pendingWith, describe) -import ParserSpec.Util (bool, num, minus, mult, index) +import ParserSpec.Util (bool, num, minus, plus, mult, index) -import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) +import Graphics.Implicit.ExtOpenScad.Definitions (StatementI(StatementI), Symbol, Expr(ListE, LamE, Var, (:$)), Statement(NewModule, ModuleCall, If, (:=)), Pattern(Name, ListP)) -- Parse an ExtOpenScad program. import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) @@ -53,6 +53,9 @@ assignmentSpec = do "[x, y] = [1, 2];" --> single (ListP [Name "x", Name "y"] := ListE [num 1, num 2]) it "handles the function keyword and definitions" $ "function foo(x, y) = x * y;" --> single fooFunction + it "handles function with let expression" $ + "function withlet(b) = let (c = 5) b + c;" --> + (single $ (Name "withlet" := LamE [Name "b"] (LamE [Name "c"] (plus [Var "b", Var "c"]) :$ [num 5]))) it "handles nested indexing" $ "x = [y[0] - z * 2];" --> single ( Name "x" := ListE [minus [index [Var "y", num 0], @@ -92,5 +95,3 @@ statementSpec = do describe "empty module definition" $ it "parses correctly" $ "module foo_bar() {}" --> single (NewModule "foo_bar" [] []) - - diff --git a/tests/ParserSpec/Util.hs b/tests/ParserSpec/Util.hs index 5666abb..0241721 100644 --- a/tests/ParserSpec/Util.hs +++ b/tests/ParserSpec/Util.hs @@ -29,6 +29,7 @@ module ParserSpec.Util , ternary , append , index + , lambda , parseWithLeftOver ) where @@ -39,7 +40,7 @@ import Prelude (Bool, String, Either, (<), ($), (.), (<*), otherwise) import Graphics.Implicit.Definitions (ℝ) -- The datatype of expressions, symbols, and values in the OpenScad language. -import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE), OVal(ONum, OBool, OString)) +import Graphics.Implicit.ExtOpenScad.Definitions (Expr(LitE, (:$), Var, ListE, LamE), OVal(ONum, OBool, OString), Pattern) import Text.ParserCombinators.Parsec (Parser, ParseError, parse, manyTill, anyChar, eof) @@ -64,6 +65,8 @@ infixr 1 -->+ (-->+) source (result, leftover) = parseWithLeftOver expr0 source `shouldBe` Right (result, leftover) +-- | Types + num :: ℝ -> Expr num x -- FIXME: the parser should handle negative number literals @@ -77,6 +80,8 @@ bool = LitE . OBool stringLiteral :: String -> Expr stringLiteral = LitE . OString +-- | Operators + plus,minus,mult,modulo,power,divide,negate,and,or,not,gt,lt,ternary,append,index :: [Expr] -> Expr minus = oapp "-" modulo = oapp "%" @@ -94,11 +99,14 @@ plus = fapp "+" mult = fapp "*" append = fapp "++" --- we need two different kinds of application functions +-- | we need two different kinds of application functions oapp,fapp :: String -> [Expr] -> Expr oapp name args = Var name :$ args fapp name args = Var name :$ [ListE args] +lambda :: [Pattern] -> Expr -> [Expr] -> Expr +lambda params expr args = LamE params expr :$ args + parseWithLeftOver :: Parser a -> String -> Either ParseError (a, String) parseWithLeftOver p = parse ((,) <$> p <*> leftOver) "" where From a2af878038e91d73f3b7b143b2f463858310f787 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 13 May 2019 20:39:38 +0100 Subject: [PATCH 188/227] =?UTF-8?q?Use=20a=20newtype=20instead=20of=20a=20?= =?UTF-8?q?type=20for=20Fast=E2=84=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Graphics/Implicit/Definitions.hs | 15 +++----- Graphics/Implicit/FastIntUtil.hs | 61 ++++++++++++++++++++++++++++++++ implicit.cabal | 1 + 3 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 Graphics/Implicit/FastIntUtil.hs diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 4fb769d..8f6a6b5 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -18,9 +18,9 @@ module Graphics.Implicit.Definitions ( ℝ, ℝ2, ℝ3, + module F, minℝ, ℕ, - Fastℕ, both, allthree, (⋅), @@ -78,17 +78,18 @@ module Graphics.Implicit.Definitions ( Rectilinear3, fromℕtoℝ, fromFastℕtoℝ, - fromFastℕ, fromℝtoFloat ) where -import Prelude (Show, Double, Integer, Int, Either, show, (*), (/), fromIntegral, Float, realToFrac) +import Prelude (Show, Double, Integer, Either, show, (*), (/), fromIntegral, Float, realToFrac) import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) +import Graphics.Implicit.FastIntUtil as F (Fastℕ(Fastℕ), fromFastℕ, toFastℕ) + -- Let's make things a bit nicer. -- Following the math notation ℝ, ℝ², ℝ³... -- Supports changing Float to Double for more precision! @@ -107,9 +108,6 @@ minℝ = 0.0000000000000002 -- Arbitrary precision integers. type ℕ = Integer --- System integers. -type Fastℕ = Int - -- | apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) both f (x,y) = (f x, f y) @@ -128,10 +126,7 @@ fromℕtoℝ :: ℕ -> ℝ fromℕtoℝ = fromIntegral fromFastℕtoℝ :: Fastℕ -> ℝ -fromFastℕtoℝ = fromIntegral - -fromFastℕ :: Fastℕ -> Int -fromFastℕ a = a +fromFastℕtoℝ (Fastℕ a) = fromIntegral a fromℝtoFloat :: ℝ -> Float fromℝtoFloat = realToFrac diff --git a/Graphics/Implicit/FastIntUtil.hs b/Graphics/Implicit/FastIntUtil.hs new file mode 100644 index 0000000..4e564e0 --- /dev/null +++ b/Graphics/Implicit/FastIntUtil.hs @@ -0,0 +1,61 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright 2014-2019, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + +-- Allow us to derive FastN when declaring fastℕ. +{-# LANGUAGE DeriveAnyClass #-} + +{-# LANGUAGE StandaloneDeriving #-} + + +module Graphics.Implicit.FastIntUtil (Fastℕ(Fastℕ), toFastℕ, fromFastℕ) where + +import Prelude (Integral, Num, Eq, Ord, Enum, Real, Show, ($), Read, Int) + +import qualified Prelude as P ((+), (*), abs, negate, signum, fromInteger, succ, pred, toEnum, fromEnum, quot, rem, quotRem, toInteger, toRational) + +import GHC.Real (Ratio((:%))) + +class FastN n where + fromFastℕ :: Fastℕ -> n + toFastℕ :: n -> Fastℕ + +instance FastN Int where + fromFastℕ (Fastℕ a) = a + toFastℕ a = Fastℕ a + +instance FastN Fastℕ where + fromFastℕ (Fastℕ a) = Fastℕ a + toFastℕ a = a + +-- System integers, meant to go fast, and have no chance of wrapping 2^31. +newtype Fastℕ = Fastℕ Int + deriving (Show, Read, Eq, Ord) + +instance Real Fastℕ where + toRational (Fastℕ a) = P.toInteger a :% 1 + +fastℕBoth :: (Int, Int) -> (Fastℕ, Fastℕ) +fastℕBoth (a, b) = (Fastℕ a, Fastℕ b) + +instance Integral Fastℕ where + toInteger (Fastℕ a) = P.toInteger a + quotRem (Fastℕ a) (Fastℕ b) = fastℕBoth $ P.quotRem a b + quot (Fastℕ a) (Fastℕ b) = Fastℕ $ P.quot a b + rem (Fastℕ a) (Fastℕ b) = Fastℕ $ P.rem a b + + +instance Num Fastℕ where + (+) (Fastℕ a) (Fastℕ b) = Fastℕ $ a P.+ b + (*) (Fastℕ a) (Fastℕ b) = Fastℕ $ a P.* b + abs (Fastℕ a) = Fastℕ $ P.abs a + negate (Fastℕ a) = Fastℕ $ P.negate a + signum (Fastℕ a) = Fastℕ $ P.signum a + fromInteger a = Fastℕ $ P.fromInteger a + +instance Enum Fastℕ where + succ (Fastℕ x) = Fastℕ $ P.succ x + pred (Fastℕ x) = Fastℕ $ P.pred x + toEnum n = Fastℕ $ P.toEnum n + fromEnum (Fastℕ n) = n + diff --git a/implicit.cabal b/implicit.cabal index c97f879..2415a12 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -53,6 +53,7 @@ Library Graphics.Implicit.Definitions Graphics.Implicit.Primitives Graphics.Implicit.Export + Graphics.Implicit.FastIntUtil Graphics.Implicit.MathUtil Graphics.Implicit.ExtOpenScad Graphics.Implicit.ObjectUtil From 7390d2e305b569507f38ac877ce0819e4043e399 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 13 May 2019 22:45:04 +0100 Subject: [PATCH 189/227] remove unneeded language extensions. --- Graphics/Implicit/FastIntUtil.hs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Graphics/Implicit/FastIntUtil.hs b/Graphics/Implicit/FastIntUtil.hs index 4e564e0..cd7b024 100644 --- a/Graphics/Implicit/FastIntUtil.hs +++ b/Graphics/Implicit/FastIntUtil.hs @@ -2,12 +2,6 @@ -- Copyright 2014-2019, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --- Allow us to derive FastN when declaring fastℕ. -{-# LANGUAGE DeriveAnyClass #-} - -{-# LANGUAGE StandaloneDeriving #-} - - module Graphics.Implicit.FastIntUtil (Fastℕ(Fastℕ), toFastℕ, fromFastℕ) where import Prelude (Integral, Num, Eq, Ord, Enum, Real, Show, ($), Read, Int) From bfec00f08f8e08b2f8ff01d06ee0f070866e77c8 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 18 May 2019 15:21:22 +0100 Subject: [PATCH 190/227] fix types and compilation of ImplicitSNAP, and fix profiling support in the Makefile. --- Makefile | 10 ++++++---- implicit.cabal | 17 +++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index e51fd9b..a9278d0 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,8 @@ convert=convert GHC=ghc # The location of the created extopenscad binary, for running shell based test cases. EXTOPENSCAD=dist/build/extopenscad/extopenscad +# The location of the implicitsnap binary, which listens for requests via http. The backend of the website. +IMPLICITSNAP=dist/build/implicitsnap/implicitsnap # The location of the benchmark binary, for benchmarking some implicitcad internals. BENCHMARK=dist/build/Benchmark/Benchmark # The location of the parser benchmark binary, specifically for benchmarking implicitcad's parser. @@ -27,12 +29,12 @@ RTSOPTS=+RTS -N RESOPTS=-r 50 # Uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. -#PROFILING= --enable-library-profiling --enable-executable-profiling +#PROFILING= --enable-profiling LIBFILES=$(shell find Graphics -name '*.hs') LIBTARGET=dist/build/Graphics/Implicit.o -EXECTARGETS=$(EXTOPENSCAD) $(BENCHMARK) $(TESTSUITE) $(PARSERBENCH) $(DOCGEN) +EXECTARGETS=$(EXTOPENSCAD) $(IMPLICITSNAP) $(BENCHMARK) $(TESTSUITE) $(PARSERBENCH) $(DOCGEN) TARGETS=$(EXECTARGETS) $(LIBTARGET) # Mark the below fake targets as unreal, so make will not get choked up if a file with one of these names is created. @@ -93,7 +95,7 @@ dist: $(TARGETS) # Generate examples. examples: $(EXTOPENSCAD) - cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done + cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; $(GHC) $$filename.hs -o $$filename; $$filename; } done # Generate images from the examples, so we can upload the images to our website. @@ -120,7 +122,7 @@ dist/build/%: programs/$$(word 2,$$(subst /, ,%)).hs Setup dist/setup-config $(L # Prepare to build. dist/setup-config: Setup implicit.cabal cabal update - cabal install --only-dependencies --upgrade-dependencies + cabal install --only-dependencies --upgrade-dependencies $(PROFILING) cabal configure --enable-tests --enable-benchmarks $(PROFILING) # The setup command, used to perform administrative tasks (haddock, upload to hackage, clean, etc...). diff --git a/implicit.cabal b/implicit.cabal index 2415a12..29b4fba 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -40,7 +40,7 @@ Library Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -53,7 +53,6 @@ Library Graphics.Implicit.Definitions Graphics.Implicit.Primitives Graphics.Implicit.Export - Graphics.Implicit.FastIntUtil Graphics.Implicit.MathUtil Graphics.Implicit.ExtOpenScad Graphics.Implicit.ObjectUtil @@ -72,6 +71,8 @@ Library Graphics.Implicit.ExtOpenScad.Primitives Other-modules: + Graphics.Implicit.FastIntUtil + Graphics.Implicit.IntegralUtil Graphics.Implicit.ObjectUtil.GetBox2 Graphics.Implicit.ObjectUtil.GetBox3 Graphics.Implicit.ObjectUtil.GetImplicit2 @@ -119,7 +120,7 @@ Executable extopenscad -optc-O3 -threaded -rtsopts - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -134,7 +135,7 @@ Executable docgen base, implicit ghc-options: - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -160,7 +161,7 @@ Executable implicitsnap -rtsopts -O2 -optc-O3 - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -178,7 +179,7 @@ Executable Benchmark Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -198,7 +199,7 @@ Test-suite test-implicit Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -216,7 +217,7 @@ Benchmark parser-bench Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra From 25c4d02cee3f0f5f6218bf05caca2d69c320f1c6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 18 May 2019 15:26:23 +0100 Subject: [PATCH 191/227] re-enable dynamic library usage, and move fastIntUtil to not being exposed. --- implicit.cabal | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 29b4fba..1046167 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -40,7 +40,7 @@ Library Ghc-options: -O2 -optc-O3 --- -dynamic + -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -72,7 +72,6 @@ Library Other-modules: Graphics.Implicit.FastIntUtil - Graphics.Implicit.IntegralUtil Graphics.Implicit.ObjectUtil.GetBox2 Graphics.Implicit.ObjectUtil.GetBox3 Graphics.Implicit.ObjectUtil.GetImplicit2 @@ -120,7 +119,7 @@ Executable extopenscad -optc-O3 -threaded -rtsopts --- -dynamic + -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -135,7 +134,7 @@ Executable docgen base, implicit ghc-options: --- -dynamic + -dynamic -- for debugging. -Wall -Wextra @@ -161,7 +160,7 @@ Executable implicitsnap -rtsopts -O2 -optc-O3 --- -dynamic + -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -179,7 +178,7 @@ Executable Benchmark Ghc-options: -O2 -optc-O3 --- -dynamic + -dynamic -- for debugging. -Wall -Wextra @@ -199,7 +198,7 @@ Test-suite test-implicit Ghc-options: -O2 -optc-O3 --- -dynamic + -dynamic -- for debugging. -Wall -Wextra @@ -217,7 +216,7 @@ Benchmark parser-bench Ghc-options: -O2 -optc-O3 --- -dynamic + -dynamic -- for debugging. -Wall -Wextra From 779531501042c9abdb8e0a3644017d2c41a58265 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 18 May 2019 15:27:24 +0100 Subject: [PATCH 192/227] actually commit change to implicitsnap. --- programs/implicitsnap.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 50b78c1..3076acd 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -26,7 +26,7 @@ import Snap.Util.GZip (withCompression) -- Our Extended OpenScad interpreter, and the extrudeR function for making 2D objects 3D. import Graphics.Implicit (runOpenscad, extrudeR) -import Graphics.Implicit.ExtOpenScad.Definitions (OVal (ONum)) +import Graphics.Implicit.ExtOpenScad.Definitions (OVal (ONum), VarLookup) -- Functions for finding a box around an object, so we can define the area we need to raytrace inside of. import Graphics.Implicit.ObjectUtil (getBox2, getBox3) @@ -144,7 +144,7 @@ getRes (varlookup, obj2s, obj3s) = -- | get the maximum dimension of the object being rendered. -- FIXME: shouldn't this get the diagonal across the box? -getWidth :: (String, [SymbolicObj2], [SymbolicObj3]) -> ℝ +getWidth :: (VarLookup, [SymbolicObj2], [SymbolicObj3]) -> ℝ getWidth (_, _, obj:_) = maximum [x2-x1, y2-y1, z2-z1] where ((x1,y1,z1),(x2,y2,z2)) = getBox3 obj getWidth (_, obj:_, _) = max (x2-x1) (y2-y1) From 4994cd43c225db6323b536acc512a56609a14ce3 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 18 May 2019 21:36:27 +0100 Subject: [PATCH 193/227] make eeverything inlinable, and implement more members of the typeclasses. --- Graphics/Implicit/FastIntUtil.hs | 52 +++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/Graphics/Implicit/FastIntUtil.hs b/Graphics/Implicit/FastIntUtil.hs index cd7b024..554524d 100644 --- a/Graphics/Implicit/FastIntUtil.hs +++ b/Graphics/Implicit/FastIntUtil.hs @@ -4,9 +4,9 @@ module Graphics.Implicit.FastIntUtil (Fastℕ(Fastℕ), toFastℕ, fromFastℕ) where -import Prelude (Integral, Num, Eq, Ord, Enum, Real, Show, ($), Read, Int) +import Prelude (Integral(toInteger, quot, rem, quotRem, div, mod, divMod), Num((+), (*), abs, negate, signum, fromInteger), Eq, Ord, Enum(succ, pred, toEnum, fromEnum), Real(toRational), Show, ($), Read, Int) -import qualified Prelude as P ((+), (*), abs, negate, signum, fromInteger, succ, pred, toEnum, fromEnum, quot, rem, quotRem, toInteger, toRational) +import qualified Prelude as P ((+), (*), abs, negate, signum, fromInteger, succ, pred, toEnum, quotRem, divMod, toInteger) import GHC.Real (Ratio((:%))) @@ -16,11 +16,15 @@ class FastN n where instance FastN Int where fromFastℕ (Fastℕ a) = a + {-# INLINABLE fromFastℕ #-} toFastℕ a = Fastℕ a + {-# INLINABLE toFastℕ #-} instance FastN Fastℕ where fromFastℕ (Fastℕ a) = Fastℕ a + {-# INLINABLE fromFastℕ #-} toFastℕ a = a + {-# INLINABLE toFastℕ #-} -- System integers, meant to go fast, and have no chance of wrapping 2^31. newtype Fastℕ = Fastℕ Int @@ -28,28 +32,48 @@ newtype Fastℕ = Fastℕ Int instance Real Fastℕ where toRational (Fastℕ a) = P.toInteger a :% 1 + {-# INLINABLE toRational #-} fastℕBoth :: (Int, Int) -> (Fastℕ, Fastℕ) fastℕBoth (a, b) = (Fastℕ a, Fastℕ b) +{-# INLINABLE fastℕBoth #-} instance Integral Fastℕ where - toInteger (Fastℕ a) = P.toInteger a + toInteger (Fastℕ a) = P.toInteger a + {-# INLINABLE toInteger #-} + quot (Fastℕ n) (Fastℕ d) = Fastℕ $ q where (q,_) = quotRem n d + {-# INLINABLE quot #-} + rem (Fastℕ n) (Fastℕ d) = Fastℕ $ r where (_,r) = quotRem n d + {-# INLINABLE rem #-} quotRem (Fastℕ a) (Fastℕ b) = fastℕBoth $ P.quotRem a b - quot (Fastℕ a) (Fastℕ b) = Fastℕ $ P.quot a b - rem (Fastℕ a) (Fastℕ b) = Fastℕ $ P.rem a b - + {-# INLINABLE quotRem #-} + div (Fastℕ n) (Fastℕ d) = Fastℕ $ q where (q,_) = divMod n d + {-# INLINABLE div #-} + mod (Fastℕ n) (Fastℕ d) = Fastℕ $ r where (_,r) = divMod n d + {-# INLINABLE mod #-} + divMod (Fastℕ n) (Fastℕ d) = fastℕBoth $ P.divMod n d + {-# INLINABLE divMod #-} instance Num Fastℕ where (+) (Fastℕ a) (Fastℕ b) = Fastℕ $ a P.+ b + {-# INLINABLE (+) #-} (*) (Fastℕ a) (Fastℕ b) = Fastℕ $ a P.* b - abs (Fastℕ a) = Fastℕ $ P.abs a - negate (Fastℕ a) = Fastℕ $ P.negate a - signum (Fastℕ a) = Fastℕ $ P.signum a - fromInteger a = Fastℕ $ P.fromInteger a + {-# INLINABLE (*) #-} + abs (Fastℕ a) = Fastℕ $ P.abs a + {-# INLINABLE abs #-} + negate (Fastℕ a) = Fastℕ $ P.negate a + {-# INLINABLE negate #-} + signum (Fastℕ a) = Fastℕ $ P.signum a + {-# INLINABLE signum #-} + fromInteger a = Fastℕ $ P.fromInteger a + {-# INLINABLE fromInteger #-} instance Enum Fastℕ where - succ (Fastℕ x) = Fastℕ $ P.succ x - pred (Fastℕ x) = Fastℕ $ P.pred x - toEnum n = Fastℕ $ P.toEnum n + succ (Fastℕ x) = Fastℕ $ P.succ x + {-# INLINABLE succ #-} + pred (Fastℕ x) = Fastℕ $ P.pred x + {-# INLINABLE pred #-} + toEnum n = Fastℕ $ P.toEnum n + {-# INLINABLE toEnum #-} fromEnum (Fastℕ n) = n - + {-# INLINABLE fromEnum #-} From 63191c87f3706015bd2715c4fa07b3d114b1ece6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 18 May 2019 21:37:34 +0100 Subject: [PATCH 194/227] =?UTF-8?q?change=20our=20implementation=20of=20?= =?UTF-8?q?=E2=84=95=20to=20a=20newtype=20wrapper.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Graphics/Implicit/Definitions.hs | 14 ++--- Graphics/Implicit/IntegralUtil.hs | 97 +++++++++++++++++++++++++++++++ implicit.cabal | 1 + 3 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 Graphics/Implicit/IntegralUtil.hs diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 8f6a6b5..7825ed1 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -15,14 +15,14 @@ -- Definitions of the types used when modeling, and a few operators. module Graphics.Implicit.Definitions ( + module F, + module N, ℝ, ℝ2, - ℝ3, - module F, - minℝ, - ℕ, both, + ℝ3, allthree, + minℝ, (⋅), (⋯*), (⋯/), @@ -82,13 +82,14 @@ module Graphics.Implicit.Definitions ( ) where -import Prelude (Show, Double, Integer, Either, show, (*), (/), fromIntegral, Float, realToFrac) +import Prelude (Show, Double, Either, show, (*), (/), fromIntegral, Float, realToFrac) import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) import Graphics.Implicit.FastIntUtil as F (Fastℕ(Fastℕ), fromFastℕ, toFastℕ) +import Graphics.Implicit.IntegralUtil as N (ℕ, fromℕ, toℕ) -- Let's make things a bit nicer. -- Following the math notation ℝ, ℝ², ℝ³... @@ -105,9 +106,6 @@ minℝ :: ℝ -- for Doubles. minℝ = 0.0000000000000002 --- Arbitrary precision integers. -type ℕ = Integer - -- | apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) both f (x,y) = (f x, f y) diff --git a/Graphics/Implicit/IntegralUtil.hs b/Graphics/Implicit/IntegralUtil.hs new file mode 100644 index 0000000..aca9428 --- /dev/null +++ b/Graphics/Implicit/IntegralUtil.hs @@ -0,0 +1,97 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright 2014-2019, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + +-- Allow us to use explicit foralls when writing function type declarations. +{-# LANGUAGE ExplicitForAll #-} + +module Graphics.Implicit.IntegralUtil (ℕ, toℕ, fromℕ) where + +import Prelude (Integral(toInteger, quot, rem, quotRem, div, mod, divMod), Num((+), (*), abs, negate, signum, fromInteger), Eq, Ord, Enum(succ, pred, toEnum, fromEnum), Real(toRational), Show, ($), Read, fromIntegral, Int, Integer) + +import qualified Prelude as P ((+), (*), abs, negate, signum, succ, pred, toEnum, fromEnum, quotRem, divMod) + +import GHC.Real (Ratio((:%))) + +-- So we can produce an instance of Fastℕ for ℕ. +import Graphics.Implicit.FastIntUtil (Fastℕ(Fastℕ)) + +-- the N typeclass. only used to define the ℕ type. +class (Integral n) => N n where + fromℕ :: ℕ -> n + toℕ :: n -> ℕ + +instance N Integer where + fromℕ (ℕ a) = a + {-# INLINABLE fromℕ #-} + toℕ a = ℕ a + {-# INLINABLE toℕ #-} + +instance N Fastℕ where + fromℕ (ℕ a) = Fastℕ $ fromIntegral a + {-# INLINABLE fromℕ #-} + toℕ a = ℕ $ fromIntegral a + {-# INLINABLE toℕ #-} + +instance N Int where + fromℕ (ℕ a) = fromIntegral a + {-# INLINABLE fromℕ #-} + toℕ a = ℕ $ fromIntegral a + {-# INLINABLE toℕ #-} + +-- Arbitrary precision integers. To be used for anything countable, or in ratios. +newtype ℕ = ℕ Integer + deriving (Show, Read, Eq, Ord) + +instance Real ℕ where + toRational (ℕ a) = a :% 1 + {-# INLINABLE toRational #-} + +bothℕ :: (Integer, Integer) -> (ℕ, ℕ) +bothℕ (a, b) = (ℕ a , ℕ b) + +instance Integral ℕ where + toInteger (ℕ a) = a + {-# INLINABLE toInteger #-} + quot (ℕ n) (ℕ d) = ℕ $ q where (q,_) = quotRem n d + {-# INLINABLE quot #-} + rem (ℕ n) (ℕ d) = ℕ $ r where (_,r) = quotRem n d + {-# INLINABLE rem #-} + quotRem (ℕ a) (ℕ b) = bothℕ $ P.quotRem a b + {-# INLINABLE quotRem #-} + div (ℕ n) (ℕ d) = ℕ $ q where (q,_) = divMod n d + {-# INLINABLE div #-} + mod (ℕ n) (ℕ d) = ℕ $ r where (_,r) = divMod n d + {-# INLINABLE mod #-} + divMod (ℕ n) (ℕ d) = bothℕ $ P.divMod n d + {-# INLINABLE divMod #-} + +instance Num ℕ where + (+) (ℕ a) (ℕ b) = ℕ $ a P.+ b + {-# INLINABLE (+) #-} + (*) (ℕ a) (ℕ b) = ℕ $ a P.* b + {-# INLINABLE (*) #-} + abs (ℕ a) = ℕ $ P.abs a + {-# INLINABLE abs #-} + negate (ℕ a) = ℕ $ P.negate a + {-# INLINABLE negate #-} + signum (ℕ a) = ℕ $ P.signum a + {-# INLINABLE signum #-} + fromInteger a = ℕ a + {-# INLINABLE fromInteger #-} + +-- | Note that we do not implement all of the members of the typeclass here. +instance Enum ℕ where + succ (ℕ x) = ℕ $ P.succ x + {-# INLINABLE succ #-} + pred (ℕ x) = ℕ $ P.pred x + {-# INLINABLE pred #-} + toEnum n = ℕ $ P.toEnum n + {-# INLINABLE toEnum #-} + fromEnum (ℕ n) = P.fromEnum n + {-# INLINABLE fromEnum #-} + + + + + diff --git a/implicit.cabal b/implicit.cabal index 1046167..347a87c 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -72,6 +72,7 @@ Library Other-modules: Graphics.Implicit.FastIntUtil + Graphics.Implicit.IntegralUtil Graphics.Implicit.ObjectUtil.GetBox2 Graphics.Implicit.ObjectUtil.GetBox3 Graphics.Implicit.ObjectUtil.GetImplicit2 From e749ebcb75854504dec8e0939c0584099bba2d4d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 21:19:22 +0100 Subject: [PATCH 195/227] do not compile dynamically, for now. --- implicit.cabal | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index 347a87c..e76e3ed 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -40,7 +40,7 @@ Library Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -120,7 +120,7 @@ Executable extopenscad -optc-O3 -threaded -rtsopts - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -135,7 +135,7 @@ Executable docgen base, implicit ghc-options: - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -179,7 +179,7 @@ Executable Benchmark Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -199,7 +199,7 @@ Test-suite test-implicit Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -217,7 +217,7 @@ Benchmark parser-bench Ghc-options: -O2 -optc-O3 - -dynamic + -- -dynamic -- for debugging. -Wall -Wextra From 20eecede8b48f26965e002fdddead1107ad75bf8 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 22:29:43 +0100 Subject: [PATCH 196/227] correct precident error that was messing up meshes. --- Graphics/Implicit/MathUtil.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index 53be028..b48c1d1 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -27,7 +27,7 @@ distFromLineSeg p (a,b) = distance p closest ab = b ^-^ a ap = p ^-^ a d :: ℝ - d = normalized ab ⋅ ap + d = (normalized ab) ⋅ ap -- the closest point to p on the line segment. closest :: ℝ2 closest From 5e4f7604e9f68038129ca27ff207e3ede46eafb0 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 22:48:47 +0100 Subject: [PATCH 197/227] stop using valgrind by default when generating examples. --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a9278d0..57703e1 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ RESOPTS=-r 50 # Uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. #PROFILING= --enable-profiling +## FIXME: escape this right +# Uncomment for valgrind on the examples. +#VALGRIND=valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` + LIBFILES=$(shell find Graphics -name '*.hs') LIBTARGET=dist/build/Graphics/Implicit.o @@ -95,7 +99,7 @@ dist: $(TARGETS) # Generate examples. examples: $(EXTOPENSCAD) - cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { valgrind --tool=cachegrind --cachegrind-out-file=$$each.cachegrind.`date +%s` ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done + cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; $(GHC) $$filename.hs -o $$filename; $$filename; } done # Generate images from the examples, so we can upload the images to our website. From f2dc24b662a390235d5222cb869b39e8b6bdfe3e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 22:54:16 +0100 Subject: [PATCH 198/227] add example from issue #19. --- Examples/example15.scad | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Examples/example15.scad diff --git a/Examples/example15.scad b/Examples/example15.scad new file mode 100644 index 0000000..ebec2f7 --- /dev/null +++ b/Examples/example15.scad @@ -0,0 +1,4 @@ +difference() { +sphere(20); +cylinder(r=17, h=100, center = true); +} From eb6ec081750ab9fe6f854405cd2e754152a718ff Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Wed, 22 May 2019 07:43:03 +0100 Subject: [PATCH 199/227] build haskell examples against the in-tree code, rather than the installed libraries. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 57703e1..f02721c 100644 --- a/Makefile +++ b/Makefile @@ -100,7 +100,7 @@ dist: $(TARGETS) # Generate examples. examples: $(EXTOPENSCAD) cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done - cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; $(GHC) $$filename.hs -o $$filename; $$filename; } done + cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; cd ..; $(GHC) Examples/$$filename.hs -o Examples/$$filename; cd Examples; $$filename; } done # Generate images from the examples, so we can upload the images to our website. images: examples From 181bca09b0dce83a4e592785ca173e4b131ec1fd Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 24 May 2019 18:54:11 +0100 Subject: [PATCH 200/227] reorder, and better comments. --- Graphics/Implicit/Export/MarchingSquares.hs | 24 +++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index a771d78..462d294 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -154,10 +154,22 @@ getSquareLineSegs (x1, y1) (x2, y2) obj = else [[midx1, midy1], [midx2, midy2]] --- Functions for cleaning up the polylines +-- Functions for cleaning up the polylines. -- Many have multiple implementations as efficiency experiments. -- At some point, we'll get rid of the redundant ones.... +-- FIXME: document the algorithm this uses better. +orderLinesDC :: [[[Polyline]]] -> [Polyline] +orderLinesDC segs = + let + halve :: [a] -> ([a], [a]) + halve l = splitAt (div (length l) 2) l + splitOrder segs' = case (halve *** halve) $ unzip $ map halve $ segs' of + ((a,b),(c,d)) -> orderLinesDC a ++ orderLinesDC b ++ orderLinesDC c ++ orderLinesDC d + in + if length segs < 5 || length (head segs) < 5 then concat $ concat segs else + splitOrder segs + {- orderLines :: [Polyline] -> [Polyline] orderLines [] = [] @@ -173,16 +185,6 @@ orderLines (present:remaining) = (Just match, others) -> orderLines $ (present ++ tail match): others -} -orderLinesDC :: [[[Polyline]]] -> [Polyline] -orderLinesDC segs = - let - halve :: [a] -> ([a], [a]) - halve l = splitAt (div (length l) 2) l - splitOrder segs' = case (halve *** halve) $ unzip $ map halve $ segs' of - ((a,b),(c,d)) -> orderLinesDC a ++ orderLinesDC b ++ orderLinesDC c ++ orderLinesDC d - in - if length segs < 5 || length (head segs) < 5 then concat $ concat segs else - splitOrder segs {- orderLinesP :: [[[Polyline]]] -> [Polyline] orderLinesP segs = From cd21b0bd12b22702dbabdc4308ac26846d0de94d Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 24 May 2019 21:17:09 +0100 Subject: [PATCH 201/227] add another object to the benchmark suite, and fix the benchmark so it outputs files again. --- programs/Benchmark.hs | 47 +++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index 06a60bd..a62782d 100644 --- a/programs/Benchmark.hs +++ b/programs/Benchmark.hs @@ -2,17 +2,17 @@ -- Copyright (C) 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --- Benchmarks +-- Our benchmarking suite. -- Let's be explicit about where things come from :) -import Prelude (($), (*), (/), String, IO, cos, pi, map, zip3, Maybe(Just, Nothing), Either(Left), fromIntegral) +import Prelude (($), (*), (/), String, IO, cos, pi, map, zip3, Maybe(Just, Nothing), Either(Left), fromIntegral, (++)) -- Use criterion for benchmarking. see -import Criterion.Main (Benchmark, bgroup, bench, nf, defaultMain) +import Criterion.Main (Benchmark, bgroup, bench, nf, nfAppIO, defaultMain) -- The parts of ImplicitCAD we know how to benchmark (in theory). -import Graphics.Implicit (union, circle, SymbolicObj2, SymbolicObj3) +import Graphics.Implicit (union, circle, sphere, SymbolicObj2, SymbolicObj3, writeDXF2, writeSVG, writePNG2, writeSTL, writeBinSTL) import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) import Graphics.Implicit.Primitives (translate, difference, extrudeRM, rect3R) @@ -24,7 +24,7 @@ import Graphics.Implicit.Definitions (ℝ, Fastℕ) -- FIXME: move each of these objects into seperate compilable files. --- | A 2D object, for benchmarking. +-- | What we extrude in the example on the website. obj2d_1 :: SymbolicObj2 obj2d_1 = union @@ -35,7 +35,7 @@ obj2d_1 = , translate (0,-22) $ circle 10 ] --- | a 3D object, for benchmarking. extruded from our 2D object. +-- | An extruded version of obj2d_1, should be identical to the website's example, and example5.escad. object1 :: SymbolicObj3 object1 = extrudeRM 0 (Just twist) Nothing Nothing obj2d_1 (Left 40) where @@ -64,35 +64,42 @@ object3 = , rect3R 1 (0,0,0) (2,2,2) ] +-- | Example 13 - the rounded union of a cube and a sphere. +object4 :: SymbolicObj3 +object4 = union [ + rect3R 0 (0,0,0) (20,20,20), + translate (20,20,20) (sphere 15) ] + -- | Benchmark a 2D object. -obj2Benchmarks :: String -> SymbolicObj2 -> Benchmark -obj2Benchmarks name obj = +obj2Benchmarks :: String -> String -> SymbolicObj2 -> Benchmark +obj2Benchmarks name filename obj = bgroup name [ --- bench "SVG write" $ writeSVG 1 "benchmark.svg" obj --- , bench "PNG write" $ writePNG2 1 "benchmark.png" obj --- , - bench "Get contour" $ nf (symbolicGetContour 1) obj + bench "SVG write" $ nfAppIO (writeSVG 1 $ filename ++ ".svg") obj, + bench "PNG write" $ nfAppIO (writePNG2 1 $ filename ++ ".png") obj, + bench "DXF write" $ nfAppIO (writeDXF2 1 $ filename ++ ".dxf") obj, + bench "Get contour" $ nf (symbolicGetContour 1) obj ] -- | Benchmark a 3D object. -obj3Benchmarks :: String -> SymbolicObj3 -> Benchmark -obj3Benchmarks name obj = +obj3Benchmarks :: String -> String -> SymbolicObj3 -> Benchmark +obj3Benchmarks name filename obj = bgroup name [ -- bench "PNG write" $ writePNG3 1 "benchmark.png" obj --- , bench "STL write" $ writeSTL 1 "benchmark.stl" obj --- , + bench "STLTEXT write" $ nfAppIO (writeSTL 1 $ filename ++ ".stl.text") obj, + bench "STL write" $ nfAppIO (writeBinSTL 1 $ filename ++ ".stl") obj, bench "Get mesh" $ nf (symbolicGetMesh 1) obj ] -- | Benchmark all of our objects. benchmarks :: [Benchmark] benchmarks = - [ obj3Benchmarks "Object 1" object1 - , obj3Benchmarks "Object 2" object2 - , obj3Benchmarks "Object 3" object3 - , obj2Benchmarks "Object 2d 1" obj2d_1 + [ obj3Benchmarks "Object 1" "example5" object1 + , obj3Benchmarks "Object 2" "object2" object2 + , obj3Benchmarks "Object 3" "object3" object3 + , obj3Benchmarks "Object 4" "object4" object4 + , obj2Benchmarks "Object 2d 1" "example18" obj2d_1 ] -- | Our entrypoint. Runs all benchmarks. From e6f4d43b7af188c89c59f1c66ef650ead8b37620 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 24 May 2019 23:35:25 +0100 Subject: [PATCH 202/227] Fix ascii stl representations so that they use Float instead of Double. --- Graphics/Implicit/Export/TextBuilderUtils.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/Export/TextBuilderUtils.hs b/Graphics/Implicit/Export/TextBuilderUtils.hs index 1a6d0b8..e9a7728 100644 --- a/Graphics/Implicit/Export/TextBuilderUtils.hs +++ b/Graphics/Implicit/Export/TextBuilderUtils.hs @@ -24,7 +24,7 @@ module Graphics.Implicit.Export.TextBuilderUtils ( import Prelude (Maybe(Nothing, Just), Int, ($)) -import Graphics.Implicit.Definitions (ℝ, ℕ) +import Graphics.Implicit.Definitions (ℝ, ℕ, fromℝtoFloat) import Data.Text.Lazy as DTL (Text, pack) -- We manually redefine this operator to avoid a dependency on base >= 4.5 -- This will become unnecessary later. @@ -42,7 +42,7 @@ toLazyText = toLazyTextWith defaultChunkSize -- | Serialize a float in full precision bf :: ℝ -> Builder -bf = formatRealFloat Exponent Nothing +bf value = formatRealFloat Exponent Nothing $ (fromℝtoFloat value) -- | Serialize a float with four decimal places buildTruncFloat :: ℝ -> Builder From 93606a547d4901ad5246359e84b0ebf0b6a0cffe Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 00:52:09 +0100 Subject: [PATCH 203/227] make more readable. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index 4d9af36..e3c3120 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -30,6 +30,8 @@ import Codec.Picture (DynamicImage, generateImage, PixelRGBA8(PixelRGBA8)) import Data.VectorSpace ((^+^), (^/), (*^), (^-^)) import Data.AffineSpace ((.-^), (.+^)) +default (ℝ) + -- | There is a discrete way to aproximate this object. -- eg. Aproximating a 3D object with a triangle mesh -- would be DiscreteApproxable Obj3 TriangleMesh @@ -50,16 +52,16 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where obj = getImplicit3 symbObj box@((x1,y1,z1), (_,y2,z2)) = getBox3 symbObj av :: ℝ -> ℝ -> ℝ - av a b = (a+b)/(2::ℝ) + av a b = (a+b)/2 avY = av y1 y2 avZ = av z1 z2 deviation = maximum [abs $ y1 - avY, abs $ y2 - avY, abs $ z1 - avZ, abs $ z2 - avZ] - camera = Camera (x1-deviation*(2.2::ℝ), avY, avZ) (0, -1, 0) (0,0, -1) 1.0 - lights = [Light (x1-deviation*(1.5::ℝ), y1 - (0.4::ℝ)*(y2-y1), avZ) ((0.03::ℝ)*deviation) ] + camera = Camera (x1-deviation*(2.2), avY, avZ) (0, -1, 0) (0,0, -1) 1.0 + lights = [Light (x1-deviation*(1.5), y1 - (0.4)*(y2-y1), avZ) ((0.03)*deviation) ] scene = Scene obj (PixelRGBA8 200 200 230 255) lights (PixelRGBA8 255 255 255 0) pixelRenderer :: Int -> Int -> Color pixelRenderer a b = renderScreen - ((fromIntegral a :: ℝ)/w - (0.5::ℝ)) ((fromIntegral b :: ℝ)/h - (0.5 ::ℝ)) + ((fromIntegral a)/w - (0.5)) ((fromIntegral b)/h - (0.5)) renderScreen :: ℝ -> ℝ -> Color renderScreen a b = average [ From 537664830ec9b531519b7be3a7f7e49b49f3ebf1 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 00:52:39 +0100 Subject: [PATCH 204/227] use operator to make more readable. --- Graphics/Implicit/Export/TriangleMeshFormats.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 1590000..4bd1336 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -26,12 +26,12 @@ import Data.ByteString (replicate) import Data.ByteString.Lazy (ByteString) import Data.Storable.Endian (LittleEndian(LE)) -import Data.VectorSpace (normalized, negateV) +import Data.VectorSpace (normalized, (^-^)) import Data.Cross (cross3) normal :: (ℝ3,ℝ3,ℝ3) -> ℝ3 normal (a,b,c) = - normalized $ (b + negateV a) `cross3` (c + negateV a) + normalized $ (b ^-^ a) `cross3` (c ^-^ a) stl :: TriangleMesh -> Text stl triangles = toLazyText $ stlHeader <> mconcat (map triangle triangles) <> stlFooter From 31907712c336e5e58c88e1f9b79685a0a4bfde1e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 12:41:04 +0100 Subject: [PATCH 205/227] make easier to read by using default(). --- Graphics/Implicit/Export/RayTrace.hs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index f71a4fe..cc711ae 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -16,19 +16,22 @@ import Control.Arrow ((***)) import Data.VectorSpace (Scalar, magnitude, (^+^), (*^), normalized, (^-^), InnerSpace) import Data.Cross (cross3) +default (ℕ, ℝ) + -- Definitions data Camera = Camera ℝ3 ℝ3 ℝ3 ℝ deriving Show +-- | A ray. A point, and a normalized point in the direction the ray is going. data Ray = Ray ℝ3 ℝ3 deriving Show +data Scene = Scene Obj3 Color [Light] Color + data Light = Light ℝ3 ℝ deriving Show -data Scene = Scene Obj3 Color [Light] Color - type Color = PixelRGBA8 color :: Pixel8 -> Pixel8 -> Pixel8 -> Pixel8 -> PixelRGBA8 @@ -56,7 +59,8 @@ average l = ((rs, gs), (bs, as)) = (unzip *** unzip) . unzip $ map (\(PixelRGBA8 r g b a) -> ((fromIntegral r, fromIntegral g), (fromIntegral b, fromIntegral a))) l :: (([ℝ], [ℝ]), ([ℝ], [ℝ])) - n = fromIntegral $ length l :: ℝ + n :: ℝ + n = fromIntegral $ length l (r', g', b', a') = (sum rs/n, sum gs/n, sum bs/n, sum as/n) in PixelRGBA8 (fromInteger . round $ r') (fromInteger . round $ g') (fromInteger . round $ b') (fromInteger . round $ a') @@ -90,13 +94,12 @@ rayBounds ray box = -- Intersection - intersection :: Ray -> ((ℝ,ℝ), ℝ) -> ℝ -> Obj3 -> Maybe ℝ3 intersection r@(Ray p v) ((a, aval),b) res obj = let - step | aval/(4::ℝ) > res = res - | aval/(2::ℝ) > res = res/(2 :: ℝ) - | otherwise = res/(10 :: ℝ) + step | aval/4 > res = res + | aval/2 > res = res/2 + | otherwise = res/10 a' = a + step a'val = obj (p ^+^ a'*^v) in if a'val < 0 @@ -119,7 +122,7 @@ refine' :: ℕ -> ℝ2 -> ℝ2 -> (ℝ -> ℝ) -> ℝ refine' 0 (a, _) _ _ = a refine' n (a, b) (aval, bval) obj = let - mid = (a+b)/(2::ℝ) + mid = (a+b)/2 midval = obj mid in if midval == 0 From 54711219fc4646610aeb4a2f4325e7fdce88925b Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 12:41:42 +0100 Subject: [PATCH 206/227] add a hint what this is used for. --- Graphics/Implicit/Export/Symbolic/Rebound3.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Graphics/Implicit/Export/Symbolic/Rebound3.hs b/Graphics/Implicit/Export/Symbolic/Rebound3.hs index c5050bb..fc9e6ae 100644 --- a/Graphics/Implicit/Export/Symbolic/Rebound3.hs +++ b/Graphics/Implicit/Export/Symbolic/Rebound3.hs @@ -10,6 +10,9 @@ import Graphics.Implicit.Definitions(BoxedObj3, ℝ3) import Data.VectorSpace((^-^), (^+^), (^/)) +-- | Slightly stretch the bounding box of an object, in order to +-- ensure that during mesh generation, there are no problems because +-- values are right at the edge. rebound3 :: BoxedObj3 -> BoxedObj3 rebound3 (obj, (a,b)) = let From 7393978a136148676e6dfa98a393b44b911265d4 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 12:43:05 +0100 Subject: [PATCH 207/227] move comment about usage with the definition of rebound3. --- Graphics/Implicit/Export/SymbolicObj3.hs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicObj3.hs b/Graphics/Implicit/Export/SymbolicObj3.hs index 47ed179..5e5b5a0 100644 --- a/Graphics/Implicit/Export/SymbolicObj3.hs +++ b/Graphics/Implicit/Export/SymbolicObj3.hs @@ -218,11 +218,9 @@ symbolicGetMesh res inputObj@(UnionR3 r objs) = else concatMap (symbolicGetMesh res) independents ++ symbolicGetMesh res (UnionR3 r dependants) --- If all that fails, coerce and apply marching cubes :( --- (rebound is for being safe about the bounding box -- --- it slightly streches it to make sure nothing will --- have problems because it is right at the edge ) +-- | If all that fails, coerce and apply marching cubes :( symbolicGetMesh res obj = - case rebound3 (getImplicit3 obj, getBox3 obj) of - (obj', (a,b)) -> getMesh a b res obj' + -- Use rebound3 to stretch bounding box. + case rebound3 (getImplicit3 obj, getBox3 obj) of + (obj', (a,b)) -> getMesh a b res obj' From 3f66c2c5d182309bb8eb69977befc176f9c8d269 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 12:52:49 +0100 Subject: [PATCH 208/227] move degenerate triangle removal to be used by the functions that need it, and extend degenerate triangle removal. --- Graphics/Implicit/Export/Render.hs | 17 +----- .../Implicit/Export/TriangleMeshFormats.hs | 56 ++++++++++++++----- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 089d0c2..fcdae49 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -185,22 +185,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = in -- (5) merge squares, etc - cleanupTris . mergedSquareTris . concat . concat $ concat sqTris - --- Removes triangles that are empty, when converting their positions to Float resolution. --- NOTE: this will need to be disabled for AMF, and other triangle formats that can handle Double. -cleanupTris :: TriangleMesh -> TriangleMesh -cleanupTris tris = - let - toFloat :: ℝ -> Float - toFloat = fromℝtoFloat - floatPoint :: (ℝ, ℝ, ℝ) -> (Float, Float, Float) - floatPoint (a,b,c) = (toFloat a, toFloat b, toFloat c) - isDegenerateTriFloat :: Eq t => (t,t,t) -> Bool - isDegenerateTriFloat (a,b,c) = (a == b) || (b == c) || (a == c) - isDegenerateTri :: Triangle -> Bool - isDegenerateTri (a, b, c) = isDegenerateTriFloat (floatPoint a, floatPoint b, floatPoint c) - in filter (not . isDegenerateTri) tris + mergedSquareTris . concat . concat $ concat sqTris -- getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ -> Obj2 -> [Polyline] diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 4bd1336..d91473c 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -11,9 +11,9 @@ -- This module exposes three functions, which convert a triangle mesh to an output file. module Graphics.Implicit.Export.TriangleMeshFormats (stl, binaryStl, jsTHREE) where -import Prelude (Real, Float, ($), (+), map, (.), realToFrac, toEnum, length, zip, return) +import Prelude (Float, Eq, Bool, ($), (+), map, (.), toEnum, length, zip, return, (==), (||), (&&), filter, not) -import Graphics.Implicit.Definitions (TriangleMesh, ℕ, ℝ3) +import Graphics.Implicit.Definitions (Triangle, TriangleMesh, ℕ, ℝ3, ℝ, fromℝtoFloat) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildℕ) import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) @@ -33,8 +33,37 @@ normal :: (ℝ3,ℝ3,ℝ3) -> ℝ3 normal (a,b,c) = normalized $ (b ^-^ a) `cross3` (c ^-^ a) +-- | Removes triangles that are empty when converting their positions to Float resolution. +cleanupTris :: TriangleMesh -> TriangleMesh +cleanupTris tris = + let + floatPoint :: (ℝ, ℝ, ℝ) -> (Float, Float, Float) + floatPoint (a,b,c) = (toFloat a, toFloat b, toFloat c) + + -- | Does this triangle fail because it is constrained on two axises? + isDegenerateTri2Axis :: forall a. (Eq a) => ((a, a, a),(a, a, a),(a, a, a)) -> Bool + isDegenerateTri2Axis tri = ((ysame tri) && (xsame tri)) || ((zsame tri) && (ysame tri)) || ((zsame tri) && (xsame tri)) + where + same :: forall a. Eq a => (a, a, a) -> Bool + same (n1, n2, n3) = n1 == n2 && n2 == n3 + xsame :: forall a. Eq a => ((a, a, a), (a, a, a), (a, a, a)) -> Bool + xsame ((x1,_,_),(x2,_,_),(x3,_,_)) = same (x1, x2, x3) + ysame :: forall a. Eq a => ((a, a, a), (a, a, a), (a, a, a)) -> Bool + ysame ((_,y1,_),(_,y2,_),(_,y3,_)) = same (y1, y2, y3) + zsame :: forall a. Eq a => ((a, a, a), (a, a, a), (a, a, a)) -> Bool + zsame ((_,_,z1),(_,_,z2),(_,_,z3)) = same (z1, z2, z3) + -- | Does this triangle fail because of two of it's points overlap, after conversion to float? + isDegenerateTriPoint :: Eq t => (t,t,t) -> Bool + isDegenerateTriPoint (a,b,c) = (a == b) || (b == c) || (a == c) + isDegenerateTri :: Triangle -> Bool + isDegenerateTri (a, b, c) = (isDegenerateTriPoint $ floatTri) || (isDegenerateTri2Axis $ floatTri) + where + floatTri = (floatPoint a, floatPoint b, floatPoint c) + in filter (not . isDegenerateTri) tris + +-- | Generate an STL file is ASCII format. stl :: TriangleMesh -> Text -stl triangles = toLazyText $ stlHeader <> mconcat (map triangle triangles) <> stlFooter +stl triangles = toLazyText $ stlHeader <> mconcat (map triangle $ cleanupTris triangles) <> stlFooter where stlHeader :: Builder stlHeader = "solid ImplictCADExport\n" @@ -53,22 +82,21 @@ stl triangles = toLazyText $ stlHeader <> mconcat (map triangle triangles) <> st <> vertex c <> "\nendloop\nendfacet\n" +-- | convert from ℝ to Float. +toFloat :: ℝ -> Float +toFloat = fromℝtoFloat --- Write a 32-bit little-endian float to a buffer. - --- convert Floats and Doubles to Float. -toFloat :: Real a => a -> Float -toFloat = realToFrac :: (Real a) => a -> Float - +-- | Write a 32-bit little-endian float to a buffer. float32LE :: Float -> Write float32LE = writeStorable . LE +-- | Generate an STL file in it's binary format. binaryStl :: TriangleMesh -> ByteString -binaryStl triangles = toLazyByteString $ header <> lengthField <> mconcat (map triangle triangles) +binaryStl triangles = toLazyByteString $ header <> lengthField <> mconcat (map triangle $ cleanupTris triangles) where header = fromByteString $ replicate 80 0 - lengthField = fromWord32le $ toEnum $ length triangles + lengthField = fromWord32le $ toEnum $ length $ cleanupTris triangles triangle (a,b,c) = normalV (a,b,c) <> point a <> point b <> point c <> fromWord16le 0 - point :: forall a a1 a2. (Real a2, Real a1, Real a) => (a, a1, a2) -> BI.Builder + point :: (ℝ3) -> BI.Builder point (x,y,z) = fromWrite $ float32LE (toFloat x) <> float32LE (toFloat y) <> float32LE (toFloat z) normalV ps = let (x,y,z) = normal ps in fromWrite $ float32LE (toFloat x) <> float32LE (toFloat y) <> float32LE (toFloat z) @@ -102,12 +130,12 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer verts = do -- extract the vertices for each triangle -- recall that a normed triangle is of the form ((vert, norm), ...) - (a,b,c) <- triangles + (a,b,c) <- cleanupTris triangles -- The vertices from each triangle take up 3 position in the resulting list [a,b,c] vertcode = mconcat $ map v verts facecode = mconcat $ do - (n,_) <- zip [0, 3 ..] triangles + (n,_) <- zip [0, 3 ..] $ cleanupTris triangles let (posa, posb, posc) = (n, n+1, n+2) :: (ℕ, ℕ, ℕ) return $ f posa posb posc From 43411604eb4e99545bb2033c9e0f249596df0581 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 13:06:01 +0100 Subject: [PATCH 209/227] use the parallel garbage collector when building Examples, and use ghc to capture performance statistics --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f02721c..192b598 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,9 @@ DOCGEN=dist/build/docgen/docgen ## Options used when calling ImplicitCAD. for testing, and for image generation. # Enable multiple CPU usage. -RTSOPTS=+RTS -N +# Use the parallel garbage collector. +# spit out some performance statistics. +RTSOPTS=+RTS -N -qg -t # The resolution to generate objects at. FIXME: what does this mean in human terms? RESOPTS=-r 50 From 9b86eaaf4cb7d97218deac6e73522214e238fa39 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 19:25:59 +0100 Subject: [PATCH 210/227] use an integer for iteration. --- Graphics/Implicit/Export/SymbolicFormats.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index e619e41..49ab3bc 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -8,14 +8,15 @@ -- output SCAD code, AKA an implicitcad to openscad converter. module Graphics.Implicit.Export.SymbolicFormats (scad2, scad3) where -import Prelude(Maybe(Just, Nothing), Either(Left), ($), (.), (*), map, ($!), (-), (/), pi, error, (+), init, (==)) +import Prelude(Maybe(Just, Nothing), Either(Left), ($), (.), (*), map, ($!), (-), (/), pi, error, (+), (==), take, floor) import Graphics.Implicit.Definitions(ℝ, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Outset2, Shell2, EmbedBoxedObj2), SymbolicObj3(Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Outset3, Shell3, ExtrudeR, ExtrudeRotateR, ExtrudeRM, EmbedBoxedObj3, RotateExtrude, ExtrudeOnEdgeOf)) import Graphics.Implicit.Export.TextBuilderUtils(Text, Builder, toLazyText, (<>), mconcat, fromLazyText, bf) import Control.Monad.Reader (Reader, runReader, return, fmap, sequence, ask) -import Data.List (intersperse) +import Data.List (intersperse, (++)) +import Data.Function (fix) scad2 :: ℝ -> SymbolicObj2 -> Text scad2 res obj = toLazyText $ runReader (buildS2 obj) res @@ -27,8 +28,7 @@ scad3 res obj = toLazyText $ runReader (buildS3 obj) res rad2deg :: ℝ -> ℝ rad2deg r = r * (180/pi) --- Format an openscad call given that all the modified objects are in the Reader monad... - +-- | Format an openscad call given that all the modified objects are in the Reader monad... callToken :: (Text, Text) -> Builder -> [Builder] -> [Reader a Builder] -> Reader a Builder callToken cs name args [] = return $ name <> buildArgs cs args <> ";" callToken cs name args [obj] = fmap ((name <> buildArgs cs args) <>) obj @@ -46,7 +46,7 @@ call = callToken ("[", "]") callNaked :: Builder -> [Builder] -> [Reader a Builder] -> Reader a Builder callNaked = callToken ("", "") --- First, the 3D objects. +-- | First, the 3D objects. buildS3 :: SymbolicObj3 -> Reader ℝ Builder buildS3 (Rect3R r (x1,y1,z1) (x2,y2,z2)) | r == 0 = call "translate" [bf x1, bf y1, bf z1] [ @@ -94,7 +94,7 @@ buildS3 (ExtrudeRM r (Just twist) Nothing Nothing obj (Left height)) | r == 0 = callNaked "linear_extrude" ["height = " <> bf res, "twist = " <> bf (twist (h+res) - twist h)][ buildS2 obj ] - ] | h <- init [0, res .. height] + ] | h <- take (floor (res / height)) $ fix (\f x -> [x] ++ f (x+res)) (0) ] -- FIXME: where are RotateExtrude, ExtrudeOnEdgeOf? From 9f38c0e133154c47428bed2c8ea8bc7be0e5c0db Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 19:32:38 +0100 Subject: [PATCH 211/227] formatting, and reorder. --- implicit.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index e76e3ed..0cedba2 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -44,9 +44,9 @@ Library -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. + -Wall -Wextra -Weverything - -Wall Exposed-modules: Graphics.Implicit @@ -217,7 +217,7 @@ Benchmark parser-bench Ghc-options: -O2 -optc-O3 - -- -dynamic +-- -dynamic -- for debugging. -Wall -Wextra From 2358c9a12435bf5dbf40ee785f83d944b6f3e923 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 22:12:05 +0100 Subject: [PATCH 212/227] minor speed boost. --- .../Implicit/Export/TriangleMeshFormats.hs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index d91473c..94d00f6 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -40,6 +40,22 @@ cleanupTris tris = floatPoint :: (ℝ, ℝ, ℝ) -> (Float, Float, Float) floatPoint (a,b,c) = (toFloat a, toFloat b, toFloat c) +{- + + -- Alternate methods of detecting degenerate triangles -- not used. + -- If you have to use one of these, please tell the maintainer. + + -- | Does this triangle fail because it's points are on the same line? + isDegenerateTriLine (p1,p2,p3) = (norm (p1,p2)) == (norm (p2,p3)) || (norm (p1,p3)) == (norm(p2,p3)) + where + norm :: ((Float,Float,Float),(Float,Float,Float)) -> (Float,Float,Float) + norm (begin, end) = normalized $ begin ^-^ end + -- | Does this triangle fail because of two of it's points overlap, after conversion to float? + isDegenerateTriPoint :: Eq t => (t,t,t) -> Bool + isDegenerateTriPoint (a,b,c) = (a == b) || (b == c) || (a == c) + +-} + -- | Does this triangle fail because it is constrained on two axises? isDegenerateTri2Axis :: forall a. (Eq a) => ((a, a, a),(a, a, a),(a, a, a)) -> Bool isDegenerateTri2Axis tri = ((ysame tri) && (xsame tri)) || ((zsame tri) && (ysame tri)) || ((zsame tri) && (xsame tri)) @@ -52,11 +68,8 @@ cleanupTris tris = ysame ((_,y1,_),(_,y2,_),(_,y3,_)) = same (y1, y2, y3) zsame :: forall a. Eq a => ((a, a, a), (a, a, a), (a, a, a)) -> Bool zsame ((_,_,z1),(_,_,z2),(_,_,z3)) = same (z1, z2, z3) - -- | Does this triangle fail because of two of it's points overlap, after conversion to float? - isDegenerateTriPoint :: Eq t => (t,t,t) -> Bool - isDegenerateTriPoint (a,b,c) = (a == b) || (b == c) || (a == c) isDegenerateTri :: Triangle -> Bool - isDegenerateTri (a, b, c) = (isDegenerateTriPoint $ floatTri) || (isDegenerateTri2Axis $ floatTri) + isDegenerateTri (a, b, c) = (isDegenerateTri2Axis $ floatTri) -- || (isDegenerateTriLine $ floatTri) || (isDegenerateTriPoint $ floatTri) where floatTri = (floatPoint a, floatPoint b, floatPoint c) in filter (not . isDegenerateTri) tris From c3805cafc04b817dc6a6cef13196055e6c540488 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 22:26:44 +0100 Subject: [PATCH 213/227] minor code deduplication, and comment clarification. --- Graphics/Implicit/Export/TriangleMeshFormats.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 94d00f6..130ddd1 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -50,7 +50,7 @@ cleanupTris tris = where norm :: ((Float,Float,Float),(Float,Float,Float)) -> (Float,Float,Float) norm (begin, end) = normalized $ begin ^-^ end - -- | Does this triangle fail because of two of it's points overlap, after conversion to float? + -- | Does this triangle fail because of two of it's points overlap? isDegenerateTriPoint :: Eq t => (t,t,t) -> Bool isDegenerateTriPoint (a,b,c) = (a == b) || (b == c) || (a == c) @@ -111,8 +111,7 @@ binaryStl triangles = toLazyByteString $ header <> lengthField <> mconcat (map t triangle (a,b,c) = normalV (a,b,c) <> point a <> point b <> point c <> fromWord16le 0 point :: (ℝ3) -> BI.Builder point (x,y,z) = fromWrite $ float32LE (toFloat x) <> float32LE (toFloat y) <> float32LE (toFloat z) - normalV ps = let (x,y,z) = normal ps - in fromWrite $ float32LE (toFloat x) <> float32LE (toFloat y) <> float32LE (toFloat z) + normalV ps = point $ normal ps jsTHREE :: TriangleMesh -> Text jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer From 910f649a835a2a29164e2e52f330e9783a6acfa9 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 26 May 2019 09:12:20 +0100 Subject: [PATCH 214/227] mark common small functions as inlinable. --- Graphics/Implicit/Definitions.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 7825ed1..069a388 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -109,25 +109,32 @@ minℝ = 0.0000000000000002 -- | apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) both f (x,y) = (f x, f y) +{-# INLINABLE both #-} -- | apply a function to all three items in the provided tuple. allthree :: forall t b. (t -> b) -> (t, t, t) -> (b, b, b) allthree f (x,y,z) = (f x, f y, f z) +{-# INLINABLE allthree #-} --- TODO: Find a better place for this +-- | TODO: Find a better place for this (⋅) :: InnerSpace a => a -> a -> Scalar a (⋅) = (<.>) +{-# INLINABLE (⋅) #-} -- Wrap the functions that convert datatypes. +-- | Convert from our Integral to our Rational. fromℕtoℝ :: ℕ -> ℝ fromℕtoℝ = fromIntegral +{-# INLINABLE fromℕtoℝ #-} fromFastℕtoℝ :: Fastℕ -> ℝ fromFastℕtoℝ (Fastℕ a) = fromIntegral a +{-# INLINABLE fromFastℕtoℝ #-} fromℝtoFloat :: ℝ -> Float fromℝtoFloat = realToFrac +{-# INLINABLE fromℝtoFloat #-} -- add aditional instances to Show, for when we dump the intermediate form of an object. instance Show (ℝ -> ℝ) where From f1ca17caeec333fb8a0d2a36a8b0856ec5a22832 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 26 May 2019 14:42:46 +0100 Subject: [PATCH 215/227] remove unused types. --- Graphics/Implicit/Definitions.hs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 069a388..f611dfe 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -74,8 +74,6 @@ module Graphics.Implicit.Definitions ( ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude), - Rectilinear2, - Rectilinear3, fromℕtoℝ, fromFastℕtoℝ, fromℝtoFloat @@ -275,9 +273,4 @@ data SymbolicObj3 = | ExtrudeOnEdgeOf SymbolicObj2 SymbolicObj2 deriving Show --- | Rectilinear 2D set -type Rectilinear2 = [Box2] - --- | Rectilinear 3D set -type Rectilinear3 = [Box3] From 6f35bf4e897894746090b88efcb5df6c07d2a4ec Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 28 May 2019 06:51:23 +0100 Subject: [PATCH 216/227] improve example18, and pull in dependencies from the main import, not the sub export. --- programs/Benchmark.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index a62782d..c9625ac 100644 --- a/programs/Benchmark.hs +++ b/programs/Benchmark.hs @@ -11,11 +11,10 @@ import Prelude (($), (*), (/), String, IO, cos, pi, map, zip3, Maybe(Just, Nothi -- Use criterion for benchmarking. see import Criterion.Main (Benchmark, bgroup, bench, nf, nfAppIO, defaultMain) --- The parts of ImplicitCAD we know how to benchmark (in theory). -import Graphics.Implicit (union, circle, sphere, SymbolicObj2, SymbolicObj3, writeDXF2, writeSVG, writePNG2, writeSTL, writeBinSTL) +-- The parts of ImplicitCAD we know how to benchmark. +import Graphics.Implicit (union, circle, sphere, SymbolicObj2, SymbolicObj3, writeDXF2, writeSVG, writePNG2, writeSTL, writeBinSTL, unionR, translate, difference, extrudeRM, rect3R) import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) -import Graphics.Implicit.Primitives (translate, difference, extrudeRM, rect3R) -- The variables defining distance and counting in our world. import Graphics.Implicit.Definitions (ℝ, Fastℕ) @@ -27,7 +26,7 @@ import Graphics.Implicit.Definitions (ℝ, Fastℕ) -- | What we extrude in the example on the website. obj2d_1 :: SymbolicObj2 obj2d_1 = - union + unionR 8 [ circle 10 , translate (22,0) $ circle 10 , translate (0,22) $ circle 10 From 0c0f12a58220bcdadfd2a1bcfffe30ba9d850756 Mon Sep 17 00:00:00 2001 From: Lisa Marie Maginnis Date: Thu, 30 May 2019 08:49:44 +0100 Subject: [PATCH 217/227] Change from type to newtype. --- Graphics/Implicit/Definitions.hs | 40 ++++++--- Graphics/Implicit/Export/DiscreteAproxable.hs | 7 +- .../Implicit/Export/MarchingSquaresFill.hs | 29 +++---- .../Export/NormedTriangleMeshFormats.hs | 11 ++- Graphics/Implicit/Export/PolylineFormats.hs | 57 +++++++----- Graphics/Implicit/Export/Render.hs | 29 ++++--- Graphics/Implicit/Export/Render/GetSegs.hs | 44 +++++----- .../Implicit/Export/Render/HandlePolylines.hs | 50 ++++++----- .../Implicit/Export/Render/HandleSquares.hs | 24 +++-- Graphics/Implicit/Export/Render/RefineSegs.hs | 87 ++++++++++--------- .../Implicit/Export/Render/TesselateLoops.hs | 19 ++-- Graphics/Implicit/Export/SymbolicObj2.hs | 34 ++++---- Graphics/Implicit/Export/SymbolicObj3.hs | 16 ++-- .../Implicit/Export/TriangleMeshFormats.hs | 25 +++--- Graphics/Implicit/Export/Util.hs | 6 +- 15 files changed, 269 insertions(+), 209 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index f611dfe..cfac4a3 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -26,12 +26,12 @@ module Graphics.Implicit.Definitions ( (⋅), (⋯*), (⋯/), - Polyline, - Polytri, - Triangle, - NormedTriangle, - TriangleMesh, - NormedTriangleMesh, + Polyline(Polyline), + Polytri(Polytri), + Triangle(Triangle), + NormedTriangle(NormedTriangle), + TriangleMesh(TriangleMesh), + NormedTriangleMesh(NormedTriangleMesh), Obj2, Obj3, Box2, @@ -89,6 +89,8 @@ import Data.VectorSpace (Scalar, InnerSpace, (<.>)) import Graphics.Implicit.FastIntUtil as F (Fastℕ(Fastℕ), fromFastℕ, toFastℕ) import Graphics.Implicit.IntegralUtil as N (ℕ, fromℕ, toℕ) +import Control.DeepSeq (NFData, rnf) + -- Let's make things a bit nicer. -- Following the math notation ℝ, ℝ², ℝ³... -- Supports changing Float to Double for more precision! @@ -164,25 +166,37 @@ instance ComponentWiseMultable ℝ3 where (x,y,z) ⋯* (x',y',z') = (x*x', y*y', z*z') (x,y,z) ⋯/ (x',y',z') = (x/x', y/y', z/z') --- | A chain of line segments, as in SVG +-- | A chain of line segments, as in SVG or DXF. -- eg. [(0,0), (0.5,1), (1,0)] ---> /\ -type Polyline = [ℝ2] +newtype Polyline = Polyline [ℝ2] -- | A triangle in 2D space (a,b,c). -type Polytri = (ℝ2, ℝ2, ℝ2) +newtype Polytri = Polytri (ℝ2, ℝ2, ℝ2) -- | A triangle in 3D space (a,b,c) = a triangle with vertices a, b and c -type Triangle = (ℝ3, ℝ3, ℝ3) +newtype Triangle = Triangle (ℝ3, ℝ3, ℝ3) -- | A triangle ((v1,n1),(v2,n2),(v3,n3)) has vertices v1, v2, v3 -- with corresponding normals n1, n2, and n3 -type NormedTriangle = ((ℝ3, ℝ3), (ℝ3, ℝ3), (ℝ3, ℝ3)) +newtype NormedTriangle = NormedTriangle ((ℝ3, ℝ3), (ℝ3, ℝ3), (ℝ3, ℝ3)) -- | A triangle mesh is a bunch of triangles, attempting to be a surface. -type TriangleMesh = [Triangle] +newtype TriangleMesh = TriangleMesh [Triangle] -- | A normed triangle mesh is a mesh of normed triangles. -type NormedTriangleMesh = [NormedTriangle] +newtype NormedTriangleMesh = NormedTriangleMesh [NormedTriangle] + +instance NFData Triangle where + rnf (Triangle (a,b,c)) = rnf (a,b,c) + +instance NFData TriangleMesh where + rnf (TriangleMesh xs) = rnf xs + +instance NFData Polytri where + rnf (Polytri (a,b,c)) = rnf (a,b,c) + +instance NFData Polyline where + rnf (Polyline xs) = rnf xs -- | A 2D object. type Obj2 = (ℝ2 -> ℝ) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index e3c3120..0d8f401 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -14,7 +14,7 @@ module Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAp import Prelude((-), (/), ($), (<), map, round, (+), maximum, abs, (*), fromIntegral, max, realToFrac, Int) -- Definitions for our number system, objects, and the things we can use to approximately represent objects. -import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj2, SymbolicObj3, Polyline, TriangleMesh, NormedTriangleMesh) +import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj2, SymbolicObj3, Polyline, Triangle, TriangleMesh(TriangleMesh), NormedTriangleMesh(NormedTriangleMesh)) import Graphics.Implicit.ObjectUtil (getImplicit2, getImplicit3, getBox2, getBox3) @@ -32,6 +32,9 @@ import Data.AffineSpace ((.-^), (.+^)) default (ℝ) +unmesh :: TriangleMesh -> [Triangle] +unmesh (TriangleMesh m) = m + -- | There is a discrete way to aproximate this object. -- eg. Aproximating a 3D object with a triangle mesh -- would be DiscreteApproxable Obj3 TriangleMesh @@ -42,7 +45,7 @@ instance DiscreteAproxable SymbolicObj3 TriangleMesh where discreteAprox = symbolicGetMesh instance DiscreteAproxable SymbolicObj3 NormedTriangleMesh where - discreteAprox res obj = map (normTriangle res (getImplicit3 obj)) $ symbolicGetMesh res obj + discreteAprox res obj = NormedTriangleMesh $ map (normTriangle res (getImplicit3 obj)) $ unmesh $ symbolicGetMesh res obj -- FIXME: way too many magic numbers. instance DiscreteAproxable SymbolicObj3 DynamicImage where diff --git a/Graphics/Implicit/Export/MarchingSquaresFill.hs b/Graphics/Implicit/Export/MarchingSquaresFill.hs index a03a131..c52965c 100644 --- a/Graphics/Implicit/Export/MarchingSquaresFill.hs +++ b/Graphics/Implicit/Export/MarchingSquaresFill.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) where import Prelude(Bool(True, False), fromIntegral, ($), (-), (+), (/), (*), (<=), (>), ceiling, concat, max, div, floor) -import Graphics.Implicit.Definitions (ℕ, ℝ, ℝ2, Polytri, Obj2, (⋯/), (⋯*)) +import Graphics.Implicit.Definitions (ℕ, ℝ, ℝ2, Polytri(Polytri), Obj2, (⋯/), (⋯*)) import Data.VectorSpace ((^-^),(^+^)) @@ -98,8 +98,8 @@ getSquareTriangles (x1, y1) (x2, y2) obj = midy2 = (x + dx*x1y2/(x1y2-x2y2), y + dy) -- decompose a square into two triangles... - square :: forall t t1. t -> t1 -> t1 -> t1 -> [(t, t1, t1)] - square aa bb cc dd = [(aa,bb,cc), (aa,cc,dd)] + square :: ℝ2 -> ℝ2 -> ℝ2 -> ℝ2 -> [Polytri] + square aa bb cc dd = [Polytri (aa,bb,cc), Polytri (aa,cc,dd)] in case (x1y2 <= 0, x2y2 <= 0, x1y1 <= 0, x2y1 <= 0) of @@ -118,33 +118,30 @@ getSquareTriangles (x1, y1) (x2, y2) obj = (True, False, True, False) -> square (x1,y1) midy1 midy2 (x1,y2) (True, False, - False, False) -> [((x1,y2), midx1, midy2)] + False, False) -> [Polytri ((x1,y2), midx1, midy2)] (False, True, True, True) -> - [(midx1, (x1,y1), midy2), ((x1,y1), (x2,y1), midy2), (midy2, (x2,y1), (x2,y2))] + [Polytri (midx1, (x1,y1), midy2), Polytri ((x1,y1), (x2,y1), midy2), Polytri (midy2, (x2,y1), (x2,y2))] (True, True, False, True) -> - [((x1,y2), midx1, (x2,y2)), (midx1, midy1, (x2,y2)), ((x2,y2), midy1, (x2,y1))] + [Polytri ((x1,y2), midx1, (x2,y2)), Polytri (midx1, midy1, (x2,y2)), Polytri ((x2,y2), midy1, (x2,y1))] (False, False, - True, False) -> [(midx1, (x1,y1), midy1)] + True, False) -> [Polytri (midx1, (x1,y1), midy1)] (True, True, True, False) -> - [(midy1,midx2,(x2,y2)), ((x2,y2), (x1,y2), midy1), (midy1, (x1,y2), (x1,y1))] + [Polytri (midy1,midx2,(x2,y2)), Polytri ((x2,y2), (x1,y2), midy1), Polytri (midy1, (x1,y2), (x1,y1))] (False, False, - False, True) -> [(midx2, midy1, (x2,y1))] + False, True) -> [Polytri (midx2, midy1, (x2,y1))] (True, False, True, True) -> - [(midy2, (x2,y1), midx2), ((x2,y1), midy2, (x1,y1)), ((x1,y1), midy2, (x1,y2))] + [Polytri (midy2, (x2,y1), midx2), Polytri ((x2,y1), midy2, (x1,y1)), Polytri ((x1,y1), midy2, (x1,y2))] (False, True, - False, False) -> [(midx2, (x2,y2), midy2)] + False, False) -> [Polytri (midx2, (x2,y2), midy2)] (True, False, False, True) -> if c > 0 - then [((x1,y2), midx1, midy2), ((x2,y1), midy1, midx2)] --[[midx1, midy2], [midx2, midy1]] + then [Polytri ((x1,y2), midx1, midy2), Polytri ((x2,y1), midy1, midx2)] --[[midx1, midy2], [midx2, midy1]] else [] --[[midx1, midy1], [midx2, midy2]] (False, True, True, False) -> if c <= 0 then [] --[[midx1, midy2], [midx2, midy1]] - else [((x1,y1), midy1, midx1), ((x2,y2), midx2, midy2)] --[[midx1, midy1], [midx2, midy2]] - - - + else [Polytri ((x1,y1), midy1, midx1), Polytri ((x2,y2), midx2, midy2)] --[[midx1, midy1], [midx2, midy2]] diff --git a/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs b/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs index 73c45b7..9fdb55b 100644 --- a/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs @@ -9,12 +9,11 @@ module Graphics.Implicit.Export.NormedTriangleMeshFormats (obj) where import Prelude(($), map, (+), (.), (*), length, (-), return) -import Graphics.Implicit.Definitions (NormedTriangle, ℝ3) +import Graphics.Implicit.Definitions (NormedTriangle(NormedTriangle), NormedTriangleMesh(NormedTriangleMesh), ℝ3) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, mconcat, buildInt) - -obj :: [NormedTriangle] -> Text -obj normedtriangles = toLazyText $ vertcode <> normcode <> trianglecode +obj :: NormedTriangleMesh -> Text +obj (NormedTriangleMesh normedtriangles) = toLazyText $ vertcode <> normcode <> trianglecode where -- A vertex line; v (0.0, 0.0, 1.0) = "v 0.0 0.0 1.0\n" v :: ℝ3 -> Builder @@ -25,12 +24,12 @@ obj normedtriangles = toLazyText $ vertcode <> normcode <> trianglecode verts = do -- extract the vertices for each triangle -- recall that a normed triangle is of the form ((vert, norm), ...) - ((a,_),(b,_),(c,_)) <- normedtriangles + NormedTriangle ((a,_),(b,_),(c,_)) <- normedtriangles -- The vertices from each triangle take up 3 position in the resulting list [a,b,c] norms = do -- extract the normals for each triangle - ((_,a),(_,b),(_,c)) <- normedtriangles + NormedTriangle ((_,a),(_,b),(_,c)) <- normedtriangles -- The normals from each triangle take up 3 position in the resulting list [a,b,c] vertcode = mconcat $ map v verts diff --git a/Graphics/Implicit/Export/PolylineFormats.hs b/Graphics/Implicit/Export/PolylineFormats.hs index a353118..91573fe 100644 --- a/Graphics/Implicit/Export/PolylineFormats.hs +++ b/Graphics/Implicit/Export/PolylineFormats.hs @@ -9,28 +9,35 @@ module Graphics.Implicit.Export.PolylineFormats (svg, hacklabLaserGCode, dxf2) where -import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, concat, show, (++), unwords, map, mapM_, snd, compare, min, max, length) +import Prelude((.), ($), (-), (+), (/), minimum, maximum, unzip, show, (++), unwords, map, mapM_, snd, compare, min, max, length, concat, foldl) -import Graphics.Implicit.Definitions (Polyline, ℝ, ℝ2) +import Graphics.Implicit.Definitions (Polyline(Polyline), ℝ, ℝ2) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, mempty, toLazyText, mconcat, bf, (<>), buildInt, buildTruncFloat) import Text.Blaze.Svg.Renderer.Text (renderSvg) -import Text.Blaze.Svg11 ((!),docTypeSvg,g,polyline,toValue) +import Text.Blaze.Svg11 ((!),docTypeSvg,g,polyline,toValue,Svg) import Text.Blaze.Internal (stringValue) import qualified Text.Blaze.Svg11.Attributes as A (version, width, height, viewbox, points, stroke, strokeWidth, fill) import Data.List (sortBy) +default (ℝ) + +-- FIXME: magic numbers. svg :: [Polyline] -> Text svg plines = renderSvg . svg11 . svg' $ plines where strokeWidth :: ℝ - strokeWidth = 1.0 - (xmin, xmax, ymin, ymax) = (minimum xs - margin, maximum xs + margin, minimum ys - margin, maximum ys + margin) + strokeWidth = 1 + (xmin, xmax, ymin, ymax) = (xmin' - margin, xmax' + margin, ymin' - margin, ymax' + margin) where margin = strokeWidth / 2 - (xs,ys) = unzip (concat plines) - + ((xmin', xmax'), (ymin', ymax')) = (maxMinList xs,maxMinList ys) + (xs,ys) = unzip $ concat $ map pair plines + pair (Polyline a) = a + maxMinList :: [ℝ] -> (ℝ,ℝ) + maxMinList (x:others) = foldl (\(l,h) y -> (min l y, max h y)) (x,x) others + maxMinList [] = (0,0) svg11 = docTypeSvg ! A.version "1.1" ! A.width (stringValue $ show (xmax-xmin) ++ "mm") ! A.height (stringValue $ show (ymax-ymin) ++ "mm") @@ -38,11 +45,12 @@ svg plines = renderSvg . svg11 . svg' $ plines -- The reason this isn't totally straightforwards is that svg has different coordinate system -- and we need to compute the requisite translation. + svg' :: [Polyline] -> Svg svg' [] = mempty -- When we have a known point, we can compute said transformation: svg' polylines = thinBlueGroup $ mapM_ poly polylines - poly line = polyline ! A.points pointList + poly (Polyline line) = polyline ! A.points pointList where pointList = toValue $ toLazyText $ mconcat [bf (x-xmin) <> "," <> bf (ymax - y) <> " " | (x,y) <- line] -- Instead of setting styles on every polyline, we wrap the lines in a group element and set the styles on it: @@ -84,11 +92,11 @@ dxf2 plines = toLazyText $ dxf2Header <> dxf2Tables <> dxf2Blocks <> dxf2Entitie dxf2Entities = mconcat [ " 0\n", "SECTION\n", " 2\n", "ENTITIES\n", - mconcat [ buildPolyline orderedPolyline | orderedPolyline <- (orderPolylines plines)], + mconcat [ buildPolyline orderedPolyline | orderedPolyline <- orderPolylines plines], " 0\n", "ENDSEC\n" ] - buildPolyline :: [ℝ2] -> Builder - buildPolyline singlePolyline = + buildPolyline :: Polyline -> Builder + buildPolyline (Polyline singlePolyline) = mconcat [ " 0\n", "POLYLINE\n", " 8\n", "0\n", @@ -110,22 +118,26 @@ dxf2 plines = toLazyText $ dxf2Header <> dxf2Tables <> dxf2Blocks <> dxf2Entitie " 20\n", buildTruncFloat y1, "\n" ] (dxfxmin, dxfxmax, dxfymin, dxfymax) = (minimum xs, maximum xs, minimum ys, maximum ys) - (xs, ys) = unzip (concat plines) + (xs, ys) = unzip $ concat $ map pair plines + pair :: Polyline -> [ℝ2] + pair (Polyline x) = x orderPolylines :: [Polyline] -> [Polyline] orderPolylines plines = map snd . sortBy (\(a,_) (b, _) -> compare a b) . map (\x -> (polylineRadius x, x)) $ plines where - polylineRadius :: [ℝ2] -> ℝ - polylineRadius [] = 0 + polylineRadius :: Polyline -> ℝ polylineRadius polyline' = max (xmax' - xmin') (ymax' - ymin') where - ((xmin', xmax'), (ymin', ymax')) = polylineRadius' polyline' - polylineRadius' :: [ℝ2] -> (ℝ2, ℝ2) - polylineRadius' [] = ((0,0),(0,0)) - polylineRadius' [(x,y)] = ((x,x),(y,y)) - polylineRadius' ((x,y):ps) = ((min x xmin,max x xmax),(min y ymin, max y ymax)) - where ((xmin, xmax), (ymin, ymax)) = polylineRadius' ps + ((xmin', xmax'), (ymin', ymax')) = polylineRadius' [polyline'] + polylineRadius' :: [Polyline] -> (ℝ2, ℝ2) + polylineRadius' lines = (maxMinList xs,maxMinList ys) + where + (xs,ys) = unzip $ concat $ map pair lines + pair (Polyline a) = a + maxMinList :: [ℝ] -> (ℝ,ℝ) + maxMinList (x:others) = foldl (\(l,h) y -> (min l y, max h y)) (x,x) others + maxMinList [] = (0,0) -- Gcode generation for the laser cutter in HackLab. Complies with https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=823374 hacklabLaserGCode :: [Polyline] -> Text @@ -146,10 +158,11 @@ hacklabLaserGCode polylines = toLazyText $ gcodeHeader <> mconcat (map interpret ,"M2 (end)"] gcodeXY :: ℝ2 -> Builder gcodeXY (x,y) = mconcat ["X", buildTruncFloat x, " Y", buildTruncFloat y] - interpretPolyline (start:others) = mconcat [ + interpretPolyline :: Polyline -> Builder + interpretPolyline (Polyline (start:others)) = mconcat [ "G00 ", gcodeXY start ,"\nM62 P0 (laser on)\n" ,mconcat [ "G01 " <> gcodeXY point <> "\n" | point <- others] ,"M63 P0 (laser off)\n\n" ] - interpretPolyline [] = mempty + interpretPolyline (Polyline []) = mempty diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index fcdae49..1e24135 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -124,7 +124,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = -- Calculate segments for each side segsZ = [[[ - map2 (inj3 z0) $ getSegs (x0,y0) (x1',y1') (obj **$ z0) + map (injZ z0) $ getSegs (x0,y0) (x1',y1') (obj **$ z0) (objX0Y0Z0, objX1Y0Z0, objX0Y1Z0, objX1Y1Z0) (midA0, midA1, midB0, midB1) |x0<-pXs|x1'<-tail pXs|midB0<-mX'' |midB1<-mX'T |midA0<-mY'' |midA1<-tail mY'' @@ -136,7 +136,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = ] `using` parBuffer (max 1 $ fromIntegral $ div nz 32) rdeepseq segsY = [[[ - map2 (inj2 y0) $ getSegs (x0,z0) (x1',z1') (obj *$* y0) + map (injY y0) $ getSegs (x0,z0) (x1',z1') (obj *$* y0) (objX0Y0Z0,objX1Y0Z0,objX0Y0Z1,objX1Y0Z1) (midA0, midA1, midB0, midB1) |x0<-pXs|x1'<-tail pXs|midB0<-mB'' |midB1<-mBT' |midA0<-mA'' |midA1<-tail mA'' @@ -148,7 +148,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = ] `using` parBuffer (max 1 $ fromIntegral $ div ny 32) rdeepseq segsX = [[[ - map2 (inj1 x0) $ getSegs (y0,z0) (y1',z1') (obj $** x0) + map (injX x0) $ getSegs (y0,z0) (y1',z1') (obj $** x0) (objX0Y0Z0,objX0Y1Z0,objX0Y0Z1,objX0Y1Z1) (midA0, midA1, midB0, midB1) |x0<-pXs| midB0<-mB'' |midB1<-mBT' |midA0<-mA'' |midA1<-mA'T @@ -250,12 +250,18 @@ getContour p1@(x1, y1) p2 res obj = -- utility functions -inj1 :: forall t t1 t2. t -> (t1, t2) -> (t, t1, t2) -inj1 a (b,c) = (a,b,c) -inj2 :: forall t t1 t2. t1 -> (t, t2) -> (t, t1, t2) -inj2 b (a,c) = (a,b,c) -inj3 :: forall t t1 t2. t2 -> (t, t1) -> (t, t1, t2) -inj3 c (a,b) = (a,b,c) +injX :: ℝ -> Polyline -> [ℝ3] +injX a (Polyline xs) = map (prepend a) xs +prepend :: ℝ -> ℝ2 -> ℝ3 +prepend a (b,c) = (a,b,c) +injY :: ℝ -> Polyline -> [ℝ3] +injY a (Polyline xs) = map (insert a) xs +insert :: ℝ -> ℝ2 -> ℝ3 +insert b (a,c) = (a,b,c) +injZ :: ℝ -> Polyline -> [ℝ3] +injZ a (Polyline xs) = map (postfix a) xs +postfix :: ℝ -> ℝ2 -> ℝ3 +postfix c (a,b) = (a,b,c) ($**) :: forall t t1 t2 t3. ((t1, t2, t3) -> t) -> t1 -> (t2, t3) -> t infixr 0 $** @@ -282,11 +288,6 @@ appBC f b c a = f (a,b,c) appAC :: forall t t1 t2 t3. ((t1, t2, t3) -> t) -> t1 -> t3 -> t2 -> t appAC f a c b = f (a,b,c) -map2 :: forall a b. (a -> b) -> [[a]] -> [[b]] -map2 f = map (map f) --- FIXME: not used? ---map2R :: forall a a1. (a1 -> a) -> [[a1]] -> [[a]] ---map2R f = map (reverse . map f) mapR :: forall a. [[a]] -> [[a]] mapR = map reverse diff --git a/Graphics/Implicit/Export/Render/GetSegs.hs b/Graphics/Implicit/Export/Render/GetSegs.hs index ffb18be..c0e5d99 100644 --- a/Graphics/Implicit/Export/Render/GetSegs.hs +++ b/Graphics/Implicit/Export/Render/GetSegs.hs @@ -4,9 +4,9 @@ module Graphics.Implicit.Export.Render.GetSegs (getSegs, getSegs') where -import Prelude(Eq, Bool(True, False), sqrt, (+), (*), (/=), map, (.), filter, ($), (<=)) +import Prelude(Bool(True, False), sqrt, (+), (*), (/=), map, (.), filter, ($), (<=)) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline) +import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline(Polyline)) import Graphics.Implicit.Export.Render.RefineSegs (refine) import Graphics.Implicit.Export.Util (centroid) @@ -73,11 +73,9 @@ getSegs p1 p2 obj (x1y1, x2y1, x1y2, x2y2) (midx1V,midx2V,midy1V,midy2V) = midy1 = (midy1V , y ) midy2 = (midy2V, y + dy) - notPointLine :: Eq a => [a] -> Bool - notPointLine [np1, np2] = np1 /= np2 - notPointLine [] = False - notPointLine [_] = False - notPointLine (_ : (_ : (_ : _))) = False + notPointLine :: Polyline -> Bool + notPointLine (Polyline [np1,np2]) = np1 /= np2 + notPointLine _ = False -- takes straight lines between mid points and subdivides them to -- account for sharp corners, etc. @@ -99,56 +97,56 @@ getSegs p1 p2 obj (x1y1, x2y1, x1y2, x2y2) (midx1V,midx2V,midy1V,midy2V) = -- Horizontal Cases (True, True, - False, False) -> [[midx1, midx2]] + False, False) -> [Polyline [midx1, midx2]] (False, False, - True, True) -> [[midx2, midx1]] + True, True) -> [Polyline [midx2, midx1]] -- Vertical Cases (False, True, - False, True) -> [[midy2, midy1]] + False, True) -> [Polyline [midy2, midy1]] (True, False, - True, False) -> [[midy1, midy2]] + True, False) -> [Polyline [midy1, midy2]] -- Corner Cases (True, False, - False, False) -> [[midx1, midy2]] + False, False) -> [Polyline [midx1, midy2]] (False, True, - True, True) -> [[midy2, midx1]] + True, True) -> [Polyline [midy2, midx1]] (True, True, - False, True) -> [[midx1, midy1]] + False, True) -> [Polyline [midx1, midy1]] (False, False, - True, False) -> [[midy1, midx1]] + True, False) -> [Polyline [midy1, midx1]] (True, True, - True, False) -> [[midy1, midx2]] + True, False) -> [Polyline [midy1, midx2]] (False, False, - False, True) -> [[midx2, midy1]] + False, True) -> [Polyline [midx2, midy1]] (True, False, - True, True) -> [[midx2, midy2]] + True, True) -> [Polyline [midx2, midy2]] (False, True, - False, False) -> [[midy2, midx2]] + False, False) -> [Polyline [midy2, midx2]] -- Dual Corner Cases (True, False, False, True) -> if c <= 0 - then [[midx1, midy1], [midx2, midy2]] - else [[midx1, midy2], [midx2, midy1]] + then [Polyline [midx1, midy1], Polyline [midx2, midy2]] + else [Polyline [midx1, midy2], Polyline [midx2, midy1]] (False, True, True, False) -> if c <= 0 - then [[midy2, midx1], [midy1, midx2]] - else [[midy1, midx1], [midy2, midx2]] + then [Polyline [midy2, midx1], Polyline [midy1, midx2]] + else [Polyline [midy1, midx1], Polyline [midy2, midx2]] -- A convenience function, we don't actually care too much about diff --git a/Graphics/Implicit/Export/Render/HandlePolylines.hs b/Graphics/Implicit/Export/Render/HandlePolylines.hs index 97ec367..b08ab94 100644 --- a/Graphics/Implicit/Export/Render/HandlePolylines.hs +++ b/Graphics/Implicit/Export/Render/HandlePolylines.hs @@ -7,9 +7,9 @@ module Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs, reducePolyline) where -import Prelude(Bool(False), Maybe(Just, Nothing), map, (.), filter, (==), last, reverse, ($), (++), tail, (-), (/), abs, (<=), (||), (&&), (*), (>), not, null, otherwise) +import Prelude(Bool(True, False), Maybe(Just, Nothing), map, (.), filter, (==), last, reverse, ($), (++), (-), (/), abs, (<=), (||), (&&), (*), (>), otherwise) -import Graphics.Implicit.Definitions (minℝ, Polyline, ℝ) +import Graphics.Implicit.Definitions (minℝ, Polyline(Polyline)) cleanLoopsFromSegs :: [Polyline] -> [Polyline] cleanLoopsFromSegs = @@ -17,35 +17,45 @@ cleanLoopsFromSegs = . joinSegs . filter polylineNotNull +-- | Join polylines that connect. joinSegs :: [Polyline] -> [Polyline] joinSegs [] = [] -joinSegs (present:remaining) = +joinSegs (Polyline present:remaining) = let - findNext ((p3:ps):segs) - | p3 == last present = (Just (p3:ps), segs) - | last ps == last present = (Just (reverse $ p3:ps), segs) - | otherwise = case findNext segs of (res1,res2) -> (res1,(p3:ps):res2) + 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 ([]:_) = (Nothing, []) + findNext (Polyline []:_) = (Nothing, []) in case findNext remaining of - (Nothing, _) -> present: joinSegs remaining - (Just match, others) -> joinSegs $ (present ++ tail match): others + (Nothing, _) -> Polyline present: joinSegs remaining + (Just (Polyline match), others) -> joinSegs $ (Polyline (present ++ match)) : others -reducePolyline :: [(ℝ, ℝ)] -> [(ℝ, ℝ)] -reducePolyline ((x1,y1):(x2,y2):(x3,y3):others) - | (x1,y1) == (x2,y2) = reducePolyline ((x2,y2):(x3,y3):others) +-- | Simplify and sort a polyline. +reducePolyline :: Polyline -> Polyline +reducePolyline (Polyline ((x1,y1):(x2,y2):(x3,y3):others)) + -- Remove 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 ((x1,y1):(x3,y3):others) - | otherwise = (x1,y1) : reducePolyline ((x2,y2):(x3,y3):others) -reducePolyline ((x1,y1):(x2,y2):others) = - if (x1,y1) == (x2,y2) then reducePolyline ((x2,y2):others) else (x1,y1):(x2,y2):others + 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 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 = l -polylineNotNull :: [a] -> Bool -polylineNotNull (_:l) = not (null l) -polylineNotNull [] = False +-- ensure that polylines are not empty. +polylineNotNull :: Polyline -> Bool +polylineNotNull (Polyline (_:_:_)) = True +polylineNotNull (Polyline [_]) = True +polylineNotNull (Polyline []) = False {-cleanLoopsFromSegs = connectPolys diff --git a/Graphics/Implicit/Export/Render/HandleSquares.hs b/Graphics/Implicit/Export/Render/HandleSquares.hs index 2eaa8ff..80374d8 100644 --- a/Graphics/Implicit/Export/Render/HandleSquares.hs +++ b/Graphics/Implicit/Export/Render/HandleSquares.hs @@ -4,9 +4,9 @@ module Graphics.Implicit.Export.Render.HandleSquares (mergedSquareTris) where -import Prelude(concatMap, (++)) +import Prelude(concatMap, (++), ($)) -import Graphics.Implicit.Definitions (TriangleMesh) +import Graphics.Implicit.Definitions (TriangleMesh(TriangleMesh), Triangle(Triangle)) import Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) import Data.VectorSpace ((^*), (*^), (^+^)) @@ -63,10 +63,15 @@ mergedSquareTris sqTris = -- We don't need to do any work on triangles. They'll just be part of -- the list of triangles we give back. So, the triangles coming from -- triangles... - triTriangles = [tri | Tris tris <- sqTris, tri <- tris ] + triTriangles :: [Triangle] + triTriangles = [tri | Tris tris <- sqTris, tri <- unmesh tris ] --concat $ map (\(Tris a) -> a) $ filter isTris sqTris -- We actually want to work on the quads, so we find those + squaresFromTris :: [TriSquare] squaresFromTris = [ Sq x y z q | Sq x y z q <- sqTris ] + + unmesh (TriangleMesh m) = m + {- -- Collect ones that are on the same plane. planeAligned = groupWith (\(Sq basis z _ _) -> (basis,z)) squares @@ -85,7 +90,7 @@ mergedSquareTris sqTris = -- merge them to triangles, and combine with the original triangles. -- Disable square merging temporarily. --triTriangles ++ concat (map squareToTri finishedSquares) - triTriangles ++ concatMap squareToTri squaresFromTris + TriangleMesh $ triTriangles ++ concatMap squareToTri squaresFromTris -- And now for a bunch of helper functions that do the heavy lifting... @@ -125,8 +130,8 @@ joinYaligned quads@((Sq b z _ yS):_) = joinYaligned [] = [] -} --- Reconstruct a triangle -squareToTri :: TriSquare -> TriangleMesh +-- Deconstruct a square into two triangles. +squareToTri :: TriSquare -> [Triangle] squareToTri (Sq (b1,b2,b3) z (x1,x2) (y1,y2)) = let zV = b3 ^* z @@ -137,8 +142,9 @@ squareToTri (Sq (b1,b2,b3) z (x1,x2) (y1,y2)) = c = zV ^+^ x1V ^+^ y2V d = zV ^+^ x2V ^+^ y2V in - [(a,b,c),(c,b,d)] - -squareToTri(Tris t) = t + [Triangle (a,b,c), Triangle (c,b,d)] +squareToTri (Tris t) = unmesh t + where + unmesh (TriangleMesh a) = a diff --git a/Graphics/Implicit/Export/Render/RefineSegs.hs b/Graphics/Implicit/Export/Render/RefineSegs.hs index f5b3e62..30e294e 100644 --- a/Graphics/Implicit/Export/Render/RefineSegs.hs +++ b/Graphics/Implicit/Export/Render/RefineSegs.hs @@ -5,9 +5,9 @@ -- export one function, which refines polylines. module Graphics.Implicit.Export.Render.RefineSegs (refine) where -import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, tail, sqrt, (<=)) +import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, sqrt, (<=)) -import Graphics.Implicit.Definitions (ℝ, ℝ2, minℝ, ℕ, Obj2, (⋅)) +import Graphics.Implicit.Definitions (ℝ, ℝ2, Polyline(Polyline), minℝ, ℕ, Obj2, (⋅)) import Graphics.Implicit.Export.Util (centroid) import Data.VectorSpace (normalized, magnitude, (^-^), (^*), (^+^)) @@ -17,64 +17,73 @@ import Data.VectorSpace (normalized, magnitude, (^-^), (^*), (^+^)) -- We break this into two steps: detail and then simplify. -refine :: ℝ -> Obj2 -> [ℝ2] -> [ℝ2] +refine :: ℝ -> Obj2 -> Polyline -> Polyline refine res obj = simplify res . detail' res obj -- we wrap detail to make it ignore very small segments, and to pass in -- an initial value for a pointer counter argument. This is detail' -- FIXME: magic number. -detail' :: ℝ -> (ℝ2 -> ℝ) -> [ℝ2] -> [ℝ2] -detail' res obj [p1@(x1,y1), p2@(x2,y2)] | (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) > res*res/200 = - detail 0 res obj [p1,p2] +detail' :: ℝ -> (ℝ2 -> ℝ) -> Polyline -> Polyline +detail' res obj (Polyline [p1@(x1,y1), p2@(x2,y2)]) + | (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) > res*res/200 = detail 0 res obj $ Polyline [p1,p2] detail' _ _ a = a --- detail adds new points to a polyline to add more detail. - -detail :: ℕ -> ℝ -> (ℝ2 -> ℝ) -> [ℝ2] -> [ℝ2] -detail n res obj [p1, p2] | n < 2 = +-- FIXME: all of the magic numbers. +-- | detail adds new points to a polyline to add more detail. +detail :: ℕ -> ℝ -> (ℝ2 -> ℝ) -> Polyline -> Polyline +detail n res obj (Polyline [p1, p2]) | n < 2 = let mid = centroid [p1,p2] midval = obj mid in if abs midval < res / 40 - then [p1, p2] - else let - normal = (\(a,b) -> (b, -a)) $ normalized (p2 ^-^ p1) - derivN = -(obj (mid ^-^ (normal ^* (midval/2))) - midval) * (2/midval) - in if abs derivN > 0.5 && abs derivN < 2 && abs (midval/derivN) < 3*res - then let - mid' = mid ^-^ (normal ^* (midval / derivN)) - in detail (n+1) res obj [p1, mid'] - ++ tail (detail (n+1) res obj [mid', p2] ) - else let - derivX = (obj (mid ^+^ (res/100, 0)) - midval)*100/res - derivY = (obj (mid ^+^ (0, res/100)) - midval)*100/res - derivNormSq = derivX*derivX + derivY*derivY - in if abs derivNormSq > 0.09 && abs derivNormSq < 4 && abs (midval/sqrt derivNormSq) < 3*res - then let - (dX, dY) = (- derivX*midval/derivNormSq, - derivY*midval/derivNormSq) - mid' = mid ^+^ (dX, dY) - midval' = obj mid' - posRatio = midval/(midval - midval') - mid'' = mid ^+^ (dX*posRatio, dY*posRatio) - in - detail (n+1) res obj [p1, mid''] ++ tail (detail (n+1) res obj [mid'', p2] ) - else [p1, p2] + then Polyline [p1, p2] + else + let + normal = (\(a,b) -> (b, -a)) $ normalized (p2 ^-^ p1) + derivN = -(obj (mid ^-^ (normal ^* (midval/2))) - midval) * (2/midval) + in + if abs derivN > 0.5 && abs derivN < 2 && abs (midval/derivN) < 3*res + then + let + mid' = mid ^-^ (normal ^* (midval / derivN)) + in + addPolylines (detail (n+1) res obj (Polyline [p1, mid'])) (detail (n+1) res obj ( Polyline [mid', p2] )) + else + let + derivX = (obj (mid ^+^ (res/100, 0)) - midval)*100/res + derivY = (obj (mid ^+^ (0, res/100)) - midval)*100/res + derivNormSq = derivX*derivX + derivY*derivY + in + if abs derivNormSq > 0.09 && abs derivNormSq < 4 && abs (midval/sqrt derivNormSq) < 3*res + then + let + (dX, dY) = (- derivX*midval/derivNormSq, - derivY*midval/derivNormSq) + mid' = mid ^+^ (dX, dY) + midval' = obj mid' + posRatio = midval/(midval - midval') + mid'' = mid ^+^ (dX*posRatio, dY*posRatio) + in + addPolylines (detail (n+1) res obj (Polyline [p1, mid''])) (detail (n+1) res obj ( Polyline [mid'', p2] )) + else Polyline [p1, p2] detail _ _ _ x = x -simplify :: ℝ -> [ℝ2] -> [ℝ2] +simplify :: ℝ -> Polyline -> Polyline simplify _ = {-simplify3 . simplify2 res . -} simplify1 -simplify1 :: [ℝ2] -> [ℝ2] -simplify1 (a:b:c:xs) = +simplify1 :: Polyline -> Polyline +simplify1 (Polyline (a:b:c:xs)) = if abs ( ((b ^-^ a) ⋅ (c ^-^ a)) - magnitude (b ^-^ a) * magnitude (c ^-^ a) ) <= minℝ - then simplify1 (a:c:xs) - else a : simplify1 (b:c:xs) + then simplify1 (Polyline (a:c:xs)) + else addPolylines (Polyline [a]) (simplify1 (Polyline (b:c:xs))) simplify1 a = a +addPolylines :: Polyline -> Polyline -> Polyline +addPolylines (Polyline as) (Polyline bs) = Polyline (as ++ bs) + {- -simplify2 :: ℝ -> [ℝ2] -> [ℝ2] +simplify2 :: ℝ -> Polyline -> Polyline simplify2 res [a,b,c,d] = if norm (b - c) < res/10 then [a, ((b + c) / (2::ℝ)), d] diff --git a/Graphics/Implicit/Export/Render/TesselateLoops.hs b/Graphics/Implicit/Export/Render/TesselateLoops.hs index efc67e0..a26a1f5 100644 --- a/Graphics/Implicit/Export/Render/TesselateLoops.hs +++ b/Graphics/Implicit/Export/Render/TesselateLoops.hs @@ -6,7 +6,7 @@ module Graphics.Implicit.Export.Render.TesselateLoops (tesselateLoop) where import Prelude(return, ($), length, (==), zip, init, tail, reverse, (<), (/), null, foldl1, (++), head, (*), abs, (>), (&&), (+), concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, Obj3, ℝ3, TriangleMesh, (⋅)) +import Graphics.Implicit.Definitions (ℝ, ℕ, Obj3, ℝ3, TriangleMesh(TriangleMesh), (⋅), Triangle(Triangle)) import Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) @@ -22,7 +22,7 @@ tesselateLoop :: ℝ -> Obj3 -> [[ℝ3]] -> [TriSquare] tesselateLoop _ _ [] = [] -tesselateLoop _ _ [[a,b],[_,c],[_,_]] = return $ Tris [(a,b,c)] +tesselateLoop _ _ [[a,b],[_,c],[_,_]] = [Tris $ TriangleMesh [Triangle (a,b,c)]] {- @@ -65,11 +65,12 @@ tesselateLoop _ _ [[a,_],[b,_],[c,_],[d,_]] | centroid [a,c] == centroid [b,d] = -} tesselateLoop res obj [[a,_],[b,_],[c,_],[d,_]] | obj (centroid [a,c]) < res/30 = - return $ Tris [(a,b,c),(a,c,d)] + return $ Tris $ TriangleMesh [Triangle (a,b,c), Triangle (a,c,d)] -- Fallback case: make fans -tesselateLoop res obj pathSides = return $ Tris $ +-- FIXME: magic numbers. +tesselateLoop res obj pathSides = return $ Tris $ TriangleMesh $ let path' = concatMap init pathSides (early_tris,path) = shrinkLoop 0 path' res obj @@ -86,16 +87,16 @@ tesselateLoop res obj pathSides = return $ Tris $ mid' = mid ^-^ normal ^* (midval/deriv) in if abs midval > res/50 && preNormalNorm > 0.5 && abs deriv > 0.5 && abs (midval/deriv) < 2*res && 3*abs (obj mid') < abs midval - then early_tris ++ [(a,b,mid') | (a,b) <- zip path (tail path ++ [head path]) ] - else early_tris ++ [(a,b,mid) | (a,b) <- zip path (tail path ++ [head path]) ] + then early_tris ++ [Triangle (a,b,mid') | (a,b) <- zip path (tail path ++ [head path]) ] + else early_tris ++ [Triangle (a,b,mid) | (a,b) <- zip path (tail path ++ [head path]) ] -shrinkLoop :: ℕ -> [ℝ3] -> ℝ -> Obj3 -> (TriangleMesh, [ℝ3]) +shrinkLoop :: ℕ -> [ℝ3] -> ℝ -> Obj3 -> ([Triangle], [ℝ3]) shrinkLoop _ path@[a,b,c] res obj = if abs (obj $ centroid [a,b,c]) < res/50 then - ( [(a,b,c)], []) + ( [Triangle (a,b,c)], []) else ([], path) @@ -103,7 +104,7 @@ shrinkLoop n path@(a:b:c:xs) res obj | n < genericLength path = if abs (obj (centroid [a,c])) < res/50 then let (tris,remainder) = shrinkLoop 0 (a:c:xs) res obj - in ((a,b,c):tris, remainder) + in ((Triangle (a,b,c)):tris, remainder) else shrinkLoop (n+1) (b:c:xs ++ [a]) res obj diff --git a/Graphics/Implicit/Export/SymbolicObj2.hs b/Graphics/Implicit/Export/SymbolicObj2.hs index 73b49db..68159a1 100644 --- a/Graphics/Implicit/Export/SymbolicObj2.hs +++ b/Graphics/Implicit/Export/SymbolicObj2.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.Export.SymbolicObj2 (symbolicGetOrientedContour, symbol import Prelude(map, ($), (-), (/), (+), (>), (*), (.), reverse, cos, pi, sin, max, fromInteger, ceiling) -import Graphics.Implicit.Definitions (ℝ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline, Polytri, (⋯*)) +import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline(Polyline), Polytri(Polytri), (⋯*)) import Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) @@ -30,36 +30,40 @@ symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res sym where obj = getImplicit2 symbObj orient :: Polyline -> Polyline - orient [] = [] - orient [_] = [] - orient points@(x:y:_) = + orient (Polyline (points@(x:y:_))) = let v = (\(a,b) -> (b, -a)) (y - x) dv = v ^/ (magnitude v / res / 0.1) in if obj (x + dv) - obj x > 0 - then points - else reverse points + then Polyline points + else Polyline $ reverse points -symbolicGetContour :: ℝ -> SymbolicObj2 -> [Polyline] -symbolicGetContour _ (RectR 0 (x1,y1) (x2,y2)) = [[ (x1,y1), (x2,y1), (x2,y2), (x1,y2), (x1,y1) ]] -symbolicGetContour res (Circle r) = [[ ( r*cos(2*pi*m/n), r*sin(2*pi*m/n) ) | m <- [0.. n] ]] where +symbolicGetContour :: ℝ -> SymbolicObj2 -> [Polyline] +symbolicGetContour _ (RectR 0 (x1,y1) (x2,y2)) = [Polyline [ (x1,y1), (x2,y1), (x2,y2), (x1,y2), (x1,y1) ]] +-- FIXME: magic number. +symbolicGetContour res (Circle r) = [Polyline [ ( r*cos(2*pi*m/n), r*sin(2*pi*m/n) ) | m <- [0.. n] ]] where n :: ℝ n = max 5 (fromInteger . ceiling $ 2*pi*r/res) -symbolicGetContour res (Translate2 v obj) = map (map (+ v) ) $ symbolicGetContour res obj -symbolicGetContour res (Scale2 s@(a,b) obj) = map (map (⋯* s)) $ symbolicGetContour (res/sc) obj +symbolicGetContour res (Translate2 v obj) = appOpPolylines (+ v) $ symbolicGetContour res obj +symbolicGetContour res (Scale2 s@(a,b) obj) = appOpPolylines (⋯* s) $ symbolicGetContour (res/sc) obj where sc = max a b symbolicGetContour res obj = case rebound2 (getImplicit2 obj, getBox2 obj) of (obj', (a,b)) -> Render.getContour a b res obj' +appOpPolylines :: (ℝ2 -> ℝ2) -> [Polyline] -> [Polyline] +appOpPolylines op polylines = map (appOpPolyline op) polylines +appOpPolyline :: (ℝ2 -> ℝ2) -> Polyline -> Polyline +appOpPolyline op (Polyline xs) = Polyline $ map op xs symbolicGetContourMesh :: ℝ -> SymbolicObj2 -> [Polytri] -symbolicGetContourMesh res (Translate2 v obj) = map (\(a,b,c) -> (a + v, b + v, c + v) ) $ +symbolicGetContourMesh res (Translate2 v obj) = map (\(Polytri (a,b,c)) -> (Polytri (a + v, b + v, c + v)) ) $ symbolicGetContourMesh res obj -symbolicGetContourMesh res (Scale2 s@(a,b) obj) = map (\(c,d,e) -> (c ⋯* s, d ⋯* s, e ⋯* s) ) $ +symbolicGetContourMesh res (Scale2 s@(a,b) obj) = map (\(Polytri (c,d,e)) -> (Polytri (c ⋯* s, d ⋯* s, e ⋯* s)) ) $ symbolicGetContourMesh (res/sc) obj where sc = max a b -symbolicGetContourMesh _ (RectR 0 (x1,y1) (x2,y2)) = [((x1,y1), (x2,y1), (x2,y2)), ((x2,y2), (x1,y2), (x1,y1)) ] +symbolicGetContourMesh _ (RectR 0 (x1,y1) (x2,y2)) = [Polytri ((x1,y1), (x2,y1), (x2,y2)), Polytri ((x2,y2), (x1,y2), (x1,y1)) ] +-- FIXME: magic number. symbolicGetContourMesh res (Circle r) = - [ ((0,0), + [ Polytri ((0,0), (r*cos(2*pi*m/n), r*sin(2*pi*m/n)), (r*cos(2*pi*(m+1)/n), r*sin(2*pi*(m+1)/n)) )| m <- [0.. n-1] ] diff --git a/Graphics/Implicit/Export/SymbolicObj3.hs b/Graphics/Implicit/Export/SymbolicObj3.hs index 5e5b5a0..edeb18b 100644 --- a/Graphics/Implicit/Export/SymbolicObj3.hs +++ b/Graphics/Implicit/Export/SymbolicObj3.hs @@ -15,7 +15,7 @@ module Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) where import Prelude(map, zip, length, filter, (>), ($), null, (++), concatMap) -import Graphics.Implicit.Definitions (ℝ, ℝ3, SymbolicObj3(UnionR3)) +import Graphics.Implicit.Definitions (ℝ, ℝ3, SymbolicObj3(UnionR3), Triangle(Triangle), TriangleMesh(TriangleMesh)) import Graphics.Implicit.Export.Render (getMesh) import Graphics.Implicit.ObjectUtil (getBox3, getImplicit3) import Graphics.Implicit.MathUtil(box3sWithin) @@ -23,7 +23,7 @@ import Graphics.Implicit.Export.Symbolic.Rebound3 (rebound3) import Control.Arrow(first, second) -symbolicGetMesh :: ℝ -> SymbolicObj3 -> [(ℝ3, ℝ3, ℝ3)] +symbolicGetMesh :: ℝ -> SymbolicObj3 -> TriangleMesh {-- -- A translated objects mesh is its mesh translated. @@ -197,7 +197,7 @@ symbolicGetMesh res (ExtrudeRM r twist scale translate obj2 h) = map transformTriangle (side_tris ++ bottom_tris ++ top_tris) -} -symbolicGetMesh res inputObj@(UnionR3 r objs) = +symbolicGetMesh res inputObj@(UnionR3 r objs) = TriangleMesh $ let boxes = map getBox3 objs boxedObjs = zip boxes objs @@ -212,11 +212,11 @@ symbolicGetMesh res inputObj@(UnionR3 r objs) = (dependants, independents) = sepFree boxedObjs in if null independents then case rebound3 (getImplicit3 inputObj, getBox3 inputObj) of - (obj, (a,b)) -> getMesh a b res obj + (obj, (a,b)) -> unmesh $ getMesh a b res obj else if null dependants - then concatMap (symbolicGetMesh res) independents - else concatMap (symbolicGetMesh res) independents - ++ symbolicGetMesh res (UnionR3 r dependants) + then concatMap unmesh $ map (symbolicGetMesh res) independents + else (concatMap unmesh $ map (symbolicGetMesh res) independents) + ++ (unmesh $ symbolicGetMesh res (UnionR3 r dependants)) -- | If all that fails, coerce and apply marching cubes :( symbolicGetMesh res obj = @@ -224,3 +224,5 @@ symbolicGetMesh res obj = case rebound3 (getImplicit3 obj, getBox3 obj) of (obj', (a,b)) -> getMesh a b res obj' +unmesh :: TriangleMesh -> [Triangle] +unmesh (TriangleMesh m) = m diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 130ddd1..078abcc 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.Export.TriangleMeshFormats (stl, binaryStl, jsTHREE) wh import Prelude (Float, Eq, Bool, ($), (+), map, (.), toEnum, length, zip, return, (==), (||), (&&), filter, not) -import Graphics.Implicit.Definitions (Triangle, TriangleMesh, ℕ, ℝ3, ℝ, fromℝtoFloat) +import Graphics.Implicit.Definitions (Triangle(Triangle), TriangleMesh(TriangleMesh), ℕ, ℝ3, ℝ, fromℝtoFloat) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildℕ) import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) @@ -29,6 +29,9 @@ import Data.Storable.Endian (LittleEndian(LE)) import Data.VectorSpace (normalized, (^-^)) import Data.Cross (cross3) +unmesh :: TriangleMesh -> [Triangle] +unmesh (TriangleMesh m) = m + normal :: (ℝ3,ℝ3,ℝ3) -> ℝ3 normal (a,b,c) = normalized $ (b ^-^ a) `cross3` (c ^-^ a) @@ -69,14 +72,14 @@ cleanupTris tris = zsame :: forall a. Eq a => ((a, a, a), (a, a, a), (a, a, a)) -> Bool zsame ((_,_,z1),(_,_,z2),(_,_,z3)) = same (z1, z2, z3) isDegenerateTri :: Triangle -> Bool - isDegenerateTri (a, b, c) = (isDegenerateTri2Axis $ floatTri) -- || (isDegenerateTriLine $ floatTri) || (isDegenerateTriPoint $ floatTri) + isDegenerateTri (Triangle (a, b, c)) = (isDegenerateTri2Axis $ floatTri) -- || (isDegenerateTriLine $ floatTri) || (isDegenerateTriPoint $ floatTri) where floatTri = (floatPoint a, floatPoint b, floatPoint c) - in filter (not . isDegenerateTri) tris + in TriangleMesh $ filter (not . isDegenerateTri) (unmesh tris) -- | Generate an STL file is ASCII format. stl :: TriangleMesh -> Text -stl triangles = toLazyText $ stlHeader <> mconcat (map triangle $ cleanupTris triangles) <> stlFooter +stl triangles = toLazyText $ stlHeader <> mconcat (map triangle $ unmesh $ cleanupTris triangles) <> stlFooter where stlHeader :: Builder stlHeader = "solid ImplictCADExport\n" @@ -86,8 +89,8 @@ stl triangles = toLazyText $ stlHeader <> mconcat (map triangle $ cleanupTris tr vector (x,y,z) = bf x <> " " <> bf y <> " " <> bf z vertex :: ℝ3 -> Builder vertex v = "vertex " <> vector v - triangle :: (ℝ3, ℝ3, ℝ3) -> Builder - triangle (a,b,c) = + triangle :: Triangle -> Builder + triangle (Triangle (a,b,c)) = "facet normal " <> vector (normal (a,b,c)) <> "\n" <> "outer loop\n" <> vertex a <> "\n" @@ -105,10 +108,10 @@ float32LE = writeStorable . LE -- | Generate an STL file in it's binary format. binaryStl :: TriangleMesh -> ByteString -binaryStl triangles = toLazyByteString $ header <> lengthField <> mconcat (map triangle $ cleanupTris triangles) +binaryStl triangles = toLazyByteString $ header <> lengthField <> mconcat (map triangle $ unmesh $ cleanupTris triangles) where header = fromByteString $ replicate 80 0 - lengthField = fromWord32le $ toEnum $ length $ cleanupTris triangles - triangle (a,b,c) = normalV (a,b,c) <> point a <> point b <> point c <> fromWord16le 0 + lengthField = fromWord32le $ toEnum $ length $ unmesh $ cleanupTris triangles + triangle (Triangle (a,b,c)) = normalV (a,b,c) <> point a <> point b <> point c <> fromWord16le 0 point :: (ℝ3) -> BI.Builder point (x,y,z) = fromWrite $ float32LE (toFloat x) <> float32LE (toFloat y) <> float32LE (toFloat z) normalV ps = point $ normal ps @@ -142,12 +145,12 @@ jsTHREE triangles = toLazyText $ header <> vertcode <> facecode <> footer verts = do -- extract the vertices for each triangle -- recall that a normed triangle is of the form ((vert, norm), ...) - (a,b,c) <- cleanupTris triangles + (Triangle (a,b,c)) <- unmesh $ cleanupTris triangles -- The vertices from each triangle take up 3 position in the resulting list [a,b,c] vertcode = mconcat $ map v verts facecode = mconcat $ do - (n,_) <- zip [0, 3 ..] $ cleanupTris triangles + (n,_) <- zip [0, 3 ..] $ unmesh $ cleanupTris triangles let (posa, posb, posc) = (n, n+1, n+2) :: (ℕ, ℕ, ℕ) return $ f posa posb posc diff --git a/Graphics/Implicit/Export/Util.hs b/Graphics/Implicit/Export/Util.hs index 73468b5..0cad8e4 100644 --- a/Graphics/Implicit/Export/Util.hs +++ b/Graphics/Implicit/Export/Util.hs @@ -11,13 +11,13 @@ module Graphics.Implicit.Export.Util (normTriangle, normVertex, centroid) where import Prelude(Fractional, (/), (-), ($), foldl, recip, realToFrac, length) -import Graphics.Implicit.Definitions (ℝ, ℝ3, Obj3, Triangle, NormedTriangle) +import Graphics.Implicit.Definitions (ℝ, ℝ3, Obj3, Triangle(Triangle), NormedTriangle(NormedTriangle)) import Data.VectorSpace (VectorSpace, Scalar, (^+^), (*^), (^/), (^-^), normalized, zeroV) normTriangle :: ℝ -> Obj3 -> Triangle -> NormedTriangle -normTriangle res obj (a,b,c) = - (normify a', normify b', normify c') +normTriangle res obj (Triangle (a,b,c)) = + NormedTriangle (normify a', normify b', normify c') where normify = normVertex res obj a' = (a ^+^ r*^b ^+^ r*^c) ^/ 1.02 From 249909ed48f9a64d0f4395cc2c059675fd208b15 Mon Sep 17 00:00:00 2001 From: Lisa Marie Maginnis Date: Thu, 30 May 2019 12:18:00 +0100 Subject: [PATCH 218/227] final changes for Polyline being a newtype. --- Graphics/Implicit/Export/Render.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 1e24135..22c33b1 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.Export.Render (getMesh, getContour) where import Prelude(Float, Bool, ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, (==), (||), filter, not, reverse, (.), Eq, concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), both, allthree, fromℝtoFloat) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), both, allthree, fromℝtoFloat) import Data.VectorSpace ((^-^)) From 8b357710fc7fb52abd6a26a1479c64d23bd67462 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 12:49:36 +0100 Subject: [PATCH 219/227] remove some warnings --- Graphics/Implicit/Export/Render.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 22c33b1..569d48c 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -11,9 +11,9 @@ -- export getContour and getMesh, which returns the edge of a 2D object, or the surface of a 3D object, respectively. module Graphics.Implicit.Export.Render (getMesh, getContour) where -import Prelude(Float, Bool, ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, (==), (||), filter, not, reverse, (.), Eq, concatMap) +import Prelude(ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, reverse, (.), concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), both, allthree, fromℝtoFloat) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), both, allthree) import Data.VectorSpace ((^-^)) From c63a329eb2a04b11efed06c2a970543a5e71e6b6 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 12:57:54 +0100 Subject: [PATCH 220/227] partial type rewrite of raytracing. clearer now. --- Graphics/Implicit/Export/DiscreteAproxable.hs | 26 ++++++++------ Graphics/Implicit/Export/RayTrace.hs | 35 ++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index 0d8f401..c4953b4 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -23,9 +23,9 @@ import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.Util (normTriangle) -- We are the only ones that use this. -import Graphics.Implicit.Export.RayTrace (dynamicImage, Color, average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) +import Graphics.Implicit.Export.RayTrace (Color(Color), Camera(Camera), Light(Light), Scene(Scene), average, traceRay, cameraRay) -import Codec.Picture (DynamicImage, generateImage, PixelRGBA8(PixelRGBA8)) +import Codec.Picture (DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8), generateImage) import Data.VectorSpace ((^+^), (^/), (*^), (^-^)) import Data.AffineSpace ((.-^), (.+^)) @@ -49,7 +49,7 @@ instance DiscreteAproxable SymbolicObj3 NormedTriangleMesh where -- FIXME: way too many magic numbers. instance DiscreteAproxable SymbolicObj3 DynamicImage where - discreteAprox _ symbObj = dynamicImage $ generateImage pixelRenderer (round w) (round h) + discreteAprox _ symbObj = ImageRGBA8 $ generateImage pixelRenderer (round w) (round h) where (w,h) = (150, 150) :: ℝ2 obj = getImplicit3 symbObj @@ -61,12 +61,13 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where deviation = maximum [abs $ y1 - avY, abs $ y2 - avY, abs $ z1 - avZ, abs $ z2 - avZ] camera = Camera (x1-deviation*(2.2), avY, avZ) (0, -1, 0) (0,0, -1) 1.0 lights = [Light (x1-deviation*(1.5), y1 - (0.4)*(y2-y1), avZ) ((0.03)*deviation) ] - scene = Scene obj (PixelRGBA8 200 200 230 255) lights (PixelRGBA8 255 255 255 0) - pixelRenderer :: Int -> Int -> Color + scene = Scene obj (Color 200 200 230 255) lights (Color 255 255 255 0) + pixelRenderer :: Int -> Int -> PixelRGBA8 pixelRenderer a b = renderScreen ((fromIntegral a)/w - (0.5)) ((fromIntegral b)/h - (0.5)) - renderScreen :: ℝ -> ℝ -> Color + renderScreen :: ℝ -> ℝ -> PixelRGBA8 renderScreen a b = + colorToPixelRGBA8 $ average [ traceRay (cameraRay camera ((a,b) ^+^ ( 0.25/w, 0.25/h))) @@ -81,28 +82,33 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where (cameraRay camera ((a,b) ^+^ (-0.25/w,-0.25/h))) 0.5 box scene ] + where + colorToPixelRGBA8 :: Color -> PixelRGBA8 + colorToPixelRGBA8 (Color rr gg bb aa) = PixelRGBA8 rr gg bb aa instance DiscreteAproxable SymbolicObj2 [Polyline] where discreteAprox = symbolicGetContour instance DiscreteAproxable SymbolicObj2 DynamicImage where - discreteAprox _ symbObj = dynamicImage $ generateImage pixelRenderer (round w) (round h) + discreteAprox _ symbObj = ImageRGBA8 $ generateImage pixelRenderer (round w) (round h) where (w,h) = (150, 150) :: ℝ2 obj = getImplicit2 symbObj (p1@(x1,_), p2@(_,y2)) = getBox2 symbObj (dx, dy) = p2 ^-^ p1 dxy = max dx dy - pixelRenderer :: Int -> Int -> Color + pixelRenderer :: Int -> Int -> PixelRGBA8 pixelRenderer mya myb = mycolor where xy a b = ((x1,y2) .-^ (dxy-dx, dy-dxy)^/2) .+^ dxy*^(a/w, -b/h) s = 0.25 :: ℝ (a', b') = (realToFrac mya, realToFrac myb) :: ℝ2 - mycolor = average [objColor $ xy a' b', objColor $ xy a' b', + mycolor = colorToPixelRGBA8 $ average [objColor $ xy a' b', objColor $ xy a' b', objColor $ xy (a'+s) (b'+s), objColor $ xy (a'-s) (b'-s), objColor $ xy (a'+s) (b'+s), objColor $ xy (a'-s) (b'-s)] - objColor p = if obj p < 0 then PixelRGBA8 150 150 160 255 else PixelRGBA8 255 255 255 0 + colorToPixelRGBA8 :: Color -> PixelRGBA8 + colorToPixelRGBA8 (Color rr gg bb aa) = PixelRGBA8 rr gg bb aa + objColor p = if obj p < 0 then Color 150 150 160 255 else Color 255 255 255 0 diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index cc711ae..a778310 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -5,15 +5,20 @@ -- FIXME: why are these needed? {-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, FlexibleContexts #-} -module Graphics.Implicit.Export.RayTrace( dynamicImage, Color, average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) where +module Graphics.Implicit.Export.RayTrace( Color(Color), average, Camera(Camera), Light(Light), Scene(Scene), traceRay, cameraRay) where import Prelude(Show, RealFrac, Maybe(Just, Nothing), Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, toRational, otherwise) import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋅), Obj3) -import Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) + +import Codec.Picture (Pixel8) + import Control.Monad (guard, return) + import Control.Arrow ((***)) + import Data.VectorSpace (Scalar, magnitude, (^+^), (*^), normalized, (^-^), InnerSpace) + import Data.Cross (cross3) default (ℕ, ℝ) @@ -23,46 +28,45 @@ default (ℕ, ℝ) data Camera = Camera ℝ3 ℝ3 ℝ3 ℝ deriving Show --- | A ray. A point, and a normalized point in the direction the ray is going. +-- | A ray. A point, and a normal pointing in the direction the ray is going. data Ray = Ray ℝ3 ℝ3 deriving Show data Scene = Scene Obj3 Color [Light] Color +-- | A light source. source point, and intensity. data Light = Light ℝ3 ℝ deriving Show -type Color = PixelRGBA8 - -color :: Pixel8 -> Pixel8 -> Pixel8 -> Pixel8 -> PixelRGBA8 -color = PixelRGBA8 - -dynamicImage :: Image PixelRGBA8 -> DynamicImage -dynamicImage = ImageRGBA8 +-- | A colour. Red Green Blue and Alpha components. +data Color = Color Pixel8 Pixel8 Pixel8 Pixel8 -- Math +-- | The distance traveled by a line segment from the first point to the second point. vectorDistance :: ℝ3 -> ℝ3 -> Scalar ℝ3 vectorDistance a b = magnitude (b-a) -colorMult :: Pixel8 -> PixelRGBA8 -> PixelRGBA8 -s `colorMult` (PixelRGBA8 a b c d) = color (s `mult` a) (s `mult` b) (s `mult` c) d +-- | Multiply a colour by an intensity. +colorMult :: Pixel8 -> Color -> Color +s `colorMult` (Color a b c d) = Color (s `mult` a) (s `mult` b) (s `mult` c) d where bound :: RealFrac a => a -> a bound = max 0 . min 254 mult :: Pixel8 -> Pixel8 -> Pixel8 mult x y = round . bound . toRational $ x * y +-- | Average a set of colours. average :: [Color] -> Color average l = let ((rs, gs), (bs, as)) = (unzip *** unzip) . unzip $ map - (\(PixelRGBA8 r g b a) -> ((fromIntegral r, fromIntegral g), (fromIntegral b, fromIntegral a))) + (\(Color r g b a) -> ((fromIntegral r, fromIntegral g), (fromIntegral b, fromIntegral a))) l :: (([ℝ], [ℝ]), ([ℝ], [ℝ])) n :: ℝ n = fromIntegral $ length l (r', g', b', a') = (sum rs/n, sum gs/n, sum bs/n, sum as/n) - in PixelRGBA8 + in Color (fromInteger . round $ r') (fromInteger . round $ g') (fromInteger . round $ b') (fromInteger . round $ a') -- Ray Utilities @@ -76,6 +80,7 @@ cameraRay (Camera p vx vy f) (x,y) = in Ray p' n +-- | Create a ray from two points. rayFromTo :: ℝ3 -> ℝ3 -> Ray rayFromTo p1 p2 = Ray p1 (normalized $ p2 ^-^ p1) @@ -93,7 +98,6 @@ rayBounds ray box = (lower, upper) -- Intersection - intersection :: Ray -> ((ℝ,ℝ), ℝ) -> ℝ -> Obj3 -> Maybe ℝ3 intersection r@(Ray p v) ((a, aval),b) res obj = let @@ -137,7 +141,6 @@ intersects a b c d = case intersection a b c d of Just _ -> True -- Trace - traceRay :: Ray -> ℝ -> (ℝ3, ℝ3) -> Scene -> Color traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultColor) = let From a72bea9d2dbb7b918993aa5df99d667a0347ee52 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 13:16:43 +0100 Subject: [PATCH 221/227] quiet some warnings. --- Graphics/Implicit/Export/SymbolicObj2.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Graphics/Implicit/Export/SymbolicObj2.hs b/Graphics/Implicit/Export/SymbolicObj2.hs index 68159a1..e58e6c3 100644 --- a/Graphics/Implicit/Export/SymbolicObj2.hs +++ b/Graphics/Implicit/Export/SymbolicObj2.hs @@ -29,6 +29,7 @@ symbolicGetOrientedContour :: ℝ -> SymbolicObj2 -> [Polyline] symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res symbObj where obj = getImplicit2 symbObj + -- FIXME: cowardly case handling. orient :: Polyline -> Polyline orient (Polyline (points@(x:y:_))) = let @@ -37,6 +38,8 @@ symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res sym in if obj (x + dv) - obj x > 0 then Polyline points else Polyline $ reverse points + orient (Polyline []) = Polyline [] + orient (Polyline [_]) = Polyline [] symbolicGetContour :: ℝ -> SymbolicObj2 -> [Polyline] symbolicGetContour _ (RectR 0 (x1,y1) (x2,y2)) = [Polyline [ (x1,y1), (x2,y1), (x2,y2), (x1,y2), (x1,y1) ]] From 9685d63eee4603f40489c8262227af36da25cfcf Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 13:17:59 +0100 Subject: [PATCH 222/227] remove some warnings. --- Graphics/Implicit/Export/SymbolicObj3.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/Export/SymbolicObj3.hs b/Graphics/Implicit/Export/SymbolicObj3.hs index edeb18b..27a72ea 100644 --- a/Graphics/Implicit/Export/SymbolicObj3.hs +++ b/Graphics/Implicit/Export/SymbolicObj3.hs @@ -15,7 +15,7 @@ module Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) where import Prelude(map, zip, length, filter, (>), ($), null, (++), concatMap) -import Graphics.Implicit.Definitions (ℝ, ℝ3, SymbolicObj3(UnionR3), Triangle(Triangle), TriangleMesh(TriangleMesh)) +import Graphics.Implicit.Definitions (ℝ, ℝ3, SymbolicObj3(UnionR3), Triangle, TriangleMesh(TriangleMesh)) import Graphics.Implicit.Export.Render (getMesh) import Graphics.Implicit.ObjectUtil (getBox3, getImplicit3) import Graphics.Implicit.MathUtil(box3sWithin) From 99da44f14451a0218901d1eb31eb6041860f4ca0 Mon Sep 17 00:00:00 2001 From: Lisa Marie Maginnis Date: Thu, 30 May 2019 14:48:05 +0100 Subject: [PATCH 223/227] switch partially away from fromIntegral. --- Graphics/Implicit/Export/Render.hs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 569d48c..118c051 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -13,7 +13,7 @@ module Graphics.Implicit.Export.Render (getMesh, getContour) where import Prelude(ceiling, ($), fromIntegral, (+), (*), max, div, tail, map, concat, reverse, (.), concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), both, allthree) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), both, allthree, fromℕtoℝ) import Data.VectorSpace ((^-^)) @@ -81,19 +81,19 @@ getMesh p1@(x1,y1,z1) p2 res obj = (nx,ny,nz) = ceiling `allthree` ( d ⋯/ (res,res,res)) -- How big are the steps? - (rx,ry,rz) = d ⋯/ (fromIntegral `allthree` (nx,ny,nz)) + (rx,ry,rz) = d ⋯/ (fromℕtoℝ `allthree` (nx,ny,nz)) -- The positions we're rendering. - pXs = [ x1 + rx*n | n <- [0.. fromIntegral nx] ] - pYs = [ y1 + ry*n | n <- [0.. fromIntegral ny] ] - pZs = [ z1 + rz*n | n <- [0.. fromIntegral nz] ] + pXs = [ x1 + rx*n | n <- [0.. fromℕtoℝ nx] ] + pYs = [ y1 + ry*n | n <- [0.. fromℕtoℝ ny] ] + pZs = [ z1 + rz*n | n <- [0.. fromℕtoℝ nz] ] par3DList :: forall t. NFData t => ℕ -> ℕ -> ℕ -> ((ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> t) -> [[[t]]] par3DList lenx leny lenz f = [[[f - (\n -> x1 + rx*fromIntegral (mx+n)) mx - (\n -> y1 + ry*fromIntegral (my+n)) my - (\n -> z1 + rz*fromIntegral (mz+n)) mz + (\n -> x1 + rx*fromℕtoℝ (mx+n)) mx + (\n -> y1 + ry*fromℕtoℝ (my+n)) my + (\n -> z1 + rz*fromℕtoℝ (mz+n)) mz | mx <- [0..lenx] ] | my <- [0..leny] ] | mz <- [0..lenz] ] `using` parBuffer (max 1 . fromIntegral $ div lenz 32) rdeepseq @@ -200,17 +200,17 @@ getContour p1@(x1, y1) p2 res obj = (nx,ny) = ceiling `both` (d ⋯/ (res,res)) -- How big are the steps? - (rx,ry) = d ⋯/ (fromIntegral `both` (nx,ny)) + (rx,ry) = d ⋯/ (fromℕtoℝ `both` (nx,ny)) -- the points inside of the region. - pYs = [ y1 + ry*fromIntegral p | p <- [0.. ny] ] - pXs = [ x1 + rx*fromIntegral p | p <- [0.. nx] ] + pYs = [ y1 + ry*fromℕtoℝ p | p <- [0.. ny] ] + pXs = [ x1 + rx*fromℕtoℝ p | p <- [0.. nx] ] par2DList :: forall t. NFData t => ℕ -> ℕ -> ((ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> t) -> [[t]] par2DList lenx leny f = [[ f - (\n -> x1 + rx*fromIntegral (mx+n)) mx - (\n -> y1 + ry*fromIntegral (my+n)) my + (\n -> x1 + rx*fromℕtoℝ (mx+n)) mx + (\n -> y1 + ry*fromℕtoℝ (my+n)) my | mx <- [0..lenx] ] | my <- [0..leny] ] `using` parBuffer (max 1 . fromIntegral $ div leny 32) rdeepseq From 638f5357cee9103325f9dfda3088132eb943243a Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 22:50:42 +0100 Subject: [PATCH 224/227] add another newtype, and simplify the computation state. --- Graphics/Implicit/ExtOpenScad.hs | 10 +++--- .../Implicit/ExtOpenScad/Eval/Statement.hs | 8 ++--- Graphics/Implicit/ExtOpenScad/Util/StateC.hs | 31 ++++++++++--------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad.hs b/Graphics/Implicit/ExtOpenScad.hs index 92eaac1..fafee68 100644 --- a/Graphics/Implicit/ExtOpenScad.hs +++ b/Graphics/Implicit/ExtOpenScad.hs @@ -11,12 +11,14 @@ module Graphics.Implicit.ExtOpenScad (runOpenscad) where import Prelude(String, Either(Left, Right), IO, ($), fmap) import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3) -import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup, OVal) +import Graphics.Implicit.ExtOpenScad.Definitions (VarLookup) import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) import Graphics.Implicit.ExtOpenScad.Eval.Statement (runStatementI) import Graphics.Implicit.ExtOpenScad.Default (defaultObjects) +import Graphics.Implicit.ExtOpenScad.Util.StateC (CompState(CompState)) import Graphics.Implicit.ExtOpenScad.Util.OVal (divideObjs) + import Text.Parsec.Error (ParseError) import Control.Monad (mapM_) import Control.Monad.State (runStateT) @@ -27,8 +29,8 @@ runOpenscad :: String -> Either ParseError (IO (VarLookup, [SymbolicObj2], [Symb runOpenscad source = let initial = defaultObjects - rearrange :: forall t t1 t2 t3 t4. (t, (t4, [OVal], t1, t2, t3)) -> (t4, [SymbolicObj2], [SymbolicObj3]) - rearrange (_, (varlookup, ovals, _ , _ , _)) = (varlookup, obj2s, obj3s) where + rearrange :: forall t. (t, CompState) -> (VarLookup, [SymbolicObj2], [SymbolicObj3]) + rearrange (_, (CompState (varlookup, ovals, _))) = (varlookup, obj2s, obj3s) where (obj2s, obj3s, _ ) = divideObjs ovals in case parseProgram source of Left e -> Left e @@ -36,6 +38,6 @@ runOpenscad source = $ fmap rearrange $ (\sts' -> do path <- getCurrentDirectory - runStateT sts' (initial, [], path, (), () ) + runStateT sts' $ CompState (initial, [], path) ) $ mapM_ runStatementI sts diff --git a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs index 7913267..e3c7d25 100644 --- a/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs +++ b/Graphics/Implicit/ExtOpenScad/Eval/Statement.hs @@ -20,7 +20,7 @@ import Graphics.Implicit.ExtOpenScad.Definitions ( import Graphics.Implicit.ExtOpenScad.Util.OVal (getErrors) import Graphics.Implicit.ExtOpenScad.Util.ArgParser (argument, defaultTo, argMap) -import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, errorC, modifyVarLookup, mapMaybeM, lookupVar, pushVals, getRelPath, withPathShiftedBy, getVals, putVals) +import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, CompState(CompState), errorC, modifyVarLookup, mapMaybeM, lookupVar, pushVals, getRelPath, withPathShiftedBy, getVals, putVals) import Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, matchPat) import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram) @@ -78,7 +78,7 @@ runStatementI (StatementI lineN columnN (NewModule name argTemplate suite)) = do argTemplate' <- forM argTemplate $ \(name', defexpr) -> do defval <- mapMaybeM evalExpr defexpr return (name', defval) - (varlookup, _, path, _, _) <- get + (CompState (varlookup, _, path)) <- get -- FIXME: \_? really? runStatementI . StatementI lineN columnN $ (Name name :=) $ LitE $ OModule $ \_ -> do newNameVals <- forM argTemplate' $ \(name', maybeDef) -> do @@ -109,7 +109,7 @@ runStatementI (StatementI lineN columnN (NewModule name argTemplate suite)) = do runStatementI (StatementI lineN columnN (ModuleCall name argsExpr suite)) = do maybeMod <- lookupVar name - (varlookup, _, path, _, _) <- get + (CompState (varlookup, _, path)) <- get childVals <- fmap reverse . liftIO $ runSuiteCapture varlookup path suite argsVal <- forM argsExpr $ \(posName, expr) -> do val <- evalExpr expr @@ -149,7 +149,7 @@ runSuiteCapture :: VarLookup -> FilePath -> [StatementI] -> IO [OVal] runSuiteCapture varlookup path suite = do (res, _) <- runStateT (runSuite suite >> getVals) - (varlookup, [], path, (), () ) + (CompState (varlookup, [], path)) return res diff --git a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs index 9c993ff..71482d7 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/StateC.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/StateC.hs @@ -9,7 +9,7 @@ {-# LANGUAGE KindSignatures, FlexibleContexts #-} {-# LANGUAGE RankNTypes, ScopedTypeVariables #-} -module Graphics.Implicit.ExtOpenScad.Util.StateC (getVarLookup, modifyVarLookup, lookupVar, pushVals, getVals, putVals, withPathShiftedBy, getPath, getRelPath, errorC, mapMaybeM, StateC) where +module Graphics.Implicit.ExtOpenScad.Util.StateC (getVarLookup, modifyVarLookup, lookupVar, pushVals, getVals, putVals, withPathShiftedBy, getPath, getRelPath, errorC, mapMaybeM, StateC, CompState(CompState)) where import Prelude(FilePath, IO, String, Maybe(Just, Nothing), Show, Monad, fmap, (.), ($), (++), return, putStrLn, show) @@ -21,46 +21,49 @@ import System.FilePath(()) import Control.Monad.IO.Class (MonadIO) import Data.Kind (Type) --- This is the state machine. It contains the variables, their values, the path, and... ? -type CompState = (VarLookup, [OVal], FilePath, (), ()) +-- | This is the state of a computation. It contains a hash of variables, an array of OVals, and a path. +newtype CompState = CompState (VarLookup, [OVal], FilePath) + type StateC = StateT CompState IO getVarLookup :: StateC VarLookup -getVarLookup = fmap (\(a,_,_,_,_) -> a) get +getVarLookup = fmap (\(CompState (a,_,_)) -> a) get modifyVarLookup :: (VarLookup -> VarLookup) -> StateC () -modifyVarLookup = modify . (\f (a,b,c,d,e) -> (f a, b, c, d, e)) +modifyVarLookup = modify . (\f (CompState (a,b,c)) -> CompState (f a, b, c)) +-- | Perform a variable lookup lookupVar :: String -> StateC (Maybe OVal) lookupVar name = do varlookup <- getVarLookup return $ lookup name varlookup pushVals :: [OVal] -> StateC () -pushVals vals = modify (\(a,b,c,d,e) -> (a, vals ++ b,c,d,e)) +pushVals vals = modify (\(CompState (a,b,c)) -> CompState (a, vals ++ b, c)) getVals :: StateC [OVal] getVals = do - (_,b,_,_,_) <- get + (CompState (_,b,_)) <- get return b putVals :: [OVal] -> StateC () putVals vals = do - (a,_,c,d,e) <- get - put (a,vals,c,d,e) + (CompState (a,_,c)) <- get + put $ CompState (a,vals,c) withPathShiftedBy :: FilePath -> StateC a -> StateC a withPathShiftedBy pathShift s = do - (a,b,path,d,e) <- get - put (a, b, path pathShift, d, e) + (CompState (a,b,path)) <- get + put $ CompState (a, b, path pathShift) x <- s - (a',b',_,d',e') <- get - put (a', b', path, d', e') + (CompState (a',b',_)) <- get + put $ CompState (a', b', path) return x +-- | Return the path stored in the state. getPath :: StateC FilePath getPath = do - (_,_,c,_,_) <- get + (CompState (_,_,c)) <- get return c getRelPath :: FilePath -> StateC FilePath From 9dcebd719d2d382387339abcd2412e7d543114cb Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 6 Jun 2019 07:56:42 +0100 Subject: [PATCH 225/227] make haskell examples also spit out timing info. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 192b598..763a184 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,7 @@ dist: $(TARGETS) # Generate examples. examples: $(EXTOPENSCAD) cd Examples && for each in `find ./ -name '*scad' -type f | sort`; do { ../$(EXTOPENSCAD) $$each $(RTSOPTS); } done - cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; cd ..; $(GHC) Examples/$$filename.hs -o Examples/$$filename; cd Examples; $$filename; } done + cd Examples && for each in `find ./ -name '*.hs' -type f | sort`; do { filename=$(basename "$$each"); filename="$${filename%.*}"; cd ..; $(GHC) Examples/$$filename.hs -o Examples/$$filename; cd Examples; echo $$filename; $$filename +RTS -t ; } done # Generate images from the examples, so we can upload the images to our website. images: examples From 2b74224ea11c54107ee49f666afbaa4650714ec5 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 6 Jun 2019 07:58:14 +0100 Subject: [PATCH 226/227] two more examples. --- Examples/example16.hs | 8 ++++++++ Examples/example17.hs | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 Examples/example16.hs create mode 100644 Examples/example17.hs diff --git a/Examples/example16.hs b/Examples/example16.hs new file mode 100644 index 0000000..08e84cc --- /dev/null +++ b/Examples/example16.hs @@ -0,0 +1,8 @@ +import Graphics.Implicit +import Graphics.Implicit.Definitions +import Graphics.Implicit.Primitives + +roundbox:: SymbolicObj3 +roundbox = implicit (\(x,y,z) -> (x^4 + y^4 + z^4 - 15000)) ((-20,-20,-20),(20,20,20)) + +main = writeSTL 2 "example16.stl" roundbox diff --git a/Examples/example17.hs b/Examples/example17.hs new file mode 100644 index 0000000..ebd23ed --- /dev/null +++ b/Examples/example17.hs @@ -0,0 +1,21 @@ +-- Example 17, pulled from our benchmarking suite. +import Graphics.Implicit +import Graphics.Implicit.Definitions + +default (Fastℕ, ℝ) + +object2 :: SymbolicObj3 +object2 = squarePipe (10,10,10) 1 100 + where + squarePipe :: (ℝ,ℝ,ℝ) -> ℝ -> ℝ -> SymbolicObj3 + squarePipe (x,y,z) diameter precision = + union + $ map (\start-> translate start + $ rect3R 0 (0,0,0) (diameter,diameter,diameter) + ) + $ zip3 (map (\n->((fromIntegral n)/precision)*x) [0..100]) + (map (\n->((fromIntegral n)/precision)*y) [0..100]) + (map (\n->((fromIntegral n)/precision)*z) [0..100]) + +main = writeSTL 1 "example17.stl" object2 + From 5f9fb211fea82d2e4994d3ec8f61838c93229b33 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 6 Jun 2019 09:14:43 +0100 Subject: [PATCH 227/227] Inlining, spacing, ordering, and using type aliasess better. --- Graphics/Implicit/Definitions.hs | 21 ++++++++++++------- Graphics/Implicit/Export.hs | 8 +++---- Graphics/Implicit/Export/DiscreteAproxable.hs | 11 ++++++++-- Graphics/Implicit/Export/MarchingSquares.hs | 6 +----- .../Export/NormedTriangleMeshFormats.hs | 15 ++++++------- Graphics/Implicit/Export/Render.hs | 1 + Graphics/Implicit/ExtOpenScad/Primitives.hs | 2 +- Graphics/Implicit/Primitives.hs | 4 ++-- implicit.cabal | 2 +- 9 files changed, 41 insertions(+), 29 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index cfac4a3..016aca6 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -13,7 +13,6 @@ {-# LANGUAGE FlexibleInstances #-} -- Definitions of the types used when modeling, and a few operators. - module Graphics.Implicit.Definitions ( module F, module N, @@ -87,24 +86,23 @@ import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) import Graphics.Implicit.FastIntUtil as F (Fastℕ(Fastℕ), fromFastℕ, toFastℕ) + import Graphics.Implicit.IntegralUtil as N (ℕ, fromℕ, toℕ) import Control.DeepSeq (NFData, rnf) -- Let's make things a bit nicer. -- Following the math notation ℝ, ℝ², ℝ³... --- Supports changing Float to Double for more precision! --- FIXME: what about using rationals instead of Float/Double? type ℝ = Double type ℝ2 = (ℝ,ℝ) type ℝ3 = (ℝ,ℝ,ℝ) +-- | A give up point for dividing ℝs minℝ :: ℝ --- for Floats. ---minℝ = 0.00000011920928955078125 * 2 - -- for Doubles. minℝ = 0.0000000000000002 +-- for Floats. +--minℝ = 0.00000011920928955078125 * 2 -- | apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) @@ -128,15 +126,17 @@ fromℕtoℝ :: ℕ -> ℝ fromℕtoℝ = fromIntegral {-# INLINABLE fromℕtoℝ #-} +-- | Convert from our Fast Integer (int32) to ℝ. fromFastℕtoℝ :: Fastℕ -> ℝ fromFastℕtoℝ (Fastℕ a) = fromIntegral a {-# INLINABLE fromFastℕtoℝ #-} +-- | Convert from our rational to a float, for output. fromℝtoFloat :: ℝ -> Float fromℝtoFloat = realToFrac {-# INLINABLE fromℝtoFloat #-} --- add aditional instances to Show, for when we dump the intermediate form of an object. +-- |add aditional instances to Show, for when we dump the intermediate form of objects. instance Show (ℝ -> ℝ) where show _ = "" @@ -155,16 +155,23 @@ instance Show (ℝ3 -> ℝ) where --instance Show BoxedObj3 where -- show _ = "" + + -- TODO: Find a better way to do this? +-- | Add multiply and divide operators for two ℝ2s or ℝ3s. class ComponentWiseMultable a where (⋯*) :: a -> a -> a (⋯/) :: a -> a -> a instance ComponentWiseMultable ℝ2 where (x,y) ⋯* (x',y') = (x*x', y*y') + {-# INLINABLE (⋯*) #-} (x,y) ⋯/ (x',y') = (x/x', y/y') + {-# INLINABLE (⋯/) #-} instance ComponentWiseMultable ℝ3 where (x,y,z) ⋯* (x',y',z') = (x*x', y*y', z*z') + {-# INLINABLE (⋯*) #-} (x,y,z) ⋯/ (x',y',z') = (x/x', y/y', z/z') + {-# INLINABLE (⋯/) #-} -- | A chain of line segments, as in SVG or DXF. -- eg. [(0,0), (0.5,1), (1,0)] ---> /\ diff --git a/Graphics/Implicit/Export.hs b/Graphics/Implicit/Export.hs index 637a366..d3f1690 100644 --- a/Graphics/Implicit/Export.hs +++ b/Graphics/Implicit/Export.hs @@ -6,7 +6,7 @@ -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} --- FIXME: Required. why? +-- Allow us to use real types in the type constraints. {-# LANGUAGE FlexibleContexts #-} module Graphics.Implicit.Export (writeObject, formatObject, writeSVG, writeSTL, writeBinSTL, writeOBJ, writeTHREEJS, writeGCodeHacklabLaser, writeDXF2, writeSCAD2, writeSCAD3, writePNG) where @@ -31,7 +31,7 @@ import qualified Graphics.Implicit.Export.NormedTriangleMeshFormats as NormedTri import qualified Graphics.Implicit.Export.SymbolicFormats as SymbolicFormats (scad2, scad3) import qualified Codec.Picture as ImageFormatCodecs (DynamicImage, savePngImage) --- Write an object to a file with LazyText IO, using the given format writer function. +-- | Write an object to a file with LazyText IO, using the given format writer function. writeObject :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution -> (aprox -> Text) -- ^ File Format Writer (Function that formats) @@ -43,7 +43,7 @@ writeObject res formatWriter filename obj = aprox = formatObject res formatWriter obj in LT.writeFile filename aprox --- Serialize an object using the given format writer, which takes the filename and writes to it.. +-- | Serialize an object using the given format writer, which takes the filename and writes to it.. writeObject' :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution -> (FilePath -> aprox -> IO ()) -- ^ File Format writer @@ -53,7 +53,7 @@ writeObject' :: (DiscreteAproxable obj aprox) writeObject' res formatWriter filename obj = formatWriter filename (discreteAprox res obj) --- Serialize an object using the given format writer. no file target implied. +-- | Serialize an object using the given format writer. No file target is implied. formatObject :: (DiscreteAproxable obj aprox) => ℝ -- ^ Resolution -> (aprox -> Text) -- ^ File Format Writer (Function that formats) diff --git a/Graphics/Implicit/Export/DiscreteAproxable.hs b/Graphics/Implicit/Export/DiscreteAproxable.hs index c4953b4..fc8567b 100644 --- a/Graphics/Implicit/Export/DiscreteAproxable.hs +++ b/Graphics/Implicit/Export/DiscreteAproxable.hs @@ -5,8 +5,8 @@ -- Allow our DiscreteAproxable class to handle multiple parameters. {-# LANGUAGE MultiParamTypeClasses #-} --- FIXME: why is this here? -{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} +-- For the instance declaration of DiscreteAproxable SymbolicObj2 [Polyline] +{-# LANGUAGE FlexibleInstances #-} -- | A module for retrieving approximate represententations of objects. module Graphics.Implicit.Export.DiscreteAproxable (DiscreteAproxable, discreteAprox) where @@ -19,7 +19,9 @@ import Graphics.Implicit.Definitions (ℝ, ℝ2, SymbolicObj2, SymbolicObj3, Pol import Graphics.Implicit.ObjectUtil (getImplicit2, getImplicit3, getBox2, getBox3) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) + import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) + import Graphics.Implicit.Export.Util (normTriangle) -- We are the only ones that use this. @@ -28,6 +30,7 @@ import Graphics.Implicit.Export.RayTrace (Color(Color), Camera(Camera), Light(Li import Codec.Picture (DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8), generateImage) import Data.VectorSpace ((^+^), (^/), (*^), (^-^)) + import Data.AffineSpace ((.-^), (.+^)) default (ℝ) @@ -51,6 +54,7 @@ instance DiscreteAproxable SymbolicObj3 NormedTriangleMesh where instance DiscreteAproxable SymbolicObj3 DynamicImage where discreteAprox _ symbObj = ImageRGBA8 $ generateImage pixelRenderer (round w) (round h) where + -- | Size of the image to produce. (w,h) = (150, 150) :: ℝ2 obj = getImplicit3 symbObj box@((x1,y1,z1), (_,y2,z2)) = getBox3 symbObj @@ -62,6 +66,7 @@ instance DiscreteAproxable SymbolicObj3 DynamicImage where camera = Camera (x1-deviation*(2.2), avY, avZ) (0, -1, 0) (0,0, -1) 1.0 lights = [Light (x1-deviation*(1.5), y1 - (0.4)*(y2-y1), avZ) ((0.03)*deviation) ] scene = Scene obj (Color 200 200 230 255) lights (Color 255 255 255 0) + -- | passed to generateImage, it's external, and determines this type. pixelRenderer :: Int -> Int -> PixelRGBA8 pixelRenderer a b = renderScreen ((fromIntegral a)/w - (0.5)) ((fromIntegral b)/h - (0.5)) @@ -92,11 +97,13 @@ instance DiscreteAproxable SymbolicObj2 [Polyline] where instance DiscreteAproxable SymbolicObj2 DynamicImage where discreteAprox _ symbObj = ImageRGBA8 $ generateImage pixelRenderer (round w) (round h) where + -- | Size of the image to produce. (w,h) = (150, 150) :: ℝ2 obj = getImplicit2 symbObj (p1@(x1,_), p2@(_,y2)) = getBox2 symbObj (dx, dy) = p2 ^-^ p1 dxy = max dx dy + -- | passed to generateImage, it's external, and determines this type. pixelRenderer :: Int -> Int -> PixelRGBA8 pixelRenderer mya myb = mycolor where diff --git a/Graphics/Implicit/Export/MarchingSquares.hs b/Graphics/Implicit/Export/MarchingSquares.hs index 462d294..eec0aac 100644 --- a/Graphics/Implicit/Export/MarchingSquares.hs +++ b/Graphics/Implicit/Export/MarchingSquares.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.Export.MarchingSquares (getContour) where import Prelude(Bool(True, False), ceiling, (/), (+), (-), filter, map, ($), (*), (/=), (<=), (>), splitAt, div, unzip, length, (++), (<), (++), head, ceiling, concat, div, max, not, null, (||), Eq, fromIntegral, floor) -import Graphics.Implicit.Definitions (ℕ, ℝ, ℝ2, Polyline, Obj2, (⋯/), (⋯*)) +import Graphics.Implicit.Definitions (ℕ, ℝ, ℝ2, both, Polyline, Obj2, (⋯/), (⋯*)) import Data.VectorSpace ((^-^), (^+^)) @@ -24,10 +24,6 @@ import Graphics.Implicit.Export.Render.HandlePolylines (reducePolyline) -- Each step on the Y axis is done in parallel using Control.Parallel.Strategies import Control.Parallel.Strategies (using, rdeepseq, parBuffer, parList) --- apply a function to both items in the provided tuple. -both :: forall t b. (t -> b) -> (t, t) -> (b, b) -both f (x,y) = (f x, f y) - -- getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ2 -> Obj2 -> [Polyline] getContour p1 p2 res obj = diff --git a/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs b/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs index 9fdb55b..46b1800 100644 --- a/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/NormedTriangleMeshFormats.hs @@ -12,25 +12,26 @@ import Prelude(($), map, (+), (.), (*), length, (-), return) import Graphics.Implicit.Definitions (NormedTriangle(NormedTriangle), NormedTriangleMesh(NormedTriangleMesh), ℝ3) import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, mconcat, buildInt) +-- | Generate a .obformat file from a NormedTriangleMesh obj :: NormedTriangleMesh -> Text obj (NormedTriangleMesh normedtriangles) = toLazyText $ vertcode <> normcode <> trianglecode where - -- A vertex line; v (0.0, 0.0, 1.0) = "v 0.0 0.0 1.0\n" + -- | A vertex line; v (0.0, 0.0, 1.0) = "v 0.0 0.0 1.0\n" v :: ℝ3 -> Builder v (x,y,z) = "v " <> bf x <> " " <> bf y <> " " <> bf z <> "\n" - -- A normal line; n (0.0, 0.0, 1.0) = "vn 0.0 0.0 1.0\n" + -- | A normal line; n (0.0, 0.0, 1.0) = "vn 0.0 0.0 1.0\n" n :: ℝ3 -> Builder n (x,y,z) = "vn " <> bf x <> " " <> bf y <> " " <> bf z <> "\n" verts = do - -- extract the vertices for each triangle - -- recall that a normed triangle is of the form ((vert, norm), ...) + -- | Extract the vertices for each triangle + -- recall that a normed triangle is of the form ((vert, norm), ...) NormedTriangle ((a,_),(b,_),(c,_)) <- normedtriangles - -- The vertices from each triangle take up 3 position in the resulting list + -- | The vertices from each triangle take up 3 position in the resulting list [a,b,c] norms = do - -- extract the normals for each triangle + -- | extract the normals for each triangle NormedTriangle ((_,a),(_,b),(_,c)) <- normedtriangles - -- The normals from each triangle take up 3 position in the resulting list + -- | The normals from each triangle take up 3 position in the resulting list [a,b,c] vertcode = mconcat $ map v verts normcode = mconcat $ map n norms diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 118c051..c5de4f3 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -88,6 +88,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = pYs = [ y1 + ry*n | n <- [0.. fromℕtoℝ ny] ] pZs = [ z1 + rz*n | n <- [0.. fromℕtoℝ nz] ] + -- | Perform a function on every point in a 3D grid. par3DList :: forall t. NFData t => ℕ -> ℕ -> ℕ -> ((ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> (ℕ -> ℝ) -> ℕ -> t) -> [[[t]]] par3DList lenx leny lenz f = [[[f diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 6298e1c..c37496f 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -475,7 +475,7 @@ obj2UpMap obj2upmod (x:xs) = case x of a -> a : obj2UpMap obj2upmod xs obj2UpMap _ [] = [] -toInterval :: Bool -> ℝ -> (ℝ, ℝ) +toInterval :: Bool -> ℝ -> ℝ2 toInterval center h = if center then (-h/2, h/2) diff --git a/Graphics/Implicit/Primitives.hs b/Graphics/Implicit/Primitives.hs index 3e7c323..45450cd 100644 --- a/Graphics/Implicit/Primitives.hs +++ b/Graphics/Implicit/Primitives.hs @@ -265,7 +265,7 @@ rotateExtrude = RotateExtrude extrudeOnEdgeOf :: SymbolicObj2 -> SymbolicObj2 -> SymbolicObj3 extrudeOnEdgeOf = ExtrudeOnEdgeOf -rotate3 :: (ℝ, ℝ, ℝ) -> SymbolicObj3 -> SymbolicObj3 +rotate3 :: ℝ3 -> SymbolicObj3 -> SymbolicObj3 rotate3 = Rotate3 rotate3V :: ℝ -> ℝ3 -> SymbolicObj3 -> SymbolicObj3 @@ -275,7 +275,7 @@ rotate3V = Rotate3V pack3 :: ℝ2 -> ℝ -> [SymbolicObj3] -> Maybe SymbolicObj3 pack3 (dx, dy) sep objs = let - boxDropZ :: forall t t1 t2 t3 t4 t5. ((t2, t3, t), (t4, t5, t1)) -> ((t2, t3), (t4, t5)) + boxDropZ :: (ℝ3,ℝ3) -> (ℝ2,ℝ2) boxDropZ ((a,b,_),(d,e,_)) = ((a,b),(d,e)) withBoxes :: [(Box2, SymbolicObj3)] withBoxes = map (\obj -> ( boxDropZ $ getBox3 obj, obj)) objs diff --git a/implicit.cabal b/implicit.cabal index 0cedba2..9f6404c 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -81,9 +81,9 @@ Library Graphics.Implicit.ExtOpenScad.Parser.Util Graphics.Implicit.ExtOpenScad.Eval.Statement Graphics.Implicit.ExtOpenScad.Eval.Expr - Graphics.Implicit.ExtOpenScad.Util.StateC Graphics.Implicit.ExtOpenScad.Util.ArgParser Graphics.Implicit.ExtOpenScad.Util.OVal + Graphics.Implicit.ExtOpenScad.Util.StateC -- Historic, but functional. Should be merged into MarchingSquaresFill. -- Graphics.Implicit.Export.MarchingSquares Graphics.Implicit.Export.MarchingSquaresFill