diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java index b734979a..e81c47b2 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java @@ -5869,7 +5869,7 @@ public class GraphicFactory { wa.v = v; wa.w = w; wa.scale = scale; - wa.setHeadWith(headWidth); + wa.setHeadWidth(headWidth); wa.setHeadLength(headLength); wa.setPoint(aPoint); if (cdata == null) { diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/GLPlot.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/GLPlot.java index b6de4739..b8da5456 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/GLPlot.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/GLPlot.java @@ -2508,46 +2508,39 @@ public class GLPlot extends Plot { } break; case POLYLINE_Z: - boolean useRender = true; - /*ColorBreak cb = graphic.getGraphicN(0).getLegend(); - if (cb instanceof StreamlineBreak) { - if (graphic.getGraphicN(0).getShape() instanceof PipeShape) - useRender = false; - } else if (cb instanceof ColorBreakCollection) { - if (((ColorBreakCollection) cb).get(0) instanceof StreamlineBreak) { - if (graphic.getGraphicN(0).getShape() instanceof PipeShape) - useRender = false; - } - }*/ - if (useRender) { - if (graphic.getGraphicN(0).getShape() instanceof PipeShape) { - if (!this.renderMap.containsKey(graphic)) { - renderMap.put(graphic, new PipeRender(gl, (GraphicCollection3D) graphic)); - } - PipeRender pipeRender = (PipeRender) renderMap.get(graphic); - pipeRender.setTransform(this.transform, this.alwaysUpdateBuffers); - pipeRender.setOrthographic(this.orthographic); - pipeRender.setLighting(this.lighting); - pipeRender.updateMatrix(); - pipeRender.draw(); - } else { - if (!this.renderMap.containsKey(graphic)) { - renderMap.put(graphic, new LineRender(gl, (GraphicCollection3D) graphic)); - } - LineRender lineRender = (LineRender) renderMap.get(graphic); - lineRender.setTransform(this.transform, this.alwaysUpdateBuffers); - lineRender.setOrthographic(this.orthographic); - lineRender.setLighting(this.lighting); - lineRender.updateMatrix(); - lineRender.draw(); + if (graphic.getGraphicN(0).getShape() instanceof PipeShape) { + if (!this.renderMap.containsKey(graphic)) { + renderMap.put(graphic, new PipeRender(gl, (GraphicCollection3D) graphic)); } + PipeRender pipeRender = (PipeRender) renderMap.get(graphic); + pipeRender.setTransform(this.transform, this.alwaysUpdateBuffers); + pipeRender.setOrthographic(this.orthographic); + pipeRender.setLighting(this.lighting); + pipeRender.updateMatrix(); + pipeRender.draw(); } else { - for (int i = 0; i < graphic.getNumGraphics(); i++) { - Graphic gg = graphic.getGraphicN(i); - this.drawGraphic(gl, gg); + if (!this.renderMap.containsKey(graphic)) { + renderMap.put(graphic, new LineRender(gl, (GraphicCollection3D) graphic)); } + LineRender lineRender = (LineRender) renderMap.get(graphic); + lineRender.setTransform(this.transform, this.alwaysUpdateBuffers); + lineRender.setOrthographic(this.orthographic); + lineRender.setLighting(this.lighting); + lineRender.updateMatrix(); + lineRender.draw(); } break; + case WIND_ARROW: + if (!this.renderMap.containsKey(graphic)) { + renderMap.put(graphic, new QuiverRender(gl, (GraphicCollection3D) graphic)); + } + QuiverRender quiverRender = (QuiverRender) renderMap.get(graphic); + quiverRender.setTransform(this.transform, this.alwaysUpdateBuffers); + quiverRender.setOrthographic(this.orthographic); + quiverRender.setLighting(this.lighting); + quiverRender.updateMatrix(); + quiverRender.draw(); + break; default: for (int i = 0; i < graphic.getNumGraphics(); i++) { Graphic gg = graphic.getGraphicN(i); @@ -3745,7 +3738,7 @@ public class GLPlot extends Plot { //float coneHgt = coneFractionAxially * norm_of_v; //float coneRadius = coneFractionRadially * norm_of_v; float coneRadius = shape.getHeadLength() * 0.02f; - float coneHgt = shape.getHeadWith() * 0.02f; + float coneHgt = shape.getHeadWidth() * 0.02f; // Set location of arrowhead to be at the startpoint of the line float[] vConeLocation = {x2, y2, z2}; diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java index eeb6eedc..1a9ff716 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/Plot3DGL.java @@ -3727,7 +3727,7 @@ public class Plot3DGL extends Plot implements GLEventListener { //float coneHgt = coneFractionAxially * norm_of_v; //float coneRadius = coneFractionRadially * norm_of_v; float coneRadius = shape.getHeadLength() * 0.02f; - float coneHgt = shape.getHeadWith() * 0.02f; + float coneHgt = shape.getHeadWidth() * 0.02f; // Set location of arrowhead to be at the startpoint of the line float[] vConeLocation = {x2, y2, z2}; diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/QuiverRender.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/QuiverRender.java new file mode 100644 index 00000000..625c6e0c --- /dev/null +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/QuiverRender.java @@ -0,0 +1,268 @@ +package org.meteoinfo.chart.render.jogl; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.util.GLBuffers; +import org.joml.Vector3f; +import org.joml.Vector4f; +import org.meteoinfo.chart.graphic.GraphicCollection3D; +import org.meteoinfo.chart.graphic.cylinder.Cylinder; +import org.meteoinfo.chart.jogl.Program; +import org.meteoinfo.chart.jogl.Transform; +import org.meteoinfo.chart.jogl.Utils; +import org.meteoinfo.geometry.graphic.Graphic; +import org.meteoinfo.geometry.legend.*; +import org.meteoinfo.geometry.shape.PointZ; +import org.meteoinfo.geometry.shape.PolylineZShape; +import org.meteoinfo.geometry.shape.WindArrow3D; +import org.meteoinfo.math.Matrix4f; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.List; + +public class QuiverRender extends JOGLGraphicRender { + + private GraphicCollection3D graphics; + private Program program; + private IntBuffer vbo; + private int quiverNumber; + private float lineWidth; + private float[] vertexPosition; + private float[] vertexColor; + private int sizePosition; + private int sizeColor; + private IntBuffer vboCone; + private float[] coneVertexPosition; + private float[] coneVertexNormal; + private float[] coneVertexColor; + private int[] coneVertexIndices; + private int sizeConePosition; + private int sizeConeNormal; + private int sizeConeColor; + + /** + * Constructor + * + * @param gl The JOGL GL2 object + */ + public QuiverRender(GL2 gl) { + super(gl); + + if (useShader) { + try { + this.compileShaders(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * Constructor + * @param gl The JOGL GL2 object + * @param graphics The quiver graphics + */ + public QuiverRender(GL2 gl, GraphicCollection3D graphics) { + this(gl); + + this.graphics = graphics; + this.quiverNumber = graphics.getNumGraphics(); + PointBreak pb = (PointBreak) graphics.getGraphicN(0).getLegend(); + this.lineWidth = pb.getOutlineSize(); + } + + private void updateVertexArrays() { + vertexPosition = new float[quiverNumber * 6]; + vertexColor = new float[quiverNumber * 8]; + List vertexPositionList = new ArrayList<>(); + List vertexNormalList = new ArrayList<>(); + List vertexColorList = new ArrayList<>(); + List vertexIndices = new ArrayList<>(); + for (int i = 0, pi = 0, ci = 0; i < quiverNumber; i++, pi+=6, ci+=8) { + Graphic graphic = graphics.getGraphicN(i); + WindArrow3D shape = (WindArrow3D) graphic.getShape(); + PointBreak pb = (PointBreak) graphic.getLegend(); + PointZ sp = (PointZ) shape.getPoint(); + PointZ ep = (PointZ) shape.getEndPoint(); + + Vector3f v1 = transform.transform((float) sp.X, (float) sp.Y, (float) sp.Z); + Vector3f v2 = transform.transform((float) ep.X, (float) ep.Y, (float) ep.Z); + float[] color = pb.getColor().getRGBComponents(null); + vertexPosition[pi] = v1.x; + vertexPosition[pi + 1] = v1.y; + vertexPosition[pi + 2] = v1.z; + vertexPosition[pi + 3] = v2.x; + vertexPosition[pi + 4] = v2.y; + vertexPosition[pi + 5] = v2.z; + System.arraycopy(color, 0, vertexColor, ci, 4); + System.arraycopy(color, 0, vertexColor, ci + 4, 4); + + Cylinder cylinder = new Cylinder(shape.getHeadWidth() * 0.02f, + 0, shape.getHeadLength() * 0.02f, 8, 1, true); + Matrix4f matrix = new Matrix4f(); + matrix.lookAt(v2.sub(v1, new Vector3f())); + matrix.translate(v2); + List vertices = cylinder.getVertices(); + int n = vertices.size(); + int nAdded = vertexPositionList.size(); + for (Vector3f v : vertices) { + vertexPositionList.add(matrix.mul(v)); + } + List normals = cylinder.getNormals(); + for (Vector3f v : normals) { + vertexNormalList.add(matrix.mul(v)); + } + for (int j = 0; j < n; j++) { + vertexColorList.add(new Vector4f(color)); + } + if (nAdded == 0) { + vertexIndices.addAll(cylinder.getIndices()); + } else { + for (int idx : cylinder.getIndices()) { + vertexIndices.add(idx + nAdded); + } + } + } + + int n = vertexPositionList.size(); + this.coneVertexPosition = new float[n * 3]; + this.coneVertexNormal = new float[n * 3]; + this.coneVertexColor = new float[n * 4]; + Vector3f v; + Vector4f v4; + for (int i = 0, j = 0, k = 0; i < n; i++, j+=3, k+=4) { + v = vertexPositionList.get(i); + coneVertexPosition[j] = v.x; + coneVertexPosition[j + 1] = v.y; + coneVertexPosition[j + 2] = v.z; + v = vertexNormalList.get(i); + coneVertexNormal[j] = v.x; + coneVertexNormal[j + 1] = v.y; + coneVertexNormal[j + 2] = v.z; + v4 = vertexColorList.get(i); + coneVertexColor[k] = v4.x; + coneVertexColor[k + 1] = v4.y; + coneVertexColor[k + 2] = v4.z; + coneVertexColor[k + 3] = v4.w; + } + coneVertexIndices = vertexIndices.stream().mapToInt(Integer::intValue).toArray(); + } + + void compileShaders() throws Exception { + String vertexShaderCode = Utils.loadResource("/shaders/surface/vertex.vert"); + String fragmentShaderCode = Utils.loadResource("/shaders/surface/surface.frag"); + program = new Program("surface", vertexShaderCode, fragmentShaderCode); + } + + @Override + public void setTransform(Transform transform, boolean alwaysUpdateBuffers) { + boolean updateBuffer = true; + if (!alwaysUpdateBuffers && this.transform != null && this.transform.equals(transform)) + updateBuffer = false; + + super.setTransform((Transform) transform.clone()); + + if (updateBuffer) { + this.updateVertexArrays(); + + FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(vertexPosition); + sizePosition = vertexBuffer.capacity() * Float.BYTES; + + FloatBuffer colorBuffer = GLBuffers.newDirectFloatBuffer(vertexColor); + sizeColor = colorBuffer.capacity() * Float.BYTES; + int totalSize = sizePosition + sizeColor; + + vbo = GLBuffers.newDirectIntBuffer(1); + gl.glGenBuffers(1, vbo); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo.get(0)); + gl.glBufferData(GL.GL_ARRAY_BUFFER, totalSize, null, GL.GL_STATIC_DRAW); + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, sizePosition, vertexBuffer); + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, sizePosition, sizeColor, colorBuffer); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + + FloatBuffer coneVertexBuffer = GLBuffers.newDirectFloatBuffer(coneVertexPosition); + sizeConePosition = coneVertexBuffer.capacity() * Float.BYTES; + FloatBuffer coneNormalBuffer = GLBuffers.newDirectFloatBuffer(coneVertexNormal); + sizeConeNormal = coneNormalBuffer.capacity() * Float.BYTES; + FloatBuffer coneColorBuffer = GLBuffers.newDirectFloatBuffer(coneVertexColor); + sizeConeColor = coneColorBuffer.capacity() * Float.BYTES; + + vboCone = GLBuffers.newDirectIntBuffer(2); + gl.glGenBuffers(2, vboCone); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboCone.get(0)); + gl.glBufferData(GL.GL_ARRAY_BUFFER, sizeConePosition + sizeConeNormal + sizeConeColor, + null, GL.GL_STATIC_DRAW); + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, sizeConePosition, coneVertexBuffer); + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, sizeConePosition, sizeConeNormal, coneNormalBuffer); + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, sizeConePosition + sizeConeNormal, sizeConeColor, + coneColorBuffer); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + + IntBuffer coneIndexBuffer = GLBuffers.newDirectIntBuffer(coneVertexIndices); + gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vboCone.get(1)); + gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, coneIndexBuffer.capacity() * Integer.BYTES, + coneIndexBuffer, GL.GL_STATIC_DRAW); + gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + } + + void setUniforms() { + + } + + @Override + public void draw() { + if (useShader) { // not working now + program.use(gl); + setUniforms(); + + gl.glUseProgram(0); + } else { + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo.get(0)); + + // enable vertex arrays + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0); + gl.glEnableClientState(GL2.GL_COLOR_ARRAY); + gl.glColorPointer(4, GL.GL_FLOAT, 0, sizePosition); + + boolean lightEnabled = this.lighting.isEnable(); + if (lightEnabled) { + this.lighting.stop(gl); + } + gl.glLineWidth(this.lineWidth * this.dpiScale); + gl.glDrawArrays(GL.GL_LINES, 0, quiverNumber); + if (lightEnabled) { + this.lighting.start(gl); + } + + // draw wind arrow + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); + gl.glDisableClientState(GL2.GL_COLOR_ARRAY); + + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboCone.get(0)); + gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vboCone.get(1)); + + // enable vertex arrays + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0); + gl.glEnableClientState(GL2.GL_NORMAL_ARRAY); + gl.glNormalPointer(GL.GL_FLOAT, 0, sizeConePosition); + gl.glEnableClientState(GL2.GL_COLOR_ARRAY); + gl.glColorPointer(4, GL.GL_FLOAT, 0, sizeConePosition + sizeConeNormal); + + gl.glDrawElements(GL2.GL_TRIANGLES, coneVertexIndices.length, GL.GL_UNSIGNED_INT, 0); + + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY); + gl.glDisableClientState(GL2.GL_NORMAL_ARRAY); + gl.glDisableClientState(GL2.GL_COLOR_ARRAY); + + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + } +} diff --git a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/shape/WindArrow3D.java b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/shape/WindArrow3D.java index 8ddcf385..b9616667 100644 --- a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/shape/WindArrow3D.java +++ b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/shape/WindArrow3D.java @@ -17,7 +17,7 @@ public class WindArrow3D extends PointZShape { public double v; public double w; public float scale = 1; - private float headWith = 1; + private float headWidth = 1; private float headLength = 2.5f; // @@ -40,16 +40,16 @@ public class WindArrow3D extends PointZShape { * Get head width * @return Head width */ - public float getHeadWith() { - return this.headWith; + public float getHeadWidth() { + return this.headWidth; } /** * Set head width * @param value Head width */ - public void setHeadWith(float value) { - this.headWith = value; + public void setHeadWidth(float value) { + this.headWidth = value; } /** diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml index c45b40f5..1b84c27c 100644 --- a/meteoinfo-lab/milconfig.xml +++ b/meteoinfo-lab/milconfig.xml @@ -1,34 +1,34 @@ - - - - - - + - - + + + + + + + - - + + - - + + @@ -36,5 +36,5 @@
- + diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl$py.class b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl$py.class index f11efcb3..8c154aa2 100644 Binary files a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl$py.class and b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py index 689e5f33..99095097 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py @@ -337,7 +337,7 @@ class Axes3DGL(Axes3D): """ self._axes.addZAxis(x, y, left) - def bar(self, x, y, z, width=0.8, bottom=None, cylinder=False, **kwargs): + def bar(self, *args, **kwargs): """ Make a 3D bar plot of x, y and z, where x, y and z are sequence like objects of the same lengths. @@ -347,7 +347,7 @@ class Axes3DGL(Axes3D): :param width: (*float*) Bar width. :param cylinder: (*bool*) Is cylinder bar or rectangle bar. :param bottom: (*bool*) Color of the points. Or z values. - :param color: (*Color*) Optional, the color of the bar faces. + :param facecolor: (*Color*) Optional, the color of the bar faces. :param edgecolor: (*Color*) Optional, the color of the bar edge. Default is black color. Edge line will not be plotted if ``edgecolor`` is ``None``. :param linewidth: (*int*) Optional, width of bar edge. @@ -362,16 +362,41 @@ class Axes3DGL(Axes3D): """ #Add data series label = kwargs.pop('label', 'S_0') - xdata = plotutil.getplotdata(x) - ydata = plotutil.getplotdata(y) - zdata = plotutil.getplotdata(z) + if len(args) == 1: + z = np.asarray(args[0]) + if z.ndim == 1: + nx, = z.shape + y = np.array([0] * nx) + x = np.arange(nx) + else: + ny, nx = z.shape + x = np.arange(nx) + y = np.arange(ny) + x, y = np.meshgrid(x, y) + elif len(args) == 2: + x = np.asarray(args[0]) + z = np.asarray(args[1]) + nx, = x.shape + y = np.array([0] * nx) + else: + x = np.asarray(args[0]) + y = np.asarray(args[1]) + z = np.asarray(args[2]) + + xdata = x._array + ydata = y._array + zdata = z._array autowidth = False + width = kwargs.pop('width', 0.8) width = np.asarray(width) + bottom = kwargs.pop('bottom', None) if not bottom is None: bottom = plotutil.getplotdata(bottom) + cylinder = kwargs.pop('cylinder', False) + #Set plot data styles fcobj = kwargs.pop('color', None) if fcobj is None: diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/miplot$py.class b/meteoinfo-lab/pylib/mipylib/plotlib/miplot$py.class index 98498494..7c6c793c 100644 Binary files a/meteoinfo-lab/pylib/mipylib/plotlib/miplot$py.class and b/meteoinfo-lab/pylib/mipylib/plotlib/miplot$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/miplot.py b/meteoinfo-lab/pylib/mipylib/plotlib/miplot.py index 76a43ec5..48e0d50a 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/miplot.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/miplot.py @@ -241,7 +241,7 @@ def bar(x, height, width=0.8, bottom=None, align='center', data=None, **kwargs): return r @_copy_docstring_and_deprecators(Axes3D.bar) -def bar3(x, y, z, width=0.8, bottom=None, cylinder=False, **kwargs): +def bar3(*args, **kwargs): global g_axes if g_axes is None: g_axes = axes3d() @@ -249,7 +249,7 @@ def bar3(x, y, z, width=0.8, bottom=None, cylinder=False, **kwargs): if not isinstance(g_axes, Axes3D): g_axes = axes3d() - r = g_axes.bar(x, y, z, width, bottom, cylinder, **kwargs) + r = g_axes.bar(*args, **kwargs) draw_if_interactive() return r