Search in sources :

Example 6 with Point

use of org.poly2tri.geometry.primitives.Point in project energy3d by concord-consortium.

the class MeshLib method fillMeshWithPolygon.

// private static boolean isAlmost180(final ReadOnlyVector3 p1, final ReadOnlyVector3 p2, final ReadOnlyVector3 p3) {
// return Math.abs(p1.subtract(p2, null).normalizeLocal().smallestAngleBetween(p3.subtract(p1, null).normalizeLocal())) > Math.PI - Math.PI / 180.0;
// }
public static void fillMeshWithPolygon(final Mesh mesh, final PolygonWithHoles polygon, final CoordinateTransform fromXY, final boolean generateNormals, final TPoint o, final TPoint u, final TPoint v, final boolean isWall) {
    /* round all points */
    for (final Point p : polygon.getPoints()) {
        p.set(Util.round(p.getX()), Util.round(p.getY()), Util.round(p.getZ()));
    }
    if (polygon.getHoles() != null) {
        for (final Polygon hole : polygon.getHoles()) {
            for (final Point p : hole.getPoints()) {
                p.set(Util.round(p.getX()), Util.round(p.getY()), Util.round(p.getZ()));
            }
        }
    }
    /* remove holes that collide with polygon or other holes */
    if (polygon.getHoles() != null) {
        // ensure polygon doesn't collide with holes
        final Path2D polygonPath = Util.makePath2D(polygon.getPoints());
        final Map<Polygon, Object> skipHoles = new HashMap<Polygon, Object>();
        for (final Polygon hole : polygon.getHoles()) {
            for (final Point p : hole.getPoints()) {
                if (!polygonPath.contains(new Point2D.Double(p.getX(), p.getY()))) {
                    skipHoles.put(hole, null);
                    break;
                }
            }
        }
        // ensure holes don't collide with each other
        for (int i = 0; i < polygon.getHoles().size(); i++) {
            final Polygon hole1 = polygon.getHoles().get(i);
            if (skipHoles.containsKey(hole1)) {
                continue;
            }
            for (int j = i + 1; j < polygon.getHoles().size(); j++) {
                final Polygon hole2 = polygon.getHoles().get(j);
                if (skipHoles.containsKey(hole2)) {
                    continue;
                }
                boolean found = false;
                for (final Point p : hole2.getPoints()) {
                    if (Util.insidePolygon(p, hole1.getPoints())) {
                        skipHoles.put(hole2, null);
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    final int n1 = hole1.getPoints().size();
                    for (int i1 = 0; i1 < n1; i1++) {
                        final Point l1p1 = hole1.getPoints().get(i1);
                        final Point l1p2 = hole1.getPoints().get((i1 + 1) % n1);
                        final Line2D line1 = new Line2D.Double(l1p1.getX(), l1p1.getY(), l1p2.getX(), l1p2.getY());
                        found = false;
                        final int n2 = hole2.getPoints().size();
                        for (int i2 = 0; i2 < n2; i2++) {
                            final Point l2p1 = hole2.getPoints().get(i2);
                            final Point l2p2 = hole2.getPoints().get((i2 + 1) % n2);
                            final Line2D line2 = new Line2D.Double(l2p1.getX(), l2p1.getY(), l2p2.getX(), l2p2.getY());
                            if (line2.intersectsLine(line1)) {
                                skipHoles.put(hole2, null);
                                found = true;
                                break;
                            }
                        }
                        if (found) {
                            break;
                        }
                    }
                }
            }
        }
        for (final Polygon hole : skipHoles.keySet()) {
            polygon.getHoles().remove(hole);
        }
    }
    try {
        Poly2Tri.triangulate(polygon);
        if (fromXY == null) {
            ArdorMeshMapper.updateTriangleMesh(mesh, polygon);
        } else {
            ArdorMeshMapper.updateTriangleMesh(mesh, polygon, fromXY);
        }
        if (generateNormals) {
            if (fromXY == null) {
                ArdorMeshMapper.updateFaceNormals(mesh, polygon.getTriangles());
            } else {
                ArdorMeshMapper.updateFaceNormals(mesh, polygon.getTriangles(), fromXY);
            }
        }
        if (o != null) {
            ArdorMeshMapper.updateTextureCoordinates(mesh, polygon.getTriangles(), 1.0, o, u, v);
        }
        mesh.getMeshData().updateVertexCount();
        mesh.updateModelBound();
    } catch (final RuntimeException e) {
        System.err.println("Points:");
        for (final Point p : polygon.getPoints()) {
            System.err.println(p);
        }
        System.err.println("Holes:");
        if (polygon.getHoles() != null) {
            for (final Polygon hole : polygon.getHoles()) {
                for (final Point p : hole.getPoints()) {
                    System.err.println(p);
                }
            }
        }
        throw e;
    }
}
Also used : HashMap(java.util.HashMap) Path2D(java.awt.geom.Path2D) PolygonPoint(org.poly2tri.geometry.polygon.PolygonPoint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Polygon(org.poly2tri.geometry.polygon.Polygon) Line2D(java.awt.geom.Line2D) CullHint(com.ardor3d.scenegraph.hint.CullHint) PolygonPoint(org.poly2tri.geometry.polygon.PolygonPoint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point)

Example 7 with Point

use of org.poly2tri.geometry.primitives.Point in project energy3d by concord-consortium.

the class Util method snapToPolygon.

public static Vector2 snapToPolygon(final ReadOnlyVector3 point, final List<ReadOnlyVector3> polygon, final List<ReadOnlyVector3> wallNormals) {
    final Vector2 p = new Vector2(point.getX(), point.getY());
    final Vector2 l1 = new Vector2();
    final Vector2 l2 = new Vector2();
    double shortestDistance = Double.MAX_VALUE;
    Vector2 closestPoint = null;
    ReadOnlyVector3 closestNormal = null;
    final int n = polygon.size();
    for (int i = 0; i < n; i++) {
        final ReadOnlyVector3 pp1 = polygon.get(i);
        l1.set(pp1.getX(), pp1.getY());
        final ReadOnlyVector3 pp2 = polygon.get((i + 1) % n);
        l2.set(pp2.getX(), pp2.getY());
        if (l1.distanceSquared(l2) > MathUtils.ZERO_TOLERANCE) {
            final Vector2 pointOnLine = projectPointOnLine(p, l1, l2, true);
            final double distance = pointOnLine.distanceSquared(p);
            if (distance < shortestDistance) {
                shortestDistance = distance;
                closestPoint = pointOnLine;
                if (wallNormals != null) {
                    if (l1.distanceSquared(closestPoint) <= l2.distanceSquared(pointOnLine)) {
                        closestNormal = wallNormals.get(i);
                    } else {
                        closestNormal = wallNormals.get((i + 1) % n);
                    }
                }
            }
        }
    }
    if (wallNormals != null) {
        closestPoint.addLocal(-closestNormal.getX() / 100.0, -closestNormal.getY() / 100.0);
    }
    return closestPoint;
}
Also used : ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) ReadOnlyVector2(com.ardor3d.math.type.ReadOnlyVector2) Vector2(com.ardor3d.math.Vector2) PickingHint(com.ardor3d.scenegraph.hint.PickingHint) Point(org.poly2tri.geometry.primitives.Point)

Example 8 with Point

use of org.poly2tri.geometry.primitives.Point in project energy3d by concord-consortium.

the class Util method makePath2D.

public static Path2D makePath2D(final List<? extends Point> polygon) {
    final Path2D path = new Path2D.Double();
    path.moveTo(polygon.get(0).getX(), polygon.get(0).getY());
    for (int i = 1; i < polygon.size(); i++) {
        final Point point = polygon.get(i);
        path.lineTo(point.getX(), point.getY());
    }
    path.closePath();
    return path;
}
Also used : Path2D(java.awt.geom.Path2D) Point(org.poly2tri.geometry.primitives.Point) PickingHint(com.ardor3d.scenegraph.hint.PickingHint) Point(org.poly2tri.geometry.primitives.Point)

Example 9 with Point

use of org.poly2tri.geometry.primitives.Point in project energy3d by concord-consortium.

the class SolarRadiation method computeOnSolarPanel.

// a solar panel typically has 6x10 cells, 6 and 10 are not power of 2 for texture. so we need some special handling here
private void computeOnSolarPanel(final int minute, final ReadOnlyVector3 directionTowardSun, final SolarPanel panel) {
    if (panel.getTracker() != SolarPanel.NO_TRACKER) {
        final Calendar calendar = Heliodon.getInstance().getCalendar();
        calendar.set(Calendar.HOUR_OF_DAY, (int) ((double) minute / (double) SolarRadiation.MINUTES_OF_DAY * 24.0));
        calendar.set(Calendar.MINUTE, minute % 60);
        panel.draw();
    }
    final ReadOnlyVector3 normal = panel.getNormal();
    if (normal == null) {
        throw new RuntimeException("Normal is null");
    }
    int nx = Scene.getInstance().getSolarPanelNx();
    int ny = Scene.getInstance().getSolarPanelNy();
    final Mesh drawMesh = panel.getRadiationMesh();
    final Mesh collisionMesh = (Mesh) panel.getRadiationCollisionSpatial();
    MeshDataStore data = onMesh.get(drawMesh);
    if (data == null) {
        data = initMeshTextureDataOnRectangle(drawMesh, nx, ny);
    }
    final ReadOnlyVector3 offset = directionTowardSun.multiply(1, null);
    final double dot = normal.dot(directionTowardSun);
    double directRadiation = 0;
    if (dot > 0) {
        directRadiation += calculateDirectRadiation(directionTowardSun, normal);
    }
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, normal);
    final FloatBuffer vertexBuffer = drawMesh.getMeshData().getVertexBuffer();
    // (0, 0)
    final Vector3 p0 = new Vector3(vertexBuffer.get(3), vertexBuffer.get(4), vertexBuffer.get(5));
    // (1, 0)
    final Vector3 p1 = new Vector3(vertexBuffer.get(6), vertexBuffer.get(7), vertexBuffer.get(8));
    // (0, 1)
    final Vector3 p2 = new Vector3(vertexBuffer.get(0), vertexBuffer.get(1), vertexBuffer.get(2));
    // this is the longer side (supposed to be y)
    final double d10 = p1.distance(p0);
    // this is the shorter side (supposed to be x)
    final double d20 = p2.distance(p0);
    final Vector3 p10 = p1.subtract(p0, null).normalizeLocal();
    final Vector3 p20 = p2.subtract(p0, null).normalizeLocal();
    // generate the heat map first. this doesn't affect the energy calculation, it just shows the distribution of solar radiation on the panel.
    // x and y must be swapped to have correct heat map texture, because nx represents rows and ny columns as we call initMeshTextureDataOnRectangle(mesh, nx, ny)
    double xSpacing = d10 / nx;
    double ySpacing = d20 / ny;
    Vector3 u = p10;
    Vector3 v = p20;
    final int iMinute = minute / Scene.getInstance().getTimeStep();
    for (int x = 0; x < nx; x++) {
        for (int y = 0; y < ny; y++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
            final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
            final ReadOnlyVector3 p = drawMesh.getWorldTransform().applyForward(p0.add(v2, null).addLocal(u2)).addLocal(offset);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
            double radiation = indirectRadiation;
            if (dot > 0) {
                final PickResults pickResults = new PrimitivePickResults();
                for (final Spatial spatial : collidables) {
                    if (spatial != collisionMesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    radiation += directRadiation;
                }
            }
            data.dailySolarIntensity[x][y] += radiation;
        }
    }
    if (panel.isRotated()) {
        // landscape
        nx = panel.getNumberOfCellsInY();
        ny = panel.getNumberOfCellsInX();
    } else {
        // portrait
        nx = panel.getNumberOfCellsInX();
        ny = panel.getNumberOfCellsInY();
    }
    // nx*ny*60: nx*ny is to get the unit cell area of the nx*ny grid; 60 is to convert the unit of timeStep from minute to kWh
    final double a = panel.getPanelWidth() * panel.getPanelHeight() * Scene.getInstance().getTimeStep() / (nx * ny * 60.0);
    // swap the x and y back to correct order
    xSpacing = d20 / nx;
    ySpacing = d10 / ny;
    u = p20;
    v = p10;
    if (cellOutputs == null || cellOutputs.length != nx || cellOutputs[0].length != ny) {
        cellOutputs = new double[nx][ny];
    }
    // calculate the solar radiation first without worrying about the underlying cell wiring and distributed efficiency
    for (int x = 0; x < nx; x++) {
        for (int y = 0; y < ny; y++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
            final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
            final ReadOnlyVector3 p = drawMesh.getWorldTransform().applyForward(p0.add(v2, null).addLocal(u2)).addLocal(offset);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
            double radiation = indirectRadiation;
            if (dot > 0) {
                final PickResults pickResults = new PrimitivePickResults();
                for (final Spatial spatial : collidables) {
                    if (spatial != collisionMesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    radiation += directRadiation;
                }
            }
            cellOutputs[x][y] = radiation * a;
        }
    }
    final double airTemperature = Weather.getInstance().getOutsideTemperatureAtMinute(dailyAirTemperatures[1], dailyAirTemperatures[0], minute);
    double syseff;
    double output;
    // cell temperature
    double tcell;
    // Tcell = Tair + (NOCT - 20) / 80 * R, where the unit of R is mW/cm^2
    final double noctFactor = (panel.getNominalOperatingCellTemperature() - 20.0) * 100.0 / (a * 80.0);
    // now consider cell wiring and distributed efficiency (Nice demo at: https://www.youtube.com/watch?v=UNPJapaZlCU)
    switch(panel.getShadeTolerance()) {
        case // the most ideal assumption that probably doesn't exist in reality (just keep it here in case someone has a breakthrough in the future)
        SolarPanel.HIGH_SHADE_TOLERANCE:
            for (int x = 0; x < nx; x++) {
                for (int y = 0; y < ny; y++) {
                    output = cellOutputs[x][y];
                    tcell = airTemperature + output * noctFactor;
                    syseff = panel.getSystemEfficiency(tcell);
                    panel.getSolarPotential()[iMinute] += output * syseff;
                }
            }
            break;
        case // all the cells are connected in a single series, so the total output is (easily) determined by the minimum
        SolarPanel.NO_SHADE_TOLERANCE:
            double min = Double.MAX_VALUE;
            for (int x = 0; x < nx; x++) {
                for (int y = 0; y < ny; y++) {
                    output = cellOutputs[x][y];
                    tcell = airTemperature + output * noctFactor;
                    syseff = panel.getSystemEfficiency(tcell);
                    output *= syseff;
                    if (output < min) {
                        min = output;
                    }
                }
            }
            panel.getSolarPotential()[iMinute] += min * ny * nx;
            break;
        case // assuming each panel uses a diode bypass to connect two columns of cells
        SolarPanel.PARTIAL_SHADE_TOLERANCE:
            min = Double.MAX_VALUE;
            if (panel.isRotated()) {
                // landscape: nx = 10, ny = 6
                for (int y = 0; y < ny; y++) {
                    if (y % 2 == 0) {
                        // reset min every two columns of cells
                        min = Double.MAX_VALUE;
                    }
                    for (int x = 0; x < nx; x++) {
                        output = cellOutputs[x][y];
                        tcell = airTemperature + output * noctFactor;
                        syseff = panel.getSystemEfficiency(tcell);
                        output *= syseff;
                        if (output < min) {
                            min = output;
                        }
                    }
                    if (y % 2 == 1) {
                        panel.getSolarPotential()[iMinute] += min * nx * 2;
                    }
                }
            } else {
                // portrait: nx = 6, ny = 10
                for (int x = 0; x < nx; x++) {
                    if (x % 2 == 0) {
                        // reset min every two columns of cells
                        min = Double.MAX_VALUE;
                    }
                    for (int y = 0; y < ny; y++) {
                        output = cellOutputs[x][y];
                        tcell = airTemperature + output * noctFactor;
                        syseff = panel.getSystemEfficiency(tcell);
                        output *= syseff;
                        if (output < min) {
                            min = output;
                        }
                    }
                    if (x % 2 == 1) {
                        panel.getSolarPotential()[iMinute] += min * ny * 2;
                    }
                }
            }
            break;
    }
}
Also used : Calendar(java.util.Calendar) Mesh(com.ardor3d.scenegraph.Mesh) FloatBuffer(java.nio.FloatBuffer) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults)

Example 10 with Point

use of org.poly2tri.geometry.primitives.Point in project energy3d by concord-consortium.

the class SolarRadiation method computeOnSensor.

private void computeOnSensor(final int minute, final ReadOnlyVector3 directionTowardSun, final Sensor sensor) {
    final int nx = 2, ny = 2;
    // nx*ny*60: nx*ny is to get the unit cell area of the nx*ny grid; 60 is to convert the unit of timeStep from minute to kWh
    final double a = Sensor.WIDTH * Sensor.HEIGHT * Scene.getInstance().getTimeStep() / (nx * ny * 60.0);
    final ReadOnlyVector3 normal = sensor.getNormal();
    if (normal == null) {
        throw new RuntimeException("Normal is null");
    }
    final Mesh drawMesh = sensor.getRadiationMesh();
    final Mesh collisionMesh = (Mesh) sensor.getRadiationCollisionSpatial();
    MeshDataStore data = onMesh.get(drawMesh);
    if (data == null) {
        data = initMeshTextureDataOnRectangle(drawMesh, nx, ny);
    }
    final ReadOnlyVector3 offset = directionTowardSun.multiply(1, null);
    final double dot = normal.dot(directionTowardSun);
    double directRadiation = 0;
    if (dot > 0) {
        directRadiation += calculateDirectRadiation(directionTowardSun, normal);
    }
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, normal);
    final FloatBuffer vertexBuffer = drawMesh.getMeshData().getVertexBuffer();
    // (0, 0)
    final Vector3 p0 = new Vector3(vertexBuffer.get(3), vertexBuffer.get(4), vertexBuffer.get(5));
    // (1, 0)
    final Vector3 p1 = new Vector3(vertexBuffer.get(6), vertexBuffer.get(7), vertexBuffer.get(8));
    // (0, 1)
    final Vector3 p2 = new Vector3(vertexBuffer.get(0), vertexBuffer.get(1), vertexBuffer.get(2));
    // this is the longer side (supposed to be y)
    final Vector3 u = p1.subtract(p0, null).normalizeLocal();
    // this is the shorter side (supposed to be x)
    final Vector3 v = p2.subtract(p0, null).normalizeLocal();
    // x and y must be swapped to have correct heat map texture, because nx represents rows and ny columns as we call initMeshTextureDataOnRectangle(mesh, nx, ny)
    final double xSpacing = p1.distance(p0) / nx;
    final double ySpacing = p2.distance(p0) / ny;
    final int iMinute = minute / Scene.getInstance().getTimeStep();
    for (int x = 0; x < nx; x++) {
        for (int y = 0; y < ny; y++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            final Vector3 u2 = u.multiply(xSpacing * (x + 0.5), null);
            final Vector3 v2 = v.multiply(ySpacing * (y + 0.5), null);
            final ReadOnlyVector3 p = drawMesh.getWorldTransform().applyForward(p0.add(v2, null).addLocal(u2)).addLocal(offset);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
            double radiation = indirectRadiation;
            if (dot > 0) {
                final PickResults pickResults = new PrimitivePickResults();
                for (final Spatial spatial : collidables) {
                    if (spatial != collisionMesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    radiation += directRadiation;
                }
            }
            data.dailySolarIntensity[x][y] += radiation;
            sensor.getSolarPotential()[iMinute] += radiation * a;
        }
    }
}
Also used : Mesh(com.ardor3d.scenegraph.Mesh) FloatBuffer(java.nio.FloatBuffer) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults)

