JMP has built in support for SVG-like paths that support cubic Bezier lines. Sometimes you need to get the points on the path between data points. This demo also shows how to use MouseTrap to interact with a graph.
// suppose you have a path with a closed-loop piece-wise cubic bezier
// and you want to get interpolated points from the path. Here's JSL...
path = [-7.22402559919283 10.6379612603778 1, // 1: move here
-7.26190136104688 13.4130874416846 0, // 0: slope leaving previous
-6.17119582605485 16.0776798987031 0, // 0: slope entering next
-4.00985906249844 17.407174886248 3, // 3: bezier
-1.84852229894202 18.7366698737928 0,
0.453266763468091 18.1024095275565 0,
2.83933945186436 17.2389336141132 3, // 3: bezier
5.22541214026063 16.3754577006699 0,
10.8233628318786 14.5173640316489 0,
11.2749197361991 10.5574301683737 3, // 3: bezier
11.7264766405196 6.59749630509847 0,
5.87042854142745 -4.16109998756125 0,
0 -3.5625 3, // 3: bezier
-5.87042854142745 -2.96390001243875 0,
-7.18614983733877 7.86283507907095 0,
-7.22402559919283 10.6379612603778 -3]; // -3: bezier, close. The is same as row 1 for a loop.
New Window( "Bézier",
gb = Graph Box( suppressaxes, <<backgroundcolor( "black" ),
framesize( 800, 800 ), X Scale( -15, 15 ), Y Scale( -10, 20 ), // square coordinates
Fill Color( "dark green" ); // the path, drawn next, uses this fill color
Path( path, 1 ); // JMP draws a nice smooth path. The following generates points that match.
Marker Size( 6 ); // so we can see them
For( i = 1, i < N Rows( path ), i += 3, // +=3 does the piece-wise parts
// generate the red points at 1/16, 2/16, ... 15/16 between the blue data points
For( t = 1 / 16, t < 1, t += 1 / 16, // you can also start at zero and end at one for first&last
// p0 isn't used, except to draw an explanatory line.
p0 = path[If( i > 1, i - 1, N Rows( path ) - 1 ), 1 :: 2]; // 1st control point is next to last
// p1 is a data point, as is p4. p2 and p3 are control points.
p1 = path[i + 0, 1 :: 2];
Marker( Color State( "blue" ), {p1[1], p1[2]} ); // blue: original data
p2 = path[i + 1, 1 :: 2];
Marker( Color State( "yellow" ), {p2[1], p2[2]} ); // yellow: control
p3 = path[i + 2, 1 :: 2];
Marker( Color State( "yellow" ), {p3[1], p3[2]} ); // yellow: control
Pen Color( "yellow" );
Line Style( "dotted" );
Line( {p0[1], p0[2]}, {p1[1], p1[2]}, {p2[1], p2[2]} );
p4 = path[i + 3, 1 :: 2];
// from https://javascript.info/bezier-curve ********* nice demo! **********
P = (1 − t) ^ 3 * P1 + 3 * (1 − t) ^ 2 * t * P2 + 3 * (1 − t) * t ^ 2 * P3 + t ^ 3 * P4;
Marker( Color State( "red" ), {p[1], p[2]} ); // red: generated
)
);
Text Color( "white" );
Text( {-14, -7}, "There are 15 red points generated between blue points." );
Text( {-14, -8}, "The yellow control points create the smooth shape." );
Text( {-14, -9}, "Drag the yellow and blue points to get a different shape." );
Mousetrap( // simple-minded point dragging; doing this better means picking the closest, not first match;
For( i = 1, i <= N Rows( path ), i += 1, // you'll see it jump to a nearby point earlier in the path.
If( Sqrt( (x - path[i, 1]) ^ 2 + (y - path[i, 2]) ^ 2 ) < 1, //x,y are the magic mouse trap variables
path[i, 1] = x;
path[i, 2] = y;
If( i == 1, // handle repeated data point
i = N Rows( path );
path[i, 1] = x;
path[i, 2] = y;
);
Break(); // took the first match, might not be the best match
)
)
);
)
);
5 original blue data points, 10 yellow control points, 15 red generated points between data points.
It is only interactive if you run it. See the web site mentioned in the JSL for visual explanation of Bezier.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.