From 1f690ca3bafae809f44658ba47a69452fa3b22a2 Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Fri, 20 Nov 2020 09:57:00 -0800 Subject: [PATCH 1/9] Implement ReflectX2 --- Graphics/Implicit/Definitions.hs | 2 ++ Graphics/Implicit/ObjectUtil/GetBox2.hs | 13 ++++++++----- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index f12f76a2..4c148f86 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -47,6 +47,7 @@ module Graphics.Implicit.Definitions ( Translate2, Scale2, Rotate2, + ReflectX2, Shell2, Outset2, EmbedBoxedObj2), @@ -259,6 +260,7 @@ data SymbolicObj2 = | Translate2 ℝ2 SymbolicObj2 | Scale2 ℝ2 SymbolicObj2 | Rotate2 ℝ SymbolicObj2 + | ReflectX2 SymbolicObj2 -- Boundary mods | Outset2 ℝ SymbolicObj2 | Shell2 ℝ SymbolicObj2 diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 603b6240..53b5d07b 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -4,11 +4,11 @@ module Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getBox2R) where -import Prelude(Bool, Fractional, Eq, (==), (||), unzip, minimum, maximum, ($), filter, not, (.), (/), fmap, (-), (+), (*), cos, sin, sqrt, min, max, head, (<), (<>), pi, atan2, (==), (>), show, (&&), otherwise, error) +import Prelude(negate, Bool, Fractional, Eq, (==), (||), unzip, minimum, maximum, ($), filter, not, (.), (/), fmap, (-), (+), (*), cos, sin, sqrt, min, max, head, (<), (<>), pi, atan2, (==), (>), show, (&&), otherwise, error) import Graphics.Implicit.Definitions (ℝ, ℝ2, Box2, (⋯*), SymbolicObj2(Shell2, Outset2, Circle, Translate2, Rotate2, UnionR2, Scale2, RectR, - PolygonR, Complement2, DifferenceR2, IntersectR2, EmbedBoxedObj2), minℝ) + PolygonR, Complement2, DifferenceR2, IntersectR2, EmbedBoxedObj2, ReflectX2), minℝ) import Data.VectorSpace ((^-^), (^+^)) @@ -53,7 +53,7 @@ intersectBoxes (x:xs) = if nmaxx > nminx && nmaxy > nminy then ((nminx, nminy), (nmaxx, nmaxy)) else emptyBox where - ((nminx, nminy), (nmaxx, nmaxy)) = ((max xmin1 xmin2, max ymin1 ymin2), (min xmax1 xmax2, min ymax1 ymax2)) + ((nminx, nminy), (nmaxx, nmaxy)) = ((max xmin1 xmin2, max ymin1 ymin2), (min xmax1 xmax2, min ymax1 ymax2)) ((xmin1, ymin1), (xmax1, ymax1)) = x ((xmin2, ymin2), (xmax2, ymax2)) = intersectBoxes xs @@ -104,6 +104,9 @@ getBox2 (Rotate2 θ symbObj) = , rotate (x2, y1) , rotate (x2, y2) ] +getBox2 (ReflectX2 symbObj) = + let ((x1,y1), (x2,y2)) = getBox2 symbObj + in ((negate x2, y1), (negate x1, y2)) -- Boundary mods getBox2 (Shell2 w symbObj) = outsetBox (w/2) $ getBox2 symbObj @@ -263,7 +266,7 @@ pointRBox (xStart, yStart) travel = twoAxis :: Axis -> Axis -> Direction -> Box2 twoAxis start stop dir | (start == PosX && stop == NegX) || - (start == PosY && stop == NegY) || + (start == PosY && stop == NegY) || (start == NegX && stop == PosX) || (start == NegY && stop == PosY) = crossOne start dir twoAxis start stop dir @@ -310,7 +313,7 @@ pointRBox (xStart, yStart) travel = mixWith :: [ℝ2] -> Box2 mixWith points = ((minimum xPoints, minimum yPoints), (maximum xPoints, maximum yPoints)) where - (xPoints, yPoints) = unzip $ points <> [(xStart, yStart), (xStop, yStop)] + (xPoints, yPoints) = unzip $ points <> [(xStart, yStart), (xStop, yStop)] invertRotation :: Direction -> Direction invertRotation Clockwise = CounterClockwise invertRotation CounterClockwise = Clockwise diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index 37d2acc6..865ef6e9 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -6,7 +6,7 @@ module Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) where import Prelude(abs, (-), (/), sqrt, (*), (+), mod, length, fmap, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, max, cos, sin, 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, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, ReflectX2, Shell2, Outset2, EmbedBoxedObj2)) import Graphics.Implicit.MathUtil (rminimum, rmaximum, distFromLineSeg) @@ -82,6 +82,8 @@ getImplicit2 (Rotate2 θ symbObj) = obj = getImplicit2 symbObj in obj ( x*cos θ + y*sin θ, y*cos θ - x*sin θ) +getImplicit2 (ReflectX2 symbObj) = + \(x,y) -> getImplicit2 symbObj (-x, y) -- Boundary mods getImplicit2 (Shell2 w symbObj) = \p -> let From 214f0c35f1af185a0e8a5bc9ab6d1621f7cf21d6 Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Fri, 20 Nov 2020 09:57:30 -0800 Subject: [PATCH 2/9] Implement ReflectX3 --- Graphics/Implicit/Definitions.hs | 2 ++ Graphics/Implicit/ObjectUtil/GetBox3.hs | 9 ++++++--- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index 4c148f86..f72c6165 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -63,6 +63,7 @@ module Graphics.Implicit.Definitions ( Scale3, Rotate3, Rotate3V, + ReflectX3, Shell3, Outset3, EmbedBoxedObj3, @@ -284,6 +285,7 @@ data SymbolicObj3 = | Scale3 ℝ3 SymbolicObj3 | Rotate3 ℝ3 SymbolicObj3 | Rotate3V ℝ ℝ3 SymbolicObj3 + | ReflectX3 SymbolicObj3 -- Boundary mods | Outset3 ℝ SymbolicObj3 | Shell3 ℝ SymbolicObj3 diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index 5955e90c..26a20882 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, (/), (-), (+), fmap, unzip, ($), (<$>), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise, take, fst, snd) +import Prelude(negate, Eq, Bool(False), Fractional, Either (Left, Right), (==), (||), max, (/), (-), (+), fmap, unzip, ($), (<$>), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise, take, fst, snd) -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), ExtrudeRMScale(C1, C2), (⋯*), fromFastℕtoℝ, fromFastℕ, toScaleFn) +import Graphics.Implicit.Definitions (ℝ, Fastℕ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, ReflectX3, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), ExtrudeRMScale(C1, C2), (⋯*), fromFastℕtoℝ, fromFastℕ, toScaleFn) import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getBox2R) @@ -98,6 +98,9 @@ getBox3 (Rotate3 (a, b, c) symbObj) = ((minimum xs, minimum ys, minimum zs), (maximum xs, maximum ys, maximum zs)) getBox3 (Rotate3V _ v symbObj) = getBox3 (Rotate3 v symbObj) +getBox3 (ReflectX3 symbObj) = + let ((x1,y1,z1), (x2,y2,z2)) = getBox3 symbObj + in ((negate x2, y1, z1), (negate x1, y2, z2)) -- Boundary mods getBox3 (Shell3 w symbObj) = outsetBox (w/2) $ getBox3 symbObj @@ -149,7 +152,7 @@ getBox3 (ExtrudeRM _ twist scale translate symbObj height) = smin s v = min v (s * v) smax s v = max v (s * v) -- FIXME: assumes minimums are negative, and maximums are positive. - scaleEach ((d1, d2),(d3, d4)) = (scalex' * d1, scaley' * d2, scalex' * d3, scaley' * d4) + scaleEach ((d1, d2),(d3, d4)) = (scalex' * d1, scaley' * d2, scalex' * d3, scaley' * d4) in case twist of Left twval -> if twval == 0 then (smin scalex' x1, smin scaley' y1, smax scalex' x2, smax scaley' y2) diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index cfe3472f..3c026635 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -9,7 +9,7 @@ import Prelude (Either(Left, Right), abs, (-), (/), (*), sqrt, (+), atan2, max, import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, - Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, + Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, ReflectX3, ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR), fromℕtoℝ, toScaleFn, (⋅), minℝ) import Graphics.Implicit.MathUtil (rmaximum, rminimum, rmax) @@ -100,6 +100,8 @@ getImplicit3 (Rotate3V θ axis symbObj) = v ^* cos θ - (axis' `cross3` v) ^* sin θ + (axis' ^* (axis' ⋅ (v ^* (1 - cos θ)))) +getImplicit3 (ReflectX3 symbObj) = + \(x,y,z) -> getImplicit3 symbObj (-x, y, z) -- Boundary mods getImplicit3 (Shell3 w symbObj) = let From 337882005176ae41468a0c7ecb041b383942aaec Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Fri, 20 Nov 2020 10:23:15 -0800 Subject: [PATCH 3/9] Implement buildS3 for ReflectX3 --- Graphics/Implicit/Export/SymbolicFormats.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index 04451ca2..ddcc1d17 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.Export.SymbolicFormats (scad2, scad3) where import Prelude(Either(Left, Right), ($), (*), ($!), (-), (/), pi, error, (+), (==), take, floor, (&&), const, pure, (<>), sequenceA, (<$>)) -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), isScaleID) +import Graphics.Implicit.Definitions(ℝ3, ℝ, 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, ReflectX3, Outset3, Shell3, ExtrudeR, ExtrudeRotateR, ExtrudeRM, EmbedBoxedObj3, RotateExtrude, ExtrudeOnEdgeOf), isScaleID) import Graphics.Implicit.Export.TextBuilderUtils(Text, Builder, toLazyText, fromLazyText, bf) import Control.Monad.Reader (Reader, runReader, ask) @@ -49,6 +49,9 @@ call = callToken ("[", "]") callNaked :: Builder -> [Builder] -> [Reader a Builder] -> Reader a Builder callNaked = callToken ("", "") +bvect3 :: ℝ3 -> Builder +bvect3 (x, y, z) = "[" <> fold (intersperse "," [bf x, bf y, bf z]) <> "]" + -- | First, the 3D objects. buildS3 :: SymbolicObj3 -> Reader ℝ Builder @@ -78,9 +81,9 @@ buildS3 (Scale3 (x,y,z) obj) = call "scale" [bf x, bf y, bf z] [buildS3 obj] buildS3 (Rotate3 (x,y,z) obj) = call "rotate" [bf (rad2deg x), bf (rad2deg y), bf (rad2deg z)] [buildS3 obj] -buildS3 (Rotate3V a v obj) = callNaked "rotate" [ "a=" <> bf (rad2deg a), "v=" <> bvect v ] [buildS3 obj] - where - bvect (x, y, z) = "[" <> fold (intersperse "," [bf x, bf y, bf z]) <> "]" +buildS3 (Rotate3V a v obj) = callNaked "rotate" [ "a=" <> bf (rad2deg a), "v=" <> bvect3 v ] [buildS3 obj] + +buildS3 (ReflectX3 obj) = callNaked "mirror" [ "v=" <> bvect3 (1, 0, 0) ] [buildS3 obj] buildS3 (Outset3 r obj) | r == 0 = call "outset" [] [buildS3 obj] From fafaa14336743bd2225588e5dc1c580a0208163e Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Fri, 20 Nov 2020 10:26:10 -0800 Subject: [PATCH 4/9] Implement buildS2 for ReflectX2 --- Graphics/Implicit/Export/SymbolicFormats.hs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index ddcc1d17..cf1d723e 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -8,9 +8,9 @@ -- output SCAD code, AKA an implicitcad to openscad converter. module Graphics.Implicit.Export.SymbolicFormats (scad2, scad3) where -import Prelude(Either(Left, Right), ($), (*), ($!), (-), (/), pi, error, (+), (==), take, floor, (&&), const, pure, (<>), sequenceA, (<$>)) +import Prelude(fmap, Either(Left, Right), ($), (*), ($!), (-), (/), pi, error, (+), (==), take, floor, (&&), const, pure, (<>), sequenceA, (<$>)) -import Graphics.Implicit.Definitions(ℝ3, ℝ, 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, ReflectX3, Outset3, Shell3, ExtrudeR, ExtrudeRotateR, ExtrudeRM, EmbedBoxedObj3, RotateExtrude, ExtrudeOnEdgeOf), isScaleID) +import Graphics.Implicit.Definitions(ℝ2, ℝ3, ℝ, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, ReflectX2, Outset2, Shell2, EmbedBoxedObj2), SymbolicObj3(Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, ReflectX3, Outset3, Shell3, ExtrudeR, ExtrudeRotateR, ExtrudeRM, EmbedBoxedObj3, RotateExtrude, ExtrudeOnEdgeOf), isScaleID) import Graphics.Implicit.Export.TextBuilderUtils(Text, Builder, toLazyText, fromLazyText, bf) import Control.Monad.Reader (Reader, runReader, ask) @@ -52,6 +52,9 @@ callNaked = callToken ("", "") bvect3 :: ℝ3 -> Builder bvect3 (x, y, z) = "[" <> fold (intersperse "," [bf x, bf y, bf z]) <> "]" +bvect2 :: ℝ2 -> Builder +bvect2 (x, y) = "[" <> fold (intersperse "," [bf x, bf y]) <> "]" + -- | First, the 3D objects. buildS3 :: SymbolicObj3 -> Reader ℝ Builder @@ -135,8 +138,7 @@ buildS2 (RectR r (x1,y1) (x2,y2)) | r == 0 = call "translate" [bf x1, bf y1] [ buildS2 (Circle r) = call "circle" [bf r] [] -buildS2 (PolygonR r points) | r == 0 = call "polygon" [buildVector [x,y] | (x,y) <- points] [] - where buildVector comps = "[" <> fold (intersperse "," $ bf <$> comps) <> "]" +buildS2 (PolygonR r points) | r == 0 = call "polygon" (fmap bvect2 points) [] buildS2 (Complement2 obj) = call "complement" [] [buildS2 obj] @@ -152,6 +154,8 @@ buildS2 (Scale2 (x,y) obj) = call "scale" [bf x, bf y] [buildS2 obj] buildS2 (Rotate2 r obj) = call "rotate" [bf (rad2deg r)] [buildS2 obj] +buildS2 (ReflectX2 obj) = callNaked "mirror" [ "v=" <> bvect2 (1, 0) ] [buildS2 obj] + buildS2 (Outset2 r obj) | r == 0 = call "outset" [] [buildS2 obj] buildS2 (Shell2 r obj) | r == 0 = call "shell" [] [buildS2 obj] From 00ee5f09f60e18539e3b8a0cea4855d49ae69f13 Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Mon, 23 Nov 2020 08:55:02 -0800 Subject: [PATCH 5/9] Rename FlipX -> Mirror, and implement arbitrary axis for mirror The math is based on this: https://en.wikipedia.org/wiki/Reflection_(mathematics)#Reflection_through_a_hyperplane_in_n_dimensions --- Graphics/Implicit/Definitions.hs | 8 +++---- Graphics/Implicit/Export/SymbolicFormats.hs | 6 ++--- Graphics/Implicit/MathUtil.hs | 20 +++++++++++++---- Graphics/Implicit/ObjectUtil/GetBox2.hs | 14 +++++++----- Graphics/Implicit/ObjectUtil/GetBox3.hs | 23 +++++++++++++++----- Graphics/Implicit/ObjectUtil/GetImplicit2.hs | 8 +++---- Graphics/Implicit/ObjectUtil/GetImplicit3.hs | 8 +++---- 7 files changed, 58 insertions(+), 29 deletions(-) diff --git a/Graphics/Implicit/Definitions.hs b/Graphics/Implicit/Definitions.hs index f72c6165..123b8d46 100644 --- a/Graphics/Implicit/Definitions.hs +++ b/Graphics/Implicit/Definitions.hs @@ -47,7 +47,7 @@ module Graphics.Implicit.Definitions ( Translate2, Scale2, Rotate2, - ReflectX2, + Mirror2, Shell2, Outset2, EmbedBoxedObj2), @@ -63,7 +63,7 @@ module Graphics.Implicit.Definitions ( Scale3, Rotate3, Rotate3V, - ReflectX3, + Mirror3, Shell3, Outset3, EmbedBoxedObj3, @@ -261,7 +261,7 @@ data SymbolicObj2 = | Translate2 ℝ2 SymbolicObj2 | Scale2 ℝ2 SymbolicObj2 | Rotate2 ℝ SymbolicObj2 - | ReflectX2 SymbolicObj2 + | Mirror2 ℝ2 SymbolicObj2 -- mirror across the line whose normal is defined by the R2 -- Boundary mods | Outset2 ℝ SymbolicObj2 | Shell2 ℝ SymbolicObj2 @@ -285,7 +285,7 @@ data SymbolicObj3 = | Scale3 ℝ3 SymbolicObj3 | Rotate3 ℝ3 SymbolicObj3 | Rotate3V ℝ ℝ3 SymbolicObj3 - | ReflectX3 SymbolicObj3 + | Mirror3 ℝ3 SymbolicObj3 -- mirror across the plane whose normal is the R3 -- Boundary mods | Outset3 ℝ SymbolicObj3 | Shell3 ℝ SymbolicObj3 diff --git a/Graphics/Implicit/Export/SymbolicFormats.hs b/Graphics/Implicit/Export/SymbolicFormats.hs index cf1d723e..99ff188a 100644 --- a/Graphics/Implicit/Export/SymbolicFormats.hs +++ b/Graphics/Implicit/Export/SymbolicFormats.hs @@ -10,7 +10,7 @@ module Graphics.Implicit.Export.SymbolicFormats (scad2, scad3) where import Prelude(fmap, Either(Left, Right), ($), (*), ($!), (-), (/), pi, error, (+), (==), take, floor, (&&), const, pure, (<>), sequenceA, (<$>)) -import Graphics.Implicit.Definitions(ℝ2, ℝ3, ℝ, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, ReflectX2, Outset2, Shell2, EmbedBoxedObj2), SymbolicObj3(Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, ReflectX3, Outset3, Shell3, ExtrudeR, ExtrudeRotateR, ExtrudeRM, EmbedBoxedObj3, RotateExtrude, ExtrudeOnEdgeOf), isScaleID) +import Graphics.Implicit.Definitions(ℝ2, ℝ3, ℝ, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Mirror2, Outset2, Shell2, EmbedBoxedObj2), SymbolicObj3(Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Mirror3, Outset3, Shell3, ExtrudeR, ExtrudeRotateR, ExtrudeRM, EmbedBoxedObj3, RotateExtrude, ExtrudeOnEdgeOf), isScaleID) import Graphics.Implicit.Export.TextBuilderUtils(Text, Builder, toLazyText, fromLazyText, bf) import Control.Monad.Reader (Reader, runReader, ask) @@ -86,7 +86,7 @@ buildS3 (Rotate3 (x,y,z) obj) = call "rotate" [bf (rad2deg x), bf (rad2deg y), b buildS3 (Rotate3V a v obj) = callNaked "rotate" [ "a=" <> bf (rad2deg a), "v=" <> bvect3 v ] [buildS3 obj] -buildS3 (ReflectX3 obj) = callNaked "mirror" [ "v=" <> bvect3 (1, 0, 0) ] [buildS3 obj] +buildS3 (Mirror3 v obj) = callNaked "mirror" [ "v=" <> bvect3 v ] [buildS3 obj] buildS3 (Outset3 r obj) | r == 0 = call "outset" [] [buildS3 obj] @@ -154,7 +154,7 @@ buildS2 (Scale2 (x,y) obj) = call "scale" [bf x, bf y] [buildS2 obj] buildS2 (Rotate2 r obj) = call "rotate" [bf (rad2deg r)] [buildS2 obj] -buildS2 (ReflectX2 obj) = callNaked "mirror" [ "v=" <> bvect2 (1, 0) ] [buildS2 obj] +buildS2 (Mirror2 v obj) = callNaked "mirror" [ "v=" <> bvect2 v ] [buildS2 obj] buildS2 (Outset2 r obj) | r == 0 = call "outset" [] [buildS2 obj] diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index b1046e0d..ca4891e8 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -1,18 +1,20 @@ --- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) -- Copyright (C) 2014 2015 2016, Julia Longtin (julial@turinglace.com) +-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) -- Released under the GNU AGPLV3+, see LICENSE +{-# LANGUAGE FlexibleContexts #-} + -- A module of math utilities. -module Graphics.Implicit.MathUtil (rmax, rmaximum, rminimum, distFromLineSeg, pack, box3sWithin) where +module Graphics.Implicit.MathUtil (rmax, rmaximum, rminimum, distFromLineSeg, pack, box3sWithin, reflect) 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, maximum, minimum, (==)) +import Prelude (Fractional, Num, Bool, Ordering, (>), (<), (+), ($), (/), otherwise, not, (||), (&&), abs, (-), (*), sin, asin, pi, max, sqrt, min, compare, (<=), fst, snd, (<>), head, flip, maximum, minimum, (==)) import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Box2, (⋅)) import Data.List (sort, sortBy, (!!)) -import Data.VectorSpace (magnitude, normalized, (^-^), (^+^), (*^)) +import Data.VectorSpace ((<.>), Scalar, (^*), InnerSpace, magnitude, normalized, (^-^), (^+^), (*^)) -- get the distance between two points. import Data.AffineSpace (distance) @@ -143,3 +145,13 @@ pack (dx, dy) sep objs = packSome sortedObjs (dx, dy) else tmap2 (presObj:) $ packSome otherBoxedObjs box packSome [] _ = ([], []) + + +reflect + :: (InnerSpace v, Fractional (Scalar v)) + => v -- ^ Mirror axis + -> v -- ^ Vector to transform + -> v +reflect a v = v ^-^ (2 * ((v <.> a) / (a <.> a))) *^ a + + diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 53b5d07b..430b4557 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -4,15 +4,16 @@ module Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getBox2R) where -import Prelude(negate, Bool, Fractional, Eq, (==), (||), unzip, minimum, maximum, ($), filter, not, (.), (/), fmap, (-), (+), (*), cos, sin, sqrt, min, max, head, (<), (<>), pi, atan2, (==), (>), show, (&&), otherwise, error) +import Prelude(Bool, Fractional, Eq, (==), (||), unzip, minimum, maximum, ($), filter, not, (.), (/), fmap, (-), (+), (*), cos, sin, sqrt, min, max, head, (<), (<>), pi, atan2, (==), (>), show, (&&), otherwise, error) import Graphics.Implicit.Definitions (ℝ, ℝ2, Box2, (⋯*), SymbolicObj2(Shell2, Outset2, Circle, Translate2, Rotate2, UnionR2, Scale2, RectR, - PolygonR, Complement2, DifferenceR2, IntersectR2, EmbedBoxedObj2, ReflectX2), minℝ) + PolygonR, Complement2, DifferenceR2, IntersectR2, EmbedBoxedObj2, Mirror2), minℝ) import Data.VectorSpace ((^-^), (^+^)) import Data.Fixed (mod') +import Graphics.Implicit.MathUtil (reflect) -- | An empty box. emptyBox :: Box2 @@ -25,6 +26,7 @@ isEmpty ((a, b), (c, d)) = a==c || b==d -- | Define a Box2 around all of the given points. pointsBox :: [ℝ2] -> Box2 +pointsBox [] = emptyBox pointsBox points = let (xs, ys) = unzip points @@ -104,9 +106,11 @@ getBox2 (Rotate2 θ symbObj) = , rotate (x2, y1) , rotate (x2, y2) ] -getBox2 (ReflectX2 symbObj) = - let ((x1,y1), (x2,y2)) = getBox2 symbObj - in ((negate x2, y1), (negate x1, y2)) +getBox2 (Mirror2 v symbObj) = + let (bl, tr) = getBox2 symbObj + in pointsBox [ reflect v bl + , reflect v tr + ] -- Boundary mods getBox2 (Shell2 w symbObj) = outsetBox (w/2) $ getBox2 symbObj diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index 26a20882..ce769dec 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -5,13 +5,14 @@ module Graphics.Implicit.ObjectUtil.GetBox3 (getBox3) where -import Prelude(negate, Eq, Bool(False), Fractional, Either (Left, Right), (==), (||), max, (/), (-), (+), fmap, unzip, ($), (<$>), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise, take, fst, snd) +import Prelude(Eq, Bool(False), Fractional, Either (Left, Right), (==), (||), max, (/), (-), (+), fmap, unzip, ($), (<$>), filter, not, (.), unzip3, minimum, maximum, min, (>), (&&), head, (*), (<), abs, either, error, const, otherwise, take, fst, snd) -import Graphics.Implicit.Definitions (ℝ, Fastℕ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, ReflectX3, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), ExtrudeRMScale(C1, C2), (⋯*), fromFastℕtoℝ, fromFastℕ, toScaleFn) +import Graphics.Implicit.Definitions (ℝ3, ℝ, Fastℕ, Box3, SymbolicObj3 (Rect3R, Sphere, Cylinder, Complement3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, Rotate3V, Mirror3, Shell3, Outset3, EmbedBoxedObj3, ExtrudeR, ExtrudeOnEdgeOf, ExtrudeRM, RotateExtrude, ExtrudeRotateR), SymbolicObj2 (Rotate2, RectR), ExtrudeRMScale(C1, C2), (⋯*), fromFastℕtoℝ, fromFastℕ, toScaleFn) import Graphics.Implicit.ObjectUtil.GetBox2 (getBox2, getBox2R) import Data.VectorSpace ((^-^), (^+^)) +import Graphics.Implicit.MathUtil (reflect) -- FIXME: many variables are being ignored here. no rounding for intersect, or difference.. etc. @@ -19,6 +20,15 @@ import Data.VectorSpace ((^-^), (^+^)) emptyBox :: Box3 emptyBox = ((0,0,0), (0,0,0)) +-- | Define a Box3 around all of the given points. +pointsBox :: [ℝ3] -> Box3 +pointsBox [] = emptyBox +pointsBox points = + let + (xs, ys, zs) = unzip3 points + in + ((minimum xs, minimum ys, minimum zs), (maximum xs, maximum ys, maximum zs)) + -- | Is a Box3 empty? -- | Really, this checks if it is one dimensional, which is good enough. isEmpty :: (Eq a2, Eq a1, Eq a) => @@ -98,9 +108,12 @@ getBox3 (Rotate3 (a, b, c) symbObj) = ((minimum xs, minimum ys, minimum zs), (maximum xs, maximum ys, maximum zs)) getBox3 (Rotate3V _ v symbObj) = getBox3 (Rotate3 v symbObj) -getBox3 (ReflectX3 symbObj) = - let ((x1,y1,z1), (x2,y2,z2)) = getBox3 symbObj - in ((negate x2, y1, z1), (negate x1, y2, z2)) +getBox3 (Mirror3 v symbObj) = + let (v1, v2) = getBox3 symbObj + in pointsBox + [ reflect v v1 + , reflect v v2 + ] -- Boundary mods getBox3 (Shell3 w symbObj) = outsetBox (w/2) $ getBox3 symbObj diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs index 865ef6e9..7d1b2280 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit2.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit2.hs @@ -6,9 +6,9 @@ module Graphics.Implicit.ObjectUtil.GetImplicit2 (getImplicit2) where import Prelude(abs, (-), (/), sqrt, (*), (+), mod, length, fmap, (<=), (&&), (>=), (||), odd, ($), (>), filter, (<), minimum, max, cos, sin, head, tail, (.)) -import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, ReflectX2, Shell2, Outset2, EmbedBoxedObj2)) +import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, (⋯/), Obj2, SymbolicObj2(RectR, Circle, PolygonR, Complement2, UnionR2, DifferenceR2, IntersectR2, Translate2, Scale2, Rotate2, Mirror2, Shell2, Outset2, EmbedBoxedObj2)) -import Graphics.Implicit.MathUtil (rminimum, rmaximum, distFromLineSeg) +import Graphics.Implicit.MathUtil (reflect, rminimum, rmaximum, distFromLineSeg) import Data.VectorSpace ((^-^)) import Data.List (nub, genericIndex, genericLength) @@ -82,8 +82,8 @@ getImplicit2 (Rotate2 θ symbObj) = obj = getImplicit2 symbObj in obj ( x*cos θ + y*sin θ, y*cos θ - x*sin θ) -getImplicit2 (ReflectX2 symbObj) = - \(x,y) -> getImplicit2 symbObj (-x, y) +getImplicit2 (Mirror2 v symbObj) = + getImplicit2 symbObj . reflect v -- Boundary mods getImplicit2 (Shell2 w symbObj) = \p -> let diff --git a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs index 3c026635..449bec0b 100644 --- a/Graphics/Implicit/ObjectUtil/GetImplicit3.hs +++ b/Graphics/Implicit/ObjectUtil/GetImplicit3.hs @@ -9,10 +9,10 @@ import Prelude (Either(Left, Right), abs, (-), (/), (*), sqrt, (+), atan2, max, import Graphics.Implicit.Definitions (ℝ, ℕ, ℝ2, ℝ3, (⋯/), Obj3, SymbolicObj3(Shell3, UnionR3, IntersectR3, DifferenceR3, Translate3, Scale3, Rotate3, - Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, ReflectX3, + Outset3, Rect3R, Sphere, Cylinder, Complement3, EmbedBoxedObj3, Rotate3V, Mirror3, ExtrudeR, ExtrudeRM, ExtrudeOnEdgeOf, RotateExtrude, ExtrudeRotateR), fromℕtoℝ, toScaleFn, (⋅), minℝ) -import Graphics.Implicit.MathUtil (rmaximum, rminimum, rmax) +import Graphics.Implicit.MathUtil (reflect, rmaximum, rminimum, rmax) import Data.Maybe (fromMaybe, isJust) @@ -100,8 +100,8 @@ getImplicit3 (Rotate3V θ axis symbObj) = v ^* cos θ - (axis' `cross3` v) ^* sin θ + (axis' ^* (axis' ⋅ (v ^* (1 - cos θ)))) -getImplicit3 (ReflectX3 symbObj) = - \(x,y,z) -> getImplicit3 symbObj (-x, y, z) +getImplicit3 (Mirror3 v symbObj) = + getImplicit3 symbObj . reflect v -- Boundary mods getImplicit3 (Shell3 w symbObj) = let From 86ddad1e134a7e7485bc3cb89dd208b2c9ddcb76 Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Mon, 23 Nov 2020 08:58:29 -0800 Subject: [PATCH 6/9] Add 'mirror' as a class method to 'Object' --- Graphics/Implicit.hs | 3 ++- Graphics/Implicit/Primitives.hs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Graphics/Implicit.hs b/Graphics/Implicit.hs index 1dd5c715..e6fb1bfe 100644 --- a/Graphics/Implicit.hs +++ b/Graphics/Implicit.hs @@ -19,6 +19,7 @@ module Graphics.Implicit ( P.Object ( P.translate, P.scale, + P.mirror, P.complement, P.unionR, P.intersectR, @@ -79,7 +80,7 @@ import Prelude(FilePath, IO) -- The primitive objects, and functions for manipulating them. -- MAYBEFIXME: impliment slice operation, regularPolygon and zsurface primitives. -import Graphics.Implicit.Primitives as P (translate, scale, complement, union, intersect, difference, unionR, intersectR, differenceR, shell, extrudeR, extrudeRM, extrudeOnEdgeOf, sphere, rect3R, circle, cylinder, cylinder2, rectR, polygonR, rotateExtrude, rotate3, rotate3V, pack3, rotate, pack2, implicit, Object) +import Graphics.Implicit.Primitives as P (translate, scale, mirror, complement, union, intersect, difference, unionR, intersectR, differenceR, shell, extrudeR, extrudeRM, extrudeOnEdgeOf, sphere, rect3R, circle, cylinder, cylinder2, rectR, polygonR, rotateExtrude, rotate3, rotate3V, pack3, rotate, pack2, implicit, Object) -- The Extended OpenScad interpreter. import Graphics.Implicit.ExtOpenScad as E (runOpenscad) diff --git a/Graphics/Implicit/Primitives.hs b/Graphics/Implicit/Primitives.hs index b32bf782..f1c32300 100644 --- a/Graphics/Implicit/Primitives.hs +++ b/Graphics/Implicit/Primitives.hs @@ -9,6 +9,7 @@ -- A module exporting all of the primitives, and some operations on them. module Graphics.Implicit.Primitives ( translate, + mirror, scale, outset, complement, union, intersect, difference, @@ -49,6 +50,7 @@ import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Box2, DifferenceR2, IntersectR2, Translate2, + Mirror2, Scale2, Rotate2, Outset2, @@ -64,6 +66,7 @@ import Graphics.Implicit.Definitions (ℝ, ℝ2, ℝ3, Box2, DifferenceR3, IntersectR3, Translate3, + Mirror3, Scale3, Rotate3, Rotate3V, @@ -177,6 +180,12 @@ class Object obj vec | obj -> vec where -> obj -- ^ Object to translate -> obj -- ^ Resulting object + -- | Mirror an object across the hyperplane whose normal is a given vector. + mirror :: + vec -- ^ Vector defining the hyperplane + -> obj -- ^ Object to mirror + -> obj -- ^ Resulting object + -- | Scale an object scale :: vec -- ^ Amount to scale by @@ -213,6 +222,7 @@ class Object obj vec | obj -> vec where instance Object SymbolicObj2 ℝ2 where translate = Translate2 + mirror = Mirror2 scale = Scale2 complement = Complement2 unionR = UnionR2 @@ -226,6 +236,7 @@ instance Object SymbolicObj2 ℝ2 where instance Object SymbolicObj3 ℝ3 where translate = Translate3 + mirror = Mirror3 scale = Scale3 complement = Complement3 unionR = UnionR3 From a53527590344ec6886f2c2ec727e941fd88409ac Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Mon, 23 Nov 2020 09:12:39 -0800 Subject: [PATCH 7/9] Comment the 'reflect' function --- Graphics/Implicit/MathUtil.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Graphics/Implicit/MathUtil.hs b/Graphics/Implicit/MathUtil.hs index ca4891e8..6e712872 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -147,6 +147,9 @@ pack (dx, dy) sep objs = packSome sortedObjs (dx, dy) packSome [] _ = ([], []) +-- | Reflect a vector across a hyperplane defined by its normal vector. +-- +-- From https://en.wikipedia.org/wiki/Reflection_(mathematics)#Reflection_through_a_hyperplane_in_n_dimensions reflect :: (InnerSpace v, Fractional (Scalar v)) => v -- ^ Mirror axis From ca92130a88310018a07fbdbdcc52b6521c1445b6 Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Mon, 23 Nov 2020 09:26:33 -0800 Subject: [PATCH 8/9] More robust bounding box calculations --- Graphics/Implicit/ObjectUtil/GetBox2.hs | 8 +++++--- Graphics/Implicit/ObjectUtil/GetBox3.hs | 12 +++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Graphics/Implicit/ObjectUtil/GetBox2.hs b/Graphics/Implicit/ObjectUtil/GetBox2.hs index 430b4557..2b085d99 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox2.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox2.hs @@ -107,9 +107,11 @@ getBox2 (Rotate2 θ symbObj) = , rotate (x2, y2) ] getBox2 (Mirror2 v symbObj) = - let (bl, tr) = getBox2 symbObj - in pointsBox [ reflect v bl - , reflect v tr + let (p1@(x1, y1), p2@(x2, y2)) = getBox2 symbObj + in pointsBox [ reflect v p1 + , reflect v p2 + , reflect v (x1, y2) + , reflect v (x2, y1) ] -- Boundary mods getBox2 (Shell2 w symbObj) = diff --git a/Graphics/Implicit/ObjectUtil/GetBox3.hs b/Graphics/Implicit/ObjectUtil/GetBox3.hs index ce769dec..6f58c593 100644 --- a/Graphics/Implicit/ObjectUtil/GetBox3.hs +++ b/Graphics/Implicit/ObjectUtil/GetBox3.hs @@ -109,10 +109,16 @@ getBox3 (Rotate3 (a, b, c) symbObj) = getBox3 (Rotate3V _ v symbObj) = getBox3 (Rotate3 v symbObj) getBox3 (Mirror3 v symbObj) = - let (v1, v2) = getBox3 symbObj + let (p1@(x1, y1, z1), p2@(x2, y2, z2)) = getBox3 symbObj in pointsBox - [ reflect v v1 - , reflect v v2 + [ reflect v p1 + , reflect v (x1, y2, z1) + , reflect v (x2, y2, z1) + , reflect v (x2, y1, z1) + , reflect v (x1, y1, z2) + , reflect v (x2, y1, z2) + , reflect v (x1, y2, z2) + , reflect v p2 ] -- Boundary mods getBox3 (Shell3 w symbObj) = From 6da09b1591e7d0a348ffa67ac131c28df1b21f9b Mon Sep 17 00:00:00 2001 From: Sandy Maguire Date: Mon, 23 Nov 2020 09:35:55 -0800 Subject: [PATCH 9/9] Fix comment that was accidentally scrambled by tooling --- 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 6e712872..4b389da7 100644 --- a/Graphics/Implicit/MathUtil.hs +++ b/Graphics/Implicit/MathUtil.hs @@ -1,5 +1,5 @@ --- Copyright (C) 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) +-- Copyright (C) 2014 2015 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE {-# LANGUAGE FlexibleContexts #-}