use of com.ardor3d.math.type.ReadOnlyVector3 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;
}
use of com.ardor3d.math.type.ReadOnlyVector3 in project energy3d by concord-consortium.
the class Util method get2DPoints.
public static Point2D.Double[] get2DPoints(final OrientedBoundingBox box) {
final Point2D.Double[] points = new Point2D.Double[4];
final ReadOnlyVector3 center = box.getCenter();
final ReadOnlyVector3 extent = box.getExtent();
final ReadOnlyVector3 vx = box.getXAxis().multiply(extent.getX(), null);
final ReadOnlyVector3 vy = box.getYAxis().multiply(extent.getY(), null);
// (1, 1)
double x = center.getX() + vx.getX();
double y = center.getY() + vy.getY();
points[0] = new Point2D.Double(x, y);
// (-1, 1)
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
points[1] = new Point2D.Double(x, y);
// (-1, -1)
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
points[2] = new Point2D.Double(x, y);
// (1, -1)
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
points[3] = new Point2D.Double(x, y);
return points;
}
use of com.ardor3d.math.type.ReadOnlyVector3 in project energy3d by concord-consortium.
the class Util method drawBoundingBox.
public static void drawBoundingBox(final Spatial spatial, final Line boundingBox) {
OrientedBoundingBox box = null;
if (spatial instanceof Mesh) {
box = getOrientedBoundingBox((Mesh) spatial);
} else if (spatial instanceof Node) {
box = getOrientedBoundingBox((Node) spatial);
} else {
return;
}
FloatBuffer buf = boundingBox.getMeshData().getVertexBuffer();
if (buf == null || buf.capacity() != 24) {
buf = BufferUtils.createVector3Buffer(24);
boundingBox.getMeshData().setVertexBuffer(buf);
} else {
buf.rewind();
buf.limit(buf.capacity());
}
final ReadOnlyVector3 center = box.getCenter();
final ReadOnlyVector3 extent = box.getExtent();
final ReadOnlyVector3 vx = box.getXAxis().multiply(extent.getX(), null);
final ReadOnlyVector3 vy = box.getYAxis().multiply(extent.getY(), null);
final ReadOnlyVector3 vz = box.getZAxis().multiply(extent.getZ(), null);
double x, y, z;
// #1: (1, 1, 1) to (-1, 1, 1)
x = center.getX() + vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #2: (1, 1, 1) to (1, -1, 1)
x = center.getX() + vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #3: (1, 1, 1) to (1, 1, -1)
x = center.getX() + vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() + vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #4: (-1, -1, -1) to (1, -1, -1)
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #5: (-1, -1, -1) to (-1, 1, -1)
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #6: (-1, -1, -1) to (-1, -1, 1)
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #7: (-1, 1, 1) to (-1, -1, 1)
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #8: (-1, 1, 1) to (-1, 1, -1)
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #9: (1, -1, 1) to (-1, -1, 1)
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #10: (1, -1, 1) to (1, -1, -1)
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() + vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #11: (1, 1, -1) to (-1, 1, -1)
x = center.getX() + vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() - vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
// #12: (1, 1, -1) to (1, -1, -1)
x = center.getX() + vx.getX();
y = center.getY() + vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
x = center.getX() + vx.getX();
y = center.getY() - vy.getY();
z = center.getZ() - vz.getZ();
buf.put((float) x).put((float) y).put((float) z);
boundingBox.updateModelBound();
boundingBox.setVisible(true);
}
use of com.ardor3d.math.type.ReadOnlyVector3 in project energy3d by concord-consortium.
the class SceneManager method drawGrids.
public Mesh drawGrids(final double gridSize) {
final Mesh gridsMesh = new Line("Ground Grids");
gridsMesh.getSceneHints().setCullHint(CullHint.Always);
gridsMesh.setDefaultColor(new ColorRGBA(0, 0, 1, 1));
final BlendState blendState = new BlendState();
blendState.setBlendEnabled(true);
gridsMesh.setRenderState(blendState);
gridsMesh.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
final ReadOnlyVector3 width = Vector3.UNIT_X.multiply(2000, null);
final ReadOnlyVector3 height = Vector3.UNIT_Y.multiply(2000, null);
final ArrayList<ReadOnlyVector3> points = new ArrayList<ReadOnlyVector3>();
final ReadOnlyVector3 pMiddle = Vector3.ZERO;
final int cols = (int) (width.length() / gridSize);
for (int col = 1; col < cols / 2 + 1; col++) {
for (int neg = -1; neg <= 1; neg += 2) {
final ReadOnlyVector3 lineP1 = width.normalize(null).multiplyLocal(neg * col * gridSize).addLocal(pMiddle).subtractLocal(height.multiply(0.5, null));
points.add(lineP1);
final ReadOnlyVector3 lineP2 = lineP1.add(height, null);
points.add(lineP2);
if (col == 0) {
break;
}
}
}
final int rows = (int) (height.length() / gridSize);
for (int row = 1; row < rows / 2 + 1; row++) {
for (int neg = -1; neg <= 1; neg += 2) {
final ReadOnlyVector3 lineP1 = height.normalize(null).multiplyLocal(neg * row * gridSize).addLocal(pMiddle).subtractLocal(width.multiply(0.5, null));
points.add(lineP1);
final ReadOnlyVector3 lineP2 = lineP1.add(width, null);
points.add(lineP2);
if (row == 0) {
break;
}
}
}
final FloatBuffer buf = BufferUtils.createVector3Buffer(points.size());
for (final ReadOnlyVector3 p : points) {
buf.put(p.getXf()).put(p.getYf()).put(0.01f);
}
gridsMesh.getMeshData().setVertexBuffer(buf);
gridsMesh.getMeshData().updateVertexCount();
Util.disablePickShadowLight(gridsMesh);
gridsMesh.setModelBound(new BoundingBox());
gridsMesh.updateModelBound();
gridsMesh.updateWorldBound(true);
return gridsMesh;
}
use of com.ardor3d.math.type.ReadOnlyVector3 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;
}
}
Aggregations