Permalink
Browse files

ffs

  • Loading branch information...
1 parent 10fb2ba commit 90feba4f0e20e40cbabda744872b6f3a23edbcb5 @Pomax committed Jun 24, 2012
View
1 Boundary.pde
@@ -194,4 +194,3 @@ class Boundary extends Positionable {
*/
String toString() { return x+","+y+","+xw+","+yh; }
}
-
View
252 CollisionDetection.pde
@@ -137,156 +137,159 @@ static class CollisionDetection {
/**
- * Line-through-box intersection algorithm.
- *
- * There are a few possibilities, with B() meaning
- * "on blocking side", I() meaning "intersecting"
- * and P() meaning "on pass-through side":
- *
- * |
- * (1) [] [] <| = B(prev), B(current)
- * |
- *
- * |
- * (2) [] [<|] = B(prev), B(current) and I(current) and P(current)
- * |
- *
- * |
- * (3) [] <| [] = B(prev), P(current)
- * |
- *
- * |
- * (4) [<|] [] = B(prev) and I(prev) and P(prev), P(current)
- * |
+ * generate the dot products for each of the current
+ * and previous corner points, relative to (ox,oy).
*
- *
- * |
- * (5) <| [] [] = P(prev), P(current)
- * |
- *
- * By computing the dot product for the eight corners of the two bounding
- * boxes, we can determine which of these situations we are in. Only in
- * (2) and (3) should we signal an intersection occuring. While (4) might
- * look like another case in which this should happen, we assume that
- * because the "previous" state is boundary overlapping, it was permitted
- * earlier (for instance, it moved through the boundary in the allowed
- * direction, but then moved back before having fully passed the boundary).
- *
- * DOCUMENTATION FIX: we can actually treat 2 and 3 as the same case, with
- * a more reliable result, by always constructing a
- * new intersection-validating parallelogram from the
- * "hot corner" and an adjacent corner in {previous} and
- * {current}.
- *
- *
- * @return null if no intersect, otherwise float[3]
- * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ * note: we want dot products with the vector that is
+ * perpendicular to the boundary, so that we have
+ * a clear "on blocked side" (positive) and "on
+ * pass-through side" (negative) value classifier.
*/
- static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
- {
- int B_current = 0, B_previous = 0, B_sum = 0;
+ private static int[] getDotProductsPerCorner(float[] line, float[] current, float[] current_dots, float[] previous, float[] previous_dots, int[] corners) {
+ int B_current = 0,
+ B_previous = 0,
+ B_sum = 0;
+
float ox=line[0], oy=line[1], tx=line[2], ty=line[3],
dotx = tx-ox, doty = ty-oy,
- otlen = sqrt(dotx*dotx + doty*doty),
- x, y, dx, dy, len, dotproduct;
+ otlen = sqrt(dotx*dotx + doty*doty);
- // normalised boundary vector
+ // normalise the boundary vector
dotx /= otlen;
doty /= otlen;
- // note: we want the dot product with the
- // vector that is perpendicular to the boundary!
float p = PI/2, cosp = cos(p), sinp = sin(p),
pdx = dotx*cosp - doty*sinp,
- pdy = dotx*sinp + doty*cosp;
+ pdy = dotx*sinp + doty*cosp,
+ dx, dy, len, dotproduct,
+ dotvalue = 999999, curval;
+
+ float corner1v=3, corner2v=3, corner3v=3, corner4v=3;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
- // how many corners are blocked in [current]?
+ // FIXME: I'm sure this can be optimised
for(int i=0; i<8; i+=2) {
- x = current[i];
- y = current[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'current'
+ dx = current[i]-ox;
+ dy = current[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_current++; }
current_dots[i] = dotproduct;
- }
- // how many corners are blocked in [previous]? (copied for speed)
- for(int i=0; i<8; i+=2) {
- x = previous[i];
- y = previous[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'previous'
+ dx = previous[i]-ox;
+ dy = previous[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_previous++; }
previous_dots[i] = dotproduct;
+
+ // this is silly, but relatively fast
+ if(dotproduct<corner1v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = corner1v; corner1v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = corner1; corner1 = i; }
+ else if(dotproduct<corner2v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = i; }
+ else if(dotproduct<corner3v) {
+ corner4v = corner3v; corner3v = dotproduct;
+ corner4 = corner3; corner3 = i; }
+ else {
+ corner4v = dotproduct;
+ corner4 = i; }
}
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
+
+ // return "on blocking side" counts for both boxes
+ return new int[]{B_current, B_previous, B_current+B_previous};
+ }
- // cases (1) or (5)?
- B_sum = B_current + B_previous;
- if (B_sum == 0 || B_sum == 8) {
-// println("case " + (B_sum==8? "1" : "5"));
- return null;
- }
- // case (4)?
- if (B_previous < 4 && B_current == 0) {
-// println("case 4");
- return null;
- }
+ /**
+ * Line-through-box intersection algorithm.
+ *
+ * There are a few possibilities, illustrated
+ * in the CollisionDetection.psd and
+ * CollisionDetection2.psd files included with
+ * this source code.
+ *
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ */
+ static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
+ {
+ float ox=line[0], oy=line[1], tx=line[2], ty=line[3];
+
+ // get the dot products per corner... just like the method says
+ int[] corners = {0,0,0,0},
+ B_counts = getDotProductsPerCorner(line, current, current_dots, previous, previous_dots, corners);
+
+ int B_current = B_counts[0],
+ B_previous = B_counts[1],
+ B_sum = B_counts[2];
- // Before we continue, find the point in [previous]
- // that is closest to the boundary, since that'll
- // cross the boundary first (based on its dot product
- // value.)
- int corner=-1, curcorner;
- float dotvalue = 999999, curval;
- for(curcorner=0; curcorner<8; curcorner+=2) {
- curval = abs(previous_dots[curcorner]);
- if(curval<dotvalue) {
- corner = curcorner;
- dotvalue = curval; }}
-
- // Right then. Case (2) or (3)?
+ // cases (1) or (6)? no collision.
+ if (B_sum == 0 || B_sum == 8) { return null; }
+
+ // case (4) or (5)? no collision, by convention.
+ if (B_previous < 4) { return null; }
+
+ // what remains is case (2) or (3)
if (B_previous == 4) {
- int currentCase = 2;
- // for (2) and (3) the approach is essentially
- // the same, except that we need different bounding
- // boxes to determine the intersection. For (2) we
- // can use the [current] bounding box, but for (3)
- // we need to construct a new box that is spanned by
- // the two points next to the "hot" corner in both
- // [previous] and [current].
- //
- // FIXME: that said, if we treat (2) as a subset
- // of (3), things seem to work better.
- checkLines = new float[8];
- arrayCopy(current,0,checkLines,0,8);
-
- currentCase = 3;
-
- // two of these edges are guaranteed to not have intersections,
- // since otherwise the intersection would be inside either
- // [previous] or [current], which is case (2) instead.
- checkLines = new float[]{previous[(corner)%8], previous[(corner+1)%8],
+
+ // first, we need to find out which corner point actually
+ // intersects our boundary. Typically this will be the
+ // first ranked corner, but not always.
+ float x1, y1, x2, y2;
+ int corner = -1;
+ float[] intersection;
+ for(int i=0, last=corners.length; i<last; i++) {
+ corner = corners[i];
+ x1 = previous[corner];
+ y1 = previous[corner+1];
+ x2 = current[corner];
+ y2 = current[corner+1];
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ if(intersection!=null) break;
+ }
+
+ // get the guaranteed hit-line
+ float px = previous[corner],
+ py = previous[corner+1],
+ cx = current[corner],
+ cy = current[corner+1];
+
+ // We now have the corner that's going to pass through
+ // the boundary first. we construct a new bbox to
+ // perform poly-through-line detection.
+ checkLines = new float[]{px, py,
previous[(corner+2)%8], previous[(corner+3)%8],
current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
+ cx, cy};
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
intersections.clear();
- float x1=checkLines[0], y1=checkLines[1], x2=x1, y2=y1;
+ x1=checkLines[0];
+ y1=checkLines[1];
+ x2=x1;
+ y2=y1;
for (int i=0, last=checkLines.length; i<last; i+=2) {
x2 = checkLines[(i+2)%last];
y2 = checkLines[(i+3)%last];
- float[] intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
if (intersection!=null) {
intersections.add(intersection);
if(intersections.size()==2) { break; }}
@@ -298,12 +301,6 @@ static class CollisionDetection {
// (We can have zero, one, or two)
int intersectionCount = intersections.size();
- // get the supposed hit-line
- float px = previous[corner],
- py = previous[corner+1],
- cx = current[corner],
- cy = current[corner+1];
-
// if we have two intersections, it's
// relatively easy to determine by how
// much we should move back.
@@ -313,21 +310,7 @@ static class CollisionDetection {
i2 = intersections.get(1);
float[] ideal = getLineLineIntersection(px,py,cx,cy, i1[0],i1[1],i2[0],i2[1], false);
if (ideal == null) {
- if(debug) println("error: could not find the case "+currentCase+" ideal point based on corner ["+corner+"]");
- /*
- println("tried to find the intersection between:");
- println("[1] "+px+","+py+","+cx+","+cy+" (blue)");
- println("[2] "+i1[0]+","+i1[1]+","+i2[0]+","+i2[1]+" (green)");
- //debugfunctions_drawBoundingBox(current,sketch);
- //debugfunctions_drawBoundingBox(previous,sketch);
- debugfunctions_drawBoundingBox(checkLines,sketch);
-
- sketch.noLoop();
- sketch.stroke(0,200,255);
- sketch.line(px,py,cx,cy);
- sketch.stroke(0,255,0);
- sketch.line(i1[0],i1[1],i2[0],i2[1]);
- */
+ if(debug) println("error: could not find the ideal point based on corner ["+corner+"]");
return new float[]{px-cx, py-cy, corner};
}
return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
@@ -364,7 +347,8 @@ static class CollisionDetection {
}
// Uncaught cases get a pass - with a warning.
-// println("unknown case! (B_current: "+B_current+", B_previous: "+B_previous+")");
+ // At least theoretically, there are no uncaught cases.
+ // But as a programmer, I can be wrong.
return null;
}
@@ -451,4 +435,4 @@ static class CollisionDetection {
// And then return the transformed coordinates, plus angle used
return new float[] {x1n, y1n, x2n, y2n, 0, 0, x4n, 0, angle};
}
-}
+}
View
BIN CollisionDetection.psd
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN CollisionDetection2.psd
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
14 LevelLayer.pde
@@ -432,7 +432,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -444,7 +449,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
View
4 Position.pde
@@ -118,7 +118,9 @@ class Position {
// overlap
float angle = atan2(dy,dx);
if(angle<0) { angle += 2*PI; }
- return new float[]{dx, dy, angle};
+ float safedx = dw-dx,
+ safedy = dh-dy;
+ return new float[]{dx, dy, angle, safedx, safedy};
}
/**
View
6 Positionable.pde
@@ -64,6 +64,10 @@ abstract class Positionable extends Position implements Drawable {
void detachFromAll() {
boundaries.clear();
}
+
+ void rewind() {
+ copyFrom(previous);
+ }
/**
* change the position, relative
@@ -282,7 +286,6 @@ abstract class Positionable extends Position implements Drawable {
// we're attached to one or more boundaries, so we
// are subject to (compound) impulse redirection.
if(boundaries.size()>0) {
-// println("redirecting impulse {"+_dx+","+_dy+"} over boundary surfaces...");
float[] redirected = new float[]{_dx, _dy};
for(int b=boundaries.size()-1; b>=0; b--) {
Boundary boundary = boundaries.get(b);
@@ -291,7 +294,6 @@ abstract class Positionable extends Position implements Drawable {
continue;
}
redirected = boundary.redirectForce(redirected[0], redirected[1]);
-// println("redirected to {"+redirected[0]+","+redirected[1]+"}.");
}
x += redirected[0];
y += redirected[1];
View
1 build.bat
@@ -1,4 +1,3 @@
cat *.pde > codebase.pde
cp codebase.pde mario/
cp codebase.pde docs/tutorial/
-rm codebase.pde
View
340 docs/tutorial/codebase.pde
@@ -591,7 +591,6 @@ class Boundary extends Positionable {
*/
String toString() { return x+","+y+","+xw+","+yh; }
}
-
/**
* A bounded interactor is a normal Interactor with
* one or more boundaries associated with it.
@@ -836,156 +835,159 @@ static class CollisionDetection {
/**
- * Line-through-box intersection algorithm.
- *
- * There are a few possibilities, with B() meaning
- * "on blocking side", I() meaning "intersecting"
- * and P() meaning "on pass-through side":
- *
- * |
- * (1) [] [] <| = B(prev), B(current)
- * |
- *
- * |
- * (2) [] [<|] = B(prev), B(current) and I(current) and P(current)
- * |
- *
- * |
- * (3) [] <| [] = B(prev), P(current)
- * |
- *
- * |
- * (4) [<|] [] = B(prev) and I(prev) and P(prev), P(current)
- * |
- *
- *
- * |
- * (5) <| [] [] = P(prev), P(current)
- * |
- *
- * By computing the dot product for the eight corners of the two bounding
- * boxes, we can determine which of these situations we are in. Only in
- * (2) and (3) should we signal an intersection occuring. While (4) might
- * look like another case in which this should happen, we assume that
- * because the "previous" state is boundary overlapping, it was permitted
- * earlier (for instance, it moved through the boundary in the allowed
- * direction, but then moved back before having fully passed the boundary).
+ * generate the dot products for each of the current
+ * and previous corner points, relative to (ox,oy).
*
- * DOCUMENTATION FIX: we can actually treat 2 and 3 as the same case, with
- * a more reliable result, by always constructing a
- * new intersection-validating parallelogram from the
- * "hot corner" and an adjacent corner in {previous} and
- * {current}.
- *
- *
- * @return null if no intersect, otherwise float[3]
- * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ * note: we want dot products with the vector that is
+ * perpendicular to the boundary, so that we have
+ * a clear "on blocked side" (positive) and "on
+ * pass-through side" (negative) value classifier.
*/
- static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
- {
- int B_current = 0, B_previous = 0, B_sum = 0;
+ private static int[] getDotProductsPerCorner(float[] line, float[] current, float[] current_dots, float[] previous, float[] previous_dots, int[] corners) {
+ int B_current = 0,
+ B_previous = 0,
+ B_sum = 0;
+
float ox=line[0], oy=line[1], tx=line[2], ty=line[3],
dotx = tx-ox, doty = ty-oy,
- otlen = sqrt(dotx*dotx + doty*doty),
- x, y, dx, dy, len, dotproduct;
+ otlen = sqrt(dotx*dotx + doty*doty);
- // normalised boundary vector
+ // normalise the boundary vector
dotx /= otlen;
doty /= otlen;
- // note: we want the dot product with the
- // vector that is perpendicular to the boundary!
float p = PI/2, cosp = cos(p), sinp = sin(p),
pdx = dotx*cosp - doty*sinp,
- pdy = dotx*sinp + doty*cosp;
+ pdy = dotx*sinp + doty*cosp,
+ dx, dy, len, dotproduct,
+ dotvalue = 999999, curval;
+
+ float corner1v=3, corner2v=3, corner3v=3, corner4v=3;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
- // how many corners are blocked in [current]?
+ // FIXME: I'm sure this can be optimised
for(int i=0; i<8; i+=2) {
- x = current[i];
- y = current[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'current'
+ dx = current[i]-ox;
+ dy = current[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_current++; }
current_dots[i] = dotproduct;
- }
- // how many corners are blocked in [previous]? (copied for speed)
- for(int i=0; i<8; i+=2) {
- x = previous[i];
- y = previous[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'previous'
+ dx = previous[i]-ox;
+ dy = previous[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_previous++; }
previous_dots[i] = dotproduct;
+
+ // this is silly, but relatively fast
+ if(dotproduct<corner1v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = corner1v; corner1v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = corner1; corner1 = i; }
+ else if(dotproduct<corner2v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = i; }
+ else if(dotproduct<corner3v) {
+ corner4v = corner3v; corner3v = dotproduct;
+ corner4 = corner3; corner3 = i; }
+ else {
+ corner4v = dotproduct;
+ corner4 = i; }
}
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
- // cases (1) or (5)?
- B_sum = B_current + B_previous;
- if (B_sum == 0 || B_sum == 8) {
-// println("case " + (B_sum==8? "1" : "5"));
- return null;
- }
+ // return "on blocking side" counts for both boxes
+ return new int[]{B_current, B_previous, B_current+B_previous};
+ }
- // case (4)?
- if (B_previous < 4 && B_current == 0) {
-// println("case 4");
- return null;
- }
- // Before we continue, find the point in [previous]
- // that is closest to the boundary, since that'll
- // cross the boundary first (based on its dot product
- // value.)
- int corner=-1, curcorner;
- float dotvalue = 999999, curval;
- for(curcorner=0; curcorner<8; curcorner+=2) {
- curval = abs(previous_dots[curcorner]);
- if(curval<dotvalue) {
- corner = curcorner;
- dotvalue = curval; }}
-
- // Right then. Case (2) or (3)?
+ /**
+ * Line-through-box intersection algorithm.
+ *
+ * There are a few possibilities, illustrated
+ * in the CollisionDetection.psd and
+ * CollisionDetection2.psd files included with
+ * this source code.
+ *
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ */
+ static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
+ {
+ float ox=line[0], oy=line[1], tx=line[2], ty=line[3];
+
+ // get the dot products per corner... just like the method says
+ int[] corners = {0,0,0,0},
+ B_counts = getDotProductsPerCorner(line, current, current_dots, previous, previous_dots, corners);
+
+ int B_current = B_counts[0],
+ B_previous = B_counts[1],
+ B_sum = B_counts[2];
+
+ // cases (1) or (6)? no collision.
+ if (B_sum == 0 || B_sum == 8) { return null; }
+
+ // case (4) or (5)? no collision, by convention.
+ if (B_previous < 4) { return null; }
+
+ // what remains is case (2) or (3)
if (B_previous == 4) {
- int currentCase = 2;
- // for (2) and (3) the approach is essentially
- // the same, except that we need different bounding
- // boxes to determine the intersection. For (2) we
- // can use the [current] bounding box, but for (3)
- // we need to construct a new box that is spanned by
- // the two points next to the "hot" corner in both
- // [previous] and [current].
- //
- // FIXME: that said, if we treat (2) as a subset
- // of (3), things seem to work better.
- checkLines = new float[8];
- arrayCopy(current,0,checkLines,0,8);
-
- currentCase = 3;
-
- // two of these edges are guaranteed to not have intersections,
- // since otherwise the intersection would be inside either
- // [previous] or [current], which is case (2) instead.
- checkLines = new float[]{previous[(corner)%8], previous[(corner+1)%8],
+
+ // first, we need to find out which corner point actually
+ // intersects our boundary. Typically this will be the
+ // first ranked corner, but not always.
+ float x1, y1, x2, y2;
+ int corner = -1;
+ float[] intersection;
+ for(int i=0, last=corners.length; i<last; i++) {
+ corner = corners[i];
+ x1 = previous[corner];
+ y1 = previous[corner+1];
+ x2 = current[corner];
+ y2 = current[corner+1];
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ if(intersection!=null) break;
+ }
+
+ // get the guaranteed hit-line
+ float px = previous[corner],
+ py = previous[corner+1],
+ cx = current[corner],
+ cy = current[corner+1];
+
+ // We now have the corner that's going to pass through
+ // the boundary first. we construct a new bbox to
+ // perform poly-through-line detection.
+ checkLines = new float[]{px, py,
previous[(corner+2)%8], previous[(corner+3)%8],
current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
+ cx, cy};
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
intersections.clear();
- float x1=checkLines[0], y1=checkLines[1], x2=x1, y2=y1;
+ x1=checkLines[0];
+ y1=checkLines[1];
+ x2=x1;
+ y2=y1;
for (int i=0, last=checkLines.length; i<last; i+=2) {
x2 = checkLines[(i+2)%last];
y2 = checkLines[(i+3)%last];
- float[] intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
if (intersection!=null) {
intersections.add(intersection);
if(intersections.size()==2) { break; }}
@@ -997,12 +999,6 @@ static class CollisionDetection {
// (We can have zero, one, or two)
int intersectionCount = intersections.size();
- // get the supposed hit-line
- float px = previous[corner],
- py = previous[corner+1],
- cx = current[corner],
- cy = current[corner+1];
-
// if we have two intersections, it's
// relatively easy to determine by how
// much we should move back.
@@ -1012,21 +1008,7 @@ static class CollisionDetection {
i2 = intersections.get(1);
float[] ideal = getLineLineIntersection(px,py,cx,cy, i1[0],i1[1],i2[0],i2[1], false);
if (ideal == null) {
- if(debug) println("error: could not find the case "+currentCase+" ideal point based on corner ["+corner+"]");
- /*
- println("tried to find the intersection between:");
- println("[1] "+px+","+py+","+cx+","+cy+" (blue)");
- println("[2] "+i1[0]+","+i1[1]+","+i2[0]+","+i2[1]+" (green)");
- //debugfunctions_drawBoundingBox(current,sketch);
- //debugfunctions_drawBoundingBox(previous,sketch);
- debugfunctions_drawBoundingBox(checkLines,sketch);
-
- sketch.noLoop();
- sketch.stroke(0,200,255);
- sketch.line(px,py,cx,cy);
- sketch.stroke(0,255,0);
- sketch.line(i1[0],i1[1],i2[0],i2[1]);
- */
+ if(debug) println("error: could not find the ideal point based on corner ["+corner+"]");
return new float[]{px-cx, py-cy, corner};
}
return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
@@ -1063,7 +1045,8 @@ static class CollisionDetection {
}
// Uncaught cases get a pass - with a warning.
-// println("unknown case! (B_current: "+B_current+", B_previous: "+B_previous+")");
+ // At least theoretically, there are no uncaught cases.
+ // But as a programmer, I can be wrong.
return null;
}
@@ -1150,8 +1133,7 @@ static class CollisionDetection {
// And then return the transformed coordinates, plus angle used
return new float[] {x1n, y1n, x2n, y2n, 0, 0, x4n, 0, angle};
}
-}
-/**
+}/**
* A static computation class.
*
* At the moment this houses the actor/boundary interaction
@@ -1977,7 +1959,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -1989,7 +1976,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -2275,7 +2267,9 @@ class Position {
// overlap
float angle = atan2(dy,dx);
if(angle<0) { angle += 2*PI; }
- return new float[]{dx, dy, angle};
+ float safedx = dw-dx,
+ safedy = dh-dy;
+ return new float[]{dx, dy, angle, safedx, safedy};
}
/**
@@ -2375,6 +2369,10 @@ abstract class Positionable extends Position implements Drawable {
void detachFromAll() {
boundaries.clear();
}
+
+ void rewind() {
+ copyFrom(previous);
+ }
/**
* change the position, relative
@@ -2593,7 +2591,6 @@ abstract class Positionable extends Position implements Drawable {
// we're attached to one or more boundaries, so we
// are subject to (compound) impulse redirection.
if(boundaries.size()>0) {
-// println("redirecting impulse {"+_dx+","+_dy+"} over boundary surfaces...");
float[] redirected = new float[]{_dx, _dy};
for(int b=boundaries.size()-1; b>=0; b--) {
Boundary boundary = boundaries.get(b);
@@ -2602,7 +2599,6 @@ abstract class Positionable extends Position implements Drawable {
continue;
}
redirected = boundary.redirectForce(redirected[0], redirected[1]);
-// println("redirected to {"+redirected[0]+","+redirected[1]+"}.");
}
x += redirected[0];
y += redirected[1];
@@ -4024,7 +4020,7 @@ void drawBackground(float width, float height) {
/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
-///*
+/*
final int screenWidth = 512;
final int screenHeight = 432;
@@ -4035,71 +4031,43 @@ void initialize() {
class TestLevel extends Level {
TestLevel(float w, float h) {
super(w,h);
- addLevelLayer("test", new TestLayer(this,w,h));
+ addLevelLayer("test", new TestLayer(this));
}
void draw() {
- fill(0,1);
+ fill(0,10);
rect(-1,-1,width+2,height+2);
super.draw();
- stroke(0,255,0,150);
- line(width/2,0,width/2,height);
- line(width/2+16,0,width/2+16,height);
}
}
class TestLayer extends LevelLayer {
TestObject t1, t2, t3, t4;
- TestLayer(Level p, float w, float h) {
- super(p,w,h);
+ TestLayer(Level p) {
+ super(p,p.width,p.height);
showBoundaries = true;
+
+ int v = 10;
- t1 = new TestObject(width/4, height/4);
+ t1 = new TestObject(width/2+v/2, height/2-200);
t1.setForces(5,0);
+
+ t1.setForces(0,0.01);
+ t1.setAcceleration(0,0.1);
- t2 = new TestObject(width/4, 3*height/4);
- t2.setForces(0,-5);
-
- t3 = new TestObject(3*width/4, 3*height/4);
- t3.setForces(-5,0);
-
- t4 = new TestObject(3*width/4, height/4);
- t4.setForces(0,5);
-
-// addPlayer(t1);
- addPlayer(t2);
-// addPlayer(t3);
-// addPlayer(t4);
+ addPlayer(t1);
addBoundary(new Boundary(width/2+230,height,width/2+200,0));
- addBoundary(new Boundary(width/2-180,0,width/2-150,height));
-
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
addBoundary(new Boundary(width,height/2-200,0,height/2-120));
addBoundary(new Boundary(0,height/2+200,width,height/2+120));
- addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ addBoundary(new Boundary(width/2,height/2,width/2 + v,height/2));
}
- void draw() {
+ void draw() {
super.draw();
-
- if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
- else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
-
- if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
- else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
-
- if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
- else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
-
- if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
- else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
- }
-
- void mouseClicked(int mx, int my, int mb) {
- if(mb==RIGHT) { noLoop(); return; }
- boundaries.clear();
- addBoundary(new Boundary(width/2, height/2, mx, my));
+ viewbox.track(parent,t1);
}
}
@@ -4111,4 +4079,4 @@ class TestObject extends Player {
}
}
-//*/
+*/
View
4,098 docs/tutorial/codebase.pde.tmp
4,098 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
4 docs/tutorial/sketches/mario/sketch17.pde
@@ -127,8 +127,8 @@ class MainLevelLayer extends LevelLayer {
// and let's add the thing that makes us win!
addGoal(1920, height-48);
- showBoundaries = true;
- showTriggers = true;
+ //showBoundaries = true;
+ //showTriggers = true;
}
/**
View
340 mario/codebase.pde
@@ -591,7 +591,6 @@ class Boundary extends Positionable {
*/
String toString() { return x+","+y+","+xw+","+yh; }
}
-
/**
* A bounded interactor is a normal Interactor with
* one or more boundaries associated with it.
@@ -836,156 +835,159 @@ static class CollisionDetection {
/**
- * Line-through-box intersection algorithm.
- *
- * There are a few possibilities, with B() meaning
- * "on blocking side", I() meaning "intersecting"
- * and P() meaning "on pass-through side":
- *
- * |
- * (1) [] [] <| = B(prev), B(current)
- * |
- *
- * |
- * (2) [] [<|] = B(prev), B(current) and I(current) and P(current)
- * |
- *
- * |
- * (3) [] <| [] = B(prev), P(current)
- * |
- *
- * |
- * (4) [<|] [] = B(prev) and I(prev) and P(prev), P(current)
- * |
- *
- *
- * |
- * (5) <| [] [] = P(prev), P(current)
- * |
- *
- * By computing the dot product for the eight corners of the two bounding
- * boxes, we can determine which of these situations we are in. Only in
- * (2) and (3) should we signal an intersection occuring. While (4) might
- * look like another case in which this should happen, we assume that
- * because the "previous" state is boundary overlapping, it was permitted
- * earlier (for instance, it moved through the boundary in the allowed
- * direction, but then moved back before having fully passed the boundary).
+ * generate the dot products for each of the current
+ * and previous corner points, relative to (ox,oy).
*
- * DOCUMENTATION FIX: we can actually treat 2 and 3 as the same case, with
- * a more reliable result, by always constructing a
- * new intersection-validating parallelogram from the
- * "hot corner" and an adjacent corner in {previous} and
- * {current}.
- *
- *
- * @return null if no intersect, otherwise float[3]
- * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ * note: we want dot products with the vector that is
+ * perpendicular to the boundary, so that we have
+ * a clear "on blocked side" (positive) and "on
+ * pass-through side" (negative) value classifier.
*/
- static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
- {
- int B_current = 0, B_previous = 0, B_sum = 0;
+ private static int[] getDotProductsPerCorner(float[] line, float[] current, float[] current_dots, float[] previous, float[] previous_dots, int[] corners) {
+ int B_current = 0,
+ B_previous = 0,
+ B_sum = 0;
+
float ox=line[0], oy=line[1], tx=line[2], ty=line[3],
dotx = tx-ox, doty = ty-oy,
- otlen = sqrt(dotx*dotx + doty*doty),
- x, y, dx, dy, len, dotproduct;
+ otlen = sqrt(dotx*dotx + doty*doty);
- // normalised boundary vector
+ // normalise the boundary vector
dotx /= otlen;
doty /= otlen;
- // note: we want the dot product with the
- // vector that is perpendicular to the boundary!
float p = PI/2, cosp = cos(p), sinp = sin(p),
pdx = dotx*cosp - doty*sinp,
- pdy = dotx*sinp + doty*cosp;
+ pdy = dotx*sinp + doty*cosp,
+ dx, dy, len, dotproduct,
+ dotvalue = 999999, curval;
+
+ float corner1v=3, corner2v=3, corner3v=3, corner4v=3;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
- // how many corners are blocked in [current]?
+ // FIXME: I'm sure this can be optimised
for(int i=0; i<8; i+=2) {
- x = current[i];
- y = current[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'current'
+ dx = current[i]-ox;
+ dy = current[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_current++; }
current_dots[i] = dotproduct;
- }
- // how many corners are blocked in [previous]? (copied for speed)
- for(int i=0; i<8; i+=2) {
- x = previous[i];
- y = previous[i+1];
- dx = x-ox;
- dy = y-oy;
+ // Dot product for corner on 'previous'
+ dx = previous[i]-ox;
+ dy = previous[i+1]-oy;
len = sqrt(dx*dx+dy*dy);
dx /= len;
dy /= len;
dotproduct = dx*pdx + dy*pdy;
if (dotproduct < 0) { B_previous++; }
previous_dots[i] = dotproduct;
+
+ // this is silly, but relatively fast
+ if(dotproduct<corner1v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = corner1v; corner1v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = corner1; corner1 = i; }
+ else if(dotproduct<corner2v) {
+ corner4v = corner3v; corner3v = corner2v; corner2v = dotproduct;
+ corner4 = corner3; corner3 = corner2; corner2 = i; }
+ else if(dotproduct<corner3v) {
+ corner4v = corner3v; corner3v = dotproduct;
+ corner4 = corner3; corner3 = i; }
+ else {
+ corner4v = dotproduct;
+ corner4 = i; }
}
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
- // cases (1) or (5)?
- B_sum = B_current + B_previous;
- if (B_sum == 0 || B_sum == 8) {
-// println("case " + (B_sum==8? "1" : "5"));
- return null;
- }
+ // return "on blocking side" counts for both boxes
+ return new int[]{B_current, B_previous, B_current+B_previous};
+ }
- // case (4)?
- if (B_previous < 4 && B_current == 0) {
-// println("case 4");
- return null;
- }
- // Before we continue, find the point in [previous]
- // that is closest to the boundary, since that'll
- // cross the boundary first (based on its dot product
- // value.)
- int corner=-1, curcorner;
- float dotvalue = 999999, curval;
- for(curcorner=0; curcorner<8; curcorner+=2) {
- curval = abs(previous_dots[curcorner]);
- if(curval<dotvalue) {
- corner = curcorner;
- dotvalue = curval; }}
-
- // Right then. Case (2) or (3)?
+ /**
+ * Line-through-box intersection algorithm.
+ *
+ * There are a few possibilities, illustrated
+ * in the CollisionDetection.psd and
+ * CollisionDetection2.psd files included with
+ * this source code.
+ *
+ *
+ * @return null if no intersect, otherwise float[3]
+ * indices: [0] = dx, [1] = dy, [2] = bbox (x) index to hit boundary first
+ */
+ static float[] getLineRectIntersection(float[] line, float[] current, float[] previous)
+ {
+ float ox=line[0], oy=line[1], tx=line[2], ty=line[3];
+
+ // get the dot products per corner... just like the method says
+ int[] corners = {0,0,0,0},
+ B_counts = getDotProductsPerCorner(line, current, current_dots, previous, previous_dots, corners);
+
+ int B_current = B_counts[0],
+ B_previous = B_counts[1],
+ B_sum = B_counts[2];
+
+ // cases (1) or (6)? no collision.
+ if (B_sum == 0 || B_sum == 8) { return null; }
+
+ // case (4) or (5)? no collision, by convention.
+ if (B_previous < 4) { return null; }
+
+ // what remains is case (2) or (3)
if (B_previous == 4) {
- int currentCase = 2;
- // for (2) and (3) the approach is essentially
- // the same, except that we need different bounding
- // boxes to determine the intersection. For (2) we
- // can use the [current] bounding box, but for (3)
- // we need to construct a new box that is spanned by
- // the two points next to the "hot" corner in both
- // [previous] and [current].
- //
- // FIXME: that said, if we treat (2) as a subset
- // of (3), things seem to work better.
- checkLines = new float[8];
- arrayCopy(current,0,checkLines,0,8);
-
- currentCase = 3;
-
- // two of these edges are guaranteed to not have intersections,
- // since otherwise the intersection would be inside either
- // [previous] or [current], which is case (2) instead.
- checkLines = new float[]{previous[(corner)%8], previous[(corner+1)%8],
+
+ // first, we need to find out which corner point actually
+ // intersects our boundary. Typically this will be the
+ // first ranked corner, but not always.
+ float x1, y1, x2, y2;
+ int corner = -1;
+ float[] intersection;
+ for(int i=0, last=corners.length; i<last; i++) {
+ corner = corners[i];
+ x1 = previous[corner];
+ y1 = previous[corner+1];
+ x2 = current[corner];
+ y2 = current[corner+1];
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ if(intersection!=null) break;
+ }
+
+ // get the guaranteed hit-line
+ float px = previous[corner],
+ py = previous[corner+1],
+ cx = current[corner],
+ cy = current[corner+1];
+
+ // We now have the corner that's going to pass through
+ // the boundary first. we construct a new bbox to
+ // perform poly-through-line detection.
+ checkLines = new float[]{px, py,
previous[(corner+2)%8], previous[(corner+3)%8],
current[(corner+2)%8], current[(corner+3)%8],
- current[(corner)%8], current[(corner+1)%8]};
+ cx, cy};
// Now that we have the correct box, perform line/line
// intersection detection for each edge on the box.
intersections.clear();
- float x1=checkLines[0], y1=checkLines[1], x2=x1, y2=y1;
+ x1=checkLines[0];
+ y1=checkLines[1];
+ x2=x1;
+ y2=y1;
for (int i=0, last=checkLines.length; i<last; i+=2) {
x2 = checkLines[(i+2)%last];
y2 = checkLines[(i+3)%last];
- float[] intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
+ intersection = getLineLineIntersection(ox,oy,tx,ty, x1,y1,x2,y2, false);
if (intersection!=null) {
intersections.add(intersection);
if(intersections.size()==2) { break; }}
@@ -997,12 +999,6 @@ static class CollisionDetection {
// (We can have zero, one, or two)
int intersectionCount = intersections.size();
- // get the supposed hit-line
- float px = previous[corner],
- py = previous[corner+1],
- cx = current[corner],
- cy = current[corner+1];
-
// if we have two intersections, it's
// relatively easy to determine by how
// much we should move back.
@@ -1012,21 +1008,7 @@ static class CollisionDetection {
i2 = intersections.get(1);
float[] ideal = getLineLineIntersection(px,py,cx,cy, i1[0],i1[1],i2[0],i2[1], false);
if (ideal == null) {
- if(debug) println("error: could not find the case "+currentCase+" ideal point based on corner ["+corner+"]");
- /*
- println("tried to find the intersection between:");
- println("[1] "+px+","+py+","+cx+","+cy+" (blue)");
- println("[2] "+i1[0]+","+i1[1]+","+i2[0]+","+i2[1]+" (green)");
- //debugfunctions_drawBoundingBox(current,sketch);
- //debugfunctions_drawBoundingBox(previous,sketch);
- debugfunctions_drawBoundingBox(checkLines,sketch);
-
- sketch.noLoop();
- sketch.stroke(0,200,255);
- sketch.line(px,py,cx,cy);
- sketch.stroke(0,255,0);
- sketch.line(i1[0],i1[1],i2[0],i2[1]);
- */
+ if(debug) println("error: could not find the ideal point based on corner ["+corner+"]");
return new float[]{px-cx, py-cy, corner};
}
return new float[]{ideal[0]-cx, ideal[1]-cy, corner};
@@ -1063,7 +1045,8 @@ static class CollisionDetection {
}
// Uncaught cases get a pass - with a warning.
-// println("unknown case! (B_current: "+B_current+", B_previous: "+B_previous+")");
+ // At least theoretically, there are no uncaught cases.
+ // But as a programmer, I can be wrong.
return null;
}
@@ -1150,8 +1133,7 @@ static class CollisionDetection {
// And then return the transformed coordinates, plus angle used
return new float[] {x1n, y1n, x2n, y2n, 0, 0, x4n, 0, angle};
}
-}
-/**
+}/**
* A static computation class.
*
* At the moment this houses the actor/boundary interaction
@@ -1977,7 +1959,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -1989,7 +1976,12 @@ abstract class LevelLayer {
float[] overlap = a.overlap(o);
if(overlap!=null) {
a.overlapOccurredWith(o, overlap);
- o.overlapOccurredWith(a, new float[]{-overlap[0], -overlap[1], overlap[2]}); }
+ int len = overlap.length;
+ float[] inverse = new float[len];
+ arrayCopy(overlap,0,inverse,0,len);
+ for(int pos=0; pos<len; pos++) { inverse[pos] = -inverse[pos]; }
+ o.overlapOccurredWith(a, inverse);
+ }
else if(o instanceof Tracker) {
((Tracker)o).track(a, x,y,w,h);
}
@@ -2275,7 +2267,9 @@ class Position {
// overlap
float angle = atan2(dy,dx);
if(angle<0) { angle += 2*PI; }
- return new float[]{dx, dy, angle};
+ float safedx = dw-dx,
+ safedy = dh-dy;
+ return new float[]{dx, dy, angle, safedx, safedy};
}
/**
@@ -2375,6 +2369,10 @@ abstract class Positionable extends Position implements Drawable {
void detachFromAll() {
boundaries.clear();
}
+
+ void rewind() {
+ copyFrom(previous);
+ }
/**
* change the position, relative
@@ -2593,7 +2591,6 @@ abstract class Positionable extends Position implements Drawable {
// we're attached to one or more boundaries, so we
// are subject to (compound) impulse redirection.
if(boundaries.size()>0) {
-// println("redirecting impulse {"+_dx+","+_dy+"} over boundary surfaces...");
float[] redirected = new float[]{_dx, _dy};
for(int b=boundaries.size()-1; b>=0; b--) {
Boundary boundary = boundaries.get(b);
@@ -2602,7 +2599,6 @@ abstract class Positionable extends Position implements Drawable {
continue;
}
redirected = boundary.redirectForce(redirected[0], redirected[1]);
-// println("redirected to {"+redirected[0]+","+redirected[1]+"}.");
}
x += redirected[0];
y += redirected[1];
@@ -4024,7 +4020,7 @@ void drawBackground(float width, float height) {
/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
-///*
+/*
final int screenWidth = 512;
final int screenHeight = 432;
@@ -4035,71 +4031,43 @@ void initialize() {
class TestLevel extends Level {
TestLevel(float w, float h) {
super(w,h);
- addLevelLayer("test", new TestLayer(this,w,h));
+ addLevelLayer("test", new TestLayer(this));
}
void draw() {
- fill(0,1);
+ fill(0,10);
rect(-1,-1,width+2,height+2);
super.draw();
- stroke(0,255,0,150);
- line(width/2,0,width/2,height);
- line(width/2+16,0,width/2+16,height);
}
}
class TestLayer extends LevelLayer {
TestObject t1, t2, t3, t4;
- TestLayer(Level p, float w, float h) {
- super(p,w,h);
+ TestLayer(Level p) {
+ super(p,p.width,p.height);
showBoundaries = true;
+
+ int v = 10;
- t1 = new TestObject(width/4, height/4);
+ t1 = new TestObject(width/2+v/2, height/2-200);
t1.setForces(5,0);
+
+ t1.setForces(0,0.01);
+ t1.setAcceleration(0,0.1);
- t2 = new TestObject(width/4, 3*height/4);
- t2.setForces(0,-5);
-
- t3 = new TestObject(3*width/4, 3*height/4);
- t3.setForces(-5,0);
-
- t4 = new TestObject(3*width/4, height/4);
- t4.setForces(0,5);
-
-// addPlayer(t1);
- addPlayer(t2);
-// addPlayer(t3);
-// addPlayer(t4);
+ addPlayer(t1);
addBoundary(new Boundary(width/2+230,height,width/2+200,0));
- addBoundary(new Boundary(width/2-180,0,width/2-150,height));
-
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
addBoundary(new Boundary(width,height/2-200,0,height/2-120));
addBoundary(new Boundary(0,height/2+200,width,height/2+120));
- addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ addBoundary(new Boundary(width/2,height/2,width/2 + v,height/2));
}
- void draw() {
+ void draw() {
super.draw();
-
- if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
- else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
-
- if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
- else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
-
- if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
- else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
-
- if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
- else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
- }
-
- void mouseClicked(int mx, int my, int mb) {
- if(mb==RIGHT) { noLoop(); return; }
- boundaries.clear();
- addBoundary(new Boundary(width/2, height/2, mx, my));
+ viewbox.track(parent,t1);
}
}
@@ -4111,4 +4079,4 @@ class TestObject extends Player {
}
}
-//*/
+*/
View
11 mario/hero.pde
@@ -118,6 +118,17 @@ class Mario extends Player {
}
/**
+ * When we touch down on a boundary,
+ * and we're in our jump animation,
+ * just go back to the idle state.
+ */
+ void gotBlocked(Boundary b, float[] intersection) {
+ if(active.name=="jumping") {
+ setCurrentState("idle");
+ }
+ }
+
+ /**
* What happens when we touch another actor?
*/
void overlapOccurredWith(Actor other, float[] direction) {
View
6 mario/mainlayer.pde
@@ -62,8 +62,8 @@ class MainLevelLayer extends LevelLayer {
// and let's add the thing that makes us win!
addGoal(1920, height-48);
-// showBoundaries = true;
-// showTriggers = true;
+ showBoundaries = true;
+ showTriggers = true;
}
/**
@@ -210,7 +210,7 @@ class MainLevelLayer extends LevelLayer {
super.draw();
viewbox.track(parent, mario);
// just in case!
- if(mario!=null && mario.active != null && mario.active.name!="dead" && mario.y>height) {
+ if(mario!=null && mario.active != null && mario.active.name!="dead" && mario.y>height*2) {
reset();
}
}
View
4 processing.js
@@ -19763,7 +19763,9 @@
}
// DEBUGGING HACK PATCH TEST TEST TEST
- return new Processing(canvas, code.join("\n"));
+ var sktch = new Processing(canvas, code.join("\n"));
+ sktch.noLoop();
+ return sktch;
// } catch(e) {
// throw "Processing.js: Unable to execute pjs sketch: " + e;
// }
View
62 spriteengine.pde
@@ -12,7 +12,7 @@
/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
-///*
+/*
final int screenWidth = 512;
final int screenHeight = 432;
@@ -23,71 +23,43 @@ void initialize() {
class TestLevel extends Level {
TestLevel(float w, float h) {
super(w,h);
- addLevelLayer("test", new TestLayer(this,w,h));
+ addLevelLayer("test", new TestLayer(this));
}
void draw() {
- fill(0,1);
+ fill(0,10);
rect(-1,-1,width+2,height+2);
super.draw();
- stroke(0,255,0,150);
- line(width/2,0,width/2,height);
- line(width/2+16,0,width/2+16,height);
}
}
class TestLayer extends LevelLayer {
TestObject t1, t2, t3, t4;
- TestLayer(Level p, float w, float h) {
- super(p,w,h);
+ TestLayer(Level p) {
+ super(p,p.width,p.height);
showBoundaries = true;
+
+ int v = 10;
- t1 = new TestObject(width/4, height/4);
+ t1 = new TestObject(width/2+v/2, height/2-200);
t1.setForces(5,0);
+
+ t1.setForces(0,0.01);
+ t1.setAcceleration(0,0.1);
- t2 = new TestObject(width/4, 3*height/4);
- t2.setForces(0,-5);
-
- t3 = new TestObject(3*width/4, 3*height/4);
- t3.setForces(-5,0);
-
- t4 = new TestObject(3*width/4, height/4);
- t4.setForces(0,5);
-
-// addPlayer(t1);
- addPlayer(t2);
-// addPlayer(t3);
-// addPlayer(t4);
+ addPlayer(t1);
addBoundary(new Boundary(width/2+230,height,width/2+200,0));
- addBoundary(new Boundary(width/2-180,0,width/2-150,height));
-
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
addBoundary(new Boundary(width,height/2-200,0,height/2-120));
addBoundary(new Boundary(0,height/2+200,width,height/2+120));
- addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ addBoundary(new Boundary(width/2,height/2,width/2 + v,height/2));
}
- void draw() {
+ void draw() {
super.draw();
-
- if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
- else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
-
- if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
- else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
-
- if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
- else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
-
- if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
- else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
- }
-
- void mouseClicked(int mx, int my, int mb) {
- if(mb==RIGHT) { noLoop(); return; }
- boundaries.clear();
- addBoundary(new Boundary(width/2, height/2, mx, my));
+ viewbox.track(parent,t1);
}
}
@@ -99,4 +71,4 @@ class TestObject extends Player {
}
}
-//*/
+*/
View
102 spriteengine.pde.old
@@ -0,0 +1,102 @@
+/***********************************
+ * *
+ * This file does nothing, *
+ * but allows Processing to *
+ * actually load the code *
+ * if located in a directory *
+ * of the same name. Feel *
+ * free to rename it. *
+ * *
+ ***********************************/
+
+
+/* @ p j s preload="docs/tutorial/graphics/mario/small/Standing-mario.gif"; */
+
+/*
+
+final int screenWidth = 512;
+final int screenHeight = 432;
+void initialize() {
+ addLevel("test", new TestLevel(width,height));
+}
+
+class TestLevel extends Level {
+ TestLevel(float w, float h) {
+ super(w,h);
+ addLevelLayer("test", new TestLayer(this,w,h));
+ }
+
+ void draw() {
+ fill(0,1);
+ rect(-1,-1,width+2,height+2);
+ super.draw();
+ stroke(0,255,0,150);
+ line(width/2,0,width/2,height);
+ line(width/2+16,0,width/2+16,height);
+ }
+}
+
+class TestLayer extends LevelLayer {
+ TestObject t1, t2, t3, t4;
+ TestLayer(Level p, float w, float h) {
+ super(p,w,h);
+ showBoundaries = true;
+
+ t1 = new TestObject(width/4, height/4);
+ t1.setForces(5,0);
+
+ t2 = new TestObject(width/4, 3*height/4);
+ t2.setForces(0,-5);
+
+ t3 = new TestObject(3*width/4, 3*height/4);
+ t3.setForces(-5,0);
+
+ t4 = new TestObject(3*width/4, height/4);
+ t4.setForces(0,5);
+
+// addPlayer(t1);
+ addPlayer(t2);
+// addPlayer(t3);
+// addPlayer(t4);
+
+ addBoundary(new Boundary(width/2+230,height,width/2+200,0));
+ addBoundary(new Boundary(width/2-180,0,width/2-150,height));
+
+ addBoundary(new Boundary(width,height/2-200,0,height/2-120));
+ addBoundary(new Boundary(0,height/2+200,width,height/2+120));
+
+ addBoundary(new Boundary(width/2,height/2,width/2 + 16,height/2));
+ }
+
+ void draw() {
+ super.draw();
+
+ if (t1.getX() > 0.75*width) { t1.setForces(-5,0); }
+ else if (t1.getX() < 0.25*width) { t1.setForces(5,0); }
+
+ if (t3.getX() > 0.75*width) { t3.setForces(-5,0); }
+ else if (t3.getX() < 0.25*width) { t3.setForces(5,0); }
+
+ if (t2.getY() > 0.75*height) { t2.setForces(0,-5); }
+ else if (t2.getY() < 0.25*height) { t2.setForces(0,5); }
+
+ if (t4.getY() > 0.75*height) { t4.setForces(0,-5); }
+ else if (t4.getY() < 0.25*height) { t4.setForces(0,5); }
+ }
+
+ void mouseClicked(int mx, int my, int mb) {
+ if(mb==RIGHT) { noLoop(); return; }
+ boundaries.clear();
+ addBoundary(new Boundary(width/2, height/2, mx, my));
+ }
+}
+
+class TestObject extends Player {
+ TestObject(float x, float y) {
+ super("test");
+ addState(new State("test","docs/tutorial/graphics/mario/small/Standing-mario.gif"));
+ setPosition(x,y);
+ }
+}
+
+*/
View
4,084 tests/backup codebase/codebase.pde
4,084 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
348 tests/boundaries/interaction/CollisionDetection.pde
@@ -0,0 +1,348 @@
+/**
+ * Alternative collision detection
+ */
+static class CollisionDetection {
+ private static boolean debug = true;
+
+ /**
+ * Static classes need global sketch binding
+ */
+ private static PApplet sketch;
+ public static void init(PApplet s) { sketch = s; }
+
+ /**
+ * Perform line/rect intersection detection. Lines represent boundaries,
+ * and rather than doing "normal" line/rect intersection using a
+ * "box on a trajectory" that normal actor movement looks like, we pretend
+ * the actor box remains stationary, and move the boundary in the opposite
+ * direction with the same speed, which gives us a "boundary box", so that
+ * we can perform box/box overlap detection instead.
+ */
+ static float[] getLineRectIntersection(float[] line, float[] previous, float[] current)
+ {
+ if(debug) sketch.println(sketch.frameCount + "> testing against: "+arrayToString(line));
+ if(debug) sketch.println(sketch.frameCount + "> previous: "+arrayToString(previous));
+ if(debug) sketch.println(sketch.frameCount + "> current : "+arrayToString(current));
+
+ // First, let's do some dot-product math, to find out whether or not
+ // the actor's bounding box is even in range of the boundary.
+ float x1=line[0], y1=line[1], x2=line[2], y2=line[3];
+ float dx = x2-x1, dy = y2-y1, pv=PI/2.0,
+ rdx = dx*cos(pv) - dy*sin(pv),
+ rdy = dx*sin(pv) + dy*cos(pv);
+
+ // determine range w.r.t. the starting point of the boundary.
+ float[] dotProducts1 = getDotProducts(x1,y1,x2,y2, previous);
+
+ // determine range w.r.t. the end point of the boundary.
+ float[] dotProducts2 = getDotProducts(x2,y2,x1,y1, previous);
+
+ // determine 'sidedness', relative to the boundary.
+ float[] dotProducts3 = getDotProducts(x1,y1,x1+rdx,y1+rdy, previous);
+ float[] dotProducts4 = getDotProducts(x1,y1,x1+rdx,y1+rdy, current);
+
+ // compute the relevant feature values based on the dot products:
+ int inRangeS = 4, inRangeE = 4, above = 0, aboveAfter = 0;
+ for(int i=0; i<8; i+=2) {
+ if (dotProducts1[i] < 0) { inRangeS--; }
+ if (dotProducts2[i] < 0) { inRangeE--; }
+ if (dotProducts3[i] <= 0) { above++; }
+ if (dotProducts4[i] <= 0) { aboveAfter++; }}
+
+ if(debug) sketch.println(sketch.frameCount +"> dotproduct result: "+inRangeS+"/"+inRangeE+"/"+above+"/"+aboveAfter);
+
+ // make sure to short-circuit if the actor cannot
+ // interact with the boundary because it is out of range.
+ if (inRangeS == 0 || inRangeE == 0) {
+ if(debug) sketch.println(sketch.frameCount +"> this boundary is not involved in collisions for this frame.");
+ return null;
+ }
+
+ if (above>0 && aboveAfter>above) {
+ if(debug) sketch.println(sketch.frameCount +"> this box is not involved in collisions for this frame (1).");
+ return null;
+ }
+
+ if (above==4 && aboveAfter==4) {
+ if(debug) sketch.println(sketch.frameCount +"> this box is not involved in collisions for this frame (2).");
+ return null;
+ }
+
+ // Now then, let's determine whether overlap will occur.
+ boolean found = false;
+
+ // We're in bounds: if 'above' is 4, meaning that our previous
+ // actor frame is on the blocking side of a boundary, and
+ // 'aboveAfter' is 0, meaning its current frame is on the other
+ // side of the boundary, then a collision MUST have occurred.
+ if (above==4 && aboveAfter==0) {
+ // note that in this situation, the overlap may look
+ // like full containment, where the actor's bounding
+ // box is fully contained by the boundary's box.
+ found = true;
+ if(debug) sketch.println(sketch.frameCount +"> collision detected (due to full containment).");
+ }
+
+ else {
+ // We're in bounds: do box/box intersection checking
+ // using the 'previous' box and the boundary-box.
+ dx = previous[0] - current[0];
+ dy = previous[1] - current[1];
+
+ // form boundary box
+ float[] bbox = {line[0], line[1],
+ line[2], line[3],
+ line[2]+dx, line[3]+dy,
+ line[0]+dx, line[1]+dy};
+
+ // do any of the "previous" edges intersect
+ // with any of the "boundary box" edges?
+ int i,j;
+ float[] p = previous, b = bbox, intersection;
+ for(i=0; i<8; i+=2) {
+ for(j=0; j<8; j+=2) {
+ intersection = getLineLineIntersection(p[i], p[i+1], p[(i+2)%8], p[(i+3)%8], b[j], b[j+1], b[(j+2)%8], b[(j+3)%8], false, true);
+ if (intersection != null) {
+ found = true;
+ if(debug) sketch.println(sketch.frameCount +"> collision detected on a box edge (box overlap).");
+ }
+ }
+ }
+ }
+
+ // Have we signaled any overlap?
+ if (found) {
+ float[] distances = getCornerDistances(x1,y1,x2,y2, previous, current);
+ int[] corners = rankCorners(distances);
+
+ if(debug) {
+ sketch.print(sketch.frameCount + "> ");
+ for(int i=0; i<4; i++) {
+ sketch.print(corners[i]+"="+distances[corners[i]]);
+ if(i<3) sketch.print(", "); }
+ sketch.println();
+ }
+
+ // Get the corner on the previous and current actor bounding
+ // box that will "hit" the boundary first.
+ int corner = 0;
+ float xp = previous[corners[corner]],
+ yp = previous[corners[corner]+1],
+ xc = current[corners[corner]],
+ yc = current[corners[corner]+1];
+
+ // The trajectory for this point may intersect with
+ // the boundary. If it does, we'll have all the information
+ // we need to move the actor back along its trajectory by
+ // an amount that will place it "on" the boundary, at the right spot.
+ float[] intersection = getLineLineIntersection(xp,yp,xc,yc, x1,y1,x2,y2, false, true);
+
+ if (intersection==null) {
+ println("nearest-to-boundary is actually not on the boundary itself. More complex math is required!");
+
+ // it's also possible that the first corner to hit the boundary
+ // actually never touches the boundary because it intersects only
+ // if the boundary is infinitely long. So... let's make that happen:
+ intersection = getLineLineIntersection(xp,yp,xc,yc, x1,y1,x2,y2, false, false);
+
+ if (intersection==null) {
+ println("line extension alone is not enoough...");
+
+ // FIXME: this is not satisfactory! A real solution should be implemented!
+ return new float[]{xp-xc, yp-yc}; // effect a full rewind for now
+ }
+
+ return new float[]{intersection[0] - xc, intersection[1] - yc};
+ }
+
+ // if we get here, there was a normal trajectory
+ // intersection with the boundary. Computing the
+ // corrective values by which to move the current
+ // frame's bounding box is really simple:
+ dx = intersection[0] - xc;
+ dy = intersection[1] - yc;
+
+ if(debug) sketch.println(sketch.frameCount +"> dx: "+dx+", dy: "+dy);
+
+ return new float[]{dx, dy};
+ }
+
+ return null;
+ }
+
+ /**
+ * For each corner in an object's bounding box, get the distance from its "previous"
+ * box to the line defined by (x1,y1,x2,y2). Return this as float[8], corresponding
+ * to the bounding box array format.
+ */
+ static float[] getCornerDistances(float x1, float y1, float x2, float y2, float[] previous, float[] current) {
+ float[] distances = {0,0,0,0,0,0,0,0}, intersection;
+ float dx, dy;
+ for(int i=0; i<8; i+=2) {
+ intersection = getLineLineIntersection(x1,y1,x2,y2, previous[i], previous[i+1], current[i], current[i+1], false, false);
+ if (intersection == null) {
+ continue;
+ }
+ dx = intersection[0] - previous[i];
+ dy = intersection[1] - previous[i+1];
+ distances[i] = sqrt(dx*dx+dy*dy);
+ distances[i+1] = distances[i];
+ }
+ return distances;
+ }
+
+
+ /**
+ * Get the intersection coordinate between two lines segments,
+ * using fairly standard, if a bit lenghty, linear algebra.
+ */
+ static float[] getLineLineIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, boolean colinearity, boolean segments)
+ {
+ float epsilon = 0.1;
+ // convert lines to the generatised form [a * x + b + y = c]
+ float a1 = -(y2 - y1), b1 = (x2 - x1), c1 = (x2 - x1) * y1 - (y2 - y1) * x1;
+ float a2 = -(y4 - y3), b2 = (x4 - x3), c2 = (x4 - x3) * y3 - (y4 - y3) * x3;
+ // find their intersection
+ float d = a1 * b2 - a2 * b1;
+ if (d == 0) {
+ // Two lines are parallel: we are not interested in the
+ // segment if the points are not colinear.
+ if (!colinearity || (x2 - x3) * (y2 - y1) != (y2 - y3) * (x2 - x1)) {
+ return null;
+ }
+ // Solve the algebraic functions [x = (x1 - x0) * t + x0] for t
+ float t1 = x3 != x4 ? (x1 - x3) / (x4 - x3) : (y1 - y3) / (y4 - y3);
+ float t2 = x3 != x4 ? (x2 - x3) / (x4 - x3) : (y2 - y3) / (y4 - y3);
+ if ((t1 < 0 && t2 < 0) || (t1 > 1 && t2 > 1)) {
+ // points 1 and 2 are outside the points 3 and 4 segment
+ return null;
+ }
+ // Clamp t values to the interval [0, 1]
+ t1 = t1 < 0 ? 0 : t1 > 1 ? 1 : t1;
+ t2 = t2 < 0 ? 0 : t2 > 1 ? 1 : t2;
+ return new float[]{(x4 - x3) * t1 + x3, (y4 - y3) * t1 + y3,
+ (x4 - x3) * t2 + x3, (y4 - y3) * t2 + y3};
+ }
+ // not colinear - find the intersection point
+ else {
+ float x = (c1 * b2 - c2 * b1) / d;
+ float y = (a1 * c2 - a2 * c1) / d;
+ // make sure the point can be found on both segments.
+ if (segments && (x < min(x1, x2) - epsilon || max(x1, x2) + epsilon < x ||
+ y < min(y1, y2) - epsilon || max(y1, y2) + epsilon < y ||
+ x < min(x3, x4) - epsilon || max(x3, x4) + epsilon < x ||
+ y < min(y3, y4) - epsilon || max(y3, y4) + epsilon < y)) {
+ // not on either, or both, segments.
+ return null;
+ }
+ return new float[]{x, y};
+ }
+ }
+
+
+ /**
+ * compute the dot product between all corner points of a
+ * bounding box, and a boundary line with origin ox/oy
+ * and end point tx/ty.
+ */
+ static float[] getDotProducts(float ox, float oy, float tx, float ty, float[] bbox) {
+ float dotx = tx-ox, doty = ty-oy,
+ otlen = sqrt(dotx*dotx + doty*doty),
+ dx, dy, len, dotproduct;
+
+ dotx /= otlen;
+ doty /= otlen;
+
+ float[] dotProducts = new float[8];
+
+ for(int i=0; i<8; i+=2) {
+ dx = bbox[i]-ox;
+ dy = bbox[i+1]-oy;
+ len = sqrt(dx*dx+dy*dy);
+ dx /= len;
+ dy /= len;
+ dotproduct = dx*dotx + dy*doty;
+ dotProducts[i] = dotproduct;
+ }
+
+ return dotProducts;
+ }
+
+
+ /**
+ * Rank the corner points for a bounding box
+ * based on it's distance to the boundary,
+ * along its trajectory path.
+ *
+ * We rank by decreasing distance.
+ */
+ // FIXME: this is a pretty fast code, but there might be
+ // better ways to achieve the desired result.
+ static int[] rankCorners(float[] distances) {
+ int[] corners = {0,0,0,0};
+ float corner1v=999999, corner2v=corner1v, corner3v=corner1v, corner4v=corner1v, distance;
+ int corner1=-1, corner2=-1, corner3=-1, corner4=-1;
+
+ for(int i=0; i<8; i+=2) {
+ distance = distances[i];
+ if (distance < corner1v) {
+ corner4v = corner3v; corner4 = corner3;
+ corner3v = corner2v; corner3 = corner2;
+ corner2v = corner1v; corner2 = corner1;
+ corner1v = distance; corner1 = i;
+ continue; }
+ if (distance < corner2v) {
+ corner4v = corner3v; corner4 = corner3;
+ corner3v = corner2v; corner3 = corner2;
+ corner2v = distance; corner2 = i;
+ continue; }
+ if (distance < corner3v) {
+ corner4v = corner3v; corner4 = corner3;
+ corner3v = distance; corner3 = i;
+ continue; }
+ corner4v = distance; corner4 = i;
+ }
+
+ // Set up the corners, ranked by
+ // proximity to the boundary.
+ corners[0] = corner1;
+ corners[1] = corner2;
+ corners[2] = corner3;
+ corners[3] = corner4;
+ return corners;
+ }
+
+
+ /**
+ * Check if a bounding box's dot product
+ * information implies it's safe, or blocked.
+ */
+ static boolean permitted(float[] dotProducts) {
+ for(int i=0; i<8; i+=2) {
+ if (dotProducts[i]>0)
+ return true; }
+ return false;
+ }
+
+
+ /**
+ * Simple drawing helper function
+ */
+ static void drawBox(float[] boundingbox) {
+ sketch.line(boundingbox[0], boundingbox[1], boundingbox[2], boundingbox[3]);
+ sketch.line(boundingbox[2], boundingbox[3], boundingbox[4], boundingbox[5]);
+ sketch.line(boundingbox[4], boundingbox[5], boundingbox[6], boundingbox[7]);
+ sketch.line(boundingbox[6], boundingbox[7], boundingbox[0], boundingbox[1]);
+ }
+
+ /**
+ * Simple printing helper function
+ */
+ static String arrayToString(float[] arr) {
+ String str = "";
+ for(int i=0; i<arr.length; i++) {
+ str += int(100*arr[i])/100.0;
+ if(i<arr.length-1) { str += ", "; }}
+ return str;
+ }
+}
View
189 tests/boundaries/interaction/interaction.pde
@@ -0,0 +1,189 @@
+
+float[] old, prev, bbox;
+ArrayList<float[]> boundaries = new ArrayList<float[]>();
+ArrayList<float[]> attached = new ArrayList<float[]>();
+
+int padding = 60;
+float epsilon = 0.5;
+int skipover = 0;
+
+/**
+ * Boilerplate setup
+ */
+void setup() {
+ size(800,800);
+ CollisionDetection.init(this);
+ ellipseMode(CENTER);
+ frameRate(1000);
+ reset();
+}
+
+/**
+ * Reset the world
+ */
+void reset() {
+ skipover = 0;
+
+ int x = width/2, y=height/2-100, dx=24, dy=24;
+ bbox = new float[]{x,y, x+dx,y, x+dx,y+dy, x,y+dy};
+ prev = new float[]{0,0,0,0,0,0,0,0};
+ old = new float[]{0,0,0,0,0,0,0,0};
+
+ boundaries.clear();
+ attached.clear();
+
+ // bounding poly
+ boundaries.add(new float[]{padding,padding,padding,height-padding});
+ boundaries.add(new float[]{padding,height-padding,width-padding,height-padding});
+ boundaries.add(new float[]{width-padding,height-padding,width-padding,padding});
+ boundaries.add(new float[]{width-padding,padding,padding,padding});
+}
+
+/**
+ * Boilerplate draw function
+ */
+void draw() {
+ fill(0,150);
+ rect(-1,-1,width+2,height+2);
+
+ if(bbox[0]<padding || bbox[2]>width-padding || bbox[1]<padding || bbox[5]>height-padding) {
+ println(frameCount +"> **************************************************************");
+ println(frameCount +"> box found in illegal position, noLoop will be called ("+bbox[0]+"--"+bbox[2]+"/"+bbox[1]+"--"+bbox[5]+")");
+ println(frameCount +"> **************************************************************");
+ noLoop();
+ skipover++;
+ }
+
+ // draw boundaries and box
+ int intensity = 0;
+ for(float[] boundary: boundaries) {
+ stroke(100 + intensity++*20,100 + intensity++*20,200);
+ line(boundary[0], boundary[1], boundary[2], boundary[3]);
+ }
+
+ stroke(0,0,255);
+ drawBox(old);
+
+ stroke(127,200,255);
+ drawBox(prev);
+
+ stroke(255);
+ drawBox(bbox);
+
+ // try to update
+ nextFrame();
+
+ println(frameCount + "> end of draw function");
+}
+
+// helper draw function for showing box wireframes
+void drawBox(float[] boundingbox) {
+ line(boundingbox[0], boundingbox[