# CSG (Constructive Solid Geometry)

For years I have looked for CSG in Processing… then I found a nice Java CSG candidate library and George Profenza in minutes made it work with Processing:

I’m very happy

And it even works with my beloved Processing Python Mode: https://github.com/villares/py.processing-play/tree/master/CSG_py

Now the next step, I guess, would be someone skilled in the ancient Processing Library template & repository maintenance arts to wrap it and make it more easily available…

2 Likes

Exciting!

would it be possible to use it for a 3D engine, e.g. let a ball roll through the hollow gaps ?

I got the thing running under Win 10. Didn’t compile but just downloaded his zip and worked from there. Great!

(not the 3D physics engine)

simplified version below.

Chrisir

``````

//https://stackoverflow.com/questions/56999816/is-it-possible-to-use-jcsg-library-with-processing
//https://discourse.processing.org/t/csg-constructive-solid-geometry/12693

// the PShape reference which will contain the converted
PShape csgResult;

void setup() {
//
size(900, 900, P3D);
noStroke();

// JCSG sample code:
// we use cube and sphere as base geometries
CSG cube = new Cube(2).toCSG();
CSG sphere = new Sphere(1.25).toCSG();

// perform difference
CSG cubeMinusSphere = cube.difference(sphere);

// Convert CSG to PShape -> Note: CSG units are small so we scale them up so the shapes are visible in Processing
csgResult = CSGToPShape(cubeMinusSphere, 45);

println("use mouse to  rotate");
}

void draw() {
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
shape(csgResult);
}

//-----------------------------------------------------------------------------

// re-usable function to convert a CSG mesh to a Processing PShape
PShape CSGToPShape(CSG mesh, float scale) {
// allocate a PShape group
PShape csgResult = createShape(GROUP);
// for each CSG polygon (Note: these can have 3,4 or more vertices)
for (Polygon p : mesh.getPolygons()) {
// make a child PShape
PShape polyShape = createShape();
// begin setting vertices to it
polyShape.beginShape();
// for each vertex in the polygon
for (Vertex v : p.vertices) {
// add each (scaled) polygon vertex
polyShape.vertex((float)v.pos.getX() * scale, (float)v.pos.getY() * scale, (float)v.pos.getZ() * scale);
}
// finish this polygon
polyShape.endShape();
//append the child PShape to the parent
}
return csgResult;
}
//
``````
1 Like

maybe you can help me with one thing.

I haven’t found it in the documentation http://lifesine.eu/so/jcsg-0.5.8-SNAPSHOT-javadoc.zip

I try to rotate a cylinder. See the commented out line with lots of ???

Thank you.

Chrisir

``````// the PShape reference which will contain the converted
PShape csgResult;

void setup() {
//
size(900, 900, P3D);
noStroke();

// JCSG sample code:
// we use cube and sphere as base geometries
CSG cube = new Cube(12, 2, 2).toCSG();
CSG sphere = new Sphere(1.25).toCSG();

// cyl2 == horizontal
CSG cyl2 = new Cylinder( cylRadius, cylRadius, 12, 43).toCSG();
cyl2 = cyl2.transformed(Transform.unity().translateX(2));
// cyl2 = cyl2.transformed(Transform.unity().rotate(1.0, 1.2, 2.0));  // ???????????????????????????????????

csgResult = CSGToPShape(cyl2, 45);
}

void draw() {
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);

shape(csgResult);
}

//-----------------------------------------------------------------------------

// re-usable function to convert a CSG mesh to a Processing PShape
PShape CSGToPShape(CSG mesh, float scale) {
// allocate a PShape group
PShape csgResult = createShape(GROUP);
// for each CSG polygon (Note: these can have 3,4 or more vertices)
for (Polygon p : mesh.getPolygons()) {
// make a child PShape
PShape polyShape = createShape();
// begin setting vertices to it
polyShape.beginShape();
// for each vertex in the polygon
for (Vertex v : p.vertices) {
// add each (scaled) polygon vertex
polyShape.vertex((float)v.pos.getX() * scale, (float)v.pos.getY() * scale, (float)v.pos.getZ() * scale);
}
// finish this polygon
polyShape.endShape();
//append the child PShape to the parent
}
return csgResult;
}
//
``````

Hi @Chrisir !

I didn’t have the time to explore this library yet. But looking at the documentation, it seems to have `rotX()`, `rotY()` and `rotZ()` methods and a `rot(double, double, double)` instead of rotate()

Cheers,
Alexandre

1 Like

Thanks a ton!

Chrisir

1 Like

That worked.
Do you have any idea about the 3D physics engine thing?

Eg. when I wanted to use the shapes as a marble track for a marble? I could have gravity but the marble would have to detect whether there is a shape underneath (and its angle).

Thanks!

Hi Chrisir,

Physics engines are a whole different beast. I can’t remember any library with 3D Physics right now, most specifically collisions are hard. I think toxiclib had some 3D physical deformations, but no collisions.

You should open a new thread/topic for that!

1 Like

PixelFlow has 3d softbody physics with collision detection, I believe.

2 Likes

