diff --git a/MeteoInfoLab/milconfig.xml b/MeteoInfoLab/milconfig.xml
index 14563164..755df592 100644
--- a/MeteoInfoLab/milconfig.xml
+++ b/MeteoInfoLab/milconfig.xml
@@ -1,6 +1,6 @@
-
+
@@ -21,22 +21,22 @@
-
-
+
+
-
-
+
+
-
+
diff --git a/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl$py.class b/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl$py.class
index b1d1078d..d83bf572 100644
Binary files a/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl$py.class and b/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl$py.class differ
diff --git a/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl.py b/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl.py
index a07e4f8e..54244d25 100644
--- a/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl.py
+++ b/MeteoInfoLab/pylib/mipylib/plotlib/axes3dgl.py
@@ -7,8 +7,9 @@
#-----------------------------------------------------
from org.meteoinfo.chart.plot import GraphicFactory
-from org.meteoinfo.legend import BreakTypes
+from org.meteoinfo.legend import BreakTypes, LegendManage
from org.meteoinfo.layer import LayerTypes
+from org.meteoinfo.shape import ShapeTypes
from org.meteoinfo.chart.jogl import Plot3DGL, GLForm, JOGLUtil
from javax.swing import WindowConstants
from java.awt import Font, Color, BasicStroke
@@ -98,6 +99,31 @@ class Axes3DGL(Axes3D):
'''
self.axes.setAngleX(elevation)
+ def set_lighting(self, enable, **kwargs):
+ '''
+ Set lighting.
+
+ :param enable: (*boolean*) Set lighting enable or not.
+ :param position: (*list of float*) Lighting position.
+ :param ambient: (*list of float*) Ambient light.
+ :param diffuse: (*list of float*) Diffuse light.
+ :param specular: (*list of float*) Specular light.
+ '''
+ lighting = self.axes.getLighting()
+ lighting.setEnable(enable)
+ position = kwargs.pop('position', None)
+ if not position is None:
+ lighting.setPosition(position)
+ ambient = kwargs.pop('ambient', None)
+ if not ambient is None:
+ lighting.setAmbient(ambient)
+ diffuse = kwargs.pop('diffuse', None)
+ if not diffuse is None:
+ lighting.setDiffuse(diffuse)
+ specular = kwargs.pop('specular', None)
+ if not specular is None:
+ lighting.setSpecular(specular)
+
def plot_layer(self, layer, **kwargs):
'''
Plot a layer in 3D axes.
@@ -135,6 +161,63 @@ class Axes3DGL(Axes3D):
if visible:
self.add_graphic(graphics)
return graphics
+
+ def plot_isosurface(self, *args, **kwargs):
+ '''
+ creates a three-dimensional isosurface plot
+
+ :param x: (*array_like*) Optional. X coordinate array.
+ :param y: (*array_like*) Optional. Y coordinate array.
+ :param z: (*array_like*) Optional. Z coordinate array.
+ :param data: (*array_like*) 3D data array.
+ :param cmap: (*string*) Color map string.
+ :param xyaxis: (*boolean*) Draw x and y axis or not.
+ :param zaxis: (*boolean*) Draw z axis or not.
+ :param grid: (*boolean*) Draw grid or not.
+ :param boxed: (*boolean*) Draw boxed or not.
+ :param mesh: (*boolean*) Draw mesh line or not.
+
+ :returns: Legend
+ '''
+ if len(args) <= 3:
+ x = args[0].dimvalue(2)
+ y = args[0].dimvalue(1)
+ z = args[0].dimvalue(0)
+ data = args[0]
+ isovalue = args[1]
+ args = args[2:]
+ else:
+ x = args[0]
+ y = args[1]
+ z = args[2]
+ data = args[3]
+ isovalue = args[4]
+ args = args[5:]
+ cmap = plotutil.getcolormap(**kwargs)
+ cvalue = kwargs.pop('cvalue', None)
+ if not cvalue is None:
+ if len(args) > 0:
+ level_arg = args[0]
+ if isinstance(level_arg, int):
+ cn = level_arg
+ ls = LegendManage.createLegendScheme(data.min(), data.max(), cn, cmap)
+ else:
+ if isinstance(level_arg, NDArray):
+ level_arg = level_arg.aslist()
+ ls = LegendManage.createLegendScheme(data.min(), data.max(), level_arg, cmap)
+ else:
+ ls = LegendManage.createLegendScheme(data.min(), data.max(), cmap)
+ ls = ls.convertTo(ShapeTypes.Polygon)
+ edge = kwargs.pop('edge', True)
+ kwargs['edge'] = edge
+ plotutil.setlegendscheme(ls, **kwargs)
+ else:
+ ls = plotutil.getlegendbreak('polygon', **kwargs)[0]
+ graphics = JOGLUtil.isosurface(data.asarray(), x.asarray(), y.asarray(), z.asarray(), isovalue, ls)
+ visible = kwargs.pop('visible', True)
+ if visible:
+ self.add_graphic(graphics)
+ return graphics
def view(self):
'''
diff --git a/MeteoInfoLab/pylib/sync.ffs_db b/MeteoInfoLab/pylib/sync.ffs_db
deleted file mode 100644
index a545e159..00000000
Binary files a/MeteoInfoLab/pylib/sync.ffs_db and /dev/null differ
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/IsosurfaceGraphics.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/IsosurfaceGraphics.java
new file mode 100644
index 00000000..844861fc
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/IsosurfaceGraphics.java
@@ -0,0 +1,57 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.meteoinfo.chart.plot3d.GraphicCollection3D;
+import org.meteoinfo.global.Extent3D;
+import org.meteoinfo.global.MIMath;
+import org.meteoinfo.shape.PointZ;
+
+/**
+ *
+ * @author yaqiang
+ */
+public class IsosurfaceGraphics extends GraphicCollection3D {
+ private List triangles = new ArrayList<>();
+
+ /**
+ * Constructor
+ */
+ public IsosurfaceGraphics() {
+ this.allTriangle = true;
+ }
+
+ /**
+ * Get triangles
+ * @return Triangles
+ */
+ public List getTriangles() {
+ return this.triangles;
+ }
+
+ /**
+ * Set triangles
+ * @param value Triangles
+ */
+ public void setTriangles(List value) {
+ this.triangles = value;
+ }
+
+ /**
+ * Add a triangle
+ * @param triangle Triangle
+ */
+ public void addTriangle(PointZ[] triangle) {
+ this.triangles.add(triangle);
+ Extent3D extent = MIMath.getExtent(triangle);
+ if (this.triangles.size() == 1)
+ this.setExtent(extent);
+ else
+ this.setExtent(MIMath.getLagerExtent(extent, this.getExtent()));
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java
index 4f1f0060..3d8cf6ef 100644
--- a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java
@@ -6,21 +6,23 @@
package org.meteoinfo.chart.jogl;
import com.jogamp.opengl.GL2;
-import com.jogamp.opengl.util.texture.Texture;
-import com.jogamp.opengl.util.texture.TextureIO;
-import java.io.File;
+import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import org.meteoinfo.chart.jogl.mc.MarchingCubes;
import org.meteoinfo.chart.plot3d.GraphicCollection3D;
import org.meteoinfo.global.Extent;
import org.meteoinfo.global.Extent3D;
import org.meteoinfo.layer.ImageLayer;
import org.meteoinfo.legend.ColorBreak;
+import org.meteoinfo.legend.PolygonBreak;
+import org.meteoinfo.ndarray.Array;
import org.meteoinfo.shape.Graphic;
import org.meteoinfo.shape.GraphicCollection;
import org.meteoinfo.shape.ImageShape;
import org.meteoinfo.shape.PointZ;
+import org.meteoinfo.shape.PolygonZShape;
/**
*
@@ -64,4 +66,69 @@ public class JOGLUtil {
return graphics;
}
+
+ /**
+ * Create isosurface graphics
+ * @param data 3d data array
+ * @param x X coordinates
+ * @param y Y coordinates
+ * @param z Z coordinates
+ * @param isoLevel iso level
+ * @param pb Polygon break
+ * @return Graphics
+ */
+ public static GraphicCollection isosurface(Array data, Array x, Array y, Array z,
+ float isoLevel, PolygonBreak pb) {
+ List vertices = MarchingCubes.marchingCubes(data, x, y, z, isoLevel);
+ IsosurfaceGraphics graphics = new IsosurfaceGraphics();
+ graphics.setLegendBreak(pb);
+ float[] v1, v2, v3;
+ for (int i = 0; i < vertices.size(); i += 3) {
+ PointZ[] points = new PointZ[3];
+ v1 = vertices.get(i);
+ v2 = vertices.get(i + 1);
+ v3 = vertices.get(i + 2);
+ points[0] = new PointZ(v1[0], v1[1], v1[2]);
+ points[1] = new PointZ(v2[0], v2[1], v2[2]);
+ points[2] = new PointZ(v3[0], v3[1], v3[2]);
+ graphics.addTriangle(points);
+ }
+
+ return graphics;
+ }
+
+ /**
+ * Create isosurface graphics
+ * @param data 3d data array
+ * @param x X coordinates
+ * @param y Y coordinates
+ * @param z Z coordinates
+ * @param isoLevel iso level
+ * @param pb Polygon break
+ * @return Graphics
+ */
+ public static GraphicCollection isosurface_bak(Array data, Array x, Array y, Array z,
+ float isoLevel, PolygonBreak pb) {
+ List vertices = MarchingCubes.marchingCubes(data, x, y, z, isoLevel);
+ GraphicCollection3D graphics = new GraphicCollection3D();
+ pb.setColor(Color.cyan);
+ float[] v1, v2, v3;
+ for (int i = 0; i < vertices.size(); i += 3) {
+ PolygonZShape ps = new PolygonZShape();
+ List points = new ArrayList<>();
+ v1 = vertices.get(i);
+ v2 = vertices.get(i + 1);
+ v3 = vertices.get(i + 2);
+ points.add(new PointZ(v1[0], v1[1], v1[2]));
+ points.add(new PointZ(v2[0], v2[1], v2[2]));
+ points.add(new PointZ(v3[0], v3[1], v3[2]));
+ points.add(new PointZ(v1[0], v1[1], v1[2]));
+ ps.setPoints(points);
+ Graphic graphic = new Graphic(ps, pb);
+ graphics.add(graphic);
+ }
+ graphics.setAllTriangle(true);
+
+ return graphics;
+ }
}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Lighting.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Lighting.java
new file mode 100644
index 00000000..3c761b2c
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Lighting.java
@@ -0,0 +1,308 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl;
+
+import com.jogamp.opengl.GL2;
+import java.awt.Color;
+import java.util.List;
+
+/**
+ *
+ * @author Yaqiang Wang
+ */
+public class Lighting {
+
+ private boolean enable;
+ private int light;
+ float[] ambient;
+ float[] diffuse;
+ float[] specular;
+ float[] position;
+ float[] mat_ambient;
+ float[] mat_specular;
+ float mat_shininess;
+
+ /**
+ * Constructor
+ */
+ public Lighting() {
+ this(false);
+ }
+
+ /**
+ * Constructor
+ * @param far Far light position or not
+ */
+ public Lighting(boolean far) {
+ this.enable = false;
+ this.light = GL2.GL_LIGHT0;
+ //this.ambient = new float[]{0.f, 0.f, 0.f, 1.f};
+ this.ambient = new float[]{0.2f, 0.2f, 0.2f, 1.f};
+ this.diffuse = new float[]{1.f, 1.f, 1.f, 1.f};
+ this.specular = new float[]{1.f, 1.f, 1.f, 1.f};
+ if (far)
+ this.position = new float[]{0.f, 0.f, 1.f, 0.f};
+ else
+ this.position = new float[]{0.f, 1.f, 0.f, 1.f};
+ this.mat_ambient = new float[]{0.f, 0.f, 0.f, 0.f};
+ this.mat_specular = new float[]{ 1.0f, 1.0f, 1.0f, 1.0f };
+ this.mat_shininess = 50.0f;
+ }
+
+ /**
+ * Get enable lighting or not
+ *
+ * @return Boolean
+ */
+ public boolean isEnable() {
+ return this.enable;
+ }
+
+ /**
+ * Set enable lighting or not
+ *
+ * @param value Boolean
+ */
+ public void setEnable(boolean value) {
+ this.enable = value;
+ }
+
+ /**
+ * Get ambient
+ *
+ * @return Ambient
+ */
+ public float[] getAmbient() {
+ return this.ambient;
+ }
+
+ /**
+ * Set ambient
+ *
+ * @param value Ambient
+ */
+ public void setAmbient(float[] value) {
+ this.ambient = value;
+ }
+
+ /**
+ * Set ambient
+ *
+ * @param value Ambient
+ */
+ public void setAmbient(List value) {
+ if (value.size() < 4) {
+ return;
+ }
+
+ this.ambient = new float[]{(float) value.get(0), (float) value.get(1), (float) value.get(2),
+ (float) value.get(3)};
+ }
+
+ /**
+ * Set ambient
+ *
+ * @param value Color
+ */
+ public void setAmbient(Color value) {
+ this.ambient = value.getRGBComponents(null);
+ }
+
+ /**
+ * Get diffuse
+ *
+ * @return Diffuse
+ */
+ public float[] getDiffuse() {
+ return this.diffuse;
+ }
+
+ /**
+ * Set diffuse
+ *
+ * @param value Diffuse
+ */
+ public void setDiffuse(float[] value) {
+ this.diffuse = value;
+ }
+
+ /**
+ * Set diffuse
+ *
+ * @param value Diffuse
+ */
+ public void setDiffuse(List value) {
+ if (value.size() < 4) {
+ return;
+ }
+
+ this.diffuse = new float[]{(float) value.get(0), (float) value.get(1), (float) value.get(2),
+ (float) value.get(3)};
+ }
+
+ /**
+ * Set diffuse
+ *
+ * @param value Color
+ */
+ public void setDiffuse(Color value) {
+ this.diffuse = value.getRGBComponents(null);
+ }
+
+ /**
+ * Get specular
+ *
+ * @return Specular
+ */
+ public float[] getSpecular() {
+ return this.specular;
+ }
+
+ /**
+ * Set specular
+ *
+ * @param value Specular
+ */
+ public void setSpecular(float[] value) {
+ this.specular = value;
+ }
+
+ /**
+ * Set specular
+ *
+ * @param value Specular
+ */
+ public void setSpecular(List value) {
+ if (value.size() < 4) {
+ return;
+ }
+
+ this.specular = new float[]{(float) value.get(0), (float) value.get(1), (float) value.get(2),
+ (float) value.get(3)};
+ }
+
+ /**
+ * Set specular
+ *
+ * @param value Color
+ */
+ public void setSpecular(Color value) {
+ this.specular = value.getRGBComponents(null);
+ }
+
+ /**
+ * Get position
+ *
+ * @return Position
+ */
+ public float[] getPosition() {
+ return this.position;
+ }
+
+ /**
+ * Set position
+ *
+ * @param value Position
+ */
+ public void setPosition(float[] value) {
+ this.position = value;
+ }
+
+ /**
+ * Set position
+ *
+ * @param value Position
+ */
+ public void setPosition(List value) {
+ if (value.size() < 4) {
+ return;
+ }
+
+ this.position = new float[]{(float) value.get(0), (float) value.get(1), (float) value.get(2),
+ (float) value.get(3)};
+ }
+
+ /**
+ * Set material ambient light
+ * @param value Material ambient light
+ */
+ public void setMat_Ambient(float[] value) {
+ this.mat_ambient = value;
+ }
+
+ /**
+ * Set material ambient light
+ *
+ * @param value Material ambient light
+ */
+ public void setMat_Ambient(List value) {
+ if (value.size() < 4) {
+ return;
+ }
+
+ this.mat_ambient = new float[]{(float) value.get(0), (float) value.get(1), (float) value.get(2),
+ (float) value.get(3)};
+ }
+
+ /**
+ * Set material specular light
+ * @param value Material specular light
+ */
+ public void setMat_Specular(float[] value) {
+ this.mat_specular = value;
+ }
+
+ /**
+ * Set material specular light
+ *
+ * @param value Material specular light
+ */
+ public void setMat_Specular(List value) {
+ if (value.size() < 4) {
+ return;
+ }
+
+ this.mat_specular = new float[]{(float) value.get(0), (float) value.get(1), (float) value.get(2),
+ (float) value.get(3)};
+ }
+
+ /**
+ * Set material shininess
+ * @param value Material shininess
+ */
+ public void setMat_Shininess(float value) {
+ this.mat_shininess = value;
+ }
+
+ /**
+ * Start the lighting
+ *
+ * @param gl GL2
+ */
+ public void start(GL2 gl) {
+ gl.glEnable(GL2.GL_LIGHTING);
+ gl.glEnable(this.light);
+ gl.glEnable(GL2.GL_DEPTH_TEST);
+ gl.glLightfv(this.light, GL2.GL_AMBIENT, ambient, 0);
+ gl.glLightfv(this.light, GL2.GL_SPECULAR, specular, 0);
+ gl.glLightfv(this.light, GL2.GL_DIFFUSE, diffuse, 0);
+ gl.glLightfv(this.light, GL2.GL_POSITION, position, 0);
+
+ //Material
+ gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT, mat_ambient, 0);
+ //gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, mat_specular, 0);
+ //gl.glMaterialf(GL2.GL_FRONT, GL2.GL_SHININESS, mat_shininess);
+ }
+
+ /**
+ * Stop light
+ * @param gl GL2
+ */
+ public void stop(GL2 gl) {
+ gl.glDisable(GL2.GL_LIGHTING);
+ gl.glDisable(GL2.GL_LIGHT0);
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java
index 15dc1919..23640d43 100644
--- a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java
@@ -99,6 +99,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
private int height;
float tickSpace = 5.0f;
float tickLen = 0.08f;
+ private Lighting lighting = new Lighting();
//
//
@@ -193,7 +194,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
public void setLineBoxColor(Color value) {
this.lineboxColor = value;
}
-
+
/**
* Get if draw bounding box or not
*
@@ -211,8 +212,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
public void setDrawBoundingBox(boolean value) {
this.drawBoundingBox = value;
}
-
-
+
/**
* Set display X/Y axis or not
*
@@ -557,6 +557,24 @@ public class Plot3DGL extends Plot implements GLEventListener {
this.zAxis.setMinMaxValue(min, max);
}
+ /**
+ * Get lighting set
+ *
+ * @return Lighting set
+ */
+ public Lighting getLighting() {
+ return this.lighting;
+ }
+
+ /**
+ * Set lighting set
+ *
+ * @param value Lighting set
+ */
+ public void setLighting(Lighting value) {
+ this.lighting = value;
+ }
+
//
//
/**
@@ -702,7 +720,12 @@ public class Plot3DGL extends Plot implements GLEventListener {
this.updateMatrix(gl);
this.drawLegend(gl);
- gl.glFlush();
+ gl.glFlush();
+
+ //Set lighting
+ if (this.lighting != null && this.lighting.isEnable()) {
+ this.lighting.start(gl);
+ }
}
/**
@@ -749,7 +772,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
return (float) Math.sqrt(Math.pow(sx2 - sx1, 2) + Math.pow(sy2 - sy1, 2));
}
-
+
private float toScreenAngle(float x1, float y1, float z1, float x2, float y2, float z2) {
float[] coord = toScreen(x1, y1, z1);
float sx1 = coord[0];
@@ -758,7 +781,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
float sx2 = coord[0];
float sy2 = coord[1];
- return (float) MeteoMath.uv2ds(sx2 - sx1, sy2 - sy1)[0];
+ return (float) MeteoMath.uv2ds(sx2 - sx1, sy2 - sy1)[0];
}
private int getLabelGap(Font font, List labels, double len) {
@@ -937,8 +960,9 @@ public class Plot3DGL extends Plot implements GLEventListener {
float angle = this.toScreenAngle(-1.0f, y, -1.0f, 1.0f, y, -1.0f);
angle = y < 0 ? 270 - angle : 90 - angle;
float yShift = Math.min(-strWidth, -strWidth);
- if (this.angleX <= -120)
+ if (this.angleX <= -120) {
yShift = -yShift;
+ }
drawString(gl, label, 0.0f, y1, -1.0f, XAlign.CENTER, yAlign, angle, 0, yShift);
}
@@ -1023,13 +1047,14 @@ public class Plot3DGL extends Plot implements GLEventListener {
//Draw y axis label
label = this.yAxis.getLabel();
- if (label != null) {
+ if (label != null) {
strWidth += this.tickSpace;
float angle = this.toScreenAngle(x, -1.0f, -1.0f, x, 1.0f, -1.0f);
angle = x > 0 ? 270 - angle : 90 - angle;
float yShift = Math.min(-strWidth, -strWidth);
- if (this.angleX <= -120)
+ if (this.angleX <= -120) {
yShift = -yShift;
+ }
drawString(gl, label, x1, 0.0f, -1.0f, XAlign.CENTER, yAlign, angle, 0, yShift);
}
}
@@ -1150,14 +1175,14 @@ public class Plot3DGL extends Plot implements GLEventListener {
Rectangle2D drawString(GL2 gl, ChartText text, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign, float xShift, float yShift) {
return drawString(gl, text.getText(), text.getFont(), text.getColor(), vx,
- vy, vz, xAlign, yAlign, xShift, yShift);
+ vy, vz, xAlign, yAlign, xShift, yShift);
}
-
+
Rectangle2D drawString(GL2 gl, String str, Font font, Color color, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign) {
return drawString(gl, str, font, color, vx, vy, vz, xAlign, yAlign, 0, 0);
}
-
+
Rectangle2D drawString(GL2 gl, String str, Font font, Color color, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign, float xShift, float yShift) {
//Get screen coordinates
@@ -1192,23 +1217,23 @@ public class Plot3DGL extends Plot implements GLEventListener {
return rect;
}
-
- Rectangle2D drawString(GL2 gl, ChartText text, float vx, float vy, float vz,
+
+ Rectangle2D drawString(GL2 gl, ChartText text, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign, float angle) {
return drawString(gl, text.getText(), text.getFont(), text.getColor(), vx, vy, vz, xAlign, yAlign, angle, 0, 0);
}
-
- Rectangle2D drawString(GL2 gl, ChartText text, float vx, float vy, float vz,
+
+ Rectangle2D drawString(GL2 gl, ChartText text, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign, float angle, float xShift, float yShift) {
- return drawString(gl, text.getText(), text.getFont(), text.getColor(), vx, vy,
+ return drawString(gl, text.getText(), text.getFont(), text.getColor(), vx, vy,
vz, xAlign, yAlign, angle, xShift, yShift);
}
-
+
Rectangle2D drawString(GL2 gl, String str, Font font, Color color, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign, float angle) {
return drawString(gl, str, font, color, vx, vy, vz, xAlign, yAlign, angle, 0, 0);
}
-
+
Rectangle2D drawString(GL2 gl, String str, Font font, Color color, float vx, float vy, float vz,
XAlign xAlign, YAlign yAlign, float angle, float xShift, float yShift) {
//Get screen coordinates
@@ -1221,14 +1246,15 @@ public class Plot3DGL extends Plot implements GLEventListener {
textRenderer.beginRendering(this.width, this.height);
textRenderer.setColor(color);
textRenderer.setSmoothing(true);
- Rectangle2D rect = textRenderer.getBounds(str.subSequence(0, str.length()));
- gl.glMatrixMode(GL2.GL_MODELVIEW);
- gl.glPushMatrix();
- gl.glTranslatef(x, y, 0.0f);
- if (angle != 0) {
- gl.glRotatef(angle, 0.0f, 0.0f, 1.0f);
+ Rectangle2D rect = textRenderer.getBounds(str.subSequence(0, str.length()));
+ gl.glMatrixMode(GL2.GL_MODELVIEW);
+ gl.glPushMatrix();
+ gl.glTranslatef(x, y, 0.0f);
+ if (angle != 0) {
+ gl.glRotatef(angle, 0.0f, 0.0f, 1.0f);
}
- x = 0; y = 0;
+ x = 0;
+ y = 0;
switch (xAlign) {
case CENTER:
x -= rect.getWidth() * 0.5;
@@ -1247,9 +1273,9 @@ public class Plot3DGL extends Plot implements GLEventListener {
}
x += xShift;
y += yShift;
- textRenderer.draw(str, (int)x, (int)y);
+ textRenderer.draw(str, (int) x, (int) y);
textRenderer.endRendering();
- gl.glPopMatrix();
+ gl.glPopMatrix();
return rect;
}
@@ -1273,18 +1299,25 @@ public class Plot3DGL extends Plot implements GLEventListener {
Graphic gg = graphic.getGraphicN(0);
this.drawGraphic(gl, gg);
} else {
- boolean isDraw = true;
- if (graphic instanceof GraphicCollection3D) {
- GraphicCollection3D gg = (GraphicCollection3D) graphic;
- if (gg.isAllQuads()) {
- this.drawQuadsPolygons(gl, gg);
- isDraw = false;
+ if (graphic instanceof IsosurfaceGraphics) {
+ this.drawIsosurface(gl, (IsosurfaceGraphics)graphic);
+ } else {
+ boolean isDraw = true;
+ if (graphic instanceof GraphicCollection3D) {
+ GraphicCollection3D gg = (GraphicCollection3D) graphic;
+ if (gg.isAllQuads()) {
+ this.drawQuadsPolygons(gl, gg);
+ isDraw = false;
+ } else if (gg.isAllTriangle()) {
+ this.drawTrianglePolygons(gl, gg);
+ isDraw = false;
+ }
}
- }
- if (isDraw) {
- for (int i = 0; i < graphic.getNumGraphics(); i++) {
- Graphic gg = graphic.getGraphicN(i);
- this.drawGraphic(gl, gg);
+ if (isDraw) {
+ for (int i = 0; i < graphic.getNumGraphics(); i++) {
+ Graphic gg = graphic.getGraphicN(i);
+ this.drawGraphic(gl, gg);
+ }
}
}
}
@@ -1493,6 +1526,82 @@ public class Plot3DGL extends Plot implements GLEventListener {
}
}
+ private void drawTrianglePolygons(GL2 gl, GraphicCollection3D graphic) {
+ PointZ p;
+ for (int i = 0; i < graphic.getNumGraphics(); i++) {
+ Graphic gg = graphic.getGraphicN(i);
+ if (extent.intersects(gg.getExtent())) {
+ PolygonZShape shape = (PolygonZShape) gg.getShape();
+ PolygonBreak pb = (PolygonBreak) gg.getLegend();
+ for (PolygonZ poly : (List) shape.getPolygons()) {
+ drawTriangle(gl, poly, pb);
+ }
+ }
+ }
+ }
+
+ private void drawTriangle(GL2 gl, PolygonZ aPG, PolygonBreak aPGB) {
+ PointZ p;
+ float[] rgba = aPGB.getColor().getRGBComponents(null);
+ if (aPGB.isDrawFill()) {
+ gl.glColor3f(rgba[0], rgba[1], rgba[2]);
+ gl.glBegin(GL2.GL_TRIANGLES);
+ for (int i = 0; i < aPG.getOutLine().size(); i++) {
+ p = ((List) aPG.getOutLine()).get(i);
+ gl.glVertex3f(transform_xf((float) p.X), transform_yf((float) p.Y), transform_zf((float) p.Z));
+ }
+ gl.glEnd();
+ }
+
+ if (aPGB.isDrawOutline()) {
+ rgba = aPGB.getOutlineColor().getRGBComponents(null);
+ gl.glLineWidth(aPGB.getOutlineSize());
+ gl.glColor3f(rgba[0], rgba[1], rgba[2]);
+ gl.glBegin(GL2.GL_LINE_STRIP);
+ for (int i = 0; i < aPG.getOutLine().size(); i++) {
+ p = ((List) aPG.getOutLine()).get(i);
+ gl.glVertex3f(transform_xf((float) p.X), transform_yf((float) p.Y), transform_zf((float) p.Z));
+ }
+ gl.glEnd();
+ }
+ }
+
+ private void drawTriangle(GL2 gl, PointZ[] points, PolygonBreak aPGB) {
+ PointZ p;
+ float[] rgba = aPGB.getColor().getRGBComponents(null);
+ if (aPGB.isDrawFill()) {
+ gl.glColor3f(rgba[0], rgba[1], rgba[2]);
+ gl.glBegin(GL2.GL_TRIANGLES);
+ for (int i = 0; i < 3; i++) {
+ p = points[i];
+ gl.glVertex3f(transform_xf((float) p.X), transform_yf((float) p.Y), transform_zf((float) p.Z));
+ }
+ gl.glEnd();
+ }
+
+ if (aPGB.isDrawOutline()) {
+ rgba = aPGB.getOutlineColor().getRGBComponents(null);
+ gl.glLineWidth(aPGB.getOutlineSize());
+ gl.glColor3f(rgba[0], rgba[1], rgba[2]);
+ gl.glBegin(GL2.GL_LINE_STRIP);
+ for (int i = 0; i < 3; i++) {
+ p = points[i];
+ gl.glVertex3f(transform_xf((float) p.X), transform_yf((float) p.Y), transform_zf((float) p.Z));
+ }
+ gl.glEnd();
+ }
+ }
+
+ private void drawIsosurface(GL2 gl, IsosurfaceGraphics isosurface) {
+ List triangles = isosurface.getTriangles();
+ PolygonBreak pgb = (PolygonBreak)isosurface.getLegendBreak();
+ //this.lighting.setEnable(true);
+ this.lighting.setMat_Ambient(pgb.getColor().getRGBComponents(null));
+ for (PointZ[] triangle : triangles) {
+ this.drawTriangle(gl, triangle, pgb);
+ }
+ }
+
private void drawImage(GL2 gl, Graphic graphic) {
ImageShape ishape = (ImageShape) graphic.getShape();
BufferedImage image = ishape.getImage();
@@ -1697,7 +1806,7 @@ public class Plot3DGL extends Plot implements GLEventListener {
caption = tLabels.get(idx);
}
if (ls.getLegendType() == LegendType.UniqueValue) {
- this.drawString(gl, caption, legend.getTickLabelFont(), Color.black,
+ this.drawString(gl, caption, legend.getTickLabelFont(), Color.black,
x + lWidth, yy + barHeight * 0.5f, 0, XAlign.LEFT, YAlign.CENTER, 5, 0);
} else {
rgba = Color.black.getRGBComponents(null);
@@ -1707,10 +1816,10 @@ public class Plot3DGL extends Plot implements GLEventListener {
gl.glVertex2f(x + lWidth * 0.5f, yy + barHeight);
gl.glVertex2f(x + lWidth, yy + barHeight);
gl.glEnd();
- this.drawString(gl, caption, legend.getTickLabelFont(), Color.black,
+ this.drawString(gl, caption, legend.getTickLabelFont(), Color.black,
x + lWidth, yy + barHeight, 0, XAlign.LEFT, YAlign.CENTER, 5, 0);
}
-
+
idx += 1;
}
yy += barHeight;
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/BenchmarkHandler.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/BenchmarkHandler.java
new file mode 100644
index 00000000..cd9bc85e
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/BenchmarkHandler.java
@@ -0,0 +1,799 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl.mc;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/**
+ * Created by Primoz on 11. 07. 2016.
+ */
+public class BenchmarkHandler {
+
+ public static void benchmarkChar(File inputFile, File outFile, final int[] size, final float voxSize[], final char isoValue, int nThreadsMin, int nThreadsMax, int iterations) {
+ char[] scalarField;
+
+ if (inputFile != null) {
+ System.out.println("PROGRESS: Reading input data.");
+ try {
+ int idx = 0;
+ scalarField = new char[size[0] * size[1] * size[2]];
+
+ DataInputStream in = new DataInputStream(new FileInputStream(inputFile));
+ while (in.available() > 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = (char) in.readByte();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldChar(size);
+ }
+
+ final char[] finalScalarField = scalarField;
+
+ System.out.println("PROGRESS: Performing benchmark.");
+ StringBuilder benchmarkResults = new StringBuilder();
+
+ for (int nThreads = nThreadsMin; nThreads <= nThreadsMax; nThreads++) {
+
+ final ArrayList times = new ArrayList<>();
+
+ for (int it = 0; it < iterations; it++) {
+
+ // TIMER
+ final long start = System.currentTimeMillis();
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesChar(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readShort();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldShort(size);
+ }
+
+ final short[] finalScalarField = scalarField;
+
+ System.out.println("PROGRESS: Performing benchmark.");
+ StringBuilder benchmarkResults = new StringBuilder();
+
+ for (int nThreads = nThreadsMin; nThreads <= nThreadsMax; nThreads++) {
+
+ final ArrayList times = new ArrayList<>();
+
+ for (int it = 0; it < iterations; it++) {
+
+ // TIMER
+ final long start = System.currentTimeMillis();
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesShort(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readInt();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldInt(size);
+ }
+
+ final int[] finalScalarField = scalarField;
+
+ System.out.println("PROGRESS: Performing benchmark.");
+ StringBuilder benchmarkResults = new StringBuilder();
+
+ for (int nThreads = nThreadsMin; nThreads <= nThreadsMax; nThreads++) {
+
+ final ArrayList times = new ArrayList<>();
+
+ for (int it = 0; it < iterations; it++) {
+
+ // TIMER
+ final long start = System.currentTimeMillis();
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesInt(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readFloat();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldFloat(size);
+ }
+
+ final float[] finalScalarField = scalarField;
+
+ System.out.println("PROGRESS: Performing benchmark.");
+ StringBuilder benchmarkResults = new StringBuilder();
+
+ for (int nThreads = nThreadsMin; nThreads <= nThreadsMax; nThreads++) {
+
+ final ArrayList times = new ArrayList<>();
+
+ for (int it = 0; it < iterations; it++) {
+
+ // TIMER
+ final long start = System.currentTimeMillis();
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesFloat(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readDouble();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldDouble(size);
+ }
+
+ final double[] finalScalarField = scalarField;
+
+ System.out.println("PROGRESS: Performing benchmark.");
+ StringBuilder benchmarkResults = new StringBuilder();
+
+ for (int nThreads = nThreadsMin; nThreads <= nThreadsMax; nThreads++) {
+
+ final ArrayList times = new ArrayList<>();
+
+ for (int it = 0; it < iterations; it++) {
+
+ // TIMER
+ final long start = System.currentTimeMillis();
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesDouble(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i vertices;
+
+ void setVertices(ArrayList vertices) {
+ this.vertices = vertices;
+ }
+
+ ArrayList getVertices() {
+ return this.vertices;
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/ExtractHandler.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/ExtractHandler.java
new file mode 100644
index 00000000..48c28a5d
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/ExtractHandler.java
@@ -0,0 +1,537 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl.mc;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/**
+ * Created by Primoz on 11. 07. 2016.
+ */
+public class ExtractHandler {
+ public static void extractHandlerChar(File inputFile, File outFile, final int[] size, final float voxSize[], final char isoValue, int nThreads) {
+ char[] scalarField;
+
+ if (inputFile != null) {
+ System.out.println("PROGRESS: Reading input data.");
+ try {
+ int idx = 0;
+ scalarField = new char[size[0] * size[1] * size[2]];
+
+ DataInputStream in = new DataInputStream(new FileInputStream(inputFile));
+ while (in.available() > 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = (char) in.readByte();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldChar(size);
+ }
+
+ final char[] finalScalarField = scalarField;
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+ System.out.println("PROGRESS: Executing marching cubes.");
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesChar(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readShort();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldShort(size);
+ }
+
+ final short[] finalScalarField = scalarField;
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+ System.out.println("PROGRESS: Executing marching cubes.");
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesShort(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readInt();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldInt(size);
+ }
+
+ final int[] finalScalarField = scalarField;
+
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+ System.out.println("PROGRESS: Executing marching cubes.");
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesInt(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readFloat();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldFloat(size);
+ }
+
+ final float[] finalScalarField = scalarField;
+
+ // TIMER
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+ System.out.println("PROGRESS: Executing marching cubes.");
+
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesFloat(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i 0) {
+ // Size does not match
+ if (idx >= scalarField.length) {
+ in.close();
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+
+ scalarField[idx++] = in.readDouble();
+ }
+
+ in.close();
+
+ // Size does not match
+ if (idx != scalarField.length) {
+ System.out.println("Invalid volume size was specified.");
+ return;
+ }
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while reading the volume");
+ return;
+ }
+ }
+ else {
+ System.out.println("PROGRESS: Generating volume data.");
+ scalarField = VolumeGenerator.generateScalarFieldDouble(size);
+ }
+
+ final double[] finalScalarField = scalarField;
+
+ // TIMER
+ ArrayList threads = new ArrayList<>();
+ final ArrayList> results = new ArrayList<>();
+
+ // Thread work distribution
+ int remainder = size[2] % nThreads;
+ int segment = size[2] / nThreads;
+
+ // Z axis offset for vertice position calculation
+ int zAxisOffset = 0;
+
+ System.out.println("PROGRESS: Executing marching cubes.");
+ for (int i = 0; i < nThreads; i++) {
+ // Distribute remainder among first (remainder) threads
+ int segmentSize = (remainder-- > 0) ? segment + 1 : segment;
+
+ // Padding needs to be added to correctly close the gaps between segments
+ final int paddedSegmentSize = (i != nThreads - 1) ? segmentSize + 1 : segmentSize;
+
+
+ // Finished callback
+ final CallbackMC callback = new CallbackMC() {
+ @Override
+ public void run() {
+ results.add(getVertices());
+ }
+ };
+
+ // Java...
+ final int finalZAxisOffset = zAxisOffset;
+
+ // Start the thread
+ Thread t = new Thread() {
+ public void run() {
+ MarchingCubes.marchingCubesDouble(finalScalarField, new int[]{size[0], size[1], paddedSegmentSize}, size[2], voxSize, isoValue, finalZAxisOffset, callback);
+ }
+ };
+
+ threads.add(t);
+ t.start();
+
+ // Correct offsets for next iteration
+ zAxisOffset += segmentSize;
+ }
+
+ // Join the threads
+ for (int i = 0; i > results, File outFile) {
+ try {
+ BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(outFile));
+
+ int idx = 0;
+ for (int i = 0; i < results.size(); i++) {
+ ArrayList resSeg = results.get(i);
+
+ for (int j = 0; j < resSeg.size(); j++) {
+ if (idx % 3 == 0) {
+ stream.write(("f " + (idx + 1) + " " + (idx + 2) + " " + (idx + 3) + "\n").getBytes());
+ }
+ idx ++;
+
+ stream.write(("v " + resSeg.get(j)[0] + " " + resSeg.get(j)[1] + " " + resSeg.get(j)[2] + "\n").getBytes());
+ }
+ }
+
+ stream.flush();
+ stream.close();
+ }
+ catch (Exception e) {
+ System.out.println("Something went wrong while writing to the output file");
+ return;
+ }
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/Main.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/Main.java
new file mode 100644
index 00000000..2507adac
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/Main.java
@@ -0,0 +1,287 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl.mc;
+
+import java.io.File;
+
+public class Main {
+
+ private static String usage = "This script may be executed in either benchmark or extract mode. Mode is specified by the first parameter [benchmark, extract].\nParameters: \n\t-input-vol\t Specifies path to the input volume. If this parameter is set volume dimensions(-vol-dim), data type(-data-type) and iso value(-iso) must also be given.\n\t-vol-dim\t Specifies the generated/read volume dimensions. Dimensions should be given as unsigned integers in format; -vol-dim X Y Z.\n\t-data-type\t Specifies the input file or generated data type. Options [char, uchar, short, ushort, int, uint, float, double].\n\t-vox-dim\t Specifies voxel dimensions used in mesh construction. Dimensions should be given as floating point numbers in format: -vox-dim X Y Z.\n\t-nThread\t Number of threads used in Marching cubes algorithm.This parameter can be either given as a single unsigned integer value or two unsigned integer values in benchmark mode, specifying the range of thread executions that will be tested.\n\t-iter\t\t Used only in benchmark mode to determine how many iterations should be executed for each configuration.\n\t-iso\t\t Isovalue that is used as a threshold for determining active voxels. Type should match the data type.\n\t-o\t\t Path to output file. In extract mode the mesh is written to file in .obj format [required]. In benchmark mode the results are written to file.\n";;
+
+ private static boolean isUint (String input) {
+ try {
+ return (Integer.parseInt(input) >= 0);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ private static boolean isFloat (String input) {
+ try {
+ Float.parseFloat(input);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ public static void main(String[] args) {
+
+ if (args.length < 1) {
+ System.out.println(usage);
+ return;
+ } else if (args[0].equals("-help")) {
+ System.out.println(usage);
+ }
+
+ // Benchmark or extract mode
+ boolean benchmark = false;
+
+ // Default num of threads is max available
+ int nThreadsMin = java.lang.Thread.activeCount();
+ if (nThreadsMin == 0) {
+ nThreadsMin = 1;
+ }
+ int nThreadsMax = nThreadsMin;
+
+ File inputFile = null;
+ File outFile = null;
+ String type = null;
+ String isoValueStr = null;
+ int iterations = 10; // Default 10 iterations per benchmark
+
+ boolean customSizeSpecified = false;
+ int[] size = {64, 64, 64};
+ float[] voxSize = {1.0f, 1.0f, 1.0f};
+
+ //region PARAMETER PARSING
+ // Read execution type
+ if (args[0].equals("benchmark")) {
+ benchmark = true;
+ } else if (!args[0].equals("extract")) {
+ System.out.println("Invalid execution type. Valid options [extract, benchmark]");
+ return;
+ }
+
+ // Flag parsing
+ for (int i = 1; i < args.length; i++) {
+ if (args[i].equals("-input-vol")) {
+ // Volume path specified
+ // Output file path is specified
+ if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') {
+ System.out.println("Missing file path after -input-vol flag.");
+ return;
+ }
+
+ // Store the file name and offset iterator
+ inputFile = new File(args[++i]);
+
+ if (!inputFile.exists() || inputFile.isDirectory()) {
+ System.out.println("Specified volume file does not exist.");
+ return;
+ }
+ } else if (args[i].equals("-vol-dim")) {
+ // Volume dimensions are given
+ if (i + 3 >= args.length || args[i + 1].charAt(0) == '-' || args[i + 2].charAt(0) == '-' || args[i + 3].charAt(0) == '-') {
+ System.out.println("Missing volume dimensions after -vol-dim flag.");
+ return;
+ }
+
+ String x = (args[++i]);
+ String y = (args[++i]);
+ String z = (args[++i]);
+
+ if (!isUint(x) || !isUint(y) || !isUint(z)) {
+ System.out.println("Invalid volume dimensions format. Specify dimensions as three unsigned integers.");
+ return;
+ }
+
+ customSizeSpecified = true;
+ size[0] = Integer.parseInt(x);
+ size[1] = Integer.parseInt(y);
+ size[2] = Integer.parseInt(z);
+ } else if (args[i].equals("-vox-dim")) {
+ // Voxel dimensions are given
+ if (i + 3 >= args.length) {
+ System.out.println("Missing voxel dimensions after -vox-dim flag.");
+ return;
+ }
+
+ String x = args[++i];
+ String y = args[++i];
+ String z = args[++i];
+
+ if (!isFloat(x) || !isFloat(y) || !isFloat(z)) {
+ System.out.println("Invalid voxel dimensions format. Specify voxel dimensions as three positive floats.");
+ return;
+ }
+
+ voxSize[0] = Float.parseFloat(x);
+ voxSize[0] = Float.parseFloat(y);
+ voxSize[0] = Float.parseFloat(z);
+ } else if (args[i].equals("-nThread")) {
+ // Number of threads is given
+ // FIRST VALUE
+ if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') {
+ System.out.println("Missing number or range of threads after -nThread flag.");
+ return;
+ }
+
+ // Validate first number
+ String tmp = args[++i];
+
+ if (!isUint(tmp)) {
+ System.out.println("Invalid nThread value format. Specify unsigned integer value or two if range.");
+ return;
+ }
+
+ // Parse C-str
+ nThreadsMin = Integer.parseInt(tmp);
+
+ // SECOND VALUE (If given)
+ if (i + 1 < args.length && args[i + 1].charAt(0) != '-') {
+ // Validate second number
+ tmp = args[++i];
+ if (!isUint(tmp)) {
+ System.out.println("Invalid nThread value format. Specify unsigned integer value or two if range.");
+ return;
+ }
+
+ // Parse C-str
+ nThreadsMax = Integer.parseInt(tmp);
+ } else {
+ nThreadsMax = nThreadsMin;
+ }
+
+ } else if (args[i].equals("-iso")) {
+ // ISO value is given
+ if (i + 1 >= args.length) {
+ System.out.println("Missing iso value after -iso flag.");
+ return;
+ }
+
+ isoValueStr = args[++i];
+
+ if (!isFloat(isoValueStr)) {
+ System.out.println("Invalid iso value format. Please specify float.");
+ return;
+ }
+ } else if (args[i].equals("-iter")) {
+ // ISO value is given
+ if (i + 1 >= args.length) {
+ System.out.println("Missing number of iterations after -iter flag.");
+ return;
+ }
+
+ String iterationsStr = args[++i];
+
+ if (!isUint(iterationsStr)) {
+ System.out.println("Invalid iterations value format. Please specify unsigned integer.");
+ return;
+ }
+
+ iterations = Integer.parseInt(iterationsStr);
+ } else if (args[i].equals("-o")) {
+ // Output file path is specified
+ if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') {
+ System.out.println("Missing file path after -o flag.");
+ return;
+ }
+
+ // Store the file name and offset iterator
+ outFile = new File(args[++i]);
+
+ if (outFile.getParentFile() != null && !outFile.getParentFile().exists()) {
+ System.out.println("Specified output file path is invaild.");
+ }
+ } else if (args[i].equals("-data-type")) {
+ // Volume data type is specified
+ if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') {
+ System.out.println("Missing type after -data-type flag.");
+ return;
+ }
+
+ // Data type is specified (char, uchar, short, ushort, int, uint, float, double)
+ if (!args[i + 1].equals("char") && !args[i + 1].equals("uchar") && !args[i + 1].equals("short") && !args[i + 1].equals("ushort") && args[i + 1].equals("uint") && args[i + 1].equals("float") && args[i + 1].equals("double")) {
+ System.out.println("Invalid data type. Available data types: char, uchar, short, ushort, int, uint, float, double.");
+ return;
+ }
+
+ type = args[++i];
+ } else {
+ System.out.println("Unknown parameter: " + args[i]);
+ return;
+ }
+ }
+
+ if (inputFile != null && (!customSizeSpecified || type == null || isoValueStr == null)) {
+ System.out.println("If custom volume is imported, you must input volume dimensions(-vol-dim), data type (-data-type) and iso value (-iso).");
+ return;
+ }
+ //endregion
+
+ if (benchmark) {
+ switch (type) {
+ case "char":
+ BenchmarkHandler.benchmarkChar(inputFile, outFile, size, voxSize, (char) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "uchar":
+ BenchmarkHandler.benchmarkChar(inputFile, outFile, size, voxSize, (char) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "short":
+ BenchmarkHandler.benchmarkShort(inputFile, outFile, size, voxSize, (short) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "ushort":
+ BenchmarkHandler.benchmarkShort(inputFile, outFile, size, voxSize, (short) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "int":
+ BenchmarkHandler.benchmarkInt(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "uint":
+ BenchmarkHandler.benchmarkInt(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "float":
+ BenchmarkHandler.benchmarkFloat(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Float.parseFloat(isoValueStr) : 0.5f), nThreadsMin, nThreadsMax, iterations);
+ break;
+ case "double":
+ BenchmarkHandler.benchmarkDouble(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Double.parseDouble(isoValueStr) : 0.5), nThreadsMin, nThreadsMax, iterations);
+ break;
+ }
+ } else {
+ if (outFile == null) {
+ System.out.println("To extract the data the output file path is needed (-o).");
+ return;
+ }
+
+ switch (type) {
+ case "char":
+ ExtractHandler.extractHandlerChar(inputFile, outFile, size, voxSize, (char) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMax);
+ break;
+ case "uchar":
+ ExtractHandler.extractHandlerChar(inputFile, outFile, size, voxSize, (char) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMax);
+ break;
+ case "short":
+ ExtractHandler.extractHandlerShort(inputFile, outFile, size, voxSize, (short) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMax);
+ break;
+ case "ushort":
+ ExtractHandler.extractHandlerShort(inputFile, outFile, size, voxSize, (short) ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0.5), nThreadsMax);
+ break;
+ case "int":
+ ExtractHandler.extractHandlerInt(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0), nThreadsMax);
+ break;
+ case "uint":
+ ExtractHandler.extractHandlerInt(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Integer.parseInt(isoValueStr) : 0), nThreadsMax);
+ break;
+ case "float":
+ ExtractHandler.extractHandlerFloat(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Float.parseFloat(isoValueStr) : 0.5f), nThreadsMax);
+ break;
+ case "double":
+ ExtractHandler.extractHandlerDouble(inputFile, outFile, size, voxSize, ((isoValueStr != null) ? Double.parseDouble(isoValueStr) : 0.5), nThreadsMax);
+ break;
+ }
+ }
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/MarchingCubes.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/MarchingCubes.java
new file mode 100644
index 00000000..f9f7caf0
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/MarchingCubes.java
@@ -0,0 +1,928 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl.mc;
+
+import java.util.ArrayList;
+import org.meteoinfo.ndarray.Array;
+
+/**
+ * Created by Primoz on 11. 07. 2016.
+ */
+public class MarchingCubes {
+ static float[] lerp(float[] vec1, float[] vec2, float alpha){
+ return new float[]{vec1[0] + (vec2[0] - vec1[0]) * alpha, vec1[1] + (vec2[1] - vec1[1]) * alpha, vec1[2] + (vec2[2] - vec1[2]) * alpha};
+ }
+
+ static void marchingCubesChar(char[] values, int[] volDim, int volZFull, float[] voxDim, char isoLevel, int offset, CallbackMC callback) {
+
+ ArrayList vertices = new ArrayList<>();
+ // Actual position along edge weighted according to function values.
+ float vertList[][] = new float[12][3];
+
+
+ // Calculate maximal possible axis value (used in vertice normalization)
+ float maxX = voxDim[0] * (volDim[0] - 1);
+ float maxY = voxDim[1] * (volDim[1] - 1);
+ float maxZ = voxDim[2] * (volZFull - 1);
+ float maxAxisVal = Math.max(maxX, Math.max(maxY, maxZ));
+
+ // Volume iteration
+ for (int z = 0; z < volDim[2] - 1; z++) {
+ for (int y = 0; y < volDim[1] - 1; y++) {
+ for (int x = 0; x < volDim[0] - 1; x++) {
+
+ // Indices pointing to cube vertices
+ // pyz ___________________ pxyz
+ // /| /|
+ // / | / |
+ // / | / |
+ // pz /___|______________/pxz|
+ // | | | |
+ // | | | |
+ // | py |______________|___| pxy
+ // | / | /
+ // | / | /
+ // | / | /
+ // |/__________________|/
+ // p px
+
+ int p = x + (volDim[0] * y) + (volDim[0] * volDim[1] * (z + offset)),
+ px = p + 1,
+ py = p + volDim[0],
+ pxy = py + 1,
+ pz = p + volDim[0] * volDim[1],
+ pxz = px + volDim[0] * volDim[1],
+ pyz = py + volDim[0] * volDim[1],
+ pxyz = pxy + volDim[0] * volDim[1];
+
+ // X Y Z
+ float position[] = new float[]{x * voxDim[0], y * voxDim[1], (z + offset) * voxDim[2]};
+
+ // Voxel intensities
+ char value0 = values[p],
+ value1 = values[px],
+ value2 = values[py],
+ value3 = values[pxy],
+ value4 = values[pz],
+ value5 = values[pxz],
+ value6 = values[pyz],
+ value7 = values[pxyz];
+
+ // Voxel is active if its intensity is above isolevel
+ int cubeindex = 0;
+ if (value0 > isoLevel) cubeindex |= 1;
+ if (value1 > isoLevel) cubeindex |= 2;
+ if (value2 > isoLevel) cubeindex |= 8;
+ if (value3 > isoLevel) cubeindex |= 4;
+ if (value4 > isoLevel) cubeindex |= 16;
+ if (value5 > isoLevel) cubeindex |= 32;
+ if (value6 > isoLevel) cubeindex |= 128;
+ if (value7 > isoLevel) cubeindex |= 64;
+
+ // Fetch the triggered edges
+ int bits = TablesMC.MC_EDGE_TABLE[cubeindex];
+
+ // If no edge is triggered... skip
+ if (bits == 0) continue;
+
+ // Interpolate the positions based od voxel intensities
+ float mu = 0.5f;
+
+ // bottom of the cube
+ if ((bits & 1) != 0) {
+ mu = (isoLevel - value0) / (value1 - value0);
+ vertList[0] = lerp(position, new float[]{position[0] + voxDim[0], position[1], position[2]}, mu);
+ }
+ if ((bits & 2) != 0) {
+ mu = (isoLevel - value1) / (value3 - value1);
+ vertList[1] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 4) != 0) {
+ mu = (isoLevel - value2) / (value3 - value2);
+ vertList[2] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 8) != 0) {
+ mu = (isoLevel - value0) / (value2 - value0);
+ vertList[3] = lerp(position, new float[]{position[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ // top of the cube
+ if ((bits & 16) != 0) {
+ mu = (isoLevel - value4) / (value5 - value4);
+ vertList[4] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 32) != 0) {
+ mu = (isoLevel - value5) / (value7 - value5);
+ vertList[5] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 64) != 0) {
+ mu = (isoLevel - value6) / (value7 - value6);
+ vertList[6] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 128) != 0) {
+ mu = (isoLevel - value4) / (value6 - value4);
+ vertList[7] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ // vertical lines of the cube
+ if ((bits & 256) != 0) {
+ mu = (isoLevel - value0) / (value4 - value0);
+ vertList[8] = lerp(position, new float[]{position[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 512) != 0) {
+ mu = (isoLevel - value1) / (value5 - value1);
+ vertList[9] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 1024) != 0) {
+ mu = (isoLevel - value3) / (value7 - value3);
+ vertList[10] = lerp(new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1]+ voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 2048) != 0) {
+ mu = (isoLevel - value2) / (value6 - value2);
+ vertList[11] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+
+ // construct triangles -- get correct vertices from triTable.
+ int i = 0;
+ // "Re-purpose cubeindex into an offset into triTable."
+ cubeindex <<= 4;
+
+ while (TablesMC.MC_TRI_TABLE[cubeindex + i] != -1) {
+ int index1 = TablesMC.MC_TRI_TABLE[cubeindex + i];
+ int index2 = TablesMC.MC_TRI_TABLE[cubeindex + i + 1];
+ int index3 = TablesMC.MC_TRI_TABLE[cubeindex + i + 2];
+
+ // Add triangles vertices normalized with the maximal possible value
+ vertices.add(new float[] {vertList[index3][0] / maxAxisVal - 0.5f, vertList[index3][1] / maxAxisVal - 0.5f, vertList[index3][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index2][0] / maxAxisVal - 0.5f, vertList[index2][1] / maxAxisVal - 0.5f, vertList[index2][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index1][0] / maxAxisVal - 0.5f, vertList[index1][1] / maxAxisVal - 0.5f, vertList[index1][2] / maxAxisVal - 0.5f});
+
+ i += 3;
+ }
+ }
+ }
+ }
+
+ callback.setVertices(vertices);
+ callback.run();
+ }
+
+ static void marchingCubesShort(short[] values, int[] volDim, int volZFull, float[] voxDim, short isoLevel, int offset, CallbackMC callback) {
+
+ ArrayList vertices = new ArrayList<>();
+ // Actual position along edge weighted according to function values.
+ float vertList[][] = new float[12][3];
+
+
+ // Calculate maximal possible axis value (used in vertice normalization)
+ float maxX = voxDim[0] * (volDim[0] - 1);
+ float maxY = voxDim[1] * (volDim[1] - 1);
+ float maxZ = voxDim[2] * (volZFull - 1);
+ float maxAxisVal = Math.max(maxX, Math.max(maxY, maxZ));
+
+ // Volume iteration
+ for (int z = 0; z < volDim[2] - 1; z++) {
+ for (int y = 0; y < volDim[1] - 1; y++) {
+ for (int x = 0; x < volDim[0] - 1; x++) {
+
+ // Indices pointing to cube vertices
+ // pyz ___________________ pxyz
+ // /| /|
+ // / | / |
+ // / | / |
+ // pz /___|______________/pxz|
+ // | | | |
+ // | | | |
+ // | py |______________|___| pxy
+ // | / | /
+ // | / | /
+ // | / | /
+ // |/__________________|/
+ // p px
+
+ int p = x + (volDim[0] * y) + (volDim[0] * volDim[1] * (z + offset)),
+ px = p + 1,
+ py = p + volDim[0],
+ pxy = py + 1,
+ pz = p + volDim[0] * volDim[1],
+ pxz = px + volDim[0] * volDim[1],
+ pyz = py + volDim[0] * volDim[1],
+ pxyz = pxy + volDim[0] * volDim[1];
+
+ // X Y Z
+ float position[] = new float[]{x * voxDim[0], y * voxDim[1], (z + offset) * voxDim[2]};
+
+ // Voxel intensities
+ short value0 = values[p],
+ value1 = values[px],
+ value2 = values[py],
+ value3 = values[pxy],
+ value4 = values[pz],
+ value5 = values[pxz],
+ value6 = values[pyz],
+ value7 = values[pxyz];
+
+ // Voxel is active if its intensity is above isolevel
+ int cubeindex = 0;
+ if (value0 > isoLevel) cubeindex |= 1;
+ if (value1 > isoLevel) cubeindex |= 2;
+ if (value2 > isoLevel) cubeindex |= 8;
+ if (value3 > isoLevel) cubeindex |= 4;
+ if (value4 > isoLevel) cubeindex |= 16;
+ if (value5 > isoLevel) cubeindex |= 32;
+ if (value6 > isoLevel) cubeindex |= 128;
+ if (value7 > isoLevel) cubeindex |= 64;
+
+ // Fetch the triggered edges
+ int bits = TablesMC.MC_EDGE_TABLE[cubeindex];
+
+ // If no edge is triggered... skip
+ if (bits == 0) continue;
+
+ // Interpolate the positions based od voxel intensities
+ float mu = 0.5f;
+
+ // bottom of the cube
+ if ((bits & 1) != 0) {
+ mu = (isoLevel - value0) / (value1 - value0);
+ vertList[0] = lerp(position, new float[]{position[0] + voxDim[0], position[1], position[2]}, mu);
+ }
+ if ((bits & 2) != 0) {
+ mu = (isoLevel - value1) / (value3 - value1);
+ vertList[1] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 4) != 0) {
+ mu = (isoLevel - value2) / (value3 - value2);
+ vertList[2] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 8) != 0) {
+ mu = (isoLevel - value0) / (value2 - value0);
+ vertList[3] = lerp(position, new float[]{position[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ // top of the cube
+ if ((bits & 16) != 0) {
+ mu = (isoLevel - value4) / (value5 - value4);
+ vertList[4] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 32) != 0) {
+ mu = (isoLevel - value5) / (value7 - value5);
+ vertList[5] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 64) != 0) {
+ mu = (isoLevel - value6) / (value7 - value6);
+ vertList[6] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 128) != 0) {
+ mu = (isoLevel - value4) / (value6 - value4);
+ vertList[7] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ // vertical lines of the cube
+ if ((bits & 256) != 0) {
+ mu = (isoLevel - value0) / (value4 - value0);
+ vertList[8] = lerp(position, new float[]{position[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 512) != 0) {
+ mu = (isoLevel - value1) / (value5 - value1);
+ vertList[9] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 1024) != 0) {
+ mu = (isoLevel - value3) / (value7 - value3);
+ vertList[10] = lerp(new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1]+ voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 2048) != 0) {
+ mu = (isoLevel - value2) / (value6 - value2);
+ vertList[11] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+
+ // construct triangles -- get correct vertices from triTable.
+ int i = 0;
+ // "Re-purpose cubeindex into an offset into triTable."
+ cubeindex <<= 4;
+
+ while (TablesMC.MC_TRI_TABLE[cubeindex + i] != -1) {
+ int index1 = TablesMC.MC_TRI_TABLE[cubeindex + i];
+ int index2 = TablesMC.MC_TRI_TABLE[cubeindex + i + 1];
+ int index3 = TablesMC.MC_TRI_TABLE[cubeindex + i + 2];
+
+ // Add triangles vertices normalized with the maximal possible value
+ vertices.add(new float[] {vertList[index3][0] / maxAxisVal - 0.5f, vertList[index3][1] / maxAxisVal - 0.5f, vertList[index3][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index2][0] / maxAxisVal - 0.5f, vertList[index2][1] / maxAxisVal - 0.5f, vertList[index2][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index1][0] / maxAxisVal - 0.5f, vertList[index1][1] / maxAxisVal - 0.5f, vertList[index1][2] / maxAxisVal - 0.5f});
+
+ i += 3;
+ }
+ }
+ }
+ }
+
+ callback.setVertices(vertices);
+ callback.run();
+ }
+
+ static void marchingCubesInt(int[] values, int[] volDim, int volZFull, float[] voxDim, int isoLevel, int offset, CallbackMC callback) {
+
+ ArrayList vertices = new ArrayList<>();
+ // Actual position along edge weighted according to function values.
+ float vertList[][] = new float[12][3];
+
+
+ // Calculate maximal possible axis value (used in vertice normalization)
+ float maxX = voxDim[0] * (volDim[0] - 1);
+ float maxY = voxDim[1] * (volDim[1] - 1);
+ float maxZ = voxDim[2] * (volZFull - 1);
+ float maxAxisVal = Math.max(maxX, Math.max(maxY, maxZ));
+
+ // Volume iteration
+ for (int z = 0; z < volDim[2] - 1; z++) {
+ for (int y = 0; y < volDim[1] - 1; y++) {
+ for (int x = 0; x < volDim[0] - 1; x++) {
+
+ // Indices pointing to cube vertices
+ // pyz ___________________ pxyz
+ // /| /|
+ // / | / |
+ // / | / |
+ // pz /___|______________/pxz|
+ // | | | |
+ // | | | |
+ // | py |______________|___| pxy
+ // | / | /
+ // | / | /
+ // | / | /
+ // |/__________________|/
+ // p px
+
+ int p = x + (volDim[0] * y) + (volDim[0] * volDim[1] * (z + offset)),
+ px = p + 1,
+ py = p + volDim[0],
+ pxy = py + 1,
+ pz = p + volDim[0] * volDim[1],
+ pxz = px + volDim[0] * volDim[1],
+ pyz = py + volDim[0] * volDim[1],
+ pxyz = pxy + volDim[0] * volDim[1];
+
+ // X Y Z
+ float position[] = new float[]{x * voxDim[0], y * voxDim[1], (z + offset) * voxDim[2]};
+
+ // Voxel intensities
+ int value0 = values[p],
+ value1 = values[px],
+ value2 = values[py],
+ value3 = values[pxy],
+ value4 = values[pz],
+ value5 = values[pxz],
+ value6 = values[pyz],
+ value7 = values[pxyz];
+
+ // Voxel is active if its intensity is above isolevel
+ int cubeindex = 0;
+ if (value0 > isoLevel) cubeindex |= 1;
+ if (value1 > isoLevel) cubeindex |= 2;
+ if (value2 > isoLevel) cubeindex |= 8;
+ if (value3 > isoLevel) cubeindex |= 4;
+ if (value4 > isoLevel) cubeindex |= 16;
+ if (value5 > isoLevel) cubeindex |= 32;
+ if (value6 > isoLevel) cubeindex |= 128;
+ if (value7 > isoLevel) cubeindex |= 64;
+
+ // Fetch the triggered edges
+ int bits = TablesMC.MC_EDGE_TABLE[cubeindex];
+
+ // If no edge is triggered... skip
+ if (bits == 0) continue;
+
+ // Interpolate the positions based od voxel intensities
+ float mu = 0.5f;
+
+ // bottom of the cube
+ if ((bits & 1) != 0) {
+ mu = (isoLevel - value0) / (value1 - value0);
+ vertList[0] = lerp(position, new float[]{position[0] + voxDim[0], position[1], position[2]}, mu);
+ }
+ if ((bits & 2) != 0) {
+ mu = (isoLevel - value1) / (value3 - value1);
+ vertList[1] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 4) != 0) {
+ mu = (isoLevel - value2) / (value3 - value2);
+ vertList[2] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 8) != 0) {
+ mu = (isoLevel - value0) / (value2 - value0);
+ vertList[3] = lerp(position, new float[]{position[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ // top of the cube
+ if ((bits & 16) != 0) {
+ mu = (isoLevel - value4) / (value5 - value4);
+ vertList[4] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 32) != 0) {
+ mu = (isoLevel - value5) / (value7 - value5);
+ vertList[5] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 64) != 0) {
+ mu = (isoLevel - value6) / (value7 - value6);
+ vertList[6] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 128) != 0) {
+ mu = (isoLevel - value4) / (value6 - value4);
+ vertList[7] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ // vertical lines of the cube
+ if ((bits & 256) != 0) {
+ mu = (isoLevel - value0) / (value4 - value0);
+ vertList[8] = lerp(position, new float[]{position[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 512) != 0) {
+ mu = (isoLevel - value1) / (value5 - value1);
+ vertList[9] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 1024) != 0) {
+ mu = (isoLevel - value3) / (value7 - value3);
+ vertList[10] = lerp(new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1]+ voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 2048) != 0) {
+ mu = (isoLevel - value2) / (value6 - value2);
+ vertList[11] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+
+ // construct triangles -- get correct vertices from triTable.
+ int i = 0;
+ // "Re-purpose cubeindex into an offset into triTable."
+ cubeindex <<= 4;
+
+ while (TablesMC.MC_TRI_TABLE[cubeindex + i] != -1) {
+ int index1 = TablesMC.MC_TRI_TABLE[cubeindex + i];
+ int index2 = TablesMC.MC_TRI_TABLE[cubeindex + i + 1];
+ int index3 = TablesMC.MC_TRI_TABLE[cubeindex + i + 2];
+
+ // Add triangles vertices normalized with the maximal possible value
+ vertices.add(new float[] {vertList[index3][0] / maxAxisVal - 0.5f, vertList[index3][1] / maxAxisVal - 0.5f, vertList[index3][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index2][0] / maxAxisVal - 0.5f, vertList[index2][1] / maxAxisVal - 0.5f, vertList[index2][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index1][0] / maxAxisVal - 0.5f, vertList[index1][1] / maxAxisVal - 0.5f, vertList[index1][2] / maxAxisVal - 0.5f});
+
+ i += 3;
+ }
+ }
+ }
+ }
+
+ callback.setVertices(vertices);
+ callback.run();
+ }
+
+ static void marchingCubesFloat(float[] values, int[] volDim, int volZFull, float[] voxDim, float isoLevel, int offset, CallbackMC callback) {
+
+ ArrayList vertices = new ArrayList<>();
+ // Actual position along edge weighted according to function values.
+ float vertList[][] = new float[12][3];
+
+
+ // Calculate maximal possible axis value (used in vertice normalization)
+ float maxX = voxDim[0] * (volDim[0] - 1);
+ float maxY = voxDim[1] * (volDim[1] - 1);
+ float maxZ = voxDim[2] * (volZFull - 1);
+ float maxAxisVal = Math.max(maxX, Math.max(maxY, maxZ));
+
+ // Volume iteration
+ for (int z = 0; z < volDim[2] - 1; z++) {
+ for (int y = 0; y < volDim[1] - 1; y++) {
+ for (int x = 0; x < volDim[0] - 1; x++) {
+
+ // Indices pointing to cube vertices
+ // pyz ___________________ pxyz
+ // /| /|
+ // / | / |
+ // / | / |
+ // pz /___|______________/pxz|
+ // | | | |
+ // | | | |
+ // | py |______________|___| pxy
+ // | / | /
+ // | / | /
+ // | / | /
+ // |/__________________|/
+ // p px
+
+ int p = x + (volDim[0] * y) + (volDim[0] * volDim[1] * (z + offset)),
+ px = p + 1,
+ py = p + volDim[0],
+ pxy = py + 1,
+ pz = p + volDim[0] * volDim[1],
+ pxz = px + volDim[0] * volDim[1],
+ pyz = py + volDim[0] * volDim[1],
+ pxyz = pxy + volDim[0] * volDim[1];
+
+ // X Y Z
+ float position[] = new float[]{x * voxDim[0], y * voxDim[1], (z + offset) * voxDim[2]};
+
+ // Voxel intensities
+ float value0 = values[p],
+ value1 = values[px],
+ value2 = values[py],
+ value3 = values[pxy],
+ value4 = values[pz],
+ value5 = values[pxz],
+ value6 = values[pyz],
+ value7 = values[pxyz];
+
+ // Voxel is active if its intensity is above isolevel
+ int cubeindex = 0;
+ if (value0 > isoLevel) cubeindex |= 1;
+ if (value1 > isoLevel) cubeindex |= 2;
+ if (value2 > isoLevel) cubeindex |= 8;
+ if (value3 > isoLevel) cubeindex |= 4;
+ if (value4 > isoLevel) cubeindex |= 16;
+ if (value5 > isoLevel) cubeindex |= 32;
+ if (value6 > isoLevel) cubeindex |= 128;
+ if (value7 > isoLevel) cubeindex |= 64;
+
+ // Fetch the triggered edges
+ int bits = TablesMC.MC_EDGE_TABLE[cubeindex];
+
+ // If no edge is triggered... skip
+ if (bits == 0) continue;
+
+ // Interpolate the positions based od voxel intensities
+ float mu = 0.5f;
+
+ // bottom of the cube
+ if ((bits & 1) != 0) {
+ mu = (float) ((isoLevel - value0) / (value1 - value0));
+ vertList[0] = lerp(position, new float[]{position[0] + voxDim[0], position[1], position[2]}, mu);
+ }
+ if ((bits & 2) != 0) {
+ mu = (float) ((isoLevel - value1) / (value3 - value1));
+ vertList[1] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 4) != 0) {
+ mu = (float) ((isoLevel - value2) / (value3 - value2));
+ vertList[2] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 8) != 0) {
+ mu = (float) ((isoLevel - value0) / (value2 - value0));
+ vertList[3] = lerp(position, new float[]{position[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ // top of the cube
+ if ((bits & 16) != 0) {
+ mu = (float) ((isoLevel - value4) / (value5 - value4));
+ vertList[4] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 32) != 0) {
+ mu = (float) ((isoLevel - value5) / (value7 - value5));
+ vertList[5] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 64) != 0) {
+ mu = (float) ((isoLevel - value6) / (value7 - value6));
+ vertList[6] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 128) != 0) {
+ mu = (float) ((isoLevel - value4) / (value6 - value4));
+ vertList[7] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ // vertical lines of the cube
+ if ((bits & 256) != 0) {
+ mu = (float) ((isoLevel - value0) / (value4 - value0));
+ vertList[8] = lerp(position, new float[]{position[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 512) != 0) {
+ mu = (float) ((isoLevel - value1) / (value5 - value1));
+ vertList[9] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 1024) != 0) {
+ mu = (float) ((isoLevel - value3) / (value7 - value3));
+ vertList[10] = lerp(new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1]+ voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 2048) != 0) {
+ mu = (float) ((isoLevel - value2) / (value6 - value2));
+ vertList[11] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+
+ // construct triangles -- get correct vertices from triTable.
+ int i = 0;
+ // "Re-purpose cubeindex into an offset into triTable."
+ cubeindex <<= 4;
+
+ while (TablesMC.MC_TRI_TABLE[cubeindex + i] != -1) {
+ int index1 = TablesMC.MC_TRI_TABLE[cubeindex + i];
+ int index2 = TablesMC.MC_TRI_TABLE[cubeindex + i + 1];
+ int index3 = TablesMC.MC_TRI_TABLE[cubeindex + i + 2];
+
+ // Add triangles vertices normalized with the maximal possible value
+ vertices.add(new float[] {vertList[index3][0] / maxAxisVal - 0.5f, vertList[index3][1] / maxAxisVal - 0.5f, vertList[index3][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index2][0] / maxAxisVal - 0.5f, vertList[index2][1] / maxAxisVal - 0.5f, vertList[index2][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index1][0] / maxAxisVal - 0.5f, vertList[index1][1] / maxAxisVal - 0.5f, vertList[index1][2] / maxAxisVal - 0.5f});
+
+ i += 3;
+ }
+ }
+ }
+ }
+
+ callback.setVertices(vertices);
+ callback.run();
+ }
+
+ static void marchingCubesDouble(double[] values, int[] volDim, int volZFull, float[] voxDim, double isoLevel, int offset, CallbackMC callback) {
+
+ ArrayList vertices = new ArrayList<>();
+ // Actual position along edge weighted according to function values.
+ float vertList[][] = new float[12][3];
+
+
+ // Calculate maximal possible axis value (used in vertice normalization)
+ float maxX = voxDim[0] * (volDim[0] - 1);
+ float maxY = voxDim[1] * (volDim[1] - 1);
+ float maxZ = voxDim[2] * (volZFull - 1);
+ float maxAxisVal = Math.max(maxX, Math.max(maxY, maxZ));
+
+ // Volume iteration
+ for (int z = 0; z < volDim[2] - 1; z++) {
+ for (int y = 0; y < volDim[1] - 1; y++) {
+ for (int x = 0; x < volDim[0] - 1; x++) {
+
+ // Indices pointing to cube vertices
+ // pyz ___________________ pxyz
+ // /| /|
+ // / | / |
+ // / | / |
+ // pz /___|______________/pxz|
+ // | | | |
+ // | | | |
+ // | py |______________|___| pxy
+ // | / | /
+ // | / | /
+ // | / | /
+ // |/__________________|/
+ // p px
+
+ int p = x + (volDim[0] * y) + (volDim[0] * volDim[1] * (z + offset)),
+ px = p + 1,
+ py = p + volDim[0],
+ pxy = py + 1,
+ pz = p + volDim[0] * volDim[1],
+ pxz = px + volDim[0] * volDim[1],
+ pyz = py + volDim[0] * volDim[1],
+ pxyz = pxy + volDim[0] * volDim[1];
+
+ // X Y Z
+ float position[] = new float[]{x * voxDim[0], y * voxDim[1], (z + offset) * voxDim[2]};
+
+ // Voxel intensities
+ double value0 = values[p],
+ value1 = values[px],
+ value2 = values[py],
+ value3 = values[pxy],
+ value4 = values[pz],
+ value5 = values[pxz],
+ value6 = values[pyz],
+ value7 = values[pxyz];
+
+ // Voxel is active if its intensity is above isolevel
+ int cubeindex = 0;
+ if (value0 > isoLevel) cubeindex |= 1;
+ if (value1 > isoLevel) cubeindex |= 2;
+ if (value2 > isoLevel) cubeindex |= 8;
+ if (value3 > isoLevel) cubeindex |= 4;
+ if (value4 > isoLevel) cubeindex |= 16;
+ if (value5 > isoLevel) cubeindex |= 32;
+ if (value6 > isoLevel) cubeindex |= 128;
+ if (value7 > isoLevel) cubeindex |= 64;
+
+ // Fetch the triggered edges
+ int bits = TablesMC.MC_EDGE_TABLE[cubeindex];
+
+ // If no edge is triggered... skip
+ if (bits == 0) continue;
+
+ // Interpolate the positions based od voxel intensities
+ float mu = 0.5f;
+
+ // bottom of the cube
+ if ((bits & 1) != 0) {
+ mu = (float) ((isoLevel - value0) / (value1 - value0));
+ vertList[0] = lerp(position, new float[]{position[0] + voxDim[0], position[1], position[2]}, mu);
+ }
+ if ((bits & 2) != 0) {
+ mu = (float) ((isoLevel - value1) / (value3 - value1));
+ vertList[1] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 4) != 0) {
+ mu = (float) ((isoLevel - value2) / (value3 - value2));
+ vertList[2] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ if ((bits & 8) != 0) {
+ mu = (float) ((isoLevel - value0) / (value2 - value0));
+ vertList[3] = lerp(position, new float[]{position[0], position[1] + voxDim[1], position[2]}, mu);
+ }
+ // top of the cube
+ if ((bits & 16) != 0) {
+ mu = (float) ((isoLevel - value4) / (value5 - value4));
+ vertList[4] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 32) != 0) {
+ mu = (float) ((isoLevel - value5) / (value7 - value5));
+ vertList[5] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 64) != 0) {
+ mu = (float) ((isoLevel - value6) / (value7 - value6));
+ vertList[6] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 128) != 0) {
+ mu = (float) ((isoLevel - value4) / (value6 - value4));
+ vertList[7] = lerp(new float[]{position[0], position[1], position[2] + voxDim[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ // vertical lines of the cube
+ if ((bits & 256) != 0) {
+ mu = (float) ((isoLevel - value0) / (value4 - value0));
+ vertList[8] = lerp(position, new float[]{position[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 512) != 0) {
+ mu = (float) ((isoLevel - value1) / (value5 - value1));
+ vertList[9] = lerp(new float[]{position[0] + voxDim[0], position[1], position[2]}, new float[]{position[0] + voxDim[0], position[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 1024) != 0) {
+ mu = (float) ((isoLevel - value3) / (value7 - value3));
+ vertList[10] = lerp(new float[]{position[0] + voxDim[0], position[1] + voxDim[1], position[2]}, new float[]{position[0] + voxDim[0], position[1]+ voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+ if ((bits & 2048) != 0) {
+ mu = (float) ((isoLevel - value2) / (value6 - value2));
+ vertList[11] = lerp(new float[]{position[0], position[1] + voxDim[1], position[2]}, new float[]{position[0], position[1] + voxDim[1], position[2] + voxDim[2]}, mu);
+ }
+
+ // construct triangles -- get correct vertices from triTable.
+ int i = 0;
+ // "Re-purpose cubeindex into an offset into triTable."
+ cubeindex <<= 4;
+
+ while (TablesMC.MC_TRI_TABLE[cubeindex + i] != -1) {
+ int index1 = TablesMC.MC_TRI_TABLE[cubeindex + i];
+ int index2 = TablesMC.MC_TRI_TABLE[cubeindex + i + 1];
+ int index3 = TablesMC.MC_TRI_TABLE[cubeindex + i + 2];
+
+ // Add triangles vertices normalized with the maximal possible value
+ vertices.add(new float[] {vertList[index3][0] / maxAxisVal - 0.5f, vertList[index3][1] / maxAxisVal - 0.5f, vertList[index3][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index2][0] / maxAxisVal - 0.5f, vertList[index2][1] / maxAxisVal - 0.5f, vertList[index2][2] / maxAxisVal - 0.5f});
+ vertices.add(new float[] {vertList[index1][0] / maxAxisVal - 0.5f, vertList[index1][1] / maxAxisVal - 0.5f, vertList[index1][2] / maxAxisVal - 0.5f});
+
+ i += 3;
+ }
+ }
+ }
+ }
+
+ callback.setVertices(vertices);
+ callback.run();
+ }
+
+ public static ArrayList marchingCubes(Array values, Array xc, Array yc, Array zc, float isoLevel) {
+
+ ArrayList vertices = new ArrayList<>();
+ // Actual position along edge weighted according to function values.
+ float vertList[][] = new float[12][3];
+
+ int[] shape = values.getShape();
+ int[] volDim = new int[]{shape[2], shape[1], shape[0]};
+
+ // Volume iteration
+ float xx, yy, zz;
+ for (int z = 0; z < volDim[2] - 1; z++) {
+ zz = zc.getFloat(z);
+ for (int y = 0; y < volDim[1] - 1; y++) {
+ yy = yc.getFloat(y);
+ for (int x = 0; x < volDim[0] - 1; x++) {
+ xx = xc.getFloat(x);
+ // Indices pointing to cube vertices
+ // pyz ___________________ pxyz
+ // /| /|
+ // / | / |
+ // / | / |
+ // pz /___|______________/pxz|
+ // | | | |
+ // | | | |
+ // | py |______________|___| pxy
+ // | / | /
+ // | / | /
+ // | / | /
+ // |/__________________|/
+ // p px
+
+ int p = x + (volDim[0] * y) + (volDim[0] * volDim[1] * z),
+ px = p + 1,
+ py = p + volDim[0],
+ pxy = py + 1,
+ pz = p + volDim[0] * volDim[1],
+ pxz = px + volDim[0] * volDim[1],
+ pyz = py + volDim[0] * volDim[1],
+ pxyz = pxy + volDim[0] * volDim[1];
+
+ // X, Y, Z position
+ //float position[] = new float[]{xx, yy, zz};
+
+ // Voxel intensities
+ float value0 = values.getFloat(p),
+ value1 = values.getFloat(px),
+ value2 = values.getFloat(py),
+ value3 = values.getFloat(pxy),
+ value4 = values.getFloat(pz),
+ value5 = values.getFloat(pxz),
+ value6 = values.getFloat(pyz),
+ value7 = values.getFloat(pxyz);
+
+ // Voxel is active if its intensity is above isolevel
+ int cubeindex = 0;
+ if (value0 > isoLevel) cubeindex |= 1;
+ if (value1 > isoLevel) cubeindex |= 2;
+ if (value2 > isoLevel) cubeindex |= 8;
+ if (value3 > isoLevel) cubeindex |= 4;
+ if (value4 > isoLevel) cubeindex |= 16;
+ if (value5 > isoLevel) cubeindex |= 32;
+ if (value6 > isoLevel) cubeindex |= 128;
+ if (value7 > isoLevel) cubeindex |= 64;
+
+ // Fetch the triggered edges
+ int bits = TablesMC.MC_EDGE_TABLE[cubeindex];
+
+ // If no edge is triggered... skip
+ if (bits == 0) continue;
+
+ // Interpolate the positions based od voxel intensities
+ float mu = 0.5f;
+
+ // bottom of the cube
+ if ((bits & 1) != 0) {
+ mu = (float) ((isoLevel - value0) / (value1 - value0));
+ vertList[0] = lerp(new float[]{xx, yy, zz}, new float[]{xc.getFloat(x + 1), yy, zz}, mu);
+ }
+ if ((bits & 2) != 0) {
+ mu = (float) ((isoLevel - value1) / (value3 - value1));
+ vertList[1] = lerp(new float[]{xc.getFloat(x + 1), yy, zz}, new float[]{xc.getFloat(x + 1), yc.getFloat(y + 1), zz}, mu);
+ }
+ if ((bits & 4) != 0) {
+ mu = (float) ((isoLevel - value2) / (value3 - value2));
+ vertList[2] = lerp(new float[]{xx, yc.getFloat(y + 1), zz}, new float[]{xc.getFloat(x + 1), yc.getFloat(y + 1), zz}, mu);
+ }
+ if ((bits & 8) != 0) {
+ mu = (float) ((isoLevel - value0) / (value2 - value0));
+ vertList[3] = lerp(new float[]{xx, yy, zz}, new float[]{xx, yc.getFloat(y + 1), zz}, mu);
+ }
+ // top of the cube
+ if ((bits & 16) != 0) {
+ mu = (float) ((isoLevel - value4) / (value5 - value4));
+ vertList[4] = lerp(new float[]{xx, yy, zc.getFloat(z + 1)}, new float[]{xc.getFloat(x + 1), yy, zc.getFloat(z + 1)}, mu);
+ }
+ if ((bits & 32) != 0) {
+ mu = (float) ((isoLevel - value5) / (value7 - value5));
+ vertList[5] = lerp(new float[]{xc.getFloat(x + 1), yy, zc.getFloat(z + 1)}, new float[]{xc.getFloat(x + 1), yc.getFloat(y + 1), zc.getFloat(z + 1)}, mu);
+ }
+ if ((bits & 64) != 0) {
+ mu = (float) ((isoLevel - value6) / (value7 - value6));
+ vertList[6] = lerp(new float[]{xx, yc.getFloat(y + 1), zc.getFloat(z + 1)}, new float[]{xc.getFloat(x + 1), yc.getFloat(y + 1), zc.getFloat(z + 1)}, mu);
+ }
+ if ((bits & 128) != 0) {
+ mu = (float) ((isoLevel - value4) / (value6 - value4));
+ vertList[7] = lerp(new float[]{xx, yy, zc.getFloat(z + 1)}, new float[]{xx, yc.getFloat(y + 1), zc.getFloat(z + 1)}, mu);
+ }
+ // vertical lines of the cube
+ if ((bits & 256) != 0) {
+ mu = (float) ((isoLevel - value0) / (value4 - value0));
+ vertList[8] = lerp(new float[]{xx, yy, zz}, new float[]{xx, yy, zc.getFloat(z + 1)}, mu);
+ }
+ if ((bits & 512) != 0) {
+ mu = (float) ((isoLevel - value1) / (value5 - value1));
+ vertList[9] = lerp(new float[]{xc.getFloat(x + 1), yy, zz}, new float[]{xc.getFloat(x + 1), yy, zc.getFloat(z + 1)}, mu);
+ }
+ if ((bits & 1024) != 0) {
+ mu = (float) ((isoLevel - value3) / (value7 - value3));
+ vertList[10] = lerp(new float[]{xc.getFloat(x + 1), yc.getFloat(y + 1), zz}, new float[]{xc.getFloat(x + 1), yc.getFloat(y + 1), zc.getFloat(z + 1)}, mu);
+ }
+ if ((bits & 2048) != 0) {
+ mu = (float) ((isoLevel - value2) / (value6 - value2));
+ vertList[11] = lerp(new float[]{xx, yc.getFloat(y + 1), zz}, new float[]{xx, yc.getFloat(y + 1), zc.getFloat(z + 1)}, mu);
+ }
+
+ // construct triangles -- get correct vertices from triTable.
+ int i = 0;
+ // "Re-purpose cubeindex into an offset into triTable."
+ cubeindex <<= 4;
+
+ while (TablesMC.MC_TRI_TABLE[cubeindex + i] != -1) {
+ int index1 = TablesMC.MC_TRI_TABLE[cubeindex + i];
+ int index2 = TablesMC.MC_TRI_TABLE[cubeindex + i + 1];
+ int index3 = TablesMC.MC_TRI_TABLE[cubeindex + i + 2];
+
+ // Add triangles vertices
+ vertices.add(new float[] {vertList[index3][0], vertList[index3][1], vertList[index3][2]});
+ vertices.add(new float[] {vertList[index2][0], vertList[index2][1], vertList[index2][2]});
+ vertices.add(new float[] {vertList[index1][0], vertList[index1][1], vertList[index1][2]});
+
+ i += 3;
+ }
+ }
+ }
+ }
+
+ return vertices;
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/TablesMC.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/TablesMC.java
new file mode 100644
index 00000000..f76425db
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/TablesMC.java
@@ -0,0 +1,303 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl.mc;
+
+/**
+ * Created by Primoz on 7.7.2016.
+ */
+public class TablesMC {
+ static int[] MC_EDGE_TABLE = {
+ 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
+ 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
+ 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
+ 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
+ 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
+ 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
+ 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
+ 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
+ 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
+ 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
+ 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
+ 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
+ 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
+ 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
+ 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
+ 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
+ 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
+ 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
+ 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
+ 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
+ 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
+ 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
+ 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
+ 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
+ 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
+ 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
+ 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
+ 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
+ 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
+ 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
+ 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
+ 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 };
+
+ static int[] MC_TRI_TABLE = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1,
+ 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1,
+ 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1,
+ 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1,
+ 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1,
+ 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1,
+ 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1,
+ 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1,
+ 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1,
+ 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1,
+ 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1,
+ 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1,
+ 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1,
+ 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1,
+ 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1,
+ 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1,
+ 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1,
+ 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1,
+ 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1,
+ 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1,
+ 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1,
+ 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1,
+ 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1,
+ 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1,
+ 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1,
+ 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1,
+ 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1,
+ 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1,
+ 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1,
+ 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1,
+ 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1,
+ 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1,
+ 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1,
+ 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1,
+ 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1,
+ 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1,
+ 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1,
+ 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1,
+ 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1,
+ 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1,
+ 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1,
+ 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1,
+ 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1,
+ 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1,
+ 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1,
+ 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1,
+ 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1,
+ 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1,
+ 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1,
+ 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1,
+ 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1,
+ 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1,
+ 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1,
+ 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1,
+ 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1,
+ 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1,
+ 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1,
+ 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1,
+ 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1,
+ 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1,
+ 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1,
+ 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1,
+ 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1,
+ 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1,
+ 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1,
+ 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1,
+ 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1,
+ 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1,
+ 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1,
+ 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1,
+ 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1,
+ 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1,
+ 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1,
+ 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1,
+ 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1,
+ 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1,
+ 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1,
+ 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1,
+ 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1,
+ 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1,
+ 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1,
+ 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1,
+ 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1,
+ 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1,
+ 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1,
+ 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1,
+ 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1,
+ 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1,
+ 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1,
+ 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1,
+ 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1,
+ 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1,
+ 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1,
+ 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1,
+ 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1,
+ 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1,
+ 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1,
+ 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1,
+ 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1,
+ 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1,
+ 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1,
+ 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1,
+ 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1,
+ 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1,
+ 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1,
+ 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1,
+ 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1,
+ 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1,
+ 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1,
+ 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1,
+ 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1,
+ 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1,
+ 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1,
+ 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1,
+ 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1,
+ 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1,
+ 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1,
+ 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1,
+ 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1,
+ 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1,
+ 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1,
+ 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1,
+ 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1,
+ 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1,
+ 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1,
+ 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1,
+ 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1,
+ 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1,
+ 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1,
+ 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1,
+ 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1,
+ 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1,
+ 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1,
+ 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1,
+ 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1,
+ 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1,
+ 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1,
+ 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1,
+ 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1,
+ 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1,
+ 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1,
+ 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1,
+ 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1,
+ 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1,
+ 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1,
+ 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1,
+ 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1,
+ 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1,
+ 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1,
+ 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1,
+ 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1,
+ 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1,
+ 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1,
+ 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1,
+ 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1,
+ 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1,
+ 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1,
+ 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1,
+ 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1,
+ 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1,
+ 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1,
+ 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1,
+ 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1,
+ 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1,
+ 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1,
+ 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1,
+ 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1,
+ 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1,
+ 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1,
+ 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1,
+ 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1,
+ 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1,
+ 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1,
+ 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1,
+ 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1,
+ 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/VolumeGenerator.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/VolumeGenerator.java
new file mode 100644
index 00000000..b5b3bb6b
--- /dev/null
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/jogl/mc/VolumeGenerator.java
@@ -0,0 +1,116 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.meteoinfo.chart.jogl.mc;
+
+/**
+ * Created by Primoz on 11. 07. 2016.
+ */
+public class VolumeGenerator {
+ public static char[] generateScalarFieldChar(int []size) {
+ final char[] scalarField = new char[size[0] * size[1] * size[2]];
+ float axisMin = -10;
+ float axisMax = 10;
+ float axisRange = axisMax - axisMin;
+
+ for (int k = 0; k < size[0]; k++) {
+ for (int j = 0; j < size[1]; j++) {
+ for (int i = 0; i < size[2]; i++) {
+ // actual values
+ char x = (char) (axisMin + axisRange * i / (size[0] - 1));
+ char y = (char) (axisMin + axisRange * j / (size[1] - 1));
+ char z = (char) (axisMin + axisRange * k / (size[2] - 1));
+ scalarField[k + size[1] * (j + size[2] * i)] = (char) (x * x + y * y - z * z - 25);
+ }
+ }
+ }
+
+ return scalarField;
+ }
+
+ public static short[] generateScalarFieldShort(int []size) {
+ final short[] scalarField = new short[size[0] * size[1] * size[2]];
+ float axisMin = -10;
+ float axisMax = 10;
+ float axisRange = axisMax - axisMin;
+
+ for (int k = 0; k < size[0]; k++) {
+ for (int j = 0; j < size[1]; j++) {
+ for (int i = 0; i < size[2]; i++) {
+ // actual values
+ short x = (short) (axisMin + axisRange * i / (size[0] - 1));
+ short y = (short) (axisMin + axisRange * j / (size[1] - 1));
+ short z = (short) (axisMin + axisRange * k / (size[2] - 1));
+ scalarField[k + size[1] * (j + size[2] * i)] = (short) (x * x + y * y - z * z - 25);
+ }
+ }
+ }
+
+ return scalarField;
+ }
+
+ public static int[] generateScalarFieldInt(int []size) {
+ final int[] scalarField = new int[size[0] * size[1] * size[2]];
+ float axisMin = -10;
+ float axisMax = 10;
+ float axisRange = axisMax - axisMin;
+
+ for (int k = 0; k < size[0]; k++) {
+ for (int j = 0; j < size[1]; j++) {
+ for (int i = 0; i < size[2]; i++) {
+ // actual values
+ int x = (int) (axisMin + axisRange * i / (size[0] - 1));
+ int y = (int) (axisMin + axisRange * j / (size[1] - 1));
+ int z = (int) (axisMin + axisRange * k / (size[2] - 1));
+ scalarField[k + size[1] * (j + size[2] * i)] = (int) (x * x + y * y - z * z - 25);
+ }
+ }
+ }
+
+ return scalarField;
+ }
+
+ public static float[] generateScalarFieldFloat(int []size) {
+ final float[] scalarField = new float[size[0] * size[1] * size[2]];
+ float axisMin = -10;
+ float axisMax = 10;
+ float axisRange = axisMax - axisMin;
+
+ for (int k = 0; k < size[0]; k++) {
+ for (int j = 0; j < size[1]; j++) {
+ for (int i = 0; i < size[2]; i++) {
+ // actual values
+ float x = axisMin + axisRange * i / (size[0] - 1);
+ float y = axisMin + axisRange * j / (size[1] - 1);
+ float z = axisMin + axisRange * k / (size[2] - 1);
+ scalarField[k + size[1] * (j + size[2] * i)] = x * x + y * y - z * z - 25;
+ }
+ }
+ }
+
+ return scalarField;
+ }
+
+ public static double[] generateScalarFieldDouble(int []size) {
+ final double[] scalarField = new double[size[0] * size[1] * size[2]];
+ double axisMin = -10;
+ double axisMax = 10;
+ double axisRange = axisMax - axisMin;
+
+ for (int k = 0; k < size[0]; k++) {
+ for (int j = 0; j < size[1]; j++) {
+ for (int i = 0; i < size[2]; i++) {
+ // actual values
+ double x = axisMin + axisRange * i / (size[0] - 1);
+ double y = axisMin + axisRange * j / (size[1] - 1);
+ double z = axisMin + axisRange * k / (size[2] - 1);
+ scalarField[k + size[1] * (j + size[2] * i)] = (x * x + y * y - z * z - 25);
+ }
+ }
+ }
+
+ return scalarField;
+ }
+}
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/plot3d/GraphicCollection3D.java b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/plot3d/GraphicCollection3D.java
index 846a28ac..617200f9 100644
--- a/MeteoInfoLib/src/main/java/org/meteoinfo/chart/plot3d/GraphicCollection3D.java
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/chart/plot3d/GraphicCollection3D.java
@@ -18,9 +18,9 @@ public class GraphicCollection3D extends GraphicCollection{
private double zValue;
private String zdir;
private List sePoint;
- private boolean allQuads;
- private boolean allTriangle;
- private boolean allConvexPolygon;
+ protected boolean allQuads;
+ protected boolean allTriangle;
+ protected boolean allConvexPolygon;
/**
* Constructor
diff --git a/MeteoInfoLib/src/main/java/org/meteoinfo/global/MIMath.java b/MeteoInfoLib/src/main/java/org/meteoinfo/global/MIMath.java
index 9d1e6a36..221901d8 100644
--- a/MeteoInfoLib/src/main/java/org/meteoinfo/global/MIMath.java
+++ b/MeteoInfoLib/src/main/java/org/meteoinfo/global/MIMath.java
@@ -571,6 +571,52 @@ public class MIMath {
return extent;
}
+
+ /**
+ * Get extent of the points
+ *
+ * @param points
+ * @return Extent
+ */
+ public static Extent3D getExtent(PointZ[] points) {
+ PointZ p = points[0];
+ double minx = p.X;
+ double maxx = p.X;
+ double miny = p.Y;
+ double maxy = p.Y;
+ double minz = p.Z;
+ double maxz = p.Z;
+ for (int i = 1; i < points.length; i++) {
+ if (minx > p.X) {
+ minx = p.M;
+ }
+ if (maxx < p.X) {
+ maxx = p.M;
+ }
+ if (miny > p.Y) {
+ miny = p.Y;
+ }
+ if (maxy < p.Y) {
+ maxy = p.Y;
+ }
+ if (minz > p.Z) {
+ minz = p.Z;
+ }
+ if (maxz < p.Z) {
+ maxz = p.Z;
+ }
+ }
+
+ Extent3D extent = new Extent3D();
+ extent.minX = minx;
+ extent.maxX = maxx;
+ extent.minY = miny;
+ extent.maxY = maxy;
+ extent.minZ = minz;
+ extent.maxZ = maxz;
+
+ return extent;
+ }
/**
* Determine if two extent cross each other