mirror of
https://github.com/Haskell-Things/ImplicitCAD.git
synced 2024-09-11 04:55:41 +03:00
174 lines
5.7 KiB
Markdown
174 lines
5.7 KiB
Markdown
ImplicitCAD Hacking How To
|
||
==========================
|
||
|
||
So you want to improve ImplicitCAD. Yay! More help is a good thing.
|
||
|
||
As of the time of writing, ImplicitCAD has 3417 lines of code, 896 lines of comments, and 877 blank lines, for a total of 5190 lines spread over 42 files. For a project of ImplicitCAD's scope, that's pretty small, but it's still enough that it can be difficult to find the section we need to change...
|
||
|
||
The structure of ImplicitCAD is as follows:
|
||
|
||
```
|
||
Graphics
|
||
└── Implicit
|
||
├── Export
|
||
│ ├── Render
|
||
│ └── Symbolic
|
||
├── ExtOpenScad
|
||
│ └── Util
|
||
└── ObjectUtil
|
||
```
|
||
|
||
`Graphics.Implicit.Export` is, as you may guess, where all the export stuff is. `Graphics.Implicit.ExtOpenScad` is the programming language interpreter for the ExtOpenScad language, our extention of openscad. Finally, the graphics engine is defined in `Graphics.Implicit` and `Graphics.Implicit.ObjectUtil`.
|
||
|
||
The rest of this file will go through different changes you are likely to want to make and how to implement them.
|
||
|
||
Language Changes
|
||
----------------
|
||
|
||
Most likely, you want to change one of four things:
|
||
|
||
* **Expressions**: Expressions are things like `1+2`, `"abc"`, and `[sin(3.14), pi]`. They are defined in `Graphics.Implicit.ExtOpenScad.Expressions`. (Note that `sin` and `pi` are variables, which are defined elsewhere.)
|
||
|
||
* **Statements**: Statements are things like variable assignment, for loops, and if statements. For example `for (a = [1,2,3]) echo (a);`. Statements are defined in `Graphics.Implicit.ExtOpenScad.Statements`.
|
||
|
||
```haskell
|
||
computationStatement = ...
|
||
ifStatement,
|
||
forStatement,
|
||
...
|
||
|
||
...
|
||
|
||
forStatement = (do
|
||
line <- fmap sourceLine getPosition
|
||
-- a for loop is of the form:
|
||
-- for ( vsymb = vexpr ) loopStatements
|
||
-- eg. for ( a = [1,2,3] ) {echo(a); echo "lol";}
|
||
-- eg. for ( [a,b] = [[1,2]] ) {echo(a+b); echo "lol";}
|
||
string "for"
|
||
many space
|
||
char '('
|
||
many space
|
||
pattern <- patternMatcher
|
||
many space
|
||
char '='
|
||
vexpr <- expression 0
|
||
char ')'
|
||
many space
|
||
loopStatements <- suite
|
||
...
|
||
```
|
||
|
||
|
||
* **Default Variables**: Like `sin`, `pi`, `sqrt`. These are all defined in ` Graphics.Implicit.ExtOpenScad.Default`. We can just use `toOObj` to convert Haskell values into `OpenscadObj`s and use them as default variable settings. (Small caveat: inputs to `toOObj` can't be polymorphic, so we use a type signature to force it to a certain type.)
|
||
|
||
```haskell
|
||
defaultFunctions = map (\(a,b) -> (a, toOObj ( b :: ℝ -> ℝ)))
|
||
[
|
||
("sin", sin),
|
||
("cos", cos),
|
||
("tan", tan),
|
||
...
|
||
]
|
||
```
|
||
|
||
* **Default Modules**: Like `sphere` and `linear_extrude`. These are all defined in `Graphics.Implicit.ExtOpenScad.Primitives`.
|
||
|
||
```haskell
|
||
primitives = [ sphere, cube, square, cylinder, ... ]
|
||
|
||
...
|
||
|
||
-- **Exmaple of implementing a module**
|
||
-- sphere is a module without a suite named sphere,
|
||
-- this means that the parser will look for this like
|
||
-- sphere(args...);
|
||
sphere = moduleWithoutSuite "sphere" $ do
|
||
example "sphere(3);"
|
||
example "sphere(r=5);"
|
||
-- What are the arguments?
|
||
-- The radius, r, which is a (real) number.
|
||
-- Because we don't provide a default, this ends right
|
||
-- here if it doesn't get a suitable argument!
|
||
r :: ℝ <- argument "r"
|
||
`doc` "radius of the sphere"
|
||
-- So what does this module do?
|
||
-- It adds a 3D object, a sphere of radius r,
|
||
-- using the sphere implementation in Prim
|
||
-- (Graphics.Implicit.Primitives)
|
||
addObj3 $ Prim.sphere r
|
||
|
||
```
|
||
|
||
Output Formats
|
||
--------------
|
||
|
||
Formats are defined in files like `Graphics.Implicit.Export.TriangleMeshFormats` (as is the case with STLs), `Graphics.Implicit.Export.PolylineMeshFormats` (as is the case with SVGs).
|
||
|
||
Then, in `Graphics.Implicit.Export`:
|
||
|
||
```haskell
|
||
writeSVG res = writeObject res PolylineFormats.svg
|
||
writeSTL res = writeObject res TriangleMeshFormats.stl
|
||
```
|
||
|
||
Rendering Algorithms
|
||
--------------------
|
||
|
||
These are defined in `Graphics.Implicit.Export.Render` and children. `Graphics.Implicit.Export.Render` begins with an outline of how rendering is done:
|
||
|
||
```haskell
|
||
-- Here's the plan for rendering a cube (the 2D case is trivial):
|
||
|
||
-- (1) We calculate midpoints using interpolate.
|
||
-- This guarentees that our mesh will line up everywhere.
|
||
-- (Contrast with calculating them in getSegs)
|
||
|
||
import Graphics.Implicit.Export.Render.Interpolate (interpolate)
|
||
|
||
...
|
||
```
|
||
|
||
If you are interested on working on this part of the code, read it. The children are also well documented.
|
||
|
||
Graphics Primitives
|
||
-------------------
|
||
|
||
The most complicated part of ImplicitCAD is the actual graphics engine. Before working on it, please familiarize yourself with the theory as described in [Chris' blog post](http://christopherolah.wordpress.com/2011/11/06/manipulation-of-implicit-functions-with-an-eye-on-cad/) (though changes have occured since then).
|
||
|
||
The simples way to implement a new primitive is using `implicit`, a contructor that takes an implicit function and boudning box, producing an object. For example, we could have originally defined `sphere` as:
|
||
|
||
```haskell
|
||
sphere :: ℝ -> SymbolicObj3
|
||
sphere r = implicit (
|
||
\(x,y,z) -> sqrt (x^2+y^2+z^2) - r,
|
||
((-r, -r, -r), (r, r, r))
|
||
)
|
||
```
|
||
|
||
and put it in `Graphics.Implicit.Primitives`. However, to allow more powerful optimizations, meta-inspection, and other goodies, frequently used objects should be put in the `SymbolicObj` definitions in `Graphics.Implicit.Definitions`. For example, `sphere`:
|
||
|
||
```haskell
|
||
data SymbolicObj3 =
|
||
CubeR ℝ ℝ3 ℝ3
|
||
| Sphere ℝ
|
||
...
|
||
```
|
||
|
||
Then one needs to make the relevant entries in `Graphics.Implicit.ObjectUtil.*`.
|
||
|
||
`Graphics.Implicit.ObjectUtil.Box3`:
|
||
|
||
```haskell
|
||
getBox3 (Sphere r ) = ((-r, -r, -r), (r,r,r))
|
||
```
|
||
|
||
`Graphics.Implicit.ObjectUtil.GetImplicit3`:
|
||
|
||
```haskell
|
||
getImplicit3 (Sphere r ) =
|
||
\(x,y,z) -> sqrt (x^2 + y^2 + z^2) - r
|
||
```
|
||
|
||
|