C-CSG

A Composite Solid Geometry Modeler

showcase

When point and click modeling just gets in the way, this is the tool you need.

Download

Note: this package is for Linux. Windows users will have to install MinGW or similar and possibly change the makefile, or use Linux from a VM.

Browse Contributed Model Files (email cyrus@c-csg.com to have yours added.)


A Few Examples

A sphere with a radius of 10 being cut by two planes, and then being exported to a triangle mesh file.

showcase

#include "model.h"
model()
{
    difference();
        sphere(10);
        plane(0, 1, 0, 5);
        plane(0, -1, 0, 5);
    end();

    save_stl("/tmp/mesh.stl", .25, 5);
}

An intersection of two spheres.

showcase

#include "model.h"
model()
{
    intersection();
        sphere(10);
        translate(-5, 0, 0);
        sphere(10);
        translate(5, 0, 0);
    end();

    save_stl("/tmp/mesh.stl", .25, 5);
}

The same as the first example, except with 32 cylinders cut out of the side.

showcase

#include "model.h"
model()
{
    difference();
    sphere(10);
    plane(0, 1, 0, 5);
    plane(0, -1, 0, 5);

    for( double i = 0.0; i < 1.0; i += 1.0 / 32.0 )
    {
        cone(0.5, 0.5, 6);
        translate( sin( i * PI2 ) * 10 ,0 , cos( i * PI2 ) * 10 );
    }
    end();

    save_stl("/tmp/mesh.stl", .1, 5);
}

Basic Use

  1. Make sure the following dependencies are installed:
  2. Edit or replace model.c with your model code, like the examples above.
  3. Compile and run with the command make run
  4. Provided model.c did not contain errors, the output file specified in save_stl() can be examined by MeshLab or other tools.

Model Description Language and API

If it wasn't clear already, the programming language used to define the model is in fact C. Running make creates an executable "./c-csg", and it's this executable that produces the output file. If you are using a model.c file from untrusted sources it is highly recommended that you review it before compilation.

There are a myriad of C tutorials explaining how to use for loops and so on, so if you are new to C you might find it useful to reference some of them. However, there is no need to fully understand the language to be proficient at modeling with C-CSG.

Defining a model is accomplished by using "compound objects" in combination with simple objects, often nested. For example:

union(); //this is the beginning of a compound object definition.
    sphere(10); //this is the first object contained within the compound object
    translate(0,10,0); //this translation moves the sphere 10 units on the y axis
    box(); //this object is also contained within the compound "union" object
    intersection(); //this is the beginning of another compound object contained within the original "union" compound object
        sphere(1);
        translate(-2,0,0);
        sphere(1);
    end(); //the nested compound object is closed
    sphere(2); //this object belongs to the original merge compound object
    translate(20,0,0); //this translation is applied to the sphere
end(); //this closes the compound object, and any objects that might follow do not become part of it.
rotate(0,PI,0); //this transformation rotates the compound object.

All compound objects must eventually be closed by a trailing end(), objects that are specified before the end() are contained within it. Transformations can be applied to compound objects before the containing objects are added, or after being closed with end() or both.

The default world space has the y-axis up and all trigonometry and rotation functions use radians unless otherwise specified.


Basic Compound Objects

intersection()

The contained objects define a solid only where they all overlap.

union()

The contained objects are grouped together as one.

difference()

The first contained object is cut by every following object.


Simple Objects

sphere(r)

r is the radius.
sphere

sphere(10);

box()

Defaults to 1x1x1 centered on the origin.
box

box();

plane(x,y,z, d)

x,y,z defines the normal, and d the distance from the origin. Generally it should only be used in a difference to cut other objects because it is infinite.


cone(r1, r2, d)

r1 and r2 define the radius of each end, and d defines the distance between the end points.
cone

cone(5,10,20);

sweep(point_list_len, point_list, r)

Uses a 2d polygon to define a circular object where point_list is an array of doubles of the form {x1,y1,x2,y2,x3,y3,...} and point_list_length is the element count, eg. {3,6,8,9} = 4. r is the inner radius.
sweep

