From fe257bbd8b9304578c38ee3e386c8a4660f8589e Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 17 Jan 2014 11:37:34 +0100 Subject: Hook up O track drawing New bugs: with the O track, the robots look in the wrong direction. --- src/RaceTrack.java | 93 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/src/RaceTrack.java b/src/RaceTrack.java index 3828d54..5c5b842 100644 --- a/src/RaceTrack.java +++ b/src/RaceTrack.java @@ -96,34 +96,78 @@ class RaceTrack extends BetterBase { public void draw(int trackNr) { // The test track is selected if (0 == trackNr) { - drawTestTrack(); + // Special case: no control points, fall back to test track. + selectedControlPoints = null; } else if (1 == trackNr) { // The O-track is selected - drawTrack(controlPointsOTrack); + selectedControlPoints = controlPointsOTrack; } else if (2 == trackNr) { // The L-track is selected - drawTrack(controlPointsLTrack); + selectedControlPoints = controlPointsLTrack; } else if (3 == trackNr) { // The C-track is selected - drawTrack(controlPointsCTrack); + selectedControlPoints = controlPointsCTrack; } else if (4 == trackNr) { // The custom track is selected - drawTrack(controlPointsOTrack); + selectedControlPoints = controlPointsCustomTrack; } + if (selectedControlPoints != null) { + assert selectedControlPoints.length % 3 == 0 : + "Multiple of three control points required"; + } + drawTrack(); + } + + /** + * For internal use, only valid for drawing Bézier splines. + */ + private int bezier_start_i; + + private double calculateBezierParams(double t) { + t = t % 1.0; + //assert t >= 0 && t < 1.0 : "t is invalid: " + t; + + int number_of_segments = selectedControlPoints.length / 3; + // number of "u" units per segment + double segment_size = 1.0 / number_of_segments; + + int segment_number = (int) (t / segment_size); + // should always hold if t < 1.0 + assert segment_number < number_of_segments; + bezier_start_i = 3 * segment_number; + + // drop segments before this one + double segment_u = t - segment_number * segment_size; + // scale the part to 0.0 to 0.1 + segment_u *= number_of_segments; + assert segment_u >= 0.0 && segment_u <= 1.0; + return segment_u; } /** * Returns the position of the curve at 0 <= {@code t} <= 1. */ public Vector getPoint(double t) { + if (selectedControlPoints != null) { + // TODO: do not call func -- optimization + double u = calculateBezierParams(t); + return getCubicBezierPnt(u, selectedControlPoints, bezier_start_i); + } return new Vector(ELLIPSE_A * cos(2 * PI * t), ELLIPSE_B * sin(2 * PI * t), 1); } + private int getNumberOfLanes() { + // TODO: get robots count from race instance + return 4; + } + /** * Returns the position of the curve at 0 <= {@code t} <= 1 and * the center of a lane at lane 1 <= laneNo <= (number of robots). */ public Vector getPointForLane(double t, double laneNo) { Vector p = getPoint(t); - Vector lanes_len = new Vector(p.x(), p.y(), 0).normalized().scale(laneNo + .5); + // relative distance from center line (positive if directed to normal) + double relDist = laneNo - getNumberOfLanes() / 2 + .5; + Vector lanes_len = getNormal(t).scale(relDist); return p.add(lanes_len); } @@ -131,6 +175,12 @@ class RaceTrack extends BetterBase { * Returns the tangent of the curve at 0 <= {@code t} <= 1. */ public Vector getTangent(double t) { + if (selectedControlPoints != null) { + // TODO: do not call func -- optimization + double u = calculateBezierParams(t); + return getCubicBezierTng(u, selectedControlPoints, bezier_start_i); + } + /* * Given a vector (-Y/B^2, X/A^2, 0) where X and Y are the coordinates * of the point p on the ellipse. A is the HALFWIDTH of the ellipse and @@ -153,35 +203,52 @@ class RaceTrack extends BetterBase { 0).normalized(); } - private void drawTestTrack() { + /** + * Returns the normal vector of the curve at t. + */ + public Vector getNormal(double t) { + Vector tangent = getTangent(t); + // right-hand rule: a (tangent direction), a x b is normal (pointing + // outside), so b must be positive Z vector. + Vector norm = tangent.cross(Vector.Z); + // for out purposes, Z is zero. + assert norm.z() == 0 : "Z is not zero!"; + assert tangent.dot(norm) == 0 : "Result is not normal?!"; + // just to be sure, unit lengths! + return norm.normalized(); + } + + private void drawTrack() { /* A track segment looks like: * B----------------------------D "outside top" * / : /| * / G- - - - - - - - - - - - -/--H "outside bottom" + * P * / / * A----------------------------C "inside top" * | | * E----------------------------F "inside bottom" * ^-- t = t0 ^-- t = t0 + 1 * Assume point A the inner point of the race track. Draw quads from - * EF (starting point) to AC, BD, GH. + * EF (starting point) to AC, BD, GH. P is a point on the center line. */ // previous points Vector point_A = null, point_B = null, point_E = null, point_G = null; for (double i = 0; i <= SEGMENTS; ++i) { double t = i / SEGMENTS; - Vector point_C = getPoint(t); - // the outer side is located on the number of lanes (4) shifted from - // the center to the side (minus 0.5). - Vector point_D = getPointForLane(t, 3.5); + Vector point_P = getPoint(t); + Vector norm_P = getNormal(t); + Vector halfLaneLen = norm_P.scale(getNumberOfLanes() / 2); + Vector point_C = point_P.subtract(halfLaneLen); + Vector point_D = point_P.add(halfLaneLen); // Z=1 to Z=-1 Vector point_F = point_C.subtract(new Vector(0, 0, 2)); Vector point_H = point_D.subtract(new Vector(0, 0, 2)); // initially, there are no "previous" vectors to use as start. if (i > 0) { - Vector norm_outside = new Vector(point_E.x(), point_E.y(), 0).normalized(); + Vector norm_outside = norm_P; Vector norm_inside = norm_outside.scale(-1).normalized(); Vector norm_up = Vector.Z; -- cgit v1.2.1