Aggregations

Point (org.poly2tri.geometry.primitives.Point)15 ReadOnlyVector3 (com.ardor3d.math.type.ReadOnlyVector3)12 TPoint (org.poly2tri.triangulation.point.TPoint)12 CullHint (com.ardor3d.scenegraph.hint.CullHint)11 Vector3 (com.ardor3d.math.Vector3)9 PickResults (com.ardor3d.intersection.PickResults)6 PrimitivePickResults (com.ardor3d.intersection.PrimitivePickResults)6 Ray3 (com.ardor3d.math.Ray3)6 Spatial (com.ardor3d.scenegraph.Spatial)6 FloatBuffer (java.nio.FloatBuffer)6 CancellationException (java.util.concurrent.CancellationException)6 PickingHint (com.ardor3d.scenegraph.hint.PickingHint)5 PolygonPoint (org.poly2tri.geometry.polygon.PolygonPoint)5 Mesh (com.ardor3d.scenegraph.Mesh)4 Calendar (java.util.Calendar)3 ArdorVector3Point (org.poly2tri.triangulation.point.ardor3d.ArdorVector3Point)3 Vector2 (com.ardor3d.math.Vector2)2 ReadOnlyVector2 (com.ardor3d.math.type.ReadOnlyVector2)2 Path2D (java.awt.geom.Path2D)2 ArrayList (java.util.ArrayList)2