/* * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package boofcv.gui.d3; import georegression.geometry.ConvertRotation3D_F64; import georegression.geometry.GeometryMath_F64; import georegression.struct.EulerType; import georegression.struct.point.Point2D_F64; import georegression.struct.point.Point3D_F64; import georegression.struct.point.Vector3D_F64; import georegression.struct.se.Se3_F64; import georegression.transform.se.SePointOps_F64; import org.ejml.data.DenseMatrix64F; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.List; /** * Draws a sequence polygons in 3D * * @author Peter Abeles */ public class Polygon3DSequenceViewer extends JPanel implements KeyListener, MouseListener, MouseMotionListener { // the shapes it's drawing List<Poly> polygons = new ArrayList<>(); // transform from world frame to camera frame Se3_F64 worldToCamera = new Se3_F64(); // intrinsic camera calibration DenseMatrix64F K; // how far it moves in the world frame for each key press double stepSize; // previous cursor location int prevX; int prevY; public Polygon3DSequenceViewer( ) { addKeyListener(this); addMouseListener(this); addMouseMotionListener(this); } public DenseMatrix64F getK() { return K; } public void setK(DenseMatrix64F k) { K = k; } public double getStepSize() { return stepSize; } public void setStepSize(double stepSize) { this.stepSize = stepSize; } public void init() { SwingUtilities.invokeLater( new Runnable() { @Override public void run() { polygons.clear(); } }); } /** * Adds a polygon to the viewer. GUI Thread safe. * * @param polygon shape being added */ public void add( Color color , Point3D_F64... polygon ) { final Poly p = new Poly(polygon.length,color); for( int i = 0; i < polygon.length; i++ ) p.pts[i] = polygon[i].copy(); SwingUtilities.invokeLater( new Runnable() { @Override public void run() { polygons.add( p ); } }); } /** * Adds a polygon to the viewer. GUI Thread safe. * * @param polygon shape being added */ public void add( Point3D_F64... polygon ) { final Poly p = new Poly(polygon.length,Color.BLACK); for( int i = 0; i < polygon.length; i++ ) p.pts[i] = polygon[i].copy(); SwingUtilities.invokeLater( new Runnable() { @Override public void run() { polygons.add( p ); } }); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; Point3D_F64 p1 = new Point3D_F64(); Point3D_F64 p2 = new Point3D_F64(); Point2D_F64 x1 = new Point2D_F64(); Point2D_F64 x2 = new Point2D_F64(); for( Poly poly : polygons ) { SePointOps_F64.transform(worldToCamera, poly.pts[0], p1); GeometryMath_F64.mult(K,p1,x1); // don't render what's behind the camera if( p1.z < 0 ) continue; g2.setColor(poly.color); boolean skip = false; for( int i = 1; i < poly.pts.length; i++ ) { SePointOps_F64.transform(worldToCamera,poly.pts[i],p2); GeometryMath_F64.mult(K,p2,x2); if( p2.z < 0 ) { skip = true; break; } g2.drawLine((int)x1.x,(int)x1.y,(int)x2.x,(int)x2.y); Point3D_F64 tempP = p1; Point2D_F64 tempX = x1; p1=p2;p2=tempP; x1=x2;x2=tempX; } if( !skip ) { SePointOps_F64.transform(worldToCamera, poly.pts[0], p2); GeometryMath_F64.mult(K,p2,x2); g2.drawLine((int)x1.x,(int)x1.y,(int)x2.x,(int)x2.y); } } } @Override public void keyTyped(KeyEvent e) { Vector3D_F64 T = worldToCamera.getT(); if( e.getKeyChar() == 'w' ) { T.z -= stepSize; } else if( e.getKeyChar() == 's' ) { T.z += stepSize; } else if( e.getKeyChar() == 'a' ) { T.x += stepSize; } else if( e.getKeyChar() == 'd' ) { T.x -= stepSize; } else if( e.getKeyChar() == 'q' ) { T.y -= stepSize; } else if( e.getKeyChar() == 'e' ) { T.y += stepSize; } else if( e.getKeyChar() == 'h' ) { worldToCamera.reset(); } repaint(); } @Override public void keyPressed(KeyEvent e) {} @Override public void keyReleased(KeyEvent e) {} @Override public void mouseClicked(MouseEvent e) { grabFocus(); } @Override public void mousePressed(MouseEvent e) { prevX = e.getX(); prevY = e.getY(); } @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mouseDragged(MouseEvent e) { double rotX = 0; double rotY = 0; double rotZ = 0; rotY += (e.getX() - prevX)*0.01; rotX += (prevY - e.getY())*0.01; Se3_F64 rotTran = new Se3_F64(); ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ,rotX,rotY,rotZ,rotTran.getR()); Se3_F64 temp = worldToCamera.concat(rotTran,null); worldToCamera.set(temp); prevX = e.getX(); prevY = e.getY(); repaint(); } @Override public void mouseMoved(MouseEvent e) {} private static class Poly { Point3D_F64[] pts; Color color; public Poly() { } public Poly(int length, Color color) { this.pts = new Point3D_F64[length]; this.color = color; } } }