Problem with Bezier Curves when exporting SVG from illustrator,

Hi @jeykech,

tldr, I agree with @Tiemen, research a library with better curve / SVG support. Processing core’s SVG support, afaik, was never intended to be comprehensive. PShape is an interface which serves as a jack-of-all trades – it has to provide a common lexicon between 2D vector-based shapes and 3D meshes.

You may also wish to look around on Adobe Illustrator forums to see if, from the export side of the workflow, your question has already been asked and answered.

Searching for they keywords SVG ‘simplify’, ‘minify’ or ‘optimize’ should also give you a bunch of options. You’ll have to experiment, though, to see if they just condense code or actually attempt to remove extraneous data from an SVG path.

Next, I’m linking to your prior thread. I’m not sure from your description above if, after anchor points have been separated from control points, the control point data can then be discarded. Since your more complex patternwork looks to use straight lines, it may be helpful to specify. (EDIT: I’m going to assume for now that they can be.)

Suppose I have this SVG file, called “demo.svg”:

<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  viewBox="0 0 512 512">
<!-- <g
  transform="
    translate(256.0, 256.0)
    rotate(0.0) 
    scale(128.0, 128.0)">
<g 
  stroke-width="0.015" 
  stroke-opacity="1.00" 
  stroke="#202020" 
  fill-opacity="1.00" 
  fill="#9ad8e2"> -->
<path d="
  M -1.0000 0.0000 
  C -0.9166 0.0833,-0.8333 0.1666,-0.7500 0.2500 
  C -0.7500 0.4166,-0.7500 0.5833,-0.7500 0.7500 
  C -0.5833 0.7500,-0.4166 0.7500,-0.2500 0.7500 
  C -0.1666 0.8333,-0.0833 0.9166,0.0000 1.0000 
  C 0.0833 0.9166,0.1666 0.8333,0.2500 0.7500 
  C 0.4166 0.7500,0.5833 0.7500,0.7500 0.7500 
  C 0.7500 0.5833,0.7500 0.4166,0.7500 0.2500 
  C 0.8333 0.1666,0.9166 0.0833,1.0000 0.0000 
  C 0.9166 -0.0833,0.8333 -0.1666,0.7500 -0.2500 
  C 0.7500 -0.4166,0.7500 -0.5833,0.7500 -0.7500 
  C 0.5833 -0.7500,0.4166 -0.7500,0.2500 -0.7500 
  C 0.1666 -0.8333,0.0833 -0.9166,0.0000 -1.0000 
  C -0.0833 -0.9166,-0.1666 -0.8333,-0.2500 -0.7500 
  C -0.4166 -0.7500,-0.5833 -0.7500,-0.7500 -0.7500 
  C -0.7500 -0.5833,-0.7500 -0.4166,-0.7500 -0.2500 
  C -0.8333 -0.1666,-0.9166 -0.0833,-1.0000 0.0000
  Z"></path>
<!-- </g> -->
<!-- </g> -->
</svg>

I’ve formatted it to make it somewhat human-readable. Also, I’ve commented out the transformation and style groups to make it simpler to deal with in Processing. The M (move-to) command, followed by the C (cubic Bezier curve-to) command, and finally Z (close path) parallels Processing’s beginShape, vertex, bezierVertex and endShape.

If you number the vertices retrieved from the PShape once it’s been loaded into Processing,

PShape demo;
PVector[] vertices;

float scale = 256.0 * 0.95;
PVector trns = new PVector(256.0, 256.0);

void setup() {
  size(512, 512);
  demo = loadShape("demo.svg");
  demo = demo.getChild(0);
  
  // This is known to be a closed shape, so the last
  // vertex and the first vertex should be the same.
  int vertCount = demo.getVertexCount() - 1;
  vertices = new PVector[vertCount];
  for (int i = 0; i < vertCount; ++i) {
    vertices[i] = demo.getVertex(i);
    vertices[i].mult(scale);
    vertices[i].add(trns);
  }
  
  textSize(14);
  textAlign(CENTER, CENTER);
}

void draw() {
  background(#fff7d5);

  int len = vertices.length;
  for (int i = 0; i < len; ++i) {

    if (i % 3 == 0) {
      fill(#232323);
    } else {
      fill(#ff2828);
    }
    
    PVector v = vertices[i];
    text(i, v.x, v.y);
  }
}

the result looks like this:

For every anchor point, in black, there are two control points, in red. Between any two anchor points, say the segment between 6 and 9, the control points 7 and 8 lie on the line segment formed by 6 and 9. I’ll leave it to you to research how to test whether a point lies on a line segment. Keep in mind, this order only holds because I’ve used a simple SVG: with all C commands.

Now suppose I simplify the above SVG file even further:

<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  viewBox="0 0 512 512">
<path d="
  M -1.0000 0.0000 
  L -0.7500 0.2500 
  L -0.7500 0.7500 
  L -0.2500 0.7500 
  L 0.0000 1.0000 
  L 0.2500 0.7500 
  L 0.7500 0.7500 
  L 0.7500 0.2500 
  L 1.0000 0.0000 
  L 0.7500 -0.2500 
  L 0.7500 -0.7500 
  L 0.2500 -0.7500 
  L 0.0000 -1.0000 
  L -0.2500 -0.7500 
  L -0.7500 -0.7500 
  L -0.7500 -0.2500 
  L -1.0000 0.0000
  Z"></path>
</svg>

I’ve replaced each C command with L (line-to) and discarded all but the last coordinate pair from the comma-separated list that followed C. If I update my Processing code in draw to draw the lines between the vertices

void draw() {
  background(#fff7d5);

  int len = vertices.length;
  PVector prev = vertices[0];
  PVector curr = vertices[1];
  fill(#202020);
  text(0, prev.x, prev.y);
  for (int i = 1; i < len; ++i) {
    curr = vertices[i];
    stroke(#ff2828);
    line(prev.x, prev.y, curr.x, curr.y);
    text(i, curr.x, curr.y);
    prev = curr;
  }
}

I get this

Depending on which commands an SVG path element’s d attribute contains, I suspect you’ll get a different count of vertices. A path with a variety of commands (for example, L, Q and C) would likely require more complex parsing, in which case I’d recommend moving away from PShape altogether. I’ll link to the source code if you wish to see how Processing parses an SVG path.

Hope that helps. Best,
Jeremy

4 Likes