/* * Copyright (C) 2010, 2011, 2012 by Arne Kesting, Martin Treiber, Ralph Germ, Martin Budden * <movsim.org@gmail.com> * ----------------------------------------------------------------------------------------- * * This file is part of * * MovSim - the multi-model open-source vehicular-traffic simulator. * * MovSim is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MovSim is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MovSim. If not, see <http://www.gnu.org/licenses/> * or <http://www.movsim.org>. * * ----------------------------------------------------------------------------------------- */ package org.movsim.roadmappings; import java.util.ArrayList; import org.movsim.simulator.roadnetwork.Lanes; import org.movsim.simulator.vehicles.Vehicle; /** * A RoadMapping maps a logical road position (given by a lane and a position on a road segment) onto a physical * position, that is an x,y coordinate (given in meters). */ public abstract class RoadMapping { /** * * @param roadPos * @param lateralOffset * offset from center of road, used mainly for drawing roadlines and road edges * @return a PosTheta object giving position and direction */ public abstract RoadMapping.PosTheta map(double roadPos, double lateralOffset); /** * Polygon with integer coordinates. */ public static class Polygon { /** * Number of points in the polygon. */ public int pointCount; /** * Array of x-coordinates of the polygon. */ public int xPoints[]; /** * Array of y-coordinates of the polygon. */ public int yPoints[]; /** * Constructor, allocate arrays for polygon points. * * @param pointCount * number of points in the polygon. */ public Polygon(int pointCount) { this.pointCount = pointCount; xPoints = new int[pointCount]; yPoints = new int[pointCount]; } } /** * Polygon with floating point coordinates. * */ public static class PolygonFloat { /** * Number of points in the polygon. */ public int pointCount; /** * Array of x-coordinates of the polygon. */ public float xPoints[]; /** * Array of y-coordinates of the polygon. */ public float yPoints[]; /** * Constructor, allocate arrays for polygon points. * * @param pointCount * number of points in the polygon. */ public PolygonFloat(int pointCount) { this.pointCount = pointCount; xPoints = new float[pointCount]; yPoints = new float[pointCount]; } } /** * <p> * Class representing a position in space plus a direction. * </p> * <p> * Angles are interpreted as in the Argand diagram, that is they are measured in a counter-clockwise direction from * the x-axis (3 o'clock position). * </p> */ public static class PosTheta { /** * x-coordinate of point. */ public double x; /** * y-coordinate of point. */ public double y; /** * cosine of angle. */ public double cosTheta; /** * sine of angle. */ public double sinTheta; /** * Returns angle, in radians, measured counter-clockwise from x-axis. * * @return angle, in radians, measured counter-clockwise from x-axis */ public double theta() { return Math.atan2(sinTheta, cosTheta); } } // Immutable Properties protected final int laneCount; protected double laneWidth; protected double roadWidth; // trafficLaneMin and trafficLaneMax set the range of lanes for normal traffic in the road // segment lanes less than trafficLaneMin or greater than trafficLaneMax are exit or entrance // ramps. private int trafficLaneMin; private int trafficLaneMax; // Road protected double roadLength; protected int roadColor; protected static int defaultRoadColor = 8421505; // gray // Positioning // pre-allocate single posTheta for the road mapping. This is shared and reused, so must be used // carefully. protected final PosTheta posTheta = new PosTheta(); protected double x0; protected double y0; // Clipping Region protected static final int POINT_COUNT = 4; protected final PolygonFloat polygonFloat = new PolygonFloat(POINT_COUNT); protected ArrayList<PolygonFloat> clippingPolygons; protected PolygonFloat outsideClippingPolygon; public static final double DEFAULT_LANE_WIDTH = 2; /** * Constructor. * * @param laneCount * @param x0 * @param y0 */ protected RoadMapping(int laneCount, double laneWidth, double x0, double y0) { this.laneCount = laneCount; this.x0 = x0; this.y0 = y0; trafficLaneMin = Lanes.LANE1; // most inner lane trafficLaneMax = laneCount; this.laneWidth = laneWidth; roadWidth = laneWidth * laneCount; roadColor = defaultRoadColor; } /** * Constructor. * * @param laneCount * @param x0 * @param y0 */ protected RoadMapping(int laneCount, double x0, double y0) { this(laneCount, DEFAULT_LANE_WIDTH, x0, y0); } /** * Called when the system is running low on memory, and would like actively running process to try to tighten their * belts. */ protected void onLowMemory() { // By default does nothing. Subclasses may implement memory saving. } /** * Returns the default road color. * * @return the default road color */ public static int defaultRoadColor() { return RoadMapping.defaultRoadColor; } /** * Sets the default road color. * * @param defaultRoadColor */ public static void setDefaultRoadColor(int defaultRoadColor) { RoadMapping.defaultRoadColor = defaultRoadColor; } /** * Sets the minimum traffic lane. Lanes with <code>lane < trafficLaneMin</code> are not traffic lanes and may be * treated differently, especially for lane changes. * * @param trafficLaneMin */ public final void setTrafficLaneMin(int trafficLaneMin) { assert trafficLaneMin >= Lanes.MOST_INNER_LANE; this.trafficLaneMin = trafficLaneMin; } /** * Returns the minimum traffic lane. * * @return the minimum traffic lane */ public final int trafficLaneMin() { return trafficLaneMin; } /** * Sets the maximum traffic lane. Lanes with <code>lane > trafficLaneMax</code> are not traffic lanes and may be * treated differently, especially for lane changes. * * @param trafficLaneMax */ public final void setTrafficLaneMax(int trafficLaneMax) { this.trafficLaneMax = trafficLaneMax; } /** * Returns the maximum traffic lane. * * @return the maximum traffic lane */ public final int trafficLaneMax() { return trafficLaneMax; } /** * Convenience function, returns the start position of the road. * * @return start position of the road */ public RoadMapping.PosTheta startPos() { return map(0.0, 0.0); } /** * Convenience function, returns the start position of the road for a given lateral offset. * * @param lateralOffset * * @return start position of the road for given lateral offset */ public RoadMapping.PosTheta startPos(double lateralOffset) { return map(0.0, lateralOffset); } /** * Convenience function, returns the end position of the road. * * @return end position of the road */ public RoadMapping.PosTheta endPos() { return map(roadLength, 0.0); } /** * Convenience function, returns the end position of the road for a given lateral offset. * * @param lateralOffset * * @return end position of the road for given lateral offset */ public RoadMapping.PosTheta endPos(double lateralOffset) { return map(roadLength, lateralOffset); } /** * Returns the end position of the ramp lane. * * @return end position of the ramp lane */ public RoadMapping.PosTheta endPosRamp() { final double lateralOffset = laneOffset(laneCount); return map(roadLength, lateralOffset); } /** * Map a longitudinal position on the road onto a position and direction in real space. * * @param roadPos * @return posTheta giving position and direction in real space */ public RoadMapping.PosTheta map(double roadPos) { return map(roadPos, 0.0); } /** * Returns the length of the road. * * @return road length, in meters */ public final double roadLength() { return roadLength; } /** * Returns the width of the road. * * @return road width, in meters */ public final double roadWidth() { return roadWidth; } /** * Sets the road color. * * @param color */ public final void setRoadColor(int color) { this.roadColor = color; } /** * Returns the road color. * * @return road color */ public final int roadColor() { return roadColor; } /** * Returns the width of the lanes. * * @return the width of the lanes, in meters */ public final double laneWidth() { return laneWidth; } /** * Returns the number of lanes. * * @return number of lanes */ public final int laneCount() { return laneCount; } /** * Returns the offset of the center of the lane. Fractional lanes are supported to facilitate the drawing of * vehicles in the process of changing lanes. * * @param lane * @return the offset of the center of the lane */ private final double laneOffset(double lane) { return (lane == Lanes.NONE) ? 0.0 : (0.5 * (1 - laneCount) + (lane - 1)) * laneWidth; // return (0.5 * (trafficLaneMin + laneCount - 1) - lane) * laneWidth; } /** * Returns the offset of the center of the lane. * * @param lane * @return the offset of the center of the lane */ public final double laneOffset(int lane) { return laneOffset((double) lane); } /** * Returns the offset of the inside edge of the lane. * * @param lane * @return the offset of the inside edge of the lane */ public final double laneInsideEdgeOffset(int lane) { return (0.5 * (1 - laneCount + 1) + (lane - 1)) * laneWidth; } /** * Set a clipping region based on the road position and length. Simple implementation at the moment: only one * clipping region is supported. * * @param pos * position of the clipping region on the road * @param length * length of the clipping region */ public void addClippingRegion(double pos, double length) { if (clippingPolygons == null) { clippingPolygons = new ArrayList<>(); } if (outsideClippingPolygon == null) { // !!! TODO - this is temporary code, need to fix clipping region // set up the outside clip polygon final float LARGE_NUMBER = 100000.0f; outsideClippingPolygon = new PolygonFloat(POINT_COUNT); outsideClippingPolygon.xPoints[0] = -LARGE_NUMBER; outsideClippingPolygon.yPoints[0] = -LARGE_NUMBER; outsideClippingPolygon.xPoints[1] = LARGE_NUMBER; outsideClippingPolygon.yPoints[1] = -LARGE_NUMBER; outsideClippingPolygon.xPoints[2] = LARGE_NUMBER; outsideClippingPolygon.yPoints[2] = LARGE_NUMBER; outsideClippingPolygon.xPoints[3] = -LARGE_NUMBER; outsideClippingPolygon.yPoints[3] = LARGE_NUMBER; } final PolygonFloat clippingPolygon = new PolygonFloat(POINT_COUNT); final double offset = 1.5 * laneCount * laneWidth; PosTheta posTheta; posTheta = map(pos + length, -offset); clippingPolygon.xPoints[0] = (float) posTheta.x; clippingPolygon.yPoints[0] = (float) posTheta.y; posTheta = map(pos + length, offset); clippingPolygon.xPoints[1] = (float) posTheta.x; clippingPolygon.yPoints[1] = (float) posTheta.y; posTheta = map(pos, offset); clippingPolygon.xPoints[2] = (float) posTheta.x; clippingPolygon.yPoints[2] = (float) posTheta.y; posTheta = map(pos, -offset); clippingPolygon.xPoints[3] = (float) posTheta.x; clippingPolygon.yPoints[3] = (float) posTheta.y; clippingPolygons.add(clippingPolygon); } /** * Returns an arraylist of the clipping polygons, or null if no clipping set. * * @return arraylist of the clipping polygons, or null if no clipping set. */ public ArrayList<RoadMapping.PolygonFloat> clippingPolygons() { return clippingPolygons; } /** * Returns the outside clipping polygon. * * @return the outside clipping polygon */ public RoadMapping.PolygonFloat outsideClippingPolygon() { return outsideClippingPolygon; } public RoadMapping.PolygonFloat mapFloat(RoadMapping.PosTheta posTheta, double length, double width) { final double lca = length * posTheta.cosTheta; final double wsa = width * posTheta.sinTheta; final double xbr = posTheta.x - 0.5 * (lca - wsa); polygonFloat.xPoints[0] = (float) (xbr + lca); // front right polygonFloat.xPoints[1] = (float) (xbr + lca - wsa); // front left polygonFloat.xPoints[2] = (float) (xbr - wsa); // back left polygonFloat.xPoints[3] = (float) xbr; // back right final double lsa = length * posTheta.sinTheta; final double wca = width * posTheta.cosTheta; final double ybr = posTheta.y + 0.5 * (lsa + wca); polygonFloat.yPoints[0] = (float) (ybr - lsa); // front right polygonFloat.yPoints[1] = (float) (ybr - wca - lsa); // front left polygonFloat.yPoints[2] = (float) (ybr - wca); // back left polygonFloat.yPoints[3] = (float) ybr; // back right return polygonFloat; } /** * Returns a polygon with its vertices at the corners of the subject vehicle. * * @param vehicle * @param time * current simulation time * @return polygon representing vehicle */ public RoadMapping.PolygonFloat mapFloat(Vehicle vehicle, double time) { final RoadMapping.PosTheta posTheta = map(vehicle.physicalQuantities().getMidPosition(), laneOffset(vehicle.getContinousLane())); return mapFloat(posTheta, vehicle.physicalQuantities().getLength(), vehicle.physicalQuantities().getWidth()); } }