packages feed

OpenSCAD 0.1.0.0 → 0.2.0.0

raw patch · 4 files changed

+598/−388 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Graphics.OpenSCAD: circle3d :: Float -> Facet -> Solid
- Graphics.OpenSCAD: data Shape
- Graphics.OpenSCAD: data Solid
- Graphics.OpenSCAD: import2d :: FilePath -> Shape
- Graphics.OpenSCAD: import3d :: FilePath -> Solid
- Graphics.OpenSCAD: mirror2d :: Point -> Shape -> Shape
- Graphics.OpenSCAD: projection3d :: Bool -> Solid -> Solid
- Graphics.OpenSCAD: rectangle3d :: Float -> Float -> Solid
- Graphics.OpenSCAD: resize2d :: Point -> Shape -> Shape
- Graphics.OpenSCAD: rotate2d :: Point -> Shape -> Shape
- Graphics.OpenSCAD: scale2d :: Point -> Shape -> Shape
- Graphics.OpenSCAD: square3d :: Float -> Solid
- Graphics.OpenSCAD: translate2d :: Point -> Shape -> Shape
- Graphics.OpenSCAD: type Point = (Float, Float)
- Graphics.OpenSCAD: type Vector = (Float, Float, Float)
+ Graphics.OpenSCAD: class Vector a
+ Graphics.OpenSCAD: data Model v
+ Graphics.OpenSCAD: drawL :: Vector v => [Model v] -> IO ()
+ Graphics.OpenSCAD: importFile :: Vector v => FilePath -> Model v
+ Graphics.OpenSCAD: instance Show Join
+ Graphics.OpenSCAD: instance Show Sides
+ Graphics.OpenSCAD: instance Show v => Show (Model v)
+ Graphics.OpenSCAD: instance Vector Vector2d
+ Graphics.OpenSCAD: instance Vector Vector3d
+ Graphics.OpenSCAD: polygon :: Int -> [[Vector2d]] -> Model2d
+ Graphics.OpenSCAD: polyhedron :: Int -> [[Vector3d]] -> Model3d
+ Graphics.OpenSCAD: renderL :: Vector v => [Model v] -> String
+ Graphics.OpenSCAD: solid :: Model2d -> Model3d
+ Graphics.OpenSCAD: surface :: FilePath -> Bool -> Int -> Model3d
+ Graphics.OpenSCAD: type Model2d = Model Vector2d
+ Graphics.OpenSCAD: type Model3d = Model Vector3d
+ Graphics.OpenSCAD: type TransMatrix = ((Double, Double, Double, Double), (Double, Double, Double, Double), (Double, Double, Double, Double), (Double, Double, Double, Double))
+ Graphics.OpenSCAD: type Vector2d = (Double, Double)
+ Graphics.OpenSCAD: type Vector3d = (Double, Double, Double)
- Graphics.OpenSCAD: box :: Float -> Float -> Float -> Solid
+ Graphics.OpenSCAD: box :: Double -> Double -> Double -> Model3d
- Graphics.OpenSCAD: circle :: Float -> Facet -> Shape
+ Graphics.OpenSCAD: circle :: Double -> Facet -> Model2d
- Graphics.OpenSCAD: color :: Colour Float -> Solid -> Solid
+ Graphics.OpenSCAD: color :: Vector v => Colour Double -> Model v -> Model v
- Graphics.OpenSCAD: cube :: Float -> Solid
+ Graphics.OpenSCAD: cube :: Double -> Model3d
- Graphics.OpenSCAD: cylinder :: Float -> Float -> Facet -> Solid
+ Graphics.OpenSCAD: cylinder :: Double -> Double -> Facet -> Model3d
- Graphics.OpenSCAD: diam :: Float -> Float
+ Graphics.OpenSCAD: diam :: Double -> Double
- Graphics.OpenSCAD: difference :: Solid -> Solid -> Solid
+ Graphics.OpenSCAD: difference :: Vector v => Model v -> Model v -> Model v
- Graphics.OpenSCAD: draw :: Solid -> IO ()
+ Graphics.OpenSCAD: draw :: Vector v => Model v -> IO ()
- Graphics.OpenSCAD: fa :: Float -> Facet
+ Graphics.OpenSCAD: fa :: Double -> Facet
- Graphics.OpenSCAD: fs :: Float -> Facet
+ Graphics.OpenSCAD: fs :: Double -> Facet
- Graphics.OpenSCAD: hull :: [Solid] -> Solid
+ Graphics.OpenSCAD: hull :: Vector v => [Model v] -> Model v
- Graphics.OpenSCAD: intersection :: [Solid] -> Solid
+ Graphics.OpenSCAD: intersection :: Vector v => [Model v] -> Model v
- Graphics.OpenSCAD: linearExtrude :: Float -> Float -> Point -> Int -> Int -> Facet -> Shape -> Solid
+ Graphics.OpenSCAD: linearExtrude :: Double -> Double -> Vector2d -> Int -> Int -> Facet -> Model2d -> Model3d
- Graphics.OpenSCAD: minkowski :: [Solid] -> Solid
+ Graphics.OpenSCAD: minkowski :: Vector v => [Model v] -> Model v
- Graphics.OpenSCAD: mirror :: Vector -> Solid -> Solid
+ Graphics.OpenSCAD: mirror :: Vector v => v -> Model v -> Model v
- Graphics.OpenSCAD: multMatrix :: Transform -> Solid -> Solid
+ Graphics.OpenSCAD: multMatrix :: TransMatrix -> Model3d -> Model3d
- Graphics.OpenSCAD: obCylinder :: Float -> Float -> Float -> Facet -> Solid
+ Graphics.OpenSCAD: obCylinder :: Double -> Double -> Double -> Facet -> Model Vector3d
- Graphics.OpenSCAD: projection :: Bool -> Solid -> Shape
+ Graphics.OpenSCAD: projection :: Bool -> Model3d -> Model2d
- Graphics.OpenSCAD: rectangle :: Float -> Float -> Shape
+ Graphics.OpenSCAD: rectangle :: Double -> Double -> Model2d
- Graphics.OpenSCAD: render :: Solid -> String
+ Graphics.OpenSCAD: render :: Vector v => Model v -> String
- Graphics.OpenSCAD: resize :: Vector -> Solid -> Solid
+ Graphics.OpenSCAD: resize :: Vector v => v -> Model v -> Model v
- Graphics.OpenSCAD: rotate :: Vector -> Solid -> Solid
+ Graphics.OpenSCAD: rotate :: Vector v => v -> Model v -> Model v
- Graphics.OpenSCAD: rotateExtrude :: Int -> Facet -> Shape -> Solid
+ Graphics.OpenSCAD: rotateExtrude :: Int -> Facet -> Model2d -> Model3d
- Graphics.OpenSCAD: scale :: Vector -> Solid -> Solid
+ Graphics.OpenSCAD: scale :: Vector v => v -> Model v -> Model v
- Graphics.OpenSCAD: sphere :: Float -> Facet -> Solid
+ Graphics.OpenSCAD: sphere :: Double -> Facet -> Model3d
- Graphics.OpenSCAD: square :: Float -> Shape
+ Graphics.OpenSCAD: square :: Double -> Model2d
- Graphics.OpenSCAD: translate :: Vector -> Solid -> Solid
+ Graphics.OpenSCAD: translate :: Vector v => v -> Model v -> Model v
- Graphics.OpenSCAD: transparent :: AlphaColour Float -> Solid -> Solid
+ Graphics.OpenSCAD: transparent :: Vector v => AlphaColour Double -> Model v -> Model v
- Graphics.OpenSCAD: union :: [Solid] -> Solid
+ Graphics.OpenSCAD: union :: Vector v => [Model v] -> Model v
- Graphics.OpenSCAD: up :: Float -> Solid -> Solid
+ Graphics.OpenSCAD: up :: Double -> Model3d -> Model3d
- Graphics.OpenSCAD: var :: Facet -> [Solid] -> Solid
+ Graphics.OpenSCAD: var :: Facet -> [Model v] -> Model v
- Graphics.OpenSCAD.Unicode: (∆) :: Solid -> Solid -> Solid
+ Graphics.OpenSCAD.Unicode: (∆) :: Vector v => Model v -> Model v -> Model v
- Graphics.OpenSCAD.Unicode: (∖) :: Solid -> Solid -> Solid
+ Graphics.OpenSCAD.Unicode: (∖) :: Vector v => Model v -> Model v -> Model v
- Graphics.OpenSCAD.Unicode: (∩) :: Solid -> Solid -> Solid
+ Graphics.OpenSCAD.Unicode: (∩) :: Vector v => Model v -> Model v -> Model v
- Graphics.OpenSCAD.Unicode: (∪) :: Solid -> Solid -> Solid
+ Graphics.OpenSCAD.Unicode: (∪) :: Vector v => Model v -> Model v -> Model v

