/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.math3d;
import static org.andork.math3d.Vecmath.dot3;
import static org.andork.math3d.Vecmath.subDot3;
/**
* Represents a planar hull and provides methods to test if it contains or
* intersects bounding boxes and points. For view volume hulls, there are
* canonical orders and properties:<br>
* <br>
* <b>origins</b> and <b>normals</b> (and <b>planeDists</b>):
*
* <pre>
* [0] left side
* [1] right side
* [2] bottom side
* [3] top side
* [4] near side (origin must be in the center of the side)
* [5] far side (origin must be in the center of the side)
* </pre>
*
* <b>vertices</b>:
*
* <pre>
* [0] left bottom near corner
* [1] right bottom near corner
* [2] left top near corner
* [3] right top near corner
* [4] left bottom far corner
* [5] right bottom far corner
* [6] left top far corner
* [7] right top far corner
* </pre>
*
* <b>triangleIndices / triangleSides</b> (use
* {@link #setCanonicalTriangleIndicesAndPlanes()}):
*
* <pre>
* [0] { 0, 6, 4 } / 0
* [1] { 6, 0, 2 } / 0
* [2] { 7, 1, 5 } / 1
* [3] { 1, 7, 3 } / 1
* [4] { 0, 5, 1 } / 2
* [5] { 5, 0, 4 } / 2
* [6] { 7, 2, 3 } / 3
* [7] { 2, 7, 6 } / 3
* [8] { 0, 3, 2 } / 4
* [9] { 3, 0, 1 } / 4
* [10] { 7, 4, 6 } / 5
* [11] { 4, 7, 5 } / 5
* </pre>
*
* The triangle planes should always be in ascending order, and the triangle
* indices should always be in counterclockwise order when viewed from outside
* the hull (in case the hull needs to be rendered for debugging purposes).
*
* @author Andy
*/
public class PlanarHull3f {
private static void set(int[] array, int a, int b, int c) {
array[0] = a;
array[1] = b;
array[2] = c;
}
public final float[][] vertices;
public final float[][] origins;
public final float[][] normals;
public final float[] planeDists;
public final int[][] triangleIndices;
public final int[] triangleSides;
public PlanarHull3f() {
this(6, 8, 12);
}
public PlanarHull3f(int numSides, int numVertices, int numTriangles) {
vertices = new float[numVertices][3];
origins = new float[numSides][3];
normals = new float[numSides][3];
planeDists = new float[numSides];
triangleIndices = new int[numTriangles][3];
triangleSides = new int[numTriangles];
}
private boolean allPointsOutside(int side, float[] box) {
for (int xd = 0; xd <= 3; xd += 3) {
float x = box[xd];
for (int yd = 1; yd <= 4; yd += 3) {
float y = box[yd];
for (int zd = 2; zd <= 5; zd += 3) {
float z = box[zd];
if (subDot3(x, y, z, origins[side], normals[side]) >= 0) {
return false;
}
}
}
}
return true;
}
public void calcPlaneDs() {
for (int side = 0; side < origins.length; side++) {
planeDists[side] = -dot3(normals[side], origins[side]);
}
}
public boolean containsBox(float[] box) {
for (int xd = 0; xd <= 3; xd += 3) {
float x = box[xd];
for (int yd = 1; yd <= 4; yd += 3) {
float y = box[yd];
for (int zd = 2; zd <= 5; zd += 3) {
float z = box[zd];
if (!containsPoint(x, y, z)) {
return false;
}
}
}
}
return true;
}
public boolean containsPoint(float x, float y, float z) {
for (int side = 0; side < origins.length; side++) {
if (subDot3(x, y, z, origins[side], normals[side]) < 0f) {
return false;
}
}
return true;
}
public boolean containsPoint(float[] p) {
return containsPoint(p[0], p[1], p[2]);
}
public boolean intersectsBox(float[] box) {
for (int side = 0; side < origins.length; side++) {
if (allPointsOutside(side, box)) {
return false;
}
}
for (int dim = 0; dim < 3; dim++) {
boolean allOutside = true;
for (float[] vertex : vertices) {
if (vertex[dim] >= box[dim]) {
allOutside = false;
break;
}
}
if (allOutside) {
return false;
}
allOutside = true;
for (float[] vertex : vertices) {
if (vertex[dim] <= box[dim + 3]) {
allOutside = false;
break;
}
}
if (allOutside) {
return false;
}
}
return true;
}
public void setCanonicalTriangleIndicesAndPlanes() {
set(triangleIndices[0], 0, 6, 4);
set(triangleIndices[1], 6, 0, 2);
set(triangleIndices[2], 7, 1, 5);
set(triangleIndices[3], 1, 7, 3);
set(triangleIndices[4], 0, 5, 1);
set(triangleIndices[5], 5, 0, 4);
set(triangleIndices[6], 7, 2, 3);
set(triangleIndices[7], 2, 7, 6);
set(triangleIndices[8], 0, 3, 2);
set(triangleIndices[9], 3, 0, 1);
set(triangleIndices[10], 7, 4, 6);
set(triangleIndices[11], 4, 7, 5);
for (int i = 0; i < 12; i++) {
triangleSides[i] = i / 2;
}
}
}