import java.awt.*;
|
import java.util.Vector;
|
|
class Spline extends Object3D implements java.io.Serializable
|
{
|
|
Spline()
|
{
|
type = 1;
|
ctrlPnts = new Vector(0,0);
|
ctrlPnts.addElement(LA.newVector(1, -1, 0));
|
ctrlPnts.addElement(LA.newVector(1, 1, 0));
|
}
|
|
Object3D deepCopy()
|
{
|
Spline spline = new Spline();
|
deepCopySelf(spline);
|
return spline;
|
}
|
|
protected void deepCopyNode(Object3D other)
|
{
|
super.deepCopyNode(other);
|
Spline spline = (Spline)other;
|
spline.type = type;
|
int count = ctrlPnts.size();
|
spline.ctrlPnts = new Vector(0,0);
|
for (int i=0; i < count; i++)
|
{
|
cVector p = (cVector)ctrlPnts.elementAt(i);
|
cVector q = new cVector();
|
LA.vecCopy(p, q);
|
spline.ctrlPnts.addElement(q);
|
}
|
|
}
|
|
void generatePOV(StringBuffer buffer)
|
{
|
int nPoints = ctrlPnts.size();
|
int degree = type;
|
if (degree > nPoints - 1)
|
degree = nPoints - 1;
|
generateIndent(buffer);
|
buffer.append(" ");
|
switch (degree)
|
{
|
case 1: // '\001'
|
buffer.append("linear_spline ");
|
break;
|
|
case 2: // '\002'
|
buffer.append("quadratic_spline ");
|
break;
|
|
case 3: // '\003'
|
buffer.append("cubic_spline ");
|
break;
|
}
|
buffer.append(nPoints);
|
buffer.append("\n");
|
for (int i=0; i < nPoints; i++)
|
{
|
cVector p = (cVector)ctrlPnts.elementAt(i);
|
generateIndent(buffer);
|
buffer.append(" <");
|
buffer.append(p.x);
|
buffer.append(',');
|
buffer.append(p.y);
|
buffer.append('>');
|
if (i < nPoints - 1)
|
buffer.append(',');
|
buffer.append('\n');
|
}
|
|
}
|
|
void getCoeffs(int seg, int degree, double a[], double b[], double c[], double d[])
|
{
|
cVector p2 = null;
|
cVector p3 = null;
|
cVector p0 = (cVector)ctrlPnts.elementAt(seg);
|
cVector p1 = (cVector)ctrlPnts.elementAt(seg + 1);
|
if (degree == 2 || degree == 3)
|
p2 = (cVector)ctrlPnts.elementAt(seg + 2);
|
if (degree == 3)
|
p3 = (cVector)ctrlPnts.elementAt(seg + 3);
|
//a[2] = b[2] = c[2] = d[2] = 0;
|
for (int i=0; i < 2; i++)
|
switch (degree)
|
{
|
case 1: // '\001'
|
a[i] = 0;
|
b[i] = 0;
|
c[i] = -1 * p0.get(i) + 1 * p1.get(i);
|
d[i] = 1 * p0.get(i);
|
break;
|
|
case 2: // '\002'
|
a[i] = 0;
|
b[i] = (0.5 * p0.get(i) - 1 * p1.get(i)) + 0.5 * p2.get(i);
|
c[i] = -0.5 * p0.get(i) + 0.5 * p2.get(i);
|
d[i] = 1 * p1.get(i);
|
break;
|
|
case 3: // '\003'
|
a[i] = ((-0.5 * p0.get(i) + 1.5 * p1.get(i)) - 1.5 * p2.get(i)) + 0.5 * p3.get(i);
|
b[i] = ((1 * p0.get(i) - 2.5 * p1.get(i)) + 2 * p2.get(i)) - 0.5 * p3.get(i);
|
c[i] = -0.5 * p0.get(i) + 0.5 * p2.get(i);
|
d[i] = 1 * p1.get(i);
|
break;
|
}
|
|
}
|
|
void solve(double t, double a[], double b[], double c[], double d[], cVector output)
|
{
|
output.x = a[0] * t * t * t + b[0] * t * t + c[0] * t + d[0];
|
output.y = a[1] * t * t * t + b[1] * t * t + c[1] * t + d[1];
|
output.z = 0;
|
}
|
|
Vertex paramFunction(double t)
|
{
|
int nPoints = ctrlPnts.size();
|
int degree = type;
|
if (degree > nPoints - 1)
|
degree = nPoints - 1;
|
int nSegs = nPoints - degree;
|
double interval = 1 / (double)nSegs;
|
int seg = (int)(t / interval);
|
if (seg >= nSegs)
|
seg = nSegs - 1;
|
double base = (double)seg * interval;
|
t = (t - base) / interval;
|
getCoeffs(seg, degree, a, b, c, d);
|
Vertex temp = new Vertex();
|
//temp.pos = new cVector();
|
solve(t, a, b, c, d, temp/*.pos*/);
|
return temp;
|
}
|
|
void draw(ClickInfo info, int level, boolean select)
|
{
|
cVector sample = new cVector();
|
info.g.setColor(Color.black);
|
int nPoints = ctrlPnts.size();
|
int degree = type;
|
if (degree > nPoints - 1)
|
degree = nPoints - 1;
|
int nSegs = nPoints - degree;
|
for (int i=0; i < nSegs; i++)
|
{
|
getCoeffs(i, degree, a, b, c, d);
|
solve(0, a, b, c, d, sample);
|
Point prev = new Point(0, 0);
|
Point curr = new Point(0, 0);
|
Rectangle dummy = new Rectangle();
|
calcHotSpot(sample, //info,
|
prev, dummy);
|
for (double t = 0.1; t < 1.01; t += 0.1)
|
{
|
solve(t, a, b, c, d, sample);
|
calcHotSpot(sample, //info,
|
curr, dummy);
|
info.g.drawLine(prev.x, prev.y, curr.x, curr.y);
|
info.g.drawLine(prev.x, prev.y + 1, curr.x, curr.y + 1);
|
info.g.drawLine(prev.x + 1, prev.y, curr.x + 1, curr.y);
|
info.g.drawLine(prev.x + 1, prev.y + 1, curr.x + 1, curr.y + 1);
|
prev.x = curr.x;
|
prev.y = curr.y;
|
}
|
|
}
|
|
}
|
|
void drawEditHandles(ClickInfo info, int level)
|
{
|
info.g.setColor(Color.red);
|
int count = ctrlPnts.size();
|
for (int i=0; i < count; i++)
|
{
|
cVector p = (cVector)ctrlPnts.elementAt(i);
|
Rectangle spot = calcHotSpot(p); //, info);
|
info.g.fillRect(spot.x, spot.y, spot.width, spot.height);
|
}
|
|
}
|
|
boolean doEditClick(ClickInfo info, int level)
|
{
|
boolean modified = (info.modifiers & CameraPane.SHIFT) != 0; // Was META
|
startX = info.x;
|
startY = info.y;
|
int nPoints = ctrlPnts.size();
|
hitSomething = false;
|
for (int i=0; i < nPoints; i++)
|
{
|
cVector p = (cVector)ctrlPnts.elementAt(i);
|
Rectangle r = calcHotSpot(p); //, info);
|
if (r.contains(info.x, info.y))
|
{
|
hitSomething = true;
|
hitIndex = i;
|
LA.vecCopy(p, startVec);
|
}
|
}
|
|
if (hitSomething)
|
if (modified)
|
{
|
if (nPoints > 2)
|
ctrlPnts.removeElementAt(hitIndex);
|
return false;
|
} else
|
{
|
return true;
|
}
|
if (!modified)
|
return false;
|
int degree = type;
|
if (degree > nPoints - 1)
|
degree = nPoints - 1;
|
int nSegs = nPoints - degree;
|
if (degree == 1)
|
hitIndex = 1;
|
else
|
hitIndex = 2;
|
cVector sample = new cVector();
|
Point pnt = new Point(0, 0);
|
Rectangle rect = new Rectangle();
|
for (int i=0; i < nSegs && !hitSomething;)
|
{
|
getCoeffs(i, degree, a, b, c, d);
|
for (double t = 0; t < 1.001; t += 0.01)
|
{
|
solve(t, a, b, c, d, sample);
|
calcHotSpot(sample, //info,
|
pnt, rect);
|
if (!rect.contains(info.x, info.y))
|
continue;
|
hitSomething = true;
|
for (int j = 0; j < 3; j++)
|
sample.set(j, (int)sample.get(j));
|
|
ctrlPnts.insertElementAt(sample, hitIndex);
|
LA.vecCopy(sample, startVec);
|
info.pane.repaint();
|
break;
|
}
|
|
i++;
|
hitIndex++;
|
}
|
|
hitIndex--;
|
return hitSomething;
|
}
|
|
void doEditDrag(ClickInfo info)
|
{
|
if (!hitSomething)
|
return;
|
cVector delta = LA.newVector(info.x - startX, startY - info.y, 0);
|
LA.xformDir(delta, info.camera.fromScreen, delta);
|
delta.x /= 100 * info.camera.SCALE / info.camera.Distance();
|
delta.y /= 100 * info.camera.SCALE / info.camera.Distance();
|
delta.z /= 100 * info.camera.SCALE / info.camera.Distance();
|
cVector p = (cVector)ctrlPnts.elementAt(hitIndex);
|
LA.vecCopy(startVec, p);
|
LA.vecAdd(p, delta, p);
|
if (p.x < 0)
|
p.x = 0;
|
info.pane.repaint();
|
}
|
|
void getBounds(cVector minima, cVector maxima, boolean xform)
|
{
|
minima.z = maxima.z = 0;
|
minima.x = minima.y = 1E+20;
|
maxima.x = maxima.y = -1E+20;
|
int nPoints = ctrlPnts.size();
|
for (int i=0; i < nPoints; i++)
|
{
|
cVector p = (cVector)ctrlPnts.elementAt(i);
|
for (int j = 0; j < 2; j++)
|
{
|
if (p.get(j) > maxima.get(j))
|
maxima.set(j, p.get(j));
|
if (p.get(j) < minima.get(j))
|
minima.set(j, p.get(j));
|
}
|
|
}
|
|
}
|
|
int intersectHorizontal(double x, double y)
|
{
|
int nPoints = ctrlPnts.size();
|
int degree = type;
|
if (degree > nPoints - 1)
|
degree = nPoints - 1;
|
int nSegs = nPoints - degree;
|
int count = 0;
|
double domain[] = {
|
0, 1
|
};
|
for (int i=0; i < nSegs; i++)
|
{
|
getCoeffs(i, degree, a, b, c, d);
|
count += intersectOne(x, y, domain);
|
}
|
|
return count;
|
}
|
|
int intersectOne(double x, double y, double domain[])
|
{
|
Interval.boxCube(domain, io1);
|
Interval.boxScale(io1, a[1]);
|
Interval.boxSquare(domain, io2);
|
Interval.boxScale(io2, b[1]);
|
Interval.boxAdd(io1, io2, io1);
|
io2[0] = domain[0];
|
io2[1] = domain[1];
|
Interval.boxScale(io2, c[1]);
|
Interval.boxAdd(io1, io2, io1);
|
io1[0] += d[1];
|
io1[1] += d[1];
|
if (io1[0] > y || io1[1] < y)
|
return 0;
|
double t = (domain[0] + domain[1]) / 2;
|
double x2 = a[0] * t * t * t + b[0] * t * t + c[0] * t + d[0];
|
if (domain[1] - domain[0] < 0.0625)
|
{
|
return x2 <= x ? 0 : 1;
|
} else
|
{
|
double d1[] = new double[2];
|
double d2[] = new double[2];
|
d1[0] = domain[0];
|
d2[1] = domain[1];
|
d1[1] = d2[0] = t;
|
return intersectOne(x, y, d1) + intersectOne(x, y, d2);
|
}
|
}
|
|
public static final int LINEAR = 1;
|
public static final int QUADRATIC = 2;
|
public static final int CUBIC = 3;
|
int type;
|
Vector ctrlPnts;
|
private static double a[] = new double[2];
|
private static double b[] = new double[2];
|
private static double c[] = new double[2];
|
private static double d[] = new double[2];
|
private static int startX;
|
private static int startY;
|
private static boolean hitSomething;
|
private static int hitIndex;
|
private static cVector startVec = new cVector();
|
private static double io1[] = new double[2];
|
private static double io2[] = new double[2];
|
|
}
|