From 32ee7783fc22da102c6ddd84b12a30a3ce639589 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 28 Apr 2019 21:08:34 +0100 Subject: [PATCH 01/24] new contributor. --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 96402a94..412a1fed 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3,6 +3,7 @@ A big thanks to our past and present contributors: shkoo -- Nils McCarthy -- nils@shkoo.com diffoperator -- Nikhil Sarda -- nikhilsarda.iitkgp@gmail.com +afcaddy -- ?? -- ?? matthewSorensen -- Matthew D Sorensen krakrjak -- Zac Slade -- krakrjak@gmail.com bergey -- Daniel Bergey -- bergey@teallabs.org From 6b1ac2db8f8add899355b4faa699de1a0d1c910e Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 28 Apr 2019 21:34:00 +0100 Subject: [PATCH 02/24] reorder, and clean up a few 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 a771d78c..462d2940 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 831ffc40dc5a9b0478a41a1308b01511451ab2ec Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 17:30:42 +0100 Subject: [PATCH 03/24] blah --- Graphics/Implicit/Definitions.hs | 47 +++-------- Graphics/Implicit/Export/RayTrace.hs | 33 ++++---- Graphics/Implicit/Export/Render.hs | 77 +++++++++---------- Graphics/Implicit/Export/Render/GetSegs.hs | 4 +- Graphics/Implicit/Export/Render/RefineSegs.hs | 15 ++-- .../Implicit/Export/Render/TesselateLoops.hs | 12 +-- Graphics/Implicit/Export/SymbolicFormats.hs | 21 ++--- Graphics/Implicit/Export/SymbolicObj2.hs | 22 +++--- Graphics/Implicit/Export/TextBuilderUtils.hs | 6 +- .../Implicit/Export/TriangleMeshFormats.hs | 7 +- Graphics/Implicit/Export/Util.hs | 18 +++-- Graphics/Implicit/ExtOpenScad/Default.hs | 13 ++-- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 15 ++-- Graphics/Implicit/ExtOpenScad/Primitives.hs | 11 +-- Graphics/Implicit/ExtOpenScad/Util/OVal.hs | 7 +- Graphics/Implicit/MathUtil.hs | 22 +++--- Graphics/Implicit/ObjectUtil/GetBox2.hs | 23 +++--- Graphics/Implicit/ObjectUtil/GetBox3.hs | 10 +-- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 4 +- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 16 ++-- implicit.cabal | 6 +- programs/Benchmark.hs | 8 +- programs/extopenscad.hs | 8 +- programs/implicitsnap.hs | 8 +- 24 files changed, 201 insertions(+), 212 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index df16aae1..58b14732 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -15,13 +15,11 @@ -- Definitions of the types used when modeling, and a few operators. module Graphics.Implicit.Definitions ( - ℝ, + module R, + module N, ℝ2, - ℝ3, - minℝ, - ℕ, - Fastℕ, both, + ℝ3, allthree, (⋅), (⋯*), @@ -75,40 +73,24 @@ module Graphics.Implicit.Definitions ( ExtrudeOnEdgeOf, RotateExtrude), Rectilinear2, - Rectilinear3, - fromℕtoℝ, - fromFastℕtoℝ, - fromFastℕ, + Rectilinear3 ) where -import Prelude (Show, Double, Integer, Int, Either, show, (*), (/), fromIntegral) +import Prelude (Show, Either, show, (*), (/)) import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) --- Let's make things a bit nicer. +import Graphics.Implicit.RationalUtil as R (ℚ(minℝ, π, sqrt, cbrt, powℝ, powℝℝ, exp, log, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, fromℝ, toℝ, normalizeℝ, normalizeℝ2, normalizeℝ3, powℝ, (%), infty, neginfty), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) + +import Graphics.Implicit.IntegralUtil as N (ℕ, Fastℕ, fromℕ, toℕ, fromFastℕ, toFastℕ) + -- 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 = (ℝ,ℝ,ℝ) -minℝ :: ℝ --- for Floats. ---minℝ = 0.00000011920928955078125 * 2 - --- for Doubles. -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) @@ -121,17 +103,6 @@ allthree f (x,y,z) = (f x, f y, f z) (⋅) :: InnerSpace a => a -> a -> Scalar a (⋅) = (<.>) --- Wrap the functions that convert datatypes. - -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/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index a892535e..61b437da 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -9,21 +9,27 @@ module Graphics.Implicit.Export.RayTrace( dynamicImage, Color, average, Camera(C 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 Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) +-- Our number system, and the definition of a 3D object. +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋅), Obj3, normalizeℝ3, sqrt) + import Control.Monad (guard, return) + import Control.Arrow ((***)) -import Data.VectorSpace (Scalar, magnitude, (^+^), (*^), normalized, (^-^), InnerSpace) + +import Data.VectorSpace (magnitudeSq, (^+^), (*^), (^-^), InnerSpace) + import Data.Cross (cross3) --- Definitions +import Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) data Camera = Camera ℝ3 ℝ3 ℝ3 ℝ deriving Show +-- | A ray. source point, and vector. data Ray = Ray ℝ3 ℝ3 deriving Show +-- | A light source. source point, and.. ? data Light = Light ℝ3 ℝ deriving Show @@ -39,8 +45,9 @@ dynamicImage = ImageRGBA8 -- Math -vectorDistance :: ℝ3 -> ℝ3 -> Scalar ℝ3 -vectorDistance a b = magnitude (b-a) +-- | The distance traveled by a line segment from the first point to the second point. +vectorDistance :: ℝ3 -> ℝ3 -> ℝ +vectorDistance a b = sqrt $ magnitudeSq (b-a) colorMult :: Pixel8 -> PixelRGBA8 -> PixelRGBA8 s `colorMult` (PixelRGBA8 a b c d) = color (s `mult` a) (s `mult` b) (s `mult` c) d @@ -68,12 +75,12 @@ cameraRay (Camera p vx vy f) (x,y) = let v = vx `cross3` vy p' = p ^+^ f*^v ^+^ x*^vx ^+^ y*^vy - n = normalized (p' ^-^ p) + n = normalizeℝ3 (p' ^-^ p) in Ray p' n rayFromTo :: ℝ3 -> ℝ3 -> Ray -rayFromTo p1 p2 = Ray p1 (normalized $ p2 ^-^ p1) +rayFromTo p1 p2 = Ray p1 (normalizeℝ3 $ p2 ^-^ p1) rayBounds :: Ray -> (ℝ3, ℝ3) -> ℝ2 rayBounds ray box = @@ -90,7 +97,6 @@ rayBounds ray box = -- Intersection - intersection :: Ray -> ((ℝ,ℝ), ℝ) -> ℝ -> Obj3 -> Maybe ℝ3 intersection r@(Ray p v) ((a, aval),b) res obj = let @@ -134,7 +140,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 @@ -144,15 +149,15 @@ traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultCo Light lightPos lightIntensity <- lights let ray'@(Ray _ v) = rayFromTo p lightPos - v' = normalized v + v' = normalizeℝ3 v guard . not $ intersects ray' ((0, obj p),20) step obj let pval = obj p 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 = normalizeℝ3 deriv + unitV = normalizeℝ3 v' proj :: InnerSpace v => v -> v -> v proj a' b' = (a'⋅b')*^b' dist = vectorDistance p lightPos @@ -166,5 +171,3 @@ traceRay ray@(Ray cameraP cameraV) step box (Scene obj objColor lights defaultCo 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 74a37e1c..99fe8632 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, ($), (+), (*), max, div, tail, map, concat, realToFrac, (==), (||), filter, not, reverse, (.), Eq, concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), both, allthree) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, both, ℝ3, allthree, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), fromℕtoℝ, fromℕ, fromFastℕ, Fastℕ) import Data.VectorSpace ((^-^)) @@ -81,21 +81,21 @@ 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*(fromℕtoℝ n) | n <- [0.. nx] ] + pYs = [ y1 + ry*(fromℕtoℝ n) | n <- [0.. ny] ] + pZs = [ z1 + rz*(fromℕtoℝ n) | n <- [0.. 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 + `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ lenz) (32::Fastℕ)) 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) @@ -106,21 +106,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 . fromIntegral $ div nz 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) (32::Fastℕ)) 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 $ fromIntegral $ div ny 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) 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 $ fromIntegral $ div nx 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nx) (32::Fastℕ)) rdeepseq -- Calculate segments for each side segsZ = [[[ @@ -133,7 +133,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 $ fromIntegral $ div nz 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) (32::Fastℕ)) rdeepseq segsY = [[[ map2 (inj2 y0) $ getSegs (x0,z0) (x1',z1') (obj *$* y0) @@ -145,7 +145,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 $ fromIntegral $ div ny 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) rdeepseq segsX = [[[ map2 (inj1 x0) $ getSegs (y0,z0) (y1',z1') (obj $** x0) @@ -157,7 +157,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 $ fromIntegral $ div nx 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nx) (32::Fastℕ)) rdeepseq -- (3) & (4) : get and tesselate loops sqTris = [[[ @@ -181,8 +181,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = ]| segZ' <- segsZ | segZT <- tail segsZ | segY' <- segsY | segX' <- segsX - ] `using` parBuffer (max 1 $ fromIntegral $ div nz 32) rdeepseq - + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) (32::Fastℕ)) rdeepseq in -- (5) merge squares, etc cleanupTris . mergedSquareTris . concat . concat $ concat sqTris @@ -202,54 +201,51 @@ cleanupTris tris = isDegenerateTri (a, b, c) = isDegenerateTriFloat (floatPoint a, floatPoint b, floatPoint c) in filter (not . isDegenerateTri) tris --- getContour gets a polyline describing the edge of a 2D object. +-- | getContour gets a polyline describing the edge of a 2D object. getContour :: ℝ2 -> ℝ2 -> ℝ -> Obj2 -> [Polyline] getContour p1@(x1, y1) p2 res obj = let - -- the size of the region we're being asked to search. + -- | the size of the region we're being asked to search. d = p2 ^-^ p1 - -- How many steps will we take on each axis? + -- | How many steps will we take on each axis? nx :: ℕ ny :: ℕ (nx,ny) = ceiling `both` (d ⋯/ (res,res)) - -- How big are the steps? - (rx,ry) = d ⋯/ (fromIntegral `both` (nx,ny)) + -- | How big are the steps? + (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] ] + -- | the points inside of the region. + 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 - - - -- Evaluate obj to avoid waste in mids, segs, later. + `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ leny) (32::Fastℕ)) rdeepseq + -- | fully evaluate obj to avoid waste in mids, segs, later. objV = par2DList (nx+2) (ny+2) $ \x _ y _ -> obj (x 0, y 0) - -- (1) Calculate mid points on X, and Y axis in 2D space. - + -- | Calculate mid points on X, and Y axis in 2D space. midsY = [[ 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 . fromIntegral $ div ny 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) 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 . fromIntegral $ div nx 32) rdeepseq - - -- Calculate segments for each side + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nx) (32::Fastℕ)) rdeepseq + -- | Calculate segments for each side segs = [[ getSegs (x0,y0) (x1',y1') obj (objX0Y0, objX1Y0, objX0Y1, objX1Y1) @@ -258,10 +254,11 @@ 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 . fromIntegral $ div ny 32) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) rdeepseq in - cleanLoopsFromSegs . concat $ concat segs -- (5) merge squares, etc + -- | merge squares, etc + cleanLoopsFromSegs . concat $ concat segs -- utility functions diff --git a/Graphics/Implicit/Export/Render/GetSegs.hs b/Graphics/Implicit/Export/Render/GetSegs.hs index ffb18be0..e3a6f06a 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(Eq, Bool(True, False), (+), (*), (/=), map, (.), filter, ($), (<=)) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline) +import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline, sqrt) import Graphics.Implicit.Export.Render.RefineSegs (refine) import Graphics.Implicit.Export.Util (centroid) diff --git a/Graphics/Implicit/Export/Render/RefineSegs.hs b/Graphics/Implicit/Export/Render/RefineSegs.hs index f5b3e623..1fe5a713 100644 --- a/Graphics/Implicit/Export/Render/RefineSegs.hs +++ b/Graphics/Implicit/Export/Render/RefineSegs.hs @@ -5,12 +5,13 @@ -- export one function, which refines polylines. module Graphics.Implicit.Export.Render.RefineSegs (refine) where -import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, tail, sqrt, (<=)) +import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, tail, (<=)) + +import Graphics.Implicit.Definitions (ℝ, ℝ2, minℝ, ℕ, Obj2, (⋅), normalizeℝ2, sqrt) -import Graphics.Implicit.Definitions (ℝ, ℝ2, minℝ, ℕ, Obj2, (⋅)) import Graphics.Implicit.Export.Util (centroid) -import Data.VectorSpace (normalized, magnitude, (^-^), (^*), (^+^)) +import Data.VectorSpace (magnitudeSq, (^-^), (^*), (^+^)) -- The purpose of refine is to add detail to a polyline aproximating -- the boundary of an implicit function and to remove redundant points. @@ -30,7 +31,7 @@ detail' res obj [p1@(x1,y1), p2@(x2,y2)] | (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) > r detail' _ _ a = a -- detail adds new points to a polyline to add more detail. - +-- FIXME: all of the magic numbers. detail :: ℕ -> ℝ -> (ℝ2 -> ℝ) -> [ℝ2] -> [ℝ2] detail n res obj [p1, p2] | n < 2 = let @@ -39,7 +40,7 @@ detail n res obj [p1, p2] | n < 2 = in if abs midval < res / 40 then [p1, p2] else let - normal = (\(a,b) -> (b, -a)) $ normalized (p2 ^-^ p1) + normal = (\(a,b) -> (b, -a)) $ normalizeℝ2 (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 @@ -50,7 +51,7 @@ detail n res obj [p1, p2] | n < 2 = 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 + 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) @@ -68,7 +69,7 @@ simplify _ = {-simplify3 . simplify2 res . -} simplify1 simplify1 :: [ℝ2] -> [ℝ2] simplify1 (a:b:c:xs) = - if abs ( ((b ^-^ a) ⋅ (c ^-^ a)) - magnitude (b ^-^ a) * magnitude (c ^-^ a) ) <= minℝ + if abs ( ((b ^-^ a) ⋅ (c ^-^ a)) - (sqrt $ magnitudeSq (b ^-^ a)) * (sqrt $ magnitudeSq (c ^-^ a)) ) <= minℝ then simplify1 (a:c:xs) else a : simplify1 (b:c:xs) simplify1 a = a diff --git a/Graphics/Implicit/Export/Render/TesselateLoops.hs b/Graphics/Implicit/Export/Render/TesselateLoops.hs index efc67e01..a7eabe61 100644 --- a/Graphics/Implicit/Export/Render/TesselateLoops.hs +++ b/Graphics/Implicit/Export/Render/TesselateLoops.hs @@ -6,25 +6,25 @@ 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, (⋅), sqrt, normalizeℝ3) import Graphics.Implicit.Export.Render.Definitions (TriSquare(Tris, Sq)) import Graphics.Implicit.Export.Util (centroid) -import Data.VectorSpace (normalized, (^-^), (^+^), magnitude, (^/), (^*)) +import Data.VectorSpace ((^-^), (^+^), magnitudeSq, (^/), (^*)) import Data.List (genericLength) import Data.Cross (cross3) + tesselateLoop :: ℝ -> Obj3 -> [[ℝ3]] -> [TriSquare] tesselateLoop _ _ [] = [] tesselateLoop _ _ [[a,b],[_,c],[_,_]] = return $ Tris [(a,b,c)] - {- #____# #____# | | | | @@ -53,8 +53,8 @@ tesselateLoop res obj [as@(_:_:_:_),[_,_], bs@(_:_:_:_), [_,_] ] | length as == tesselateLoop _ _ [[a,_],[b,_],[c,_],[d,_]] | centroid [a,c] == centroid [b,d] = let - b1 = normalized $ a ^-^ b - b2 = normalized $ c ^-^ b + b1 = normalizeℝ3 $ a ^-^ b + b2 = normalizeℝ3 $ c ^-^ b b3 = b1 `cross3` b2 in [Sq (b1,b2,b3) (a ⋅ b3) (a ⋅ b1, c ⋅ b1) (a ⋅ b2, c ⋅ b2) ] @@ -80,7 +80,7 @@ tesselateLoop res obj pathSides = return $ Tris $ midval = obj mid preNormal = foldl1 (^+^) [ a `cross3` b | (a,b) <- zip path (tail path ++ [head path]) ] - preNormalNorm = magnitude preNormal + preNormalNorm = sqrt $ magnitudeSq preNormal normal = preNormal ^/ preNormalNorm deriv = (obj (mid ^+^ (normal ^* (res/100)) ) ^-^ midval)/res*100 mid' = mid ^-^ normal ^* (midval/deriv) diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index e619e418..905272f5 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -8,14 +8,16 @@ -- 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, ($!), (-), (/), 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.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 @@ -23,12 +25,12 @@ scad2 res obj = toLazyText $ runReader (buildS2 obj) res scad3 :: ℝ -> SymbolicObj3 -> Text scad3 res obj = toLazyText $ runReader (buildS3 obj) res --- used by rotate2 and rotate3 +-- | used by rotate2 and rotate3 rad2deg :: ℝ -> ℝ -rad2deg r = r * (180/pi) +rad2deg r = r * 180 / π --- 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 +48,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 +96,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? @@ -112,8 +114,7 @@ buildS3(EmbedBoxedObj3 _) = error "cannot provide roundness when exporting opens 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. - +-- | Now the 2D objects/transforms. buildS2 :: SymbolicObj2 -> Reader ℝ Builder buildS2 (RectR r (x1,y1) (x2,y2)) | r == 0 = call "translate" [bf x1, bf y1] [ diff --git a/Graphics/Implicit/Export/SymbolicObj2.hs b/Graphics/Implicit/Export/SymbolicObj2.hs index 73b49dbf..a0f748ff 100644 --- a/Graphics/Implicit/Export/SymbolicObj2.hs +++ b/Graphics/Implicit/Export/SymbolicObj2.hs @@ -11,9 +11,9 @@ module Graphics.Implicit.Export.SymbolicObj2 (symbolicGetOrientedContour, symbolicGetContour, symbolicGetContourMesh) where -import Prelude(map, ($), (-), (/), (+), (>), (*), (.), reverse, cos, pi, sin, max, fromInteger, ceiling) +import Prelude(map, ($), (-), (/), (+), (>), (*), (.), reverse, max, fromInteger, fromIntegral, ceiling) -import Graphics.Implicit.Definitions (ℝ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline, Polytri, (⋯*)) +import Graphics.Implicit.Definitions (ℝ, Fastℕ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline, Polytri, (⋯*), cos, sin, π, sqrt) import Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) @@ -23,7 +23,7 @@ import Graphics.Implicit.Export.Symbolic.Rebound2 (rebound2) import qualified Graphics.Implicit.Export.Render as Render (getContour) -import Data.VectorSpace ((^/), magnitude) +import Data.VectorSpace ((^/), magnitudeSq) symbolicGetOrientedContour :: ℝ -> SymbolicObj2 -> [Polyline] symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res symbObj @@ -35,16 +35,16 @@ symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res sym orient points@(x:y:_) = let v = (\(a,b) -> (b, -a)) (y - x) - dv = v ^/ (magnitude v / res / 0.1) + dv = v ^/ ((sqrt $ magnitudeSq v) / res / 0.1) in if obj (x + dv) - obj x > 0 then points else 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 - n :: ℝ - n = max 5 (fromInteger . ceiling $ 2*pi*r/res) +symbolicGetContour res (Circle r) = [[ ( r*cos(2*π*(fromIntegral m)/(fromIntegral n)), r*sin(2*π*(fromIntegral m)/(fromIntegral n)) ) | m <- [0.. n] ]] where + n :: Fastℕ + n = max 5 (fromInteger . ceiling $ 2*π*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 where sc = max a b @@ -60,11 +60,11 @@ symbolicGetContourMesh res (Scale2 s@(a,b) obj) = map (\(c,d,e) -> (c ⋯* s, d symbolicGetContourMesh _ (RectR 0 (x1,y1) (x2,y2)) = [((x1,y1), (x2,y1), (x2,y2)), ((x2,y2), (x1,y2), (x1,y1)) ] symbolicGetContourMesh res (Circle r) = [ ((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)) + (r*cos(2*π*(fromIntegral m)/(fromIntegral n)), r*sin(2*π*(fromIntegral m)/(fromIntegral n))), + (r*cos(2*π*((fromIntegral m)+1)/(fromIntegral n)), r*sin(2*π*((fromIntegral m)+1)/(fromIntegral n))) )| m <- [0.. n-1] ] where - n :: ℝ - n = max 5 (fromInteger . ceiling $ 2*pi*r/res) + n :: Fastℕ + n = max 5 (fromInteger . ceiling $ 2*π*r/res) symbolicGetContourMesh res obj = case rebound2 (getImplicit2 obj, getBox2 obj) of (obj', (a,b)) -> getContourMesh a b (res,res) obj' diff --git a/Graphics/Implicit/Export/TextBuilderUtils.hs b/Graphics/Implicit/Export/TextBuilderUtils.hs index 1a6d0b8d..6bc93f9b 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,11 +42,11 @@ 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 -buildTruncFloat = formatRealFloat Fixed $ Just 4 +buildTruncFloat value = formatRealFloat Fixed (Just 4) (fromℝtoFloat value) buildℕ :: ℕ -> Builder buildℕ = decimal diff --git a/Graphics/Implicit/Export/TriangleMeshFormats.hs b/Graphics/Implicit/Export/TriangleMeshFormats.hs index 1590000b..939e40a8 100644 --- a/Graphics/Implicit/Export/TriangleMeshFormats.hs +++ b/Graphics/Implicit/Export/TriangleMeshFormats.hs @@ -13,7 +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 (TriangleMesh, ℕ, ℝ3) +import Graphics.Implicit.Definitions (TriangleMesh, ℕ, ℝ3, normalizeℝ3) + import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildℕ) import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) @@ -26,12 +27,12 @@ import Data.ByteString (replicate) import Data.ByteString.Lazy (ByteString) import Data.Storable.Endian (LittleEndian(LE)) -import Data.VectorSpace (normalized, negateV) +import Data.VectorSpace (negateV) import Data.Cross (cross3) normal :: (ℝ3,ℝ3,ℝ3) -> ℝ3 normal (a,b,c) = - normalized $ (b + negateV a) `cross3` (c + negateV a) + normalizeℝ3 $ (b + negateV a) `cross3` (c + negateV a) stl :: TriangleMesh -> Text stl triangles = toLazyText $ stlHeader <> mconcat (map triangle triangles) <> stlFooter diff --git a/Graphics/Implicit/Export/Util.hs b/Graphics/Implicit/Export/Util.hs index 73468b5f..3b0a6763 100644 --- a/Graphics/Implicit/Export/Util.hs +++ b/Graphics/Implicit/Export/Util.hs @@ -5,15 +5,17 @@ -- FIXME: why are these needed? {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances #-} --- Functions to make meshes/polylines finer. +{-# LANGUAGE TypeFamilies #-} + +-- | Functions to make meshes/polylines finer. module Graphics.Implicit.Export.Util (normTriangle, normVertex, centroid) where -import Prelude(Fractional, (/), (-), ($), foldl, recip, realToFrac, length) +import Prelude((/), (-), ($), foldl, recip, length, (.)) -import Graphics.Implicit.Definitions (ℝ, ℝ3, Obj3, Triangle, NormedTriangle) +import Graphics.Implicit.Definitions (ℝ, ℝ3, Obj3, Triangle, NormedTriangle, normalizeℝ3, fromℕtoℝ, toℕ) -import Data.VectorSpace (VectorSpace, Scalar, (^+^), (*^), (^/), (^-^), normalized, zeroV) +import Data.VectorSpace (VectorSpace, Scalar, (^+^), (*^), (^/), (^-^), zeroV) normTriangle :: ℝ -> Obj3 -> Triangle -> NormedTriangle normTriangle res obj (a,b,c) = @@ -38,14 +40,14 @@ normVertex res obj p = dx = d (1, 0, 0) dy = d (0, 1, 0) dz = d (0, 0, 1) - in (p, normalized (dx,dy,dz)) + in (p, normalizeℝ3 (dx,dy,dz)) -centroid :: (VectorSpace v, Fractional (Scalar v)) => [v] -> v +centroid :: (VectorSpace v, s ~ (Scalar v), s ~ ℝ) => [v] -> v centroid pts = (norm *^) $ foldl (^+^) zeroV pts where - norm :: Fractional a => a - norm = recip $ realToFrac $ length pts + norm :: ℝ + norm = recip . fromℕtoℝ . toℕ $ length pts {-# INLINABLE centroid #-} {--- If we need to make a 2D mesh finer... diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 5543160d..136f471b 100644 --- a/Graphics/Implicit/ExtOpenScad/Default.hs +++ b/Graphics/Implicit/ExtOpenScad/Default.hs @@ -10,9 +10,10 @@ 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 Prelude (String, Bool(True, False), Maybe(Just, Nothing), ($), (++), map, + abs, signum, fromInteger, (.), floor, ceiling, round, max, min, flip, (<), (>), (<=), (>=), (==), (/=), (&&), (||), not, show, foldl, (*), (/), mod, (+), zipWith, (-), otherwise) -import Graphics.Implicit.Definitions (ℝ, ℕ) +import Graphics.Implicit.Definitions (ℝ, ℕ, cos, sin, tan, acos, asin, atan, sinh, cosh, tanh, π, sqrt, exp, powℝℝ, atan2, log) 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) @@ -31,10 +32,12 @@ defaultObjects = fromList $ -- Missing standard ones: -- rand, lookup, +-- FIXME: what standard? + defaultConstants :: [(String, OVal)] defaultConstants = map (\(a,b) -> (a, toOObj (b::ℝ) )) - [("pi", pi)] + [("pi", π)] defaultFunctions :: [(String, OVal)] defaultFunctions = map (\(a,b) -> (a, toOObj ( b :: ℝ -> ℝ))) @@ -66,7 +69,7 @@ defaultFunctions2 = map (\(a,b) -> (a, toOObj (b :: ℝ -> ℝ -> ℝ) )) ("max", max), ("min", min), ("atan2", atan2), - ("pow", (**)) + ("pow", powℝℝ) ] defaultFunctionsSpecial :: [(String, OVal)] @@ -93,7 +96,7 @@ defaultPolymorphicFunctions = ("/", divide), ("-", toOObj sub), ("%", toOObj omod), - ("^", toOObj ((**) :: ℝ -> ℝ -> ℝ)), + ("^", toOObj (powℝℝ :: ℝ -> ℝ -> ℝ)), ("negate", toOObj negatefun), ("index", toOObj index), ("splice", toOObj osplice), diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 1c1b4e33..19863346 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -5,7 +5,10 @@ -- 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) + +-- for a 'power' operator, from our math library. +import Graphics.Implicit.Definitions (powℝ) -- The parsec parsing library. import Text.ParserCombinators.Parsec (GenParser, string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try) @@ -29,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 `powℝ` 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 `powℝ` read c) *<|> do a <- many1 digit _ <- char '.' @@ -44,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 `powℝ` read c) *<|> do a <- many1 digit _ <- char '.' @@ -52,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 `powℝ` 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 `powℝ` read b) *<|> do a <- many1 digit _ <- char '.' diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 6298e1c4..0b27d8d0 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -15,9 +15,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), ($), return, either, id, (-), (==), (&&), (<), (*), cos, sin, pi, (/), (>), const, uncurry, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) +import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), ($), return, either, id, (-), (==), (&&), (<), (*), (/), (>), const, uncurry, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn) -import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, ℕ, SymbolicObj2, SymbolicObj3, fromℕtoℝ) +import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, ℕ, cos, sin, π, SymbolicObj2, SymbolicObj3, fromℕtoℝ) import Graphics.Implicit.ExtOpenScad.Definitions (OVal (OObj2, OObj3), ArgParser) @@ -180,7 +180,7 @@ cylinder = moduleWithoutSuite "cylinder" $ do addObj3 $ if r1 == 1 && r2 == 1 then let 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]]] + [(r*cos θ, r*sin θ )| θ <- [2*π*(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 @@ -200,7 +200,7 @@ circle = moduleWithoutSuite "circle" $ do addObj2 $ if sides < 3 then Prim.circle r else Prim.polygonR 0 $ - [(r*cos θ, r*sin θ )| θ <- [2*pi*(fromℕtoℝ n)/(fromℕtoℝ sides) | n <- [0 .. sides - 1]]] + [(r*cos θ, r*sin θ )| θ <- [2*π*(fromℕtoℝ n)/(fromℕtoℝ sides) | n <- [0 .. sides - 1]]] polygon :: (String, [OVal] -> ArgParser (IO [OVal])) polygon = moduleWithoutSuite "polygon" $ do @@ -269,7 +269,7 @@ translate = moduleWithSuite "translate" $ \children -> do objMap (Prim.translate (x,y)) (Prim.translate (x,y,z)) children deg2rad :: ℝ -> ℝ -deg2rad x = x / 180.0 * pi +deg2rad x = x / 180.0 * π -- This is mostly insane rotate :: (String, [OVal] -> ArgParser (IO [OVal])) @@ -446,6 +446,7 @@ unit = moduleWithSuite "unit" $ \children -> do moduleWithSuite :: t -> t1 -> (t, t1) moduleWithSuite name modArgMapper = (name, modArgMapper) + moduleWithoutSuite :: t -> a -> (t, b -> a) moduleWithoutSuite name modArgMapper = (name, const modArgMapper) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index 8af19a27..b05bbbd5 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -6,12 +6,13 @@ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE FlexibleInstances #-} 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, head, flip, filter, not, return, head) +import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left,Right), Char, String, ($), (.), map, error, (++), show, head, flip, filter, not, return) -import Graphics.Implicit.Definitions(ℝ, ℕ, SymbolicObj2, SymbolicObj3, fromℕtoℝ) +import Graphics.Implicit.Definitions(ℝ, ℕ, SymbolicObj2, SymbolicObj3, fromℝtoℕ, fromℕtoℝ) import Graphics.Implicit.ExtOpenScad.Definitions (OVal(ONum, OBool, OString, OList, OFunc, OUndefined, OModule, OError, OObj2, OObj3)) @@ -42,7 +43,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) = fromℝtoℕ n fromOObj _ = Nothing {-# INLINABLE fromOObj #-} toOObj = ONum . fromℕtoℝ diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index 53be0286..aed214cd 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -9,31 +9,32 @@ 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, (++), head, flip) +import Prelude (Bool, Num, Ord, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), max, min, compare, (<=), fst, snd, (++), head, flip) -import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Box2, (⋅)) +import Graphics.Implicit.Definitions (ℝ, toℝ, ℝ2, ℝ3, π, sqrt, sin, asin, Box2, (⋅), normalizeℝ, normalizeℝ2) import Data.List (sort, sortBy, (!!)) -import Data.VectorSpace (magnitude, normalized, (^-^), (^+^), (*^)) +import Data.VectorSpace (magnitudeSq, (^-^), (^+^), (*^)) -- get the distance between two points. -import Data.AffineSpace (distance) +import Data.AffineSpace (distanceSq) -- | The distance a point p is from a line segment (a,b) distFromLineSeg :: ℝ2 -> (ℝ2, ℝ2) -> ℝ -distFromLineSeg p (a,b) = distance p closest +-- FIXME: distanceSq is stripping off ℝs. why? +distFromLineSeg p (a,b) = sqrt $ toℝ $ distanceSq p closest where ab = b ^-^ a ap = p ^-^ a d :: ℝ - d = normalized ab ⋅ ap + d = normalizeℝ $ ab ⋅ ap -- the closest point to p on the line segment. closest :: ℝ2 closest | d < 0 = a - | d > magnitude ab = b - | otherwise = a ^+^ d *^ normalized ab + | d > sqrt (magnitudeSq ab) = b + | otherwise = a ^+^ d *^ (normalizeℝ2 ab) box3sWithin :: ℝ -> (ℝ3, ℝ3) -> (ℝ3, ℝ3) -> Bool box3sWithin r ((ax1, ay1, az1),(ax2, ay2, az2)) ((bx1, by1, bz1),(bx2, by2, bz2)) = @@ -54,10 +55,9 @@ rmax :: -> ℝ -- ^ second number to round maximum -> ℝ -- ^ resulting number rmax r x y = if abs (x-y) < r - then y - r*sin(pi/4-asin((x-y)/r/sqrt 2)) + r + then y - r*(sin (π/4-(asin((x-y)/r/sqrt(2))))) + r else max x y - -- | Rounded minimum rmin :: ℝ -- ^ radius @@ -65,7 +65,7 @@ rmin :: -> ℝ -- ^ second number to round minimum -> ℝ -- ^ resulting number rmin r x y = if abs (x-y) < r - then y + r*sin(pi/4+asin((x-y)/r/sqrt 2)) - r + then y + r*(sin (π/4+(asin((x-y)/r/sqrt(2))))) - r else min x y -- | Like rmax, but on a list instead of two. diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index d162210a..0c67ba2c 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -5,19 +5,22 @@ -- FIXME: Document what these are for. {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE TypeFamilies #-} module Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) where -import Prelude(Bool, Fractional, (==), (||), unzip, minimum, maximum, ($), filter, not, (.), (/), map, (-), (+), (*), cos, sin, sqrt, min, max, abs, head) +import Prelude(Bool, (==), (||), unzip, minimum, maximum, ($), filter, not, (.), (/), map, (-), (+), (*), min, max, abs, head) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Box2, (⋯*), +-- | our number model, and all of the 2D object types. +import Graphics.Implicit.Definitions (ℝ, ℝ2, Box2, (⋯*), sqrt, cos, sin, infty, neginfty, SymbolicObj2(Shell2, Outset2, Circle, Translate2, Rotate2, UnionR2, Scale2, RectR, PolygonR, Complement2, DifferenceR2, IntersectR2, EmbedBoxedObj2)) -import Data.VectorSpace (magnitude, (^-^), (^+^)) +import Data.VectorSpace (magnitudeSq ,(^-^), (^+^), InnerSpace, Scalar) -- | Is a Box2 empty? -- | Really, this checks if it is one dimensional, which is good enough. @@ -54,10 +57,7 @@ getBox2 (Circle r) = ((-r, -r), (r,r)) getBox2 (PolygonR _ points) = pointsBox points -- (Rounded) CSG getBox2 (Complement2 _) = - ((-infty, -infty), (infty, infty)) - where - infty :: (Fractional t) => t - infty = 1/0 + ((neginfty, neginfty), (infty, infty)) getBox2 (UnionR2 r symbObjs) = outsetBox r $ unionBoxes (map getBox2 symbObjs) getBox2 (DifferenceR2 _ symbObjs) = getBox2 $ head symbObjs @@ -110,8 +110,11 @@ getBox2 (EmbedBoxedObj2 (_,box)) = box -- 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] +getDist2 p (Circle radius) = radius + (distance p) + where + distance :: (InnerSpace v, ℝ ~ Scalar v) => v -> ℝ + distance point = sqrt $ magnitudeSq point +getDist2 p (PolygonR r points) = r + maximum [ sqrt $ magnitudeSq (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) diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index aae74cc4..2b7a4050 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -8,11 +8,12 @@ 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, take) +import Prelude(Eq, Bool(False), 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 (ℝ, 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.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), (⋯*), infty, neginfty, fromFastℕtoℝ, fromFastℕ) + import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getDist2) import Data.Maybe (fromMaybe) @@ -37,10 +38,7 @@ 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 _) = - ((-infty, -infty, -infty), (infty, infty, infty)) - where - infty :: (Fractional t) => t - infty = 1/0 + ((neginfty, neginfty, neginfty), (infty, infty, infty)) getBox3 (UnionR3 r symbObjs) = ((left-r,bot-r,inward-r), (right+r,top+r,out+r)) where boxes = map getBox3 symbObjs diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index 56a04eec..3c965e38 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -10,9 +10,9 @@ 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, (-), (/), (*), (+), mod, length, map, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, (==), maximum, max, head, tail, (.)) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2)) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, (⋯/), sqrt, sin, cos, Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Shell2, Outset2, EmbedBoxedObj2)) import Graphics.Implicit.MathUtil (rminimum, rmaximum, distFromLineSeg) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index dccb3a69..106d8947 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, return, error, head, tail, Num) +import Prelude (Either(Left, Right), abs, (-), (/), (*), (+), max, map, (==), minimum, ($), maximum, const, (.), Bool(True, False), ceiling, floor, return, error, head, tail, Num) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), sqrt, cbrt, cos, sin, π, atan2, Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, - ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR), fromℕtoℝ) + ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR), normalizeℝ3, fromℕtoℝ) import Graphics.Implicit.MathUtil (rmaximum, rminimum, rmax) @@ -21,7 +21,7 @@ import Data.Maybe (fromMaybe, isJust) import qualified Data.Either as Either (either) -import Data.VectorSpace ((^-^), (^+^), (^*), (<.>), normalized) +import Data.VectorSpace ((^-^), (^+^), (^*), (<.>)) import Data.Cross (cross3) @@ -82,7 +82,7 @@ getImplicit3 (Translate3 v symbObj) = getImplicit3 (Scale3 s@(sx,sy,sz) symbObj) = let obj = getImplicit3 symbObj - k = (abs $ sx*sy*sz)**(1/3) + k = cbrt $ abs $ sx*sy*sz in \p -> k * obj (p ⋯/ s) getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = @@ -98,7 +98,7 @@ getImplicit3 (Rotate3 (yz, zx, xy) symbObj) = rotateXY xy $ rotateZX zx $ rotateYZ yz obj getImplicit3 (Rotate3V θ axis symbObj) = let - axis' = normalized axis + axis' = normalizeℝ3 axis obj = getImplicit3 symbObj in \v -> obj $ @@ -138,7 +138,7 @@ getImplicit3 (ExtrudeRM r twist scale translate symbObj height) = rotateVec :: ℝ -> ℝ2 -> ℝ2 rotateVec θ (x,y) = (x*(cos θ) + y*(sin θ), y*(cos θ) - x*(sin θ)) k :: ℝ - k = pi/180 + k = π/180 in \(x,y,z) -> let h = height' (x,y) in rmax r @@ -153,7 +153,7 @@ getImplicit3 (ExtrudeOnEdgeOf symbObj1 symbObj2) = getImplicit3 (RotateExtrude totalRotation round translate rotate symbObj) = let tau :: ℝ - tau = 2 * pi + tau = 2 * π k :: ℝ k = tau / 360 totalRotation' = totalRotation*k diff --git a/implicit.cabal b/implicit.cabal index c97f879b..f0cff8bd 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -35,7 +35,8 @@ Library blaze-svg, storable-endian, JuicyPixels, - transformers + transformers, + integer-gmp Ghc-options: -O2 @@ -71,6 +72,9 @@ Library Graphics.Implicit.ExtOpenScad.Primitives Other-modules: + Graphics.Implicit.RationalFunctions + Graphics.Implicit.IntegralUtil + Graphics.Implicit.RationalUtil Graphics.Implicit.ObjectUtil.GetBox2 Graphics.Implicit.ObjectUtil.GetBox3 Graphics.Implicit.ObjectUtil.GetImplicit2 diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index 06a60bd1..7909ea9e 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), fromIntegral) +import Prelude (($), (*), (/), String, IO, 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 variables defining distance and counting in our world. -import Graphics.Implicit.Definitions (ℝ, Fastℕ) +-- The types for distance and counting in our world, along with some trig functions/values. +import Graphics.Implicit.Definitions (ℝ, Fastℕ, cos, π) -- Haskell representations of objects to benchmark. @@ -40,7 +40,7 @@ object1 :: SymbolicObj3 object1 = extrudeRM 0 (Just twist) Nothing Nothing obj2d_1 (Left 40) where twist :: ℝ -> ℝ - twist h = 35*cos(h*2*pi/60) + twist h = 35*cos(h*2*π/60) -- | another 3D object, for benchmarking. object2 :: SymbolicObj3 diff --git a/programs/extopenscad.hs b/programs/extopenscad.hs index 4b85aa12..c1ab4981 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, 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, 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) @@ -20,7 +20,7 @@ import Graphics.Implicit (runOpenscad, writeSVG, writeDXF2, writeBinSTL, writeOB 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, ℝ) +import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3, ℝ, cbrt, sqrt) -- Use default values when a Maybe is Nothing. import Data.Maybe (fromMaybe) @@ -157,8 +157,8 @@ getRes (varlookup, _, obj:_) = ((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) + ONum qual | qual > 0 -> min (minimum [x,y,z]/2) ((cbrt (x*y*z/qual)) / 22) + _ -> min (minimum [x,y,z]/2) ((cbrt (x*y*z )) / 22) -- | Use a resolution chosen for 2D objects. -- FIXME: magic numbers. getRes (varlookup, obj:_, _) = diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 50b78c11..d0570523 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -15,7 +15,7 @@ -- 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) +import Prelude (IO, Maybe(Just, Nothing), Ord, String, Bool(True, False), Either(Left, Right), Show, ($), (++), (>), (.), (-), (/), (*), min, max, minimum, maximum, show, return) import Control.Applicative ((<|>)) @@ -32,7 +32,7 @@ import Graphics.Implicit.ExtOpenScad.Definitions (OVal (ONum)) 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, ℝ) +import Graphics.Implicit.Definitions (SymbolicObj2, SymbolicObj3, ℝ, cbrt, sqrt) -- Use default values when a Maybe is Nothing. import Data.Maybe (fromMaybe) @@ -99,8 +99,8 @@ getRes (varlookup, _, obj:_) = ((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) + ONum qual | qual > 0 -> min (minimum [x,y,z]/2) ((cbrt (x*y*z/qual)) / 22) + _ -> min (minimum [x,y,z]/2) ((cbrt (x*y*z )) / 22) -- | ... Or use a resolution chosen for 2D objects. -- FIXME: magic numbers. getRes (varlookup, obj:_, _) = From c839829203d871fbdf0ad9589b5a1d1053524a86 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 17:31:59 +0100 Subject: [PATCH 04/24] add integral and rational functions --- Graphics/Implicit/IntegralUtil.hs | 106 +++++++++++ Graphics/Implicit/RationalFunctions.hs | 132 +++++++++++++ Graphics/Implicit/RationalUtil.hs | 248 +++++++++++++++++++++++++ 3 files changed, 486 insertions(+) create mode 100644 Graphics/Implicit/IntegralUtil.hs create mode 100644 Graphics/Implicit/RationalFunctions.hs create mode 100644 Graphics/Implicit/RationalUtil.hs diff --git a/Graphics/Implicit/IntegralUtil.hs b/Graphics/Implicit/IntegralUtil.hs new file mode 100644 index 00000000..f8bbc785 --- /dev/null +++ b/Graphics/Implicit/IntegralUtil.hs @@ -0,0 +1,106 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- 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 + +--{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} + +module Graphics.Implicit.IntegralUtil (ℕ, Fastℕ, toℕ, fromℕ, toFastℕ, fromFastℕ) where + +import Prelude (Integral(quotRem, toInteger), Num((*),(+),abs,negate,signum,fromInteger), Eq, Ord, Enum(succ, pred, toEnum, fromEnum), Real(toRational), Show, ($), Read, fromIntegral, Int) + +import GHC.Integer (timesInteger, plusInteger, absInteger, negateInteger, signumInteger, quotRemInteger, Integer) + +import GHC.Real (Ratio((:%))) + +default () + +-- the N typeclass. only used to define the ℕ type, with a few extensions. +class (Integral n) => N n where + fromℕ :: ℕ -> n + toℕ :: n -> ℕ + +instance N Integer where + fromℕ (ℕ a) = a + toℕ a = ℕ a + +instance N Fastℕ where + fromℕ (ℕ a) = Fastℕ $ fromIntegral a + toℕ a = ℕ $ fromIntegral a + +instance N Int where + fromℕ (ℕ a) = fromIntegral a + toℕ a = ℕ $ fromIntegral a + +-- Arbitrary precision integers. To be used for anything countable, or in ratios. +newtype ℕ = ℕ Integer + deriving (Show, Read, Eq, Ord, N) +-- deriving stock (Show, Read, Eq, Ord) +-- deriving newtype (N, Real, Enum) + +-- manual derivations of Integral and Num, so that we do not define unneeded functions. +instance Integral ℕ where + quotRem (ℕ a) (ℕ b) = + let (# q, r #) = quotRemInteger a b + in (ℕ q , ℕ r) + toInteger (ℕ a) = a + +instance Num ℕ where + (*) a b = ℕ $ timesInteger (fromℕ a) (fromℕ b) + (+) a b = ℕ $ plusInteger (fromℕ a) (fromℕ b) + abs a = ℕ $ absInteger (fromℕ a) + negate a = ℕ $ negateInteger (fromℕ a) + signum a = ℕ $ signumInteger (fromℕ a) + fromInteger a = ℕ a + +instance Enum ℕ where + succ (ℕ x) = ℕ $ succ x + pred (ℕ x) = ℕ $ pred x + toEnum n = ℕ $ toEnum n + fromEnum (ℕ n) = fromEnum n + +-- {-# INLINE enumFrom #-} +-- {-# INLINE enumFromThen #-} +-- {-# INLINE enumFromTo #-} +-- {-# INLINE enumFromThenTo #-} +-- enumFrom x = enumDeltaInteger x 1 +-- enumFromThen x y = enumDeltaInteger x (y-x) +-- enumFromTo x lim = enumDeltaToInteger x 1 lim +-- enumFromThenTo x y lim = enumDeltaToInteger x (y-x) lim + +instance Real ℕ where + toRational (ℕ a) = a :% 1 + +class FastN n where + fromFastℕ :: Fastℕ -> n + toFastℕ :: n -> Fastℕ + +instance FastN Int where + fromFastℕ (Fastℕ a) = a + toFastℕ a = Fastℕ a + +-- System integers, meant to go fast, and have no chance of wrapping 2^31. +newtype Fastℕ = Fastℕ Int + deriving (Show, Read, Eq, Ord, FastN, Real, Integral) +-- deriving stock (Show, Read, Eq, Ord) +-- deriving newtype (FastN, Enum, Real, Integral) + +instance Num Fastℕ where + (+) (Fastℕ a) (Fastℕ b) = Fastℕ $ a + b + (*) (Fastℕ a) (Fastℕ b) = Fastℕ $ a * b + abs (Fastℕ a) = Fastℕ $ abs a + negate (Fastℕ a) = Fastℕ $ negate a + signum (Fastℕ a) = Fastℕ $ signum a + fromInteger a = Fastℕ $ fromInteger a + +instance Enum Fastℕ where + succ (Fastℕ x) = Fastℕ $ succ x + pred (Fastℕ x) = Fastℕ $ pred x + toEnum n = Fastℕ $ toEnum n + fromEnum (Fastℕ n) = n diff --git a/Graphics/Implicit/RationalFunctions.hs b/Graphics/Implicit/RationalFunctions.hs new file mode 100644 index 00000000..44866a47 --- /dev/null +++ b/Graphics/Implicit/RationalFunctions.hs @@ -0,0 +1,132 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright 2016, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + +{-# LANGUAGE TypeFamilies #-} + +module Graphics.Implicit.RationalFunctions (cosℝp, sinℝp, asinℝp, normalizeℝp, normalizeℝ2p, normalizeℝ3p, sqrtℝp, cbrtℝp) where + +import Prelude (Double, (+), (*), (<), (^), (>), (-), ($), (/=), abs, otherwise, fromIntegral, sum, realToFrac, Integer, (/), negate, (**)) + +import qualified Prelude as P (sqrt) + +import Data.Maybe(Maybe(Nothing), fromJust) + +import Data.List(find, iterate) + +import Graphics.Implicit.IntegralUtil (Fastℕ, ℕ, toℕ, fromℕ) + +import Data.VectorSpace (InnerSpace, Scalar, (^/), magnitudeSq) + +import Data.Ratio ((%)) + +import GHC.Real (Ratio((:%))) + +-- FIXME: since the taylor series is acurate only in a certain range, rollover in that range. +asinℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ +asinℝp precision x + | x > 0 = res + | x < 0 = -res + | otherwise = 0 + where + res=((abs x))+(sum [asinTerm (abs x) i | i <- [1..precision]]) + +asinTerm :: Ratio ℕ -> Fastℕ -> Ratio ℕ +asinTerm x n = ((x^oddTerm)*((factorialskip $ oddTerm-2)%1)) / (((factorialskip evenTerm)*(toℕ $ oddTerm))%1) + where + oddTerm :: Fastℕ + oddTerm = 2*n + 1 + evenTerm :: Fastℕ + evenTerm = 2*n + +factorialskip :: Fastℕ -> ℕ +factorialskip 0 = 1 +factorialskip 1 = 1 +factorialskip n = (toℕ n) * factorialskip (n-2) + +cosℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ +cosℝp precision x = 1-(sum [cosTerm (abs x) (toℕ i) | i <- [1..precision]]) + +cosTerm :: Ratio ℕ -> ℕ -> Ratio ℕ +cosTerm x i = ((x^evenTerm) / ((factorial evenTerm)%1))*(-1)^(i-1) + where evenTerm = 2*i + +sinℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ +sinℝp precision x + | x > 0 = res + | x < 0 = -res + | otherwise = 0 + where + res=sum [sinTerm (abs x) (toℕ i) | i <- [1..precision]] + +sinTerm :: Ratio ℕ -> ℕ -> Ratio ℕ +sinTerm x i = (x^oddTerm / ((factorial oddTerm)%1))*(-1)^(i-1) + where oddTerm = 2*i - 1 + +factorial :: ℕ -> ℕ +factorial 1 = 1 +factorial n = n * factorial (n-1) + +-- | normalize a rational. +normalizeℝp :: (InnerSpace v, s ~ Scalar v, s ~ Ratio ℕ) => Fastℕ -> v -> v +normalizeℝp precision v = v ^/ magnitudeForced + where + magnitudeForced :: (Ratio ℕ) + magnitudeForced = sqrtℝp precision $ magnitudeSq v +{-# INLINABLE normalizeℝp #-} + +-- | normalize a tuple of two rationals. +normalizeℝ2p :: (InnerSpace s, v ~ Scalar (s,s), s ~ Ratio ℕ) => Fastℕ -> (s, s) -> (v, v) +normalizeℝ2p precision v = v ^/ magnitudeForced + where + magnitudeForced :: (Ratio ℕ) + magnitudeForced = sqrtℝp precision $ magnitudeSq v +{-# INLINABLE normalizeℝ2p #-} + +-- | normalize a tuple of three rationals. +normalizeℝ3p :: (InnerSpace s, v ~ Scalar (s,s,s), s ~ Ratio ℕ) => Fastℕ -> (s, s, s) -> (v, v, v) +normalizeℝ3p precision v = v ^/ magnitudeForced + where + magnitudeForced :: (Ratio ℕ) + magnitudeForced = sqrtℝp precision $ magnitudeSq v +-- | get a cube root using our recursive function, using the double based method as a springboard.. +cbrtℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ +cbrtℝp precision x + | x > 0 = fromJust $ refineCbrtGuess precision (abs x) $ preseed x + | x < 0 = negate $ fromJust $ refineCbrtGuess precision (abs x) $ preseed x + | otherwise = 0 + where + preseed :: Ratio ℕ -> Ratio ℕ + preseed p = realToFrac $ (**(1/3)) $ ratℕ2Double p + ratℕ2Double :: Ratio ℕ -> Double + ratℕ2Double ( n :% d ) = (fromIntegral (fromℕ n :: Integer)) / (fromIntegral (fromℕ d :: Integer)) + +-- | Use newton's method to zoom in on a correct answer. +-- See: https://codereview.stackexchange.com/questions/63743/square-root-calculation-with-newtons-method +refineCbrtGuess :: Fastℕ -> Ratio ℕ -> Ratio ℕ -> Maybe (Ratio ℕ) +refineCbrtGuess precision x initial + | x /= 0 = find withinPrecision $ iterate newtons initial + | otherwise = Nothing + where + withinPrecision guess = abs (guess*guess*guess - x) < (1%(2^precision))*x + newtons guess = (2*guess + x / (guess*guess)) /3 + +-- | get a square root using our recursive function, using the native square root function as a springboard. +sqrtℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ +sqrtℝp precision x + | x /= 0 = fromJust $ refineSqrtGuess precision (abs x) $ preseed x + | otherwise = 0 + where + preseed :: Ratio ℕ -> Ratio ℕ + preseed p = realToFrac $ P.sqrt $ ratℕ2Double p + ratℕ2Double :: Ratio ℕ -> Double + ratℕ2Double ( n :% d ) = (fromIntegral (fromℕ n :: Integer)) / (fromIntegral (fromℕ d :: Integer)) +-- | Use newton's method to zoom in on a correct answer. +-- See: https://codereview.stackexchange.com/questions/63743/square-root-calculation-with-newtons-method +refineSqrtGuess :: Fastℕ -> Ratio ℕ -> Ratio ℕ -> Maybe (Ratio ℕ) +refineSqrtGuess precision x initial + | x /= 0 = find withinPrecision $ iterate newtons initial + | otherwise = Nothing + where + withinPrecision guess = abs (guess*guess - x) < (1%(2^precision))*x + newtons guess = (guess + x / guess) / 2 diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs new file mode 100644 index 00000000..5b988f5b --- /dev/null +++ b/Graphics/Implicit/RationalUtil.hs @@ -0,0 +1,248 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright 2016, Julia Longtin (julial@turinglace.com) +-- Released under the GNU AGPLV3+, see LICENSE + +-- Rational, arbitrary precision trig functions. + +-- Required. FIXME: why? +{-# LANGUAGE FlexibleInstances #-} + +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE UndecidableInstances #-} + +{-# LANGUAGE TypeApplications #-} + +-- {-# LANGUAGE DeriveGeneric #-} + +module Graphics.Implicit.RationalUtil (ℚ(..), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) where + +import Prelude ( (/), (-), fromIntegral, abs, realToFrac, RealFrac, Fractional, Ord, Double, (**), Show, Eq, Num((+), (*), negate, signum, fromInteger), Real, Read, ($), seq, (==), floor, Float) + +import qualified Prelude as P (sqrt, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, pi, exp, log) + +import Data.Coerce (coerce) + +import Data.AffineSpace (AffineSpace(Diff, (.-.), (.+^))) + +import Data.Maybe (Maybe(Just, Nothing)) + +import Graphics.Implicit.IntegralUtil (ℕ, Fastℕ) + +import Data.VectorSpace (InnerSpace((<.>)), AdditiveGroup, VectorSpace(Scalar,(*^)), (^/), magnitude) + +import Control.DeepSeq(NFData(rnf)) + +-- import GHC.Generics (Generic) + +-- FIXME: this looks a lot like VectorSpace. Make ScadSpace? +-- Properties of ScadSpace: +-- It's own Cos, Sin, and Arctan2 implementations. +-- It's own power operator. +-- It's own stopping point when trying to decide what side of a line a ray is on. +-- One implementation for float, one for Double, and one for Ratio ℕ. + +-- Let's make things a bit nicer. +-- Supports using Float, Double, or a Ratio of ℕ for more precision! +class (RealFrac v) => ℚ v where + π :: v + minℝ :: v + infty :: v + neginfty :: v + powℝ :: v -> ℕ -> v + powℝℝ :: v -> v -> v + sin :: v -> v + cos :: v -> v + tan :: v -> v + asin :: v -> v + acos :: v -> v + atan :: v -> v + sinh :: v -> v + cosh :: v -> v + tanh :: v -> v + exp :: v -> v + log :: v -> v + sqrt :: v -> v + cbrt :: v -> v + atan2 :: v -> v -> v + normalizeℝ :: v -> v + normalizeℝ2 :: (v,v) -> (v,v) + normalizeℝ3 :: (v,v,v) -> (v,v,v) + toℝ :: v -> ℝ + fromℝ :: ℝ -> v + (%) :: ℕ -> ℕ -> v + +-- CUT HERE -- +{- + +import GHC.Real (Ratio((:%))) + +import Graphics.Implicit.RationalFunctions (cosℝp, sinℝp, asinℝp, normalizeℝp, normalizeℝ2p, normalizeℝ3p, sqrtℝp, cbrtℝp) + +import Prelude (Integer, (^^), Real(toRational)) + +import Graphics.Implicit.IntegralUtil (Fastℕ, ℕ, toℕ, fromℕ) + +-- Double Precision sqrt is 53 bits. +sqrtPrecision :: Fastℕ +sqrtPrecision=53 + +-- FIXME: find a better representation of the default precision of the trig functions. + +instance ℚ (Ratio ℕ) where + π = 245850922%78256779 + -- FIXME: placeholders. + minℝ = 1%10000000000000000 + infty=10000000000000000%1 + neginfty=(-10000000000000000%1) + powℝ a b = a ^^ b + sin = sinℝp sqrtPrecision + cos = cosℝp sqrtPrecision +-- tan = tanℝp sqrtPrecision + asin = asinℝp sqrtPrecision + acos = acosℝp sqrtPrecision +-- atan = atanℝp sqrtPrecision +-- sinh = sinhℝp sqrtPrecision +-- cosh = coshℝp sqrtPrecision +-- tanh = tanhℝp sqrtPrecision + sqrt = sqrtℝp sqrtPrecision + cbrt = cbrtℝp sqrtPrecision +-- atan2 = atan2ℝp sqrtPrecision + normalizeℝ = normalizeℝp sqrtPrecision + normalizeℝ2 = normalizeℝ2p sqrtPrecision + normalizeℝ3 = normalizeℝ3p sqrtPrecision +-- fromℝ (ℝ (a :% b)) = (fromIntegral (fromℕ a :: Integer)) :% (fromIntegral (fromℕ b :: Integer)) + fromℝ (ℝ (a :% b)) = (fromℕ a) :% (fromℕ b) + toℝ (a :% b) = ℝ ((toℕ a) :% (toℕ b)) + (%) a b = a :% b + +newtype ℝ = ℝ (Ratio ℕ) + deriving stock (Read, Show, Ord, Eq) + deriving newtype (RealFrac, Fractional, ℚ, AdditiveGroup, AffineSpace) + +instance NFData ℝ where + rnf (ℝ (x :% y)) = rnf (fromℕ x :: Integer) `seq` + rnf (fromℕ y :: Integer) `seq` + () + +instance Num ℝ where + (+) (ℝ a) (ℝ b) = ℝ $ a + b + (*) (ℝ a) (ℝ b) = ℝ $ a * b + abs (ℝ a) = ℝ $ abs a + negate (ℝ a) = ℝ $ negate a + signum (ℝ a) = ℝ $ signum a + fromInteger a = ℝ $ (toℕ a):%1 + +instance Real ℝ where + toRational (ℝ (a :% b)) = (fromℕ a) :% (fromℕ b) + +fromℕtoℝ :: ℕ -> ℝ +fromℕtoℝ a = ℝ $ a % (1::ℕ) + +acosℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ +acosℝp precision x = (π/2)-(asinℝp precision (abs x)) + +-} +-- CUT HERE -- + +instance ℚ Double where + π = P.pi + minℝ = 0.000000000000002 + -- yes, these are nonsense. never meant to be evaluated. + infty = 1/0 + neginfty = -1/0 + powℝ a b = a ** (fromIntegral b) + powℝℝ a b = a ** b + sin = P.sin + cos = P.cos + tan = P.tan + asin = P.asin + acos = P.acos + atan = P.atan + sinh = P.sinh + cosh = P.cosh + tanh = P.tanh + atan2 = P.atan2 + exp = P.exp + log = P.log + sqrt = P.sqrt + cbrt = (**(1/3)) + normalizeℝ v = v ^/ magnitude v + normalizeℝ2 v = v ^/ magnitude v + normalizeℝ3 v = v ^/ magnitude v + fromℝ = realToFrac + toℝ a = ℝ a + (%) a b = (/) (fromIntegral a) (fromIntegral b) + +newtype ℝ = ℝ Double +-- deriving (Read, Show, Ord, Eq, RealFrac, Fractional, Generic, ℚ, AdditiveGroup, Real) + deriving stock (Read, Show, Ord, Eq) + deriving newtype (RealFrac, Fractional, ℚ, AdditiveGroup, Real) + +instance NFData ℝ where + rnf (ℝ a) = rnf a `seq` () + +instance Num ℝ where + (+) (ℝ a) (ℝ b) = ℝ $ a + b + (*) (ℝ a) (ℝ b) = ℝ $ a * b + abs (ℝ a) = ℝ $ abs a + negate (ℝ a) = ℝ $ negate a + signum (ℝ a) = ℝ $ signum a + fromInteger a = ℝ $ realToFrac a + +fromℕtoℝ :: ℕ -> ℝ +fromℕtoℝ a = ℝ $ realToFrac a + +fromFastℕtoℝ :: Fastℕ -> ℝ +fromFastℕtoℝ a = ℝ $ realToFrac a + +fromInttoℝ :: Int -> ℝ +fromInttoℝ a = ℝ $ realToFrac a + +fromℝtoFloat :: ℝ -> Float +fromℝtoFloat a = (realToFrac a :: Float) + +-- CUT HERE -- + +{- +import Prelude (Float) + +instance ℚ Float where + π = P.pi + minℝ = 0.00000002 + -- yes, these are nonsense. never meant to be evaluated. + infty = 1/0 + neginfty = -1/0 + powℝ a b = a ** (fromIntegral b) + cos = P.cos + sin = P.sin + acos = P.acos + asin = P.asin + sqrt = P.sqrt + normalizeℝ v = v ^/ magnitude v + fromℝ = realToFrac + toℝ a = ℝ a +newtype ℝ = ℝ Float +-} + +-- Thanks, Solonarv and #haskell +-- copied from the VectorSpace (Ratio a) instance +instance VectorSpace ℝ where + type Scalar ℝ = ℝ + (*^) = coerce ((*^) @(ℝ)) + +instance InnerSpace ℝ where + (<.>) = (*) + +instance AffineSpace ℝ where + type Diff ℝ = ℝ + (.-.) = (-) + (.+^) = (+) + + +fromℝtoℕ :: ℝ -> Maybe ℕ +fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing + + From d4afd2856fd527ca08ad667e8ffd473f09c8d3b3 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 10 May 2019 17:41:54 +0100 Subject: [PATCH 05/24] not yet --- Graphics/Implicit/RationalUtil.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 5b988f5b..f12bc643 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -198,8 +198,8 @@ fromℕtoℝ a = ℝ $ realToFrac a fromFastℕtoℝ :: Fastℕ -> ℝ fromFastℕtoℝ a = ℝ $ realToFrac a -fromInttoℝ :: Int -> ℝ -fromInttoℝ a = ℝ $ realToFrac a +--fromInttoℝ :: Int -> ℝ +--fromInttoℝ a = ℝ $ realToFrac a fromℝtoFloat :: ℝ -> Float fromℝtoFloat a = (realToFrac a :: Float) From 351a49774c28194d622158c5521464d4bab600ca Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 11 May 2019 15:30:32 +0100 Subject: [PATCH 06/24] make read work. --- Graphics/Implicit/RationalUtil.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index f12bc643..25d82b41 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -178,8 +178,8 @@ instance ℚ Double where newtype ℝ = ℝ Double -- deriving (Read, Show, Ord, Eq, RealFrac, Fractional, Generic, ℚ, AdditiveGroup, Real) - deriving stock (Read, Show, Ord, Eq) - deriving newtype (RealFrac, Fractional, ℚ, AdditiveGroup, Real) + deriving stock (Show, Ord, Eq) + deriving newtype (Read, RealFrac, Fractional, ℚ, AdditiveGroup, Real) instance NFData ℝ where rnf (ℝ a) = rnf a `seq` () From 3aa219df5aeb27a61cb71e19be4f0b307386d428 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 12 May 2019 09:07:43 +0100 Subject: [PATCH 07/24] whoops. broke something in the merge. fix it. --- Graphics/Implicit/Definitions.hs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 21c268da..c5a847e1 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -74,14 +74,10 @@ module Graphics.Implicit.Definitions ( RotateExtrude), Rectilinear2, Rectilinear3, - fromℕtoℝ, - fromFastℕtoℝ, - fromFastℕ, - fromℝtoFloat ) where -import Prelude (Show, Double, Integer, Int, Either, show, (*), (/), fromIntegral, Float, realToFrac) +import Prelude (Show, Either, show, (*), (/)) import Data.Maybe (Maybe) From cf40185abd93a36fa4c611c5c22c3d34fcb0786c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 13 May 2019 20:50:07 +0100 Subject: [PATCH 08/24] break FastInt into a separate file. --- Graphics/Implicit/Definitions.hs | 5 +- Graphics/Implicit/IntegralUtil.hs | 72 +++++++++----------------- Graphics/Implicit/RationalFunctions.hs | 4 +- Graphics/Implicit/RationalUtil.hs | 4 +- 4 files changed, 35 insertions(+), 50 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index c5a847e1..0302b676 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -17,6 +17,7 @@ module Graphics.Implicit.Definitions ( module R, module N, + module F, ℝ2, both, ℝ3, @@ -85,7 +86,9 @@ import Data.VectorSpace (Scalar, InnerSpace, (<.>)) import Graphics.Implicit.RationalUtil as R (ℚ(minℝ, π, sqrt, cbrt, powℝ, powℝℝ, exp, log, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, fromℝ, toℝ, normalizeℝ, normalizeℝ2, normalizeℝ3, powℝ, (%), infty, neginfty), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) -import Graphics.Implicit.IntegralUtil as N (ℕ, Fastℕ, fromℕ, toℕ, fromFastℕ, toFastℕ) +import Graphics.Implicit.IntegralUtil as N (ℕ, fromℕ, toℕ) + +import Graphics.Implicit.FastIntUtil as F (Fastℕ, fromFastℕ, toFastℕ) -- Following the math notation ℝ, ℝ², ℝ³... type ℝ2 = (ℝ,ℝ) diff --git a/Graphics/Implicit/IntegralUtil.hs b/Graphics/Implicit/IntegralUtil.hs index f8bbc785..52861cd9 100644 --- a/Graphics/Implicit/IntegralUtil.hs +++ b/Graphics/Implicit/IntegralUtil.hs @@ -1,25 +1,29 @@ -- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) --- Copyright 2014 2015 2016, 2017, 2018, Julia Longtin (julial@turinglace.com) --- Copyright 2015 2016, Mike MacHenry (mike.machenry@gmail.com) +-- Copyright 2014-2019, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE --{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DerivingStrategies #-} + +-- Allow us to use explicit foralls when writing function type declarations. +{-# LANGUAGE ExplicitForAll #-} + +-- Allow us to derive N when declaring ℕ. {-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE UnboxedTuples #-} -module Graphics.Implicit.IntegralUtil (ℕ, Fastℕ, toℕ, fromℕ, toFastℕ, fromFastℕ) where -import Prelude (Integral(quotRem, toInteger), Num((*),(+),abs,negate,signum,fromInteger), Eq, Ord, Enum(succ, pred, toEnum, fromEnum), Real(toRational), Show, ($), Read, fromIntegral, Int) +module Graphics.Implicit.IntegralUtil (ℕ, toℕ, fromℕ) where + +import Prelude (Integral(toInteger), Num, Eq, Ord, Enum(fromEnum), Real(toRational), Show, ($), Read, fromIntegral, Int, Integer) -import GHC.Integer (timesInteger, plusInteger, absInteger, negateInteger, signumInteger, quotRemInteger, Integer) +import qualified Prelude as P ((+), (*), abs, negate, signum, fromInteger, succ, pred, toEnum, fromEnum, quot, rem, toInteger) import GHC.Real (Ratio((:%))) -default () +-- So we can produce an instance of Fastℕ for ℕ. +import Graphics.Implicit.FastIntUtil (Fastℕ(Fastℕ)) -- the N typeclass. only used to define the ℕ type, with a few extensions. class (Integral n) => N n where @@ -44,26 +48,24 @@ newtype ℕ = ℕ Integer -- deriving stock (Show, Read, Eq, Ord) -- deriving newtype (N, Real, Enum) --- manual derivations of Integral and Num, so that we do not define unneeded functions. instance Integral ℕ where - quotRem (ℕ a) (ℕ b) = - let (# q, r #) = quotRemInteger a b - in (ℕ q , ℕ r) - toInteger (ℕ a) = a + toInteger (ℕ a) = a + quot (ℕ a) (ℕ b) = ℕ $ P.quot a b + rem (ℕ a) (ℕ b) = ℕ $ P.rem a b instance Num ℕ where - (*) a b = ℕ $ timesInteger (fromℕ a) (fromℕ b) - (+) a b = ℕ $ plusInteger (fromℕ a) (fromℕ b) - abs a = ℕ $ absInteger (fromℕ a) - negate a = ℕ $ negateInteger (fromℕ a) - signum a = ℕ $ signumInteger (fromℕ a) + (+) (ℕ a) (ℕ b) = ℕ $ a P.+ b + (*) (ℕ a) (ℕ b) = ℕ $ a P.* b + abs (ℕ a) = ℕ $ P.abs a + negate (ℕ a) = ℕ $ P.negate a + signum (ℕ a) = ℕ $ P.signum a fromInteger a = ℕ a instance Enum ℕ where - succ (ℕ x) = ℕ $ succ x - pred (ℕ x) = ℕ $ pred x - toEnum n = ℕ $ toEnum n - fromEnum (ℕ n) = fromEnum n + succ (ℕ x) = ℕ $ P.succ x + pred (ℕ x) = ℕ $ P.pred x + toEnum n = ℕ $ P.toEnum n + fromEnum (ℕ n) = P.fromEnum n -- {-# INLINE enumFrom #-} -- {-# INLINE enumFromThen #-} @@ -77,30 +79,6 @@ instance Enum ℕ where instance Real ℕ where toRational (ℕ a) = a :% 1 -class FastN n where - fromFastℕ :: Fastℕ -> n - toFastℕ :: n -> Fastℕ -instance FastN Int where - fromFastℕ (Fastℕ a) = a - toFastℕ a = Fastℕ a --- System integers, meant to go fast, and have no chance of wrapping 2^31. -newtype Fastℕ = Fastℕ Int - deriving (Show, Read, Eq, Ord, FastN, Real, Integral) --- deriving stock (Show, Read, Eq, Ord) --- deriving newtype (FastN, Enum, Real, Integral) - -instance Num Fastℕ where - (+) (Fastℕ a) (Fastℕ b) = Fastℕ $ a + b - (*) (Fastℕ a) (Fastℕ b) = Fastℕ $ a * b - abs (Fastℕ a) = Fastℕ $ abs a - negate (Fastℕ a) = Fastℕ $ negate a - signum (Fastℕ a) = Fastℕ $ signum a - fromInteger a = Fastℕ $ fromInteger a - -instance Enum Fastℕ where - succ (Fastℕ x) = Fastℕ $ succ x - pred (Fastℕ x) = Fastℕ $ pred x - toEnum n = Fastℕ $ toEnum n - fromEnum (Fastℕ n) = n + diff --git a/Graphics/Implicit/RationalFunctions.hs b/Graphics/Implicit/RationalFunctions.hs index 44866a47..169c26c7 100644 --- a/Graphics/Implicit/RationalFunctions.hs +++ b/Graphics/Implicit/RationalFunctions.hs @@ -14,7 +14,9 @@ import Data.Maybe(Maybe(Nothing), fromJust) import Data.List(find, iterate) -import Graphics.Implicit.IntegralUtil (Fastℕ, ℕ, toℕ, fromℕ) +import Graphics.Implicit.IntegralUtil (ℕ, toℕ, fromℕ) + +import Graphics.Implicit.FastIntUtil (Fastℕ) import Data.VectorSpace (InnerSpace, Scalar, (^/), magnitudeSq) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 25d82b41..5e8fd4b9 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -29,7 +29,9 @@ import Data.AffineSpace (AffineSpace(Diff, (.-.), (.+^))) import Data.Maybe (Maybe(Just, Nothing)) -import Graphics.Implicit.IntegralUtil (ℕ, Fastℕ) +import Graphics.Implicit.IntegralUtil (ℕ) + +import Graphics.Implicit.FastIntUtil (Fastℕ) import Data.VectorSpace (InnerSpace((<.>)), AdditiveGroup, VectorSpace(Scalar,(*^)), (^/), magnitude) From 9069578ac3e8944375f34bd4ab101a98d4a5a831 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 13 May 2019 20:52:24 +0100 Subject: [PATCH 09/24] remove dependency on integer-gmp, and continue to add fastInt as a seperate file. --- implicit.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implicit.cabal b/implicit.cabal index f0cff8bd..eadc67de 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -35,8 +35,7 @@ Library blaze-svg, storable-endian, JuicyPixels, - transformers, - integer-gmp + transformers Ghc-options: -O2 @@ -74,6 +73,7 @@ Library Other-modules: Graphics.Implicit.RationalFunctions Graphics.Implicit.IntegralUtil + Graphics.Implicit.FastIntUtil Graphics.Implicit.RationalUtil Graphics.Implicit.ObjectUtil.GetBox2 Graphics.Implicit.ObjectUtil.GetBox3 From 2e1333803817b980b7d63fb8672d518cbad88925 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 14 May 2019 08:00:53 +0100 Subject: [PATCH 10/24] steal form from the current-tested fastIntUtil.hs --- Graphics/Implicit/IntegralUtil.hs | 44 +++++++++++++------------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/Graphics/Implicit/IntegralUtil.hs b/Graphics/Implicit/IntegralUtil.hs index 52861cd9..03c0198c 100644 --- a/Graphics/Implicit/IntegralUtil.hs +++ b/Graphics/Implicit/IntegralUtil.hs @@ -2,30 +2,21 @@ -- Copyright 2014-2019, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE ---{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE DerivingStrategies #-} - -- Allow us to use explicit foralls when writing function type declarations. {-# LANGUAGE ExplicitForAll #-} --- Allow us to derive N when declaring ℕ. -{-# LANGUAGE DeriveAnyClass #-} - - module Graphics.Implicit.IntegralUtil (ℕ, toℕ, fromℕ) where -import Prelude (Integral(toInteger), Num, Eq, Ord, Enum(fromEnum), Real(toRational), Show, ($), Read, fromIntegral, Int, Integer) +import Prelude (Integral, Num, Eq, Ord, Enum, Real(toRational), Show, ($), Read, fromIntegral, Int, Integer) -import qualified Prelude as P ((+), (*), abs, negate, signum, fromInteger, succ, pred, toEnum, fromEnum, quot, rem, toInteger) +import qualified Prelude as P ((+), (*), abs, negate, signum, fromInteger, succ, pred, toEnum, fromEnum, quot, rem, toInteger, quotRem) import GHC.Real (Ratio((:%))) --- So we can produce an instance of Fastℕ for ℕ. +-- So we can produce an instance of Fastℕ for ℕ. import Graphics.Implicit.FastIntUtil (Fastℕ(Fastℕ)) --- the N typeclass. only used to define the ℕ type, with a few extensions. +-- the N typeclass. only used to define the ℕ type. class (Integral n) => N n where fromℕ :: ℕ -> n toℕ :: n -> ℕ @@ -44,22 +35,27 @@ instance N Int where -- Arbitrary precision integers. To be used for anything countable, or in ratios. newtype ℕ = ℕ Integer - deriving (Show, Read, Eq, Ord, N) --- deriving stock (Show, Read, Eq, Ord) --- deriving newtype (N, Real, Enum) + deriving (Show, Read, Eq, Ord) + +instance Real ℕ where + toRational (ℕ a) = a :% 1 + +bothℕ :: (Integer, Integer) -> (ℕ, ℕ) +bothℕ (a, b) = (ℕ a , ℕ b) instance Integral ℕ where - toInteger (ℕ a) = a - quot (ℕ a) (ℕ b) = ℕ $ P.quot a b - rem (ℕ a) (ℕ b) = ℕ $ P.rem a b + toInteger (ℕ a) = a + quotRem (ℕ a) (ℕ b) = bothℕ $ P.quotRem a b + quot (ℕ a) (ℕ b) = ℕ $ P.quot a b + rem (ℕ a) (ℕ b) = ℕ $ P.rem a b instance Num ℕ where (+) (ℕ a) (ℕ b) = ℕ $ a P.+ b (*) (ℕ a) (ℕ b) = ℕ $ a P.* b - abs (ℕ a) = ℕ $ P.abs a - negate (ℕ a) = ℕ $ P.negate a - signum (ℕ a) = ℕ $ P.signum a - fromInteger a = ℕ a + abs (ℕ a) = ℕ $ P.abs a + negate (ℕ a) = ℕ $ P.negate a + signum (ℕ a) = ℕ $ P.signum a + fromInteger a = ℕ a instance Enum ℕ where succ (ℕ x) = ℕ $ P.succ x @@ -76,8 +72,6 @@ instance Enum ℕ where -- enumFromTo x lim = enumDeltaToInteger x 1 lim -- enumFromThenTo x y lim = enumDeltaToInteger x (y-x) lim -instance Real ℕ where - toRational (ℕ a) = a :% 1 From 28ad19821bb6f103589a897ada4ed163e7909615 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Mon, 20 May 2019 09:51:13 +0100 Subject: [PATCH 11/24] profiling, and improvements in RationalUtils. still looping. --- Graphics/Implicit/Definitions.hs | 5 +- Graphics/Implicit/Export/Symbolic/Rebound2.hs | 4 +- Graphics/Implicit/Export/Symbolic/Rebound3.hs | 4 +- Graphics/Implicit/MathUtil.hs | 4 +- Graphics/Implicit/RationalUtil.hs | 124 +++++++++++++----- Makefile | 4 +- implicit.cabal | 15 ++- 7 files changed, 107 insertions(+), 53 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index bfdb8f2e..2658d3aa 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -18,13 +18,10 @@ module Graphics.Implicit.Definitions ( module F, module N, module R, - module F, - module N, ℝ2, both, ℝ3, allthree, - minℝ, (⋅), (⋯*), (⋯/), @@ -87,7 +84,7 @@ import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) -import Graphics.Implicit.RationalUtil as R (ℚ(minℝ, π, sqrt, cbrt, powℝ, powℝℝ, exp, log, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, fromℝ, toℝ, normalizeℝ, normalizeℝ2, normalizeℝ3, powℝ, (%), infty, neginfty), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) +import Graphics.Implicit.RationalUtil as R (ℚ(minℝ, π, sqrt, cbrt, powℝ, powℝℝ, exp, log, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, fromℝ, toℝ, normalizeℝ2, normalizeℝ3, powℝ, (%), infty, neginfty), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) import Graphics.Implicit.IntegralUtil as N (ℕ, fromℕ, toℕ) diff --git a/Graphics/Implicit/Export/Symbolic/Rebound2.hs b/Graphics/Implicit/Export/Symbolic/Rebound2.hs index 698e1ad9..841e75e5 100644 --- a/Graphics/Implicit/Export/Symbolic/Rebound2.hs +++ b/Graphics/Implicit/Export/Symbolic/Rebound2.hs @@ -6,10 +6,12 @@ module Graphics.Implicit.Export.Symbolic.Rebound2 (rebound2) where import Prelude() -import Graphics.Implicit.Definitions (BoxedObj2, ℝ2) +import Graphics.Implicit.Definitions (BoxedObj2, ℝ, ℝ2) import Data.VectorSpace ((^-^), (^+^), (^/)) +default (ℝ) + rebound2 :: BoxedObj2 -> BoxedObj2 rebound2 (obj, (a,b)) = let diff --git a/Graphics/Implicit/Export/Symbolic/Rebound3.hs b/Graphics/Implicit/Export/Symbolic/Rebound3.hs index c5050bbe..2d86186c 100644 --- a/Graphics/Implicit/Export/Symbolic/Rebound3.hs +++ b/Graphics/Implicit/Export/Symbolic/Rebound3.hs @@ -6,10 +6,12 @@ module Graphics.Implicit.Export.Symbolic.Rebound3 (rebound3) where import Prelude() -import Graphics.Implicit.Definitions(BoxedObj3, ℝ3) +import Graphics.Implicit.Definitions(BoxedObj3, ℝ, ℝ3) import Data.VectorSpace((^-^), (^+^), (^/)) +default (ℝ) + rebound3 :: BoxedObj3 -> BoxedObj3 rebound3 (obj, (a,b)) = let diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index aed214cd..030564df 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -11,7 +11,7 @@ module Graphics.Implicit.MathUtil (rmax, rmaximum, rminimum, distFromLineSeg, pa -- Explicitly include what we need from Prelude. import Prelude (Bool, Num, Ord, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), max, min, compare, (<=), fst, snd, (++), head, flip) -import Graphics.Implicit.Definitions (ℝ, toℝ, ℝ2, ℝ3, π, sqrt, sin, asin, Box2, (⋅), normalizeℝ, normalizeℝ2) +import Graphics.Implicit.Definitions (ℝ, toℝ, ℝ2, ℝ3, π, sqrt, sin, asin, Box2, (⋅), normalizeℝ2) import Data.List (sort, sortBy, (!!)) @@ -28,7 +28,7 @@ distFromLineSeg p (a,b) = sqrt $ toℝ $ distanceSq p closest ab = b ^-^ a ap = p ^-^ a d :: ℝ - d = normalizeℝ $ ab ⋅ ap + d = (normalizeℝ2 ab) ⋅ ap -- the closest point to p on the line segment. closest :: ℝ2 closest diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index fb90d36d..f72bce99 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -5,23 +5,15 @@ -- Rational, arbitrary precision trig functions. -- Required. FIXME: why? -{-# LANGUAGE FlexibleInstances #-} - -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE TypeApplications #-} --- {-# LANGUAGE DeriveGeneric #-} - module Graphics.Implicit.RationalUtil (ℚ(..), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) where -import Prelude ( (/), (-), fromIntegral, abs, realToFrac, RealFrac, Fractional, Ord, Double, (**), Show, Eq, Num((+), (*), negate, signum, fromInteger), Real, Read, ($), seq, (==), floor, Float) +import Prelude (RealFrac(properFraction), Fractional(fromRational, (/)), Ord, Double, Show(show), Eq, Num((+), (*), abs, negate, signum, fromInteger), Real(toRational), Read(readsPrec), ($), seq, (==), floor, Float, map) -import qualified Prelude as P ((/), (**), realToFrac, sqrt, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, pi, exp, log, fromIntegral) +import qualified Prelude as P ((+), (-), (*), abs, signum, negate, (/), (**), realToFrac, sqrt, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, pi, exp, log, fromIntegral, toRational, properFraction, show, fromRational, readsPrec) import Data.Coerce (coerce) @@ -33,10 +25,13 @@ import Graphics.Implicit.IntegralUtil (ℕ) import Graphics.Implicit.FastIntUtil (Fastℕ) -import Data.VectorSpace (InnerSpace((<.>)), AdditiveGroup, VectorSpace(Scalar,(*^)), (^/), magnitude) +import Data.VectorSpace (InnerSpace((<.>)), AdditiveGroup((^+^), (^-^), zeroV, negateV), VectorSpace(Scalar,(*^)), (^/), magnitude) import Control.DeepSeq(NFData(rnf)) +import qualified Control.DeepSeq as CDS (rnf) + + -- import GHC.Generics (Generic) -- FIXME: this looks a lot like VectorSpace. Make ScadSpace? @@ -69,7 +64,6 @@ class (RealFrac v) => ℚ v where sqrt :: v -> v cbrt :: v -> v atan2 :: v -> v -> v - normalizeℝ :: v -> v normalizeℝ2 :: (v,v) -> (v,v) normalizeℝ3 :: (v,v,v) -> (v,v,v) toℝ :: v -> ℝ @@ -112,7 +106,6 @@ instance ℚ (Ratio ℕ) where sqrt = sqrtℝp sqrtPrecision cbrt = cbrtℝp sqrtPrecision -- atan2 = atan2ℝp sqrtPrecision - normalizeℝ = normalizeℝp sqrtPrecision normalizeℝ2 = normalizeℝ2p sqrtPrecision normalizeℝ3 = normalizeℝ3p sqrtPrecision -- fromℝ (ℝ (a :% b)) = (fromIntegral (fromℕ a :: Integer)) :% (fromIntegral (fromℕ b :: Integer)) @@ -146,8 +139,6 @@ fromℕtoℝ a = ℝ $ a % (1::ℕ) acosℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ acosℝp precision x = (π/2)-(asinℝp precision (abs x)) --} --- CUT HERE -- instance ℚ Double where π = P.pi @@ -170,41 +161,103 @@ instance ℚ Double where exp = P.exp log = P.log sqrt = P.sqrt - cbrt = (**(1/3)) - normalizeℝ v = v ^/ magnitude v + cbrt = (P.**(1/3)) normalizeℝ2 v = v ^/ magnitude v normalizeℝ3 v = v ^/ magnitude v fromℝ = P.realToFrac toℝ a = ℝ a (%) a b = (P./) (P.fromIntegral a) (P.fromIntegral b) +-} +-- CUT HERE -- newtype ℝ = ℝ Double --- deriving (Read, Show, Ord, Eq, RealFrac, Fractional, Generic, ℚ, AdditiveGroup, Real) - deriving stock (Show, Ord, Eq) - deriving newtype (Read, RealFrac, Fractional, ℚ, AdditiveGroup, Real) + deriving (Ord, Eq) + + +instance ℚ ℝ where + π = ℝ $ P.pi + minℝ = ℝ $ 0.000000000000002 + -- yes, these are nonsense. never meant to be evaluated. + infty = ℝ $ 1/0 + neginfty = ℝ $ -1/0 + powℝ (ℝ a) b = ℝ $ a P.** (P.fromIntegral b) + powℝℝ (ℝ a) (ℝ b) = ℝ $ a P.** b + sin (ℝ x) = ℝ $ P.sin x + cos (ℝ x) = ℝ $ P.cos x + tan (ℝ x) = ℝ $ P.tan x + asin (ℝ x) = ℝ $ P.asin x + acos (ℝ x) = ℝ $ P.acos x + atan (ℝ x) = ℝ $ P.atan x + sinh (ℝ x) = ℝ $ P.sinh x + cosh (ℝ x) = ℝ $ P.cosh x + tanh (ℝ x) = ℝ $ P.tanh x + atan2 (ℝ x) (ℝ y) = ℝ $ P.atan2 x y + exp (ℝ x) = ℝ $ P.exp x + log (ℝ x) = ℝ $ P.log x + sqrt (ℝ x) = ℝ $ P.sqrt x + cbrt (ℝ x) = ℝ $ (P.**(1/3)) x + normalizeℝ2 (ℝ x, ℝ y) = bothℝ $ (x, y) ^/ magnitude (x, y) + where bothℝ (a, b) = (ℝ a, ℝ b) + normalizeℝ3 (ℝ x, ℝ y, ℝ z) = allThreeℝ $ (x, y, z) ^/ magnitude (x, y, z) + where allThreeℝ (a, b, c) = (ℝ a, ℝ b, ℝ c) + fromℝ (ℝ x) = P.realToFrac x + toℝ a = a + (%) a b = (P./) (P.fromIntegral a) (P.fromIntegral b) + +instance Read ℝ where + readsPrec prec input = map promoteFst $ (P.readsPrec prec input) + where + promoteFst :: (Double, a) -> (ℝ, a) + promoteFst (q, r) = (ℝ q, r) +-- readsListPrec +-- readsList + +instance Show ℝ where + show (ℝ a) = P.show a + +instance RealFrac ℝ where + properFraction (ℝ a) = promoteSnd $ P.properFraction a + where + promoteSnd :: (a, Double) -> (a, ℝ) + promoteSnd (q, r) = (q, ℝ r) + -- ceiling, floor, truncate, round. + +instance Fractional ℝ where + fromRational x = ℝ $ P.fromRational x + (/) (ℝ x) (ℝ y) = ℝ $ (P./) x y + +instance AdditiveGroup ℝ where + zeroV = ℝ $ 0 + (^-^) (ℝ a) (ℝ b) = ℝ $ (P.-) a b + (^+^) (ℝ a) (ℝ b) = ℝ $ (P.+) a b + negateV (ℝ a) = ℝ $ P.negate a + +instance Real ℝ where + toRational (ℝ a) = P.toRational a + {-# INLINABLE toRational #-} instance NFData ℝ where - rnf (ℝ a) = rnf a `seq` () + rnf (ℝ a) = CDS.rnf a `seq` () instance Num ℝ where - (+) (ℝ a) (ℝ b) = ℝ $ a + b - (*) (ℝ a) (ℝ b) = ℝ $ a * b - abs (ℝ a) = ℝ $ abs a - negate (ℝ a) = ℝ $ negate a - signum (ℝ a) = ℝ $ signum a - fromInteger a = ℝ $ realToFrac a + (+) (ℝ a) (ℝ b) = ℝ $ (P.+) a b + (*) (ℝ a) (ℝ b) = ℝ $ (P.*) a b + abs (ℝ a) = ℝ $ P.abs a + negate (ℝ a) = ℝ $ P.negate a + signum (ℝ a) = ℝ $ P.signum a + fromInteger a = ℝ $ P.realToFrac a fromℕtoℝ :: ℕ -> ℝ -fromℕtoℝ a = ℝ $ realToFrac a +fromℕtoℝ a = ℝ $ P.realToFrac a fromFastℕtoℝ :: Fastℕ -> ℝ -fromFastℕtoℝ a = ℝ $ realToFrac a +fromFastℕtoℝ a = ℝ $ P.realToFrac a --fromInttoℝ :: Int -> ℝ ---fromInttoℝ a = ℝ $ realToFrac a +--fromInttoℝ a = ℝ $ P.realToFrac a fromℝtoFloat :: ℝ -> Float -fromℝtoFloat a = (realToFrac a :: Float) +fromℝtoFloat a = (P.realToFrac a :: Float) -- CUT HERE -- @@ -232,17 +285,16 @@ newtype ℝ = ℝ Float -- Thanks, Solonarv and #haskell -- copied from the VectorSpace (Ratio a) instance instance VectorSpace ℝ where - type Scalar ℝ = ℝ + type Scalar ℝ = ℝ (*^) = coerce ((*^) @(ℝ)) instance InnerSpace ℝ where - (<.>) = (*) + (<.>) = (P.*) instance AffineSpace ℝ where type Diff ℝ = ℝ - (.-.) = (-) - (.+^) = (+) - + (.-.) = (P.-) + (.+^) = (P.+) fromℝtoℕ :: ℝ -> Maybe ℕ fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing diff --git a/Makefile b/Makefile index a9278d05..176b4a80 100644 --- a/Makefile +++ b/Makefile @@ -24,12 +24,12 @@ DOCGEN=dist/build/docgen/docgen ## Options used when calling ImplicitCAD. for testing, and for image generation. # Enable multiple CPU usage. -RTSOPTS=+RTS -N +RTSOPTS=+RTS -N -xc # 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. -#PROFILING= --enable-profiling +PROFILING= --enable-library-profiling LIBFILES=$(shell find Graphics -name '*.hs') LIBTARGET=dist/build/Graphics/Implicit.o diff --git a/implicit.cabal b/implicit.cabal index 49368234..6d44f49f 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. @@ -122,7 +122,8 @@ Executable extopenscad -optc-O3 -threaded -rtsopts - -dynamic + -prof +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -137,7 +138,7 @@ Executable docgen base, implicit ghc-options: - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -163,7 +164,7 @@ Executable implicitsnap -rtsopts -O2 -optc-O3 - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. @@ -181,7 +182,7 @@ Executable Benchmark Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -201,7 +202,7 @@ Test-suite test-implicit Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra @@ -219,7 +220,7 @@ Benchmark parser-bench Ghc-options: -O2 -optc-O3 - -dynamic +-- -dynamic -- for debugging. -Wall -Wextra From b5d24c04a881cb94c00b6df1618f8d8beeaaa3ac Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 06:08:14 +0100 Subject: [PATCH 12/24] remove nonsense function, clean up some comments. --- Graphics/Implicit/RationalFunctions.hs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Graphics/Implicit/RationalFunctions.hs b/Graphics/Implicit/RationalFunctions.hs index 169c26c7..7ec80ace 100644 --- a/Graphics/Implicit/RationalFunctions.hs +++ b/Graphics/Implicit/RationalFunctions.hs @@ -2,9 +2,10 @@ -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE +-- for the type arithmatic in the types of normalizedℝ(2,3). {-# LANGUAGE TypeFamilies #-} -module Graphics.Implicit.RationalFunctions (cosℝp, sinℝp, asinℝp, normalizeℝp, normalizeℝ2p, normalizeℝ3p, sqrtℝp, cbrtℝp) where +module Graphics.Implicit.RationalFunctions (cosℝp, sinℝp, asinℝp, normalizeℝ2p, normalizeℝ3p, sqrtℝp, cbrtℝp) where import Prelude (Double, (+), (*), (<), (^), (>), (-), ($), (/=), abs, otherwise, fromIntegral, sum, realToFrac, Integer, (/), negate, (**)) @@ -24,7 +25,7 @@ import Data.Ratio ((%)) import GHC.Real (Ratio((:%))) --- FIXME: since the taylor series is acurate only in a certain range, rollover in that range. +-- FIXME: since the taylor series for asin is acurate only in a certain range, rollover in that range. asinℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ asinℝp precision x | x > 0 = res @@ -69,14 +70,6 @@ factorial :: ℕ -> ℕ factorial 1 = 1 factorial n = n * factorial (n-1) --- | normalize a rational. -normalizeℝp :: (InnerSpace v, s ~ Scalar v, s ~ Ratio ℕ) => Fastℕ -> v -> v -normalizeℝp precision v = v ^/ magnitudeForced - where - magnitudeForced :: (Ratio ℕ) - magnitudeForced = sqrtℝp precision $ magnitudeSq v -{-# INLINABLE normalizeℝp #-} - -- | normalize a tuple of two rationals. normalizeℝ2p :: (InnerSpace s, v ~ Scalar (s,s), s ~ Ratio ℕ) => Fastℕ -> (s, s) -> (v, v) normalizeℝ2p precision v = v ^/ magnitudeForced From 7da010308117d6cbda22eb76d4ba1ae8db6b0baa Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 20:29:43 +0100 Subject: [PATCH 13/24] fix a loop, and clean up a type. --- Graphics/Implicit/RationalUtil.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index f72bce99..4abf5800 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -173,7 +173,7 @@ instance ℚ Double where newtype ℝ = ℝ Double deriving (Ord, Eq) - +-- Use this instance when ℝ ~ Double instance ℚ ℝ where π = ℝ $ P.pi minℝ = ℝ $ 0.000000000000002 @@ -202,7 +202,7 @@ instance ℚ ℝ where where allThreeℝ (a, b, c) = (ℝ a, ℝ b, ℝ c) fromℝ (ℝ x) = P.realToFrac x toℝ a = a - (%) a b = (P./) (P.fromIntegral a) (P.fromIntegral b) + (%) a b = ℝ $ (P./) (P.fromIntegral a) (P.fromIntegral b) instance Read ℝ where readsPrec prec input = map promoteFst $ (P.readsPrec prec input) @@ -211,7 +211,7 @@ instance Read ℝ where promoteFst (q, r) = (ℝ q, r) -- readsListPrec -- readsList - + instance Show ℝ where show (ℝ a) = P.show a @@ -286,7 +286,7 @@ newtype ℝ = ℝ Float -- copied from the VectorSpace (Ratio a) instance instance VectorSpace ℝ where type Scalar ℝ = ℝ - (*^) = coerce ((*^) @(ℝ)) + (*^) (ℝ a) (ℝ b) = ℝ $ (P.*) a b instance InnerSpace ℝ where (<.>) = (P.*) From f06bd79c091de8c300af7d57f44aae91fe6398af Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Tue, 21 May 2019 20:59:36 +0100 Subject: [PATCH 14/24] remove profiling. --- Makefile | 4 ++-- implicit.cabal | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 176b4a80..fe68689b 100644 --- a/Makefile +++ b/Makefile @@ -24,12 +24,12 @@ DOCGEN=dist/build/docgen/docgen ## Options used when calling ImplicitCAD. for testing, and for image generation. # Enable multiple CPU usage. -RTSOPTS=+RTS -N -xc +RTSOPTS=+RTS -N # 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. -PROFILING= --enable-library-profiling +#PROFILING= --enable-library-profiling LIBFILES=$(shell find Graphics -name '*.hs') LIBTARGET=dist/build/Graphics/Implicit.o diff --git a/implicit.cabal b/implicit.cabal index 6d44f49f..bfc5bb0c 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 @@ -122,7 +122,6 @@ Executable extopenscad -optc-O3 -threaded -rtsopts - -prof -- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing From 8b09b7c98b5bd7b6eb7b504c0326f03d091d1f37 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 24 May 2019 19:05:10 +0100 Subject: [PATCH 15/24] use defaulting to make code a bit more readable. --- Graphics/Implicit/Export/Render.hs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 0033907c..d0d94984 100644 --- a/Graphics/Implicit/Export/Render.hs +++ b/Graphics/Implicit/Export/Render.hs @@ -11,7 +11,7 @@ -- 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, ($), (+), (*), max, div, tail, map, concat, realToFrac, (==), (||), filter, not, reverse, (.), Eq, concatMap) +import Prelude(Float, Bool, ceiling, ($), (+), (*), max, div, tail, map, concat, (==), (||), filter, not, reverse, (.), Eq, concatMap) import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, both, ℝ3, allthree, TriangleMesh, Obj2, Obj3, Triangle, Polyline, (⋯/), fromℕtoℝ, fromℕ, fromFastℕ, fromℝtoFloat, Fastℕ) @@ -67,6 +67,9 @@ import Control.DeepSeq (NFData) -- For the 2D case, we need one last thing, cleanLoopsFromSegs: import Graphics.Implicit.Export.Render.HandlePolylines (cleanLoopsFromSegs) +-- Set the default types for the numbers in this file. +default (ℕ, Fastℕ, ℝ) + -- FIXME: res should be ℝ3, not ℝ. getMesh :: ℝ3 -> ℝ3 -> ℝ -> Obj3 -> TriangleMesh getMesh p1@(x1,y1,z1) p2 res obj = @@ -95,7 +98,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = (\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 (fromFastℕ $ max 1 $ div (fromℕ lenz) (32::Fastℕ)) rdeepseq + `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ 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) @@ -106,21 +109,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 (fromFastℕ $ max 1 $ div (fromℕ nz) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ 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 (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ 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 (fromFastℕ $ max 1 $ div (fromℕ nx) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nx) 32) rdeepseq -- Calculate segments for each side segsZ = [[[ @@ -133,7 +136,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = |objY0Z0 <- objZ0 | objY1Z0 <- tail objZ0 ]|z0<-pZs |mX' <-midsX| mY' <-midsY |objZ0 <- objV - ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) 32) rdeepseq segsY = [[[ map2 (inj2 y0) $ getSegs (x0,z0) (x1',z1') (obj *$* y0) @@ -145,7 +148,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 (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ ny) 32) rdeepseq segsX = [[[ map2 (inj1 x0) $ getSegs (y0,z0) (y1',z1') (obj $** x0) @@ -157,7 +160,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 (fromFastℕ $ max 1 $ div (fromℕ nx) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nx) 32) rdeepseq -- (3) & (4) : get and tesselate loops sqTris = [[[ @@ -181,7 +184,7 @@ getMesh p1@(x1,y1,z1) p2 res obj = ]| segZ' <- segsZ | segZT <- tail segsZ | segY' <- segsY | segX' <- segsX - ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nz) 32) rdeepseq in -- (5) merge squares, etc cleanupTris . mergedSquareTris . concat . concat $ concat sqTris @@ -226,7 +229,7 @@ getContour p1@(x1, y1) p2 res obj = (\n -> x1 + rx*(fromℕtoℝ (mx+n))) mx (\n -> y1 + ry*(fromℕtoℝ (my+n))) my | mx <- [0..lenx] ] | my <- [0..leny] ] - `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ leny) (32::Fastℕ)) rdeepseq + `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ leny) 32) rdeepseq -- | fully evaluate obj to avoid waste in mids, segs, later. objV = par2DList (nx+2) (ny+2) $ \x _ y _ -> obj (x 0, y 0) @@ -236,14 +239,14 @@ 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 (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ 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 (fromFastℕ $ max 1 $ div (fromℕ nx) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ nx) 32) rdeepseq -- | Calculate segments for each side segs = [[ @@ -254,7 +257,7 @@ 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 (fromFastℕ $ max 1 $ div (fromℕ ny) (32::Fastℕ)) rdeepseq + ] `using` parBuffer (fromFastℕ $ max 1 $ div (fromℕ ny) 32) rdeepseq in -- | merge squares, etc From 9801dba0d172c716de552b1cf266dc469a750505 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 24 May 2019 19:08:46 +0100 Subject: [PATCH 16/24] cleanup, and reorder. --- Graphics/Implicit/RationalUtil.hs | 72 ++++--------------------------- 1 file changed, 9 insertions(+), 63 deletions(-) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 4abf5800..0aa6c277 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -15,8 +15,6 @@ import Prelude (RealFrac(properFraction), Fractional(fromRational, (/)), Ord, Do import qualified Prelude as P ((+), (-), (*), abs, signum, negate, (/), (**), realToFrac, sqrt, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, pi, exp, log, fromIntegral, toRational, properFraction, show, fromRational, readsPrec) -import Data.Coerce (coerce) - import Data.AffineSpace (AffineSpace(Diff, (.-.), (.+^))) import Data.Maybe (Maybe(Just, Nothing)) @@ -139,34 +137,6 @@ fromℕtoℝ a = ℝ $ a % (1::ℕ) acosℝp :: Fastℕ -> Ratio ℕ -> Ratio ℕ acosℝp precision x = (π/2)-(asinℝp precision (abs x)) - -instance ℚ Double where - π = P.pi - minℝ = 0.000000000000002 - -- yes, these are nonsense. never meant to be evaluated. - infty = 1/0 - neginfty = -1/0 - powℝ a b = a P.** (P.fromIntegral b) - powℝℝ a b = a P.** b - sin = P.sin - cos = P.cos - tan = P.tan - asin = P.asin - acos = P.acos - atan = P.atan - sinh = P.sinh - cosh = P.cosh - tanh = P.tanh - atan2 = P.atan2 - exp = P.exp - log = P.log - sqrt = P.sqrt - cbrt = (P.**(1/3)) - normalizeℝ2 v = v ^/ magnitude v - normalizeℝ3 v = v ^/ magnitude v - fromℝ = P.realToFrac - toℝ a = ℝ a - (%) a b = (P./) (P.fromIntegral a) (P.fromIntegral b) -} -- CUT HERE -- @@ -226,19 +196,10 @@ instance Fractional ℝ where fromRational x = ℝ $ P.fromRational x (/) (ℝ x) (ℝ y) = ℝ $ (P./) x y -instance AdditiveGroup ℝ where - zeroV = ℝ $ 0 - (^-^) (ℝ a) (ℝ b) = ℝ $ (P.-) a b - (^+^) (ℝ a) (ℝ b) = ℝ $ (P.+) a b - negateV (ℝ a) = ℝ $ P.negate a - instance Real ℝ where toRational (ℝ a) = P.toRational a {-# INLINABLE toRational #-} -instance NFData ℝ where - rnf (ℝ a) = CDS.rnf a `seq` () - instance Num ℝ where (+) (ℝ a) (ℝ b) = ℝ $ (P.+) a b (*) (ℝ a) (ℝ b) = ℝ $ (P.*) a b @@ -259,31 +220,18 @@ fromFastℕtoℝ a = ℝ $ P.realToFrac a fromℝtoFloat :: ℝ -> Float fromℝtoFloat a = (P.realToFrac a :: Float) --- CUT HERE -- +fromℝtoℕ :: ℝ -> Maybe ℕ +fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing -{- -import Prelude (Float) +instance AdditiveGroup ℝ where + zeroV = ℝ $ 0 + (^-^) (ℝ a) (ℝ b) = ℝ $ (P.-) a b + (^+^) (ℝ a) (ℝ b) = ℝ $ (P.+) a b + negateV (ℝ a) = ℝ $ P.negate a -instance ℚ Float where - π = P.pi - minℝ = 0.00000002 - -- yes, these are nonsense. never meant to be evaluated. - infty = 1/0 - neginfty = -1/0 - powℝ a b = a ** (fromIntegral b) - cos = P.cos - sin = P.sin - acos = P.acos - asin = P.asin - sqrt = P.sqrt - normalizeℝ v = v ^/ magnitude v - fromℝ = realToFrac - toℝ a = ℝ a -newtype ℝ = ℝ Float --} +instance NFData ℝ where + rnf (ℝ a) = CDS.rnf a `seq` () --- Thanks, Solonarv and #haskell --- copied from the VectorSpace (Ratio a) instance instance VectorSpace ℝ where type Scalar ℝ = ℝ (*^) (ℝ a) (ℝ b) = ℝ $ (P.*) a b @@ -296,7 +244,5 @@ instance AffineSpace ℝ where (.-.) = (P.-) (.+^) = (P.+) -fromℝtoℕ :: ℝ -> Maybe ℕ -fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing From 35679413e123ab91ad7b6a7b63e6500254e179ea Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Fri, 24 May 2019 19:10:01 +0100 Subject: [PATCH 17/24] yes, normalize can only apply to the one argument.' --- 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 d8f56dd9..030564df 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -28,7 +28,7 @@ distFromLineSeg p (a,b) = sqrt $ toℝ $ distanceSq p closest ab = b ^-^ a ap = p ^-^ a d :: ℝ - d = normalizeℝ2 $ ab ⋅ ap + d = (normalizeℝ2 ab) ⋅ ap -- the closest point to p on the line segment. closest :: ℝ2 closest From 41ffd31295469001e85739fb8fa9dbcf6af83853 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 25 May 2019 16:50:54 +0100 Subject: [PATCH 18/24] missing zero --- Graphics/Implicit/RationalUtil.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 0aa6c277..960d1b77 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -146,7 +146,7 @@ newtype ℝ = ℝ Double -- Use this instance when ℝ ~ Double instance ℚ ℝ where π = ℝ $ P.pi - minℝ = ℝ $ 0.000000000000002 + minℝ = ℝ $ 0.0000000000000002 -- yes, these are nonsense. never meant to be evaluated. infty = ℝ $ 1/0 neginfty = ℝ $ -1/0 From 802405ddee5999aa06846abaff747f3f0f7eedcc Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 26 May 2019 08:11:52 +0100 Subject: [PATCH 19/24] fix bad merge. --- 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 06443114..42c63447 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -8,7 +8,7 @@ -- 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, (+), (==), take, floor) +import Prelude(Maybe(Just, Nothing), Either(Left), ($), (.), (*), map, ($!), (-), (/), 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) From b156ee067609a9588e6fae0e685ea00494c61526 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 16:19:44 +0100 Subject: [PATCH 20/24] cleanup after merge, and for rationalutils, some inlining, and more implementations. --- Graphics/Implicit/Export/RayTrace.hs | 2 - Graphics/Implicit/Export/Render.hs | 2 +- Graphics/Implicit/Export/Render/GetSegs.hs | 2 +- Graphics/Implicit/Export/Render/RefineSegs.hs | 2 +- Graphics/Implicit/Export/SymbolicObj2.hs | 2 +- Graphics/Implicit/RationalUtil.hs | 37 +++++++++++++++---- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index e89af7d1..2db9dd48 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -22,8 +22,6 @@ import Data.VectorSpace (magnitudeSq, (^+^), (*^), (^-^), InnerSpace) import Data.Cross (cross3) -import Codec.Picture (Pixel8, Image, DynamicImage(ImageRGBA8), PixelRGBA8(PixelRGBA8)) - default (ℕ, ℝ) -- Definitions diff --git a/Graphics/Implicit/Export/Render.hs b/Graphics/Implicit/Export/Render.hs index 62eaf6cb..a4880251 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, ($), (+), (*), max, div, tail, map, concat, reverse, (.), concatMap) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, both, ℝ3, allthree, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), fromℕtoℝ, fromℕ, fromFastℕ, fromℝtoFloat, Fastℕ) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, both, ℝ3, allthree, TriangleMesh, Obj2, Obj3, Polyline(Polyline), (⋯/), fromℕtoℝ, fromℕ, fromFastℕ, Fastℕ) import Data.VectorSpace ((^-^)) diff --git a/Graphics/Implicit/Export/Render/GetSegs.hs b/Graphics/Implicit/Export/Render/GetSegs.hs index 2d89caa2..f47f8c8c 100644 --- a/Graphics/Implicit/Export/Render/GetSegs.hs +++ b/Graphics/Implicit/Export/Render/GetSegs.hs @@ -4,7 +4,7 @@ module Graphics.Implicit.Export.Render.GetSegs (getSegs, getSegs') where -import Prelude(Eq, Bool(True, False), (+), (*), (/=), map, (.), filter, ($), (<=)) +import Prelude(Bool(True, False), (+), (*), (/=), map, (.), filter, ($), (<=)) import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline(Polyline), sqrt) diff --git a/Graphics/Implicit/Export/Render/RefineSegs.hs b/Graphics/Implicit/Export/Render/RefineSegs.hs index 93fff4ac..17decc7e 100644 --- a/Graphics/Implicit/Export/Render/RefineSegs.hs +++ b/Graphics/Implicit/Export/Render/RefineSegs.hs @@ -5,7 +5,7 @@ -- export one function, which refines polylines. module Graphics.Implicit.Export.Render.RefineSegs (refine) where -import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, tail, sqrt, (<=)) +import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, (<=)) import Graphics.Implicit.Definitions (ℝ, ℝ2, Polyline(Polyline), minℝ, ℕ, Obj2, (⋅), normalizeℝ2, sqrt) diff --git a/Graphics/Implicit/Export/SymbolicObj2.hs b/Graphics/Implicit/Export/SymbolicObj2.hs index 6cd51921..aed4c175 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, max, fromInteger, fromIntegral, ceiling) -import Graphics.Implicit.Definitions (ℝ, Fastℕ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline(Polyline), Polytri(Polytri), (⋯*), cos, sin, π, sqrt) +import Graphics.Implicit.Definitions (ℝ, ℝ2, Fastℕ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline(Polyline), Polytri(Polytri), (⋯*), cos, sin, π, sqrt) import Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 960d1b77..0c1627a9 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -11,9 +11,9 @@ module Graphics.Implicit.RationalUtil (ℚ(..), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) where -import Prelude (RealFrac(properFraction), Fractional(fromRational, (/)), Ord, Double, Show(show), Eq, Num((+), (*), abs, negate, signum, fromInteger), Real(toRational), Read(readsPrec), ($), seq, (==), floor, Float, map) +import Prelude (RealFrac(properFraction, ceiling, floor, round, truncate), Fractional(fromRational, (/)), Ord, Double, Show(show), Eq, Num((+), (*), abs, negate, signum, fromInteger), Real(toRational), Read(readsPrec), ($), seq, (==), floor, Float, map) -import qualified Prelude as P ((+), (-), (*), abs, signum, negate, (/), (**), realToFrac, sqrt, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, pi, exp, log, fromIntegral, toRational, properFraction, show, fromRational, readsPrec) +import qualified Prelude as P ((+), (-), (*), abs, signum, negate, (/), (**), realToFrac, sqrt, cos, sin, tan, asin, acos, atan, sinh, cosh, tanh, atan2, pi, exp, log, fromIntegral, toRational, properFraction, show, fromRational, readsPrec, ceiling, floor, round, truncate) import Data.AffineSpace (AffineSpace(Diff, (.-.), (.+^))) @@ -179,22 +179,35 @@ instance Read ℝ where where promoteFst :: (Double, a) -> (ℝ, a) promoteFst (q, r) = (ℝ q, r) + {-# INLINABLE readsPrec #-} -- readsListPrec -- readsList instance Show ℝ where - show (ℝ a) = P.show a + show (ℝ a) = P.show a + {-# INLINABLE show #-} + instance RealFrac ℝ where + ceiling (ℝ a) = P.ceiling a + {-# INLINABLE ceiling #-} + floor (ℝ a) = P.floor a + {-# INLINABLE floor #-} + truncate (ℝ a) = P.truncate a + {-# INLINABLE truncate #-} + round (ℝ a) = P.round a + {-# INLINABLE round #-} properFraction (ℝ a) = promoteSnd $ P.properFraction a where promoteSnd :: (a, Double) -> (a, ℝ) promoteSnd (q, r) = (q, ℝ r) - -- ceiling, floor, truncate, round. + {-# INLINABLE properFraction #-} instance Fractional ℝ where fromRational x = ℝ $ P.fromRational x + {-# INLINABLE fromRational #-} (/) (ℝ x) (ℝ y) = ℝ $ (P./) x y + {-# INLINABLE (/) #-} instance Real ℝ where toRational (ℝ a) = P.toRational a @@ -210,39 +223,49 @@ instance Num ℝ where fromℕtoℝ :: ℕ -> ℝ fromℕtoℝ a = ℝ $ P.realToFrac a +{-# INLINABLE fromℕtoℝ #-} fromFastℕtoℝ :: Fastℕ -> ℝ fromFastℕtoℝ a = ℝ $ P.realToFrac a - ---fromInttoℝ :: Int -> ℝ ---fromInttoℝ a = ℝ $ P.realToFrac a +{-# INLINABLE fromFastℕtoℝ #-} fromℝtoFloat :: ℝ -> Float fromℝtoFloat a = (P.realToFrac a :: Float) +{-# INLINABLE fromℝtoFloat #-} fromℝtoℕ :: ℝ -> Maybe ℕ fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing +{-# INLINABLE fromℝtoℕ #-} instance AdditiveGroup ℝ where zeroV = ℝ $ 0 + {-# INLINABLE zeroV #-} (^-^) (ℝ a) (ℝ b) = ℝ $ (P.-) a b + {-# INLINABLE (^-^) #-} (^+^) (ℝ a) (ℝ b) = ℝ $ (P.+) a b + {-# INLINABLE (^+^) #-} negateV (ℝ a) = ℝ $ P.negate a + {-# INLINABLE negateV #-} instance NFData ℝ where rnf (ℝ a) = CDS.rnf a `seq` () + {-# INLINABLE rnf #-} instance VectorSpace ℝ where type Scalar ℝ = ℝ (*^) (ℝ a) (ℝ b) = ℝ $ (P.*) a b + {-# INLINABLE (*^) #-} instance InnerSpace ℝ where (<.>) = (P.*) + {-# INLINABLE (<.>) #-} instance AffineSpace ℝ where type Diff ℝ = ℝ (.-.) = (P.-) + {-# INLINABLE (.-.) #-} (.+^) = (P.+) + {-# INLINABLE (.+^) #-} From 085032306700d55427f2e5bf06508cb88087fc2c Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 16:59:36 +0100 Subject: [PATCH 21/24] move helper functions into the typeclass and its instance, and more inlining. --- Graphics/Implicit/RationalUtil.hs | 49 ++++++++++++++++++------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 0c1627a9..4a5439c7 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -9,7 +9,7 @@ {-# LANGUAGE TypeApplications #-} -module Graphics.Implicit.RationalUtil (ℚ(..), ℝ, fromFastℕtoℝ, fromℕtoℝ, fromℝtoℕ, fromℝtoFloat) where +module Graphics.Implicit.RationalUtil (ℚ(..), ℝ) where import Prelude (RealFrac(properFraction, ceiling, floor, round, truncate), Fractional(fromRational, (/)), Ord, Double, Show(show), Eq, Num((+), (*), abs, negate, signum, fromInteger), Real(toRational), Read(readsPrec), ($), seq, (==), floor, Float, map) @@ -64,8 +64,12 @@ class (RealFrac v) => ℚ v where atan2 :: v -> v -> v normalizeℝ2 :: (v,v) -> (v,v) normalizeℝ3 :: (v,v,v) -> (v,v,v) - toℝ :: v -> ℝ - fromℝ :: ℝ -> v + toℝ :: v -> v + fromℕtoℝ :: ℕ -> v + fromFastℕtoℝ :: Fastℕ -> v + fromℝ :: v -> v + fromℝtoFloat :: v -> Float + fromℝtoℕ :: v -> Maybe ℕ (%) :: ℕ -> ℕ -> v -- CUT HERE -- @@ -168,11 +172,24 @@ instance ℚ ℝ where cbrt (ℝ x) = ℝ $ (P.**(1/3)) x normalizeℝ2 (ℝ x, ℝ y) = bothℝ $ (x, y) ^/ magnitude (x, y) where bothℝ (a, b) = (ℝ a, ℝ b) + {-# INLINABLE normalizeℝ2 #-} normalizeℝ3 (ℝ x, ℝ y, ℝ z) = allThreeℝ $ (x, y, z) ^/ magnitude (x, y, z) where allThreeℝ (a, b, c) = (ℝ a, ℝ b, ℝ c) - fromℝ (ℝ x) = P.realToFrac x + {-# INLINABLE normalizeℝ3 #-} toℝ a = a + {-# INLINABLE toℝ #-} + fromℕtoℝ a = ℝ $ P.realToFrac a + {-# INLINABLE fromℕtoℝ #-} + fromFastℕtoℝ a = ℝ $ P.realToFrac a + {-# INLINABLE fromFastℕtoℝ #-} + fromℝtoFloat a = (P.realToFrac a :: Float) + {-# INLINABLE fromℝtoFloat #-} + fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing + {-# INLINABLE fromℝtoℕ #-} + fromℝ (ℝ x) = P.realToFrac x + {-# INLINABLE fromℝ #-} (%) a b = ℝ $ (P./) (P.fromIntegral a) (P.fromIntegral b) + {-# INLINABLE (%) #-} instance Read ℝ where readsPrec prec input = map promoteFst $ (P.readsPrec prec input) @@ -180,6 +197,7 @@ instance Read ℝ where promoteFst :: (Double, a) -> (ℝ, a) promoteFst (q, r) = (ℝ q, r) {-# INLINABLE readsPrec #-} +-- FIXME: implement: -- readsListPrec -- readsList @@ -187,7 +205,6 @@ instance Show ℝ where show (ℝ a) = P.show a {-# INLINABLE show #-} - instance RealFrac ℝ where ceiling (ℝ a) = P.ceiling a {-# INLINABLE ceiling #-} @@ -215,27 +232,17 @@ instance Real ℝ where instance Num ℝ where (+) (ℝ a) (ℝ b) = ℝ $ (P.+) a b + {-# INLINABLE (+) #-} (*) (ℝ a) (ℝ b) = ℝ $ (P.*) a 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 = ℝ $ P.realToFrac a - -fromℕtoℝ :: ℕ -> ℝ -fromℕtoℝ a = ℝ $ P.realToFrac a -{-# INLINABLE fromℕtoℝ #-} - -fromFastℕtoℝ :: Fastℕ -> ℝ -fromFastℕtoℝ a = ℝ $ P.realToFrac a -{-# INLINABLE fromFastℕtoℝ #-} - -fromℝtoFloat :: ℝ -> Float -fromℝtoFloat a = (P.realToFrac a :: Float) -{-# INLINABLE fromℝtoFloat #-} - -fromℝtoℕ :: ℝ -> Maybe ℕ -fromℝtoℕ n = if n == fromℕtoℝ (floor n) then Just (floor n) else Nothing -{-# INLINABLE fromℝtoℕ #-} + {-# INLINABLE fromInteger #-} instance AdditiveGroup ℝ where zeroV = ℝ $ 0 From 637cef198dbb0a6e1ad62bad2faf9ba2a43a5ab4 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Thu, 30 May 2019 17:18:18 +0100 Subject: [PATCH 22/24] finish off inlining. --- Graphics/Implicit/RationalUtil.hs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Graphics/Implicit/RationalUtil.hs b/Graphics/Implicit/RationalUtil.hs index 4a5439c7..cfd4c27b 100644 --- a/Graphics/Implicit/RationalUtil.hs +++ b/Graphics/Implicit/RationalUtil.hs @@ -150,26 +150,46 @@ newtype ℝ = ℝ Double -- Use this instance when ℝ ~ Double instance ℚ ℝ where π = ℝ $ P.pi + {-# INLINABLE π #-} minℝ = ℝ $ 0.0000000000000002 + {-# INLINABLE minℝ #-} -- yes, these are nonsense. never meant to be evaluated. infty = ℝ $ 1/0 + {-# INLINABLE infty #-} neginfty = ℝ $ -1/0 + {-# INLINABLE neginfty #-} powℝ (ℝ a) b = ℝ $ a P.** (P.fromIntegral b) + {-# INLINABLE powℝ #-} powℝℝ (ℝ a) (ℝ b) = ℝ $ a P.** b + {-# INLINABLE powℝℝ #-} sin (ℝ x) = ℝ $ P.sin x + {-# INLINABLE sin #-} cos (ℝ x) = ℝ $ P.cos x + {-# INLINABLE cos #-} tan (ℝ x) = ℝ $ P.tan x + {-# INLINABLE tan #-} asin (ℝ x) = ℝ $ P.asin x + {-# INLINABLE asin #-} acos (ℝ x) = ℝ $ P.acos x + {-# INLINABLE acos #-} atan (ℝ x) = ℝ $ P.atan x + {-# INLINABLE atan #-} sinh (ℝ x) = ℝ $ P.sinh x + {-# INLINABLE sinh #-} cosh (ℝ x) = ℝ $ P.cosh x + {-# INLINABLE cosh #-} tanh (ℝ x) = ℝ $ P.tanh x + {-# INLINABLE tanh #-} atan2 (ℝ x) (ℝ y) = ℝ $ P.atan2 x y + {-# INLINABLE atan2 #-} exp (ℝ x) = ℝ $ P.exp x + {-# INLINABLE exp #-} log (ℝ x) = ℝ $ P.log x + {-# INLINABLE log #-} sqrt (ℝ x) = ℝ $ P.sqrt x + {-# INLINABLE sqrt #-} cbrt (ℝ x) = ℝ $ (P.**(1/3)) x + {-# INLINABLE cbrt #-} normalizeℝ2 (ℝ x, ℝ y) = bothℝ $ (x, y) ^/ magnitude (x, y) where bothℝ (a, b) = (ℝ a, ℝ b) {-# INLINABLE normalizeℝ2 #-} From 2a2e2ca7f79dc5c44bcf88f2e44da04b598ffb27 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sat, 22 Jun 2019 17:00:29 +0100 Subject: [PATCH 23/24] remove merge noise. --- Graphics/Implicit/ExtOpenScad/Primitives.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index c5c9e505..c1c07a63 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -454,8 +454,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 (<|>) = mplus From 40fab5edf2c34de251a42b3fab72d14ad65aac90 Mon Sep 17 00:00:00 2001 From: Julia Longtin Date: Sun, 14 Jul 2019 12:59:42 +0100 Subject: [PATCH 24/24] merge E related changes from master. --- Graphics/Implicit/ExtOpenScad/Parser/Expr.hs | 58 +++++++++----------- tests/ParserSpec/Expr.hs | 9 ++- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 3a485915..e1202c25 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -14,7 +14,7 @@ import Prelude (Char, Maybe(Nothing, Just), String, fmap, ($), (.), (>>), return import Graphics.Implicit.Definitions (powℝ) -- The parsec parsing library. -import Text.Parsec (string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try, optional) +import Text.Parsec (string, many1, digit, char, many, noneOf, sepBy, sepBy1, optionMaybe, try, optional, option) import Text.Parsec.String (GenParser) @@ -48,40 +48,36 @@ literal = ("literal" ?:) $ *<|> "number" ?: ( do a <- many1 digit - _ <- char 'e' - _ <- optional $ char '+' - b <- many1 digit - return . LitE $ ONum $ read a * (10 `powℝ` read b) - *<|> do - a <- many1 digit - _ <- char '.' - b <- many digit + b <- option "" ( + do + _ <- char '.' + e <- many1 digit + return ("." ++ e) + ) _ <- char 'e' _ <- optional $ char '+' c <- many1 digit - return . LitE $ ONum $ read (a ++ "." ++ b) * (10 `powℝ` read c) - *<|> do - a <- many1 digit - _ <- char 'e' - _ <- char '-' - b <- many1 digit - return . LitE $ ONum $ read a / (10 `powℝ` read b) + return . LitE $ ONum $ read (a ++ b) * (10 `powℝ` read c) *<|> do a <- many1 digit - _ <- char '.' - b <- many digit - _ <- char 'e' - _ <- char '-' + b <- option "" ( + do + _ <- char '.' + e <- many1 digit + return ("." ++ e) + ) + _ <- string "e-" c <- many1 digit - return . LitE $ ONum $ read (a ++ "." ++ b) / (10 `powℝ` read c) - *<|> do - a <- many1 digit - _ <- char '.' - b <- many digit - return . LitE $ ONum $ read (a ++ "." ++ b) + return . LitE $ ONum $ read (a ++ b) / (10 `powℝ` read c) *<|> do a <- many1 digit - return . LitE $ ONum $ read a + b <- option "" ( + do + _ <- char '.' + e <- many1 digit + return ("." ++ e) + ) + return . LitE $ ONum $ read (a ++ b) ) *<|> "string" ?: do _ <- char '"' @@ -98,15 +94,11 @@ literal = ("literal" ?:) $ letExpr :: GenParser Char st Expr letExpr = "let expression" ?: do _ <- string "let" - _ <- genSpace - _ <- char '(' - _ <- genSpace + _ <- padChar '(' bindingPairs <- sepBy ( do _ <- genSpace boundName <- variableSymb - _ <- genSpace - _ <- char '=' - _ <- genSpace + _ <- padChar '=' boundExpr <- expr0 return $ ListE [Var boundName, boundExpr]) (char ',') diff --git a/tests/ParserSpec/Expr.hs b/tests/ParserSpec/Expr.hs index 41030fbc..c6d58ad7 100644 --- a/tests/ParserSpec/Expr.hs +++ b/tests/ParserSpec/Expr.hs @@ -75,9 +75,12 @@ literalSpec = do it "handles floats" $ "23.42" --> num 23.42 describe "E notation" $ do - it "accepts e with positive sign" $ "1e+1" --> num 10 - it "accepts e with negative sign" $ "10e-1" --> num 1 - it "accepts e with no sign" $ "1e1" --> num 10 + it "accepts integer e with positive sign" $ "1e+1" --> num 10 + it "accepts integer e with negative sign" $ "10e-1" --> num 1 + it "accepts integer e with no sign" $ "1e1" --> num 10 + it "accepts floating e with positive sign" $ "1.1e+1" --> num 11 + it "accepts floating e with negative sign" $ "1.28e-1" --> num 0.128 + it "accepts floating e with no sign" $ "1.1e1" --> num 11 describe "booleans" $ do it "accepts true" $ "true" --> bool True it "accepts false" $ "false" --> bool False