Design Overview
Descartes current uses implicit modeling to create solid models. This is a brief overview of the system internals.
Implicit Geometry Pipeline
UX
The UX (API) is designed to be familiar to those who use OpenSCAD. Let's start with a simple example to show the similarities.
This can all be executed in the Julia REPL, so follow along:
using Descartes
c = Cuboid([5,5,5])
h = translate([2.5,2.5,0])Cylinder(1,5)
obj = diff(c,h)
m = HomogenousMesh(obj)
save("cube_with_hole.stl", m)
This first line:
using Descartes
This imports the Descartes library.
c = Cuboid([5,5,5])
Here we construct a cube of size 5x5x5.
h = translate([2.5,2.5,0])Cylinder(1,5)
This line shows the OpenSCADisms. This can be read right-to-left like a matrix operation. First we construct a cylinder of radius 1, and height 5. Next we translate the cylinder to the coordinates 2.5, 2.5, 0 to put it in the center of our cube.
obj = diff(c,h)
Here we difference the cylinder from the cube. The corresponding set operations are union
and intersect
.
m = HomogenousMesh(obj)
At this point we convert our object to a mesh. Prior to this point the model is simply a data structure. In the next section we will discuss the process of meshign more in-depth.
save("cube_with_hole.stl", m)
Finally we save the mesh as an STL. Other formats such as OBJ, PLY, and OFF are also supported.
Meshing
In the prior section we mentioned that the "model" we create is just a data structure until we mesh it. So let's explore what happens in the meshing process.
The code to convert our data structure to a mesh is actually fairly short:
function (::Type{MT})(primitives::AbstractPrimitive{3, T}...;
samples=(128,128,128),
algorithm=MarchingCubes()
) where {T, MT <: AbstractMesh}
f(x) = FRep(primitives[1], x)
mesh = MT(f, HyperRectangle(primitives[1]), samples, algorithm)
for i = 2:length(primitives)
b = HyperRectangle(primitives[i])
lm = MT(x -> FRep(primitives[i], x), b, samples, algorithm)
mesh = merge(mesh, lm)
end
return mesh
end
The first two lines here are where the meshing happens:
f(x) = FRep(primitives[1], x)
mesh = MT(f, HyperRectangle(primitives[1]), samples, algorithm)
Any primitve or operation must implement two core operations; FRep
and HyperRectangle
.
FRep
is the functional representation (implicit representation) of the model. The first argument to FRep
is always the primitive. The second is a generic AbstractVector
. In julia we have type inference, so we do not need to annotate, so this will always be fast. FRep by convention must use 0 as the surface of the model, positive outside the model, and negative inside.
HyperRectangle
should return the extents of the primitive. The underlying API will handle transformations and basic set operations for us.
In the next line we actually perform the meshing operation. We call the meshtype with the implicit function we created, f
, in the bounds generated by HyperRectangle
, uniformly sample the space by samples
, and actually generate the triangular meshing using algorithm
. In this case, the defaults are samples=(128,128,128)
and algorithm=MarchingCubes()
.
In the loop for i = 2:length(primitives) ... end
we handle additional primitives passed to our meshing function so you can create a single mesh output from several different objects. In order to maintain performance and resolution these are not union
ed.