Thanks for finding / sharing this. I think at the point that someone who was working with it was willing to develop some PDE example documentation of how to use it… then it would make sense to wrap the library with examples and distribute it through the contributions manager. For now, it seems like you can drop it in and use it – that’s pretty accessible already…?

2 Likes

for 3D physics-engine-library see also : 3D physics engine library

More advanced example with a single marble track :

• and below full Marble Track
``````//https://stackoverflow.com/questions/56999816/is-it-possible-to-use-jcsg-library-with-processing
//https://discourse.processing.org/t/csg-constructive-solid-geometry/12693

// the PShape reference which will contain the converted
PShape csgResult, // red marble track
csgResult2;// green marble

void setup() {
//
size(900, 900, P3D);
noStroke();

// JCSG sample code:

// green marble
CSG sphere = new Sphere(.60).toCSG();
// red marble track
CSG cubeMinusSphere = defineRedCSG();

// make to PShape
csgResult = CSGToPShape(cubeMinusSphere, 45, color(255, 0, 0)); // red marble track
csgResult2 = CSGToPShape(sphere, 45, color(0, 255, 0)); // green marble
}

void draw() {
//
background(0);
lights();

translate(width * 0.5, height * 0.5, 253);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));

shape(csgResult); // red marble track
shape(csgResult2);// green marble
}

//-----------------------------------------------------------------------------

CSG defineRedCSG() {
// define the red marble track "cubeMinusSphere"

// we use cube as base geometries (long thin cube for the track)
CSG cubeMain = new Cube(12, 2, 2).toCSG();

// this we cut out later
CSG cube2 = new Cube(12, 2, 2).toCSG();
cube2 = translateXYZ(cube2, -2.1, 0, 1.14);

// this we cut out later
// cyl1 == vertical right
CSG cyl1 = new Cylinder( cylRadius, cylRadius, 12, 43).toCSG();
cyl1 = translateXYZ(cyl1, 5, 0, 0);

// this we cut out later
// cyl2 == horizontal, long
CSG cyl2 = new Cylinder( cylRadius, cylRadius, 12, 43).toCSG();
cyl2 = cyl2.transformed(Transform.unity().rot(0, 90, 0));  // rotate in degrees
cyl2 = translateXYZ(cyl2, cylRadius+1.2+2.1+1.2+.5, 0, 0);

// this we cut out later
// cyl3 == vertical left
CSG cyl3 = new Cylinder( cylRadius, cylRadius, 22, 43).toCSG();
cyl3 = translateXYZ(cyl3, -5, 0, -4);

// perform difference (cut out)
CSG cubeMinusSphere = cubeMain.difference(cyl1);
cubeMinusSphere = cubeMinusSphere.difference(cyl2);
cubeMinusSphere = cubeMinusSphere.difference(cube2);
cubeMinusSphere = cubeMinusSphere.difference(cyl3);

return cubeMinusSphere;
}

CSG translateXYZ ( CSG csgItem,
float x, float y, float z ) {
// tool for translate
CSG csgItemNew = csgItem.transformed(Transform.unity().translateX(x));
csgItemNew = csgItemNew.transformed(Transform.unity().translateY(y));
csgItemNew = csgItemNew.transformed(Transform.unity().translateZ(z));

return csgItemNew;
}

PShape CSGToPShape(CSG mesh, float scale, color colPShape) {
// re-usable function to convert a CSG mesh to a Processing PShape

// allocate a PShape group
PShape csgResult = createShape(GROUP);

// for each CSG polygon (Note: these can have 3,4 or more vertices)
for (Polygon p : mesh.getPolygons()) {
// make a child PShape
PShape polyShape = createShape();
// begin setting vertices to it
polyShape.beginShape();
fill(colPShape);

// for each vertex in the polygon
for (Vertex v : p.vertices) {
// add each (scaled) polygon vertex
polyShape.vertex((float)v.pos.getX() * scale, (float)v.pos.getY() * scale, (float)v.pos.getZ() * scale);
}//for
// finish this polygon

polyShape.endShape();
//append the child PShape to the parent
}//for

return csgResult;
}
//
``````

• and below full Marble Track
``````
//https://stackoverflow.com/questions/56999816/is-it-possible-to-use-jcsg-library-with-processing
//https://discourse.processing.org/t/csg-constructive-solid-geometry/12693

import peasy.*;

PeasyCam cam;

// the PShape ArrayList which will contain the converted PShapes
ArrayList<PShape> list = new ArrayList(); // red marble track and green marble

void setup() {
//
size(2000, 1100, P3D);
noStroke();
cam=new PeasyCam(this, 2100);

makeSphere();
makeMarbleTrack();
} // func

void draw() {
//
background(0);
lights();

translate(width * 0.5, height * 0.5, 0); //  or use translate(width * 0.5, height * 0.5, 253);  OR  translate(width * 0.5, height * 0.5, 0);
//rotateY(map(mouseX, 0, width, -PI, PI));
//rotateX(map(mouseY, 0, height, PI, -PI));

for ( PShape ps : list ) {
shape(ps); // red marble track and green marble
}//for
//
}//func

//-----------------------------------------------------------------------------

