Bezier Curve Length in Processing
Measuring the length of curvy, bendy shapes like Bezier curves is notoriously hard in Processing (and most other languages!).
Processing gives us some helpful tools like the bezierPoint() function, but that works based on the curve’s parameterization of 0-1, not the actual length. There has been some talk of adding functions to the API, and for now there’s a good curve parameterization example under Topics/Curves in Processing.
Here’s a quick-and-dirty set of functions that work together to measure a Bezier curve in Processing, within a certain tolerance. Please see the comments for a more in-depth explanation of how it works, and where I got it from!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
void setup(){ size(800, 800); noFill(); //make a Bezier curve //do it inside a PShape object so we can reference it later PShape bez = createShape(); bez.beginShape(); bez.vertex(85, 20); bez.bezierVertex(10, 10, 90, 90, 15, 80); bez.endShape(); //draw the bezier curve shape(bez); //print the length println(bezLength( bez, 0.00001)); } /* the three functions below work together to figure out the length of a bezier curve within a certain tolerance the gist is: 1. given a Bezier curve and an error tolerance, 2. get the difference bewteen straight-line distance between endpoints and the sum of distances between segments 3. if the difference is within tolerance, return it as the Bezier's length 4. if the difference is greater than the tolerance, split the Bezier in half and repeat 2-4 recursively based on examples published at via http://steve.hollasch.net/cgindex/curves/cbezarclen.html, with some adaptations for Java/Processing's lack of pointers. */ double bezLength(PShape bez, float error){ double[] myBezLength = new double[1]; //we want this to get passed as a reference so we put it in an array :) PVector[] bezPts = new PVector[bez.getVertexCount()]; for( int i=0; i<bez.getVertexCount(); i++){ bezPts[i] = new PVector( bez.getVertex(i).x, bez.getVertex(i).y ); } addIfClose( bezPts, myBezLength, error ); return myBezLength[0]; } void bezSplit(PVector[] v, PVector[] left, PVector[] right){ ArrayList<PVector[]> vTemp = new ArrayList<PVector[]>(); for( int g=0; g<4; g++){ vTemp.add(new PVector[4]); for(int h=0; h<4; h++){ vTemp.get(g)[h] = new PVector(0, 0); } } //copy control points PVector[] ctrlPts = new PVector[4]; for( int i=0; i<=3; i++ ){ ctrlPts[i] = new PVector(v[i].x, v[i].y); } vTemp.set(0, ctrlPts ); /* Triangle computation */ for (int j = 1; j <= 3; j++) { for (int k =0 ; k <= 3 - j; k++) { PVector tri = new PVector( 0.5 * vTemp.get(j-1)[k].x + 0.5 * vTemp.get(j-1)[k+1].x, 0.5 * vTemp.get(j-1)[k].y + 0.5 * vTemp.get(j-1)[k+1].y); vTemp.get(j)[k] = tri; } } for (int l = 0; l <= 3; l++){ left[l] = vTemp.get(l)[0]; } for (int m = 0; m <= 3; m++){ right[m] = vTemp.get(3-m)[m]; } } void addIfClose( PVector[] v, double[] myBezLength, float error){ PVector[] left = new PVector[4], right = new PVector[4]; double len=0, chord=0; for (int i=0; i<=2; i++ ){ len += dist(v[i].x, v[i].y, v[i+1].x, v[i+1].y); } chord = dist(v[0].x, v[0].y, v[3].x, v[3].y); if( len-chord > error ){ bezSplit(v, left, right); addIfClose(left, myBezLength, error); addIfClose(right, myBezLength, error); return; } myBezLength[0] += len; } |