Skip to content

dzufferey/scadla

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Scadla: a scala library for constructive solid geometry

Scadla is a small library to create 3D models. Those model can then, for instance, be 3D printed. Scadla is very much influence by OpenSCAD and has the same goal of being a programmer's CAD tool. The name scadla is a portmanteau of scad and scala.

Why scadla? OpenSCAD is doing a pretty good job and is becoming a de facto standard in the maker community.

That is true. OpenSCAD is usually doing a pretty fine job and we currently use it in the back end. However, there is a few things that, IMO, OpenSCAD did not get right or missing features that a good general purpose programming language, like scala, gives you for free. So instead of developing a new language, scadla simply embed CSG into an existing language. The main points on which we try to improve are:

  • modularity/reusability, package/namespace, toolchain support, etc. IMO OpenSCAD does not scale very well to projects across multiple files. The use vs include is very coarse grained. The absence of namespace gives rise to very long variable names in order to avoid clash. Fortunately, you get those things for free in scala. Also the toolchain is more mature. For instance, one can use maven to share libraries of objects and then just specify in project configuration which library to use and sbt will download them automatically, add them to the classpath, ...

  • first class objects An OpenSCAD program roughly build a tree with primitive objects as leaves and CSG operations as internal nodes. However, that tree is built implicitly and one cannot access it directly. In scadla, that tree is just an algebraic datatype. This means that you can reference it, e.g., val u = Union(Cube(...), Sphere(...)) and also inspect/modify it using pattern matching, e.g., u match { case Union(...) => ... }

  • type-safe units with the squants library. Let the compiler warns you when you try to rotate a object by 10mm. To keep a lightweight syntax EverythingIsIn provides implicit conversions from numbers to length and angles. For instance, add import scadla.EverythingIsIn.{millimeters, radians} in your file to work use millimeters and radians by default.

  • a small caveat: OpenSCAD does has only floating points number but scala makes the distinction between integers and floating points. For instance, 1/2 is the integer division and returns 0, not 0.5. If you want floating point numbers write 1/2.0 or 1.0/2.

Additional Backends and Operations

Scadla core operations are based on CSG. However, most recent CAD systems uses boundary representation which offers a wider range of operations. There is new backend for scadla based on BREP in development at https://github.com/dzufferey/scadla-oce-backend/.

Dependencies

For the moment, scadla uses

  • required: Java 8 or above
  • recommended: OpenSCAD for the CSG operations
  • optional: Meshlab to display the models. Scadla can try to display the model using X3Dom but it is currently quite limited and requires an internet connection.

Examples

For examples, please look in the example folder.

For a complete example, you can head to MecanumWheel.scala for a parametric design of an omnidirectional wheel.

The primitives and CSG operations are defined in Solid.scala. This gives you a basic verbose syntax. For instance, a cube written cube([1,2,1]) in OpenSCAD is written Cube(1 mm, 2 mm, 1 mm) in scadla. Scadla has explicits units. In that case, we use millimeters.

Let us consider the following example:

Intersection(
  Union(
    Cube(1 mm, 1 mm, 1mm),
    Translate(-0.5 mm, -0.5 mm, 0 mm, Cube(1 mm, 1 mm, 1 mm))
  ),
  Sphere(1.5 mm)
)

In the most verbose form, it corresponds to the full CSG tree. However, we can make it prettier. First, we can store Solids in variables and reuse them:

val c = Cube(1 mm, 1 mm, 1 mm)
val s = Sphere(1.5 mm)
val u = Union(c, Translate(-0.5 mm, -0.5 mm, 0 mm, c))
Intersection(u, s)

Next, we can replace the operation with a less verbose syntax using import InlineOps._

val c = Cube(1 mm, 1 mm, 1 mm)
val s = Sphere(1.5 mm)
(c + c.move(-0.5 mm, -0.5 mm, 0 mm)) * s

To avoid putting mm everywhere, it is possible to use implicit conversion with import scadla.EverythingIsIn.{millimeters, radians}

val c = Cube(1, 1, 1)
val s = Sphere(1.5)
(c + c.move(-0.5, -0.5, 0)) * s

The compiler will interpret numeric constant as millimeters when used as length and as radians for angles. It is also possible to use inches instead of millimeters and degrees instead of radians. Beware, when using the implicit conversions as it can be unpredictable.

Once, we have the description of the object we want to make, we need to evaluate the CSG tree to get a 3D model. We currently use OpenSCAD for that. See OpenSCAD.scala for the details.

Assuming that we the object we are interested in is stored in the obj variable. We can do a few things:

  • evaluate the tree and save the result in a .stl file with OpenSCAD.toSTL(obj, file_name)
  • view the result OpenSCAD.view(obj) (this requires meshlab)
  • evaluate the tree and get back a Polyhedron with OpenSCAD.getResult(obj)

By default, scadla will run openscad with $fa=4; $fs=0.5;, so complex designs can take a while to render. You can change that by giving the appropriate arguments to the different methods (see OpenSCAD.scala for the details)

To run this example execute sbt run and select scadla.examples.MecanumWheel.

Compiling and Using it

You can build scadla using sbt. To install sbt follow the instructions at http://www.scala-sbt.org/release/tutorial/Setup.html.

Then, in a console, execute:

$ sbt
> compile

To try some examples execute sbt run and select one example.

If you want to use it in another project, you need to add to your build.sbt:

resolvers += "jitpack" at "https://jitpack.io"

libraryDependencies += "com.github.dzufferey" %% "scadla" % "0.1.1"

JupyterLab

It is possible to use scadla with JupyterLab. First you need to install Almond. Then, look in sample.ipynb to see how it work.

Contributors

  • Damien Zufferey (basic infrastrucure)
  • Jan Ypma (adding type-safe units)

ToDo

Features that may (or may not) be implemented, depending on time and motivation:

  • features

  • implementation

    • file format:
      • PLY parser (this format is evil!!!)
    • InBox: Polyhedron: multiply, hull
    • object cache (speed-up recomputation)
    • try VTK as backend
      • installation
      • how to decide whether or not to compile VTK: it cannot be resolved as a MVN dependency but only a a local one ...
        • split the project into (1) core, (2) renderers, (3) examples
      • boolean operation
      • transform
      • convex hull
      • minkowski sum ???
    • built-in model viewer
      • choosing window size according to screen size, and allow resizing
      • moving the point of view
  • Assemblies: group of object linked by joints (can move), the goal is to make it easier to visualized larger projects and make sure everything fits/move properly

    • better notation for the connections
    • joint: what about time transformers (for min-max travel)
    • part: rendered part (visibility and serialization)
    • GUI: sliders for time and expansion
  • ongoing work in a rendering backend based on Open CASCADE Community edition: https://github.com/dzufferey/scadla-oce-backend

    • support for different operations: chamfer, fillet, and offset, but no convex hull or Minkowski sum.
    • much more ...
  • Documentation and tutorials

  • ...

Also, pull requests are welcome.