package org.osm2world.core.world.modules; import static org.osm2world.core.math.GeometryUtil.*; import static org.osm2world.core.world.modules.common.WorldModuleGeometryUtil.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup; import org.osm2world.core.map_data.data.MapWaySegment; import org.osm2world.core.map_data.data.overlaps.MapIntersectionWW; import org.osm2world.core.map_elevation.data.EleConnector; import org.osm2world.core.map_elevation.data.GroundState; import org.osm2world.core.math.VectorXYZ; import org.osm2world.core.math.VectorXZ; import org.osm2world.core.target.RenderableToAllTargets; import org.osm2world.core.target.Target; import org.osm2world.core.target.common.material.Materials; import org.osm2world.core.world.data.WaySegmentWorldObject; import org.osm2world.core.world.data.WorldObject; import org.osm2world.core.world.modules.WaterModule.Water; import org.osm2world.core.world.modules.common.AbstractModule; import org.osm2world.core.world.modules.common.BridgeOrTunnel; import org.osm2world.core.world.network.AbstractNetworkWaySegmentWorldObject; /** * adds bridges to the world. * * Needs to be applied <em>after</em> all the modules that generate * whatever runs over the bridge. */ public class BridgeModule extends AbstractModule { public static final boolean isBridge(TagGroup tags) { return tags.containsKey("bridge") && !"no".equals(tags.getValue("bridge")); } public static final boolean isBridge(MapWaySegment segment) { return isBridge(segment.getTags()); } @Override protected void applyToWaySegment(MapWaySegment segment) { WaySegmentWorldObject primaryRepresentation = segment.getPrimaryRepresentation(); if (primaryRepresentation instanceof AbstractNetworkWaySegmentWorldObject && isBridge(segment)) { segment.addRepresentation(new Bridge(segment, (AbstractNetworkWaySegmentWorldObject) primaryRepresentation)); } } public static final double BRIDGE_UNDERSIDE_HEIGHT = 0.2f; private static class Bridge extends BridgeOrTunnel implements RenderableToAllTargets { public Bridge(MapWaySegment segment, AbstractNetworkWaySegmentWorldObject primaryWO) { super(segment, primaryWO); } @Override public GroundState getGroundState() { return GroundState.ABOVE; } @Override public Iterable<EleConnector> getEleConnectors() { // TODO EleConnectors for pillars return super.getEleConnectors(); } @Override public void renderTo(Target<?> target) { drawBridgeUnderside(target); drawBridgePillars(target); } private void drawBridgeUnderside(Target<?> target) { List<VectorXYZ> leftOutline = primaryRep.getOutline(false); List<VectorXYZ> rightOutline = primaryRep.getOutline(true); List<VectorXYZ> belowLeftOutline = sequenceAbove(leftOutline, -BRIDGE_UNDERSIDE_HEIGHT); List<VectorXYZ> belowRightOutline = sequenceAbove(rightOutline, -BRIDGE_UNDERSIDE_HEIGHT); List<VectorXYZ> strip1 = createTriangleStripBetween( belowLeftOutline, leftOutline); List<VectorXYZ> strip2 = createTriangleStripBetween( belowRightOutline, belowLeftOutline); List<VectorXYZ> strip3 = createTriangleStripBetween( rightOutline, belowRightOutline); target.drawTriangleStrip(Materials.BRIDGE_DEFAULT, strip1, null); target.drawTriangleStrip(Materials.BRIDGE_DEFAULT, strip2, null); target.drawTriangleStrip(Materials.BRIDGE_DEFAULT, strip3, null); } private void drawBridgePillars(Target<?> target) { List<VectorXZ> pillarPositions = equallyDistributePointsAlong( 2f, false, primaryRep.getStartPosition(), primaryRep.getEndPosition()); //make sure that the pillars doesn't pierce anything on the ground Collection<WorldObject> avoidedObjects = new ArrayList<WorldObject>(); for (MapIntersectionWW i : segment.getIntersectionsWW()) { for (WorldObject otherRep : i.getOther(segment).getRepresentations()) { if (otherRep.getGroundState() == GroundState.ON && !(otherRep instanceof Water) //TODO: choose better criterion! ) { avoidedObjects.add(otherRep); } } } filterWorldObjectCollisions(pillarPositions, avoidedObjects); //draw the pillars for (VectorXZ pos : pillarPositions) { drawBridgePillarAt(target, pos); } } private void drawBridgePillarAt(Target<?> target, VectorXZ pos) { /* determine the bridge elevation at that point */ VectorXYZ top = null; List<VectorXYZ> vs = primaryRep.getCenterline(); for (int i = 0; i + 1 < vs.size(); i++) { if (isBetween(pos, vs.get(i).xz(), vs.get(i+1).xz())) { top = interpolateElevation(pos, vs.get(i), vs.get(i+1)); } } /* draw the pillar */ // TODO: start pillar at ground instead of just 100 meters below the bridge target.drawColumn(Materials.BRIDGE_PILLAR_DEFAULT, null, top.addY(-100), 100, 0.2, 0.2, false, false); } } }