Files
android-calculatorpp/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3d.java
Sergey Solovyev 400d5003a3 new plotter
2013-01-14 14:00:21 +04:00

360 lines
12 KiB
Java
Executable File

// Copyright (C) 2009-2010 Mihai Preda
package org.solovyev.android.calculator.plot;
import android.graphics.Color;
import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
class Graph3d {
// vertices count per polygon (triangle = 3)
public static final int VERTICES_COUNT = 3;
// color components count per color
public static final int COLOR_COMPONENTS_COUNT = 4;
// linear polygons count
private final int n;
private final boolean useHighQuality3d;
private ShortBuffer verticeIdx;
private FloatBuffer vertexBuf;
private ByteBuffer colorBuf;
private int vertexVbo, colorVbo, vertexElementVbo;
private boolean useVBO;
private int polygonsⁿ;
Graph3d(GL11 gl, boolean useHighQuality3d) {
this.useHighQuality3d = useHighQuality3d;
this.n = useHighQuality3d ? 36 : 24;
short[] b = new short[n * n];
int p = 0;
for (int i = 0; i < n; i++) {
short v = 0;
for (int j = 0; j < n; v += n + n, j += 2) {
b[p++] = (short) (v + i);
b[p++] = (short) (v + n + n - 1 - i);
}
v = (short) (n * (n - 2));
i++;
for (int j = n - 1; j >= 0; v -= n + n, j -= 2) {
b[p++] = (short) (v + n + n - 1 - i);
b[p++] = (short) (v + i);
}
}
verticeIdx = buildBuffer(b);
String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
useVBO = extensions.indexOf("vertex_buffer_object") != -1;
//Calculator.log("VBOs support: " + useVBO + " version " + gl.glGetString(GL10.GL_VERSION));
if (useVBO) {
int[] out = new int[3];
gl.glGenBuffers(3, out, 0);
vertexVbo = out[0];
colorVbo = out[1];
vertexElementVbo = out[2];
}
}
private static FloatBuffer buildBuffer(float[] b) {
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 2);
bb.order(ByteOrder.nativeOrder());
FloatBuffer sb = bb.asFloatBuffer();
sb.put(b);
sb.position(0);
return sb;
}
private static ShortBuffer buildBuffer(short[] b) {
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);
bb.order(ByteOrder.nativeOrder());
ShortBuffer sb = bb.asShortBuffer();
sb.put(b);
sb.position(0);
return sb;
}
private static ByteBuffer buildBuffer(byte[] b) {
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);
bb.order(ByteOrder.nativeOrder());
bb.put(b);
bb.position(0);
return bb;
}
public void update(@NotNull GL11 gl, @NotNull ArityPlotFunction fpd, float zoom) {
final Function function = fpd.getFunction();
final PlotLineDef lineDef = fpd.getLineDef();
final int NTICK = useHighQuality3d ? 5 : 0;
final float size = 4 * zoom;
//Calculator.log("update VBOs " + vertexVbo + ' ' + colorVbo + ' ' + vertexElementVbo);
polygonsⁿ = n * n + 6 + 8 + NTICK * 6;
// triangle polygon => 3 vertices per polygon
final float vertices[] = new float[polygonsⁿ * VERTICES_COUNT];
float maxAbsZ = fillFunctionPolygonVertices(function, size, vertices);
final byte[] colors = prepareFunctionPolygonColors(lineDef, vertices, maxAbsZ);
int base = n * n * 3;
int colorBase = n * n * 4;
final int baseSize = 2;
fillBasePolygonVectors(vertices, colors, base, colorBase, baseSize);
base += 8 * 3;
colorBase += 8 * 4;
fillAxisPolygonVectors(vertices, colors, base, colorBase);
base += 6 * 3;
colorBase += 6 * 4;
fillAxisGridPolygonVectors(NTICK, vertices, colors, base, colorBase);
vertexBuf = buildBuffer(vertices);
colorBuf = buildBuffer(colors);
if (useVBO) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, vertexBuf.capacity() * 4, vertexBuf, GL11.GL_STATIC_DRAW);
vertexBuf = null;
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, colorBuf.capacity(), colorBuf, GL11.GL_STATIC_DRAW);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
colorBuf = null;
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);
gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, verticeIdx.capacity() * 2, verticeIdx, GL11.GL_STATIC_DRAW);
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
private void fillAxisGridPolygonVectors(int NTICK, float[] vertices, byte[] colors, int base, int colorBase) {
int p = base;
final float tick = .03f;
final float offset = .01f;
for (int i = 1; i <= NTICK; ++i) {
vertices[p] = i - tick;
vertices[p + 1] = -offset;
vertices[p + 2] = -offset;
vertices[p + 3] = i + tick;
vertices[p + 4] = offset;
vertices[p + 5] = offset;
p += 6;
vertices[p] = -offset;
vertices[p + 1] = i - tick;
vertices[p + 2] = -offset;
vertices[p + 3] = offset;
vertices[p + 4] = i + tick;
vertices[p + 5] = offset;
p += 6;
vertices[p] = -offset;
vertices[p + 1] = -offset;
vertices[p + 2] = i - tick;
vertices[p + 3] = offset;
vertices[p + 4] = offset;
vertices[p + 5] = i + tick;
p += 6;
}
for (int i = colorBase + NTICK * 6 * 4 - 1; i >= colorBase; --i) {
colors[i] = (byte) 255;
}
}
private void fillAxisPolygonVectors(float[] vertices, byte[] colors, int base, int colorBase) {
final float unit = 2;
final float axis[] = {
0, 0, 0,
unit, 0, 0,
0, 0, 0,
0, unit, 0,
0, 0, 0,
0, 0, unit,
};
System.arraycopy(axis, 0, vertices, base, 6 * 3);
for (int i = colorBase; i < colorBase + 6 * 4; i += 4) {
colors[i] = (byte) 255;
colors[i + 1] = (byte) 255;
colors[i + 2] = (byte) 255;
colors[i + 3] = (byte) 255;
}
}
private void fillBasePolygonVectors(float[] vertices, byte[] colors, int base, int colorBase, int baseSize) {
int p = base;
for (int i = -baseSize; i <= baseSize; i += 2 * baseSize) {
vertices[p] = i;
vertices[p + 1] = -baseSize;
vertices[p + 2] = 0;
p += 3;
vertices[p] = i;
vertices[p + 1] = baseSize;
vertices[p + 2] = 0;
p += 3;
vertices[p] = -baseSize;
vertices[p + 1] = i;
vertices[p + 2] = 0;
p += 3;
vertices[p] = baseSize;
vertices[p + 1] = i;
vertices[p + 2] = 0;
p += 3;
}
for (int i = colorBase; i < colorBase + 8 * 4; i += 4) {
colors[i] = (byte) 255;
colors[i + 1] = (byte) 255;
colors[i + 2] = (byte) 255;
colors[i + 3] = (byte) 255;
}
}
private float fillFunctionPolygonVertices(Function function, float size, float[] vertices) {
final int arity = function.arity();
final float minX = -size;
final float maxX = size;
final float minY = -size;
final float maxY = size;
float Δx = (maxX - minX) / (n - 1);
float Δy = (maxY - minY) / (n - 1);
float y = minY;
float x = minX - Δx;
float maxAbsZ = 0;
float z = 0;
if (arity == 0) {
z = (float) function.eval();
}
int k = 0;
for (int i = 0; i < n; i++, y += Δy) {
float xinc = (i & 1) == 0 ? Δx : -Δx;
x += xinc;
if (arity == 1) {
z = (float) function.eval(y);
}
for (int j = 0; j < n; j++, x += xinc, k += VERTICES_COUNT) {
if (arity == 2) {
z = (float) function.eval(y, x);
}
vertices[k] = x;
vertices[k + 1] = y;
vertices[k + 2] = z;
if (!Float.isNaN(z)) {
final float absZ = Math.abs(z);
if (absZ > maxAbsZ) {
maxAbsZ = absZ;
}
} else {
vertices[k + 2] = 0;
}
}
}
return maxAbsZ;
}
private byte[] prepareFunctionPolygonColors(PlotLineDef lineDef, float[] vertices, float maxAbsZ) {
// 4 color components per polygon (color[i] = red, color[i+1] = green, color[i+2] = blue, color[i+3] = alpha )
final byte colors[] = new byte[polygonsⁿ * COLOR_COMPONENTS_COUNT];
final int lineColor = lineDef.getLineColor();
final int colorComponentsCount = n * n * COLOR_COMPONENTS_COUNT;
for (int i = 0, j = VERTICES_COUNT - 1; i < colorComponentsCount; i += COLOR_COMPONENTS_COUNT, j += VERTICES_COUNT) {
final float z = vertices[j];
if (!Float.isNaN(z)) {
if (lineDef.getLineColorType() == PlotLineColorType.color_map) {
final float color = z / maxAbsZ;
final float abs = Math.abs(color);
colors[i] = floatToByte(color);
colors[i + 1] = floatToByte(1 - abs * .3f);
colors[i + 2] = floatToByte(-color);
} else {
colors[i] = (byte) Color.red(lineColor);
colors[i + 1] = (byte) Color.green(lineColor);
colors[i + 2] = (byte) Color.blue(lineColor);
}
colors[i + 3] = (byte) 255;
} else {
colors[i] = 0;
colors[i + 1] = 0;
colors[i + 2] = 0;
colors[i + 3] = 0;
}
}
return colors;
}
private byte floatToByte(float v) {
if (v <= 0) {
return (byte) 0;
} else {
if (v >= 1) {
return (byte) 255;
} else {
return (byte) (v * 255);
}
}
}
public void draw(GL11 gl) {
if (useVBO) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, 0);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
// gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N*N);
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);
gl.glDrawElements(GL10.GL_LINE_STRIP, n * n, GL10.GL_UNSIGNED_SHORT, 0);
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
} else {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuf);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colorBuf);
gl.glDrawElements(GL10.GL_LINE_STRIP, n * n, GL10.GL_UNSIGNED_SHORT, verticeIdx);
}
final int N2 = n * n;
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N2);
gl.glDrawArrays(GL10.GL_LINES, N2, polygonsⁿ - N2);
}
public boolean isUseHighQuality3d() {
return useHighQuality3d;
}
}