//
//
//
/**
Demonstrates the support for splines in DirectAnimation, including:
- the correspondence between a path and behavior cubic splines,
- the use of draggable points for making the spline interactive
- the use of the derivative operation for obtaining the spline tangent
- animating a sprite along the spline with the orientation of the tangent
**/
//
//
import com.ms.dxmedia.*; // All direct Animation classes
import java.net.*; // the URL class
import java.awt.*; // the Dimension class
import java_utility.*; // the DraggableImage class
// Splines is an applet that invokes SplinesModel to construct an
// interactive spline with the specified number of polynomial pieces
public class Splines extends DXMApplet {
public void init() {
super.init() ;
setModel (new SplinesModel(this, 4));
}
}
//
class SplinesModel extends Model {
SplinesModel(DXMApplet dxma, int numPolys) {
_numPolys = numPolys;
// Get the size of the viewport.
Dimension dim = dxma.getSize();
// The base unit of measure for DirectX Media is the meter.
// Convert the size into meters by multiplying it with the pixelBvr.
_halfWidth = mul(toBvr(dim.width*0.5),pixelBvr);
_halfHeight = mul(toBvr(dim.height*0.5),pixelBvr);
}
//
public void createModel(BvrsToRun listBvrs) {
//
// build a URL to import images
URL imgBase = buildURL(getImportBase(),"image/");
//
//
//
// Construct a cubic BSpline with initial control points that are
// colinear and equally spaced. The points are draggable for manipulating
// the shape of the spline.
// The spline is a uniform BSpline with standard end conditions,
// where end points are interpolated.
// The only parameter that is needed is the
// number of polynomial pieces that are desired in the spline.
// number of control points is related to the number of polynomials
int numPts = _numPolys + 3; // number of control points >= 4
// vertically center initial control points
double initY = 0;
NumberBvr initX = mul(neg(_halfWidth),toBvr(0.7)); // 15% from left side
NumberBvr incX = div(mul(_halfWidth,toBvr(1.4)),toBvr((numPts - 1))); // use 70% of range
// for the control points of the spline
Point2Bvr[] ptsPt2 = new Point2Bvr[numPts];
// 2 more knots than control points since a cubic
NumberBvr[] knotsNum = new NumberBvr[numPts + 2];
Point2Bvr initPt2; // initial position of a draggable
DraggableButton draggable; // holder of a draggable point
ImageBvr ptsImg = emptyImage; // the accumulated image
// construct the control points and the knot vector
for (int i = 0; i < numPts; i++) {
initPt2 = point2(add(initX,mul(toBvr(i),incX)), toBvr(initY));
draggable = new DraggableButton(initPt2);
ptsPt2[i] = draggable.getPointBvr();
ptsImg = overlay(ptsImg, draggable.getImageBvr());
knotsNum[i] = toBvr(i-2); // we want the first 3 knots to be 0, see next
}
// Set first and last two knots to achieve multiplicity 3,
// this makes the spline pass through the first and last control points.
knotsNum[0] = knotsNum[1] = toBvr(0);
knotsNum[numPts] = knotsNum[numPts + 1] = knotsNum[numPts - 1];
// generate images of the spline and its control polygon
Path2Bvr cubicCurvePath = cubicBSplinePath(ptsPt2, knotsNum);
Path2Bvr controlPolyPath = polyline(ptsPt2);
LineStyleBvr lLnS = defaultLineStyle.width(toBvr(0.5*mm));
ImageBvr cubicCurveImg = cubicCurvePath.draw(lLnS.color(green));
ImageBvr controlPolyImg = controlPolyPath.draw(lLnS.color(white));
//
//
// Create an image behavior of a car by importing a bitmap.
ImageBvr car1Img = importImage(buildURL(imgBase, "car3.gif"));
// Car2 is the mirror image of car1 along Y;
// use this later to keep the sprite upright.
ImageBvr car2Img = car1Img.transform(scale(1,-1));
// Get an image moving back and forth along the spline,
// the spline is well defined between the third knot from the begining
// (= 0) and the third knot from the end since it is a cubic.
NumberBvr limitNum = knotsNum[numPts - 1]; // third knot from end
// Evaluator spans the parameter space of the spline and then
// returns to the beginning. It then repeats.
NumberBvr evaluatorNum = NumberBvr.newUninitBvr();
evaluatorNum.init(until(localTime,
timer(limitNum),
until(sub(limitNum, localTime),
timer(limitNum),
evaluatorNum)));
// Construct a point2 behavior that goes back and forth along the spline.
Point2Bvr splPt2 =
bSpline(DEGREE, knotsNum, ptsPt2, null, evaluatorNum);
// Speed is the speed of traversal of the spline;
// it switches among three values upon rightButtonDown.
NumberBvr speedNum = NumberBvr.newUninitBvr();
speedNum.init ( until(toBvr(0.6),
rightButtonDown,
until(toBvr(1.2),
rightButtonDown,
until(toBvr(1.8),
rightButtonDown,
speedNum))));
// Construct a new point2 behavior that goes back and forth at
// the rate of "speed" along the original spline.
// Integrate the speed and invoke substituteTime.
// The runOnce construct is necessary so that the integration does not get
// restarted everytime the rightButtonDown event occurs.
splPt2 = (Point2Bvr)splPt2.substituteTime(integral((NumberBvr)speedNum.runOnce()));
// Orient the car based on the spline.
// The tangent to the spline is its derivative.
// Notice that the derivative of a point behavior is a vector behavior.
Vector2Bvr tangentVec2 = derivative(splPt2);
// Extract the angle between the tangent and the X axis;
// it is in [-PI, +PI] range.
NumberBvr angleNum = tangentVec2.getPolarCoordAngle();
// The quadrant in which the tangent lies is in [0, 1, -2, -1] CCW
NumberBvr quadrentNum = floor(div(angleNum, toBvr(Math.PI/2)));
// carImg is always upright as the traversal of the spline takes place
ImageBvr carImg = (ImageBvr)cond(or(eq(quadrentNum, toBvr(0)),
eq(quadrentNum, toBvr(-1))),
car1Img,
car2Img);
// construct a car that travels along the spline always upright
ImageBvr movingCarImg = carImg.transform(compose(translate(sub(splPt2, origin2)),
rotate(angleNum)));
// Finally, combine all these parts into the final model.
setImage(overlay(movingCarImg,
overlay(cubicCurveImg,
overlay(ptsImg,
overlay(controlPolyImg, solidColorImage(blue))))));
}
private static int DEGREE = 3; // only cubic pathes are supported
private int _numPolys; // number of polynomials in the spline
//
//
private NumberBvr _halfWidth; // half width of viewport
private NumberBvr _halfHeight; // half height of viewport
//
}
//
//
// Here, use the draggable class from the utility library.
// Construct a draggable based on a small (4 mm) cube with a reactive color
// the color is green while dragged and red otherwise. Provide accessors to get
// back the image and the position point of the draggable.
//
class DraggableButton extends Statics {
public DraggableButton(Point2Bvr initPt2) {
NumberBvr halfDimNum = toBvr(2*mm);
ColorBvr cClr = ColorBvr.newUninitBvr();
ImageBvr blockImg = solidColorImage(cClr).
crop(point2(neg(halfDimNum), neg(halfDimNum)), point2(halfDimNum, halfDimNum));
_dragImg = new DraggableImage(blockImg, initPt2);
cClr.init(until(red, _dragImg.getGrabEvent(),
until(green, _dragImg.getReleaseEvent(), cClr)));
}
public ImageBvr getImageBvr() { return _dragImg.getImageBvr(); }
public Point2Bvr getPointBvr() { return _dragImg.getPointBvr(); }
DraggableImage _dragImg;
}
//