/*******************************************************************************
* 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;
import static org.andork.spatial.Rectmath.ppunion;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.andork.func.StreamUtils;
import org.andork.jogl.JoglScene;
import org.andork.jogl.JoglScreenPolygon;
import org.andork.jogl.JoglViewState;
import org.andork.math3d.PickXform;
import org.andork.math3d.PlanarHull3f;
import org.andork.math3d.Vecmath;
import org.andork.spatial.EdgeTrees;
import org.andork.spatial.RBranch;
import org.andork.spatial.RTraversal;
import org.andork.spatial.RfStarTree;
import org.andork.swing.async.TaskService;
import org.andork.util.Reparam;
import org.breakout.model.Survey3dModel;
import org.breakout.model.Survey3dModel.Shot3d;
import com.jogamp.opengl.GLAutoDrawable;
public class WindowSelectionMouseHandler extends MouseAdapter {
public static interface Context {
public void endSelection();
public GLAutoDrawable getDrawable();
public TaskService getRebuildTaskService();
public JoglScene getScene();
public Survey3dModel getSurvey3dModel();
public JoglViewState getViewState();
public void selectShots(Set<Shot3d> newSelected, boolean add, boolean toggle);
}
private final Context context;
private final List<float[]> points = new ArrayList<float[]>();
JoglScreenPolygon selectionPolygon = new JoglScreenPolygon();
private PlanarHull3f hull = new PlanarHull3f();
final float[] pointOnScreen = new float[3];
public WindowSelectionMouseHandler(Context context) {
this.context = context;
}
public void end() {
points.clear();
context.getDrawable().invoke(true, drawable -> {
context.getScene().remove(selectionPolygon);
return false;
});
context.endSelection();
}
@Override
public void mouseDragged(MouseEvent e) {
mouseMoved(e);
}
@Override
public void mouseMoved(MouseEvent e) {
if (!points.isEmpty()) {
float[] last = points.get(points.size() - 1);
last[0] = e.getX();
last[1] = context.getDrawable().getSurfaceHeight() - e.getY();
context.getDrawable().invoke(true, drawable -> {
selectionPolygon.setPoints(points);
return false;
});
}
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
selectLoopedShots((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0,
(e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0);
end();
return;
}
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
points.add(new float[] { e.getX(), context.getDrawable().getSurfaceHeight() - e.getY() });
context.getDrawable().invoke(false, drawable -> {
selectionPolygon.setPoints(points);
return false;
});
}
@Override
public void mouseReleased(MouseEvent e) {
}
protected void selectLoopedShots(boolean add, boolean toggle) {
final Survey3dModel model3d = context.getSurvey3dModel();
final List<float[]> points = new ArrayList<>(this.points);
if (model3d == null) {
return;
}
context.getRebuildTaskService().submit(task -> {
task.setStatus("Finding lassoed shots...");
task.setIndeterminate(true);
RBranch<float[], Shot3d> root = model3d.getTree().getRoot();
RfStarTree<float[]> edgeTree = new RfStarTree<>(2, 4, 1, 2);
StreamUtils.forEachPairLooped(points.stream(), (p1, p2) -> {
edgeTree.insert(edgeTree.createLeaf(ppunion(p1, p2), new float[] { p1[0], p1[1], p2[0], p2[1] }));
});
float[] mbr = edgeTree.getRoot().mbr();
PickXform pickXform = context.getViewState().pickXform();
GLAutoDrawable canvas = context.getDrawable();
int cw = canvas.getSurfaceWidth();
int ch = canvas.getSurfaceHeight();
pickXform.exportViewVolume(hull, mbr, cw, ch);
// BasicJOGLObject bounds = new BasicJOGLObject( );
// BufferHelper bufferHelper = new BufferHelper( );
// for( float[ ] vertex : hull.vertices )
// {
// bufferHelper.put( vertex );
// }
// bounds.addVertexBuffer( bufferHelper.toByteBuffer( ) );
// BufferHelper indexBufferHelper = new BufferHelper( );
// indexBufferHelper.put( 0 , 1 , 0 , 2 , 1 , 3 , 2 , 3 , 4 , 5 , 4
// , 6 , 5 , 7 , 6 , 7 , 0 , 4 , 1 , 5 , 2 , 6 , 3 , 7 );
// bounds.indexBuffer( indexBufferHelper.toByteBuffer( ) );
// bounds.vertexCount( 8 );
// bounds.indexCount( 24 );
// bounds.indexType( GL.GL_UNSIGNED_INT );
// bounds.drawMode( GL.GL_LINES );
// bounds.vertexShaderCode( new BasicVertexShader( ).toString( )
// ).add( bounds.new Attribute3fv( ).name( "a_pos" ) );
// bounds.fragmentShaderCode( new FlatFragmentShader( ).color( 0 , 1
// , 0 , 1 ).toString( ) );
//
// BasicJOGLObject normals = new BasicJOGLObject( );
// bufferHelper = new BufferHelper( );
// for( int side = 0 ; side < hull.origins.length ; side++ )
// {
// bufferHelper.put( hull.origins[ side ] );
// bufferHelper.put( hull.origins[ side ][ 0 ] + hull.normals[ side
// ][ 0 ] * 50 );
// bufferHelper.put( hull.origins[ side ][ 1 ] + hull.normals[ side
// ][ 1 ] * 50 );
// bufferHelper.put( hull.origins[ side ][ 2 ] + hull.normals[ side
// ][ 2 ] * 50 );
// }
// normals.addVertexBuffer( bufferHelper.toByteBuffer( ) );
// normals.vertexCount( 12 );
// normals.drawMode( GL.GL_LINES );
// normals.vertexShaderCode( new BasicVertexShader( ).toString( )
// ).add( normals.new Attribute3fv( ).name( "a_pos" ) );
// normals.fragmentShaderCode( new FlatFragmentShader( ).color( 1 ,
// 1 , 0 , 1 ).toString( ) );
//
// context.getCanvas( ).invoke( false , drawable -> {
// bounds.init( ( GL2ES2 ) drawable.getGL( ) );
// context.getScene( ).add( bounds );
// normals.init( ( GL2ES2 ) drawable.getGL( ) );
// context.getScene( ).add( normals );
// return false;
// } );
JoglViewState viewState = context.getViewState();
float[] pv = Vecmath.newMat4f();
Vecmath.mmul(viewState.projXform(), viewState.viewXform(), pv);
Set<Shot3d> newSelected = new HashSet<>();
RTraversal.traverse(root, node -> hull.intersectsBox(node.mbr()), leaf -> {
for (float[] point : leaf.object().coordIterable()) {
Vecmath.mpmul(pv, point, pointOnScreen);
pointOnScreen[0] = Reparam.linear(pointOnScreen[0], -1, 1, 0, cw);
pointOnScreen[1] = Reparam.linear(pointOnScreen[1], -1, 1, 0, ch);
if (!EdgeTrees.isInPolygon(pointOnScreen, edgeTree.getRoot())) {
return true;
}
}
newSelected.add(leaf.object());
return true;
});
context.selectShots(newSelected, add, toggle);
context.getDrawable().display();
});
}
public void start(MouseEvent e) {
points.add(new float[] { e.getX(), context.getDrawable().getSurfaceHeight() - e.getY() });
points.add(new float[] { e.getX(), context.getDrawable().getSurfaceHeight() - e.getY() });
context.getDrawable().invoke(false, drawable -> {
selectionPolygon.setPoints(points);
context.getScene().add(selectionPolygon);
return false;
});
}
}