Search in sources :

Example 11 with PrimitivePickResults

use of com.ardor3d.intersection.PrimitivePickResults 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 12 with PrimitivePickResults

use of com.ardor3d.intersection.PrimitivePickResults 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)

Example 13 with PrimitivePickResults

use of com.ardor3d.intersection.PrimitivePickResults in project energy3d by concord-consortium.

the class SolarRadiation method computeOnParabolicTrough.

// Unlike PV solar panels, no indirect (ambient or diffuse) radiation should be included in reflection calculation. The mesh is a parabolic surface.
private void computeOnParabolicTrough(final int minute, final ReadOnlyVector3 directionTowardSun, final ParabolicTrough trough) {
    final int nAxis = trough.getNSectionAxis();
    final int nPara = trough.getNSectionParabola();
    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);
    trough.draw();
    final ReadOnlyVector3 normal = trough.getNormal();
    if (normal == null) {
        throw new RuntimeException("Normal is null");
    }
    // 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 = trough.getApertureWidth() * trough.getTroughLength() * Scene.getInstance().getTimeStep() / (nAxis * nPara * 60.0);
    final Mesh mesh = trough.getRadiationMesh();
    MeshDataStore data = onMesh.get(mesh);
    if (data == null) {
        // axis is row and parabola is column
        data = initMeshTextureDataOnRectangle(mesh, nAxis, nPara);
    }
    final double dot = normal.dot(directionTowardSun);
    double directRadiation = 0;
    if (dot > 0) {
        directRadiation += calculateDirectRadiation(directionTowardSun, normal);
    }
    final FloatBuffer vertexBuffer = mesh.getMeshData().getVertexBuffer();
    // number of vertex coordinates on each end
    final int j = vertexBuffer.limit() / 2;
    // (0, 0)
    final Vector3 p0 = new Vector3(vertexBuffer.get(0), vertexBuffer.get(1), vertexBuffer.get(2));
    // (1, 0)
    final Vector3 p1 = new Vector3(vertexBuffer.get(j - 3), vertexBuffer.get(j - 2), vertexBuffer.get(j - 1));
    // (0, 1)
    final Vector3 p2 = new Vector3(vertexBuffer.get(j), vertexBuffer.get(j + 1), vertexBuffer.get(j + 2));
    // final Vector3 q0 = mesh.localToWorld(p0, null);
    // final Vector3 q1 = mesh.localToWorld(p1, null);
    // final Vector3 q2 = mesh.localToWorld(p2, null);
    // System.out.println("***" + q0.distance(q1) * Scene.getInstance().getAnnotationScale() + "," + q0.distance(q2) * Scene.getInstance().getAnnotationScale());
    // this is perpendicular to the direction of the cylinder axis (nPara)
    final Vector3 u = p1.subtract(p0, null).normalizeLocal();
    // this is parallel to the direction of the cylinder axis (nAxis)
    final Vector3 v = p2.subtract(p0, null).normalizeLocal();
    final double xSpacing = p1.distance(p0) / nPara;
    final double ySpacing = p2.distance(p0) / nAxis;
    // as the parabolic trough always faces the sun, we only have to deal with its aperture plane
    final int iMinute = minute / Scene.getInstance().getTimeStep();
    for (int x = 0; x < nPara; x++) {
        for (int y = 0; y < nAxis; 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);
            // on the aperture plane of the parabolic trough
            final Vector3 q = p0.add(v2, null).addLocal(u2);
            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
                    data.dailySolarIntensity[y][x] += directRadiation;
                    // sum all the solar energy up over all meshes and store in the foundation's solar potential array
                    trough.getSolarPotential()[iMinute] += directRadiation * a;
                }
            }
        }
    }
}
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 PrimitivePickResults

use of com.ardor3d.intersection.PrimitivePickResults in project energy3d by concord-consortium.

the class SolarRadiation method computeOnMesh.

