Package org.osm2world.core.world.modules

Source Code of org.osm2world.core.world.modules.PowerModule$PhotovoltaicPlant

package org.osm2world.core.world.modules;

import static java.lang.Math.PI;
import static java.util.Arrays.asList;
import static java.util.Collections.*;
import static org.osm2world.core.math.VectorXYZ.Z_UNIT;
import static org.osm2world.core.target.common.material.Materials.PLASTIC_GREY;
import static org.osm2world.core.target.common.material.NamedTexCoordFunction.STRIP_WALL;
import static org.osm2world.core.target.common.material.TexCoordUtil.texCoordLists;
import static org.osm2world.core.world.modules.common.WorldModuleGeometryUtil.*;
import static org.osm2world.core.world.modules.common.WorldModuleParseUtil.*;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.map_data.data.MapWaySegment;
import org.osm2world.core.map_data.data.overlaps.MapOverlap;
import org.osm2world.core.map_elevation.data.GroundState;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.LineSegmentXZ;
import org.osm2world.core.math.SimplePolygonXZ;
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.Material;
import org.osm2world.core.target.common.material.Materials;
import org.osm2world.core.world.data.AbstractAreaWorldObject;
import org.osm2world.core.world.data.NoOutlineNodeWorldObject;
import org.osm2world.core.world.data.NoOutlineWaySegmentWorldObject;
import org.osm2world.core.world.data.WorldObject;
import org.osm2world.core.world.data.WorldObjectWithOutline;
import org.osm2world.core.world.modules.common.AbstractModule;

/**
* module for power infrastructure
*/
public final class PowerModule extends AbstractModule {
   
  private static TowerConfig generateTowerConfig(MapNode node) {

    List<MapWaySegment> powerLines = new ArrayList<MapWaySegment>();
    for (MapWaySegment way : node.getConnectedWaySegments()) {
      if (way.getTags().contains("power", "line")) {
        powerLines.add(way);
      }
    }

    VectorXZ dir = VectorXZ.NULL_VECTOR;
    int cables = -1;
    int voltage = -1;
    for (MapWaySegment powerLine : powerLines) {
      dir = dir.add(powerLine.getDirection());
     
      try {
        cables = Integer.valueOf(powerLine.getTags().getValue("cables"));
      } catch (NumberFormatException e) {}
      try {
        voltage = Integer.valueOf(powerLine.getTags().getValue("voltage"));
      } catch (NumberFormatException e) {}
    }
    dir = dir.mult(1.0/powerLines.size());

    return new TowerConfig(node, cables, voltage, dir);
  }
 
  @Override
  protected void applyToNode(MapNode node) {
   
    if (node.getTags().contains("power", "cable_distribution_cabinet")) {
      node.addRepresentation(new PowerCabinet(node));
    }
   
    if (node.getTags().contains("power", "pole")) {
      node.addRepresentation(new Powerpole(node));
    }
    if (node.getTags().contains("power", "generator")
      && node.getTags().contains("generator:source", "wind")) {
      node.addRepresentation(new WindTurbine(node));
    }
   
    if (node.getTags().contains("power", "tower")) {

      TowerConfig config = generateTowerConfig(node);
     
      if (node.getPrimaryRepresentation() == null) {
        if (config.isHighVoltagePowerTower()) {
          node.addRepresentation(new HighVoltagePowerTower(node, config));
        } else {
          node.addRepresentation(new PowerTower(node, config));
        }
      }
    }
  }
 
  @Override
  protected void applyToWaySegment(MapWaySegment segment) {
    if (segment.getTags().contains("power", "minor_line")) {
      segment.addRepresentation(new PowerMinorLine(segment));
    }
   
    if (segment.getTags().contains("power", "line")) {
      segment.addRepresentation(new PowerLine(segment));
    }
  }
 
  @Override
  protected void applyToArea(MapArea area) {
    if (area.getTags().contains("power", "generator")
        && area.getTags().contains("generator:method", "photovoltaic")) {
      area.addRepresentation(new PhotovoltaicPlant(area));
    }
  }
 
  private static final class PowerCabinet extends NoOutlineNodeWorldObject
  implements RenderableToAllTargets {

    public PowerCabinet(MapNode node) {
      super(node);
    }
   
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }
   
