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 :smiley:

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
    csgResult.addChild(polyShape);
  }
  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   
  float cylRadius=.65;
  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
    csgResult.addChild(polyShape);
  }
  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() :slight_smile:

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

I made it work easily by downloading the zip.

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
  float cylRadius=.65;
  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
    csgResult.addChild(polyShape);
  }//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
  list.add(csgResult);
}

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
  list.add(csgResult); 

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

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

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

  // ------------------------
  // 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); 
  list.add(csgResult);

  // ------------------------
  // 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);
  list.add(csgResult);

  // ------------------------
  // 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); 
  list.add(csgResult);

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

  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); 
  list.add(csgResult);

  // ------------------------
  // 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); 
  list.add(csgResult);

  // ------------------------
  // 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); 
  list.add(csgResult);

  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); 
  list.add(csgResult);
  //----
}

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
  float cylRadius=.65;
  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
    csgResult.addChild(polyShape);
  }//for

  return csgResult;
}
//
2 Likes