// Formula from http://en.wikipedia.org/wiki/Air_mass_(solar_energy)#Solar_intensity
private void computeOnMesh(final int minute, final ReadOnlyVector3 directionTowardSun, final HousePart housePart, final Mesh drawMesh, final Mesh collisionMesh, final ReadOnlyVector3 normal) {
    if (normal == null) {
        // FIXME: normal can be null sometimes, fix it
        return;
    }
    if (Scene.getInstance().getOnlySolarComponentsInSolarMap()) {
        return;
    }
    MeshDataStore data = onMesh.get(drawMesh);
    if (data == null) {
        data = initMeshTextureData(drawMesh, collisionMesh, normal, !(housePart instanceof Window));
    }
    /* needed in order to prevent picking collision with neighboring wall at wall edge (seem 0.1 is too small, 0.5 is about right) */
    final ReadOnlyVector3 offset = directionTowardSun.multiply(0.5, null);
    final double dot = normal.dot(directionTowardSun);
    final double directRadiation = dot > 0 ? calculateDirectRadiation(directionTowardSun, normal) : 0;
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, normal);
    final int timeStep = Scene.getInstance().getTimeStep();
    final double solarStep = Scene.getInstance().getSolarStep();
    final double annotationScale = Scene.getInstance().getAnnotationScale();
    final double scaleFactor = annotationScale * annotationScale / 60 * timeStep;
    // a window itself doesn't really absorb solar energy, but it passes the energy into the house to be absorbed
    final float absorption = housePart instanceof Window ? 1 : 1 - housePart.getAlbedo();
    if (housePart instanceof Roof) {
        // for now, only store this for roofs that have different meshes
        if (data.solarPotential == null) {
            data.solarPotential = new double[MINUTES_OF_DAY / timeStep];
        }
        if (data.heatLoss == null) {
            data.heatLoss = new double[MINUTES_OF_DAY / timeStep];
        }
    }
    for (int col = 0; col < data.cols; col++) {
        final double w = col == data.cols - 1 ? data.p2.distance(data.u.multiply(col * solarStep, null).addLocal(data.p0)) : solarStep;
        final ReadOnlyVector3 pU = data.u.multiply(col * solarStep + 0.5 * w, null).addLocal(data.p0);
        for (int row = 0; row < data.rows; row++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            if (data.dailySolarIntensity[row][col] == -1) {
                continue;
            }
            final double h = row == data.rows - 1 ? data.p1.distance(data.p0) - row * solarStep : solarStep;
            final ReadOnlyVector3 p = data.v.multiply(row * solarStep + 0.5 * h, null).addLocal(pU).add(offset, null);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            final PickResults pickResults = new PrimitivePickResults();
            // assuming that indirect (ambient or diffuse) radiation can always reach a grid point
            double radiation = indirectRadiation;
            final double scaledArea = w * h * scaleFactor;
            if (dot > 0) {
                for (final Spatial spatial : collidables) {
                    if (EnergyPanel.getInstance().isCancelled()) {
                        throw new CancellationException();
                    }
                    if (spatial != collisionMesh) {
                        PickingUtil.findPick(spatial, pickRay, pickResults, false);
                        if (pickResults.getNumber() != 0) {
                            if (housePart instanceof Foundation) {
                                // at this point, we only show radiation heat map on the first floor
                                final HousePart collidableOwner = collidablesToParts.get(spatial);
                                if (collidableOwner instanceof Window) {
                                    radiation += directRadiation * ((Window) collidableOwner).getSolarHeatGainCoefficient();
                                }
                            }
                            break;
                        }
                    }
                }
                if (pickResults.getNumber() == 0) {
                    radiation += directRadiation;
                }
            }
            data.dailySolarIntensity[row][col] += Scene.getInstance().getOnlyAbsorptionInSolarMap() ? absorption * radiation : radiation;
            if (data.solarPotential != null) {
                data.solarPotential[minute / timeStep] += radiation * scaledArea;
            }
            if (!(housePart instanceof Foundation)) {
                // exclude radiation on foundation
                housePart.getSolarPotential()[minute / timeStep] += radiation * scaledArea;
            }
        }
    }
}
Also used : Window(org.concord.energy3d.model.Window) 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) Roof(org.concord.energy3d.model.Roof) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults) Foundation(org.concord.energy3d.model.Foundation) HousePart(org.concord.energy3d.model.HousePart)

