From 3ef62df14340e2127ce21fc7ccab1f69f832abc0 Mon Sep 17 00:00:00 2001 From: Reino Ruusu Date: Thu, 5 Mar 2020 15:58:25 +0200 Subject: [PATCH] Tools for fitting contents to view in Plant3D editor gitlab #100 Also fixes clipping in parallel projection mode by keeping scale and camera distance at values that match the camera view angle. Change-Id: I82ad77bdc339fa0ad7814c31fcac97512c440972 --- .../handlers/ParallelPerspectiveHandler.java | 33 +------ .../vtk/swt/vtkCameraAndSelectorAction.java | 87 ++++++++++++++++--- org.simantics.plant3d/plugin.xml | 2 +- .../plant3d/editor/Plant3DEditor.java | 79 ++++++++++++----- 4 files changed, 136 insertions(+), 65 deletions(-) diff --git a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/handlers/ParallelPerspectiveHandler.java b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/handlers/ParallelPerspectiveHandler.java index 43ec97a0..59f94c0b 100644 --- a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/handlers/ParallelPerspectiveHandler.java +++ b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/handlers/ParallelPerspectiveHandler.java @@ -11,15 +11,12 @@ *******************************************************************************/ package org.simantics.g3d.vtk.handlers; -import javax.vecmath.Vector3d; - import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.Command; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.handlers.HandlerUtil; -import org.simantics.g3d.math.MathTools; import org.simantics.g3d.vtk.common.VtkView; import org.simantics.utils.threads.AWTThread; import org.simantics.utils.threads.ThreadUtils; @@ -46,37 +43,15 @@ public class ParallelPerspectiveHandler extends AbstractHandler { vtkRenderer ren = panel.getRenderer(); vtkCamera cam = ren.GetActiveCamera(); if (activate){ - - Vector3d focal = new Vector3d(panel.getRenderer().GetActiveCamera().GetFocalPoint()); - Vector3d pos = new Vector3d(panel.getRenderer().GetActiveCamera().GetPosition()); - double dist = MathTools.distance(pos, focal); - cam.SetParallelScale(dist/4.0); - // camera must be moved backwards, or graphics get clipped when parallel view is zoomed out - // TODO : is there a better way to do this? - Vector3d dir = new Vector3d(pos); - dir.sub(focal); - dir.normalize(); - dir.scale(100); - dir.add(focal); - cam.SetPosition(dir.x, dir.y, dir.z); - + double distance = cam.GetDistance(); + double angle = cam.GetViewAngle(); + double scale = Math.tan(Math.toRadians(angle / 2)) * distance; + cam.SetParallelScale(scale); cam.SetParallelProjection(1); ren.ResetCameraClippingRange(); } else { - - double scale = cam.GetParallelScale(); cam.SetParallelProjection(0); - - Vector3d focal = new Vector3d(panel.getRenderer().GetActiveCamera().GetFocalPoint()); - Vector3d pos = new Vector3d(panel.getRenderer().GetActiveCamera().GetPosition()); - Vector3d dir = new Vector3d(pos); - dir.sub(focal); - dir.normalize(); - dir.scale(scale*4.0); - dir.add(focal); - cam.SetPosition(dir.x, dir.y, dir.z); ren.ResetCameraClippingRange(); - } panel.refresh(); diff --git a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/vtkCameraAndSelectorAction.java b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/vtkCameraAndSelectorAction.java index 7a6f0d89..a5ea6149 100644 --- a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/vtkCameraAndSelectorAction.java +++ b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/vtkCameraAndSelectorAction.java @@ -18,6 +18,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.vecmath.Vector3d; + import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; @@ -28,6 +30,7 @@ import org.simantics.g3d.tools.AdaptationUtils; import vtk.vtkCamera; import vtk.vtkProp; +import vtk.vtkProp3D; import vtk.vtkRenderWindow; import vtk.vtkRenderer; @@ -167,16 +170,13 @@ public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelecti // zoom if (this.InteractionMode == 3) { double zoomFactor; - // double clippingRange[]; - zoomFactor = Math.pow(1.02, (y - lastY)); + cam.Dolly(zoomFactor); + if (cam.GetParallelProjection() == 1) { - cam.SetParallelScale(cam.GetParallelScale() / zoomFactor); - resetCameraClippingRange(); - } else { - cam.Dolly(zoomFactor); - resetCameraClippingRange(); + updateParallelScale(); } + resetCameraClippingRange(); } lastX = x; lastY = y; @@ -208,16 +208,21 @@ public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelecti double zoomFactor; // double clippingRange[]; zoomFactor = Math.pow(1.02, (e.getWheelRotation())); - if (cam.GetParallelProjection() == 1) { - cam.SetParallelScale(cam.GetParallelScale() / zoomFactor); - } else { - cam.Dolly(zoomFactor); - resetCameraClippingRange(); - } + cam.Dolly(zoomFactor); + updateParallelScale(); + resetCameraClippingRange(); panel.refresh(); return true; } + private void updateParallelScale() { + // Make height of parallel projection match the distance + double distance = cam.GetDistance(); + double angle = cam.GetViewAngle(); + double scale = Math.tan(Math.toRadians(angle / 2)) * distance; + cam.SetParallelScale(scale); + } + protected List selectActors = new ArrayList(); protected List hoverActor = new ArrayList(); @@ -415,5 +420,61 @@ public class vtkCameraAndSelectorAction extends vtkSwtAction implements ISelecti // panel.UpdateLight(); UnLock(); } + + public void fitToView(Collection props) { + if (props.isEmpty()) + return; + + double[] bounds = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE }, b = new double[6]; + for (vtkProp3D prop : props) { + prop.GetBounds(b); + for (int i = 0; i < 6; i+=2) + bounds[i] = Math.min(bounds[i], b[i]); + for (int i = 1; i < 6; i+=2) + bounds[i] = Math.max(bounds[i], b[i]); + } + + fitToView(bounds); + } + + public void fitToView(double[] bounds) { + Vector3d center = new Vector3d((bounds[0] + bounds[1])/2, (bounds[2] + bounds[3])/2, (bounds[4] + bounds[5])/2); + Vector3d viewDir = new Vector3d(cam.GetDirectionOfProjection()); + Vector3d upDir = new Vector3d(cam.GetViewUp()); + viewDir.normalize(); + upDir.normalize(); + Vector3d sideDir = new Vector3d(); + sideDir.cross(viewDir, upDir); + sideDir.normalize(); + + double width = getBoxWidth(bounds, sideDir); + double height = getBoxWidth(bounds, upDir); + double depth = getBoxWidth(bounds, viewDir); + + int[] size = rw.GetActualSize(); + + double distance1 = height / 2 / Math.tan(Math.toRadians(cam.GetViewAngle()) / 2); + double distance2 = distance1 * (width / size[0] / (height / size[1])); + + double distance = Math.max(distance1, distance2) + depth / 2; + + viewDir.scale(-distance); + viewDir.add(center); + + cam.SetPosition(viewDir.x, viewDir.y, viewDir.z); + focus(center.x, center.y, center.z); + + if (cam.GetParallelProjection() == 1) { + cam.SetParallelScale(Math.max(height, width * size[1] / size[0]) / 2); + } + } + + private static double getBoxWidth(double[] bounds, Vector3d dir) { + double dx = bounds[1] - bounds[0]; + double dy = bounds[3] - bounds[2]; + double dz = bounds[5] - bounds[4]; + + return Math.abs(dx * dir.x) + Math.abs(dy * dir.y) + Math.abs(dz * dir.z); + } } diff --git a/org.simantics.plant3d/plugin.xml b/org.simantics.plant3d/plugin.xml index e935e5e9..7d6b9042 100644 --- a/org.simantics.plant3d/plugin.xml +++ b/org.simantics.plant3d/plugin.xml @@ -135,7 +135,7 @@ type="push"> + value="-0.57735026918962576450914878050196,0.57735026918962576450914878050196,-0.57735026918962576450914878050196"> props = new ArrayList(); + collectProps(rootNode, props); + fitToWindow(props); } }); - } }); @@ -342,7 +347,7 @@ public class Plant3DEditor extends ResourceEditorPart { panel.getComponent().setFocus(); } - private void createScene() { + protected void createScene() { vtkRenderer ren1 = panel.getRenderer(); boolean multiPass = false; @@ -407,7 +412,6 @@ public class Plant3DEditor extends ResourceEditorPart { AxesDisplay axesDisplay = new AxesDisplay(panel); axesDisplay.show(); - } protected Menu contextMenu; @@ -426,19 +430,11 @@ public class Plant3DEditor extends ResourceEditorPart { protected void createContextMenu(IMenuManager m) { List selected = selectionProvider.getSelectedNodes(); - List> selectedItems = selectionProvider.getSelectedItems(); - if (selectedItems.size() > 0) { - List props = new ArrayList<>(); - for (VTKSelectionItem i : selectedItems) { - vtkProp p = (vtkProp)i.getAdapter(vtkProp.class); - if (p != null) - props.add(p); - } - if (props.size() > 0) { - focusAction.setProps(props); - m.add(focusAction); - } - } + + createFocusMenu(m, selected); + + m.add(new Separator()); + try { if (selected.size() == 0) { m.add(new AddEquipmentAction(rootNode, getLibraryUri())); @@ -518,6 +514,31 @@ public class Plant3DEditor extends ResourceEditorPart { ExceptionUtils.logAndShowError(e); } } + + protected void createFocusMenu(IMenuManager m, List selected) { + m.add(new Action("Fit to Window") { + @Override + public void run() { + List props = new ArrayList<>(); + final Collection collection = !selected.isEmpty() ? selected : getRootNode().getChild(); + for (INode n : collection) + collectProps(n, props); + + fitToWindow(props); + getPanel().refresh(); + } + }); + + if (!selected.isEmpty()) { + List actors = new ArrayList<>(); + for (INode n : selected) + collectProps(n, actors); + if (actors.size() > 0) { + focusAction.setProps(new ArrayList<>(actors)); + m.add(focusAction); + } + } + } private IContentOutlinePage createOutline() { if (rootNode == null || selectionProvider == null) @@ -585,4 +606,18 @@ public class Plant3DEditor extends ResourceEditorPart { public P3DNodeMap getNodeMap() { return nodeMap; } + + public void fitToWindow(Collection props) { + cameraAction.fitToView(props); + } + + protected static void collectProps(INode node, List props) { + if (node instanceof IP3DVisualNode) + props.addAll(((IP3DVisualNode) node).getActors()); + + if (node instanceof ParentNode) + for (INode n : ((ParentNode) node).getNodes()) { + collectProps(n, props); + } + } } -- 2.45.2