PShape Intersections

Hi there :wave:

I’m using processing to generate SVG files for pen plotting.

I have a PShape object which is a custom geometry, I would like to use that geometry as a mask/intersection or a boolean check to then draw a some additional geometry within it as a fill.

I’m not able to use textures or PImage objects for the fill as I need to be able to export an SVG at the end of the script.

I’m aware of RShape from geometrive, which does offer a handy .intersection() function, although I’m not sure how to get that to play nicely with my PShape objects.

Are there any other methods I could investigate to solve my problem?
(I’m trying to avoid writing lengthy region checks)

For reference; the geometries that I would like to fill are these sections of a circle:

Thanks for any help you can offer.

1 Like

This is a bit of a hack, maybe not an answer you’re looking for …

Sometimes I render ‘whole’ shapes in the Processing sketch, then apply the ‘boolean’ operation I desire in Inkscape (you might even automate this using Inkscape’s command line mode). So, the fill is one set of shapes, and the mask another. Additionally, you can separate shapes into layers to make the Inkscape work easier.

I’d rather expedite the process of getting results onto paper when I’m plotting, even if the sketch isn’t ‘fully-automated.’

2 Likes

Processing Geometry Suite provides 2D shape boolean operations like Geomerative but takes in (and outputs) PShapes.

4 Likes

Wow! That’s a seriously well featured suite!

There’s a lot of methods in there that I’d been putting off trying for fear of over-complication.

I’m going to enjoy playing with this package, thank you very much!

Yeah that is a bit of a hack… :grimacing:
I once lined some disparate parts of a plot up in Inkscape and still feel a little guilty about it.

I guess I take the opinion that the final drawing is only an abstraction of the input code if it remains as unadulterated as possible throughout the entire digital workflow.
I’m either a purist or a pedant and I’m not sure which is worse! :sweat_smile:

1 Like

I’m having some issues getting .intersection to work as I’d expect.

As above I’m taking my sectioned circle and trying to fill each section with some diagonal lines.

I’m applying your .intersection() method as below:

      shape(section);
      if (i%2==0) {
        fill = PGS_ShapeBoolean.intersect(section, leftFill);
      } else {
        fill = PGS_ShapeBoolean.intersect(section, rightFill);
      }
      PGS_Conversion.setAllStrokeColor(fill, 0, 1);
      shape(fill);

I’m seeing vertical lines being output, not the diagonal lines that are contained in rightFill & leftFill.

Any thoughts?

Can you post your full code? I’m not fully clear about what you’re trying to do… slice the vertical strips? What are leftFill and rightFill exactly?

Excuse my messy code!
The idea is that I generate the sectioned circle as above and then fill each section with an alternating fill, leftFill & rightFill. Which are PShape objects made up of many diagonal lines with varying spacing.

I had hoped that .intersect() would allow me to make a masked PShape object to apply rightFill & leftFill to the main sectioned circle shape. Although I’m guessing that I’m getting my use cases mixed up…

import processing.svg.*;
import micycle.pgs.*;

void setup() {
  size(420, 594);
  background(255);
  noLoop();
  noFill();
  beginRecord(SVG, "sectionedCircle.svg");
}

PShape createLineFills(float a, float maxspacing) {
  float spacing = maxspacing;
  PShape line = createShape();
  
  line.beginShape(LINES);
  for (float x=-height; x<=height; x+=spacing) {
    line.vertex(x, -height);
    line.vertex(x, height);
    spacing = map(noise(x*0.01), 0, 1, 0, maxspacing);
  }
  line.endShape();
  line.rotate(a);
  return line;
}

float sagitta(float r, float s) {
  return sqrt(2*s*r-s*s);
}