Example 15 with PrimitivePickResults

use of com.ardor3d.intersection.PrimitivePickResults in project energy3d by concord-consortium.

the class SolarRadiation method computeOnLand.

private void computeOnLand(final ReadOnlyVector3 directionTowardSun) {
    final double indirectRadiation = calculateDiffuseAndReflectedRadiation(directionTowardSun, Vector3.UNIT_Z);
    final double totalRadiation = calculateDirectRadiation(directionTowardSun, Vector3.UNIT_Z) + indirectRadiation;
    final double step = Scene.getInstance().getSolarStep() * 4;
    final int rows = (int) (256 / step);
    final int cols = rows;
    MeshDataStore data = onMesh.get(SceneManager.getInstance().getSolarLand());
    if (data == null) {
        data = new MeshDataStore();
        data.dailySolarIntensity = new double[rows][cols];
        onMesh.put(SceneManager.getInstance().getSolarLand(), data);
    }
    final Vector3 p = new Vector3();
    final double absorption = 1 - Scene.getInstance().getGround().getAdjustedAlbedo(Heliodon.getInstance().getCalendar().get(Calendar.MONTH));
    for (int col = 0; col < cols; col++) {
        p.setX((col - cols / 2) * step + step / 2.0);
        for (int row = 0; row < rows; row++) {
            if (EnergyPanel.getInstance().isCancelled()) {
                throw new CancellationException();
            }
            p.setY((row - rows / 2) * step + step / 2.0);
            final Ray3 pickRay = new Ray3(p, directionTowardSun);
            final PickResults pickResults = new PrimitivePickResults();
            for (final Spatial spatial : collidables) {
                PickingUtil.findPick(spatial, pickRay, pickResults, false);
                if (pickResults.getNumber() != 0) {
                    break;
                }
            }
            if (pickResults.getNumber() == 0) {
                data.dailySolarIntensity[row][col] += Scene.getInstance().getOnlyAbsorptionInSolarMap() ? totalRadiation * absorption : totalRadiation;
            } else {
                // if shaded, it still receives indirect radiation
                data.dailySolarIntensity[row][col] += Scene.getInstance().getOnlyAbsorptionInSolarMap() ? indirectRadiation * absorption : indirectRadiation;
            }
        }
    }
}
Also used : PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) CancellationException(java.util.concurrent.CancellationException) Spatial(com.ardor3d.scenegraph.Spatial) ReadOnlyVector3(com.ardor3d.math.type.ReadOnlyVector3) Vector3(com.ardor3d.math.Vector3) PrimitivePickResults(com.ardor3d.intersection.PrimitivePickResults) PickResults(com.ardor3d.intersection.PickResults) CullHint(com.ardor3d.scenegraph.hint.CullHint) TPoint(org.poly2tri.triangulation.point.TPoint) Point(org.poly2tri.geometry.primitives.Point) Ray3(com.ardor3d.math.Ray3)

Aggregations

PickResults (com.ardor3d.intersection.PickResults)18 PrimitivePickResults (com.ardor3d.intersection.PrimitivePickResults)18 Ray3 (com.ardor3d.math.Ray3)18 ReadOnlyVector3 (com.ardor3d.math.type.ReadOnlyVector3)16 Spatial (com.ardor3d.scenegraph.Spatial)15 Vector3 (com.ardor3d.math.Vector3)13 CullHint (com.ardor3d.scenegraph.hint.CullHint)11 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 FloatBuffer (java.nio.FloatBuffer)8 Calendar (java.util.Calendar)6 Node (com.ardor3d.scenegraph.Node)5 Foundation (org.concord.energy3d.model.Foundation)4 HousePart (org.concord.energy3d.model.HousePart)4 Vector2 (com.ardor3d.math.Vector2)3 ReadOnlyVector2 (com.ardor3d.math.type.ReadOnlyVector2)3 Roof (org.concord.energy3d.model.Roof)3 Window (org.concord.energy3d.model.Window)3