Files

Graphics/OpenSCAD.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances,	FunctionalDependencies #-}+ {- | Module      : Graphics.OpenSCAD Description : Type-checked wrappers for the OpenSCAD primitives.@@ -6,17 +8,34 @@ Maintainer  : mwm@mired.org Stability   : experimental += Overview+ The Graphics.OpenSCAD module provides abstract data types for creating OpenSCAD model definitions calls, along with a function to render it as a string, and some utilities. The primary goal is that the output should always be valid OpenSCAD. If you manage to generate OpenSCAD source that causes OpenSCAD to complain, please open an issue. +The primary affect of this is that Graphics.OpenSCAD distinguishes+between 2d and 3d 'Model's. If you want to mix them, you must+explicitly convert between them.  While two-dimensional model creation+could be polymorphic functions that create either, so that such models+could be treated as either 2d or 3d, you'd still have to explicitly+convert models whose type was fixed as 2d by a transformation, and+'render' wouldn't work if the type was still ambiguous, ala @render $+square 2@.++= Usage+ Standard usage is to have a @main@ function that looks like:  @ main = draw $ /Solid/ @+or+@+main = drawL $ [/Solid/]+@  and then set your IDE's compile command to use @runhaskell@ or equivalent to run your code and send the output to a .scad file. Open@@ -24,11 +43,11 @@ changes. Recompiling your program will cause the model to be loaded and displayed by OpenSCAD. -The type constructors are generally not exported, with functions being-exported in their stead.  This allows extra checking to be done on-those that need it.  It also provides consistency, as otherwise you'd-have to remember whether 'box' is a constructor or a convenience-function, etc.+The type constructors are not exported, with functions being exported+in their stead.  This allows extra checking to be done on those that+need it.  It also provides consistency, as otherwise you'd have to+remember whether 'box' is a constructor or a convenience function,+etc.  Because of this, the constructors are not documented, the exported functions are. The documentation is generally just the corresponding@@ -37,386 +56,463 @@ it's the same as the 'Graphics.OpenSCAD' function. You should check the OpenSCAD documentation for usage information. += Oddities++'importFile' has been left polymorphic. I couldn't find a sane way to+check that you're importing the right file type, so detecting such+errors - including importing a 3d file and trying to extrude it - have+to be left up to OpenSCAD in any case.  So for now, there's just+'importFile'. This does create the oddity that if you import a file+and try and render it without doing something to indicate how many+dimensions it has (one of the transformations, an extrusion or+projection, or 'solid') you'll get a compile error because the type is+ambiguous. Later, this may turn into @import2d@ and @import3d@.++The interfaces for 'polygon's and 'polyhedron's is seriously different+from the OpenSCAD interface. Rather than expecting you to enter a list+of points and then references to them, you just enter the points+directly. If you really want to do it the OpenSCAD way, you can do+something like:++@+draw $ polyhedron [[(p 0, p 1, p 2), (p 0, p 2, p 3), ... ]]+where points = [.....]+      p i = points !! i+@++Also, the OpenSCAD polyedron code recently changed. The old version+requires that the faces all be triangles, the new version allows for+them to be arbitrary polygons. 'Graphics.OpenSCAD' supports both: if+all your faces are triangles, it will use the old version. If some+have more points, the new version will be used. If any have fewer than+three points you get an error. At this time, no tests are done on the+faces. That will probably change in the future.++Offset is missing even though it's documented, as it isn't supported+by a released version of OpenSCAD, so presumably subject to change. It+is implemented, but untested as yet. You can add it to the module's+export lists if you want to play with it.+ -}  module Graphics.OpenSCAD (-  -- * Basic data types-  Solid,-  Shape,-  Facet,-  -- * Type aliases to save typing-  Vector, Point,-  -- * Rendering functions-  render, draw,-  -- * Constructors-  -- ** 'Solid's-  sphere, box, cube, cylinder, obCylinder, rectangle3d, square3d, circle3d,-  import3d, linearExtrude, rotateExtrude,-  -- ** 'Shape's-  rectangle, square, circle, import2d, projection,- -- * Combinations of 'Solid's+  -- * Types+  -- ** A 'Model' to be rendered, and a 'Vector' that fixes the+  -- number of dimensions it has.+  Model, Vector,+  -- ** Types aliases with fixed dimensions+  Model2d, Model3d, Vector2d, Vector3d,+  --  ** Other type aliases+  Facet, TransMatrix,+  -- * Primitive creation+  -- ** 'Model2d's+  rectangle, square, circle, polygon, projection, importFile,+  -- ** 'Model3d's+  sphere, box, cube, cylinder, obCylinder, polyhedron,+  multMatrix, linearExtrude, rotateExtrude, surface, solid,+  -- * Functions+  -- ** Combinations   union, intersection, difference, minkowski, hull,-  -- * Transformations-  -- ** 'Solid's-  scale, resize, rotate, translate, mirror, multMatrix, color, transparent, up,-  projection3d, -  -- ** 'Shape's-  scale2d, resize2d, rotate2d, translate2d, mirror2d,-  -- ** General convenience functions-  diam,-  -- * Convenience functions for 'Facet's.+  -- ** Transformations+  scale, resize, rotate, translate, mirror, color, transparent, up,+  -- ** Rendering+  render, renderL,+  -- ** 'Facet's.   var, fn, fs, fa, def,+  -- ** General convenience functions+  diam, draw, drawL,   module Colours)  where  import Data.Colour (Colour, AlphaColour, alphaChannel, darken, over, black)+import Data.Colour.Names as Colours import Data.Colour.SRGB (channelRed, channelBlue, channelGreen, toSRGB)+import Data.List (elemIndices, nub, intercalate) import System.FilePath (FilePath)-import qualified Data.Colour.Names as Colours --- | 'Vector' is used where OpenSCAD expects an OpenSCAD @vector@ of length 3.-type Vector = (Float, Float, Float)+-- A vector in 2 or 3-space. They are used in transformations of+-- 'Model's of their type.+class Vector a where+  rVector :: a -> String --- | 'Point' is used where OpenSCAD expects an OpenSCAD @vector@ of length 3.-type Point = (Float, Float)+-- | 'Vector2d' is used where 'Graphics.OpenSCAD' expects an OpenSCAD+-- @vector@ of length 2.+type Vector2d = (Double, Double)+instance Vector Vector2d where+  rVector (x, y) = "[" ++ show x ++ "," ++ show y ++ "]" --- | These are for @Poly*s@, which don't work yet.-type Path = [Int]-type Face = (Int, Int, Int)+-- | 'Vector3d' is used where 'Graphics.OpenSCAD' expects an OpenSCAD+-- @vector@ of length 3.+type Vector3d = (Double, Double, Double)+instance Vector Vector3d where+  rVector (a, b, c) = "[" ++ show a ++ "," ++ show b ++ "," ++ show c ++ "]" -type Transform = ((Float, Float, Float, Float), (Float, Float, Float, Float),-                  (Float, Float, Float, Float), (Float, Float, Float, Float))+-- | A 4x4 transformation matrix specifying a complete 3-space+-- transform of a 'Model3d'.+type TransMatrix =+  ((Double, Double, Double, Double), (Double, Double, Double, Double),+   (Double, Double, Double, Double), (Double, Double, Double, Double))  --- While it's tempting to add more options to Solid, don't do it. Instead,--- add functions that add that functionality, like cube vs. box.------ Missing at this time: Poly*s, some special features.--- There's also no way to set $f? vars globally, due to an OpenSCAD quirk.+-- While it's tempting to add more options to Solid, Shape or Model,+-- don't do it. Instead, add functions that add that functionality,+-- by building the appropriate structure, like cube vs. box.  -- | A 'Facet' is used to set one of the special variables that -- control the mesh used during generation of circular objects. They -- appear as arguments to various constructors, as well as in the -- 'var' function to set them for the argument objects.-data Facet = Fa Float | Fs Float | Fn Int | Def deriving Show+data Facet = Fa Double | Fs Double | Fn Int | Def deriving Show --- | A 'Shape' is a two-dimensional object. They are a separate type--- so that Haskell can type check that we aren't using a 2d operation--- on a 3d shape, or vice versa. Unfortunately, this means the--- dynamically typed functions that accept either - and possibly--- generate either - need to have two versions of those functions.  I--- believe the 2d creation functions are more common for 2d objects,--- but the 3d transformation functions are more common. Hence the 2d--- creation functions (which have the names of 2d objects like circle,--- square, etc.) that create 'Solid's have @3d@ appended, but the 3d--- version of transformations that have both 2d and 3d versions have--- @3d@ appended.-data Shape =-             Rectangle Float Float-           | Circle Float Facet-           -- add | Polygon [Point] [Path] Int-           | Import2d FilePath-           | Projection Bool Solid-           -- 2d versions of the transformations-           | Scale2d Point Shape-           | Resize2d Point Shape-           | Rotate2d Point Shape-           | Translate2d Point Shape-           | Mirror2d Point Shape+-- | A 'Join' controls how edges in a 'polygon' are joined by the+-- 'offset' operation.+data Join = Bevel | Round | Miter Double deriving Show++-- A 'Shape' is a 2-dimensional primitive to be used in a 'Model2d'.+data Shape = Rectangle Double Double+           | Circle Double Facet+           | Polygon Int [Vector2d] [[Int]]+           | Projection Bool Model3d+           | Offset Double Join Shape            deriving Show --- | A 'Solid' is a solid object in OpenSCAD. Since we don't have--- optional or named objects, some constructors appear twice to allow--- two different variants to be used. And of course, they all have all--- their arguments.-data Solid =-             Sphere Float Facet-           | Box Float Float Float-           | Cylinder Float Float Facet-           | ObCylinder Float Float Float Facet-           -- add | Polyhedron [Vector] [Face] Int-           | Import3d FilePath-           | Shape Shape-           -- Combinations-           | Union [Solid]-           | Intersection [Solid]-           | Difference Solid Solid-           | Minkowski [Solid]-           | Hull [Solid]-           -- Transformations-           | Scale Vector Solid-           | Resize Vector Solid-           | Rotate Vector Solid-           | Translate Vector Solid-           | Mirror Vector Solid-           | MultMatrix Transform Solid-           | Color (Colour Float) Solid-           | Transparent (AlphaColour Float) Solid-           | LinearExtrude Float Float Point Int Int Facet Shape-           | RotateExtrude Int Facet Shape-           -- Mesh control-           | Var Facet [Solid]+data Sides = Faces [[Int]] | Triangles [[Int]] deriving Show++-- A 'Solid' is a 3-dimensional primitive to be used in a 'Model3d'.+data Solid = Sphere Double Facet+           | Box Double Double Double+           | Cylinder Double Double Facet+           | ObCylinder Double Double Double Facet+           | Polyhedron Int [Vector3d] Sides+           | MultMatrix TransMatrix Model3d+           | LinearExtrude Double Double Vector2d Int Int Facet Model2d+           | RotateExtrude Int Facet Model2d+           | Surface FilePath Bool Int+           | ToSolid Model2d            deriving Show --- | 'render' does all the real work. It will walk the AST for a 'Solid',--- returning an OpenSCAD program in a 'String'.-render :: Solid -> String-render (Sphere x f) = "sphere(" ++ show x ++ rFacet f ++ ");\n\n"-render (Box x y z) =-  "cube([" ++ show x ++ "," ++ show y ++ "," ++ show z ++ "]);\n"-render (Cylinder r h f) =-  "cylinder(r=" ++ show r ++ ",h=" ++ show h ++ rFacet f ++ ");\n\n"-render (ObCylinder r1 h r2 f) =-    "cylinder(r1=" ++ show r1 ++ ",h=" ++ show h ++ ",r2=" ++ show r2 ++ rFacet f-    ++ ");\n\n"-render (Import3d f) = "import(" ++ f ++");\n\n"-render (Shape s) = rShape s-render (Union ss) = rList "union()" ss-render (Intersection ss) = rList "intersection()" ss-render (Difference s1 s2) = "difference(){" ++ render s1 ++ render s2 ++ "}\n\n"-render (Minkowski ss) = rList "minkowski()" ss-render (Hull ss) = rList "hull()" ss-render (Scale v s) = rVecSolid "scale" v s-render (Resize v s) = rVecSolid "resize" v s-render (Translate v s) = rVecSolid "translate" v s-render (Rotate v s) = "rotate(a=" ++ rVector v ++ ")" ++ render s-render (Mirror v s) = rVecSolid "mirror" v s-render (MultMatrix (a, b, c, d) s) =-    "multmatrix([" ++ rQuad a ++ "," ++ rQuad b ++ "," ++ rQuad c ++ ","-    ++ rQuad d ++"])\n" ++ render s-render (Color c s) = let r = toSRGB c in-    "color(" ++ rVector (channelRed r, channelGreen r, channelBlue r) ++ ")\n"-    ++ render s-render (Transparent c s) =-    "color(" ++ rQuad (channelRed r, channelGreen r, channelBlue r, a) ++ ")"-    ++ render s-    where r = toSRGB $ toPure c-          a = alphaChannel c-          toPure ac = if a > 0 then darken (recip a) (ac `over` black) else black-render (LinearExtrude h t sc sl c f sh) =-    "linear_extrude(height=" ++ show h ++ ",twist=" ++ show t ++ ",scale="-    ++ rPoint sc ++ ",slices=" ++ show sl ++ ",convexity=" ++ show c ++ rFacet f-    ++ ")" ++ rShape sh-render (RotateExtrude c f sh) =-  "rotate_extrude(convexity=" ++ show c ++ rFacet f ++ ")" ++ rShape sh-render (Var (Fa f) ss) = rList ("assign($fa=" ++ show f ++ ")") ss-render (Var (Fs f) ss) = rList ("assign($fs=" ++ show f ++ ")") ss-render (Var (Fn n) ss) = rList ("assign($fn=" ++ show n ++ ")") ss+-- | A 'Model' is either a 'Model2d', a 'Model3d', a transformation of+-- a 'Model', a combination of 'Model's, or a 'Model' with it's+-- rendering tweaked by a 'Facet'. 'Model's can be rendered.+data Model v = Shape Shape+             | Solid Solid+             | Scale v (Model v)+             | Resize v (Model v)+             | Rotate v (Model v)+             | Translate v (Model v)+             | Mirror v (Model v)+             | Color (Colour Double) (Model v)+             | Transparent (AlphaColour Double) (Model v)+             -- and combinations+             | Union [Model v]+             | Intersection [Model v]+             | Minkowski [Model v]+             | Hull [Model v]+             | Difference (Model v) (Model v)+             -- And oddball stuff control+             | Import FilePath+             | Var Facet [Model v]+             deriving Show --- | 'draw' is a convenience function to write the rendered 'Solid' to--- standard output.-draw = putStrLn . render+-- | A two-dimensional model. Note that the types do not mix+-- implicitly. You must turn a 'Model2d' into a 'Model3d' using one of+-- 'linearExtrude', 'rotateExtrude', or 'solid'.+type Model2d = Model Vector2d +-- | A three-dimensional model. You can create a 'Model2d' from a+-- 'Model3d' using 'projection'.+type Model3d = Model Vector3d --- utilities for rendering Shapes.-rShape (Rectangle r f) = "square([" ++ show r ++ "," ++ show f ++ "]);\n\n"-rShape (Circle r f) = "circle(" ++ show r ++ rFacet f ++ ");\n\n"-rShape (Import2d f) = "import(" ++ f ++ ");\n\n"-rShape (Projection c s) =-  "projection(cut=" ++ (if c then "true)" else "false)") ++ render s-rShape (Scale2d p s) = "scale(" ++ rPoint p ++ ")" ++ rShape s-rShape (Resize2d p s) = "resize(" ++ rPoint p ++ ")" ++ rShape s-rShape (Rotate2d p s) = "rotate(" ++ rPoint p ++ ")" ++ rShape s-rShape (Translate2d p s) = "translate(" ++ rPoint p ++ ")" ++ rShape s-rShape (Mirror2d p s) = "mirror(" ++ rPoint p ++ ")" ++ rShape s+-- Tools for creating 'Model2d's.+-- | Create a rectangular 'Model2d' with @rectangle /x-size y-size/@.+rectangle :: Double -> Double -> Model2d+rectangle w h = Shape $ Rectangle w h --- And some misc. rendering utilities.-rList n ss = n ++ "{\n" ++  concatMap render ss ++ "}"-rSolid n s = n ++ "()\n" ++ render s-rVector (a, b, c) = "[" ++ show a ++ "," ++ show b ++ "," ++ show c ++ "]"-rVecSolid n v s = n ++ "(" ++ rVector v ++ ")\n" ++ render s-rQuad (w, x, y, z) =-  "[" ++ show w ++ "," ++ show x ++ "," ++ show y ++ "," ++ show z ++ "]"-rFacet Def = ""-rFacet f = "," ++ showFacet f-rPoint (x, y)  = "[" ++ show x ++ "," ++ show y ++ "]"+-- | 'square' is a 'rectangle' with both sides the same size.+square :: Double -> Model2d+square s = rectangle s s --- render a facet setting.-showFacet :: Facet -> String-showFacet (Fa f) = "$fa=" ++ show f-showFacet (Fs f) = "$fs=" ++ show f-showFacet (Fn n) = "$fn=" ++ show n-showFacet Def    = ""+-- | Create a circular 'Model' with @circle /radius/ 'Facet'@.+circle :: Double -> Facet -> Model2d+circle r f = Shape $ Circle r f +-- | Project a 'Model3d' into a 'Model' with @projection /cut 'Model3d'/@.+projection :: Bool -> Model3d -> Model2d+projection c s = Shape $ Projection c s++-- | Turn a list of list of 'Vector2d's and an int into @polygon+-- /points path convexity/@. The argument to polygon is the list of+-- paths that is the second argument to the OpenSCAD polygon function,+-- except the points are 'Vector2d's, not references to 'Vector2d's in+-- that functions points argument.  If you were just going to pass in+-- the points, it now needs to be in an extra level of 'List'.+polygon ::  Int -> [[Vector2d]] -> Model2d+polygon convexity paths = Shape . Polygon convexity points+                  $ map (concatMap (\p -> elemIndices p points)) paths+  where points = nub $ concat paths++-- | 'offset' a 'Model2d's edges by @offset /delta join/@.+offset :: Double -> Join -> Model2d -> Model2d+offset d j (Shape s) = Shape $ Offset d j s++-- Tools for creating Model3ds -- | Create a sphere with @sphere /radius 'Facet'/@.-sphere :: Float -> Facet -> Solid-sphere = Sphere+sphere :: Double -> Facet -> Model3d+sphere r f = Solid $ Sphere r f  -- | Create a box with @cube /x-size y-size z-size/@-box :: Float -> Float -> Float -> Solid-box = Box+box :: Double -> Double -> Double -> Model3d+box x y z= Solid $ Box x y z  -- | A convenience function for creating a cube as a 'box' with all -- sides the same length.-cube :: Float -> Solid-cube x = Box x x x+cube :: Double -> Model3d+cube x = box x x x  -- | Create a cylinder with @cylinder /radius height 'Facet'/@.-cylinder :: Float -> Float -> Facet -> Solid-cylinder = Cylinder+cylinder :: Double -> Double -> Facet -> Model3d+cylinder h r f = Solid $ Cylinder h r f --- | Create an oblique cylinder with @cylinder /radius1 height radius2--- 'Facet'/@-obCylinder :: Float -> Float -> Float -> Facet -> Solid-obCylinder = ObCylinder+-- | Create an oblique cylinder with @cylinder /radius1 height radius2 'Facet'/@.+obCylinder :: Double -> Double -> Double -> Facet -> Model Vector3d+obCylinder r1 h r2 f= Solid $ ObCylinder r1 h r2 f --- | __UNTESTED__ 'import3d' is @import /filename/@, where /filename/--- is an stl file.  It's /3d/ because import is a key word.-import3d :: FilePath -> Solid-import3d = Import3d+-- | Turn a list of list of 'Vector3d's and an int into @polyhedron+-- /points 'Sides' convexity/@. The argument to polyhedron is the list+-- of paths that is the second argument to the OpenSCAD polygon+-- function, except the points are 'Vector3d's, not the references to+-- 'Vector3d's used in that functions @points@ argument.  The function+-- will build the appropriate function call, using @faces@ if you pass+-- in a side that uses more than 3 points, or @triangles@ if not. Note+-- that @faces@ doesn't work in older versions of OpenSCAD, an+-- @triangles@ is depreciate. Until a mechanism to set the version of+-- OpenSCAD is provided, generating the @faces@ version will cause an+-- error.+polyhedron ::  Int -> [[Vector3d]] -> Model3d+polyhedron convexity paths = Solid . Polyhedron convexity points $ sides sin+  where points = nub $ concat paths+        sin = map (concatMap (\p -> elemIndices p points)) paths+        sides ss | any ((> 3) . length) ss  = Faces sin+                 | all ((== 3) . length) ss = Triangles sin+                 | otherwise = error "All faces must have at least 3 sides." --- | __UNTESTED__ 'import2d' is @import /filename/@, where /filename/--- is an image or other 2d object.-import2d :: FilePath -> Shape-import2d = Import2d+-- | Transform a 'Model3d' with a 'TransMatrix'+multMatrix :: TransMatrix -> Model3d -> Model3d+multMatrix t m = Solid $ MultMatrix t m --- | Create the union of a list of 'Solid's.-union :: [Solid] -> Solid-union = Union+-- | Turn a 'Model2d' into a 'Model3d' exactly as is.+solid :: Model2d -> Model3d+solid = Solid . ToSolid --- | Create the intersection of a list of 'Solid's.-intersection :: [Solid] -> Solid-intersection = Intersection+-- | Extrude a 'Model2d' along a line with @linear_extrude@.+linearExtrude :: Double   -- ^ height+              -> Double   -- ^ twist+              -> Vector2d -- ^ scale+              -> Int      -- ^ slices+              -> Int      -- ^ convexity+              -> Facet+              -> Model2d  -- ^ to extrude+              -> Model3d+linearExtrude h t sc sl c f m = Solid $ LinearExtrude h t sc sl c f m --- | The difference between two 'Solid's.-difference :: Solid -> Solid -> Solid-difference = Difference+-- | Rotate a 'Model2d' around the origin with @rotate_extrude+-- /convexity 'Facet' 'Model'/@+rotateExtrude ::  Int -> Facet -> Model2d -> Model3d+rotateExtrude c f m = Solid $ RotateExtrude c f m --- | The Minkowski sum of a list of 'Solid's.-minkowski :: [Solid] -> Solid-minkowski = Minkowski+-- | Load a height map from a file with @surface /FilePath Invert Convexity/@.+surface :: FilePath -> Bool -> Int -> Model3d+surface f i c = Solid $ Surface f i c --- | The convex hull of a list of 'Solid's.-hull :: [Solid] -> Solid-hull = Hull+-- And the one polymorphic function we have.+-- | 'importFile' is @import /filename/@.+importFile :: Vector v => FilePath -> Model v+importFile = Import --- | Scale a 'Solid', specifying the scale factor for each axis.-scale :: Vector -> Solid -> Solid++-- Transformations+-- | Scale a 'Model', the vector specifying the scale factor for each axis.+scale :: Vector v => v -> Model v -> Model v scale = Scale --- | __UNTESTED__ Resize a 'Solid' to occupy the given dimensions.-resize :: Vector -> Solid -> Solid+-- | Resize a 'Model' to occupy the dimensions given by the vector. Note that+-- this does nothing prior to the 2014 versions of OpenSCAD.+resize :: Vector v => v -> Model v -> Model v resize = Resize --- | Rotate a 'Solid' by different amounts around each of the three axis.-rotate :: Vector -> Solid -> Solid+-- | Rotate a 'Model' by different amounts around each of the three axis.+rotate :: Vector v => v -> Model v -> Model v rotate = Rotate --- | Translate a 'Solid' along a 'Vector'.-translate :: Vector -> Solid -> Solid+-- | Translate a 'Model' along a 'Vector'.+translate :: Vector v => v -> Model v -> Model v translate = Translate --- | Mirror a 'Solid' across a plane intersecting the origin.-mirror :: Vector -> Solid -> Solid+-- | Mirror a 'Model' across a plane intersecting the origin.+mirror :: Vector v => v -> Model v -> Model v mirror = Mirror --- | Transform a 'Solid' with a 'Transform' matrix.-multMatrix :: Transform -> Solid -> Solid-multMatrix = MultMatrix---- | A 'translate' that just goes up, since those seem to be common.-up :: Float -> Solid -> Solid-up f = Translate (0, 0, f)----- | Render a 'Solid' in a specific color. This doesn't us the+-- | Render a 'Model' in a specific color. This doesn't use the -- OpenSCAD color model, but instead uses the 'Data.Colour' model. The -- 'Graphics.OpenSCAD' module rexports 'Data.Colour.Names' so you can--- conveniently say @'color' 'red' /'Solid'/@.-color :: Colour Float -> Solid -> Solid+-- conveniently say @'color' 'red' /'Model'/@.+color :: Vector v => Colour Double -> Model v -> Model v color = Color --- | Render a 'Solid' in a transparent color. This uses the+-- | Render a 'Model' in a transparent color. This uses the -- 'Data.Coulor.AphaColour' color model.-transparent :: AlphaColour Float -> Solid -> Solid+transparent :: Vector v => AlphaColour Double -> Model v -> Model v transparent = Transparent --- | Extrude a 'Shape' along a line with @linear_extrude@.-linearExtrude :: Float         -- ^ height-              -> Float         -- ^ twist-              -> Point         -- ^ scale-              -> Int           -- ^ slices-              -> Int           -- ^ convexity-              -> Facet-              -> Shape         -- ^ to extrude-              -> Solid-linearExtrude = LinearExtrude+-- | A 'translate' that just goes up, since those seem to be common.+up :: Double -> Model3d -> Model3d +up f = translate (0, 0, f) --- | Rotate a 'Shape' around the origin with @rotate_extrude--- /convexity 'Facet' 'Shape'/@-rotateExtrude ::  Int -> Facet -> Shape -> Solid-rotateExtrude = RotateExtrude --- | Use 'diam' to turn a diameter into a radius for circles, spheres, etc.-diam :: Float -> Float-diam = (/ 2)+-- Combinations+-- | Create the union of a list of 'Model's.+union :: Vector v => [Model v] -> Model v+union = Union --- | Create a rectangular 'Shape' with @rectangle /x-size y-size/@.-rectangle :: Float -> Float -> Shape-rectangle = Rectangle+-- | Create the intersection of a list of 'Model's.+intersection :: Vector v => [Model v] -> Model v+intersection = Intersection --- | Create a rectangular 'Solid' with @rectangle /x-size y-size/@.-rectangle3d :: Float -> Float -> Solid-rectangle3d w d = Shape $ Rectangle w d+-- | The difference between two 'Model's.+difference :: Vector v => Model v -> Model v -> Model v+difference = Difference --- | 'square' is a 'rectangle' with both sides the same size.-square :: Float -> Shape-square s = rectangle s s+-- | The Minkowski sum of a list of 'Model's.+minkowski :: Vector v => [Model v] -> Model v+minkowski = Minkowski --- | 'square3d' is a 'rectangle3d' with both sides the same size.-square3d :: Float -> Solid-square3d s = rectangle3d s s+-- | The convex hull of a list of 'Model's.+hull :: Vector v => [Model v] -> Model v+hull = Hull --- | Create a circular 'Shape' with @circle /radius/ 'Facet'@.-circle :: Float -> Facet -> Shape-circle = Circle --- | Create a circular 'Solid' with @circle /radius/ 'Facet'@.-circle3d :: Float -> Facet -> Solid-circle3d r f = Shape $ Circle r f+-- | 'render' does all the real work. It will walk the AST for a 'Model',+-- returning an OpenSCAD program in a 'String'.+render :: Vector v => Model v -> String+render (Shape s) = rShape s+render (Solid s) = rSolid s+render (Union ss) = rList "union()" ss+render (Intersection ss) = rList "intersection()" ss+render (Difference s1 s2) = "difference(){" ++ render s1 ++ render s2 ++ "}\n"+render (Minkowski ss) = rList "minkowski()" ss+render (Hull ss) = rList "hull()" ss+render (Scale v s) = rVecSolid "scale" v s+render (Resize v s) = rVecSolid "resize" v s+render (Translate v s) = rVecSolid "translate" v s+render (Rotate v s) = "rotate(" ++ rVector v ++ ")" ++ render s+render (Mirror v s) = rVecSolid "mirror" v s+render (Import f) = "import(\"" ++ f ++ "\");\n"+render (Color c s) = let r = toSRGB c in+    "color(" ++ rVector (channelRed r, channelGreen r, channelBlue r) ++ ")\n"+    ++ render s+render (Transparent c s) =+    "color(" ++ rQuad (channelRed r, channelGreen r, channelBlue r, a) ++ ")"+    ++ render s+    where r = toSRGB $ toPure c+          a = alphaChannel c+          toPure ac = if a > 0 then darken (recip a) (ac `over` black) else black+render (Var (Fa f) ss) = rList ("assign($fa=" ++ show f ++ ")") ss+render (Var (Fs f) ss) = rList ("assign($fs=" ++ show f ++ ")") ss+render (Var (Fn n) ss) = rList ("assign($fn=" ++ show n ++ ")") ss --- | Project a 'Solid' into a 'Shape' with @projection /cut 'Solid'/@.-projection :: Bool -> Solid -> Shape-projection = Projection+-- utility for rendering Shapes.+rShape :: Shape -> String+rShape (Rectangle r f) = "square([" ++ show r ++ "," ++ show f ++ "]);\n"+rShape (Circle r f) = "circle(" ++ show r ++ rFacet f ++ ");\n"+rShape (Projection c s) =+  "projection(cut=" ++ (if c then "true)" else "false)") ++ render s+rShape (Polygon c points paths) = "polygon(points=" ++ rVectorL points +++ ",paths=" ++ show paths ++ ",convexity=" ++ show c ++ ");\n"+rShape (Offset d j s) =+  "offset(delta=" ++ show d ++ "," ++ rJoin j ++ ")" ++ rShape s --- | Project a 'Solid' to a thin 'Solid' with @projection /cut 'Solid'/@.-projection3d :: Bool -> Solid -> Solid-projection3d c s = Shape $ Projection c s+-- utility for rendering Joins+rJoin :: Join -> String+rJoin Bevel = "join_type=bevel"+rJoin Round = "join_type=round"+rJoin (Miter l) = "miter_limit=" ++ show l --- | 'scale2d' is 'scale' for 'Shape's.-scale2d :: Point -> Shape -> Shape-scale2d = Scale2d+-- utilities for rendering Solids.+rSolid :: Solid -> String+rSolid (Sphere x f) = "sphere(" ++ show x ++ rFacet f ++ ");\n"+rSolid (Box x y z) =+  "cube([" ++ show x ++ "," ++ show y ++ "," ++ show z ++ "]);\n"+rSolid (Cylinder r h f) =+  "cylinder(r=" ++ show r ++ ",h=" ++ show h ++ rFacet f ++ ");\n"+rSolid (ObCylinder r1 h r2 f) =+    "cylinder(r1=" ++ show r1 ++ ",h=" ++ show h ++ ",r2=" ++ show r2 ++ rFacet f+    ++ ");\n"+rSolid (Polyhedron c ps ss) = "polyhedron(points=" ++  rVectorL ps ++ rSides ss+                              ++ ",convexity=" ++ show c ++ ");\n"+rSolid (MultMatrix (a, b, c, d) s) =+    "multmatrix([" ++ rQuad a ++ "," ++ rQuad b ++ "," ++ rQuad c ++ ","+    ++ rQuad d ++"])\n" ++ render s+rSolid (LinearExtrude h t sc sl c f sh) =+    "linear_extrude(height=" ++ show h ++ ",twist=" ++ show t ++ ",scale="+    ++ rVector sc ++ ",slices=" ++ show sl ++ ",convexity=" ++ show c+    ++ rFacet f ++ ")" ++ render sh+rSolid (RotateExtrude c f sh) =+  "rotate_extrude(convexity=" ++ show c ++ rFacet f ++ ")" ++ render sh+rSolid (Surface f i c) =+  "surface(file=\"" ++ f ++ "\"," ++ (if i then "invert=true," else "")+  ++ "convexity=" ++ show c ++ ");\n"+rSolid (ToSolid s) = render s --- | 'resize2d' is 'resize' for 'Shape's.-resize2d :: Point -> Shape -> Shape-resize2d = Resize2d+-- render a list of vectors as an Openscad vector of vectors.+rVectorL vs = "[" ++ (intercalate "," $ map rVector vs) ++ "]" --- | 'rotate2d' is 'rotate' for 'Shape's.-rotate2d :: Point -> Shape -> Shape-rotate2d = Rotate2d+-- render a Sides.+rSides (Faces vs) = ",faces=" ++ rListL vs+rSides (Triangles vs) = ",triangles=" ++ rListL vs+rListL vs = "[" ++ (intercalate "," $ map show vs) ++ "]" --- | 'translate2d' is 'translate' for 'Shape's.-translate2d :: Point -> Shape -> Shape-translate2d = Translate2d+-- | A convenience function to render a list of 'Model's by taking+-- their union.+renderL :: Vector v => [Model v] -> String+renderL = render . union --- | 'mirror2d' is 'mirror' for 'Shape's.-mirror2d :: Point -> Shape -> Shape-mirror2d = Mirror2d+-- | A convenience function to write the rendered 'Model' to+-- standard output.+draw :: Vector v => Model v -> IO ()+draw = putStrLn . render --- Convenience functions for Facets.+-- | A convenience function to write a 'union' of 'Model's to+-- standard output.+drawL :: Vector v => [Model v] -> IO ()+drawL = draw . Union --- Maybe this should have type [Facet] -> [Solid] -> [Solid]--- | 'var' uses @assign@ to set a special variable for the 'Solid's.-var :: Facet -> [Solid] -> Solid+-- And some misc. rendering utilities.+rList n ss = n ++ "{\n" ++  concatMap render ss ++ "}"+rVecSolid n v s = n ++ "(" ++ rVector v ++ ")\n" ++ render s+rQuad (w, x, y, z) =+  "[" ++ show w ++ "," ++ show x ++ "," ++ show y ++ "," ++ show z ++ "]"+rFacet Def = ""+rFacet f = "," ++ showFacet f++-- render a facet setting.+showFacet :: Facet -> String+showFacet (Fa f) = "$fa=" ++ show f+showFacet (Fs f) = "$fs=" ++ show f+showFacet (Fn n) = "$fn=" ++ show n+showFacet Def    = ""++-- Convenience functions for Facets.+-- | 'var' uses @assign@ to set a 'Facet' variable for it's 'Model's.+var :: Facet -> [Model v] -> Model v var = Var  -- | 'fa' is used to set the @$fa@ variable in a 'Facet' or 'var'.-fa :: Float -> Facet+fa :: Double -> Facet fa = Fa  -- | 'fs' is used to set the @$fs@ variable in a 'Facet' or 'var'.-fs :: Float -> Facet+fs :: Double -> Facet fs = Fs  -- | 'fn' is used to set the @$fn@ variable in a 'Facet' or 'var'.@@ -427,3 +523,9 @@ -- any of the values. def :: Facet def = Def++-- And one last convenience function.+-- | Use 'diam' to turn a diameter into a radius for circles, spheres, etc.+diam :: Double -> Double+diam = (/ 2)+
Graphics/OpenSCAD/Unicode.hs view
@@ -2,7 +2,7 @@  {- Module      : Graphics.OpenSCAD.Unicode-Description : Unicode operators so you can write @Solid@ expressions.+Description : Unicode operators so you can write 'Model' expressions. Copyright   : © Mike Meyer, 2014 License     : BSD4 Maintainer  : mwm@mired.org@@ -21,24 +21,24 @@ -- | (∪) = 'union' -- -- U+222A, UNION-(∪) :: Solid -> Solid -> Solid+(∪) :: Vector v => Model v -> Model v -> Model v a ∪ b = union [a, b]  -- | (∩) = 'intersection' -- -- U+2229, INTERSECTION-(∩) :: Solid -> Solid -> Solid+(∩) :: Vector v => Model v -> Model v -> Model v a ∩ b = intersection [a, b]  -- | (∖) = 'difference' -- -- U+2216, SET MINUS-(∖):: Solid -> Solid -> Solid+(∖):: Vector v => Model v -> Model v -> Model v (∖) = difference  -- | (∆) = Symmetric difference -- -- U+2206, INCREMENT-(∆) :: Solid -> Solid -> Solid+(∆) :: Vector v => Model v -> Model v -> Model v a ∆ b = (a ∖ b) ∪ (b ∖ a) 
OpenSCAD.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                OpenSCAD-version:             0.1.0.0+version:             0.2.0.0 synopsis:            ADT wrapper and renderer for OpenSCAD models. description:         An algebraic data type for describing OpenSCAD models,                      functions to make building such models easier, and
UnitTest.hs view
@@ -1,3 +1,4 @@+#!/usr/bin/env runghc module Main where  import Data.Monoid@@ -6,96 +7,203 @@ import Test.Framework.Providers.HUnit  import Graphics.OpenSCAD-import Data.Colour.Names import Data.Colour (withOpacity)  sw = concat . words st n e a = testCase n $ (sw e) @=?(sw $ render a) -main = defaultMainWithOpts [-  st "sphere 1"     "sphere(1.0);"                    (sphere 1 def),-  st "sphere 2"     "sphere(2.0,$fn=100);"            (sphere 2 $ fn 100),-  st "sphere 3"     "sphere(2.0,$fa=5.0);"            (sphere 2 $ fa 5),-  st "sphere 4"     "sphere(2.0,$fs=0.1);"            (sphere 2 $ fs 0.1),-  st "box"          "cube([1.0,2.0,3.0]);"            (box 1 2 3),-  st "cube"         "cube([2.0,2.0,2.0]);"            (cube 2),-  st "cylinder 1"   "cylinder(r=1.0,h=2.0);"          (cylinder 1 2 def),-  st "cylinder 2"   "cylinder(r=1.0,h=2.0,$fs=0.6);"  (cylinder 1 2 $ fs 0.6),-  st "cylinder 3"   "cylinder(r=1.0,h=2.0,$fn=10);"   (cylinder 1 2 $ fn 10),-  st "cylinder 4"   "cylinder(r=1.0,h=2.0,$fa=30.0);" (cylinder 1 2 $ fa 30),-  st "obCylinder 1" "cylinder(r1=1.0,h=2.0,r2=2.0);"  (obCylinder 1 2 2 def),-  st "obCylinder 2" "cylinder(r1=1.0,h=2.0,r2=2.0,$fs=0.6);"-     (obCylinder 1 2 2 $ fs 0.6),-  st "obCylinder 3" "cylinder(r1=1.0,h=2.0,r2=2.0,$fn=10);"-     (obCylinder 1 2 2 $ fn 10),-  st "obCylinder 4" "cylinder(r1=1.0,h=2.0,r2=2.0,$fa=30.0);"-     (obCylinder 1 2  2 $ fa 30),-  -- polyhedron & import3D goes here-  st "rectangle"    "square([2.0,3.0]);"              (rectangle3d 2 3),-  st "square"       "square([2.0,2.0]);"              (square3d 2),-  st "circle 1"     "circle(1.0);"                    (circle3d 1 def),-  st "circle 2"     "circle(2.0,$fn=100);"            (circle3d 2 $ fn 100),-  st "circle 3"     "circle(2.0,$fa=5.0);"            (circle3d 2 $ fa 5),-  st "circle 4"     "circle(2.0,$fs=0.1);"            (circle3d 2 $ fs 0.1),-  -- polygon & import2D goes here-  st "projection"   "projection(cut=false)scale([10.0,10.0,10.0])difference(){translate([0.0,0.0,1.0])cube([1.0,1.0,1.0]);translate([0.25,0.25,0.0])cube([0.5,0.5,3.0]);}"-     (projection3d False $ scale (10, 10, 10) $ difference (up 1 (cube 1)) $ translate (0.25, 0.25, 0) (box 0.5 0.5 3)),-  -- Transformations-  st "scale"        "scale([0.5,1.0,2.0])cube([1.0,1.0,1.0]);"-     (scale(0.5, 1, 2) $ cube 1),-  -- resize goes here-  st "rotate 1" "rotate(a=[180.0,0.0,0.0])cube([2.0,2.0,2.0]);"-     (rotate (180, 0, 0) $ cube 2),-  st "rotate 2" "rotate(a=[0.0,180.0,0.0])cube([2.0,2.0,2.0]);"-     (rotate (0, 180, 0) $ cube 2),-  st "rotate 3" "rotate(a=[0.0,180.0,180.0])cube([2.0,2.0,2.0]);"-     (rotate (0, 180, 180) $ cube 2),-  st "mirror 1" "mirror([1.0,0.0,0.0])cube([2.0,2.0,2.0]);"-     (mirror (1, 0, 0) $ cube 2),-  st "mirror 2" "mirror([0.0,1.0,0.0])cube([2.0,2.0,2.0]);"-     (mirror (0, 1, 0) $ cube 2),-  st "mirror 3" "rotate(a=[0.0,1.0,1.0])cube([2.0,2.0,2.0]);"-     (rotate (0, 1, 1) $ cube 2),-  st "multmatrix" "multmatrix([[1.0,0.0,0.0,10.0],[0.0,1.0,0.0,20.0],[0.0,0.0,1.0,30.0],[0.0,0.0,0.0,1.0]])cylinder(r=2.0,h=3.0);"-     (multMatrix ( (1, 0, 0, 10),-                   (0, 1, 0, 20),-                   (0, 0, 1, 30),-                   (0, 0, 0,  1) ) $ cylinder 2 3 def),-  st "color" "color([1.0,0.0,0.0])cube([1.0,1.0,1.0]);" (color red $ cube 1),-  st "transparent" "color([1.0,0.0,0.0,0.7])cube([1.0,1.0,1.0]);"-     (transparent (red `withOpacity` 0.7) $ cube 1),-  st "linearExtrude 1"-     "linear_extrude(height=10.0,twist=0.0,scale=[1.0,1.0],slices=10,convexity=10)circle(1.0);"-     (linearExtrude 10 0 (1, 1) 10 10 def $ circle 1 def),-  st "linearExtrude 2"-     "linear_extrude(height=10.0,twist=100.0,scale=[1.0,1.0],slices=10,convexity=10)translate([2.0,0.0])circle(1.0);"-     (linearExtrude 10 100 (1, 1) 10 10 def $ translate2d (2, 0) $ circle 1 def),-  st "linearExtrude 3"-     "linear_extrude(height=10.0,twist=500.0,scale=[1.0,1.0],slices=10,convexity=10)translate([2.0,0.0])circle(1.0);"-     (linearExtrude 10 500 (1, 1) 10 10 def $ translate2d (2, 0) $ circle 1 def),-  st "linearExtrude 4"-     "linear_extrude(height=10.0,twist=360.0,scale=[1.0,1.0],slices=100,convexity=10)translate([2.0,0.0])circle(1.0);"-     (linearExtrude 10 360 (1, 1) 100 10 def $ translate2d (2, 0) $ circle 1 def),-  st "linearExtrude 5"-     "linear_extrude(height=10.0,twist=360.0,scale=[1.0,1.0],slices=100,convexity=10,$fn=100)translate([2.0,0.0])circle(1.0);"-     (linearExtrude 10 360 (1, 1) 100 10 (fn 100) $ translate2d (2, 0) $ circle 1 def),-  st "linearExtrude 6"-     "linear_extrude(height=10.0,twist=0.0,scale=[3.0,3.0],slices=100,convexity=10)translate([2.0,0.0])circle(1.0);"-     (linearExtrude 10 0 (3, 3) 100 10 def $ translate2d (2, 0) $ circle 1 def),-  st "linearExtrude 7"-     "linear_extrude(height=10.0,twist=0.0,scale=[1.0,5.0],slices=100,convexity=10,$fn=100)translate([2.0,0.0])circle(1.0);"-     (linearExtrude 10 0 (1, 5) 100 10 (fn 100) $ translate2d (2, 0)-      $ circle 1 def),-  st "rotate_extrude 1"-     "rotate_extrude(convexity=10)translate([2.0,0.0])circle(1.0);"-     (rotateExtrude 10 def $ translate2d (2, 0) $ circle 1 def),-  st "rotate_extrude 2"-     "rotate_extrude(convexity=10,$fn=100)translate([2.0,0.0])circle(1.0,$fn=100);"-     (rotateExtrude 10 (fn 100) $ translate2d (2, 0) $ circle 1 $ fn 100),-  st "facet 1" "assign($fn=100){sphere(2.0,$fn=100);}"-     (var (fn 100) [sphere 2 $ fn 100]),-  st "facet 2" "assign($fa=5.0){sphere(2.0,$fa=5.0);}"-     (var (fa 5) [sphere 2 $ fa 5]),-  st "facet 3" "assign($fs=0.1){sphere(2.0,$fs=0.1);}"-     (var (fs 0.1) [sphere 2 $ fs 0.1])-  ] mempty+{- About the test result values.++Running "cabal test" does not verify that the results do the intended+thing in OpenSCAD. Possibly we'll add shell tests for that at some+point, but not yet.++For now, if you change or add strings, please manually copy them into+OpenSCAD and make sure they do what you want the Model data structure+that they are testing does.+-}++tests = [+  testGroup "3d-primitives" [+     testGroup "Spheres" [+        st "1" "sphere(1.0);"         (sphere 1 def),+        st "2" "sphere(2.0,$fn=100);" (sphere 2 $ fn 100),+        st "3" "sphere(2.0,$fa=5.0);" (sphere 2 $ fa 5),+        st "4" "sphere(2.0,$fs=0.1);" (sphere 2 $ fs 0.1)+        ],++     testGroup "Boxes" [+       st "box"  "cube([1.0,2.0,3.0]);" (box 1 2 3),+       st "cube" "cube([2.0,2.0,2.0]);" (cube 2)+       ],++     testGroup "Cylinders" [+       st "1" "cylinder(r=1.0,h=2.0);"          (cylinder 1 2 def),+       st "2" "cylinder(r=1.0,h=2.0,$fs=0.6);"  (cylinder 1 2 $ fs 0.6),+       st "3" "cylinder(r=1.0,h=2.0,$fn=10);"   (cylinder 1 2 $ fn 10),+       st "4" "cylinder(r=1.0,h=2.0,$fa=30.0);" (cylinder 1 2 $ fa 30)+       ],++     testGroup "Oblique-Cylinders" [+       st "1" "cylinder(r1=1.0,h=2.0,r2=2.0);"  (obCylinder 1 2 2 def),+       st "2" "cylinder(r1=1.0,h=2.0,r2=2.0,$fs=0.6);"+          (obCylinder 1 2 2 $ fs 0.6),+       st "3" "cylinder(r1=1.0,h=2.0,r2=2.0,$fn=10);"+          (obCylinder 1 2 2 $ fn 10),+       st "4" "cylinder(r1=1.0,h=2.0,r2=2.0,$fa=30.0);"+          (obCylinder 1 2  2 $ fa 30)+       ],++     testGroup "Misc" [+       st "import" "import(\"test.stl\");" (solid $ importFile "test.stl"),+       st "polyhedron 1"+          "polyhedron(points=[[10.0,10.0,0.0],[10.0,-10.0,0.0],[0.0,0.0,10.0],[-10.0,-10.0,0.0],[-10.0,10.0,0.0]],triangles=[[0,1,2],[1,3,2],[3,4,2],[4,0,2],[1,0,4],[3,1,4]],convexity=1);"+          (polyhedron 1 [[(10, 10, 0), (10, -10, 0), (0, 0, 10)],+                         [(10, -10, 0), (-10, -10, 0), (0, 0, 10)],+                         [(-10, -10, 0), (-10, 10, 0), (0, 0, 10)],+                         [(-10, 10, 0), (10, 10, 0), (0, 0, 10)],+                         [(10, -10, 0), (10, 10, 0), (-10, 10, 0)],+                         [(-10, -10, 0), (10, -10, 0), (-10, 10, 0)]]),+       st "polyhedron 2"+       "polyhedron(points=[[10.0,10.0,0.0],[10.0,-10.0,0.0],[0.0,0.0,10.0],[-10.0,-10.0,0.0],[-10.0,10.0,0.0]],faces=[[0,1,2],[1,3,2],[3,4,2],[4,0,2],[0,1,3,4]],convexity=1);"+          (polyhedron 1 [[(10, 10, 0), (10, -10, 0), (0, 0, 10)],+                         [(10, -10, 0), (-10, -10, 0), (0, 0, 10)],+                         [(-10, -10, 0), (-10, 10, 0), (0, 0, 10)],+                         [(-10, 10, 0), (10, 10, 0), (0, 0, 10)],+                         [(10, 10, 0), (10, -10, 0), (-10, -10, 0), (-10, 10, 0)]])+       ],++     testGroup "Linear-Extrusion" [+       st "1" +          "linear_extrude(height=10.0,twist=0.0,scale=[1.0,1.0],slices=10,convexity=10)circle(1.0);"+          (linearExtrude 10 0 (1, 1) 10 10 def $ circle 1 def),+       st "2" +          "linear_extrude(height=10.0,twist=100.0,scale=[1.0,1.0],slices=10,convexity=10)translate([2.0,0.0])circle(1.0);"+          (linearExtrude 10 100 (1, 1) 10 10 def $ translate (2, 0)+           $ circle 1 def),+       st "3" +          "linear_extrude(height=10.0,twist=500.0,scale=[1.0,1.0],slices=10,convexity=10)translate([2.0,0.0])circle(1.0);"+          (linearExtrude 10 500 (1, 1) 10 10 def $ translate (2, 0)+           $ circle 1 def),+       st "4" +          "linear_extrude(height=10.0,twist=360.0,scale=[1.0,1.0],slices=100,convexity=10)translate([2.0,0.0])circle(1.0);"+          (linearExtrude 10 360 (1, 1) 100 10 def $ translate (2, 0)+           $ circle 1 def),+       st "5" +          "linear_extrude(height=10.0,twist=360.0,scale=[1.0,1.0],slices=100,convexity=10,$fn=100)translate([2.0,0.0])circle(1.0);"+          (linearExtrude 10 360 (1, 1) 100 10 (fn 100) $ translate (2, 0)+           $ circle 1 def),+       st "6" +          "linear_extrude(height=10.0,twist=0.0,scale=[3.0,3.0],slices=100,convexity=10)translate([2.0,0.0])circle(1.0);"+          (linearExtrude 10 0 (3, 3) 100 10 def $ translate (2, 0) $ circle 1 def),+       st "7" +          "linear_extrude(height=10.0,twist=0.0,scale=[1.0,5.0],slices=100,convexity=10,$fn=100)translate([2.0,0.0])circle(1.0);"+          (linearExtrude 10 0 (1, 5) 100 10 (fn 100) $ translate (2, 0)+           $ circle 1 def)+       ],++     testGroup "Rotated-Extrusion" [+       st "1" "rotate_extrude(convexity=10)translate([2.0,0.0])circle(1.0);"+          (rotateExtrude 10 def $ translate (2, 0) $ circle 1 def),+       st "2"+          "rotate_extrude(convexity=10,$fn=100)translate([2.0,0.0])circle(1.0,$fn=100);"+          (rotateExtrude 10 (fn 100) $ translate (2, 0) $ circle 1 $ fn 100)+       ],++     testGroup "Surface" [+       st "Normal" "surface(file=\"test.dat\",convexity=5);"+          (surface "test.dat" False 5),+       st "Inverted" "surface(file=\"test.dat\",invert=true,convexity=5);"+          (surface "test.dat" True 5)	-- Requires  2014.QX+       ]+     ],+     +  testGroup "2d-primitives" [+    testGroup "Squares" [+       st "rectangle" "square([2.0,3.0]);"   (rectangle 2 3),+       st "square"    "square([2.0,2.0]);"   (square 2)+       ],+    testGroup "Circles" [+      st "1" "circle(1.0);"         (circle 1 def),+      st "2" "circle(2.0,$fn=100);" (circle 2 $ fn 100),+      st "3" "circle(2.0,$fa=5.0);" (circle 2 $ fa 5),+      st "4" "circle(2.0,$fs=0.1);" (circle 2 $ fs 0.1)+      ],+    testGroup "Misc" [+      st "import" "import(\"test.dxf\");"   (solid $ importFile "test.dxf"),+      st "polygon"+         "polygon(points=[[0.0,0.0],[100.0,0.0],[0.0,100.0],[10.0,10.0],[80.0,10.0],[10.0,80.0]],paths=[[0,1,2],[3,4,5]],convexity=10);"+         (polygon 10 [[(0,0),(100,0),(0,100)],[(10,10),(80,10),(10,80)]]),+      st "projection"+         "projection(cut=false)scale([10.0,10.0,10.0])difference(){translate([0.0,0.0,1.0])cube([1.0,1.0,1.0]);translate([0.25,0.25,0.0])cube([0.5,0.5,3.0]);}"+         (projection False . scale (10, 10, 10) . difference (up 1 (cube 1))+          $ translate (0.25, 0.25, 0) (box 0.5 0.5 3))+      ]+    ],++  testGroup "Transformations" [+    testGroup "Size changes" [+       st "scale 1" "scale([0.5,1.0,2.0])cube([1.0,1.0,1.0]);"+          (scale (0.5, 1, 2) $ cube 1),+       st "scale 2" "scale([0.5,2.0])square([1.0,1.0]);"+          (scale (0.5, 2) $ rectangle 1 1),+       st "resize 1" "resize([10.0,20.0])square([2.0,2.0]);"+          (resize (10, 20) $ square 2),+       st "resize 2" "resize([10.0,20.0,30.0])cube([2.0,2.0,2.0]);"+          (resize (10, 20, 30) $ cube 2)+       ],++    testGroup "Rotations" [+      st "1" "rotate([180.0,0.0,0.0])cube([2.0,2.0,2.0]);"+         (rotate (180, 0, 0) $ cube 2),+      st "2" "rotate([0.0,180.0,0.0])cube([2.0,2.0,2.0]);"+         (rotate (0, 180, 0) $ cube 2),+      st "3" "rotate([0.0,180.0,180.0])cube([2.0,2.0,2.0]);"+         (rotate (0, 180, 180) $ cube 2),+      st "4" "rotate([180.0,0.0])square([2.0,1.0]);"+         (rotate (180, 0) $ rectangle 2 1),+      st "5" "rotate([0.0,180.0])square([2.0,1.0]);"+         (rotate (0, 180) $ rectangle 2 1)+      ],+    testGroup "Mirrors" [+      st "1" "mirror([1.0,0.0,0.0])cube([2.0,2.0,2.0]);"+         (mirror (1, 0, 0) $ cube 2),+      st "2" "mirror([0.0,1.0,0.0])cube([2.0,2.0,2.0]);"+         (mirror (0, 1, 0) $ cube 2),+      st "3" "rotate([0.0,1.0,1.0])cube([2.0,2.0,2.0]);"+         (rotate (0, 1, 1) $ cube 2),+      st "4" "mirror([1.0,0.0])square([2.0,1.0]);"+         (mirror (1, 0) $ rectangle 2 1),+      st "2" "mirror([0.0,1.0])square([2.0,1.0]);"+         (mirror (0, 1) $ rectangle 2 1)+         ],+    +    st "multmatrix"+       "multmatrix([[1.0,0.0,0.0,10.0],[0.0,1.0,0.0,20.0],[0.0,0.0,1.0,30.0],[0.0,0.0,0.0,1.0]])cylinder(r=2.0,h=3.0);"+       (multMatrix ( (1, 0, 0, 10),+                     (0, 1, 0, 20),+                     (0, 0, 1, 30),+                     (0, 0, 0,  1) ) $ cylinder 2 3 def),++    testGroup "Colors" [+      st "color 1" "color([1.0,0.0,0.0])cube([1.0,1.0,1.0]);" (color red $ cube 1),+      st "color 2" "color([1.0,0.0,0.0])square([1.0,1.0]);"+         (color red $ square 1),+      st "transparent 1" "color([1.0,0.0,0.0,0.7])cube([1.0,1.0,1.0]);"+         (transparent (red `withOpacity` 0.7) $ cube 1),+      st "transparent 2" "color([1.0,0.0,0.0,0.7])square([1.0,1.0]);"+         (transparent (red `withOpacity` 0.7) $ square 1)+      ]+    ],++  testGroup "Facets" [+    st "facet 1" "assign($fn=100){sphere(2.0,$fn=100);}"+       (var (fn 100) [sphere 2 $ fn 100]),+    st "facet 2" "assign($fa=5.0){sphere(2.0,$fa=5.0);}"+       (var (fa 5) [sphere 2 $ fa 5]),+    st "facet 3" "assign($fs=0.1){sphere(2.0,$fs=0.1);}"+       (var (fs 0.1) [sphere 2 $ fs 0.1])+    ]+  ]++main = defaultMain tests