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