diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 71104eeb..732ae178 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -16,12 +16,11 @@ module Graphics.Implicit.Definitions ( module F, module N, - ℝ, + module R, ℝ2, both, ℝ3, allthree, - minℝ, (⋅), (⋯*), (⋯/), @@ -72,38 +71,29 @@ module Graphics.Implicit.Definitions ( ExtrudeRotateR, ExtrudeRM, ExtrudeOnEdgeOf, - RotateExtrude), - fromℕtoℝ, - fromFastℕtoℝ, - fromℝtoFloat + RotateExtrude) ) where -import Prelude (Show, Double, Either, show, (*), (/), fromIntegral, Float, realToFrac) +import Prelude (Show, Either, show, (*), (/)) import Data.Maybe (Maybe) import Data.VectorSpace (Scalar, InnerSpace, (<.>)) -import Graphics.Implicit.FastIntUtil as F (Fastℕ(Fastℕ), fromFastℕ, toFastℕ) +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ℕ) +import Graphics.Implicit.FastIntUtil as F (Fastℕ(Fastℕ), fromFastℕ, toFastℕ) + import Control.DeepSeq (NFData, rnf) -- Let's make things a bit nicer. -- Following the math notation ℝ, ℝ², ℝ³... -type ℝ = Double type ℝ2 = (ℝ,ℝ) type ℝ3 = (ℝ,ℝ,ℝ) --- | A give up point for dividing ℝs -minℝ :: ℝ --- for Doubles. -minℝ = 0.0000000000000002 --- for Floats. ---minℝ = 0.00000011920928955078125 * 2 - -- | apply a function to both items in the provided tuple. both :: forall t b. (t -> b) -> (t, t) -> (b, b) both f (x,y) = (f x, f y) @@ -119,23 +109,6 @@ allthree f (x,y,z) = (f x, f y, f z) (⋅) = (<.>) {-# INLINABLE (⋅) #-} --- Wrap the functions that convert datatypes. - --- | Convert from our Integral to our Rational. -fromℕtoℝ :: ℕ -> ℝ -fromℕtoℝ = fromIntegral -{-# INLINABLE fromℕtoℝ #-} - --- | Convert from our Fast Integer (int32) to ℝ. -fromFastℕtoℝ :: Fastℕ -> ℝ -fromFastℕtoℝ (Fastℕ a) = fromIntegral a -{-# INLINABLE fromFastℕtoℝ #-} - --- | Convert from our rational to a float, for output to a file. -fromℝtoFloat :: ℝ -> Float -fromℝtoFloat = realToFrac -{-# INLINABLE fromℝtoFloat #-} - -- | add aditional instances to Show, for when we dump the intermediate form of objects. -- FIXME: store functions in a dumpable form! -- These instances cover functions @@ -291,4 +264,3 @@ data SymbolicObj3 = SymbolicObj2 -- object to extrude | ExtrudeOnEdgeOf SymbolicObj2 SymbolicObj2 deriving Show - diff --git a/Graphics/Implicit/Export/RayTrace.hs b/Graphics/Implicit/Export/RayTrace.hs index 9df15830..abfb7392 100644 --- a/Graphics/Implicit/Export/RayTrace.hs +++ b/Graphics/Implicit/Export/RayTrace.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.Export.RayTrace( Color(Color), average, Camera(Camera), import Prelude(Show, RealFrac, Maybe(Just, Nothing), Bool(False, True), (-), (.), ($), (*), (/), min, fromInteger, max, round, fromIntegral, unzip, map, length, sum, maximum, minimum, (>), (+), (<), (==), pred, flip, not, abs, floor, toRational, otherwise) -- Our number system, and the definition of a 3D object. -import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, ℝ3, (⋅), Obj3) +import Graphics.Implicit.Definitions (ℝ, Fastℕ, ℝ2, ℝ3, (⋅), Obj3, normalizeℝ3, sqrt) import Codec.Picture (Pixel8) @@ -18,7 +18,7 @@ import Control.Monad (guard, return) import Control.Arrow ((***)) -import Data.VectorSpace (Scalar, magnitude, (^+^), (*^), normalized, (^-^), InnerSpace) +import Data.VectorSpace (magnitudeSq, (^+^), (*^), (^-^), InnerSpace) import Data.Cross (cross3) @@ -45,8 +45,8 @@ data Color = Color Pixel8 Pixel8 Pixel8 Pixel8 -- Math -- | The distance traveled by a line segment from the first point to the second point. -vectorDistance :: ℝ3 -> ℝ3 -> Scalar ℝ3 -vectorDistance a b = magnitude (b-a) +vectorDistance :: ℝ3 -> ℝ3 -> ℝ +vectorDistance a b = sqrt $ magnitudeSq (b-a) -- | Multiply a colour by an intensity. colorMult :: Pixel8 -> Color -> Color @@ -77,13 +77,13 @@ 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 -- | Create a ray from two points. 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 = @@ -153,15 +153,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 @@ -175,5 +175,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/GetSegs.hs b/Graphics/Implicit/Export/Render/GetSegs.hs index d15f6e19..0fbe76aa 100644 --- a/Graphics/Implicit/Export/Render/GetSegs.hs +++ b/Graphics/Implicit/Export/Render/GetSegs.hs @@ -4,10 +4,12 @@ module Graphics.Implicit.Export.Render.GetSegs (getSegs) where -import Prelude(Bool(True, False), sqrt, (+), (*), (/=), map, (.), filter, ($), (<=)) +import Prelude(Bool(True, False), (+), (*), (/=), map, (.), filter, ($), (<=)) + +import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline(Polyline), sqrt) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Obj2, Polyline(Polyline)) import Graphics.Implicit.Export.Render.RefineSegs (refine) + import Graphics.Implicit.Export.Util (centroid) import Data.VectorSpace ((^-^)) diff --git a/Graphics/Implicit/Export/Render/RefineSegs.hs b/Graphics/Implicit/Export/Render/RefineSegs.hs index bb02e66a..3c1c8c4d 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, sqrt, (<=)) +import Prelude((<), (/), (++), (*), ($), (&&), (-), (+), (.), (>), abs, (<=)) + +import Graphics.Implicit.Definitions (ℝ, ℝ2, Polyline(Polyline), minℝ, Fastℕ, Obj2, (⋅), normalizeℝ2, sqrt) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Polyline(Polyline), minℝ, Fastℕ, Obj2, (⋅)) import Graphics.Implicit.Export.Util (centroid) -import Data.VectorSpace (normalized, magnitude, (^-^), (^*), (^+^)) +import Data.VectorSpace (magnitudeSq, (^-^), (^*), (^+^)) default (Fastℕ, ℝ) @@ -39,7 +40,7 @@ detail n res obj (Polyline [p1, p2]) | n < 2 = then Polyline [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 @@ -54,7 +55,7 @@ detail n res obj (Polyline [p1, p2]) | n < 2 = 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 + if abs derivNormSq > 0.09 && abs derivNormSq < 4 && abs (midval/(sqrt derivNormSq)) < 3*res then let (dX, dY) = (- derivX*midval/derivNormSq, - derivY*midval/derivNormSq) @@ -74,7 +75,7 @@ simplify _ = {-simplify3 . simplify2 res . -} simplify1 simplify1 :: Polyline -> Polyline simplify1 (Polyline (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 (Polyline (a:c:xs)) else addPolylines (Polyline [a]) (simplify1 (Polyline (b:c:xs))) simplify1 a = a diff --git a/Graphics/Implicit/Export/Render/TesselateLoops.hs b/Graphics/Implicit/Export/Render/TesselateLoops.hs index c6574e94..d2f554c9 100644 --- a/Graphics/Implicit/Export/Render/TesselateLoops.hs +++ b/Graphics/Implicit/Export/Render/TesselateLoops.hs @@ -6,13 +6,13 @@ 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(TriangleMesh), (⋅), Triangle(Triangle)) +import Graphics.Implicit.Definitions (ℝ, ℕ, Obj3, ℝ3, TriangleMesh(TriangleMesh), (⋅), Triangle(Triangle), 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) @@ -25,7 +25,6 @@ tesselateLoop _ _ [] = [] tesselateLoop _ _ [[a,b],[_,c],[_,_]] = [Tris $ TriangleMesh [Triangle (a,b,c)]] - {- #____# #____# | | | | @@ -54,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) ] @@ -83,7 +82,7 @@ tesselateLoop res obj pathSides = return $ Tris $ TriangleMesh $ 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/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 fc9e6ae8..c2051784 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 (ℝ) + -- | Slightly stretch the bounding box of an object, in order to -- ensure that during mesh generation, there are no problems because -- values are right at the edge. diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index 9ff0341e..6c4bba55 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -8,14 +8,15 @@ -- output SCAD code, AKA an implicitcad to openscad converter. module Graphics.Implicit.Export.SymbolicFormats (scad2, scad3) where -import Prelude(Maybe(Just, Nothing), Either(Left), ($), (.), (*), map, ($!), (-), (/), pi, error, (+), (==), 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.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.Function (fix) default (ℝ) @@ -26,9 +27,9 @@ 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... callToken :: (Text, Text) -> Builder -> [Builder] -> [Reader a Builder] -> Reader a Builder @@ -96,8 +97,8 @@ 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 <- take (floor (res / height)) $ fix (\f x -> x : f (x+res)) 0 - ] + ] | h <- take (floor (res / height)) $ fix (\f x -> x : f (x+res)) 0 + ] -- FIXME: where are RotateExtrude, ExtrudeOnEdgeOf? @@ -114,8 +115,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 48972245..cec9d643 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, ceiling) +import Prelude(map, ($), (-), (/), (+), (>), (*), reverse, max, ceiling) -import Graphics.Implicit.Definitions (ℝ, ℝ2, Fastℕ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline(Polyline), Polytri(Polytri), (⋯*), fromFastℕtoℝ) +import Graphics.Implicit.Definitions (ℝ, ℝ2, Fastℕ, SymbolicObj2(RectR, Circle, Translate2, Scale2), Polyline(Polyline), Polytri(Polytri), (⋯*), cos, sin, π, sqrt, fromFastℕtoℝ) import Graphics.Implicit.Export.MarchingSquaresFill (getContourMesh) @@ -23,8 +23,9 @@ import Graphics.Implicit.Export.Symbolic.Rebound2 (rebound2) import Graphics.Implicit.Export.Render (getContour) -import Data.VectorSpace ((^/), magnitude) +import Data.VectorSpace ((^/), magnitudeSq) +-- FIXME: magic number: 0.1 symbolicGetOrientedContour :: ℝ -> SymbolicObj2 -> [Polyline] symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res symbObj where @@ -34,7 +35,7 @@ symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res sym orient (Polyline points@(p1:p2:_)) = let v = (\(a,b) -> (b, -a)) (p2 - p1) - dv = v ^/ (magnitude v / res / 0.1) + dv = v ^/ ((sqrt $ magnitudeSq v) / res / 0.1) in if obj (p1 + dv) - obj p1 > 0 then Polyline points else Polyline $ reverse points @@ -43,10 +44,10 @@ symbolicGetOrientedContour res symbObj = map orient $ symbolicGetContour res sym symbolicGetContour :: ℝ -> SymbolicObj2 -> [Polyline] symbolicGetContour _ (RectR 0 (x1,y1) (x2,y2)) = [Polyline [ (x1,y1), (x2,y1), (x2,y2), (x1,y2), (x1,y1) ]] --- FIXME: magic number. -symbolicGetContour res (Circle r) = [Polyline [ ( r*cos(2*pi*fromFastℕtoℝ m/fromFastℕtoℝ n), r*sin(2*pi*fromFastℕtoℝ m/fromFastℕtoℝ n) ) | m <- [0.. n] ]] where +-- FIXME: magic number: 5 +symbolicGetContour res (Circle r) = [Polyline [ ( r*cos(2*π*fromFastℕtoℝ m/fromFastℕtoℝ n), r*sin(2*π*fromFastℕtoℝ m/fromFastℕtoℝ n) ) | m <- [0.. n] ]] where n :: Fastℕ - n = max 5 $ ceiling $ 2*pi*r/res + n = max 5 $ ceiling $ 2*π*r/res symbolicGetContour res (Translate2 v obj) = appOpPolylines (+ v) $ symbolicGetContour res obj symbolicGetContour res (Scale2 s@(a,b) obj) = appOpPolylines (⋯* s) $ symbolicGetContour (res/sc) obj where sc = max a b @@ -64,14 +65,14 @@ symbolicGetContourMesh res (Translate2 v obj) = map (\(Polytri (a,b,c)) -> Polyt symbolicGetContourMesh res (Scale2 s@(a,b) obj) = map (\(Polytri (c,d,e)) -> Polytri (c ⋯* s, d ⋯* s, e ⋯* s) ) $ symbolicGetContourMesh (res/sc) obj where sc = max a b symbolicGetContourMesh _ (RectR 0 (x1,y1) (x2,y2)) = [Polytri ((x1,y1), (x2,y1), (x2,y2)), Polytri ((x2,y2), (x1,y2), (x1,y1)) ] --- FIXME: magic number. +-- FIXME: magic number: 5 symbolicGetContourMesh res (Circle r) = [ Polytri ((0,0), - (r*cos(2*pi*fromFastℕtoℝ m/fromFastℕtoℝ n), r*sin(2*pi*fromFastℕtoℝ m/fromFastℕtoℝ n)), - (r*cos(2*pi*fromFastℕtoℝ (m+1)/fromFastℕtoℝ n), r*sin(2*pi*fromFastℕtoℝ (m+1)/fromFastℕtoℝ n)) + (r*cos(2*π*fromFastℕtoℝ m/fromFastℕtoℝ n), r*sin(2*π*fromFastℕtoℝ m/fromFastℕtoℝ n)), + (r*cos(2*π*fromFastℕtoℝ (m+1)/fromFastℕtoℝ n), r*sin(2*π*fromFastℕtoℝ (m+1)/fromFastℕtoℝ n)) )| m <- [0.. n-1] ] where n :: Fastℕ - n = max 5 $ ceiling $ 2*pi*r/res + n = max 5 $ 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 a4ddae67..77f51478 100644 --- a/Graphics/Implicit/Export/TextBuilderUtils.hs +++ b/Graphics/Implicit/Export/TextBuilderUtils.hs @@ -46,7 +46,7 @@ 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 2f87c8ee..750226eb 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 (Float, Eq, Bool, ($), (+), map, (.), toEnum, length, zip, return, (==), (||), (&&), filter, not) -import Graphics.Implicit.Definitions (Triangle(Triangle), TriangleMesh(TriangleMesh), ℕ, ℝ3, ℝ, fromℝtoFloat) +import Graphics.Implicit.Definitions (Triangle(Triangle), TriangleMesh(TriangleMesh), ℕ, ℝ, ℝ3, normalizeℝ3, fromℝtoFloat) + import Graphics.Implicit.Export.TextBuilderUtils (Text, Builder, toLazyText, (<>), bf, buildℕ) import Blaze.ByteString.Builder (Write, writeStorable, toLazyByteString, fromByteString, fromWord32le, fromWord16le, fromWrite) @@ -26,7 +27,7 @@ import Data.ByteString (replicate) import Data.ByteString.Lazy (ByteString) import Data.Storable.Endian (LittleEndian(LE)) -import Data.VectorSpace (normalized, (^-^)) +import Data.VectorSpace ((^-^)) import Data.Cross (cross3) unmesh :: TriangleMesh -> [Triangle] @@ -34,7 +35,7 @@ unmesh (TriangleMesh m) = m normal :: (ℝ3,ℝ3,ℝ3) -> ℝ3 normal (a,b,c) = - normalized $ (b ^-^ a) `cross3` (c ^-^ a) + normalizeℝ3 $ (b ^-^ a) `cross3` (c ^-^ a) -- | Removes triangles that are empty when converting their positions to Float resolution. cleanupTris :: TriangleMesh -> TriangleMesh diff --git a/Graphics/Implicit/Export/Util.hs b/Graphics/Implicit/Export/Util.hs index 20c28350..2dd063b6 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(Triangle), NormedTriangle(NormedTriangle)) +import Graphics.Implicit.Definitions (ℝ, ℝ3, Obj3, Triangle(Triangle), NormedTriangle(NormedTriangle), normalizeℝ3, fromℕtoℝ, toℕ) -import Data.VectorSpace (VectorSpace, Scalar, (^+^), (*^), (^/), (^-^), normalized, zeroV) +import Data.VectorSpace (VectorSpace, Scalar, (^+^), (*^), (^/), (^-^), zeroV) -- | Change the default for bare numbers in this file. default (ℝ) @@ -44,14 +46,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 #-} {- diff --git a/Graphics/Implicit/ExtOpenScad/Default.hs b/Graphics/Implicit/ExtOpenScad/Default.hs index 18be1083..bab93058 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, fst, snd) +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, fst, snd) -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(VarLookup), OVal(OList, ONum, OString, OUndefined, OError, OModule, OFunc), Symbol(Symbol)) import Graphics.Implicit.ExtOpenScad.Util.OVal (toOObj, oTypeStr) import Graphics.Implicit.ExtOpenScad.Primitives (primitives) @@ -30,11 +31,12 @@ defaultObjects = VarLookup $ fromList $ -- Missing standard ones: -- rand, lookup, +-- FIXME: what standard? defaultConstants :: [(Symbol, OVal)] defaultConstants = map (\(a,b) -> (a, toOObj (b :: ℝ) )) - [(Symbol "pi", pi), - (Symbol "PI", pi)] + [(Symbol "pi", π), + (Symbol "PI", π)] defaultFunctions :: [(Symbol, OVal)] defaultFunctions = map (\(a,b) -> (a, toOObj ( b :: ℝ -> ℝ))) @@ -66,7 +68,7 @@ defaultFunctions2 = map (\(a,b) -> (a, toOObj (b :: ℝ -> ℝ -> ℝ) )) (Symbol "max", max), (Symbol "min", min), (Symbol "atan2", atan2), - (Symbol "pow", (**)) + (Symbol "pow", powℝℝ) ] defaultFunctionsSpecial :: [(Symbol, OVal)] @@ -95,7 +97,7 @@ defaultPolymorphicFunctions = (Symbol "/", divide), (Symbol "-", toOObj sub), (Symbol "%", toOObj omod), - (Symbol "^", toOObj ((**) :: ℝ -> ℝ -> ℝ)), + (Symbol "^", toOObj (powℝℝ :: ℝ -> ℝ -> ℝ)), (Symbol "negate", toOObj negatefun), (Symbol "index", toOObj index), (Symbol "splice", toOObj osplice), diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs index 44013144..3b9df4c5 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Expr.hs @@ -8,7 +8,8 @@ -- A parser for a numeric expressions. module Graphics.Implicit.ExtOpenScad.Parser.Expr(expr0) where -import Prelude (Char, Maybe(Nothing, Just), String, (.), (>>), return, ($), (++), id, foldl, map, foldl1, unzip, tail, zipWith3, foldr, (==), length, mod, head, (&&)) +import Prelude (Char, Maybe(Nothing, Just), String, ($), (.), (>>), return, (++), id, foldl, map, foldl1, unzip, tail, zipWith3, foldr, (==), length, mod, head, (&&)) + -- The parsec parsing library. import Text.Parsec (oneOf, string, many1, many, sepBy, sepBy1, optionMaybe, try, option) diff --git a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs index 9fc8ebdf..1f24797c 100644 --- a/Graphics/Implicit/ExtOpenScad/Parser/Util.hs +++ b/Graphics/Implicit/ExtOpenScad/Parser/Util.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.ExtOpenScad.Parser.Util ((*<|>), (?:), tryMany, patternMatcher, sourcePosition, number, variable, boolean, scadString, scadUndefined) where -import Prelude (String, Char, ($), foldl1, map, (.), return, (>>), Bool(True, False), read, (**), (*), (==), (++)) +import Prelude (String, Char, ($), foldl1, map, (.), return, (>>), Bool(True, False), read, (*), (==), (++)) import Text.Parsec (SourcePos, (<|>), (), try, char, sepBy, noneOf, string, many, digit, many1, optional, choice, option, oneOf) @@ -24,7 +24,7 @@ import Data.Functor.Identity (Identity) import Graphics.Implicit.ExtOpenScad.Definitions (Pattern(Wild, Name, ListP), SourcePosition(SourcePosition), Symbol(Symbol), Expr(LitE, Var), OVal(ONum, OString, OBool, OUndefined)) -import Graphics.Implicit.Definitions (toFastℕ) +import Graphics.Implicit.Definitions (toFastℕ, powℝ) -- The lexer. import Graphics.Implicit.ExtOpenScad.Parser.Lexer (matchIdentifier, matchTok, matchUndef, matchTrue, matchFalse, whiteSpace) @@ -97,7 +97,7 @@ number = ("number" ?:) $ do _ <- whiteSpace return . LitE $ ONum $ if d == "0" then read h - else read h * (10 ** read d) + else read h * (10 `powℝ` read d) -- | Parse a variable reference. -- NOTE: abused by the parser for function calls. diff --git a/Graphics/Implicit/ExtOpenScad/Primitives.hs b/Graphics/Implicit/ExtOpenScad/Primitives.hs index 145bca35..c9ef3658 100644 --- a/Graphics/Implicit/ExtOpenScad/Primitives.hs +++ b/Graphics/Implicit/ExtOpenScad/Primitives.hs @@ -24,9 +24,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, otherwise) +import Prelude(String, IO, Either(Left, Right), Bool(False), Maybe(Just, Nothing), ($), return, either, id, (-), (==), (&&), (<), (*), (/), (>), const, uncurry, fmap, fromInteger, round, (/=), (||), not, null, map, (++), putStrLn, otherwise) -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, Symbol(Symbol)) @@ -204,7 +204,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 @@ -224,7 +224,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]]] -- | FIXME: handle rectangles that are not grid alligned. -- | FIXME: allow for rounding of polygon corners, specification of vertex ordering. @@ -317,7 +317,7 @@ translate = moduleWithSuite "translate" $ \children -> do objMap (Prim.translate (x,y)) (Prim.translate (x,y,z)) children deg2rad :: ℝ -> ℝ -deg2rad x = x / 180 * pi +deg2rad x = x / 180 * π -- This is mostly insane -- | FIXME: rotating a module that is not found returns no geometry, instead of an error. @@ -488,13 +488,12 @@ 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 moduleWithSuite :: String -> ([OVal] -> ArgParser (IO [OVal])) -> (Symbol, [OVal] -> ArgParser (IO [OVal])) moduleWithSuite name modArgMapper = (Symbol name, modArgMapper) + moduleWithoutSuite :: String -> ArgParser (IO [OVal]) -> (Symbol, b -> ArgParser (IO [OVal])) moduleWithoutSuite name modArgMapper = (Symbol name, const modArgMapper) diff --git a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs index 23bad4ca..6c847a74 100644 --- a/Graphics/Implicit/ExtOpenScad/Util/OVal.hs +++ b/Graphics/Implicit/ExtOpenScad/Util/OVal.hs @@ -11,12 +11,13 @@ {-# 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, OUModule, OError, OObj2, OObj3)) @@ -47,7 +48,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/IntegralUtil.hs b/Graphics/Implicit/IntegralUtil.hs index 3f48f7ab..5b25b531 100644 --- a/Graphics/Implicit/IntegralUtil.hs +++ b/Graphics/Implicit/IntegralUtil.hs @@ -87,7 +87,7 @@ instance Enum ℕ where {-# INLINABLE toEnum #-} fromEnum (ℕ n) = P.fromEnum n {-# INLINABLE fromEnum #-} - + diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index fd6e5df6..ca7d6d7b 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -6,31 +6,32 @@ module Graphics.Implicit.MathUtil (rmax, rmaximum, rminimum, distFromLineSeg, pack, box3sWithin) where -- Explicitly include what we need from Prelude. -import Prelude (Bool, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), sin, asin, pi, max, sqrt, min, compare, (<=), fst, snd, (++), head, flip) +import Prelude (Bool, 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ℝ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ℝ2 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)) = @@ -51,10 +52,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 @@ -62,7 +62,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 525b1ff1..75a42a9d 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -2,15 +2,18 @@ -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE +{-# 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. @@ -47,10 +50,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 @@ -103,8 +103,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 1c18ad0f..88ce9f47 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -5,9 +5,9 @@ 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 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) @@ -34,10 +34,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 0180a57a..df29944c 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -13,9 +13,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 08a22bfc..a8a3ba04 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) @@ -84,7 +84,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) = @@ -100,7 +100,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 $ @@ -140,7 +140,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 @@ -155,7 +155,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/Graphics/Implicit/RationalFunctions.hs b/Graphics/Implicit/RationalFunctions.hs new file mode 100644 index 00000000..7ec80ace --- /dev/null +++ b/Graphics/Implicit/RationalFunctions.hs @@ -0,0 +1,127 @@ +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- 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ℝ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 (ℕ, toℕ, fromℕ) + +import Graphics.Implicit.FastIntUtil (Fastℕ) + +import Data.VectorSpace (InnerSpace, Scalar, (^/), magnitudeSq) + +import Data.Ratio ((%)) + +import GHC.Real (Ratio((:%))) + +-- 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 + | 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 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..cfd4c27b --- /dev/null +++ b/Graphics/Implicit/RationalUtil.hs @@ -0,0 +1,298 @@ +-- 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 TypeFamilies #-} + +{-# LANGUAGE TypeApplications #-} + +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) + +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, (.-.), (.+^))) + +import Data.Maybe (Maybe(Just, Nothing)) + +import Graphics.Implicit.IntegralUtil (ℕ) + +import Graphics.Implicit.FastIntUtil (Fastℕ) + +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? +-- 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ℝ2 :: (v,v) -> (v,v) + normalizeℝ3 :: (v,v,v) -> (v,v,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 -- +{- + +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ℝ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 -- + +newtype ℝ = ℝ Double + deriving (Ord, Eq) + +-- 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 #-} + normalizeℝ3 (ℝ x, ℝ y, ℝ z) = allThreeℝ $ (x, y, z) ^/ magnitude (x, y, z) + where allThreeℝ (a, b, c) = (ℝ a, ℝ b, ℝ c) + {-# 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) + where + promoteFst :: (Double, a) -> (ℝ, a) + promoteFst (q, r) = (ℝ q, r) + {-# INLINABLE readsPrec #-} +-- FIXME: implement: +-- readsListPrec +-- readsList + +instance Show ℝ where + 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) + {-# 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 + {-# INLINABLE toRational #-} + +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 + {-# INLINABLE fromInteger #-} + +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 (.+^) #-} + + + diff --git a/Makefile b/Makefile index 290ef6a1..7e4c90c9 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ RESOPTS=-r 50 SCADOPTS?=-q # Uncomment for profiling support. Note that you will need to recompile all of the libraries, as well. -#PROFILING= --enable-profiling +#PROFILING= --enable-library-profiling ## FIXME: escape this right # Uncomment for valgrind on the examples. diff --git a/implicit.cabal b/implicit.cabal index 465aea4d..638d0e88 100644 --- a/implicit.cabal +++ b/implicit.cabal @@ -86,6 +86,8 @@ Library Graphics.Implicit.ObjectUtil.GetBox3 Graphics.Implicit.ObjectUtil.GetImplicit2 Graphics.Implicit.ObjectUtil.GetImplicit3 + Graphics.Implicit.RationalFunctions + Graphics.Implicit.RationalUtil Graphics.Implicit.ExtOpenScad.Default Graphics.Implicit.ExtOpenScad.Parser.Lexer Graphics.Implicit.ExtOpenScad.Parser.Util @@ -180,7 +182,7 @@ Executable implicitsnap -rtsopts -O2 -optc-O3 - -dynamic +-- -dynamic -- see GHC manual 8.2.1 section 6.5.1. -feager-blackholing -- for debugging. diff --git a/programs/Benchmark.hs b/programs/Benchmark.hs index c6eece71..138f3e9a 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, nfAppIO, defaultMain) @@ -16,8 +16,10 @@ import Graphics.Implicit (union, circle, sphere, SymbolicObj2, SymbolicObj3, wri import Graphics.Implicit.Export.SymbolicObj2 (symbolicGetContour) import Graphics.Implicit.Export.SymbolicObj3 (symbolicGetMesh) --- 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, π) + +default (Fastℕ, ℝ) -- Haskell representations of objects to benchmark. @@ -39,7 +41,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 264281f0..da837621 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), IO, Bool(True, False), FilePath, Show, Eq, String, (++), ($), (*), (/), (==), (>), (**), (-), readFile, minimum, drop, error, map, fst, min, sqrt, tail, take, length, putStrLn, show, (>>=), lookup, return, unlines, filter, not, null, (||), (&&), (.)) +import Prelude (Read(readsPrec), Maybe(Just, Nothing), IO, Bool(True, False), FilePath, Show, Eq, String, (++), ($), (*), (/), (==), (>), (-), readFile, minimum, drop, error, map, fst, min, tail, take, length, putStrLn, show, (>>=), lookup, return, unlines, filter, not, null, (||), (&&), (.)) -- 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(UnionR2), SymbolicObj3(UnionR3), ℝ) +import Graphics.Implicit.Definitions (SymbolicObj2(UnionR2), SymbolicObj3(UnionR3), ℝ, cbrt, sqrt) -- Use default values when a Maybe is Nothing. import Data.Maybe (fromMaybe, maybe) @@ -198,8 +198,8 @@ getRes (vars, _, obj:objs, _) = ((x1,y1,z1),(x2,y2,z2)) = getBox3 (UnionR3 0 (obj:objs)) (x,y,z) = (x2-x1, y2-y1, z2-z1) in case fromMaybe (ONum 1) $ lookupVarIn "$quality" vars 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 (vars, obj:objs, _, _) = diff --git a/programs/implicitsnap.hs b/programs/implicitsnap.hs index 94208f43..92ebca19 100644 --- a/programs/implicitsnap.hs +++ b/programs/implicitsnap.hs @@ -12,7 +12,7 @@ -- Let's be explicit about what we're getting from where :) -import Prelude (IO, Maybe(Just, Nothing), String, Bool(True, False), Show, ($), (++), (>), (.), (-), (/), (*), (**), (==), null, sqrt, min, max, minimum, maximum, show, return, map, otherwise, filter, not) +import Prelude (IO, Maybe(Just, Nothing), String, Bool(True, False), Show, ($), (++), (>), (.), (-), (/), (*), (==), null, min, max, minimum, maximum, show, return, map, otherwise, filter, not) import Control.Applicative ((<|>)) @@ -30,7 +30,8 @@ import Graphics.Implicit.ExtOpenScad.Definitions (OVal(ONum), VarLookup, lookupV 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(UnionR2), SymbolicObj3(UnionR3), ℝ, Polyline, TriangleMesh) + +import Graphics.Implicit.Definitions (SymbolicObj2(UnionR2), SymbolicObj3(UnionR3), ℝ, cbrt, sqrt, Polyline, TriangleMesh) -- Use default values when a Maybe is Nothing. import Data.Maybe (fromMaybe, maybe) @@ -98,8 +99,8 @@ getRes (vars, _, obj:objs, _) = ((x1,y1,z1),(x2,y2,z2)) = getBox3 (UnionR3 0 (obj:objs)) (x,y,z) = (x2-x1, y2-y1, z2-z1) in case fromMaybe (ONum 1) $ lookupVarIn "$quality" vars 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 (vars, obj:objs, _, _) =