org.invenzzia.opentrans.visitons.network.Track.java Source code

Java tutorial

Introduction

Here is the source code for org.invenzzia.opentrans.visitons.network.Track.java

Source

/*
 * Copyright (C) 2013 Invenzzia Group <http://www.invenzzia.org/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.invenzzia.opentrans.visitons.network;

import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.invenzzia.helium.data.interfaces.IIdentifiable;
import org.invenzzia.opentrans.visitons.geometry.ArcOps;
import org.invenzzia.opentrans.visitons.geometry.Characteristics;
import org.invenzzia.opentrans.visitons.geometry.Geometry;
import org.invenzzia.opentrans.visitons.geometry.LineOps;
import org.invenzzia.opentrans.visitons.network.objects.ITrackObject;
import org.invenzzia.opentrans.visitons.network.objects.TrackObject;

/**
 * Represents a track in the network infrastructure graph. Tracks are created from
 * track records.
 * 
 * @author Tomasz Jdrzejewski
 */
public class Track {
    /**
     * Numerical identifier for the storage purposes.
     */
    private long id;
    /**
     * Switchable track type.
     */
    private byte type;
    /**
     * First connected vertex.
     */
    private IVertex v1;
    /**
     * Second connected vertex.
     */
    private IVertex v2;
    /**
     * Track length in world units (metres).
     */
    private double length;
    /**
     * Extra metadata for certain forms of tracks.
     */
    private double metadata[];
    /**
     * Various stationary objects put on the track.
     */
    private List<TrackObject> trackObjects;
    /**
     * Junctions on this track.
     */
    private List<Junction> junctions = ImmutableList.of();

    /**
     * @return Unique track ID.
     */
    public long getId() {
        return this.id;
    }

    /**
     * Sets the track ID, if it is not set yet.
     * 
     * @param id New track ID.
     */
    public void setId(long id) {
        if (IIdentifiable.NEUTRAL_ID != this.id) {
            throw new IllegalStateException("The ID is already set for the track.");
        }
        this.id = id;
    }

    /**
     * Return the type of the vertex: straight, curved or free.
     * 
     * @return Track type. See {@link NetworkConst} constants.
     */
    public byte getType() {
        return this.type;
    }

    /**
     * Returns the track type metadata. Do not even try to modify the returned array.
     * 
     * @return Track metadata for drawing etc.
     */
    public double[] getMetadata() {
        return this.metadata;
    }

    public IVertex getFirstVertex() {
        return this.v1;
    }

    public IVertex getSecondVertex() {
        return this.v2;
    }

    /**
     * Returns the length of this track.
     * 
     * @return Length of this track in world units.
     */
    public double getLength() {
        return this.length;
    }

    /**
     * Sets the length. This method shall not be used outside the I/O code.
     * 
     * @param length 
     */
    public void setLength(double length) {
        this.length = length;
    }

    /**
     * Returns the vertex on the opposite side of the track.
     * 
     * @param tested The vertex we know.
     * @return The vertex opposite to it.
     */
    public IVertex getOppositeVertex(IVertex tested) {
        if (this.v1 == tested) {
            return this.v2;
        } else {
            return this.v1;
        }
    }

    /**
     * Sets the track vertices. The method exists primarily for debugging/testing purposes and should
     * not be used in the production.
     * 
     * @param v1
     * @param v2 
     */
    @Deprecated
    public void setVertices(IVertex v1, IVertex v2) {
        this.v1 = Preconditions.checkNotNull(v1);
        this.v2 = Preconditions.checkNotNull(v2);
    }

    /**
     * Helper method for the {@link World} instance to deal with track removal.
     */
    public void removeFromVertices() {
        this.v1.removeTrack(this);
        this.v2.removeTrack(this);
    }