double k[]={0.9,0.0, 0.9,4.5, 1.2,4.5, 1.0,1.4, 1.2,0.0};
sweep(len(k), k, 8.0);

lathe(point_list, point_list_length, distance, twists)

Uses a 2d polygon with y coordinates between 0 and 1 to define a twisting shape, eg. screw threads. Distance is the height of the shape, twists is the number of rotations that the polygon will sweep through before reaching the specified distance. Negative values for twist can be used to make counter-clockwise threads.
lathe

double m[]={3.0,1.0, 4.0,1.0, 5.0,0.5, 4.0,0.0, 3.0,0.0};
lathe(len(m),m,10,8);

extrude(point_list_length, point_list, depth)

Uses a 2d polygon and a depth value to create an extruded object.
extrude

double k[]={0.9,0, 0.9,4.5, 1.2,4.5, 4.6,1.4, 1.2,0};
extrude(len(k), k, 1.0);

extrude_quad(point_list_length, point_list, depth)

Uses a 2d polygon made from straight lines and quadratic beziers to create an extruded object. The point_list is formatted like so: x,y,control_x,control_y,x,y,control_x,control_y,x,y... The final point must equal the first, and straight lines should have their control points set to the value of QUAD_LINE. Note: the bezier curves will be clipped to the internal polygon consisting of all the points if the control point intrudes too far in upon another part of the curve.
bezier

double q[]={0.9,0.0, -2,2.2, 0.9,4.5, QUAD_LINE,QUAD_LINE, 1.2,4.5, 3.0,2.0, 2.6,1.4, QUAD_LINE,QUAD_LINE, 1.2,0.0, QUAD_LINE,QUAD_LINE, 0.9,0.0};
extrude_quad(len(q),q,4);

text(string, font, d)

Creates a group of extrude_quad objects using the specified utf8 encoded string and a path to a ttf font. Because the dimensions are variable depending on the font it is often helpful to use the dimensions function to scale it appropriately.
text

text("Hello World!","/usr/share/fonts/truetype/droid/DroidSerif-Bold.ttf",4.5);

lathe_quad(point_list_length, point_list, distance)

Uses a 2d polygon made from straight lines and quadratic beziers to create an object of the same nature as the lathe object.
lathe_q

double q[]={3.0,1.0, QUAD_LINE,QUAD_LINE, 4.0,1.0, 5.0,0.5, 4.0,0.0, QUAD_LINE,QUAD_LINE, 3.0,0.0, QUAD_LINE,QUAD_LINE, 3.0,1.0};
lathe_quad(len(q),q,8,3);

sweep_quad(point_list_length, point_list, r)

Uses a 2d polygon made from straight lines and quadratic beziers to create an object of the same nature as the sweep object.
sweep_q

double q[]={0.9,0.0, -2,2.2, 0.9,4.5, QUAD_LINE,QUAD_LINE, 1.2,4.5, 3.0,2.0, 2.6,1.4, QUAD_LINE,QUAD_LINE, 1.2,0, QUAD_LINE, QUAD_LINE, 0.9,0};
sweep_quad(len(q),q,8);

Special Compound Objects

interior(noise_function(x,y,z))

Defines a non-solid interior for the enclosed objects.
interior

#include "model.h"

int my_interior_noise(double *local)
{

    // use three octaves 
    double v0,v1,v2;

    v0 = open_simplex_noise3(osctx,  local[0] /2.0, local[1]/2.0 ,  local[2]/2.0);
    v1 = open_simplex_noise3(osctx,  local[0] / 4.0, local[1] / 4.0,  local[2] / 4.0);
    v2 = open_simplex_noise3(osctx,  local[0] / 8.0, local[1] / 8.0,  local[2] / 8.0);

    double v = v0+v1+v2;

    if(v>=-1.0 && v<=0.4) //arbitrary range is solid
        return 1;

    return 0;   
}


