Search in sources :

Example 11 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SelectUtil method pickPart.

public static PickedHousePart pickPart(final int x, final int y, final Class<?>[] typesOfHousePart) {
    pickResults.clear();
    final Ray3 pickRay = SceneManager.getInstance().getCamera().getPickRay(new Vector2(x, y), false, null);
    for (final Class<?> typeOfHousePart : typesOfHousePart) {
        if (typeOfHousePart == null) {
            PickingUtil.findPick(SceneManager.getInstance().getLand(), pickRay, pickResults, false);
        } else {
            for (final HousePart part : Scene.getInstance().getParts()) {
                if (!part.getLockEdit() && typeOfHousePart.isInstance(part)) {
                    PickingUtil.findPick(part.getCollisionSpatial(), pickRay, pickResults, false);
                }
            }
        }
    }
    final PickedHousePart picked = getPickResultForImportedMesh();
    if (picked != null) {
        return picked;
    }
    return getPickResult(pickRay);
}
Also used : Vector2(com.ardor3d.math.Vector2) PickedHousePart(org.concord.energy3d.model.PickedHousePart) HousePart(org.concord.energy3d.model.HousePart) Ray3(com.ardor3d.math.Ray3) PickedHousePart(org.concord.energy3d.model.PickedHousePart)

Example 12 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SelectUtil method pickPart.

public static PickedHousePart pickPart(final int x, final int y, final Mesh mesh) {
    pickResults.clear();
    final Ray3 pickRay = SceneManager.getInstance().getCamera().getPickRay(new Vector2(x, y), false, null);
    PickingUtil.findPick(mesh, pickRay, pickResults, false);
    final PickedHousePart picked = getPickResultForImportedMesh();
    if (picked != null) {
        return picked;
    }
    return getPickResult(pickRay);
}
Also used : Vector2(com.ardor3d.math.Vector2) Ray3(com.ardor3d.math.Ray3) PickedHousePart(org.concord.energy3d.model.PickedHousePart)

Example 13 with Ray3

use of com.ardor3d.math.Ray3 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 14 with Ray3

use of com.ardor3d.math.Ray3 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)

Example 15 with Ray3

use of com.ardor3d.math.Ray3 in project energy3d by concord-consortium.

the class SolarRadiation method computeOnParabolicDish.

// Unlike PV solar panels, no indirect (ambient or diffuse) radiation should be included in reflection calculation. The mesh is a parabolic surface.
private void computeOnParabolicDish(final int minute, final ReadOnlyVector3 directionTowardSun, final ParabolicDish dish) {
    final int n = Scene.getInstance().getParabolicDishN();
    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);
    dish.draw();
    final ReadOnlyVector3 normal = dish.getNormal();
    if (normal == null) {
        throw new RuntimeException("Normal is null");
    }
    // n*n*60: n*n is to get the unit cell area of the nxn grid; 60 is to convert the unit of timeStep from minute to kWh
    final double a = 4 * dish.getRimRadius() * dish.getRimRadius() * Scene.getInstance().getTimeStep() / (n * n * 60.0);
    final Mesh mesh = dish.getRadiationMesh();
    MeshDataStore data = onMesh.get(mesh);
    if (data == null) {
        data = initMeshTextureDataOnRectangle(mesh, n, n);
    }
    final double dot = normal.dot(directionTowardSun);
    double directRadiation = 0;
    if (dot > 0) {
        directRadiation += calculateDirectRadiation(directionTowardSun, normal);
    }
    final double radius = dish.getRimRadius() / Scene.getInstance().getAnnotationScale();
    final double depth = dish.getRimRadius() * dish.getRimRadius() / (4 * dish.getFocalLength() * Scene.getInstance().getAnnotationScale());
    // center of the rim circle
    final Vector3 center = new Vector3(0, 0, depth);
    final Vector3 q = center.clone();
    final double spacing = 2 * radius / n;
    // as the parabolic dish always faces the sun, we only have to deal with its aperture plane (rim circle)
    final int iMinute = minute / Scene.getInstance().getTimeStep();
    for (int x = 0; x < n; x++) {
        for (int y = 0; y < n; y++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            q.setX(spacing * (x + 0.5 * (1 - n)));
            q.setY(spacing * (y + 0.5 * (1 - n)));
            final ReadOnlyVector3 p = mesh.localToWorld(q, null);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            if (dot > 0) {
                final PickResults pickResults = new PrimitivePickResults();
                for (final Spatial spatial : collidables) {
                    if (spatial != mesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    // for heat map generation (for now, just use a square image for texture)
                    data.dailySolarIntensity[y][x] += directRadiation;
                    if (q.distanceSquared(center) < radius * radius) {
                        // sum all the solar energy up over all meshes and store in the foundation's solar potential array
                        dish.getSolarPotential()[iMinute] += directRadiation * a;
                    }
                }
            }
        }
    }
}
Also used : Calendar(java.util.Calendar) Mesh(com.ardor3d.scenegraph.Mesh) 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

Ray3 (com.ardor3d.math.Ray3)24 PickResults (com.ardor3d.intersection.PickResults)18 PrimitivePickResults (com.ardor3d.intersection.PrimitivePickResults)18 ReadOnlyVector3 (com.ardor3d.math.type.ReadOnlyVector3)18 Vector3 (com.ardor3d.math.Vector3)15 Spatial (com.ardor3d.scenegraph.Spatial)15 CullHint (com.ardor3d.scenegraph.hint.CullHint)12 Point (org.poly2tri.geometry.primitives.Point)11 TPoint (org.poly2tri.triangulation.point.TPoint)11 Mesh (com.ardor3d.scenegraph.Mesh)10 CancellationException (java.util.concurrent.CancellationException)10 Vector2 (com.ardor3d.math.Vector2)9 FloatBuffer (java.nio.FloatBuffer)9 Calendar (java.util.Calendar)6 HousePart (org.concord.energy3d.model.HousePart)6 Node (com.ardor3d.scenegraph.Node)5 Foundation (org.concord.energy3d.model.Foundation)5 PickedHousePart (org.concord.energy3d.model.PickedHousePart)5 ReadOnlyVector2 (com.ardor3d.math.type.ReadOnlyVector2)3 Roof (org.concord.energy3d.model.Roof)3