    /**
     * Surprise! This method is the part of the public API, because track objects are added directly
     * to the real tracks. The method also attaches the track itself to the track object.
     * 
     * @param trackObject 
     */
    public void addTrackObject(TrackObject trackObject) {
        if (null == this.trackObjects) {
            this.trackObjects = new LinkedList<>();
        }
        this.trackObjects.add(trackObject);
        trackObject.setTrack(this);
    }

    /**
     * Returns an immutable copy of the track object list.
     * 
     * @return Immutable copy of the track object list.
     */
    public List<TrackObject> getTrackObjects() {
        if (null == this.trackObjects) {
            return ImmutableList.of();
        }
        return ImmutableList.copyOf(this.trackObjects);
    }

    /**
     * Returns true, if the track has any track objects.
     * 
     * @return 
     */
    public boolean hasTrackObjects() {
        return this.trackObjects != null;
    }

    /**
     * We remove the track object by providing the actual object backed by it.
     * 
     * @param backedObject 
     */
    public void removeTrackObject(ITrackObject backedObject) {
        if (null != this.trackObjects) {
            TrackObject toRemove = null;
            for (TrackObject to : this.trackObjects) {
                if (to.getObject().equals(backedObject)) {
                    toRemove = to;
                }
            }
            if (null != toRemove) {
                this.trackObjects.remove(toRemove);
                if (this.trackObjects.isEmpty()) {
                    this.trackObjects = null;
                }
            }
        }
    }

    /**
     * Returns true, if the track has any junctions.
     */
    public boolean hasJunctions() {
        return null != this.junctions;
    }

    /**
     * Imports the junctions from the given list of records.
     * 
     * @param records List of junction records.
     * @param world World instance used for finding the actual entity.
     */
    public void importJunctions(List<JunctionRecord> records, BiMap<Long, Long> vertexMapping, World world) {
        if (records.isEmpty()) {
            this.junctions = ImmutableList.of();
        }
        Junction juncs[] = new Junction[records.size()];
        int i = 0;
        for (JunctionRecord jr : records) {
            long theId = jr.getId();
            if (vertexMapping.containsKey(theId)) {
                theId = vertexMapping.get(theId);
            }
            IVertex vertex = world.findVertex(theId);
            if (!(vertex instanceof Junction)) {
                throw new IllegalStateException("The vertex #" + theId + " was expected to be a junction.");
            }
            juncs[i++] = (Junction) vertex;
        }
        this.junctions = ImmutableList.copyOf(juncs);
    }

    /**
     * @return Immutable collection of junctions. 
     */
    public List<Junction> getJunctions() {
        return this.junctions;
    }

