March 09, 2023

I built the general workflow of replicad to be similar to a standard CAD workflow: draw in 2D, sketch this in a plane in 3D, and then give it some thickness (extrude, revolve or sweep).

Unfortunately, there is no support for some basic 2D CAD operations in open
cascade. For instance, there is no **2D boolean operations**: fusing shapes
together, cutting one with another.

I found multiple libraries to do 2D operations, but they all failed to be the CAD library I expected. Let’s see what they lack (in my opinion, of course), and build my list of requirements.

We can start with the other open source CAD kernel, solvespace. It is a full
featured kernel, but lack an important feature for 2D CAD, **offsets**.

Offsets can be understood as parallel curves. They tend to be very useful to transform a line into a 2D shape (by giving it some kind of stroke width). CAM tools also use these to draw tool paths.

Then let’s have a look at Clipper. This is a very performant library
that does boolean operation (i.e. what they call clipping) and offsetting.
Unfortunately, it **only works with polygons** (i.e. shapes built with straight
lines).

What is this issue with polygons? Depending on your use case polygons are great. You can approximate any curve by a set of lines, and then use clipper to do your operations on it. This is what this great gear generator does.

This is the analogous of a mesh for 3D objects. The trade off is also the same
– you gain some simplicity of computation (and performance), but lose
information about the shape itself (the topology). It becomes more difficult to
select an *edge* of your shape (what is an edge then?).

Note that many other libraries that deal with 2D geometries have the same issue. I could find shapely for instance.

Cavalier contours is a rust library for offsets and boolean operations that supports arcs of circle in addition to straight lines. It seems that it is based on well researched algorithms, is fast and has some nice webassembly builds.

The issue I have with it, is its data model. As a very well focussed library and offers a simple API for defining poly lines. But it stops at that point – its data structure does not support anything like translations or rotations. There is no concept of holes within closed poly lines and ways to understand the inclusion logic.

I am looking for a **full CAD library**. Cavalier contours is not that. It is
good at some singular operations - but I would need to be extended a lot to
become a proper CAD library.

Flatten offers all the functionality that clippers and cavalier contours are missing. It supports arcs, is meant as a CAD library, with a data model for “islands” and “holes” and APIs around inclusion of shapes within other shapes.

But arcs are not enough – if you want to draw shapes that are slightly organic
or need to be smooth, it becomes useful to offer Bézier curves. For
instance fonts for text rendering are represented as sets of **Bézier curves**.
This is also a significant part of paths in SVG (or vector graphics in
general).

It is possible to approximate Bézier curves with arcs - but we get the same issue that we had with polygons. You loose the semantic relation between what an edge is and how you represent it.

maker.js (that I used to build the paper models in deckinabox) has the same issue. It is a full CAD library, it let you define Bézier curves, but they are just arcs in disguise.

Note that cavalier contours does not support Bézier either.

There are some other typical features of 2D CAD software that I did not
mention. For instance, **performance** is not my main concern now - I am more
looking for functionality first (and then, potentially, performance).

Then, solvespace, while it does not have offsets, offers a **constraints
solver**. This is very powerful for visual CAD (where these are more intuitive
than raw numbers). I have focussed on the *code CAD* aspects of a 2D library
– and I don’t think that constraints are great for code. Note that open cascade
also has a constraints solver.

Within replicad I use open cascade as the kernel. As I mentioned earlier 2D support within open cascade is sparse.

While it does not offer the CAD operations themselves in 2D (not 2D booleans or offsets), it gives the primitives needed (intersections and offsets of a single curve). So I built the complex curves data structures, boolean operations and offsets on top of the open cascade primitives.

This is still not great. A big drawback is that you need to pull a lot of things from open cascade and deal with garbage collection yourself. It is also far from perfect in many ways. For instance offsets of Béziers tend to fail a lot.

But it has other advantages that none of the other libraries provide. Open
cascade has an **arc of ellipses** type of curve, it provides ways to work with
**BSplines** (even better than Béziers!), it supports **stretching and
shearing** transformations and it has built-in **approximation routines**.

So what do I want from a 2D CAD library:

- basic geometric transformations (translations, rotations)
- boolean operations
- offsets of chains of curves
- complex types of curves (arcs, ellipses, Béziers)
- data structures thought for CAD with ways to express containment

And there are some other aspects that would be nice to have:

- performance
- approximation routines
- a constraints solver
- all affine transformations
- BSplines as a type of curve

Even with my minimal requirements I could not find any library that fit the bill. Within replicad, the best I could achieve was to extend open cascade so that it worked with its basic 2D primitives.

You can discuss this post on Mastondon

Personal blog by **Steve Genoud**.

Carrots are good for your health.

Carrots are good for your health.