1

I am trying to write some software that will do something similar to Adobe Illustrator's blend tool. The goal is to create new paths using stored paths as base ingredients at different weights (i.e. 30% path1 70% path2). For those unfamiliar, the Adobe Illustrator example looks like this:3 steps between two vector paths

The problem is, I really don't know where to start. Ideally this would be able to run in a browser so I was hoping to do it in JavaScript. I've looked into using SVGs with Raphael.js and standard vectors in html5 canvas, but it is important to be able to import vector art from Illustrator one way or another (parsing SVG files manually is no problem since it's just xml).

However, I have also considered doing it in Processing since it's vector support is very prominent and the geomerative library looks very powerful.

I just need to be pointed in the right direction so I can get started, thanks so much!

Saiato
  • 257
  • 1
  • 3
  • 14

1 Answers1

7

You've mentioned SVG/JS/Raphael.js and geomerative. As far as I can tell, you're interested in the interpolation bit, not the vector loading/handling bit (which will depend on what version you'll use JS/Java).

As long as you've got the vertices of the shape you want to blend and the ones of the shape you want to blend to (the number of vertices will be the same for both sets of vertices), all you need to do is interpolate the values. This will be something like:(1-t)*start+t*end; where t is the blend amount, a value between 0 (start shape) and 1 (end shape).

Here's a function that interpolates two sets of points(stored as PVector instances):

void interpolateVerts(float t,ArrayList<PVector> start,ArrayList<PVector> end,ArrayList<PVector> current){
  int numVerts = start.size();
  while(numVerts-- > 0) current.get(numVerts).set(PVector.add(PVector.mult(start.get(numVerts),(1-t)),PVector.mult(end.get(numVerts),t)));
}

It expects t (the blend amount), start and end positions plus a vector to store the current blended positions into.

You can see a quick and dirty demo here (notice the shape around the Stop Icon blending).

Another way to achieve this is to use the lerp() function which is made to interpolate between two values. You will be interpolating each coordinate for your shape, and PShapes' getVertex() method returns x y coordinates into a float[]. I presume looping though a single array and 'lerp'-ing is simpler and more portable than using PVector:

void lerpArray(float t,float[] start,float[] end, float[] current){
  int i = start.length;
  while(i-- > 0) current[i] = lerp(start[i],end[i],t);
}

And here's a quick sketch test sketch to illustrate the idea (mouseX = blend amount):

float[] start,end,current;
int len = 8;
void setup(){
  size(200,200);
  smooth();
  start = new float[len];
  end = new float[len];
  current = new float[len];
  setSquare(start,50,50,100,100);
  setSquare(current,50,50,100,100);
  setSquare(end,25,20,150,50);
}
void draw(){
  background(0);
  lerpArray((float)mouseX/width,start,end,current);
  beginShape();
  for(int i = 0 ; i < len; i+=2) vertex(current[i],current[i+1]);
  endShape();
}
void lerpArray(float t,float[] start,float[] end, float[] current){
  int i = start.length;
  while(i-- > 0) current[i] = lerp(start[i],end[i],t);
}
void setSquare(float[] verts,float x,float y,float width,float height){
  verts[0] = x+width;
  verts[1] = y;
  verts[2] = x+width;
  verts[3] = y+height;
  verts[4] = x;
  verts[5] = y+height;
  verts[6] = x;
  verts[7] = y;
}

Update

Hmmm...turns out it's a bit more complicated than that. Getting vertices isn't the problem. Redrawing the shape is one. PShape vertices have 'types'(VERTEX,BEZIER_VERTEX,etc.) so after interpolation, the vertices will need to be redrawn. I've tried extending PShape and PShapeSVG, but run into issues quickly (because of PShape's tree like structure). Since Processing is opensource, out of curiosity I tweaked the PShape class by adding setters for vertex positions and getters/setters for the fill colour then recompiled the library (core.jar)

BlendShape1BlendShape2BlendShape3

You can download the eclipse project here, which includes the recompiled core.jar file in the user folder. Note that this is NOT the recommended way to solve problems. Fore reference here is the code:

package svgblend;

import processing.core.PApplet;
import processing.core.PShape;

public class SVGBlend extends PApplet {
    PShape start,end,current;

    public void setup(){
      size(200,200);
      smooth();
      start   = loadShape("start.svg").getChild("start");
      end     = loadShape("end.svg").getChild("end");
      current = loadShape("start.svg").getChild("start");
      println(current.getFamily());
    }
    public void draw(){
      background(255);
      blendShape(start,end,current,(float)mouseX/width);
      shape(current,0,0);
    }
    void blendShape(PShape start,PShape end, PShape current,float amt){
        int i = start.getVertexCount();
        //verts
        while(i-- > 0) {
          float[] s = start.getVertex(i);
          float[] e = end.getVertex(i);
          current.setVertexX(i, lerp(s[0],e[0],amt));
          current.setVertexY(i, lerp(s[1],e[1],amt));
        }
        //colour
        int sFill = start.getFillColor();
        int eFill = end.getFillColor();
        current.setFillColor((int)lerpColor(sFill,eFill,amt));
    }
}

Aside the hacky PShape approach, this works/looks ok only because the number of vertices in the start shape are the same as the ones in the end shape, and the positions don't change by an incredible amount. In conclusion, this is a dirty and inflexible take on this.

Using another library like Geomerative or implementing a cleaner way to modify/updates in a shape generated from parsing an SVG is better, but solves half of the problem. The other half is properly blending. In Illustrator, for example, you can blend from two arbitrary shapes(the number of vertices differ in the two shapes). Before interpolating, the extra vertices will need to be generated when the shapes have different number of vertices and placed properly. There are a few papers out there discussing Shape Blending. I hope this information provides you with the direction you need.

Goodluck!

George Profenza
  • 50,687
  • 19
  • 144
  • 218