void makeSphere() {

// JCSG sample code:

// green marble
CSG sphere = new Sphere(.60).toCSG();

// make sphere to PShape and add to list
PShape csgResult = CSGToPShape(sphere, 45, color(0, 255, 0)); // green marble
}

void makeMarbleTrack() {

// JCSG sample code:

PShape csgResult;

// red marble track
CSG cubeMinusSphere = defineRedCSG();

// staple red
csgResult = CSGToPShape(cubeMinusSphere, 45, color(255, 0, 0)); // red marble track

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(0, 0, -91);
csgResult.rotateZ(PI);

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(0, 0, 2*-91);
// csgResult.rotateZ(PI);

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(0, 0, 3*-91);
csgResult.rotateZ(PI);

// ------------------------
// 1st to the right
csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(-500+55, 0, 4*-91);
csgResult.rotateZ(PI);

// ------------------------
// 2nd to the right
csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
//  csgResult.rotateZ(PI);
csgResult.translate(2*(-500+55), 0, 5*-91);
csgResult.rotateZ(PI+.4);
csgResult.translate(62, -259, 0);

// ------------------------
// staple red II
float xTranslate=1315;
float yTranslate=175;
csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(0, 0, 6*-91);
csgResult.rotateZ(PI);
csgResult.translate(xTranslate, yTranslate, 0);

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(xTranslate, yTranslate, 7*-91);

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(0, 0, 8*-91);
csgResult.rotateZ(PI);
csgResult.translate(xTranslate, yTranslate, 0);

// ------------------------
// next right

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 0, 0)); // red marble track
csgResult.translate(0, 0, 9*-91);
csgResult.rotateZ(PI);
csgResult.translate(xTranslate+447, yTranslate, 0);

// ------------------------
// 2 final ones going back

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 222, 0)); // red marble track
csgResult.translate(0, 0, 10*-91);
csgResult.rotateZ(PI/2-.2);
csgResult.translate(xTranslate+447+40+136, yTranslate-131-60-33, 0);

csgResult = CSGToPShape(cubeMinusSphere.clone(), 45, color(255, 2, 222)); // red marble track
csgResult.translate(0, 0, 11*-91);
csgResult.rotateZ(PI/2-.2-.22);
csgResult.translate(xTranslate+447-340-3+17-77+111+111+222, yTranslate+131-222-222-66-199-77, 0);
//----
}

CSG defineRedCSG() {
// define the red marble track "cubeMinusSphere"

// we use cube as base geometries (long thin cube for the track)
CSG cubeMain = new Cube(12, 2, 2).toCSG();

// this we cut out later
CSG cube2 = new Cube(12, 2, 2).toCSG();
cube2 = translateXYZ(cube2, -2.1, 0, 1.14);

// this we cut out later
// cyl1 == vertical right
CSG cyl1 = new Cylinder( cylRadius, cylRadius, 12, 43).toCSG();
cyl1 = translateXYZ(cyl1, 5, 0, 0);

// this we cut out later
// cyl2 == horizontal, long
CSG cyl2 = new Cylinder( cylRadius, cylRadius, 12, 43).toCSG();
cyl2 = cyl2.transformed(Transform.unity().rot(0, 90, 0));  // rotate in degrees
cyl2 = translateXYZ(cyl2, cylRadius+1.2+2.1+1.2+.5, 0, 0);

// this we cut out later
// cyl3 == vertical left
CSG cyl3 = new Cylinder( cylRadius, cylRadius, 22, 43).toCSG();
cyl3 = translateXYZ(cyl3, -5, 0, -4);

// perform difference (cut out)
CSG cubeMinusSphere = cubeMain.difference(cyl1);
cubeMinusSphere = cubeMinusSphere.difference(cyl2);
cubeMinusSphere = cubeMinusSphere.difference(cube2);
cubeMinusSphere = cubeMinusSphere.difference(cyl3);

return cubeMinusSphere;
}

CSG translateXYZ ( CSG csgItem,
float x, float y, float z ) {
// tool for translate
CSG csgItemNew = csgItem.transformed(Transform.unity().translateX(x));
csgItemNew = csgItemNew.transformed(Transform.unity().translateY(y));
csgItemNew = csgItemNew.transformed(Transform.unity().translateZ(z));

return csgItemNew;
}

PShape CSGToPShape(CSG mesh, float scale, color colPShape) {
// re-usable function to convert a CSG mesh to a Processing PShape

// allocate a PShape group
PShape csgResult = createShape(GROUP);

// for each CSG polygon (Note: these can have 3,4 or more vertices)
for (Polygon p : mesh.getPolygons()) {
// make a child PShape
PShape polyShape = createShape();
// begin setting vertices to it
polyShape.beginShape();
fill(colPShape);

// for each vertex in the polygon
for (Vertex v : p.vertices) {
// add each (scaled) polygon vertex
polyShape.vertex(
(float)v.pos.getX() * scale,
(float)v.pos.getY() * scale,
(float)v.pos.getZ() * scale);
}//for
// finish this polygon

polyShape.endShape();
//append the child PShape to the parent