    /**
     * Converts the position from range <tt>[0.0, 1.0]</tt> to the point-tangent information.
     * 
     * @param t Position on a track.
     * @return Point coordinates and the tangent.
     */
    public Characteristics getPointCharacteristics(double t) {
        double k, a, b;
        double ax = this.v1.pos().getAbsoluteX();
        double ay = this.v1.pos().getAbsoluteY();
        switch (this.type) {
        case NetworkConst.TRACK_STRAIGHT:
            return new Characteristics(LineOps.linePoint(t, ax, this.v2.pos().getAbsoluteX()),
                    LineOps.linePoint(t, ay, this.v2.pos().getAbsoluteY()), this.v1.tangentFor(this));
        case NetworkConst.TRACK_CURVED:
            k = ArcOps.param2Angle(t, this.metadata[6] + ax, this.metadata[7] + ay, ax, ay, this.metadata[3],
                    this.metadata[5]);
            return new Characteristics(Math.cos(k) * (this.metadata[2] / 2.0) + this.metadata[6] + ax,
                    Math.sin(k) * (this.metadata[2] / 2.0) + this.metadata[7] + ay,
                    Geometry.normalizeAngle(k + Math.PI / 2.0));
        case NetworkConst.TRACK_FREE:
            if (t < 0.5) {
                t *= 2.0;
                k = ArcOps.param2Angle(t, this.metadata[6] + ax, this.metadata[7] + ay, ax, ay, this.metadata[3],
                        this.metadata[5]);
                return new Characteristics(Math.cos(k) * (this.metadata[2] / 2.0) + this.metadata[6] + ax,
                        Math.sin(k) * (this.metadata[2] / 2.0) + this.metadata[7] + ay,
                        Geometry.normalizeAngle(k + Math.PI / 2.0));
            } else {
                t = (t - 0.5) * 2.0;
                k = ArcOps.param2Angle(t, this.metadata[14] + ax, this.metadata[15] + ay, this.metadata[18] + ax,
                        this.metadata[19] + ay, this.metadata[11], this.metadata[13]);
                return new Characteristics(Math.cos(k) * (this.metadata[10] / 2.0) + this.metadata[14] + ax,
                        Math.sin(k) * (this.metadata[10] / 2.0) + this.metadata[15] + ay,
                        Geometry.normalizeAngle(k + Math.PI / 2.0));
            }
        }
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Imports the current data from the track record.
     * 
     * @param tr The source track record.
     * @param world Find the actual vertex instances here.
     * @param vertexMapping Vertices in track record may be new - we need a mapping to the actual ID-s.
     */
    public void importFrom(TrackRecord tr, World world, BiMap<Long, Long> vertexMapping) {
        this.type = tr.getType();

        this.metadata = tr.getMetadata();
        long actualId = tr.getFirstVertex().getId();
        if (actualId < IIdentifiable.NEUTRAL_ID) {
            actualId = vertexMapping.get(Long.valueOf(actualId));
        }
        this.v1 = world.findVertex(actualId);
        actualId = tr.getSecondVertex().getId();
        if (actualId < IIdentifiable.NEUTRAL_ID) {
            actualId = vertexMapping.get(Long.valueOf(actualId));
        }
        this.v2 = world.findVertex(actualId);

        // Metadata must be converted to relative coordinates.
        double originalMeta[] = tr.getMetadata();
        if (null != originalMeta) {
            double metadata[] = new double[originalMeta.length];
            System.arraycopy(originalMeta, 0, metadata, 0, originalMeta.length);
            double dx = tr.getFirstVertex().x();
            double dy = tr.getFirstVertex().y();
            switch (this.type) {
            case NetworkConst.TRACK_STRAIGHT:
                metadata[0] -= dx;
                metadata[1] -= dy;
                metadata[2] -= dx;
                metadata[3] -= dy;
                break;
            case NetworkConst.TRACK_FREE:
                metadata[8] -= dx;
                metadata[9] -= dy;
                metadata[14] -= dx;
                metadata[15] -= dy;
                metadata[0] -= dx;
                metadata[1] -= dy;
                metadata[6] -= dx;
                metadata[7] -= dy;
                metadata[16] -= dx;
                metadata[17] -= dy;
                metadata[18] -= dx;
                metadata[19] -= dy;
                break;
            case NetworkConst.TRACK_CURVED:
                metadata[0] -= dx;
                metadata[1] -= dy;
                metadata[6] -= dx;
                metadata[7] -= dy;
                metadata[8] -= dx;
                metadata[9] -= dy;
                metadata[10] -= dx;
                metadata[11] -= dy;
                break;

            }
            this.metadata = metadata;
        }

        this.length = tr.getLength();
    }

    /**
     * Imports the connections to the vertices.
     * 
     * @param tr
     * @param world
     * @param vertexMapping 
     */
    public void importConnections(TrackRecord tr, World world, BiMap<Long, Long> vertexMapping) {
        long id1 = tr.getFirstVertex().getId();
        long id2 = tr.getSecondVertex().getId();
        if (id1 < IIdentifiable.NEUTRAL_ID) {
            id1 = vertexMapping.get(id1);
        }
        if (id2 < IIdentifiable.NEUTRAL_ID) {
            id2 = vertexMapping.get(id2);
        }
        this.v1 = world.findVertex(id1);
        this.v2 = world.findVertex(id2);
    }
}