void drawSectionedLinedCircle(float x, float y, float r, int n) {
  translate(x, y);
  float rInc = (2*r)/n;
  float s = rInc;
  float a1 = 0;
  float a2 = asin(sagitta(r, s)/r);
  float maxShift = 100;
  float scale = 0.15;

  PShape leftFill = createLineFills(QUARTER_PI, 50);
  PShape rightFill = createLineFills(-QUARTER_PI, 50);
  PShape fill;

  while (a2<=PI) {
    for (float i=0; i<n; i++) {
      
      pushMatrix();
      translate(0, map(noise(i*scale), 0, 1, -maxShift, maxShift));
      
      PShape section = createShape();
      section.beginShape();

      for (float a=a1; a<=a2; a+=radians(0.5)) {
        section.vertex(r*cos(a), r*sin(a));
      }
      
      for (float a=a2; a>=a1; a-=radians(0.5)) {
        section.vertex(r*cos(-a), r*sin(-a));
      }
      section.endShape(CLOSE);
      
      a1 = a2;
      s += rInc;
      a2 = asin(sagitta(r, s)/r);
      if (s>=r)
        a2 += 2*(HALF_PI-a2);
      //println(degrees(a2));

      shape(section);
      if (i%2==0) {
        fill = PGS_ShapeBoolean.intersect(section, leftFill);
      } else {
        fill = PGS_ShapeBoolean.intersect(section, rightFill);
      }
      PGS_Conversion.setAllStrokeColor(fill, 150, 2);
      shape(fill);
      popMatrix();
    }
  }
}

void draw() {
  drawSectionedLinedCircle(width/2, height/2, width/2, 9);
  endRecord();
}
1 Like

Your approach was correct, but you were thrown off by the way geometry operations work. Lines are 1D objects (they have no “height”) so intersecting a line with a polygon returns nothing. The solution is to buffer the lines into actual 2D polygons so intersection() becomes a meaningful operation.

I’ve also had to replace line.rotate(a) with a PGS rotation method. The problem with .rotate() is that it affects the shape’s rotation matrix only and not the actual coordinates of its vertices. A PShape’s rotation matrix is not accessible and so the buffer() method would otherwise buffer the unrotated (vertical) lines.

So in the end only your createLineFills() method needed changing in the last few lines.

PShape createLineFills(float a, float maxspacing) {
  float spacing = maxspacing;
  PShape line = createShape();

  line.beginShape(LINES);
  for (float x=-height; x<=height; x+=spacing) {
    line.vertex(x, -height);
    line.vertex(x, height);
    spacing = map(noise(x*0.01), 0, 1, 0, maxspacing);
  }
  line.endShape();
  line = PGS_Transformation.rotateAroundCenter(line, a);
  line = PGS_Morphology.buffer(line, 1);
  return line;
}

image

4 Likes

Ah Thanks so much!
I think I’m going to spend some time exploring the quirks of PGS further.

Glad to be able to put a lid on this little project. The final output is interesting to look at, but I’m very excited to be able to quickly and easily create fills for custom geometry.

Thanks Again!

4 Likes

@micycle PGS is exactly the tool that will make one of my projects easier to do. There’s something in your above ‘createLineFills’ method here that’s baffling me though. It seems like all lines are somehow getting doubled? Even if I strip out the ‘for’ loops and use line.vertex to draw a single diagonal from the upper-left to the lower-right of the container, it renders (and saves as an SVG) with two diagonal lines separated by a few pixels. Is this a known quirk, and/or is there a way to have it draw just a single line?

Edit: I just read your response above about needing the lines to form a closed loop in order for the intersection operation to work.

What I said before was actually incorrect. PGS can (at least now) intersect a LINES shape with a polygon, returning the linework as expected. For instance, a hilbert curve intersected with a heart-shaped polygon:

1 Like

@micycle sweet, is that a more recent development? I need to check which version of PGS I’m using and update if it’s not the current.

Well I’m not sure why I didn’t think it worked like that before. Either way, latest dev build is here.

1 Like

Thanks for sharing this project! :raised_hands:t4: