public myLineSegment(double x1, double y1, double x2, double y2) { p1 = new myPoint(x1, y1); p2 = new myPoint(x2, y2); v = new myVector(x2 - x1, y2 - y1); }
// Update this segment based on new information public void Update(myPoint p1, myPoint p2) { this.p1 = p1; this.p2 = p2; v = new myVector(p2.x - p1.x, p2.y - p1.y); }
public myLineSegment(double x, double y, myVector v) { p1 = new myPoint(x, y); p2 = new myPoint(x + v.x, y + v.y); this.v = v; }
//////////////////////////////////// // Derp-LineSegment Circle Casting - for field tile collisions // Returns: true if collision, false otherwise // out double t: this will be the parameter t, how far we can move along our vector before we get within Derp's radius of the line segment //////////////////////////////////// public static bool DerpLineSegmentCast(Derp d, myVector v, myLineSegment wall, out double t) { myLineSegment derpMoveLine = new myLineSegment(d.x, d.y, v); t = derpMoveLine.SegmentIntersectionWithRadius(wall, d.stats.radius); return(t < 1.0 - 1e-6); }
public myLineSegment(myPoint p1, myPoint p2) { this.p1 = p1; this.p2 = p2; if (p1 != null && p2 != null) { v = new myVector(p2.x - p1.x, p2.y - p1.y); } }
public double SegmentIntersectionWithRadius(myLineSegment o, double radius) { // get determinate and check for parallel lines double D = v.cross(o.v); if (Math.Abs(D) < 1e-6) { // Are we already within the line radius? if (o.DistToPoint2(p1) < radius * radius) { return(0.0); } // Check if we are now moving onto the line segement. else if (NonIntersectingSegmentDistance(o, 0.0, radius) < 0.1) { // return 1.0; return(Math.Min(Geometry.MovingCirlcePointCast(this, radius, o.p1), Geometry.MovingCirlcePointCast(this, radius, o.p2))); } // No collision else { return(1.0); } } // relative vector from the other segments starting point to my own myVector relVect = new myVector(p1, o.p1); double t = relVect.cross(o.v) / D; double u = relVect.cross(v) / D; // find if the intersection happens along the other line segment if (u < 0.0 || u > 1.0) { return(NonIntersectingSegmentDistance(o, t, radius)); } // find if the intersection happens along this line segment if (t < 0.0 || t > 1.0) { return(NonIntersectingSegmentDistance(o, t, radius)); } // An intersection is happening! Scoot the t parameter back a bit for the radius and buffer then return it // Scoot t back so the radius is equal to our radius double sintheta = Math.Abs(o.v.cross(v)) / (v.mag() * o.v.mag()); t -= (radius / sintheta) / v.mag(); // also remove the collision buffer, then return it t -= (CollisionBuffer / v.mag()); return(t < 0.0 ? 0.0 : t); }
// The above situation in a more general form static public double MovingCirlcePointCast(myLineSegment move, double radius, myPoint point) { double distX = move.p1.x - point.x; double distY = move.p1.y - point.y; // make sure we are close before we do any of the computational checking if (distX * distX + distY * distY > distSquaredThreshholdX) { return(1.0); } // solve the quadratic to find the intersection of two circles myPoint p1 = move.p1; myPoint p2 = point; myVector v = move.v; double A = v.x * v.x + v.y * v.y; double B = 2 * (p1.x * v.x - p2.x * v.x + p1.y * v.y - p2.y * v.y); double C = (p1.x * p1.x) + (p2.x * p2.x) - 2 * p2.x * p1.x + (p1.y * p1.y) + (p2.y * p2.y) - 2 * p2.y * p1.y - (radius * radius); // unreal answers or a t that is greater than 1.0 means no collision double discr = (B * B) - 4 * A * C; if (discr < 0.0) { return(1.0); //imaginary answer } double t = (-B - Math.Sqrt(discr)) / (2 * A); if (t > 1.0) { return(1.0); // no collision with this step size } // this is detecting a collision behind us // NOTICE: there may be issues here in the future... if someone moves into us? (wait... they can't though. we guarentee that no one will move somewhere they can't... I think...) if (t < 0.0) { return(1.0); } // BUFFER // if we let them get right up next to each other, sometimes the floating point error is enough to let them slip through (I think) // add a buffer if there is a collision if (t < 1.0 - 1e-6) { t -= bufferDistance / Math.Sqrt(v.x * v.x + v.y * v.y); t = Math.Max(t, 0.0); } return(t); }
// Find the nearest cardinal direction to a vector (for snapping movement and sprites to 8 frames) public static int GetNearestCardinalDir(myVector v) { int ret = 0; for (int i = 1; i < 8; ++i) { if (v.dot(Geometry.cardinalUnitVectors[i]) > v.dot(Geometry.cardinalUnitVectors[ret])) { ret = i; } } return(ret); }
// return the squared distanc to the point (remove squareroots) public double DistToPoint2(myPoint p) { // my length squared double l2 = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); // find the parameterized value of where the nearest point would be // (dot product of my vector to the vector from my start to the point) myVector toPoint = new myVector(p1, p); double t = v.dot(toPoint) / l2; if (t < 0.0) { return(p1.distance2(p)); } if (t > 1.0) { return(p2.distance2(p)); } // it's on the line segment, project to the position myPoint projPoint = new myPoint(p1.x + t * v.x, p1.y + t * v.y); return(projPoint.distance2(p)); }
public double cross(myVector o) { return(x * o.y - y * o.x); }
public double dot(myVector o) { return(x * o.x + y * o.y); }
// Check for a Derp's Circle-Cast across the field // This will work like a BFS, starting from the initial position // then check all 4 corners around the position to build on the bfs public bool CheckFieldCollision(Derp d, myVector v, out double t, out myLineSegment col) { // initial optimistic setup that there will not be a collision bool ret = false; t = 1.0; col = null; // calculate starting point int startX = (int)(d.x / BLOCK_WIDTH); int startY = (int)(d.y / BLOCK_HEIGHT); // Unit Vector in desired direction myVector vUnit = new myVector(v.x, v.y); vUnit.toUnit(); // set up the bfs Queue <SimpleNode> q = new Queue <SimpleNode>(); bool[,] vis = new bool[height + 1, width + 1]; q.Enqueue(new SimpleNode(startX, startY, 0)); vis[startY, startX] = true; // Create the 4 line segments so we don't have to do quiiite as much object creation in this loop myLineSegment[] segs = new myLineSegment[4]; for (int i = 0; i < 4; ++i) { segs[i] = new myLineSegment(null, null); } // BFS int[] dx = { 0, 1, 0, -1 }; int[] dy = { -1, 0, 1, 0 }; int cur_step = 0; while (q.Count > 0) { SimpleNode cur = q.Dequeue(); // end early if we had a hit already in a previous step if (ret && cur_step != cur.step) { break; } // checking 4 nodes around us myPoint p1 = new myPoint(cur.x * BLOCK_WIDTH, cur.y * BLOCK_HEIGHT); myPoint p2 = new myPoint((cur.x + 1) * BLOCK_WIDTH, cur.y * BLOCK_HEIGHT); myPoint p3 = new myPoint((cur.x + 1) * BLOCK_WIDTH, (cur.y + 1) * BLOCK_HEIGHT); myPoint p4 = new myPoint(cur.x * BLOCK_WIDTH, (cur.y + 1) * BLOCK_HEIGHT); segs[0].Update(p1, p2); segs[1].Update(p2, p3); segs[2].Update(p4, p3); segs[3].Update(p1, p4); for (int i = 0; i < 4; ++i) { int nx = cur.x + dx[i]; int ny = cur.y + dy[i]; if (nx < 0 || nx > width || ny < 0 || ny >= height || vis[ny, nx]) { continue; } double possible_t; if (Geometry.DerpLineSegmentCast(d, v, segs[i], out possible_t)) { // We have a hit! If the next zone is safe to move in, then continue the bfs if (gameGrid[ny, nx] != '0') { q.Enqueue(new SimpleNode(nx, ny, cur.step + 1)); vis[ny, nx] = true; } // We hit an unnavigable space. Stop the BFS, this is as far as we go else { ret = true; if (Math.Abs(possible_t - t) < 1e-5 && col != null) { // break ties by taking the furthest behind the direction we wish to go // Calculate the center point on the wall, and get the dot product of the vector to that point. // The most negative value is the furthest behind myPoint segMidPoint1 = new myPoint((segs[i].p1.x + segs[i].p2.x) / 2.0, (segs[i].p1.y + segs[i].p2.y) / 2.0); myVector toMidPoint1 = new myVector(segMidPoint1.x - d.x, segMidPoint1.y - d.y); myPoint segMidPoint2 = new myPoint((col.p1.x + col.p2.x) / 2.0, (col.p1.y + col.p2.y) / 2.0); myVector toMidPoint2 = new myVector(segMidPoint2.x - d.x, segMidPoint2.y - d.y); if (vUnit.dot(toMidPoint1) < vUnit.dot(toMidPoint2)) { t = possible_t; col = new myLineSegment(segs[i].p1.x, segs[i].p1.y, segs[i].p2.x, segs[i].p2.y); // careful... memory bugs } } else if (possible_t < t) { t = possible_t; col = new myLineSegment(segs[i].p1.x, segs[i].p1.y, segs[i].p2.x, segs[i].p2.y); // careful... memory bugs } } } } // if we are a special diagonal case, then check the cross hit as well myLineSegment diag = null; char c = gameGrid[cur.y, cur.x]; if (c == '1' || c == '3') { diag = new myLineSegment(p2, p4); } if (c == '2' || c == '4') { diag = new myLineSegment(p1, p3); } if (diag != null) { double possible_t; if (Geometry.DerpLineSegmentCast(d, v, diag, out possible_t)) { ret = true; if (Math.Abs(possible_t - t) < 1e-5 && col != null) { // break ties by taking the furthest behind the direction we wish to go // Calculate the center point on the wall, and get the dot product of the vector to that point. // The most negative value is the furthest behind myPoint segMidPoint1 = new myPoint((diag.p1.x + diag.p2.x) / 2.0, (diag.p1.y + diag.p2.y) / 2.0); myVector toMidPoint1 = new myVector(segMidPoint1.x - d.x, segMidPoint1.y - d.y); myPoint segMidPoint2 = new myPoint((col.p1.x + col.p2.x) / 2.0, (col.p1.y + col.p2.y) / 2.0); myVector toMidPoint2 = new myVector(segMidPoint2.x - d.x, segMidPoint2.y - d.y); if (vUnit.dot(toMidPoint1) < vUnit.dot(toMidPoint2)) { t = possible_t; col = new myLineSegment(diag.p1.x, diag.p1.y, diag.p2.x, diag.p2.y); // careful... memory bugs } } else if (possible_t < t) { t = possible_t; col = new myLineSegment(diag.p1.x, diag.p1.y, diag.p2.x, diag.p2.y); // careful... memory bugs } } } cur_step = cur.step; } return(ret); }