Developer Documentation
PlanePlugin.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2020, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 #include "PlanePlugin.hh"
43 
44 #include <iostream>
45 
48 
49 #include <ACG/Scenegraph/LineNode.hh>
50 
51 const char *PLANE = "CreatePlaneNode";
52 const char *PLANE_RESIZE = "PlaneResize";
53 
54 //------------------------------------------------------------------------------
55 
60  createPlane_(nullptr),
61  planeNodeAction_(nullptr),
62  dragging_(false),
63  pickedCorner_(0)
64 {}
65 
66 /*******************************************************************************
67  BaseInterface implementation
68  *******************************************************************************/
69 
74  // register keys
75  emit registerKey(Qt::Key_Escape, Qt::NoModifier, "Cancel Plane", true);
76 }
77 
78 //------------------------------------------------------------------------------
79 
85 
86  emit addPickMode(PLANE);
87 
88  emit addPickMode(PLANE_RESIZE);
89 
90  createPlane_ =
92  connect(createPlane_, SIGNAL(signalTriggerCut()), this,
93  SLOT(slotCreatePlaneTriggered()));
94  connect(createPlane_, SIGNAL(updateViewProxy()), this, SIGNAL(updateView()));
95 
96  // Add a Toolbar
97  QToolBar *toolbar = new QToolBar("Plane Toolbar");
98  // createPlane_Node
99  planeNodeAction_ = new QAction(tr("&Create Plane"), this);
100  planeNodeAction_->setCheckable(true);
101  planeNodeAction_->setStatusTip(tr("Create a Plane Node"));
102  planeNodeAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr() +
103  OpenFlipper::Options::dirSeparator() +
104  "plane_plane.png"));
105  connect(planeNodeAction_, SIGNAL(triggered()), this,
106  SLOT(slotCreatePlaneNode()));
107  toolbar->addAction(planeNodeAction_);
108 
109  emit addToolbar(toolbar);
110 }
111 
112 /*******************************************************************************
113  PickingInterface implementation
114  *******************************************************************************/
115 
120 void PlanePlugin::slotPickModeChanged(const std::string &_mode) {
121  planeNodeAction_->setChecked(_mode == PLANE);
122 }
123 
124 /*******************************************************************************
125 KeyInterface implementation
126 *******************************************************************************/
127 
132 void PlanePlugin::slotKeyReleaseEvent(QKeyEvent *_event) {
133  if (PluginFunctions::pickMode() == PLANE)
134  createPlane_->slotKeyReleaseEvent(_event);
135 }
136 
137 /*******************************************************************************
138  MouseInterface implementation
139  *******************************************************************************/
140 
145 void PlanePlugin::slotMouseEvent(QMouseEvent *_event) {
146 
147  // auto determinant = [](ACG::Vec3f const &c1, ACG::Vec3f const &c2,
148  // ACG::Vec3f const &c3) -> float {
149  // return (c1[0] * c2[1] * c3[2]) + (c2[0] * c3[1] * c1[2]) +
150  // (c3[0] * c1[1] * c2[2]) - (c3[0] * c2[1] * c1[2]) -
151  // (c2[0] * c1[1] * c3[2]) - (c1[0] * c3[1] * c2[2]);
152  // };
154  // auto rayPlaneIntersection =
155  // [&determinant](ACG::Vec3f const &s1, ACG::Vec3f const &s2,
156  // ACG::Vec3f const &p, ACG::Vec3f const &q,
157  // ACG::Vec3f const &r, float &alpha, float &beta) {
158  // /// implementation of cramers rule to compute closed form
159  // solution for
160  // /// plane ray intersections (closed form solution) problematic
161  // if ray
162  // /// points away or converges to parallel to plane
163  // /// s1 and s2 are two points that define the ray (origin &
164  // origin+dir)
165  // /// p,q,r span a triangle that defines the plane
166  // /// alpha & beta are return params for the barycentric
167  // coordinates
168  // /// alpha belongs to p, beta belongs to q
169  // const auto denom = determinant(s1, r - p, r - q);
170  // alpha = determinant(s1, r - s2, r - q) / denom;
171  // beta = determinant(s1, r - p, r - s2) / denom;
172  // };
173 
175  auto rayPlaneIntersection = [](ACG::Vec3d const &origin,
176  ACG::Vec3d const &dir, ACG::Vec3d const &p,
177  ACG::Vec3d const &q, ACG::Vec3d const &r,
178  double &alpha, double &beta) -> bool {
179  {
180  ACG::Vec3d edge1, edge2, tvec, pvec, qvec;
181  double det, inv_det;
182 
183  edge1 = q - p;
184  edge2 = r - p;
185  pvec = dir % edge2;
186  det = edge1 | pvec;
187 
188  constexpr double EPSILON = std::numeric_limits<double>::epsilon() * 1e2;
189  if (det > -EPSILON && det < EPSILON) {
190  std::cerr << "det within eps!" << std::endl;
191  return false;
192  }
193  inv_det = 1.f / det;
194  tvec = origin - p;
195  alpha = (tvec | pvec) * inv_det;
196  qvec = tvec % edge1;
197  beta = (dir | qvec) * inv_det;
198 
202  if (alpha < 0.0 || alpha > 1.0)
203  return false;
204  if (beta < 0.0 || alpha + beta > 1.0)
205  return false;
206 
207  return true;
208  }
209  };
210 
211  if (PluginFunctions::pickMode() == PLANE)
212  createPlane_->slotMouseEvent(_event);
213  if (PluginFunctions::pickMode() == PLANE_RESIZE) {
214  switch (_event->type()) {
215  case QEvent::MouseButtonPress: {
216  ACG::Vec3d sourcePoint3D;
217  size_t node_idx, target_idx;
219  _event->pos(), node_idx, target_idx,
220  &sourcePoint3D)) {
221  BaseObjectData *obj = nullptr;
222  if (PluginFunctions::getPickedObject(node_idx, obj)) {
223  // is picked object Plane?
225  lastObjId_ = obj->id();
226 
227  if (curPlane_) {
228  origPlane_ = curPlane_->plane();
229  QPoint position = _event->pos();
230 
232  ACG::Vec3d origin;
234  position.x(),
236  position.y(),
237  origin, viewDirection_);
238 
240  {
241  const auto center = curPlane_->plane().position;
242  const auto xdir = curPlane_->plane().xDirection / 2.;
243  const auto ydir = curPlane_->plane().yDirection / 2.;
244  const auto p0 = center + xdir + ydir;
245  const auto p1 = center + xdir - ydir;
246  const auto p2 = center - xdir - ydir;
247  double u = -1, v = -1, w = -1;
248  rayPlaneIntersection(origin, viewDirection_, p0, p1, p2, u, v);
249  w = 1 - (u + v);
250  wMouseDownPosition_ = (w * p0) + (u * p1) + (v * p2);
251  }
252 
253  pickedCorner_ = target_idx;
254 
255  // We hit a corner so start dragging
256  dragging_ = true;
257  }
258  }
259  }
260 
261  break;
262  }
263 
264  case QEvent::MouseMove: {
265 
266  if (dragging_ && curPlane_) {
267 
268  QPoint position = _event->pos();
269 
271  auto plane = origPlane_;
272 
275  {
277  const auto viewCoord = ACG::Vec3d(
278  position.x(),
280  position.y(),
281  .5);
282 
283  ACG::Vec3d origin;
284  ACG::Vec3d dir;
286  viewCoord[0], viewCoord[1], origin, dir);
287  double u = -3.1415926539, v = -3.1415926539;
288  const auto center = plane.position;
289  const auto xdir = plane.xDirection / 2.;
290  const auto ydir = plane.yDirection / 2.;
291  const auto p0 = ACG::Vec3d{center + xdir + ydir};
292  const auto p1 = ACG::Vec3d{center + xdir - ydir};
293  const auto p2 = ACG::Vec3d{center - xdir - ydir};
297 
299  rayPlaneIntersection(origin, dir, p0, p1, p2, u, v);
300  const auto w = 1. - (u + v);
301  wCurrMousePos_ = (w * p0) + (u * p1) + (v * p2);
302  }
303 
305  const auto wMouseDownPosition = wMouseDownPosition_ - plane.position;
307  const auto wCurrMousePos = wCurrMousePos_ - plane.position;
308 
310  const auto worldSpaceUpdate = (wCurrMousePos - wMouseDownPosition) / 2.;
311 
312  if (pickedCorner_ != 0) {
314 
315  const auto distClickOrigin = wMouseDownPosition;
316 
318  auto scale = (worldSpaceUpdate + distClickOrigin) / distClickOrigin;
319  scale[2] = 1;
320 
321  const auto xscale = scale[0] * plane.xDirection.length();
322  const auto yscale = scale[1] * plane.yDirection.length();
323  plane.setSize(xscale, yscale);
324  }
325 
328  plane.position += worldSpaceUpdate;
329 
330  curPlane_->plane() = plane;
331 
333  if (lastObjId_ > 0)
334  emit updatedObject(lastObjId_, UPDATE_GEOMETRY);
335  else
336  emit log(LOGERR, tr("COULD NOT UPDATE OBJECT"));
337  }
338 
339  break;
340  }
341  case QEvent::MouseButtonRelease: {
342 
343  // Stop dragging operation reset all properties
344  dragging_ = false;
345  pickedCorner_ = 0;
347  curPlane_ = nullptr;
348  lastObjId_ = -1;
349 
350  break;
351  }
352  default:
353  break;
354  }
355  }
356 }
357 
358 /*******************************************************************************
359  PlanePlugin Implementation
360  *******************************************************************************/
361 
362 //------------------------------------------------------------------------------
363 
368  PluginFunctions::actionMode(Viewer::PickingMode);
370 }
371 
372 //------------------------------------------------------------------------------
373 
375  // get object
376  BaseObjectData *obj;
377 
378  if (PluginFunctions::getPickedObject(createPlane_->getNode(), obj)) {
379  if (obj == nullptr) {
380  emit log(LOGERR, "Unable to get object");
381  return;
382  }
383 
384  // generate a plane object
385  int planeId = -1;
386 
387  // add new plane
388  emit addEmptyObject(DATA_PLANE, planeId);
389 
390  // get current planeobject
391  BaseObjectData *planeObj;
392  PluginFunctions::getObject(planeId, planeObj);
393 
394  // get plane object
395  PlaneObject *currentPlane = PluginFunctions::planeObject(planeObj);
396 
397  ACG::Vec3d point = createPlane_->getSourcePoint();
398  ACG::Vec3d normal = createPlane_->getNormal();
399 
400  ACG::Vec3d center;
401  double radius;
402 
403  if (getIntersectionParams(*obj, center, radius)) {
404  radius = 1.5 * radius;
405  radius = std::min(radius, PluginFunctions::sceneRadius());
406 
407  currentPlane->plane().setPlane(center, normal);
408  currentPlane->plane().setSize(2.0 * radius, 2.0 * radius);
409 
410  } else {
411  std::cerr << "unable to get intersection params" << std::endl;
412  currentPlane->plane().setPlane(point, normal);
413  currentPlane->plane().setSize(PluginFunctions::sceneRadius(),
415  }
416 
417  currentPlane->planeNode()->show();
418 
419  emit updatedObject(planeId, UPDATE_ALL);
420  }
421 }
422 
423 //------------------------------------------------------------------------------
424 
425 bool PlanePlugin::getIntersectionParams(BaseObjectData &_obj,
426  ACG::Vec3d &_center, double &_radius) {
427  ACG::Vec3d point = createPlane_->getSourcePoint();
428  ACG::Vec3d normal = createPlane_->getNormal();
429 
430  // get the intersection points
431  std::vector<ACG::Vec3d> linePoints;
432  bool closed = false;
433  if (_obj.dataType(DATA_TRIANGLE_MESH)) {
434  TriMesh *mesh = PluginFunctions::triMesh(&_obj);
435 
436  if (mesh == nullptr)
437  return false;
438 
439  // get a edge of the mesh that is cut by the plane
440  TriMesh::EdgeHandle eh = getCuttedEdge(*mesh, normal, point);
441 
442  if (!eh.is_valid())
443  return false;
444 
445  TriMesh::FaceHandle fh = mesh->face_handle(mesh->halfedge_handle(eh, 0));
446 
447  if (!fh.is_valid())
448  fh = mesh->face_handle(mesh->halfedge_handle(eh, 1));
449 
450  // get all intersection points
451  linePoints = getIntersectionPoints(mesh, fh.idx(), normal, point, closed);
452 
453  } else {
454  PolyMesh *mesh = PluginFunctions::polyMesh(&_obj);
455 
456  if (mesh == nullptr)
457  return false;
458 
459  // get a edge of the mesh that is cut by the plane
460  PolyMesh::EdgeHandle eh = getCuttedEdge(*mesh, normal, point);
461 
462  if (!eh.is_valid())
463  return false;
464 
465  PolyMesh::FaceHandle fh = mesh->face_handle(mesh->halfedge_handle(eh, 0));
466 
467  if (!fh.is_valid())
468  fh = mesh->face_handle(mesh->halfedge_handle(eh, 1));
469 
470  // get all intersection points
471  linePoints = getIntersectionPoints(mesh, fh.idx(), normal, point, closed);
472  }
473 
474  if (linePoints.empty())
475  return false;
476 
477  _center = ACG::Vec3d(0.0, 0.0, 0.0);
478 
479  for (uint i = 0; i < linePoints.size(); i++)
480  _center += linePoints[i];
481 
482  _center /= (double)linePoints.size();
483 
484  _radius = 0;
485 
486  for (uint i = 0; i < linePoints.size(); i++) {
487  double dist = (_center - linePoints[i]).norm();
488 
489  if (dist > _radius)
490  _radius = dist;
491  }
492  return true;
493 }
494 
495 //------------------------------------------------------------------------------
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
ACG::Vec3d wMouseDownPosition_
additional information
Definition: PlanePlugin.hh:186
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
void slotMouseEvent(QMouseEvent *_event)
a mouse event occured
Definition: PlanePlugin.cc:145
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
ACG::Vec3d wCurrMousePos_
additional information
Definition: PlanePlugin.hh:187
void initializePlugin()
Initialize the plugin.
Definition: PlanePlugin.cc:73
QtPlaneSelect * createPlane_
additional information
Definition: PlanePlugin.hh:174
MeshT::EdgeHandle getCuttedEdge(MeshT &_mesh, ACG::Vec3d &_planeNormal, ACG::Vec3d &_planePoint)
get an edge of the mesh that is cut by the plane
bool dragging_
additional information
Definition: PlanePlugin.hh:180
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
void show()
Show node: set status to Active.
Definition: BaseNode.hh:407
PlaneNode * planeNode()
Get the scenegraph Node.
Definition: PlaneObject.cc:181
void slotCreatePlaneTriggered()
Create a plane node when position/normal have been drawn.
Definition: PlanePlugin.cc:374
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
std::vector< ACG::Vec3d > getIntersectionPoints(MeshT *_mesh, uint _fh, ACG::Vec3d _planeNormal, ACG::Vec3d _planePoint, bool &_closed)
get the points from the intersection between mesh and plane
QAction * planeNodeAction_
additional information
Definition: PlanePlugin.hh:176
int id() const
Definition: BaseObject.cc:190
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
void setDescriptions()
set scripting slot descriptions
PlaneObject * curPlane_
additional information
Definition: PlanePlugin.hh:190
void setPlane(const ACG::Vec3d &_position, const ACG::Vec3d &_xDirection, const ACG::Vec3d &)
Set plane.
Definition: PlaneType.cc:48
Viewer::ActionMode actionMode()
Get the current Action mode.
pick any of the prior targets (should be implemented for all nodes)
Definition: PickTarget.hh:84
double sceneRadius()
Returns the current scene radius from the active examiner widget.
int pickedCorner_
additional information
Definition: PlanePlugin.hh:183
void slotMouseEvent(QMouseEvent *_event)
const std::string pickMode()
Get the current Picking mode.
int lastObjId_
additional information
Definition: PlanePlugin.hh:194
PlaneObject * planeObject(BaseObjectData *_object)
Cast an BaseObject to a PlaneObject if possible.
ACG::Vec3d viewDirection_
additional information
Definition: PlanePlugin.hh:188
void setSize(double _xDirection, double _yDirection)
Set plane size.
Definition: PlaneType.cc:100
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
Definition: GLState.cc:930
auto length() const -> decltype(std::declval< VectorT< S, DIM >>().norm())
compute squared euclidean norm
Definition: Vector11T.hh:442
void slotKeyReleaseEvent(QKeyEvent *_event)
a keyRelease event occured
Definition: PlanePlugin.cc:132
void slotPickModeChanged(const std::string &_mode)
the pickMode changed
Definition: PlanePlugin.cc:120
Viewer::ViewerProperties & viewerProperties(int _id)
Get the viewer properties Use this functions to get basic viewer properties such as backgroundcolor o...
#define DATA_PLANE
Definition: Plane.hh:58
void pluginsInitialized()
Second initialization phase.
Definition: PlanePlugin.cc:83
PlanePlugin()
Constructor.
Definition: PlanePlugin.cc:59
int context_height() const
get gl context height
Definition: GLState.hh:855
void slotCreatePlaneNode()
Plane Node Button.
Definition: PlanePlugin.cc:367
Plane origPlane_
additional information
Definition: PlanePlugin.hh:192
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:121
ACG::GLState & glState()
Get the glState of the Viewer.