/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.breakout.model;
import static com.jogamp.opengl.GL.GL_ARRAY_BUFFER;
import static com.jogamp.opengl.GL.GL_BGRA;
import static com.jogamp.opengl.GL.GL_CLAMP_TO_EDGE;
import static com.jogamp.opengl.GL.GL_DEPTH_TEST;
import static com.jogamp.opengl.GL.GL_ELEMENT_ARRAY_BUFFER;
import static com.jogamp.opengl.GL.GL_FLOAT;
import static com.jogamp.opengl.GL.GL_LINEAR;
import static com.jogamp.opengl.GL.GL_LINES;
import static com.jogamp.opengl.GL.GL_RGBA;
import static com.jogamp.opengl.GL.GL_TEXTURE0;
import static com.jogamp.opengl.GL.GL_TEXTURE_2D;
import static com.jogamp.opengl.GL.GL_TEXTURE_MAG_FILTER;
import static com.jogamp.opengl.GL.GL_TEXTURE_MIN_FILTER;
import static com.jogamp.opengl.GL.GL_TEXTURE_WRAP_S;
import static com.jogamp.opengl.GL.GL_TEXTURE_WRAP_T;
import static com.jogamp.opengl.GL.GL_TRIANGLE_STRIP;
import static com.jogamp.opengl.GL.GL_UNSIGNED_BYTE;
import static com.jogamp.opengl.GL.GL_UNSIGNED_INT;
import static org.andork.math3d.Vecmath.setf;
import static org.andork.spatial.Rectmath.nmax;
import static org.andork.spatial.Rectmath.nmin;
import static org.andork.spatial.Rectmath.rayIntersects;
import static org.andork.spatial.Rectmath.voidRectf;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Stream;
import org.andork.collect.LinkedHashSetMultiMap;
import org.andork.collect.MultiMap;
import org.andork.func.FloatBinaryOperator;
import org.andork.graph.Graphs;
import org.andork.jogl.BufferHelper;
import org.andork.jogl.JoglBuffer;
import org.andork.jogl.JoglDrawContext;
import org.andork.jogl.JoglDrawable;
import org.andork.jogl.JoglResource;
import org.andork.jogl.old.BasicJOGLObject.Uniform1fv;
import org.andork.jogl.old.BasicJOGLObject.Uniform3fv;
import org.andork.jogl.old.BasicJOGLObject.Uniform4fv;
import org.andork.jogl.util.JoglUtils;
import org.andork.math3d.InConeTester3f;
import org.andork.math3d.LinePlaneIntersection3f;
import org.andork.math3d.PlanarHull3f;
import org.andork.math3d.TwoPlaneIntersection3f;
import org.andork.math3d.TwoPlaneIntersection3f.ResultType;
import org.andork.math3d.Vecmath;
import org.andork.spatial.RBranch;
import org.andork.spatial.RLeaf;
import org.andork.spatial.RNode;
import org.andork.spatial.RTraversal;
import org.andork.spatial.Rectmath;
import org.andork.spatial.RfStarTree;
import org.andork.spatial.RfStarTree.Branch;
import org.andork.spatial.RfStarTree.Leaf;
import org.andork.spatial.RfStarTree.Node;
import org.andork.swing.async.Subtask;
import org.andork.swing.async.Task;
import org.andork.util.IterableUtils;
import org.breakout.PickResult;
import org.breakout.awt.ParamGradientMapPaint;
import org.omg.CORBA.FloatHolder;
import com.andork.plot.LinearAxisConversion;
import com.jogamp.nativewindow.awt.DirectDataBufferInt;
import com.jogamp.nativewindow.awt.DirectDataBufferInt.BufferedImageInt;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GL2GL3;
import com.jogamp.opengl.GL3;
public class Survey3dModel implements JoglDrawable, JoglResource {
private class AxialSegment3dDrawer extends OneParamSegment3dDrawer {
private int u_axis_location;
private int u_origin_location;
@Override
protected void afterDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
super.afterDraw(segment3ds, context, gl, m, n);
gl.glDisable(GL2GL3.GL_PRIMITIVE_RESTART);
}
@Override
protected void beforeDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
super.beforeDraw(segment3ds, context, gl, m, n);
gl.glUniform3fv(u_axis_location, 1, depthAxis.value(), 0);
gl.glUniform3fv(u_origin_location, 1, depthOrigin.value(), 0);
gl.glEnable(GL2GL3.GL_PRIMITIVE_RESTART);
((GL3) gl).glPrimitiveRestartIndex(RESTART_INDEX);
}
@Override
protected void beforeDraw(Segment3d segment3d, GL2ES2 gl, float[] m, float[] n) {
super.beforeDraw(segment3d, gl, m, n);
segment3d.lineIndices.init(gl);
segment3d.fillIndices.init(gl);
}
@Override
protected String createVertexShaderCode() {
return super.createVertexShaderCode() +
" v_param = dot(a_pos - u_origin, u_axis);";
}
@Override
protected String createVertexShaderVariables() {
return super.createVertexShaderVariables() +
"uniform vec3 u_axis;" +
"uniform vec3 u_origin;" +
"out float v_param;";
}
@Override
protected void doDraw(Segment3d segment3d, GL2ES2 gl) {
gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, segment3d.lineIndices.id());
gl.glDrawElements(GL_LINES, segment3d.lineIndices.buffer().capacity() / BPI, GL_UNSIGNED_INT, 0);
gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, segment3d.fillIndices.id());
gl.glDrawElements(GL_TRIANGLE_STRIP, segment3d.fillIndices.buffer().capacity() / BPI, GL_UNSIGNED_INT,
0);
}
@Override
public void init(GL2ES2 gl) {
if (program <= 0) {
super.init(gl);
u_axis_location = gl.glGetUniformLocation(program, "u_axis");
u_origin_location = gl.glGetUniformLocation(program, "u_origin");
}
}
}
private abstract class BaseSegment3dDrawer implements Segment3dDrawer {
protected int program = 0;
protected int m_location;
protected int v_location;
protected int p_location;
protected int n_location;
protected int a_pos_location;
protected int a_norm_location;
protected int u_ambient_location;
protected int u_nearDist_location;
protected int u_farDist_location;
protected int a_glow_location;
protected int u_glowColor_location;
protected int a_highlightIndex_location;
protected int u_highlightColors_location;
protected void afterDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
gl.glDisable(GL_DEPTH_TEST);
gl.glDisableVertexAttribArray(a_pos_location);
gl.glDisableVertexAttribArray(a_norm_location);
gl.glDisableVertexAttribArray(a_glow_location);
gl.glDisableVertexAttribArray(a_highlightIndex_location);
gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
protected void beforeDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
gl.glUniformMatrix4fv(m_location, 1, false, m, 0);
gl.glUniformMatrix3fv(n_location, 1, false, n, 0);
gl.glUniformMatrix4fv(v_location, 1, false, context.viewXform(), 0);
gl.glUniformMatrix4fv(p_location, 1, false, context.projXform(), 0);
gl.glUniform1fv(u_ambient_location, 1, ambient.value(), 0);
gl.glUniform1fv(u_nearDist_location, 1, nearDist.value(), 0);
gl.glUniform1fv(u_farDist_location, 1, farDist.value(), 0);
gl.glUniform4fv(u_glowColor_location, 1, glowColor.value(), 0);
gl.glUniform4fv(u_highlightColors_location, highlightColors.count(), highlightColors.value(), 0);
gl.glEnableVertexAttribArray(a_pos_location);
gl.glEnableVertexAttribArray(a_norm_location);
gl.glEnableVertexAttribArray(a_glow_location);
gl.glEnableVertexAttribArray(a_highlightIndex_location);
gl.glEnable(GL_DEPTH_TEST);
}
protected void beforeDraw(Segment3d segment3d, GL2ES2 gl, float[] m, float[] n) {
segment3d.geometry.init(gl);
segment3d.stationAttrs.init(gl);
if (segment3d.stationAttrsNeedRebuffering.compareAndSet(true, false)) {
segment3d.stationAttrs.rebuffer(gl);
}
gl.glBindBuffer(GL_ARRAY_BUFFER, segment3d.geometry.id());
gl.glVertexAttribPointer(a_pos_location, 3, GL_FLOAT, false, GEOM_BPV, 0);
gl.glVertexAttribPointer(a_norm_location, 3, GL_FLOAT, false, GEOM_BPV, 12);
gl.glBindBuffer(GL_ARRAY_BUFFER, segment3d.stationAttrs.id());
gl.glVertexAttribPointer(a_glow_location, 2, GL_FLOAT, false, STATION_ATTR_BPV, 0);
gl.glVertexAttribPointer(a_highlightIndex_location, 1, GL_FLOAT, false, STATION_ATTR_BPV, 8);
}
protected String createFragmentShader() {
return "#version 330\n" +
createFragmentShaderVariables() +
"void main() {" +
createFragmentShaderCode() +
"}";
}
protected String createFragmentShaderCode() {
return " float temp;"
+
" vec4 indexedHighlight;"
+
// distance coloration
" color.xyz *= mix(1.0, u_ambient, clamp((v_dist - u_nearDist) / (u_farDist - u_nearDist), 0.0, 1.0));"
+
// glow
" color = mix(color, u_glowColor, clamp(min(v_glow.x, v_glow.y), 0.0, 1.0));"
+
// lighting
" temp = dot(v_norm, vec3(0.0, 0.0, 1.0));"
+
" temp = u_ambient + temp * (1.0 - u_ambient);"
+
" color.xyz *= temp;"
+
// highlights
" indexedHighlight = u_highlightColors[int(floor(v_highlightIndex + 0.5))];"
+
" color = clamp(color + vec4(indexedHighlight.xyz * indexedHighlight.w, 0.0), 0.0, 1.0);";
}
protected String createFragmentShaderVariables() {
// lighting
return "in vec3 v_norm;" +
"uniform float u_ambient;" +
// distance coloration
"in float v_dist;" +
"uniform float u_farDist;" +
"uniform float u_nearDist;" +
// glow
"in vec2 v_glow;" +
"uniform vec4 u_glowColor;" +
// highlights
"uniform vec4 u_highlightColors[3];" +
"in float v_highlightIndex;" +
"out vec4 color;";
}
protected String createVertexShader() {
return "#version 330\n" +
createVertexShaderVariables() +
"void main() {" +
createVertexShaderCode() +
"}";
}
protected String createVertexShaderCode() {
return " gl_Position = p * v * m * vec4(a_pos, 1.0);" +
" v_norm = (v * vec4(normalize(n * a_norm), 0.0)).xyz;" +
" v_dist = -(v * m * vec4(a_pos, 1.0)).z;" +
" v_glow = a_glow;" +
" v_highlightIndex = a_highlightIndex;";
}
protected String createVertexShaderVariables() {
return "uniform mat4 m;" +
"uniform mat4 v;" +
"uniform mat4 p;" +
"in vec3 a_pos;" +
// lighting
"in vec3 a_norm;" +
"out vec3 v_norm;" +
"uniform mat3 n;" +
// distance coloration
"out float v_dist;" +
// glow
"in vec2 a_glow;" +
"out vec2 v_glow;" +
// highlights
"in float a_highlightIndex;" +
"out float v_highlightIndex;";
}
@Override
public void dispose(GL2ES2 gl) {
if (program > 0) {
gl.glDeleteProgram(program);
program = 0;
}
}
protected abstract void doDraw(Segment3d segment3d, GL2ES2 gl);
@Override
public void draw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
if (program <= 0) {
init(gl);
}
gl.glUseProgram(program);
beforeDraw(segment3ds, context, gl, m, n);
for (Segment3d segment3d : segment3ds) {
draw(segment3d, context, gl, m, n);
}
afterDraw(segment3ds, context, gl, m, n);
}
public void draw(Segment3d segment3d, JoglDrawContext context, GL2ES2 gl, float[] m, float[] n) {
beforeDraw(segment3d, gl, m, n);
doDraw(segment3d, gl);
}
@Override
public void init(GL2ES2 gl) {
String vertShader, fragShader;
if (program <= 0) {
vertShader = createVertexShader();
fragShader = createFragmentShader();
program = JoglUtils.loadProgram(gl, vertShader, fragShader);
m_location = gl.glGetUniformLocation(program, "m");
v_location = gl.glGetUniformLocation(program, "v");
p_location = gl.glGetUniformLocation(program, "p");
n_location = gl.glGetUniformLocation(program, "n");
a_pos_location = gl.glGetAttribLocation(program, "a_pos");
a_norm_location = gl.glGetAttribLocation(program, "a_norm");
a_glow_location = gl.glGetAttribLocation(program, "a_glow");
a_highlightIndex_location = gl.glGetAttribLocation(program, "a_highlightIndex");
u_ambient_location = gl.glGetUniformLocation(program, "u_ambient");
u_nearDist_location = gl.glGetUniformLocation(program, "u_nearDist");
u_farDist_location = gl.glGetUniformLocation(program, "u_farDist");
u_glowColor_location = gl.glGetUniformLocation(program, "u_glowColor");
u_highlightColors_location = gl.glGetUniformLocation(program, "u_highlightColors");
}
}
}
private static enum Direction {
FORWARD, BACKWARD;
}
private abstract class OneParamSegment3dDrawer extends BaseSegment3dDrawer {
private int u_loParam_location;
private int u_hiParam_location;
private int u_paramSampler_location;
@Override
protected void beforeDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
super.beforeDraw(segment3ds, context, gl, m, n);
gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_2D, paramTexture);
gl.glUniform1i(u_paramSampler_location, 0);
gl.glUniform1fv(u_loParam_location, 1, loParam.value(), 0);
gl.glUniform1fv(u_hiParam_location, 1, hiParam.value(), 0);
}
@Override
protected String createFragmentShaderCode() {
// param coloration
return " color = texture(u_paramSampler, vec2(0.5, clamp((v_param - u_loParam) / (u_hiParam - u_loParam), 0.0, 1.0)));"
+
super.createFragmentShaderCode();
}
@Override
protected String createFragmentShaderVariables() {
return super.createFragmentShaderVariables() +
"uniform float u_loParam;" +
"uniform float u_hiParam;" +
"uniform sampler2D u_paramSampler;" +
"in float v_param;";
}
@Override
public void init(GL2ES2 gl) {
if (program <= 0) {
super.init(gl);
u_loParam_location = gl.glGetUniformLocation(program, "u_loParam");
u_hiParam_location = gl.glGetUniformLocation(program, "u_hiParam");
u_paramSampler_location = gl.glGetUniformLocation(program, "u_paramSampler");
}
}
}
private class Param0Segment3dDrawer extends OneParamSegment3dDrawer {
private int a_param0_location;
@Override
protected void afterDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
super.afterDraw(segment3ds, context, gl, m, n);
gl.glDisableVertexAttribArray(a_param0_location);
gl.glDisable(GL2GL3.GL_PRIMITIVE_RESTART);
}
@Override
protected void beforeDraw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n) {
super.beforeDraw(segment3ds, context, gl, m, n);
gl.glEnableVertexAttribArray(a_param0_location);
gl.glEnable(GL2GL3.GL_PRIMITIVE_RESTART);
((GL3) gl).glPrimitiveRestartIndex(RESTART_INDEX);
}
@Override
protected void beforeDraw(Segment3d segment3d, GL2ES2 gl, float[] m, float[] n) {
if (segment3d.param0 == null) {
return;
}
super.beforeDraw(segment3d, gl, m, n);
segment3d.param0.init(gl);
if (segment3d.param0NeedsRebuffering.compareAndSet(true, false)) {
segment3d.param0.rebuffer(gl);
}
segment3d.lineIndices.init(gl);
segment3d.fillIndices.init(gl);
gl.glBindBuffer(GL_ARRAY_BUFFER, segment3d.param0.id());
gl.glVertexAttribPointer(a_param0_location, 1, GL_FLOAT, false, 4, 0);
}
@Override
protected String createVertexShaderCode() {
return super.createVertexShaderCode() +
" v_param = a_param0;";
}
@Override
protected String createVertexShaderVariables() {
return super.createVertexShaderVariables() +
"in float a_param0;" +
"out float v_param;";
}
@Override
protected void doDraw(Segment3d segment3d, GL2ES2 gl) {
if (segment3d.param0 == null) {
return;
}
gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, segment3d.lineIndices.id());
gl.glDrawElements(GL_LINES, segment3d.lineIndices.buffer().capacity() / BPI, GL_UNSIGNED_INT, 0);
gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, segment3d.fillIndices.id());
gl.glDrawElements(GL_TRIANGLE_STRIP, segment3d.fillIndices.buffer().capacity() / BPI, GL_UNSIGNED_INT,
0);
}
@Override
public void init(GL2ES2 gl) {
if (program <= 0) {
super.init(gl);
a_param0_location = gl.glGetAttribLocation(program, "a_param0");
}
}
}
public static class Segment3d {
final ArrayList<Shot3d> shot3ds = new ArrayList<Shot3d>();
final LinkedList<Segment3dDrawer> drawers = new LinkedList<Segment3dDrawer>();
JoglBuffer geometry;
JoglBuffer stationAttrs;
final AtomicBoolean stationAttrsNeedRebuffering = new AtomicBoolean();
JoglBuffer param0;
final AtomicBoolean param0NeedsRebuffering = new AtomicBoolean();
JoglBuffer fillIndices;
JoglBuffer lineIndices;
int[] shotIndicesInVertexArrays;
int[] shotIndicesInFillIndices;
void addShot(Shot3d shot3d) {
shot3d.segment3d = this;
shot3d.indexInSegment = shot3ds.size();
shot3ds.add(shot3d);
}
void calcParam0(Survey3dModel model, ColorParam param) {
param0 = new JoglBuffer().buffer(createBuffer(shot3ds.size() * GEOM_VPS * 4));
param0.buffer().position(0);
for (Shot3d shot3d : shot3ds) {
Shot origShot = model.originalShots.get(shot3d.number);
if (!param.isStationMetric()) {
return;
}
float fromValue = param.calcStationParam(origShot, origShot.from);
float toValue = param.calcStationParam(origShot, origShot.to);
if (Float.isNaN(fromValue)) {
fromValue = toValue;
}
if (Float.isNaN(toValue)) {
toValue = fromValue;
}
param0.buffer().putFloat(fromValue);
param0.buffer().putFloat(fromValue);
param0.buffer().putFloat(fromValue);
param0.buffer().putFloat(fromValue);
param0.buffer().putFloat(toValue);
param0.buffer().putFloat(toValue);
param0.buffer().putFloat(toValue);
param0.buffer().putFloat(toValue);
}
param0NeedsRebuffering.set(true);
}
void calcParam0(Survey3dModel model, Map<Station, Double> stationValues) {
param0 = new JoglBuffer().buffer(createBuffer(shot3ds.size() * GEOM_VPS * 4));
param0.buffer().position(0);
for (Shot3d shot3d : shot3ds) {
Shot origShot = model.originalShots.get(shot3d.number);
Double fromValue = stationValues.get(origShot.from);
if (fromValue == null) {
fromValue = Double.NaN;
}
Double toValue = stationValues.get(origShot.to);
if (toValue == null) {
toValue = Double.NaN;
}
param0.buffer().putFloat(fromValue.floatValue());
param0.buffer().putFloat(fromValue.floatValue());
param0.buffer().putFloat(fromValue.floatValue());
param0.buffer().putFloat(fromValue.floatValue());
param0.buffer().putFloat(toValue.floatValue());
param0.buffer().putFloat(toValue.floatValue());
param0.buffer().putFloat(toValue.floatValue());
param0.buffer().putFloat(toValue.floatValue());
}
param0NeedsRebuffering.set(true);
}
void calcParamRange(Survey3dModel model, ColorParam param, float[] rangeInOut) {
float[] origin = model.depthOrigin.value();
float[] axis = model.depthAxis.value();
if (param == ColorParam.DEPTH) {
geometry.buffer().position(0);
for (int i = 0; i < geometry.buffer().capacity(); i += GEOM_BPV) {
float x = geometry.buffer().getFloat(i);
float y = geometry.buffer().getFloat(i + 4);
float z = geometry.buffer().getFloat(i + 8);
float f = (x - origin[0]) * axis[0] + (y - origin[1]) * axis[1] + (z - origin[2])
* axis[2];
if (!Double.isNaN(f)) {
rangeInOut[0] = Math.min(rangeInOut[0], f);
rangeInOut[1] = Math.max(rangeInOut[1], f);
}
}
} else {
param0.buffer().position(0);
while (param0.buffer().hasRemaining()) {
float f = param0.buffer().getFloat();
if (!Double.isNaN(f)) {
rangeInOut[0] = Math.min(rangeInOut[0], f);
rangeInOut[1] = Math.max(rangeInOut[1], f);
}
}
param0.buffer().position(0);
}
}
void clearGlow() {
ByteBuffer buffer = stationAttrs.buffer();
buffer.position(0);
for (int i = 0; i < buffer.capacity(); i += Survey3dModel.STATION_ATTR_BPV) {
buffer.putFloat(i, -Float.MAX_VALUE);
buffer.putFloat(i + 4, -Float.MAX_VALUE);
}
}
void clearHighlights() {
ByteBuffer buffer = stationAttrs.buffer();
buffer.position(0);
for (int i = 8; i < buffer.capacity(); i += Survey3dModel.STATION_ATTR_BPV) {
if (buffer.getFloat(i) != 1) {
buffer.putFloat(i, 0);
}
}
}
public void dispose(GL2ES2 gl) {
geometry.dispose(gl);
stationAttrs.dispose(gl);
if (param0 != null) {
param0.dispose(gl);
}
fillIndices.dispose(gl);
lineIndices.dispose(gl);
}
void populateData(ByteBuffer allGeomBuffer) {
geometry = new JoglBuffer().buffer(createBuffer(shot3ds.size() * GEOM_BPS));
stationAttrs = new JoglBuffer().buffer(createBuffer(shot3ds.size() * STATION_ATTR_BPS));
shotIndicesInVertexArrays = new int[shot3ds.size()];
shotIndicesInFillIndices = new int[shot3ds.size()];
List<Integer> fillIndicesList = new ArrayList<>();
List<Integer> lineIndicesList = new ArrayList<>();
for (Shot3d shot3d : shot3ds) {
shotIndicesInVertexArrays[shot3d.indexInSegment] = geometry.buffer().position() / GEOM_BPV;
shotIndicesInFillIndices[shot3d.indexInSegment] = fillIndicesList.size() * BPI;
copyBytes(allGeomBuffer, geometry.buffer(), shot3d.number, GEOM_BPS);
for (int index : offset(shot3d.indexInSegment * GEOM_VPS, 0, 4, 2, 6, 1, 5, 3, 7, 0, 4)) {
fillIndicesList.add(index);
lineIndicesList.add(index);
}
fillIndicesList.add(RESTART_INDEX);
}
ByteBuffer fillIndicesBuffer = createBuffer(fillIndicesList.size() * BPI);
fillIndices = new JoglBuffer().buffer(fillIndicesBuffer);
for (Integer i : fillIndicesList) {
fillIndicesBuffer.putInt(i);
}
ByteBuffer lineIndicesBuffer = createBuffer(lineIndicesList.size() * BPI);
lineIndices = new JoglBuffer().buffer(lineIndicesBuffer);
for (Integer i : lineIndicesList) {
lineIndicesBuffer.putInt(i);
}
geometry.buffer().position(0);
stationAttrs.buffer().position(0);
fillIndices.buffer().position(0);
lineIndices.buffer().position(0);
}
}
private static interface Segment3dDrawer extends JoglResource {
public void draw(Collection<Segment3d> segment3ds, JoglDrawContext context, GL2ES2 gl, float[] m,
float[] n);
}
public final class SelectionEditor {
final Set<Shot3d> selected = new HashSet<Shot3d>();
final Set<Shot3d> deselected = new HashSet<Shot3d>();
boolean committed = false;
private SelectionEditor() {
}
public void commit() {
if (committed) {
throw new IllegalStateException("already committed");
}
committed = true;
for (Shot3d shot3d : selected) {
selectedShots.add(shot3d);
}
for (Shot3d shot3d : deselected) {
selectedShots.remove(shot3d);
}
Set<Shot3d> affectedShots = new HashSet<Shot3d>();
affectedShots.addAll(selected);
affectedShots.addAll(deselected);
updateHighlights(affectedShots);
}
public SelectionEditor deselect(Shot3d shot3d) {
selected.remove(shot3d);
deselected.add(shot3d);
return this;
}
public SelectionEditor select(Shot3d shot3d) {
selected.add(shot3d);
deselected.remove(shot3d);
return this;
}
}
public static class Shot3d {
int number;
Segment3d segment3d;
int indexInSegment;
RfStarTree.Leaf<Shot3d> leaf;
Shot3d(int number) {
super();
this.number = number;
}
public void calcParamRange(Survey3dModel model, ColorParam param, float[] rangeInOut) {
float[] origin = model.depthOrigin.value();
float[] axis = model.depthAxis.value();
if (param == ColorParam.DEPTH) {
for (float[] coord : coordIterable()) {
float f = (coord[0] - origin[0]) * axis[0] +
(coord[1] - origin[1]) * axis[1] +
(coord[2] - origin[2]) * axis[2];
if (!Double.isNaN(f)) {
rangeInOut[0] = Math.min(rangeInOut[0], f);
rangeInOut[1] = Math.max(rangeInOut[1], f);
}
}
} else {
int i = segment3d.shotIndicesInVertexArrays[indexInSegment];
boolean last = indexInSegment == segment3d.shot3ds.size() - 1;
ByteBuffer param0buffer = segment3d.param0.buffer();
int maxIndex = last ? param0buffer.capacity() / 4
: segment3d.shotIndicesInVertexArrays[indexInSegment + 1];
while (i < maxIndex) {
float f = param0buffer.getFloat(i * 4);
if (!Double.isNaN(f)) {
rangeInOut[0] = Math.min(rangeInOut[0], f);
rangeInOut[1] = Math.max(rangeInOut[1], f);
}
i++;
}
}
}
public Iterable<float[]> coordIterable() {
return coordIterable(new float[3]);
}
public Iterable<float[]> coordIterable(float[] coord) {
return new Iterable<float[]>() {
@Override
public Iterator<float[]> iterator() {
return new Iterator<float[]>() {
int index = 0;
@Override
public boolean hasNext() {
return index < GEOM_VPS;
}
@Override
public float[] next() {
getCoordinate(index, coord);
index++;
return coord;
}
};
}
};
}
public void getCoordinate(int i, float[] result) {
if (i < 0) {
throw new IllegalArgumentException("i must be > 0");
}
if (indexInSegment < segment3d.shotIndicesInVertexArrays.length - 1) {
if (i >= segment3d.shotIndicesInVertexArrays[indexInSegment + 1]) {
throw new IndexOutOfBoundsException("i is not in the bounds of this shot");
}
}
ByteBuffer vertBuffer = segment3d.geometry.buffer();
int baseIndex = (segment3d.shotIndicesInVertexArrays[indexInSegment] + i) * GEOM_BPV;
result[0] = vertBuffer.getFloat(baseIndex);
result[1] = vertBuffer.getFloat(baseIndex + 4);
result[2] = vertBuffer.getFloat(baseIndex + 8);
vertBuffer.position(0);
}
public float getFromGlowA() {
return Survey3dModel.getFromGlowA(segment3d.stationAttrs.buffer(), indexInSegment);
}
public float getFromGlowB() {
return Survey3dModel.getFromGlowB(segment3d.stationAttrs.buffer(), indexInSegment);
}
public int getNumber() {
return number;
}
public float getToGlowA() {
return Survey3dModel.getToGlowA(segment3d.stationAttrs.buffer(), indexInSegment);
}
public float getToGlowB() {
return Survey3dModel.getToGlowB(segment3d.stationAttrs.buffer(), indexInSegment);
}
public void pick(float[] coneOrigin, float[] coneDirection, float coneAngle, Shot3dPickContext c,
List<PickResult<Shot3d>> pickResults) {
Shot3dPickResult result = null;
ByteBuffer indexBuffer = segment3d.fillIndices.buffer();
ByteBuffer vertBuffer = segment3d.geometry.buffer();
int k = segment3d.shotIndicesInFillIndices[indexInSegment];
indexBuffer.position(k);
int i = 0;
int i0 = 0;
int i1 = 0;
int i2;
boolean last = indexInSegment == segment3d.shot3ds.size() - 1;
int maxIndex = last ? indexBuffer.capacity() : segment3d.shotIndicesInFillIndices[indexInSegment + 1];
while (k < maxIndex) {
i2 = indexBuffer.getInt();
k += 4;
if (i2 == RESTART_INDEX) {
i = 0;
continue;
}
if (i >= 2) {
boolean even = i % 2 == 0;
vertBuffer.position((even ? i0 : i2) * GEOM_BPV);
c.p0[0] = vertBuffer.getFloat();
c.p0[1] = vertBuffer.getFloat();
c.p0[2] = vertBuffer.getFloat();
vertBuffer.position(i1 * GEOM_BPV);
c.p1[0] = vertBuffer.getFloat();
c.p1[1] = vertBuffer.getFloat();
c.p1[2] = vertBuffer.getFloat();
vertBuffer.position((even ? i2 : i0) * GEOM_BPV);
c.p2[0] = vertBuffer.getFloat();
c.p2[1] = vertBuffer.getFloat();
c.p2[2] = vertBuffer.getFloat();
try {
c.lpx.lineFromRay(coneOrigin, coneDirection);
c.lpx.planeFromPoints(c.p0, c.p1, c.p2);
c.lpx.findIntersection();
if (c.lpx.isPointIntersection() && c.lpx.isOnRay() && c.lpx.isInTriangle()) {
if (result == null || result.lateralDistance > 0 || c.lpx.t < result.distance) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = c.lpx.t;
result.locationAlongShot = even ? c.lpx.u : 1 - c.lpx.u;
result.lateralDistance = 0;
setf(result.location, c.lpx.result);
}
} else if (result == null || result.lateralDistance > 0) {
if (c.inConeTester.isLineSegmentInCone(c.p0, c.p1, coneOrigin, coneDirection,
coneAngle)) {
if (result == null || c.inConeTester.lateralDistance
* result.distance < result.lateralDistance * c.inConeTester.t) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = c.inConeTester.t;
result.lateralDistance = c.inConeTester.lateralDistance;
result.locationAlongShot = even ? c.inConeTester.s : 1 - c.inConeTester.s;
Vecmath.interp3(c.p0, c.p1, c.inConeTester.s, result.location);
}
} else if (c.inConeTester.isLineSegmentInCone(c.p1, c.p2, coneOrigin, coneDirection,
coneAngle)) {
if (result == null || c.inConeTester.lateralDistance
* result.distance < result.lateralDistance * c.inConeTester.t) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = c.inConeTester.t;
result.lateralDistance = c.inConeTester.lateralDistance;
result.locationAlongShot = even ? 1 - c.inConeTester.s : c.inConeTester.s;
Vecmath.interp3(c.p1, c.p2, c.inConeTester.s, result.location);
}
} else if (c.inConeTester.isLineSegmentInCone(c.p2, c.p0, coneOrigin, coneDirection,
coneAngle)) {
if (result == null || c.inConeTester.lateralDistance
* result.distance < result.lateralDistance * c.inConeTester.t) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = c.inConeTester.t;
result.lateralDistance = c.inConeTester.lateralDistance;
result.locationAlongShot = i % 2;
Vecmath.interp3(c.p2, c.p0, c.inConeTester.s, result.location);
}
}
}
} catch (Exception ex) {
}
}
i0 = i1;
i1 = i2;
i++;
}
if (result != null) {
pickResults.add(result);
}
vertBuffer.position(0);
indexBuffer.position(0);
}
public void pick(float[] rayOrigin, float[] rayDirection, Shot3dPickContext c,
List<PickResult<Shot3d>> pickResults) {
Shot3dPickResult result = null;
ByteBuffer indexBuffer = segment3d.fillIndices.buffer();
ByteBuffer vertBuffer = segment3d.geometry.buffer();
indexBuffer.position(segment3d.shotIndicesInFillIndices[indexInSegment]);
int i = 0;
int i0 = 0;
int i1 = 0;
int i2;
boolean last = indexInSegment == segment3d.shot3ds.size() - 1;
int maxIndex = last ? indexBuffer.capacity() : segment3d.shotIndicesInFillIndices[indexInSegment + 1];
while (indexBuffer.position() < maxIndex) {
i2 = indexBuffer.getInt();
if (i2 == RESTART_INDEX) {
i = 0;
continue;
}
if (i >= 2) {
boolean even = i % 2 == 0;
vertBuffer.position((even ? i0 : i2) * GEOM_BPV);
c.p0[0] = vertBuffer.getFloat();
c.p0[1] = vertBuffer.getFloat();
c.p0[2] = vertBuffer.getFloat();
vertBuffer.position(i1 * GEOM_BPV);
c.p1[0] = vertBuffer.getFloat();
c.p1[1] = vertBuffer.getFloat();
c.p1[2] = vertBuffer.getFloat();
vertBuffer.position((even ? i2 : i0) * GEOM_BPV);
c.p2[0] = vertBuffer.getFloat();
c.p2[1] = vertBuffer.getFloat();
c.p2[2] = vertBuffer.getFloat();
try {
c.lpx.lineFromRay(rayOrigin, rayDirection);
c.lpx.planeFromPoints(c.p0, c.p1, c.p2);
c.lpx.findIntersection();
if (c.lpx.isPointIntersection() && c.lpx.isOnRay() && c.lpx.isInTriangle()) {
if (result == null || c.lpx.t < result.distance) {
result = new Shot3dPickResult();
result.picked = this;
result.distance = c.lpx.t;
result.locationAlongShot = i % 2 == 0 ? c.lpx.u : 1 - c.lpx.u;
setf(result.location, c.lpx.result);
}
}
} catch (Exception ex) {
}
}
i0 = i1;
i1 = i2;
i++;
}
if (result != null) {
pickResults.add(result);
}
vertBuffer.position(0);
indexBuffer.position(0);
}
public void pick(PlanarHull3f hull, Shot3dPickContext c, List<PickResult<Shot3d>> pickResults) {
Shot3dPickResult result = null;
ByteBuffer indexBuffer = segment3d.fillIndices.buffer();
ByteBuffer vertBuffer = segment3d.geometry.buffer();
int k = segment3d.shotIndicesInFillIndices[indexInSegment];
indexBuffer.position(k);
int i = 0;
int i0 = 0;
int i1 = 0;
int i2;
boolean last = indexInSegment == segment3d.shot3ds.size() - 1;
int maxIndex = last ? indexBuffer.capacity() : segment3d.shotIndicesInFillIndices[indexInSegment + 1];
while (k < maxIndex) {
i2 = indexBuffer.getInt();
k += 4;
if (i2 == RESTART_INDEX) {
i = 0;
continue;
}
if (i >= 2) {
boolean even = i % 2 == 0;
vertBuffer.position((even ? i0 : i2) * GEOM_BPV);
c.p0[0] = vertBuffer.getFloat();
c.p0[1] = vertBuffer.getFloat();
c.p0[2] = vertBuffer.getFloat();
vertBuffer.position(i1 * GEOM_BPV);
c.p1[0] = vertBuffer.getFloat();
c.p1[1] = vertBuffer.getFloat();
c.p1[2] = vertBuffer.getFloat();
vertBuffer.position((even ? i2 : i0) * GEOM_BPV);
c.p2[0] = vertBuffer.getFloat();
c.p2[1] = vertBuffer.getFloat();
c.p2[2] = vertBuffer.getFloat();
try {
c.lpx.lineFromPoints(hull.origins[4], hull.origins[5]);
c.lpx.planeFromPoints(c.p0, c.p1, c.p2);
c.lpx.findIntersection();
if (c.lpx.isPointIntersection() && c.lpx.isOnRay() && c.lpx.isInTriangle()
&& hull.containsPoint(c.lpx.result)) {
if (result == null || result.lateralDistance > 0 || c.lpx.t < result.distance) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = c.lpx.t;
result.locationAlongShot = even ? c.lpx.u : 1 - c.lpx.u;
result.lateralDistance = 0;
setf(result.location, c.lpx.result);
}
} else if (result == null || result.lateralDistance > 0) {
for (int[] triangle : hullTriangleIndices) {
c.tpx.plane1FromPoints(c.p0, c.p1, c.p2);
c.tpx.plane2FromPoints(
hull.vertices[triangle[0]],
hull.vertices[triangle[1]],
hull.vertices[triangle[2]]);
c.tpx.twoTriangleIntersection();
if (c.tpx.intersectionType == ResultType.LINEAR_INTERSECTION) {
c.tpx.calcIntersectionPoint(c.tpx.t2[0], c.x0);
c.tpx.calcIntersectionPoint(c.tpx.t2[1], c.x1);
float distance0 = Vecmath.subDot3(c.x0, hull.origins[4], hull.normals[4]);
float distance1 = Vecmath.subDot3(c.x1, hull.origins[4], hull.normals[4]);
if (distance1 < distance0) {
distance0 = distance1;
setf(c.x0, c.x1);
}
float diagonal = Vecmath.distance3sq(hull.origins[4], c.x0);
float lateralDistance = (float) Math.sqrt(diagonal - distance0 * distance0);
if (result == null
|| lateralDistance * result.distance < result.lateralDistance * distance0) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = distance0;
result.lateralDistance = lateralDistance;
result.locationAlongShot = k >= 4 ? 1 : 0;
setf(result.location, c.x0);
}
}
}
}
} catch (Exception ex) {
}
}
i0 = i1;
i1 = i2;
i++;
}
if (result == null) {
k = 0;
for (float[] coord : coordIterable()) {
if (hull.containsPoint(coord)) {
float distance = Vecmath.subDot3(coord, hull.origins[4], hull.normals[4]);
float diagonal = Vecmath.distance3sq(hull.origins[4], coord);
float lateralDistance = (float) Math.sqrt(diagonal - distance * distance);
if (result == null || lateralDistance * result.distance < result.lateralDistance * distance) {
if (result == null) {
result = new Shot3dPickResult();
}
result.picked = this;
result.distance = distance;
result.lateralDistance = lateralDistance;
result.locationAlongShot = k >= 4 ? 1 : 0;
setf(result.location, coord);
}
}
k++;
}
}
if (result != null) {
pickResults.add(result);
}
vertBuffer.position(0);
indexBuffer.position(0);
}
public void setFromGlowA(float glow) {
Survey3dModel.setFromGlowA(segment3d.stationAttrs.buffer(), indexInSegment, glow);
}
public void setFromGlowB(float glow) {
Survey3dModel.setFromGlowB(segment3d.stationAttrs.buffer(), indexInSegment, glow);
}
public void setToGlowA(float glow) {
Survey3dModel.setToGlowA(segment3d.stationAttrs.buffer(), indexInSegment, glow);
}
public void setToGlowB(float glow) {
Survey3dModel.setToGlowB(segment3d.stationAttrs.buffer(), indexInSegment, glow);
}
public void unionMbrInto(float[] mbr) {
Rectmath.union3(mbr, leaf.mbr(), mbr);
}
}
public static final class Shot3dPickContext {
final LinePlaneIntersection3f lpx = new LinePlaneIntersection3f();
final float[] p0 = new float[3];
final float[] p1 = new float[3];
final float[] p2 = new float[3];
final float[] x0 = new float[3];
final float[] x1 = new float[3];
final float[] adjacent = new float[3];
final float[] opposite = new float[3];
final InConeTester3f inConeTester = new InConeTester3f();
final TwoPlaneIntersection3f tpx = new TwoPlaneIntersection3f();
}
public static class Shot3dPickResult extends PickResult<Shot3d> {
public float locationAlongShot;
}
private static final int GEOM_BPV = 24;
private static final int GEOM_VPS = 8;
private static final int GEOM_BPS = GEOM_BPV * GEOM_VPS;
private static final int STATION_ATTR_BPV = 12;
private static final int STATION_ATTR_VPS = GEOM_VPS;
private static final int STATION_ATTR_BPS = STATION_ATTR_BPV
* STATION_ATTR_VPS;
private static final int BPI = 4;
private static final int RESTART_INDEX = 0xffffffff;
private static final int[][] hullTriangleIndices = {
{ 0, 6, 4 }, { 6, 0, 2 },
{ 2, 7, 6 }, { 7, 2, 3 },
{ 3, 5, 7 }, { 5, 3, 1 },
{ 1, 4, 5 }, { 4, 1, 0 },
{ 0, 3, 2 }, { 3, 0, 1 },
{ 4, 7, 6 }, { 7, 4, 5 }
};
private static void addShots(Node<Shot3d> node, Segment3d segment3d) {
if (node instanceof Leaf) {
segment3d.addShot(((Leaf<Shot3d>) node).object());
} else if (node instanceof Branch) {
Branch<Shot3d> branch = (Branch<Shot3d>) node;
for (int i = 0; i < branch.numChildren(); i++) {
addShots(branch.childAt(i), segment3d);
}
}
}
private static void copyBytes(ByteBuffer src, ByteBuffer dest, int shotIndex, int bytesPerShot) {
src.clear();
src.position(shotIndex * bytesPerShot);
src.limit(src.position() + bytesPerShot);
dest.put(src);
}
public static Survey3dModel create(List<Shot> originalShots, int maxChildrenPerBranch, int minSplitSize,
int numToReinsert, Task task) {
Subtask rootSubtask = null;
int renderProportion = 5;
if (task != null) {
task.setTotal(1000);
rootSubtask = new Subtask(task);
} else {
rootSubtask = Subtask.dummySubtask();
}
rootSubtask.setStatus("Updating view");
rootSubtask.setTotal(renderProportion + 5);
List<Shot3d> shot3ds = new ArrayList<Shot3d>();
for (int i = 0; i < originalShots.size(); i++) {
shot3ds.add(new Shot3d(i));
}
if (rootSubtask.isCanceling()) {
return null;
}
rootSubtask.setCompleted(rootSubtask.getCompleted() + 1);
ByteBuffer geomBuffer = createInitialGeometry(originalShots, rootSubtask.beginSubtask(1));
if (rootSubtask.isCanceling()) {
return null;
}
rootSubtask.setCompleted(rootSubtask.getCompleted() + 1);
RfStarTree<Shot3d> tree = createTree(shot3ds, geomBuffer, maxChildrenPerBranch, minSplitSize,
numToReinsert, rootSubtask.beginSubtask(1));
if (rootSubtask.isCanceling()) {
return null;
}
rootSubtask.setCompleted(rootSubtask.getCompleted() + 1);
int segmentLevel = Math.min(tree.getRoot().level(), 3);
Set<Segment3d> segment3ds = createSegments(tree, segmentLevel, rootSubtask.beginSubtask(1));
if (rootSubtask.isCanceling()) {
return null;
}
rootSubtask.setCompleted(rootSubtask.getCompleted() + 1);
Subtask renderSubtask = rootSubtask.beginSubtask(renderProportion);
renderSubtask.setStatus("sending data to graphics card");
renderSubtask.setTotal(segment3ds.size() * 2);
for (Segment3d segment3d : segment3ds) {
segment3d.populateData(geomBuffer);
if (renderSubtask.isCanceling()) {
return null;
}
renderSubtask.setCompleted(renderSubtask.getCompleted() + 1);
}
Survey3dModel model = new Survey3dModel(originalShots, shot3ds, tree, segment3ds, renderSubtask);
if (rootSubtask.isCanceling()) {
return null;
}
renderSubtask.end();
rootSubtask.setCompleted(rootSubtask.getCompleted() + renderProportion);
return model;
}
private static ByteBuffer createBuffer(int capacity) {
ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
buffer.order(ByteOrder.nativeOrder());
return buffer;
}
private static void createFillIndices(ByteBuffer dest, int shotCount) {
for (int i = 0; i < shotCount; i++) {
for (int index : offset(i * GEOM_VPS,
0, 4, 2, 6, 2, 4,
2, 6, 1, 5, 1, 6,
1, 5, 3, 7, 3, 5,
3, 7, 0, 4, 0, 7)) {
dest.putInt(index);
}
}
}
private static ByteBuffer createInitialGeometry(List<Shot> originalShots, Subtask task) {
task.setStatus("creating geometry");
task.setTotal(originalShots.size());
final double[] left = new double[3];
BufferHelper geomHelper = new BufferHelper();
int count = 0;
for (Shot shot : originalShots) {
if (Vecmath.distance3(shot.from.position, shot.to.position) > 200) {
System.err.println(shot.from.name + ": " + Arrays.toString(shot.from.position) + " - "
+ shot.to.name + ": " + Arrays.toString(shot.to.position));
}
putValues(geomHelper, shot.fromSplayPoints, shot.fromSplayNormals);
putValues(geomHelper, shot.toSplayPoints, shot.toSplayNormals);
// double shotAzimuth = shot.azimuthAt( shot.from );
//
// double fromAzimuth = shotAzimuth - Math.PI * 0.5;
//
// if( shot.from.shots.size( ) == 2 )
// {
// Shot otherShot = otherShot( shot.from , shot );
// double otherAzimuth = otherShot.azimuthAt( shot.from );
// if( !Double.isNaN( otherAzimuth ) )
// {
// fromAzimuth = otherAzimuth + AngleUtils.clockwiseRotation(
// otherAzimuth , shotAzimuth ) * 0.5;
// }
// }
//
// left[ 0 ] = Math.sin( fromAzimuth );
// left[ 1 ] = 0;
// left[ 2 ] = -Math.cos( fromAzimuth );
//
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.from.position[ i ] + left[ i ] *
// shot.fromXsection.dist[ 0 ] );
// }
// geomHelper.putAsFloats( left );
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.from.position[ i ] - left[ i ] *
// shot.fromXsection.dist[ 1 ] );
// }
// geomHelper.putAsFloats( -left[ 0 ] , -left[ 1 ] , -left[ 2 ] );
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.from.position[ i ] + ( i == 1 ?
// shot.fromXsection.dist[ 2 ] : 0.0 ) );
// }
// geomHelper.putAsFloats( 0 , 1 , 0 );
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.from.position[ i ] - ( i == 1 ?
// shot.fromXsection.dist[ 3 ] : 0.0 ) );
// }
// geomHelper.putAsFloats( 0 , -1 , 0 );
//
// double toAzimuth = shotAzimuth - Math.PI * 0.5;
//
// if( shot.to.shots.size( ) == 2 )
// {
// Shot otherShot = otherShot( shot.to , shot );
// double otherAzimuth = otherShot.azimuthAt( shot.to );
// if( !Double.isNaN( otherAzimuth ) )
// {
// toAzimuth = otherAzimuth - AngleUtils.counterclockwiseRotation(
// otherAzimuth ,
// shot.azimuthAt( shot.to ) ) * 0.5;
// }
// }
//
// left[ 0 ] = Math.sin( toAzimuth );
// left[ 1 ] = 0;
// left[ 2 ] = -Math.cos( toAzimuth );
//
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.to.position[ i ] + left[ i ] *
// shot.toXsection.dist[ 0 ] );
// }
// geomHelper.putAsFloats( left );
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.to.position[ i ] - left[ i ] *
// shot.toXsection.dist[ 1 ] );
// }
// geomHelper.putAsFloats( -left[ 0 ] , -left[ 1 ] , -left[ 2 ] );
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.to.position[ i ] + ( i == 1 ?
// shot.toXsection.dist[ 2 ] : 0.0 ) );
// }
// geomHelper.putAsFloats( 0 , 1 , 0 );
// for( int i = 0 ; i < 3 ; i++ )
// {
// geomHelper.putAsFloats( shot.to.position[ i ] - ( i == 1 ?
// shot.toXsection.dist[ 3 ] : 0.0 ) );
// }
// geomHelper.putAsFloats( 0 , -1 , 0 );
if (count++ % 100 == 0 && task != null) {
if (task.isCanceling()) {
return null;
}
task.setCompleted(count);
}
}
task.end();
return geomHelper.toByteBuffer();
}
private static void createLineIndices(ByteBuffer dest, int shotCount) {
for (int i = 0; i < shotCount; i++) {
for (int index : offset(i * GEOM_VPS,
0, 4, 0, 2, 4, 2, 4, 6,
2, 6, 2, 1, 6, 1, 6, 5,
1, 5, 1, 3, 5, 3, 5, 7,
3, 7, 3, 0, 7, 0, 7, 4)) {
dest.putInt(index);
}
}
}
private static Segment3d createSegment(Node<Shot3d> node) {
Segment3d segment3d = new Segment3d();
addShots(node, segment3d);
segment3d.shot3ds.trimToSize();
return segment3d;
}
private static void createSegments(RfStarTree.Node<Shot3d> node, int segmentLevel, Set<Segment3d> result) {
if (node.level() == segmentLevel) {
result.add(createSegment(node));
} else if (node instanceof RfStarTree.Branch) {
RfStarTree.Branch<Shot3d> branch = (RfStarTree.Branch<Shot3d>) node;
for (int i = 0; i < branch.numChildren(); i++) {
createSegments(branch.childAt(i), segmentLevel, result);
}
}
}
private static Set<Segment3d> createSegments(RfStarTree<Shot3d> tree, int segmentLevel, Subtask task) {
task.setStatus("creating render segments");
task.setIndeterminate(true);
Set<Segment3d> result = new HashSet<Segment3d>();
createSegments(tree.getRoot(), segmentLevel, result);
task.end();
return result;
}
private static RfStarTree<Shot3d> createTree(List<Shot3d> shot3ds, ByteBuffer geomBuffer,
int maxChildrenPerBranch, int minSplitSize, int numToReinsert, Subtask task) {
RfStarTree<Shot3d> tree = new RfStarTree<Shot3d>(3, maxChildrenPerBranch, minSplitSize, numToReinsert);
int numShots = geomBuffer.capacity() / GEOM_BPS;
task.setStatus("creating spatial index");
task.setTotal(numShots);
for (int s = 0; s < numShots; s++) {
float[] mbr = voidRectf(3);
int shotStart = s * GEOM_BPS;
for (int v = 0; v < GEOM_VPS; v++) {
geomBuffer.position(shotStart + v * GEOM_BPV);
float x = geomBuffer.getFloat();
float y = geomBuffer.getFloat();
float z = geomBuffer.getFloat();
mbr[0] = nmin(mbr[0], x);
mbr[1] = nmin(mbr[1], y);
mbr[2] = nmin(mbr[2], z);
mbr[3] = nmax(mbr[3], x);
mbr[4] = nmax(mbr[4], y);
mbr[5] = nmax(mbr[5], z);
}
Shot3d shot = shot3ds.get(s);
shot.leaf = tree.createLeaf(mbr, shot);
tree.insert(shot.leaf);
if (s % 100 == 0 && task.isCanceling()) {
return null;
}
task.setCompleted(s);
}
task.end();
return tree;
}
private static float getFromGlowA(ByteBuffer buffer, int shotIndex) {
return buffer.getFloat(shotIndex * STATION_ATTR_BPS);
}
private static float getFromGlowB(ByteBuffer buffer, int shotIndex) {
return buffer.getFloat(shotIndex * STATION_ATTR_BPS + 4);
}
private static float getToGlowA(ByteBuffer buffer, int shotIndex) {
return buffer.getFloat(shotIndex * STATION_ATTR_BPS + STATION_ATTR_BPV * STATION_ATTR_VPS / 2);
}
private static float getToGlowB(ByteBuffer buffer, int shotIndex) {
return buffer.getFloat(shotIndex * STATION_ATTR_BPS + STATION_ATTR_BPV * STATION_ATTR_VPS / 2 + 4);
}
private static int[] offset(int offset, int... in) {
for (int i = 0; i < in.length; i++) {
in[i] += offset;
}
return in;
}
private static Shot otherShot(Station station, Shot shot) {
for (Shot other : station.shots) {
if (other != shot) {
return other;
}
}
return null;
}
private static void putValues(BufferHelper geomHelper, float[][] splayPoints, float[][] splayNormals) {
for (int i = 0; i < 4; i++) {
if (splayPoints == null || splayPoints[i] == null) {
geomHelper.put(Float.NaN, Float.NaN, Float.NaN);
} else {
geomHelper.put(splayPoints[i]);
}
if (splayNormals == null || splayNormals[i] == null) {
geomHelper.put(Float.NaN, Float.NaN, Float.NaN);
} else {
geomHelper.put(splayNormals[i]);
}
}
}
private static void setFromGlowA(ByteBuffer buffer, int shotIndex, float value) {
int index = shotIndex * STATION_ATTR_BPS;
for (int i = 0; i < STATION_ATTR_VPS / 2; i++) {
buffer.putFloat(index + i * STATION_ATTR_BPV, value);
}
}
private static void setFromGlowB(ByteBuffer buffer, int shotIndex, float value) {
int index = shotIndex * STATION_ATTR_BPS + 4;
for (int i = 0; i < STATION_ATTR_VPS / 2; i++) {
buffer.putFloat(index + i * STATION_ATTR_BPV, value);
}
}
private static void setToGlowA(ByteBuffer buffer, int shotIndex, float value) {
int index = shotIndex * STATION_ATTR_BPS + STATION_ATTR_BPV * STATION_ATTR_VPS / 2;
for (int i = 0; i < STATION_ATTR_VPS / 2; i++) {
buffer.putFloat(index + i * STATION_ATTR_BPV, value);
}
}
private static void setToGlowB(ByteBuffer buffer, int shotIndex, float value) {
int index = shotIndex * STATION_ATTR_BPS + STATION_ATTR_BPV * STATION_ATTR_VPS / 2 + 4;
for (int i = 0; i < STATION_ATTR_VPS / 2; i++) {
buffer.putFloat(index + i * STATION_ATTR_BPV, value);
}
}
List<Shot> originalShots;
List<Shot3d> shot3ds;
RfStarTree<Shot3d> tree;
Set<Segment3d> segment3ds;
ColorParam colorParam = ColorParam.DEPTH;
AxialSegment3dDrawer axialSegment3dDrawer = new AxialSegment3dDrawer();
Param0Segment3dDrawer param0Segment3dDrawer = new Param0Segment3dDrawer();
AtomicReference<MultiMap<Segment3dDrawer, Segment3d>> drawers = new AtomicReference<>();
final Set<Shot3d> selectedShots = new HashSet<Shot3d>();
Shot3d hoveredShot;
Float hoverLocation;
LinearAxisConversion glowExtentConversion;
final Set<Segment3d> segmentsWithGlow = new HashSet<Segment3d>();
LinearGradientPaint paramPaint;
int paramTexture;
BufferedImageInt paramTextureImage;
boolean paramTextureNeedsUpdate;
Uniform4fv highlightColors;
Uniform3fv depthAxis;
Uniform3fv depthOrigin;
Uniform1fv ambient;
Uniform1fv nearDist;
Uniform1fv farDist;
Uniform1fv loParam;
Uniform1fv hiParam;
Uniform4fv glowColor;
private Survey3dModel(List<Shot> originalShots, List<Shot3d> shot3ds, RfStarTree<Shot3d> tree,
Set<Segment3d> segment3ds, Subtask renderSubtask) {
super();
this.originalShots = originalShots;
this.shot3ds = shot3ds;
this.tree = tree;
this.segment3ds = segment3ds;
highlightColors = new Uniform4fv().name("u_highlightColors");
highlightColors.value(
0f, 0f, 0f, 0f,
0f, 1f, 1f, 0.5f,
0f, 1f, 1f, 0.5f);
highlightColors.count(3);
depthAxis = new Uniform3fv().name("u_axis").value(0f, -1f, 0f);
depthOrigin = new Uniform3fv().name("u_origin").value(0f, 0f, 0f);
glowColor = new Uniform4fv().name("u_glowColor").value(0f, 1f, 1f, 1f);
ambient = new Uniform1fv().name("u_ambient").value(0.5f);
loParam = new Uniform1fv().name("u_loParam").value(0);
hiParam = new Uniform1fv().name("u_hiParam").value(1000);
nearDist = new Uniform1fv().name("u_nearDist").value(0);
farDist = new Uniform1fv().name("u_farDist").value(1000);
for (Segment3d segment3d : segment3ds) {
segment3d.drawers.add(axialSegment3dDrawer);
}
MultiMap<Segment3dDrawer, Segment3d> drawers = LinkedHashSetMultiMap.newInstance();
drawers.putAll(axialSegment3dDrawer, segment3ds);
this.drawers.set(drawers);
}
public void addOriginalShotsTo(Collection<? super Shot> dest) {
dest.addAll(originalShots);
}
public void addSelectedShotsTo(Collection<? super Shot3d> dest) {
dest.addAll(selectedShots);
}
private void applySelectionHighlights(Shot3d shot3d) {
ByteBuffer buffer = shot3d.segment3d.stationAttrs.buffer();
for (int i = 0; i < STATION_ATTR_VPS; i++) {
int index = shot3d.indexInSegment * STATION_ATTR_BPS + 8 + i * STATION_ATTR_BPV;
if (buffer.getFloat(index) != 1) {
buffer.putFloat(index, 2f);
}
}
}
public float[] calcAutofitAllParamRange(Subtask subtask) {
if (subtask != null) {
subtask.setTotal(segment3ds.size());
subtask.setCompleted(0);
subtask.setIndeterminate(false);
}
final float[] range = { Float.MAX_VALUE, -Float.MAX_VALUE };
int completed = 0;
for (Segment3d segment3d : segment3ds) {
segment3d.calcParamRange(Survey3dModel.this, colorParam, range);
if (completed++ % 100 == 0) {
if (subtask != null) {
if (subtask.isCanceling()) {
return null;
}
subtask.setCompleted(completed);
}
}
}
return range;
}
public float[] calcAutofitParamRange(Collection<Shot3d> shots, Subtask subtask) {
if (subtask != null) {
subtask.setTotal(segment3ds.size());
subtask.setCompleted(0);
subtask.setIndeterminate(false);
}
final float[] range = { Float.MAX_VALUE, -Float.MAX_VALUE };
int completed = 0;
for (Shot3d shot3d : shots) {
shot3d.calcParamRange(Survey3dModel.this, colorParam, range);
if (completed++ % 100 == 0) {
if (subtask != null) {
if (subtask.isCanceling()) {
return null;
}
subtask.setCompleted(completed);
}
}
}
return range;
}
public float[] calcAutofitParamRange(Subtask subtask) {
if (selectedShots.size() < 2 || colorParam == ColorParam.DISTANCE_ALONG_SHOTS) {
return calcAutofitAllParamRange(subtask);
} else {
return calcAutofitSelectedParamRange(subtask);
}
}
public float[] calcAutofitSelectedParamRange(Subtask subtask) {
if (subtask != null) {
subtask.setTotal(segment3ds.size());
subtask.setCompleted(0);
subtask.setIndeterminate(false);
}
final float[] range = { Float.MAX_VALUE, -Float.MAX_VALUE };
int completed = 0;
for (Shot3d shot3d : selectedShots) {
shot3d.calcParamRange(Survey3dModel.this, colorParam, range);
if (completed++ % 100 == 0) {
if (subtask != null) {
if (subtask.isCanceling()) {
return null;
}
subtask.setCompleted(completed);
}
}
}
return range;
}
public void calcDistFromSelected(Subtask subtask) {
final Map<Station, Double> distances = new HashMap<Station, Double>();
class PEntry implements Comparable<PEntry> {
public final double priority;
public final Station station;
public PEntry(double priority, Station station) {
super();
this.priority = priority;
this.station = station;
}
@Override
public int compareTo(PEntry o) {
return Double.compare(priority, o.priority);
}
}
PriorityQueue<PEntry> queue = new PriorityQueue<PEntry>();
for (Shot3d shot3d : selectedShots) {
Shot origShot = originalShots.get(shot3d.number);
distances.put(origShot.from, 0.0);
distances.put(origShot.to, 0.0);
queue.add(new PEntry(0.0, origShot.from));
queue.add(new PEntry(0.0, origShot.to));
}
while (!queue.isEmpty()) {
PEntry next = queue.poll();
for (Shot shot : next.station.shots) {
Station nextStation = shot.otherStation(next.station);
if (!distances.containsKey(nextStation)) {
double distance = next.priority + colorParam.calcTraversalDistance(shot);
distances.put(nextStation, distance);
queue.add(new PEntry(distance, nextStation));
}
}
}
if (subtask != null) {
subtask.setTotal(segment3ds.size());
subtask.setCompleted(0);
subtask.setIndeterminate(false);
}
final Iterator<Segment3d> segmentIterator = segment3ds.iterator();
int processed = 0;
while (segmentIterator.hasNext()) {
Segment3d segment3d = segmentIterator.next();
segment3d.calcParam0(Survey3dModel.this, distances);
if (processed++ % 100 == 0) {
if (subtask != null) {
if (subtask.isCanceling()) {
return;
}
subtask.setCompleted(processed);
}
}
}
}
public void calcDistFromShots(Set<Shot3d> shots, Subtask subtask) {
final Map<Station, Double> distances = new HashMap<Station, Double>();
class PEntry implements Comparable<PEntry> {
public final double priority;
public final Station station;
public PEntry(double priority, Station station) {
super();
this.priority = priority;
this.station = station;
}
@Override
public int compareTo(PEntry o) {
return Double.compare(priority, o.priority);
}
}
PriorityQueue<PEntry> queue = new PriorityQueue<PEntry>();
for (Shot3d shot3d : shots) {
Shot origShot = originalShots.get(shot3d.number);
distances.put(origShot.from, 0.0);
distances.put(origShot.to, 0.0);
queue.add(new PEntry(0.0, origShot.from));
queue.add(new PEntry(0.0, origShot.to));
}
while (!queue.isEmpty()) {
PEntry next = queue.poll();
for (Shot shot : next.station.shots) {
Station nextStation = shot.otherStation(next.station);
if (!distances.containsKey(nextStation)) {
double distance = next.priority + colorParam.calcTraversalDistance(shot);
distances.put(nextStation, distance);
queue.add(new PEntry(distance, nextStation));
}
}
}
if (subtask != null) {
subtask.setTotal(segment3ds.size());
subtask.setCompleted(0);
subtask.setIndeterminate(false);
}
final Iterator<Segment3d> segmentIterator = segment3ds.iterator();
int processed = 0;
while (segmentIterator.hasNext()) {
Segment3d segment3d = segmentIterator.next();
segment3d.calcParam0(Survey3dModel.this, distances);
if (processed++ % 100 == 0) {
if (subtask != null) {
if (subtask.isCanceling()) {
return;
}
subtask.setCompleted(processed);
}
}
}
}
public Set<Shot3d> copySelectedShots() {
return new HashSet<>(selectedShots);
}
@Override
public void dispose(GL2ES2 gl) {
for (Segment3d segment3d : segment3ds) {
segment3d.dispose(gl);
}
MultiMap<Segment3dDrawer, Segment3d> drawers = this.drawers.get();
for (Segment3dDrawer drawer : drawers.keySet()) {
drawer.dispose(gl);
}
disposeParamTexture(gl);
}
private void disposeParamTexture(GL2ES2 gl) {
if (paramTexture > 0) {
gl.glDeleteTextures(1, new int[] { paramTexture }, 0);
paramTexture = 0;
}
}
@Override
public void draw(JoglDrawContext context, GL2ES2 gl, float[] m, float[] n) {
if (paramTextureNeedsUpdate) {
updateParamTexture(gl);
paramTextureNeedsUpdate = false;
}
MultiMap<Segment3dDrawer, Segment3d> drawers = this.drawers.get();
for (Segment3dDrawer drawer : drawers.keySet()) {
drawer.draw(drawers.get(drawer), context, gl, m, n);
}
}
public SelectionEditor editSelection() {
return new SelectionEditor();
}
public void getCenter(float[] center) {
float[] mbr = tree.getRoot().mbr();
center[0] = (mbr[0] + mbr[3]) * 0.5f;
center[1] = (mbr[1] + mbr[4]) * 0.5f;
center[2] = (mbr[2] + mbr[5]) * 0.5f;
}
private float getFarthestExtent(Set<Shot3d> shotsInView, float[] shotsInViewMbr, float[] direction,
FloatBinaryOperator extentFunction) {
FloatHolder farthest = new FloatHolder(Float.NaN);
float[] testPoint = new float[3];
RTraversal.traverse(getTree().getRoot(),
node -> {
if (!Rectmath.intersects3(shotsInViewMbr, node.mbr())) {
return false;
}
// return Rectmath.findCorner3( node.mbr( ) , testPoint ,
// corner -> {
// float dist = Vecmath.dot3( corner , direction );
// return farthest.value != extentFunction.applyAsFloat(
// farthest.value , dist ) ? true : null;
// } ) != null;
return true;
} ,
leaf -> {
if (shotsInView.contains(leaf.object())) {
for (float[] coord : leaf.object().coordIterable()) {
float dist = Vecmath.dot3(coord, direction);
farthest.value = extentFunction.applyAsFloat(farthest.value, dist);
}
}
return true;
});
return farthest.value;
}
public Set<Shot3d> getHoveredShots() {
return hoveredShot == null ? Collections.<Shot3d> emptySet() : Collections.singleton(hoveredShot);
}
public List<Shot> getOriginalShots() {
return Collections.unmodifiableList(originalShots);
}
public float[] getOrthoBounds(Set<Shot3d> shotsInView, float[] orthoRight, float[] orthoUp,
float[] orthoForward) {
float[] result = new float[6];
float[] shotsInViewMbr = Rectmath.voidRectf(3);
for (Shot3d shot : shotsInView) {
shot.unionMbrInto(shotsInViewMbr);
}
FloatBinaryOperator minFunc = (a, b) -> Float.isNaN(a) || b < a ? b : a;
FloatBinaryOperator maxFunc = (a, b) -> Float.isNaN(a) || b > a ? b : a;
result[0] = getFarthestExtent(shotsInView, shotsInViewMbr, orthoRight, minFunc);
result[1] = getFarthestExtent(shotsInView, shotsInViewMbr, orthoUp, minFunc);
result[2] = getFarthestExtent(shotsInView, shotsInViewMbr, orthoForward, minFunc);
result[3] = getFarthestExtent(shotsInView, shotsInViewMbr, orthoRight, maxFunc);
result[4] = getFarthestExtent(shotsInView, shotsInViewMbr, orthoUp, maxFunc);
result[5] = getFarthestExtent(shotsInView, shotsInViewMbr, orthoForward, maxFunc);
return result;
}
public Set<Shot3d> getSelectedShots() {
return Collections.unmodifiableSet(selectedShots);
}
public Shot3d getShot(int number) {
return shot3ds.get(number);
}
public List<Shot3d> getShots() {
return Collections.unmodifiableList(shot3ds);
}
public Set<Shot3d> getShotsIn(PlanarHull3f hull) {
Set<Shot3d> result = new HashSet<Shot3d>();
getShotsIn(hull, result);
return result;
}
public void getShotsIn(PlanarHull3f hull, Set<Shot3d> result) {
RTraversal.traverse(getTree().getRoot(), node -> {
if (hull.containsBox(node.mbr())) {
RTraversal.traverse(node, node2 -> true, leaf -> result.add(leaf.object()));
return false;
}
return hull.intersectsBox(node.mbr());
} , leaf -> {
for (float[] coord : leaf.object().coordIterable()) {
if (!hull.containsPoint(coord)) {
return true;
}
}
result.add(leaf.object());
return true;
});
}
public RfStarTree<Shot3d> getTree() {
return tree;
}
@Override
public void init(GL2ES2 gl) {
}
public void pickShots(float[] coneOrigin, float[] coneDirection, float coneAngle,
Shot3dPickContext spc, List<PickResult<Shot3d>> pickResults) {
pickShots(tree.getRoot(), coneOrigin, coneDirection, coneAngle, spc, pickResults);
}
public void pickShots(float[] rayOrigin, float[] rayDirection,
Shot3dPickContext spc, List<PickResult<Shot3d>> pickResults) {
pickShots(tree.getRoot(), rayOrigin, rayDirection, spc, pickResults);
}
public void pickShots(PlanarHull3f pickHull, Shot3dPickContext spc, List<PickResult<Shot3d>> pickResults) {
RTraversal.traverse(tree.getRoot(),
node -> pickHull.intersectsBox(node.mbr()),
leaf -> {
leaf.object().pick(pickHull, spc, pickResults);
return true;
});
}
private void pickShots(RNode<float[], Shot3d> node, float[] coneOrigin, float[] coneDirection,
float coneAngle,
Shot3dPickContext spc, List<PickResult<Shot3d>> pickResults) {
if (spc.inConeTester.boxIntersectsCone(node.mbr(), coneOrigin, coneDirection, coneAngle)) {
if (node instanceof RBranch) {
RBranch<float[], Shot3d> branch = (RBranch<float[], Shot3d>) node;
for (int i = 0; i < branch.numChildren(); i++) {
pickShots(branch.childAt(i), coneOrigin, coneDirection, coneAngle, spc, pickResults);
}
} else if (node instanceof RLeaf) {
Shot3d shot3d = ((RLeaf<float[], Shot3d>) node).object();
shot3d.pick(coneOrigin, coneDirection, coneAngle, spc, pickResults);
}
}
}
private void pickShots(RNode<float[], Shot3d> node, float[] rayOrigin, float[] rayDirection,
Shot3dPickContext spc, List<PickResult<Shot3d>> pickResults) {
if (rayIntersects(rayOrigin, rayDirection, node.mbr())) {
if (node instanceof RBranch) {
RBranch<float[], Shot3d> branch = (RBranch<float[], Shot3d>) node;
for (int i = 0; i < branch.numChildren(); i++) {
pickShots(branch.childAt(i), rayOrigin, rayDirection, spc, pickResults);
}
} else if (node instanceof RLeaf) {
Shot3d shot3d = ((RLeaf<float[], Shot3d>) node).object();
shot3d.pick(rayOrigin, rayDirection, spc, pickResults);
}
}
}
public void setAmbientLight(float ambientLight) {
ambient.value(ambientLight);
}
public void setColorParam(final ColorParam colorParam, Subtask subtask) {
if (this.colorParam == colorParam) {
return;
}
this.colorParam = colorParam;
if (subtask != null) {
subtask.setTotal(segment3ds.size());
subtask.setCompleted(0);
subtask.setIndeterminate(false);
}
final Iterator<Segment3d> segmentIterator = segment3ds.iterator();
MultiMap<Segment3dDrawer, Segment3d> newDrawers = LinkedHashSetMultiMap.newInstance();
int completed = 0;
while (segmentIterator.hasNext()) {
Segment3d segment3d = segmentIterator.next();
segment3d.drawers.clear();
if (colorParam == ColorParam.DEPTH) {
segment3d.drawers.add(axialSegment3dDrawer);
} else {
if (colorParam.isStationMetric()) {
segment3d.calcParam0(Survey3dModel.this, colorParam);
}
segment3d.drawers.add(param0Segment3dDrawer);
}
for (Segment3dDrawer drawer : segment3d.drawers) {
newDrawers.put(drawer, segment3d);
}
if (completed++ % 100 == 0) {
if (subtask != null) {
if (subtask.isCanceling()) {
return;
}
subtask.setCompleted(completed);
}
}
}
drawers.set(newDrawers);
if (colorParam.isTraversalMetric()) {
calcDistFromSelected(subtask);
}
}
public void setDepthAxis(float[] axis) {
depthAxis.value(axis);
}
public void setDepthOrigin(float[] origin) {
depthOrigin.value(origin);
}
public void setFarDist(float farDist) {
this.farDist.value(farDist);
}
public void setHiParam(float hiParam) {
this.hiParam.value(hiParam);
}
public void setLoParam(float loParam) {
this.loParam.value(loParam);
}
public void setNearDist(float nearDist) {
this.nearDist.value(nearDist);
}
public void setParamPaint(LinearGradientPaint paint) {
if (paramPaint != paint) {
paramPaint = paint;
paramTextureNeedsUpdate = true;
}
}
public void updateGlow(Shot3d hoveredShot, Float hoverLocation, LinearAxisConversion glowExtentConversion,
Subtask subtask) {
this.hoveredShot = hoveredShot;
this.hoverLocation = hoverLocation;
this.glowExtentConversion = glowExtentConversion;
Set<Segment3d> newSegmentsWithGlow = new HashSet<Segment3d>();
if (hoveredShot != null) {
Shot origShot = originalShots.get(hoveredShot.number);
final Function<Station, Stream<Shot>> connected = station -> station.shots.stream();
Graphs.traverse2(Stream.<Station> builder().add(origShot.from).add(origShot.to).build(),
station -> (station == origShot.from ? hoverLocation : 1 - hoverLocation) * origShot.dist,
(station, priority) -> {
float glow = (float) glowExtentConversion.convert(priority);
connected.apply(station).forEach(
connectedShot -> {
Shot3d shot3d = shot3ds.get(connectedShot.number);
if (newSegmentsWithGlow.add(shot3d.segment3d)) {
segmentsWithGlow.add(shot3d.segment3d);
shot3d.segment3d.clearGlow();
}
if (station == connectedShot.from) {
shot3d.setFromGlowA(glow);
shot3d.setFromGlowB(glow);
} else {
shot3d.setToGlowA(glow);
shot3d.setToGlowB(glow);
}
shot3d.segment3d.stationAttrsNeedRebuffering.set(true);
});
return glow > 0;
} ,
connected,
(Shot shot) -> shot.dist,
(station, shot) -> station == shot.from ? shot.to : shot.from,
() -> subtask != null && !subtask.isCanceling());
hoveredShot.setFromGlowA(2 - hoveredShot.getFromGlowB());
hoveredShot.setToGlowA(2 - hoveredShot.getToGlowB());
}
Iterator<Segment3d> segIter = segmentsWithGlow.iterator();
for (Segment3d segment3d : IterableUtils.iterable(segIter)) {
if (subtask != null && subtask.isCanceling()) {
return;
}
if (!newSegmentsWithGlow.contains(segment3d)) {
segIter.remove();
segment3d.clearGlow();
segment3d.stationAttrsNeedRebuffering.set(true);
}
}
}
private void updateHighlights(Collection<Shot3d> affectedShots) {
// find the segments that are affected by the affected shots
// (not just the segments containing those shots but segments containing
// shots within highlight distance from an affected shot)
Set<Segment3d> affectedSegments = new HashSet<Segment3d>();
for (Shot3d shot3d : affectedShots) {
affectedSegments.add(shot3d.segment3d);
}
for (Segment3d segment3d : affectedSegments) {
segment3d.clearHighlights();
}
for (Shot3d shot3d : selectedShots) {
if (affectedSegments.contains(shot3d.segment3d)) {
applySelectionHighlights(shot3d);
}
}
for (Segment3d segment3d : affectedSegments) {
segment3d.stationAttrsNeedRebuffering.set(true);
}
}
private void updateParamTexture(GL2ES2 gl) {
if (paramTextureImage == null) {
paramTextureImage = DirectDataBufferInt.createBufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB,
new Point(), new Hashtable<Object, Object>());
}
Graphics2D g2 = paramTextureImage.createGraphics();
g2.clearRect(0, 0, paramTextureImage.getWidth(), paramTextureImage.getHeight());
if (paramPaint != null) {
g2.setPaint(new ParamGradientMapPaint(
new float[] { 0, 0 },
new float[] { 0, paramTextureImage.getHeight() },
new float[] { paramTextureImage.getWidth(), 0 },
0, 1,
paramPaint.getFractions(),
paramPaint.getColors()));
g2.fillRect(0, 0, paramTextureImage.getWidth(), paramTextureImage.getHeight());
}
g2.dispose();
IntBuffer paramTextureBuffer = ((DirectDataBufferInt) paramTextureImage.getRaster().getDataBuffer())
.getData();
if (paramTexture == 0) {
int textures[] = new int[1];
gl.glGenTextures(1, textures, 0);
paramTexture = textures[0];
}
gl.glBindTexture(GL_TEXTURE_2D, paramTexture);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, paramTextureImage.getWidth(), paramTextureImage.getHeight(),
0, GL_BGRA, GL_UNSIGNED_BYTE, paramTextureBuffer);
gl.glBindTexture(GL_TEXTURE_2D, 0);
}
}