-
Notifications
You must be signed in to change notification settings - Fork 0
Home
SDCurve.js is a library built to make subdivision curves easy to create, interact with, and use in visualizations.
Download sdcurve.js from the Github repository and include it in your HTML or project file: https://github.com/rpusch/sdcurve.js/
An SDCurve must be initialized with an array of control points that will define the curve. Each point is a map with x
and y
fields.
var pts = [{x:0,y:0}, {x:200,y:200}];
To create a SDCurve, call the new SDCurve
constructor and provide a map as the argument. At minimum, the points
field must be filled.
var myCurve = new SDCurve({
points: pts
});
You can optionally specify more information about the curve by providing more fields in this map. The following fields are accepted. If these fields are not provided, they will be given a default value.
-
type
(string): The curve scheme used, which determines the final shape of the curve. Accepted values are"bspline"
,"dyn-levin"
, and"catmull-rom"
. Dyn-Levin and Catmull-Rom are both schemes that interpolate the control points, while B-spline curves create a smooth blend of the control points. (default:"bspline"
) -
resolution
(integer): The number of subdivision steps. Not very many subdivision steps are needed to produce a visually pleasing curve. For curves with large numbers of control points, you may want to set this number lower. Setting the resolution to 0 will give a final "curve" that is the same as the control points. (default: 5) -
open
(boolean): If true, the curve will interpolate the endpoints. If false, the curve will be a closed loop. (default: true) -
degree
(integer): If"bspline"
is the curve type, this determines the degree of the B-spline curve. Has no effect for any other curve type. Higher degrees produce a curve that is a stronger blend of the control points. (default: 2, i.e., quadratic) -
catmullTension
(float): If"catmull-rom"
is the curve type, this determines the tension parameter for the Catmull-Rom creation process. Set the value to 0 for a uniform spline, 0.5 for a centripetal spline, or 1 for a chordal spline. Changes the shape of the curve, especially if control points are close together. Has no effect for any other curve type. (default: 0.5)
To change these properties on an existing curve, or to query the value of these properties, the following functions exist: resolution()
, type()
, open()
, degree()
, catmullTension()
. These functions accept an argument which will change the value of the curve's property and recompute the curve as needed, and will also return the current value.
To access the curve points, call the curve()
function on an existing curve object. curve()
returns an array of maps, which contain the curve points themselves (under the point
field for each array object) as well as information on how each curve point was calculated (under the weights
field). Most of the time, you will only need to use the point
field.
To draw the curve using D3, include the d3.js library and then simply use D3's built-in line()
function in this way:
var lineFunction = d3.svg.line()
.x(function(d) { return d.point.x; })
.y(function(d) { return d.point.y; });
Then, the curve can be drawn to any SVG element by specifying lineFunction(myCurve.curve())
to the d
attribute of a path.
Here are some example curves showing the various curve types.
Left: Two B-Spline curves (blue: degree 2, green: degree 5). Right: Two interpolating curves (red: Catmull-Rom, light blue: Dyn-Levin).
To get a point some distance along the curve, call the pointAt(u)
function, where 0 <= u
<= 1 (u=0 is the start of the curve and u=1 is the end of the curve). The distance is relative to the arclength of the curve.
To get the closest point on the curve to any point in space, call the getClosestPoint(pt)
function. The parameter is a map with x
and y
fields, just like when creating a curve. This function is most useful, for example, to snap the location of a mouse click directly on the curve.
Both the pointAt
and getClosestPoint
functions return an object with the following fields:
-
pointOnCurve
: The actual point, represented by a map ofx
andy
values, that lies on the curve. -
u
: The distance along the curve, between 0 and 1, thatpointOnCurve
lies. -
index
: The index of the point in the "curve()" array that is closest topointOnCurve
(but still before it). -
weights
: HowpointOnCurve
was calculated from the array of control points provided. Returns a map of indices into the points() array mapped to the weight coefficient. All coefficients will sum to 1.
For most applications, you will only be interested in the pointOnCurve
attribute.
To reset the control points to a new set of points, call the points(ptArray)
function and pass in a new array of control points. The old points will be overwritten and the new curve will be created.
To adjust only some of the control points, call the adjustPoints(map)
function. The parameter is a map between control point indices and new positions. Only the parts of the curve that are affected by the changed control points will be updated, which improves performance. Here is an example of adjustPoints
:
// move the control point at index 1 and index 3 to new positions
var newPts = {1: {x:30, y:40}, 3: {x:100, 200}};
myCurve.adjustPoints(newPts);
Both the above methods require understanding of the underlying control points to change the curve. In SDCurve.js, it is possible to move the curve without any knowledge of the underlying control point structure (for example, to move the curve from a user's mouse click to a new position).
To do this, call the moveCurve(pointToMove, delta, width)
function:
-
pointToMove
: The object returned from either thepointAt
orgetClosestPoint
function, outlined above. This will let you move any point along the curve (measured by arclength), or the closest point on the curve from any point in space, such as a mouse click. -
delta
: The movement vector. Note that this is not the desired point's final position, but rather a vector for how much it should move. -
width
(optional): How many of the underlying control points will be allowed to move. This is the "sharpness" or "smoothness" factor, and depends on the curve scheme used. Typical values will be between 1 and 3. (default: 1)
Here are some examples of how to use moveCurve
.
// move the halfway point of the curve down 20 pixels
var pt = myCurve.pointAt(0.5);
myCurve.moveCurve(pt, {x:0,y:20});
// drag the curve around based on the user's mouse click
var clickPt, mouseDelta; // assume these variables are saved from the mouse events
var pt = myCurve.getClosestPoint(clickPt);
myCurve.moveCurve(pt, mouseDelta);
moveCurve
also returns a point map that describes which points were moved and to where. You can use this map to change other curves, via adjustPoints
, that share the same control point structure as the curve that was moved.
- We recommend you use the B-spline and Catmull-Rom curve types for smooth or interpolating curves.
- Moving a Catmull-Rom curve requires a bit more recalculation than moving either the B-spline or Dyn-Levin control types, but the curve should still be responsive on most browsers and platforms. If the Dyn-Levin scheme produces curves pleasing to your visualization, and performance is sluggish with Catmull-Rom, try using the Dyn-Levin scheme instead.
- If you have many control points that are close together, and you are frequently moving the curve, you may try to reduce the resolution of your curve from the default 5 to keep performance crisp.
- SDCurve can be used to resample a series of points (for example, dense points from drawing a curve via mouse input into a series of usable control points). Create a temporary curve object with resolution 0, and then use the
pointAt
function to sample equally spaced points between 0 and 1.
An example of this is below:
var denseMouseInput; // an array of many points from a user drawing a stroke on the screen
var numSamples = 10; // change denseMouseInput to be a "similar" curve using only this many points
var tempCurve = new SDCurve({ points: denseMouseInput, resolution: 0});
var newPoints = [];
for(var i=0; i<numSamples; i++)
newPoints.push(tempCurve.pointAt( i / (numSamples-1) ).pointOnCurve);
var myCurve = new SDCurve({points: newPoints}); // the new curve with only 10 control points