    @Override
    public void renderTo(Target<?> target) {
     
      double directionAngle = parseDirection(node.getTags(), PI);
      VectorXZ faceVector = VectorXZ.fromAngle(directionAngle);
           
      target.drawBox(PLASTIC_GREY, getBase(),
          faceVector, 1.5, 0.8, 0.3);
     
    }
   
    }
 
  private final static class TowerConfig {
    MapNode pos;
    int cables;
    int voltage;
    VectorXZ direction;
   
    public TowerConfig(MapNode pos, int cables, int voltage, VectorXZ direction) {
      super();
      this.pos = pos;
      this.cables = cables;
      this.voltage = voltage;
      this.direction = direction;
    }
   
    public boolean isHighVoltagePowerTower() {
      return voltage >= 50000 || cables >= 6;
    }
  }
 
  private static final class Powerpole extends NoOutlineNodeWorldObject
      implements RenderableToAllTargets {
   
    public Powerpole(MapNode node) {
      super(node);
    }
   
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }
   
    @Override
    public void renderTo(Target<?> target) {
     
      /* determine material */
     
      Material material = null;
     
      //TODO parse color
     
      if (material == null) {
        material = Materials.getSurfaceMaterial(
            node.getTags().getValue("material"));
      }
       
      if (material == null) {
        material = Materials.getSurfaceMaterial(
            node.getTags().getValue("surface"), Materials.WOOD);
      }
     
      target.drawColumn(material, null, getBase(),
          parseHeight(node.getTags(), 8f),
          0.15, 0.15, false, true);
    }
   
  }
 
  private static final class WindTurbine extends NoOutlineNodeWorldObject
      implements RenderableToAllTargets {
   
    public WindTurbine(MapNode node) {
      super(node);
    }
   
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }
   
    @Override
    public void renderTo(Target<?> target) {
     
      float poleHeight = parseHeight(node.getTags(), 100f);
      float poleRadiusBottom = parseWidth(node.getTags(), 5) / 2;
      float poleRadiusTop = poleRadiusBottom / 2;
      float nacelleHeight = poleHeight * 0.05f;
      float nacelleDepth = poleHeight * 0.1f;
      float bladeLength = poleHeight / 2;
     
      /* determine material */
     
      Material poleMaterial = null;
      Material nacelleMaterial = Materials.STEEL;
      Material bladeMaterial = Materials.STEEL; // probably fibre, but color matches roughly :)
     
      //TODO parse color
     
      if (poleMaterial == null) {
        poleMaterial = Materials.getSurfaceMaterial(
            node.getTags().getValue("material"));
      }
       
      if (poleMaterial == null) {
        poleMaterial = Materials.getSurfaceMaterial(
            node.getTags().getValue("surface"), Materials.STEEL);
      }
     
      /* draw pole */
      target.drawColumn(poleMaterial, null,
          getBase(),
          poleHeight,
          poleRadiusBottom, poleRadiusTop, false, false);
     
      /* draw nacelle */
      VectorXZ nacelleVector = VectorXZ.X_UNIT;
      target.drawBox(nacelleMaterial,
          getBase().addY(poleHeight).add(nacelleDepth/2 - poleRadiusTop*2, 0f, 0f),
          nacelleVector, nacelleHeight, nacelleHeight, nacelleDepth);
     
      /* draw blades */
     
      // define first blade
      List<VectorXYZ> bladeFront = asList(
        getBase().addY(poleHeight).add(-poleRadiusTop*2, nacelleHeight/2, +nacelleHeight/2),
        getBase().addY(poleHeight).add(-poleRadiusTop*2, nacelleHeight/2 - bladeLength, 0f),
        getBase().addY(poleHeight).add(-poleRadiusTop*2, nacelleHeight/2, -nacelleHeight/2)
      );
      List<VectorXYZ> bladeBack = asList(
        bladeFront.get(0),
        bladeFront.get(2),
        bladeFront.get(1)
      );
     
      // rotate and draw blades
      double rotCenterY = getBase().y + poleHeight + nacelleHeight/2;
      double rotCenterZ = node.getPos().getZ();
     
      bladeFront = rotateShapeX(bladeFront, 60, rotCenterY, rotCenterZ);
      bladeBack  = rotateShapeX(bladeBack, 60, rotCenterY, rotCenterZ);
      target.drawTriangleStrip(bladeMaterial, bladeFront, null);
      target.drawTriangleStrip(bladeMaterial, bladeBack, null);
      bladeFront = rotateShapeX(bladeFront, 120, rotCenterY, rotCenterZ);
      bladeBack  = rotateShapeX(bladeBack, 120, rotCenterY, rotCenterZ);
      target.drawTriangleStrip(bladeMaterial, bladeFront, null);
      target.drawTriangleStrip(bladeMaterial, bladeBack, null);
      bladeFront = rotateShapeX(bladeFront, 120, rotCenterY, rotCenterZ);
      bladeBack  = rotateShapeX(bladeBack, 120, rotCenterY, rotCenterZ);
      target.drawTriangleStrip(bladeMaterial, bladeFront, null);
      target.drawTriangleStrip(bladeMaterial, bladeBack, null);
     
    }
   
  }
 
  private static class PowerMinorLine
    extends NoOutlineWaySegmentWorldObject
    implements RenderableToAllTargets {
   
    private static final float DEFAULT_THICKN = 0.05f; // width and height
    private static final float DEFAULT_CLEARING_BL = 7.5f; // power pole height is 8
    private static final Material material = Materials.PLASTIC;
   
    public PowerMinorLine(MapWaySegment segment) {
      super(segment);
    }
   
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }
   
    @Override
    public void renderTo(Target<?> target) {
     
      List<VectorXYZ> powerlineShape = asList(
        new VectorXYZ(-DEFAULT_THICKN/2, DEFAULT_CLEARING_BL, 0),
        new VectorXYZ(-DEFAULT_THICKN/2, DEFAULT_CLEARING_BL + DEFAULT_THICKN, 0),
        new VectorXYZ(+DEFAULT_THICKN/2, DEFAULT_CLEARING_BL + DEFAULT_THICKN, 0),
        new VectorXYZ(+DEFAULT_THICKN/2, DEFAULT_CLEARING_BL, 0)
      );
     
      List<VectorXYZ> path = getBaseline();
     
      List<List<VectorXYZ>> strips = createShapeExtrusionAlong(
          powerlineShape,
          path,
          nCopies(path.size(), VectorXYZ.Y_UNIT));
     
      for (List<VectorXYZ> strip : strips) {
        target.drawTriangleStrip(material, strip, null);
      }
     
    }
   
  }

  private final static class PowerLine
    extends NoOutlineWaySegmentWorldObject
    implements RenderableToAllTargets {

    private static final float CABLE_THICKNESS = 0.05f;
    // TODO: we need black plastic for cable material
    private final static Material CABLE_MATERIAL = Materials.PLASTIC;
    private static final double SLACK_SPAN = 6;
    private static final double INTERPOLATION_STEPS = 10;
    // TODO: Once createShapeExtrusionAlong supports arbitrary shapes, we want to switch to a circle instead of a polygon
    private static final double diag = Math.sqrt(2)*CABLE_THICKNESS/2;
    private static final List<VectorXYZ> powerlineShape = asList(
        new VectorXYZ(-diag, 0, 0),
        new VectorXYZ(-CABLE_THICKNESS/2, CABLE_THICKNESS/2, 0),
        new VectorXYZ(0, diag, 0),
        new VectorXYZ(CABLE_THICKNESS/2, CABLE_THICKNESS/2, 0),
        new VectorXYZ(diag, 0, 0),
        new VectorXYZ(+CABLE_THICKNESS/2, -CABLE_THICKNESS/2, 0),
        new VectorXYZ(0, -diag, 0),
        new VectorXYZ(-CABLE_THICKNESS/2, -CABLE_THICKNESS/2, 0),
        new VectorXYZ(-diag, 0, 0)
      );
   
    private int cables = -1;
    private int voltage = -1;
    private TowerConfig start;
    private TowerConfig end;
    private List<VectorXYZ> startPos = null;
    private List<VectorXYZ> endPos = null;
   
   
    public PowerLine(MapWaySegment line) {
      super(line);
    }
   
    private void addPos(VectorXYZ baseStart, VectorXYZ baseEnd, double gotoRight, double up) {
      startPos.add(baseStart.add(start.direction.rightNormal().mult(gotoRight)).add(0, up, 0));
      endPos.add(baseEnd.add(end.direction.rightNormal().mult(gotoRight)).add(0, up, 0));
    }

    private void addPos(VectorXYZ baseStart, VectorXYZ baseEnd, double gotoRight, double upStart, double upEnd) {
      startPos.add(baseStart.add(start.direction.rightNormal().mult(gotoRight)).add(0, upStart, 0));
      endPos.add(baseEnd.add(end.direction.rightNormal().mult(gotoRight)).add(0, upEnd, 0));
    }

    private void setup() {
     
      startPos = new ArrayList<VectorXYZ>();
      endPos = new ArrayList<VectorXYZ>();
     
      // check number of power lines
      try {
        cables = Integer.valueOf(segment.getTags().getValue("cables"));
      } catch (NumberFormatException e) {}

      // check voltage
      try {
        voltage = Integer.valueOf(segment.getTags().getValue("voltage"));
      } catch (NumberFormatException e) {}

      if (cables <= 0) {
        return;
      }

      // get the tower configurations for start and end tower
      start = generateTowerConfig(segment.getStartNode());
      end = generateTowerConfig(segment.getEndNode());
           
      if (!start.isHighVoltagePowerTower() && !end.isHighVoltagePowerTower()) {

        // normal PowerTower...

        double startHeight = parseHeight(start.pos.getTags(), 14) + 0.25;
        double endHeight = parseHeight(end.pos.getTags(), 14) + 0.25;
       
        VectorXYZ baseStart = getStartXYZ().addY(startHeight - 0.5);
        VectorXYZ baseEnd = getEndXYZ().addY(endHeight - 0.5);
     
        // power lines at the top left and right
        addPos(baseStart, baseEnd, 2, 0.5);
        addPos(baseStart, baseEnd, -2, 0.5);
       
        if (cables >= 3) {
          // additional power line at the top center
          addPos(baseStart, baseEnd, 0, 0.5);
        }
        if (cables >= 5) {
          // further power lines at the left and right below the column
          addPos(baseStart, baseEnd, 1.5, -0.5);
          addPos(baseStart, baseEnd, -1.5, -0.5);
        }
      } else {

        // High voltage PowerTower ...
       
        float default_height = voltage > 150000 ? 40 : 30;
        float pole_width = voltage > 150000 ? 16 : 13;
       
        double startHeight = parseHeight(start.pos.getTags(), default_height);
        double endHeight = parseHeight(end.pos.getTags(), default_height);

        double heightS = 2.5 * (((int) (startHeight / 2.5)) / 5);
        double heightE = 2.5 * (((int) (endHeight / 2.5)) / 5);

        VectorXYZ baseStart = getStartXYZ().addY(-0.5);
        VectorXYZ baseEnd = getEndXYZ().addY(-0.5);

        // power line at the tower's top
        addPos(baseStart, baseEnd, 0, 5*heightS, 5*heightE);

        // power lines start a little bit below the tower's columns
        baseStart = baseStart.add(0, -0.2, 0);
        baseEnd = baseEnd.add(0, -0.2, 0);

        // power lines at the base column
        addPos(baseStart, baseEnd, 0.9*pole_width, startHeight/2, endHeight/2);
        addPos(baseStart, baseEnd, -0.9*pole_width, startHeight/2, endHeight/2);
        if ((cables > 3) && (cables <= 9)) {
          addPos(baseStart, baseEnd, 0.45*pole_width, startHeight/2, endHeight/2);
          addPos(baseStart, baseEnd, -0.45*pole_width, startHeight/2, endHeight/2);
        } else if (cables > 9) {
          addPos(baseStart, baseEnd, 0.6*pole_width, startHeight/2, endHeight/2);
          addPos(baseStart, baseEnd, -0.6*pole_width, startHeight/2, endHeight/2);
          addPos(baseStart, baseEnd, 0.3*pole_width, startHeight/2, endHeight/2);
          addPos(baseStart, baseEnd, -0.3*pole_width, startHeight/2, endHeight/2);
        }

        // additional power lines at the upper column
        if (cables >= 7) {
          addPos(baseStart, baseEnd, 0.9*0.6*pole_width, 4*heightS, 4*heightE);
          addPos(baseStart, baseEnd, -0.9*0.6*pole_width, 4*heightS, 4*heightE);
          if (cables >= 9) {
            addPos(baseStart, baseEnd, 0.45*0.6*pole_width, 4*heightS, 4*heightE);
            addPos(baseStart, baseEnd, -0.45*0.6*pole_width, 4*heightS, 4*heightE);
          }
        }
      }
    }
   
    @Override
    public GroundState getGroundState() {
      return GroundState.ABOVE;
    }
   
    @Override
    public void renderTo(Target<?> target) {
     
      // do initial setup for height and position calculation, if necessary
      if (startPos == null) {
        setup();
      }
     
      for (int i = 0; i < startPos.size(); i++) {

        VectorXYZ start = startPos.get(i);
        VectorXYZ end = endPos.get(i);
       
        double lenToEnd = end.distanceToXZ(start);
        double heightDiff = end.y - start.y;

        double stepSize = lenToEnd / INTERPOLATION_STEPS;
        VectorXZ dir = end.xz().subtract(start.xz()).normalize();
       
        List<VectorXYZ> path = new ArrayList<VectorXYZ>();
        for (int x = 0; x <= INTERPOLATION_STEPS; x++) {
          double ratio = x / INTERPOLATION_STEPS;
         
          // distance from start to position x
          double dx = stepSize * x;
         
          // use a simple parabola between two towers
          double height = (1 - Math.pow(2.0*(ratio - 0.5), 2)) * -SLACK_SPAN;
          // add a linear function to account for different tower/terrain heights
          height += ratio * heightDiff;
         
          path.add(start.add(dir.mult(dx)).add(0, height, 0));
        }
       
        List<List<VectorXYZ>> strips = createShapeExtrusionAlong(
            powerlineShape, path,
            nCopies(path.size(), VectorXYZ.Y_UNIT));
       
        for (List<VectorXYZ> strip : strips) {
          target.drawTriangleStrip(CABLE_MATERIAL, strip, null);
        }
      }
    }
   
  }
 
 
  private static final class PowerTower extends NoOutlineNodeWorldObject
    implements RenderableToAllTargets {

    private TowerConfig config;
 
    public PowerTower(MapNode node, TowerConfig config) {
      super(node);
      this.config = config;
    }
 
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }

    // TODO we're missing the ceramics to hold the power lines

    @Override
    public void renderTo(Target<?> target) {

      VectorXYZ base = getBase().addY(-0.5);
      double height = parseHeight(node.getTags(), 14);

      Material material = Materials.getSurfaceMaterial(node.getTags().getValue("material"));
      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("surface"), Materials.STEEL);
      }
     
      // draw base column
      target.drawColumn(material, null, base, height, 0.5, 0.25, true, true);
     
      // draw cross "column"
      target.drawBox(material, base.add(0, height, 0), config.direction, 0.25, 5, 0.25);
   
      // draw pieces holding the power lines
      base = base.add(0, height + 0.25, 0);
      target.drawColumn(Materials.CONCRETE, null, base.add(config.direction.rightNormal().mult(2)), 0.5, 0.1, 0.1, true, true);
      target.drawColumn(Materials.CONCRETE, null, base.add(config.direction.rightNormal().mult(-2)), 0.5, 0.1, 0.1, true, true);
      if (config.cables >= 3) {
        target.drawColumn(Materials.CONCRETE, null, base, 0.5, 0.1, 0.1, true, true);
      }
      if (config.cables >= 5) {
        target.drawColumn(Materials.CONCRETE, null, base.add(config.direction.rightNormal().mult(1.5)), -0.5, 0.1, 0.1, true, true);
        target.drawColumn(Materials.CONCRETE, null, base.add(config.direction.rightNormal().mult(-1.5)), -0.5, 0.1, 0.1, true, true);
      }
    }
  }


  private static final class HighVoltagePowerTower extends NoOutlineNodeWorldObject
    implements RenderableToAllTargets {

    private TowerConfig config;
    private VectorXZ direction;
   
    public HighVoltagePowerTower(MapNode node, TowerConfig config) {
      super(node);
      this.config = config;
      this.direction = config.direction;
    }
 
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }

    /**
     * parse height tag or provide a reasonable default based on the tower's voltage
     */
    private double getTowerHeight() {

      float default_height = config.voltage > 150000 ? 40 : 30;
      double height = parseHeight(node.getTags(), default_height);

      return height;
    }
   
    private VectorXZ[][] getCorners(VectorXZ center, double diameter) {
      double half = diameter/2;
      VectorXZ ortho = direction.rightNormal();
     
      VectorXZ right_in = center.add(direction.mult(half));
      VectorXZ left_in = center.add(direction.mult(-half));
      VectorXZ right_out = center.add(direction.mult(half));
      VectorXZ left_out = center.add(direction.mult(-half));

      // TODO: if we can switch off backface culling we'd only need one face here
      return new VectorXZ[][]{
          new VectorXZ[]{
              right_in.add(ortho.mult(-half)),
              right_in.add(ortho.mult(half)),
              left_in.add(ortho.mult(half)),
              left_in.add(ortho.mult(-half))
          },
          new VectorXZ[]{
              right_out.add(ortho.mult(half)),
              right_out.add(ortho.mult(-half)),
              left_out.add(ortho.mult(-half)),
              left_out.add(ortho.mult(half))
          }};
    }
 
    private void drawSegment(Target<?> target,
        VectorXZ[] low, VectorXZ[] high, double base, double height) {
     
      for (int a = 0; a < 4; a++) {
     
        List<VectorXYZ> vs = new ArrayList<VectorXYZ>();
        List<VectorXZ> tex = new ArrayList<VectorXZ>();
        List<List<VectorXZ>> texList =
          nCopies(Materials.POWER_TOWER_VERTICAL.getNumTextureLayers(), tex);
       
        for (int i = 0; i < 2; i++) {
          int idx = (a+i)%4;
          vs.add(high[idx].xyz(height));
          vs.add(low[idx].xyz(base));
          tex.add(new VectorXZ(i, 1));
          tex.add(new VectorXZ(i, 0));
        }
       
        target.drawTriangleStrip(Materials.POWER_TOWER_VERTICAL, vs, texList);
      }
    }
 
    private void drawHorizontalSegment(Target<?> target,
        VectorXZ left, VectorXZ right, double base,
        double left_height, double right_height) {

      List<VectorXYZ> vs = new ArrayList<VectorXYZ>();
      List<VectorXZ> tex = new ArrayList<VectorXZ>();
      List<List<VectorXZ>> texList =
          nCopies(Materials.POWER_TOWER_HORIZONTAL.getNumTextureLayers(), tex);
   
      vs.add(right.xyz(base));
      vs.add(left.xyz(base));
      vs.add(right.xyz(base+right_height));
      vs.add(left.xyz(base+left_height));
   
      tex.add(new VectorXZ(1, 1));
      tex.add(new VectorXZ(0, 1));
      tex.add(new VectorXZ(1, 0));
      tex.add(new VectorXZ(0, 0));
   
      target.drawTriangleStrip(Materials.POWER_TOWER_HORIZONTAL, vs, texList);
    }

    private void drawHorizontalTop(Target<?> target, VectorXZ[][] points,
        double base, double border, double middle, double center) {
     
      double[] height = new double[]{border, middle, center, center, middle, border};
     
      int len = height.length;
      for (int a = 0; a < len-1; a++) {
     
        List<VectorXYZ> vs = new ArrayList<VectorXYZ>();
        List<VectorXZ> tex = new ArrayList<VectorXZ>();
        List<List<VectorXZ>> texList =
            nCopies(Materials.POWER_TOWER_VERTICAL.getNumTextureLayers(), tex);

        for (int i = 0; i < 2; i++) {
          vs.add(points[1][a+i].xyz(base + height[a+i]));
          vs.add(points[2][a+i].xyz(base + height[a+i]));
          tex.add(new VectorXZ(0, i));
          tex.add(new VectorXZ(1, i));
        }
        target.drawTriangleStrip(Materials.POWER_TOWER_VERTICAL, vs, texList);
      }
    }
   
   
    private double drawPart(Target<?> target, double elevation,
        int nr_segments, double segment_height, double ground_size,
        double top_size) {

      for (int i = 0; i < nr_segments; i++) {

        double bottom = ground_size + i * (top_size - ground_size) / nr_segments;
        double top = ground_size + (i + 1) * (top_size - ground_size) / nr_segments;

        VectorXZ[][] low = getCorners(node.getPos(), bottom);
        VectorXZ[][] high = getCorners(node.getPos(), top);

        drawSegment(target, low[0], high[0], elevation, elevation + segment_height);
        drawSegment(target, low[1], high[1], elevation, elevation + segment_height);

        elevation += segment_height;
      }

      return elevation;
    }

    private VectorXZ[] getPoleCoordinates(VectorXZ base,
        VectorXZ direction, double width, double size) {
     
      return new VectorXZ[] {
          base.add(direction.mult(-width - size)),
          base.add(direction.mult(-width / 2 - size)),
          base.add(direction.mult(-size)),
          base.add(direction.mult(size)),
          base.add(direction.mult(width / 2 + size)),
          base.add(direction.mult(width + size))
      };
    }

    private void drawHorizontalPole(Target<?> target, double elevation,
        double diameter, double width) {

      double half = diameter / 2;
      VectorXZ ortho = direction.rightNormal();

      // TODO: if we can switch off backface culling we'd only need one face here
      VectorXZ[][] draw = new VectorXZ[][] {
          getPoleCoordinates(node.getPos().add(direction.mult(-half)), ortho, width, half),
          getPoleCoordinates(node.getPos().add(direction.mult(-half)), ortho.invert(), width, half),
          getPoleCoordinates(node.getPos().add(direction.mult(half)), ortho.invert(), width, half),
          getPoleCoordinates(node.getPos().add(direction.mult(half)), ortho, width, half)
      };

      for (int i = 0; i < 4; i++) {
        drawHorizontalSegment(target, draw[i][0], draw[i][1], elevation, 0.1, diameter/2);
        drawHorizontalSegment(target, draw[i][1], draw[i][2], elevation, diameter/2, diameter);
        drawHorizontalSegment(target, draw[i][2], draw[i][3], elevation, diameter, diameter);
        drawHorizontalSegment(target, draw[i][3], draw[i][4], elevation, diameter, diameter/2);
        drawHorizontalSegment(target, draw[i][4], draw[i][5], elevation, diameter/2, 0.1);
      }
     
      drawHorizontalTop(target, draw, elevation, 0.1, diameter/2, diameter);
    }

    // TODO we're missing the ceramics to hold the power lines
   
    @Override
    public void renderTo(Target<?> target) {

      float pole_width = config.voltage > 150000 ? 16 : 13;
      float[] tower_width = config.voltage > 150000 ? new float[]{11,6,4f,0} : new float[]{8,5,3,0};
      double height = getTowerHeight();

      double segment_height = 2.5;
      double base = getBase().y - 0.5;

      int parts = (int) (height / segment_height);
      int low_parts = parts / 5;

      // draw the tower itself
      double ele = drawPart(target, base, low_parts, segment_height, tower_width[0], tower_width[1]);
      ele = drawPart(target, ele, 3 * low_parts, segment_height, tower_width[1], tower_width[2]);
      drawPart(target, ele, low_parts, segment_height, tower_width[2], tower_width[3]);

      // draw the vertical poles
      drawHorizontalPole(target, base + height/2, 0.7*tower_width[1], pole_width);
      if (config.cables > 6) {
        drawHorizontalPole(target, ele, 0.55*tower_width[2], 0.6*pole_width);
      }
    }
  }
 
  private static final class PhotovoltaicPlant extends AbstractAreaWorldObject
    implements RenderableToAllTargets {

    //TODO create individual EleConnector for panels
   
    /** compares vectors by x coordinate */
    private static final Comparator<VectorXZ> X_COMPARATOR = new Comparator<VectorXZ>() {
      @Override public int compare(VectorXZ v1, VectorXZ v2) {
        return Double.compare(v1.x, v2.x);
      }
    };

    protected PhotovoltaicPlant(MapArea area) {
      super(area);
    }
   
    @Override
    public GroundState getGroundState() {
      return GroundState.ON;
    }
   
    @Override
    public void renderTo(Target<?> target) {
     
      /* construct panel geometry */

      double panelAngle = PI / 4;
      double panelHeight = 5;
     
      VectorXYZ upVector = Z_UNIT.mult(panelHeight).rotateX(-panelAngle);
           
      /* place and draw rows of panels */
     
      AxisAlignedBoundingBoxXZ box = this.getAxisAlignedBoundingBoxXZ();
     
      List<SimplePolygonXZ> obstacles = getGroundObstacles();
     
      double posZ = box.minZ;
     
      while (posZ + upVector.z < box.maxZ) {
       
        LineSegmentXZ rowLine = new LineSegmentXZ(
             new VectorXZ(box.minX - 10, posZ),
             new VectorXZ(box.maxX + 10, posZ));
       
        // calculate start and end points (maybe more than one each)
       
        List<VectorXZ> intersections =
            area.getPolygon().intersectionPositions(rowLine);
       
        assert intersections.size() % 2 == 0;
       
        sort(intersections, X_COMPARATOR);
       
        // add more start/end points at ground-level obstacles
       
        for (SimplePolygonXZ obstacle : obstacles) {
         
          List<VectorXZ> obstacleIntersections =
              obstacle.intersectionPositions(rowLine);
         
          for (int i = 0; i + 1 < obstacleIntersections.size(); i += 2) {
           
            int insertionIndexA = -binarySearch(intersections,
                obstacleIntersections.get(i), X_COMPARATOR) - 1;
            int insertionIndexB = -binarySearch(intersections,
                obstacleIntersections.get(i + 1), X_COMPARATOR) - 1;
           
            sort(obstacleIntersections, X_COMPARATOR);
           
            if (insertionIndexA == insertionIndexB
                && insertionIndexA >= 0
                && insertionIndexA % 2 == 1) {
             
              intersections.add(insertionIndexA,
                  obstacleIntersections.get(i+1));
              intersections.add(insertionIndexA,
                  obstacleIntersections.get(i));
             
            }
           
          }
         
        }
       
        // draw row of panels between each start/end pair
       
        assert intersections.size() % 2 == 0;
       
        for (int i = 0; i + 1 < intersections.size(); i += 2) {
         
          // TODO: take elevation into account
          // Might necessitate individual panels or shorter strips.
         
//          renderPanelsTo(target,
//              eleProfile.getWithEle(intersections.get(i)),
//              eleProfile.getWithEle(intersections.get(i+1)),
//              upVector);
         
        }
       
        posZ += upVector.z * 1.5;
       
      }
     
    }

    /**
     * returns outlines from ground objects overlapping this area
     */
    private List<SimplePolygonXZ> getGroundObstacles() {
     
      List<SimplePolygonXZ> obstacles = new ArrayList<SimplePolygonXZ>();
     
      for (MapOverlap<?, ?> overlap : area.getOverlaps()) {
        for (WorldObject otherWO : overlap.getOther(area).getRepresentations()) {
         
          if (otherWO.getGroundState() == GroundState.ON
              && otherWO instanceof WorldObjectWithOutline) {
           
            obstacles.add(((WorldObjectWithOutline)otherWO).getOutlinePolygonXZ());
           
          }
       
        }
      }
     
      return obstacles;
     
    }
   
    private void renderPanelsTo(Target<?> target, VectorXYZ bottomLeft,
        VectorXYZ bottomRight, VectorXYZ upVector) {
     
      /* draw front */
     
      List<VectorXYZ> vs = asList(
          bottomLeft.add(upVector),
          bottomLeft,
          bottomRight.add(upVector),
          bottomRight);
     
      target.drawTriangleStrip(Materials.SOLAR_PANEL, vs,
          texCoordLists(vs, Materials.SOLAR_PANEL, STRIP_WALL));
     
      /* draw back */
     
      vs = asList(vs.get(2), vs.get(3), vs.get(0), vs.get(1));
     
      target.drawTriangleStrip(Materials.PLASTIC_GREY, vs,
          texCoordLists(vs, Materials.PLASTIC_GREY, STRIP_WALL));
           
    }
   
   
  }
 
}
TOP

Related Classes of org.osm2world.core.world.modules.PowerModule$PhotovoltaicPlant

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.