model()
{
    interior(my_interior_noise);
        sphere(5);
        sphere(5);
        translate(5,0,0);
    end();

    save_stl("/tmp/mesh.stl", .25/6, 5);
}

exterior(noise_function(x,y,z), padding)

Moves the surface of enclosed objects according to the user defined noise function, the padding value is used to expand the bounding box and should be at least equal to the maximum deviation that the noise function applies to the object.
exterior

#include "model.h"

void my_exterior_noise(double *local)
{   
    // use three octaves 
    double v0,v1,v2;
    v0 = open_simplex_noise3(osctx,  local[0] /2.0, local[1]/2.0 ,  local[2]/2.0);
    v1 = open_simplex_noise3(osctx,  local[0] / 4.0, local[1] / 4.0,  local[2] / 4.0);
    v2 = open_simplex_noise3(osctx,  local[0] / 8.0, local[1] / 8.0,  local[2] / 8.0);

    double v = v0+v1+v2;

    if(v>=-1.0 && v<=0.4) //if the noise is in this range we move the point passed to us
    {
        local[0]+=v;
        local[1]+=v;
        local[2]+=v;
    }
}


model()
{

    exterior(my_exterior_noise, 1.0);
        sphere(5);
        sphere(5);
        translate(5,0,0);
    end();

    save_stl("/tmp/mesh.stl", .25/6, 5);
}

Transformation Functions

scale(x,y,z)

Scale current object.


translate(x,y,z)

Move current object.


identity()

Reset transforms for current object. Can be used to remove transformations from a previously copied object.

paste(o);
identity(); //orientation, scaling, and rotation of copied object are now clear

rotate(x,y,z)

Rotate current object by x, y, and z in that order.


rotate_arbitrary(a, x,y,z)

Rotate an object by angle a along an arbitrary axis defined by x,y,z.


Other Functions

flip_world_axis(x,y,z)

If an integer 1 is set for x, y, or z the axis will be inverted.


world_z_up()

Changes from a y-up world to a z-up world.


print_tree()

Prints object tree to console, useful for understanding how a model is defined in memory.


end()

This function is required to follow a compound object to indicate the end of the enclosed objects.


dimensions(*cx, *cy, *cz)

Changes the values in the variables pointed to by cx,cy,cz to the width, height, and depth of the bounding box, generally useful for resizing text and aligning objects without prior knowledge of their general size.

double plate_cx = 60.0;
double plate_cz = 25.0;
double text_border = 5.0;
double text_cx,text_cy,text_cz;

difference();

    box();
    translate(0,-0.5,0);
    scale(plate_cx,4,plate_cz); 

    text("C-CSG","/usr/share/fonts/truetype/droid/DroidSerif-Bold.ttf",4.5);

    //center text on plate
    dimensions(&text_cx,&text_cy,&text_cz);         
    translate(-text_cx/2.0,-4,-text_cz/2.0); //center text on zero  
    scale((plate_cx-(text_border*2.0))/text_cx,1,(plate_cz-(text_border*2.0))/text_cz);  //scale to correct size to fill plate minus the border padding

end();

OBJECT cut()

Removes the current object and children from the model and stores it in OBJECT.

intersection();
    sphere(10);
    translate(-5,0,0);
    sphere(10);
    translate(5,0,0);
    sphere(10);
    translate(0,0,-5);
    sphere(10);
    translate(0,0,5);
end();
OBJECT my_var = cut(); //removes current object (the intersection and contents) from the model and stores it in the variable "my_var".

paste(OBJECT)

Puts a copy of the stored OBJECT into the model.

copypaste

intersection();
    sphere(10);
    translate(-5,0,0);
    sphere(10);
    translate(5,0,0);
    sphere(10);
    translate(0,0,-5);
    sphere(10);
    translate(0,0,5);
end();

OBJECT my_var = cut();

difference();
    sphere(10);
    paste(my_var);
    translate(0,5,0);
    paste(my_var);
    translate(0,-5,0);
end();

save_stl(filename,resolution,threads)

Exports the model as a triangle mesh using the specified number of CPU threads and the specified resolution.