package org.geogebra.common.geogebra3D.kernel3D.implicit3D;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoElement3D;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoTriangulatedSurface3D;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.Matrix.Coords3;
import org.geogebra.common.kernel.Matrix.CoordsDouble3;
import org.geogebra.common.kernel.arithmetic.Equation;
import org.geogebra.common.kernel.arithmetic.EquationValue;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.FunctionNVar;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.arithmetic.Traversing.VariableReplacer;
import org.geogebra.common.kernel.arithmetic.ValueType;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunctionNVar;
import org.geogebra.common.kernel.kernelND.GeoCoordSys2D;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoImplicitSurfaceND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.plugin.Operation;
import org.geogebra.common.util.debug.Log;
/**
*
*
*/
public class GeoImplicitSurface extends GeoElement3D
implements GeoImplicitSurfaceND, EquationValue {
private static final boolean DEBUG = false;
private static final Coords3 DUMMY_NORMAL = new CoordsDouble3(0, 0, 1.0);
private boolean defined;
private boolean hasDerivatives;
private double[] evals = new double[3];
private double[] normEval = new double[3];
private GeoFunctionNVar expression;
private GeoTriangulatedSurface3D surface3D;
private FunctionNVar[] derivFunc = new FunctionNVar[3];
/**
* Create an empty GeoImplicitSurface
*
* @param cons
* {@link Construction}
*/
public GeoImplicitSurface(Construction cons) {
super(cons);
setAlphaValue(0.75f); // TODO remove that when construction default will
// be created
this.surface3D = new GeoTriangulatedSurface3D();
}
/**
*
* @param cons
* {@link Construction}
* @param eqn
* {@link Equation}
* @param label
* label for the object
*/
public GeoImplicitSurface(Construction cons, Equation eqn) {
super(cons);
setAlphaValue(0.75f); // TODO remove that when construction default will
// be created
this.surface3D = new GeoTriangulatedSurface3D();
fromEquation(eqn);
}
/**
* Copy Constructor
*
* @param cons
* {@link Construction}
* @param geoSurface
* {@link GeoImplicitSurface}
*/
public GeoImplicitSurface(Construction cons,
GeoImplicitSurface geoSurface) {
this(cons);
this.set(geoSurface);
}
/**
* @return true for implicit surfaces
*/
@Override
public boolean isGeoImplicitSurface() {
return true;
}
/**
* @param eqn
* surface equation
*/
void fromEquation(Equation eqn) {
setDefinition(eqn.wrap());
ExpressionNode leftHandSide = eqn.getLHS();
ExpressionNode rightHandSide = eqn.getRHS();
ExpressionNode functionExpression = new ExpressionNode(kernel,
leftHandSide, Operation.MINUS, rightHandSide);
FunctionVariable x = new FunctionVariable(kernel, "x");
FunctionVariable y = new FunctionVariable(kernel, "y");
FunctionVariable z = new FunctionVariable(kernel, "z");
VariableReplacer repl = VariableReplacer.getReplacer(kernel);
VariableReplacer.addVars("x", x);
VariableReplacer.addVars("y", y);
VariableReplacer.addVars("z", z);
functionExpression.traverse(repl);
FunctionNVar fun = new FunctionNVar(functionExpression,
new FunctionVariable[] { x, y, z });
expression = new GeoFunctionNVar(cons, fun);
defined = expression.isDefined();
setDerivatives(x, y, z);
updateSurface();
}
/**
* Set derivatives which are required to evaluate normals
*
* @param x
* @param y
* @param z
*/
private void setDerivatives(FunctionVariable x, FunctionVariable y,
FunctionVariable z) {
FunctionNVar fn = expression.getFunction();
try {
derivFunc[0] = fn.getDerivativeNoCAS(x, 1);
derivFunc[1] = fn.getDerivativeNoCAS(y, 1);
derivFunc[2] = fn.getDerivativeNoCAS(z, 1);
this.hasDerivatives = true;
} catch (Exception ex) {
this.hasDerivatives = false;
}
}
/**
* Check whether normal can be evaluated for the function using derivatives
*
* @return true if normal can be evaluated
*/
public boolean isNormalEvaluable() {
return hasDerivatives;
}
/**
* Evaluate normal at given points
*
* @param x
* x-coordinate
* @param y
* y-coordinate
* @param z
* z-coordinate
* @return direction of normal vector
*/
public Coords3 evaluateNormalAt(double x, double y, double z) {
if (!hasDerivatives) {
return DUMMY_NORMAL;
}
normEval[0] = x;
normEval[1] = y;
normEval[2] = z;
CoordsDouble3 N = new CoordsDouble3();
N.x = derivFunc[0].evaluate(normEval);
N.y = derivFunc[1].evaluate(normEval);
N.z = derivFunc[2].evaluate(normEval);
if (N.isDefined()) {
double mx = Math.max(Math.abs(N.x), Math.abs(N.y));
mx = Math.max(mx, Math.abs(N.z));
N.x = N.x / mx;
N.y = N.y / mx;
N.z = N.z / mx;
return N.isDefined() ? N : DUMMY_NORMAL;
}
return DUMMY_NORMAL;
}
/**
* Evaluate normal at given coordinate
*
* @param coords
* 3D Coordinate
* @return direction of normal
*/
public Coords3 evaluateNormalAt(Coords3 coords) {
return evaluateNormalAt(coords.getXd(), coords.getYd(), coords.getZd());
}
/**
* Evaluate normal at coordinate c and store result in r
*
* @param c
* coordinate where normal is to be evaluated
* @param r
* coordinate where output normal vector is stored
*/
public void evaluateNormalAt(Coords c, Coords r) {
r.val[0] = 0;
r.val[1] = 0;
r.val[2] = 0;
if (!hasDerivatives) {
return;
}
double lt, rt, e = 1e-3, e2 = 2 * e;
normEval[0] = c.val[0];
normEval[1] = c.val[1];
normEval[2] = c.val[2];
r.val[0] = derivFunc[0].evaluate(normEval);
r.val[1] = derivFunc[1].evaluate(normEval);
r.val[2] = derivFunc[2].evaluate(normEval);
for (int i = 0; i < 3; i++) {
if (!MyDouble.isFinite(r.val[i])) {
normEval[i] -= e;
lt = evaluateAt(normEval);
normEval[i] += e2;
rt = evaluateAt(normEval);
r.val[i] = (rt - lt) / e2;
}
}
r.normalize(true);
}
/**
*
* @param x
* x coordinate
* @param y
* y coordinate
* @param z
* z coordinate
* @return value of the function at (x, y, z)
*/
public double evaluateAt(double x, double y, double z) {
evals[0] = x;
evals[1] = y;
evals[2] = z;
return evaluateAt(evals);
}
/**
*
* @param xyz
* {x, y, z} coordinate
* @return value of function at (x, y, z)
*/
public double evaluateAt(double[] xyz) {
return expression.evaluate(xyz);
}
/**
* @return Surface3D
*/
public GeoTriangulatedSurface3D getSurface3D() {
return surface3D;
}
@Override
public Coords getLabelPosition() {
return Coords.VZ;
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.IMPLICIT_SURFACE_3D;
}
@Override
public GeoElement copy() {
GeoImplicitSurface surface = new GeoImplicitSurface(cons);
surface.set(this);
return surface;
}
@Override
public void set(GeoElementND geo) {
Equation equationCopy = (Equation) geo.getDefinition().unwrap()
.deepCopy(kernel);
fromEquation(equationCopy);
}
/**
* force to re-evaluate the surface
*
* @param bounds
* surface bound : {xmin, xmax, ymin, ymax, zmin, zmax, xscale,
* yscale, zscale}
*/
public void updateSurface(double[] bounds) {
if (isDefined()) {
surface3D.clear();
MarchingCube m = new MarchingCube(this);
m.update(bounds);
}
}
/**
* Force to re-evaluate the surface
*/
public void updateSurface() {
double[] bounds = new double[9];
double[] views = kernel.getViewBoundsForGeo(this);
bounds[0] = views[0];
bounds[1] = views[1];
bounds[2] = views[2];
bounds[3] = views[3];
bounds[4] = kernel.getZmin(2);
bounds[5] = kernel.getZmax(2);
bounds[6] = views[4];
bounds[7] = views[5];
bounds[8] = kernel.getZscale(2);
updateSurface(bounds);
}
@Override
public FunctionNVar getExpression() {
return expression.getFunction();
}
@Override
public boolean isDefined() {
return defined;
}
@Override
public void setUndefined() {
this.defined = false;
}
@Override
public String toValueString(StringTemplate tpl) {
return getDefinition().toValueString(tpl);
}
@Override
public String toString(StringTemplate tpl) {
return label + ": " + toValueString(tpl);
}
@Override
public boolean showInAlgebraView() {
return true;
}
@Override
protected boolean showInEuclidianView() {
return true;
}
@Override
public boolean isEqual(GeoElementND geo) {
return false;
}
@Override
public HitType getLastHitType() {
return HitType.ON_FILLING;
}
@Override
public boolean isTranslateable() {
return true;
}
@Override
public void dilate(NumberValue r, Coords S) {
expression.dilate3D(r, S);
}
@Override
public void translate(Coords v) {
expression.translate3D(v);
}
@Override
public void rotate(NumberValue r) {
expression.rotate(r);
}
@Override
public void rotate(NumberValue r, GeoPointND S) {
expression.rotate(r, S);
}
@Override
public void mirror(Coords Q) {
expression.mirror3D(Q);
}
@Override
public void mirror(GeoLineND g) {
// TODO : implement it correctly
// expression.mirror3D(g);
}
@Override
public void mirror(GeoCoordSys2D plane) {
// TODO implement mirror at plane
}
/**
*
* @param debug
* message to debug
*/
static void debug(String debug) {
if (DEBUG) {
Log.debug(debug);
}
}
/**
*
* @param array
* list of double values
*/
static void debug(double... array) {
debug(toString(array));
}
/**
*
* @param coords
* coordinates
* @return String representation of coordinate
*/
static String toString(double[] coords) {
if (!DEBUG) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append("{");
for (int i = 0; i < coords.length; i++) {
sb.append(coords[i]);
}
sb.append("}");
return sb.toString();
}
private static abstract class ImplicitSurface {
private static final int EMPTY_OR_INVALID = 0x1ff;
protected GeoImplicitSurface s;
public ImplicitSurface(GeoImplicitSurface s) {
this.s = s;
}
private final int[][] EDGE_TABLE = new int[][] { {}, // 0x00, 0xff
{ 0, 4, 3 }, // 0x01, 00000001
{ 0, 5, 1 }, // 0x02, 00000010
{ 1, 3, 4, 4, 5, 1 }, // 0x03, 00000011
{ 1, 2, 6 }, // 0x04, 00000100
{ 0, 4, 3, 1, 2, 6 }, // 0x05, 00000101
{ 6, 5, 0, 0, 2, 6 }, // 0x06, 00000110
{ 6, 5, 4, 6, 4, 3, 3, 2, 6 }, // 0x07, 0x00000111
{ 2, 3, 7 }, // 0x08, 00001000
{ 0, 4, 7, 7, 2, 0 }, // 0x09, 00001001
{ 2, 3, 7, 0, 5, 1 }, // 0x0A, 00001010
{ 5, 4, 7, 7, 2, 1, 1, 5, 7 }, // 0x0B, 00001011
{ 1, 3, 7, 7, 6, 1 }, // 0x0C, 0x00001100
{ 4, 7, 6, 6, 1, 0, 0, 4, 6 }, // 0x0D, 0x00001101
{ 7, 6, 5, 5, 0, 3, 3, 7, 5 }, // 0x0E, 0x00001110
{ 5, 4, 7, 7, 6, 5 }, // 0x0F, 00001111
{ 4, 8, 11 }, // 0x10, 00010000
{ 0, 8, 11, 0, 3, 11 }, // 0x11, 00010001
{ 4, 8, 11, 0, 1, 5 }, // 0x12, 00010010
{ 1, 3, 11, 1, 8, 11, 1, 8, 5 }, // 0x13, 00010011
{ 4, 8, 11, 1, 2, 6 }, // 0x14, 00010100
{ 0, 8, 11, 0, 3, 11, 1, 2, 6 }, // 0x15, 00010101
{ 4, 8, 11, 2, 6, 5, 5, 0, 2 }, // 0x16, 00010110
{ 8, 5, 11, 8, 10, 11, 5, 2, 3, 5, 2, 6 }, // 0x17, 000010111
{ 4, 8, 11, 2, 3, 7 }, // 0x18, 00011000
{ 0, 2, 8, 2, 8, 11, 11, 2, 7 }, // 0x19, 00011001
{ 4, 8, 11, 2, 3, 7, 0, 1, 5 }, // 0x1A, 00011010
{ 1, 5, 8, 1, 2, 8, 2, 8, 11, 2, 7, 11 }, // 0x1B, 00011011
{ 4, 8, 11, 1, 3, 6, 3, 6, 7 }, // 0x1C, 00011100
{ 0, 1, 8, 1, 6, 7, 1, 7, 8, 7, 8, 11 }, // 0x1D, 00011101
{ 5, 6, 7, 0, 5, 7, 3, 7, 0, 4, 8, 11 }, // 0x1E, 00011110
{ 5, 6, 7, 5, 8, 11, 5, 11, 7 }, // 0x1F, 00011111
{ 5, 8, 9 }, // 0x20, 00100000
{ 5, 8, 9, 0, 3, 4 }, // 0x21, 00100001
{ 1, 8, 9, 1, 0, 8 }, // 0x22, 00100010
{ 1, 3, 9, 9, 3, 8, 3, 8, 4 }, // 0x23, 00100011
{ 5, 8, 9, 1, 2, 6 }, // 0x24, 00100100
{ 5, 8, 9, 1, 2, 6, 0, 3, 4 }, // 0x25, 00100101
{ 0, 2, 8, 8, 9, 2, 9, 2, 6 }, // 0x26, 00100110
{ 4, 8, 9, 3, 4, 9, 2, 3, 6, 3, 6, 9 }, // 0x27, 00100111
{ 5, 8, 9, 2, 3, 7 }, // 0x28, 00101000
{ 5, 8, 9, 0, 4, 7, 0, 2, 7 }, // 0x29, 00101001
{ 1, 8, 9, 1, 0, 8, 2, 3, 7 }, // 0x2A, 00101010
{ 2, 4, 7, 2, 4, 8, 1, 2, 8, 1, 8, 9 }, // 0x2B, 00101011
{ 5, 8, 9, 1, 3, 6, 3, 6, 7 }, // 0x2C, 00101100
{ 4, 6, 7, 4, 6, 1, 4, 1, 0, 5, 8, 9 }, // 0x2D, 00101101
{ 0, 3, 8, 3, 8, 9, 3, 7, 9, 6, 7, 9 }, // 0x2E, 00101110
{ 4, 6, 7, 4, 8, 9, 4, 9, 6 }, // 0x2F, 00101111
{ 4, 5, 9, 4, 9, 11 }, // 0x30, 00110000
{ 3, 9, 11, 0, 3, 9, 0, 5, 9 }, // 0x31, 00110001
{ 1, 9, 11, 0, 1, 11, 0, 4, 11 }, // 0x32, 00110010
{ 1, 9, 11, 11, 3, 1 }, // 0x33, 00110011
{ 4, 5, 9, 4, 9, 11, 1, 2, 6 }, // 0x34, 00110100
{ 3, 9, 11, 0, 3, 9, 0, 5, 9, 1, 2, 6 }, // 0x35, 00110101
{ 6, 8, 10, 4, 6, 8, 1, 4, 6, 1, 3, 4 }, // 0x36, 00110110
{ 3, 9, 11, 3, 6, 9, 6, 2, 3 }, // 0x37, 00110111
{ 4, 5, 9, 4, 9, 11, 2, 3, 7 }, // 0x38, 00111000
{ 6, 9, 11, 4, 6, 9, 0, 2, 4, 2, 4, 6 }, // 0x39, 00111001
{ 1, 9, 11, 0, 1, 11, 0, 4, 11, 2, 3, 7 }, // 0x3A, 00111010
{ 1, 9, 11, 1, 2, 7, 1, 7, 11 }, // 0x3B, 00111011
{ 4, 5, 9, 4, 9, 11, 1, 3, 6, 3, 6, 7 }, // 0x3C, 00111100
{ 7, 9, 11, 6, 7, 9, 0, 1, 5 }, // 0x3D, 00111101
{ 7, 9, 11, 6, 7, 9, 0, 3, 4 }, // 0x3E, 00111110
{ 7, 9, 11, 6, 7, 9 }, // 0x3F, 00111111
{ 6, 9, 10 }, // 0x40, 01000000
{ 6, 9, 10, 0, 3, 4 }, // 0x41, 01000001
{ 6, 9, 10, 0, 1, 5 }, // 0x42, 01000010
{ 6, 9, 10, 1, 3, 4, 1, 4, 5 }, // 0x43, 01000011
{ 1, 2, 9, 2, 9, 10 }, // 0x44, 01000100
{ 1, 2, 9, 2, 9, 10, 0, 3, 4 }, // 0x45, 01000101
{ 0, 2, 10, 0, 5, 10, 5, 9, 10 }, // 0x46, 01000110
{ 4, 5, 9, 3, 4, 9, 2, 3, 10, 3, 9, 10 }, // 0x47, 01000111
{ 6, 9, 10, 2, 3, 7 }, // 0x48, 01001000
{ 6, 9, 10, 0, 4, 7, 0, 2, 7 }, // 0x49, 01001001
{ 6, 9, 10, 2, 3, 7, 0, 1, 5 }, // 0x4A, 01001010
{ 4, 5, 7, 2, 5, 7, 1, 2, 5, 6, 9, 10 }, // 0x4B, 01001011
{ 1, 9, 3, 3, 9, 10, 3, 10, 7 }, // 0x4C, 01001100
{ 4, 7, 10, 0, 4, 10, 0, 1, 9, 0, 9, 10 }, // 0x4D, 01001101
{ 0, 3, 5, 3, 5, 9, 3, 7, 9, 7, 9, 10 }, // 0x4E, 01001110
{ 4, 5, 7, 9, 10, 7, 9, 7, 5 }, // 0x4F, 01001111
{ 6, 9, 10, 4, 8, 11 }, // 0x50, 01010000
{ 6, 9, 10, 0, 8, 11, 0, 3, 11 }, // 0x51, 01010001
{ 6, 9, 10, 4, 8, 11, 0, 1, 5 }, // 0x52, 01010010
{ 1, 3, 11, 1, 8, 11, 1, 8, 5, 6, 9, 10 }, // 0x53, 01010011
{ 1, 9, 10, 1, 2, 10, 3, 4, 11 }, // 0x54, 01010100
{ 0, 8, 11, 0, 3, 11, 1, 9, 10, 1, 2, 10 }, // 0x55, 01010101
{ 0, 2, 10, 0, 5, 10, 5, 9, 10, 4, 8, 11 }, // 0x56, 01010110
{ 2, 3, 11, 2, 10, 11, 5, 8, 9 }, // 0x57, 01010111
{ 2, 3, 7, 6, 9, 10, 4, 8, 11 }, // 0x58, 01011000
{ 0, 2, 8, 2, 8, 11, 11, 2, 7, 6, 9, 10 }, // 0x59, 01011001
{ 0, 1, 5, 2, 3, 7, 4, 8, 11, 6, 9, 10 }, // 0x5A, 01011010
{ 1, 2, 6, 5, 8, 9, 7, 11, 10 }, // 0x5B, 01011011
{ 1, 9, 3, 3, 9, 10, 3, 10, 7, 4, 8, 11 }, // 0x5C, 01011100
{ 0, 1, 8, 1, 8, 9, 7, 10, 11 }, // 0x5D, 01011101
{ 0, 3, 4, 5, 8, 9, 7, 10, 11 }, // 0x5E, 01011110
{ 5, 8, 9, 7, 10, 11 }, // 0x5F, 01011111
{ 5, 6, 10, 5, 8, 10 }, // 0x60, 01100000
{ 5, 6, 10, 5, 8, 10, 0, 3, 4 }, // 0x61, 01100001
{ 0, 8, 10, 0, 1, 6, 0, 6, 10 }, // 0x62, 01100010
{ 6, 8, 10, 4, 6, 8, 1, 3, 4, 1, 4, 6 }, // 0x63, 01100011
{ 2, 8, 10, 1, 2, 8, 1, 5, 8 }, // 0x64, 01100100
{ 2, 8, 10, 1, 2, 8, 1, 5, 8, 0, 3, 4 }, // 0x65, 01100101
{ 0, 2, 10, 0, 10, 8 }, // 0x66, 01100110
{ 2, 8, 10, 2, 3, 4, 2, 4, 8 }, // 0x67, 01100111
{ 5, 6, 10, 5, 8, 10, 2, 3, 7 }, // 0x68, 01101000
{ 0, 4, 7, 0, 2, 7, 5, 6, 10, 5, 8, 10 }, // 0x69, 01101001
{ 0, 8, 10, 0, 1, 6, 0, 6, 10, 2, 3, 7 }, // 0x6A, 01101010
{ 4, 8, 10, 10, 7, 4, 1, 2, 6 }, // 0x6B, 01101011
{ 7, 8, 10, 5, 7, 8, 1, 3, 5, 3, 5, 7 }, // 0x6C, 01101100
{ 4, 8, 10, 10, 7, 4, 0, 1, 5 }, // 0x6D, 01101101
{ 0, 8, 10, 0, 3, 10, 3, 7, 10 }, // 0x6E, 01101110
{ 4, 8, 10, 10, 7, 4 }, // 0x6F, 01101111
{ 4, 5, 6, 4, 6, 10, 4, 10, 11 }, // 0x70, 01110000
{ 3, 10, 11, 0, 3, 10, 0, 6, 10, 0, 5, 6 }, // 0x71, 01110001
{ 0, 1, 4, 1, 4, 6, 4, 6, 11, 6, 10, 11 }, // 0x72, 01110010
{ 1, 3, 11, 1, 6, 10, 1, 10, 11 }, // 0x73, 01110011
{ 1, 2, 10, 1, 10, 11, 1, 5, 11, 4, 5, 11 }, // 0x74, 01110100
{ 3, 10, 11, 2, 3, 10, 0, 1, 5 }, // 0x75, 01110101
{ 0, 2, 10, 0, 4, 10, 4, 10, 11 }, // 0x76, 01110110
{ 3, 10, 11, 2, 3, 10 }, // 0x77, 01110111
{ 4, 5, 6, 4, 6, 10, 4, 10, 11, 2, 3, 7 }, // 0x78, 01111000
{ 7, 10, 11, 0, 5, 6, 0, 2, 6 }, // 0x79, 01111001
{ 7, 10, 11, 0, 3, 4, 1, 2, 6 }, // 0x7A, 01111010
{ 7, 10, 11, 1, 2, 6 }, // 0x7B, 01111011
{ 7, 10, 11, 1, 5, 4, 4, 3, 1 }, // 0x7C, 01111100
{ 7, 10, 11, 0, 1, 5 }, // 0x7D, 01111101
{ 7, 10, 11, 0, 3, 4 }, // 0x7E, 01111110
{ 7, 10, 11 }, // 0x7F, 01111111
{ 7, 10, 11 }, // 0x80, 10000000
{ 7, 10, 11, 0, 3, 4 }, // 0x81, 10000001
{ 7, 10, 11, 0, 1, 5 }, // 0x82, 10000010
{ 7, 10, 11, 1, 5, 4, 4, 3, 1 }, // 0x83, 10000011
{ 7, 10, 11, 1, 2, 6 }, // 0x84, 10000100
{ 7, 10, 11, 0, 3, 4, 1, 2, 6 }, // 0x85, 10000101
{ 7, 10, 11, 0, 5, 6, 0, 2, 6 }, // 0x86, 10000110
{ 7, 10, 11, 4, 5, 6, 4, 6, 2, 2, 3, 4 }, // 0x87, 10000111
{ 3, 10, 11, 2, 3, 10 }, // 0x88, 10001000
{ 0, 2, 10, 0, 4, 10, 4, 10, 11 }, // 0x89, 10001001
{ 3, 10, 11, 2, 3, 10, 0, 1, 5 }, // 0x8A, 10001010
{ 1, 4, 5, 1, 4, 11, 1, 2, 11, 2, 10, 11 }, // 0x8B, 10001011
{ 1, 3, 11, 1, 6, 10, 1, 10, 11 }, // 0x8C, 10001100
{ 0, 1, 4, 1, 4, 11, 1, 6, 11, 6, 10, 11 }, // 0x8D, 10001101
{ 0, 3, 10, 3, 10, 11, 0, 5, 6, 0, 6, 10 }, // 0x8E, 10001110
{ 4, 5, 6, 4, 6, 10, 4, 10, 11 }, // 0x8F, 10001111
{ 4, 8, 10, 10, 7, 4 }, // 0x90, 10010000
{ 0, 8, 10, 0, 3, 10, 3, 7, 10 }, // 0x91, 10010001
{ 4, 8, 10, 10, 7, 4, 0, 1, 5 }, // 0x92, 10010010
{ 1, 3, 5, 3, 5, 7, 4, 5, 7, 4, 7, 10 }, // 0x93, 10010011
{ 4, 8, 10, 10, 7, 4, 1, 2, 6 }, // 0x94, 10010100
{ 0, 8, 10, 0, 3, 10, 3, 7, 10, 1, 2, 6 }, // 0x95, 10010101
{ 0, 5, 6, 0, 2, 6, 4, 8, 10, 10, 7, 4 }, // 0x96, 10010110
{ 5, 6, 10, 5, 8, 10, 2, 3, 7 }, // 0x97, 10010111
{ 2, 8, 10, 2, 3, 4, 2, 4, 8 }, // 0x98, 10011000
{ 0, 8, 10, 10, 2, 0 }, // 0x99, 10011001
{ 2, 8, 10, 2, 3, 4, 2, 4, 8, 0, 1, 5 }, // 0x9A, 10011010
{ 2, 8, 10, 1, 2, 5, 2, 5, 8 }, // 0x9B, 10011011
{ 2, 3, 6, 3, 4, 6, 4, 6, 10, 4, 8, 10 }, // 0x9C, 10011100
{ 0, 8, 10, 0, 1, 6, 0, 6, 10 }, // 0x9D, 10011101
{ 5, 6, 10, 5, 8, 10, 0, 3, 4 }, // 0x9E, 10011110
{ 5, 6, 10, 5, 8, 10 }, // 0x9F, 10011111
{ 7, 10, 11, 5, 8, 9 }, // 0xA0, 10100000
{ 7, 10, 11, 5, 8, 9, 0, 3, 4 }, // 0xA1, 10100001
{ 7, 10, 11, 0, 1, 8, 1, 8, 9 }, // 0xA2, 10100010
{ 1, 3, 9, 9, 3, 8, 3, 8, 4, 7, 10, 11 }, // 0xA3, 10100011
{ 7, 10, 11, 5, 8, 9, 1, 2, 6 }, // 0xA4, 10100100
{ 7, 10, 11, 5, 8, 9, 1, 2, 6, 0, 3, 4 }, // 0xA5, 10100101
{ 7, 10, 11, 0, 2, 8, 8, 9, 2, 9, 2, 6 }, // 0xA6, 10100110
{ 6, 9, 10, 4, 8, 11, 2, 3, 7 }, // 0xA7, 10100111
{ 3, 10, 11, 2, 3, 10, 5, 8, 9 }, // 0xA8, 10101000
{ 5, 8, 9, 0, 2, 10, 0, 4, 10, 4, 10, 11 }, // 0xA9, 10101001
{ 3, 10, 11, 2, 3, 10, 0, 1, 8, 1, 8, 9 }, // 0xAA, 10101010
{ 1, 9, 10, 1, 2, 10, 4, 8, 11 }, // 0xAB, 10101011
{ 5, 8, 9, 1, 3, 11, 1, 6, 10, 1, 10, 11, }, // 0xAC, 10101100
{ 6, 9, 10, 4, 8, 11, 0, 1, 5 }, // 0xAD, 10101101
{ 0, 8, 11, 0, 3, 11, 6, 9, 10 }, // 0xAE, 10101110
{ 6, 9, 10, 4, 8, 11 }, // 0xAF, 10101111
{ 4, 5, 7, 5, 7, 10, 5, 9, 10 }, // 0xB0, 10110000
{ 0, 3, 5, 3, 5, 7, 5, 7, 9, 7, 9, 10 }, // 0xB1, 10110001
{ 0, 1, 9, 0, 9, 10, 0, 4, 10, 4, 7, 10 }, // 0xB2, 10110010
{ 1, 3, 9, 3, 9, 10, 3, 7, 10 }, // 0xB3, 10110011
{ 1, 2, 6, 4, 5, 7, 5, 7, 10, 5, 9, 10 }, // 0xB4, 10110100
{ 6, 9, 10, 2, 3, 7, 0, 1, 5 }, // 0xB5, 10110101
{ 0, 4, 7, 0, 2, 7, 6, 9, 10 }, // 0xB6, 10110110
{ 6, 9, 10, 2, 3, 7 }, // 0xB7, 10110111
{ 4, 5, 7, 5, 7, 10, 5, 9, 10, 1, 2, 6 }, // 0xB8, 10111000
{ 0, 2, 10, 0, 5, 9, 0, 9, 10 }, // 0xB9, 10111001
{ 0, 3, 4, 1, 2, 10, 1, 9, 10 }, // 0xBA, 10111010
{ 1, 2, 10, 1, 9, 10 }, // 0xBB, 10111011
{ 6, 9, 10, 1, 5, 4, 4, 3, 1 }, // 0xBC, 10111100
{ 0, 1, 5, 6, 9, 10 }, // 0xBD, 10111101
{ 0, 3, 4, 6, 9, 10 }, // 0xBE, 10111110
{ 6, 9, 10 }, // 0xBF, 10111111
{ 6, 7, 9, 7, 9, 11 }, // 0xC0, 11000000
{ 0, 3, 4, 6, 7, 9, 7, 9, 11 }, // 0xC1, 11000001
{ 0, 1, 5, 6, 7, 9, 7, 9, 11 }, // 0xC2, 11000010
{ 1, 3, 4, 1, 4, 5, 6, 7, 9, 7, 9, 11 }, // 0xC3, 11000011
{ 1, 9, 11, 1, 2, 7, 1, 7, 11 }, // 0xC4, 11000100
{ 0, 3, 4, 1, 9, 11, 1, 2, 7, 1, 7, 11 }, // 0xC5, 11000101
{ 0, 2, 7, 0, 5, 7, 5, 7, 11, 5, 9, 11 }, // 0xC6, 11000110
{ 4, 5, 9, 4, 9, 11, 2, 3, 7 }, // 0xC7, 11000111
{ 3, 9, 11, 2, 3, 9, 2, 6, 9 }, // 0xC8, 11001000
{ 0, 2, 4, 2, 4, 6, 4, 6, 11, 6, 9, 11 }, // 0xC9, 11001001n
{ 0, 1, 5, 3, 9, 11, 2, 3, 9, 2, 6, 9 }, // 0xCA, 11001010
{ 4, 5, 9, 4, 9, 11, 1, 2, 6 }, // 0xCB, 11001011
{ 1, 9, 11, 11, 3, 1 }, // 0xCC, 11001100
{ 1, 9, 11, 0, 1, 4, 1, 4, 11 }, // 0xCD, 11001101
{ 3, 9, 11, 0, 3, 9, 0, 5, 9 }, // 0xCE, 11001110
{ 4, 5, 9, 4, 9, 11 }, // 0xCF, 11001111
{ 4, 7, 6, 4, 6, 8, 6, 8, 9 }, // 0xD0, 11010000
{ 0, 3, 8, 3, 8, 9, 3, 7, 9, 6, 7, 9 }, // 0xD1, 11010001
{ 0, 1, 5, 4, 7, 6, 4, 6, 8, 6, 8, 9 }, // 0xD2, 11010010
{ 5, 8, 9, 1, 6, 7, 1, 3, 7 }, // 0xD3, 11010011
{ 2, 4, 7, 2, 4, 8, 1, 2, 8, 1, 8, 9 }, // 0xD4, 11010100
{ 0, 1, 8, 1, 8, 9, 2, 3, 7 }, // 0xD5, 11010101
{ 5, 8, 9, 0, 4, 7, 0, 2, 7 }, // 0xD6, 11010110
{ 5, 8, 9, 2, 3, 7 }, // 0xD7, 11010111
{ 3, 4, 8, 2, 3, 9, 3, 8, 9, 2, 6, 9 }, // 0xD8, 11011000
{ 0, 2, 8, 2, 6, 9, 2, 8, 9 }, // 0xD9, 11011001
{ 0, 3, 4, 5, 8, 9, 1, 2, 6 }, // 0xDA, 11011010
{ 5, 8, 9, 1, 2, 6 }, // 0xDB, 11011011
{ 1, 3, 9, 3, 4, 8, 8, 9, 3 }, // 0xDC, 11011100
{ 0, 1, 8, 1, 8, 9 }, // 0xDD, 11011101
{ 5, 8, 9, 0, 3, 4 }, // 0xDE, 11011110
{ 5, 8, 9 }, // 0xDF, 11011111
{ 5, 6, 7, 5, 7, 8, 7, 8, 11 }, // 0xE0, 11100000
{ 0, 3, 4, 5, 6, 7, 5, 7, 8, 7, 8, 11 }, // 0xE1, 11100001
{ 0, 1, 11, 0, 8, 11, 1, 7, 11, 1, 6, 7 }, // 0xE2, 11100010
{ 1, 6, 7, 1, 3, 7, 4, 8, 11 }, // 0xE3, 11100011
{ 1, 5, 8, 1, 2, 8, 2, 8, 11, 2, 7, 11 }, // 0xE4, 11100100
{ 4, 8, 11, 2, 3, 7, 0, 1, 5 }, // 0xE5, 11100101
{ 0, 2, 8, 2, 7, 8, 7, 8, 11 }, // 0xE6, 11100110
{ 4, 8, 11, 2, 3, 7 }, // 0xE7, 11100111
{ 2, 3, 11, 2, 8, 11, 2, 6, 8, 5, 6, 8 }, // 0xE8, 11101000
{ 0, 5, 6, 0, 2, 6, 4, 8, 11 }, // 0xE9, 11101001
{ 0, 8, 11, 0, 3, 11, 1, 2, 6 }, // 0xEA, 11101010
{ 4, 8, 11, 1, 2, 6 }, // 0xEB, 11101011
{ 1, 3, 11, 1, 5, 8, 1, 8, 11 }, // 0xEC, 11101100
{ 0, 1, 5, 4, 8, 11 }, // 0xED, 11101101
{ 0, 8, 11, 0, 3, 11 }, // 0xEE, 11101110
{ 4, 8, 11 }, // 0xEF, 11101111
{ 4, 5, 6, 6, 7, 4 }, // 0xF0, 11110000
{ 5, 6, 7, 0, 5, 7, 0, 3, 7 }, // 0xF1, 11110001
{ 4, 6, 7, 0, 1, 6, 0, 4, 6 }, // 0xF2, 11110010
{ 1, 6, 7, 1, 3, 7 }, // 0xF3, 11110011
{ 4, 5, 7, 2, 5, 7, 1, 2, 5 }, // 0xF4, 11110100
{ 2, 3, 7, 0, 1, 5 }, // 0xF5, 11110101
{ 0, 2, 4, 2, 4, 7 }, // 0xF6, 11110110
{ 2, 3, 7 }, // 0xF7, 11110111
{ 4, 5, 6, 4, 6, 2, 2, 3, 4 }, // 0xF8, 11111000
{ 6, 5, 0, 0, 2, 6 }, // 0xF9, 11111001
{ 0, 3, 4, 1, 2, 6 }, // 0xFA, 11111010
{ 1, 2, 6 }, // 0xFB, 11111011
{ 1, 5, 4, 1, 3, 4 }, // 0xFC, 11111100
{ 0, 1, 5 }, // 0xFD, 11111101
{ 0, 3, 4 }, // 0xFE, 11111110
{}, // 0xFF, 11111111
};
protected double x1;
protected double y1;
protected double z1;
protected double x2;
protected double y2;
protected double z2;
protected double fracX;
protected double fracY;
protected double fracZ;
protected double scaleX;
protected double scaleY;
protected double scaleZ;
protected GeoTriangulatedSurface3D surf;
private final Coords p1 = new Coords(0, 0, 0);
private final Coords p2 = new Coords(0, 0, 0);
private final Coords p3 = new Coords(0, 0, 0);
private final Coords p4 = new Coords(0, 0, 0);
private final Coords p5 = new Coords(0, 0, 0);
private final Coords n1 = new Coords(0, 0, 0);
private final Coords n2 = new Coords(0, 0, 0);
private final Coords n3 = new Coords(0, 0, 0);
public void update(double[] bounds) {
this.x1 = bounds[0];
this.y1 = bounds[2];
this.z1 = bounds[4];
this.x2 = bounds[1];
this.y2 = bounds[3];
this.z2 = bounds[5];
this.scaleX = bounds[6];
this.scaleY = bounds[7];
this.scaleZ = bounds[8];
this.surf = s.getSurface3D();
this.update();
}
public abstract void update();
public int config(Cube cube) {
int config = cube.sign(Cube.V7);
config = (config << 1) | cube.sign(Cube.V6);
config = (config << 1) | cube.sign(Cube.V5);
config = (config << 1) | cube.sign(Cube.V4);
config = (config << 1) | cube.sign(Cube.V3);
config = (config << 1) | cube.sign(Cube.V2);
config = (config << 1) | cube.sign(Cube.V1);
config = (config << 1) | cube.sign(Cube.V0);
return config <= 0 || config == 0xff ? EMPTY_OR_INVALID : config;
}
public void addSurface(Cube cube) {
int config = config(cube);
double det;
if (config != EMPTY_OR_INVALID) {
int[] edges = EDGE_TABLE[config];
int len = edges.length;
for (int i = 0; i < len; i += 3) {
surf.beginTriangulation();
cube.pointOfIntersection(edges[i], p1.val);
cube.pointOfIntersection(edges[i + 1], p2.val);
cube.pointOfIntersection(edges[i + 2], p3.val);
p2.sub(p1, p4);
p3.sub(p1, p5);
s.evaluateNormalAt(p1, n1);
s.evaluateNormalAt(p2, n2);
s.evaluateNormalAt(p3, n3);
det = p4.dotCrossProduct(n1, p5);
if (det < 0) {
surf.insertPoint(p1.val, n1.val);
surf.insertPoint(p2.val, n2.val);
surf.insertPoint(p3.val, n3.val);
} else {
surf.insertPoint(p1.val, n1.val);
surf.insertPoint(p3.val, n3.val);
surf.insertPoint(p2.val, n2.val);
}
surf.endTriangulation();
}
}
}
}
private static class MarchingCube extends ImplicitSurface {
private static final int AVE_PXL = 40;
private static final int MAX_SUB_DIV = 25;
private int sizeX = 20;
private int sizeY = 20;
private int sizeZ = 20;
public MarchingCube(GeoImplicitSurface s) {
super(s);
}
private static int pixels(double c1, double c2, double scale) {
return (int) Math.ceil((Math.abs(c1 - c2) * scale));
}
@Override
public void update() {
sizeX = Math.min(MAX_SUB_DIV, pixels(x1, x2, scaleX) / AVE_PXL + 1);
sizeY = Math.min(MAX_SUB_DIV, pixels(y1, y2, scaleY) / AVE_PXL + 1);
sizeZ = Math.min(MAX_SUB_DIV, pixels(z1, z2, scaleZ) / AVE_PXL + 1);
debug("{x:" + sizeX + ";y:" + sizeY + ";z:" + sizeZ + "}");
this.fracX = (x2 - x1) / sizeX;
this.fracY = (y2 - y1) / sizeY;
this.fracZ = (z2 - z1) / sizeZ;
double[] xcoords = new double[sizeX + 1];
double[] ycoords = new double[sizeY + 1];
double[] zcoords = new double[sizeZ + 1];
double[][] grid2d = new double[sizeY + 1][sizeX + 1];
double[] grid1d = new double[sizeX + 1];
double cur, prev;
Cube cube = new Cube();
for (int i = 0; i <= sizeX; i++) {
xcoords[i] = x1 + i * fracX;
}
for (int i = 0; i <= sizeY; i++) {
ycoords[i] = y1 + i * fracY;
}
for (int i = 0; i <= sizeZ; i++) {
zcoords[i] = z1 + i * fracZ;
}
for (int i = 0; i <= sizeY; i++) {
for (int j = 0; j <= sizeX; j++) {
grid2d[i][j] = s.evaluateAt(xcoords[j], ycoords[i], z1);
}
}
for (int k = 1; k <= sizeZ; k++) {
for (int i = 0; i <= sizeX; i++) {
grid1d[i] = s.evaluateAt(xcoords[i], y1, zcoords[k]);
}
cube.coords[Cube.Z1] = zcoords[k - 1];
cube.coords[Cube.Z2] = zcoords[k];
for (int i = 1; i <= sizeY; i++) {
prev = s.evaluateAt(x1, ycoords[i], zcoords[k]);
cube.coords[Cube.Y1] = ycoords[i - 1];
cube.coords[Cube.Y2] = ycoords[i];
for (int j = 1; j <= sizeX; j++) {
cur = s.evaluateAt(xcoords[j], ycoords[i], zcoords[k]);
cube.coords[Cube.X1] = xcoords[j - 1];
cube.coords[Cube.X2] = xcoords[j];
cube.cache[Cube.V0] = prev;
cube.cache[Cube.V1] = cur;
cube.cache[Cube.V2] = grid2d[i][j];
cube.cache[Cube.V3] = grid2d[i][j - 1];
cube.cache[Cube.V4] = grid1d[j - 1];
cube.cache[Cube.V5] = grid1d[j];
cube.cache[Cube.V6] = grid2d[i - 1][j];
cube.cache[Cube.V7] = grid2d[i - 1][j - 1];
super.addSurface(cube);
grid2d[i - 1][j - 1] = grid1d[j - 1];
grid1d[j - 1] = prev;
prev = cur;
}
grid2d[i - 1][sizeX] = grid1d[sizeX];
grid1d[sizeX] = prev;
}
System.arraycopy(grid1d, 0, grid2d[sizeY], 0, sizeX + 1);
}
}
}
// Here is vertices and edges numbering convention used throughout the
// marching cube. Thus we can see vertices 7 and 1 map to (x1, y1, z1) and
// (x2, y2, z2) respectively
// ...........0___________0____________1
// ........../|......................./|
// ........./.|....................../.|
// ......../..|...................../..|
// .......4...|....................5...|
// ....../....|.................../....|
// ...../.....3................../.....1
// ..../......|................./......|
// ...4_______|____8___________5.......|
// ...|.......|................|.......|
// ...|.......|................|.......|
// ...|.......3__________2_____|_______2
// ...|....../.................|....../
// ..11...../..................9...../
// ...|..../...................|..../
// ...|...7....................|...6
// ...|../.....................|../
// ...|./......................|./
// ...7/___________10__________6/
//
private static class Cube {
public static final int X1 = 0x00;
public static final int Y1 = 0x01;
public static final int Z1 = 0x02;
public static final int X2 = 0x03;
public static final int Y2 = 0x04;
public static final int Z2 = 0x05;
public static final int V0 = 0x00;
public static final int V1 = 0x01;
public static final int V2 = 0x02;
public static final int V3 = 0x03;
public static final int V4 = 0x04;
public static final int V5 = 0x05;
public static final int V6 = 0x06;
public static final int V7 = 0x07;
private static final int[][] EDGES = { { 0, 1 }, { 1, 2 }, { 2, 3 },
{ 3, 0 }, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, { 4, 5 },
{ 5, 6 }, { 6, 7 }, { 7, 4 } };
private static final int[][] VERTICES = { { 0, 4, 5 }, { 3, 4, 5 },
{ 3, 4, 2 }, { 0, 4, 2 }, { 0, 1, 5 }, { 3, 1, 5 }, { 3, 1, 2 },
{ 0, 1, 2 } };
/**
* Coordinates of the cube (x1, y1, z1) (x2, y2, z2)
*/
public double[] coords = new double[6];
/**
* Cached evaluated value at each corner of the cube
*/
public double[] cache = new double[8];
protected Cube() {
}
/**
* Fill point array with point of intersection of edge e with the plane.
* Caveat: It does not check if the given edge intersect with the
* surface
*
* @param e
* the edge number as per the convention
* @param pts
* an array of length >= 3
*/
public void pointOfIntersection(int e, double[] pts) {
int[] v = EDGES[e];
int[] v1 = VERTICES[v[0]];
int[] v2 = VERTICES[v[1]];
double fa = eval(v[0]);
double fb = eval(v[1]);
if ((e & 4) == 4) {
// y is changing (edges 4, 5, 6, and 7)
pts[0] = coords[v1[0]];
pts[1] = interpolate(fa, fb, coords[v1[1]], coords[v2[1]]);
pts[2] = coords[v1[2]];
} else if ((e & 1) == 1) {
// z is changing (odd numbered edges)
pts[0] = coords[v1[0]];
pts[1] = coords[v1[1]];
pts[2] = interpolate(fa, fb, coords[v1[2]], coords[v2[2]]);
} else {
// x is changing (even numbered edges)
pts[0] = interpolate(fa, fb, coords[v1[0]], coords[v2[0]]);
pts[1] = coords[v1[1]];
pts[2] = coords[v1[2]];
}
}
/**
* Sign of the vertex
*
* @param vertex
* vertex
* @return isFinite(v) ? ((v <= 0) ? 0 : 1) : -1, where v is evaluated
* value at given vertex
*
*/
public int sign(int vertex) {
double v = eval(vertex);
if (MyDouble.isFinite(v)) {
return v <= 0.0 ? 0 : 1;
}
return -1;
}
public double eval(int vertex) {
return cache[vertex];
}
public static double interpolate(double fa, double fb, double p1,
double p2) {
double r = -fb / (fa - fb);
if (r <= 1.0 && r >= 0.0) {
return r * (p1 - p2) + p2;
}
return p2 + (p1 - p2) * 0.5;
}
}
@Override
public boolean isFillable() {
return true;
}
@Override
public ValueType getValueType() {
return ValueType.PARAMETRIC3D;
}
@Override
public Equation getEquation() {
return kernel.getAlgebraProcessor().parseEquation(this);
}
}