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
51const char *PLANE = "CreatePlaneNode";
52const 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 emit registerKey(Qt::Key_Shift, Qt::NoModifier, tr("PP"), true);
77 emit registerKey(Qt::Key_Shift, Qt::ShiftModifier, tr("PP"), true);
78 emit registerKey(Qt::Key_Shift, Qt::ControlModifier | Qt::ShiftModifier, tr("PP"), true);
79}
80
81//------------------------------------------------------------------------------
82
88
89 emit addPickMode(PLANE);
90
91 emit addPickMode(PLANE_RESIZE);
92
95 connect(createPlane_, SIGNAL(signalTriggerCut()), this,
97 connect(createPlane_, SIGNAL(updateViewProxy()), this, SIGNAL(updateView()));
98
99 // Add a Toolbar
100 QToolBar *toolbar = new QToolBar("Plane Toolbar");
101 // createPlane_Node
102 planeNodeAction_ = new QAction(tr("&Create Plane"), this);
103 planeNodeAction_->setCheckable(true);
104 planeNodeAction_->setStatusTip(tr("Create a Plane Node"));
105 planeNodeAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr() +
106 OpenFlipper::Options::dirSeparator() +
107 "plane_plane.png"));
108 connect(planeNodeAction_, SIGNAL(triggered()), this,
109 SLOT(slotCreatePlaneNode()));
110 toolbar->addAction(planeNodeAction_);
111
112 emit addToolbar(toolbar);
113}
114
115/*******************************************************************************
116 PickingInterface implementation
117 *******************************************************************************/
118
123void PlanePlugin::slotPickModeChanged(const std::string &_mode) {
124 planeNodeAction_->setChecked(_mode == PLANE);
125}
126
127/*******************************************************************************
128KeyInterface implementation
129*******************************************************************************/
130
135void PlanePlugin::slotKeyEvent(QKeyEvent *_event)
136{
137 if (_event->key() == Qt::Key_Shift)
138 {
139 createPlane_->setClamping(true);
140 ortho_ = true;
141 }
142}
143
148void PlanePlugin::slotKeyReleaseEvent(QKeyEvent *_event) {
149 if (_event->key() == Qt::Key_Shift)
150 {
151 createPlane_->setClamping(false);
152 ortho_ = false;
153 }
154 if (PluginFunctions::pickMode() == PLANE)
155 createPlane_->slotKeyReleaseEvent(_event);
156}
157
158/*******************************************************************************
159 MouseInterface implementation
160 *******************************************************************************/
161
166void PlanePlugin::slotMouseEvent(QMouseEvent *_event) {
167
168 // auto determinant = [](ACG::Vec3f const &c1, ACG::Vec3f const &c2,
169 // ACG::Vec3f const &c3) -> float {
170 // return (c1[0] * c2[1] * c3[2]) + (c2[0] * c3[1] * c1[2]) +
171 // (c3[0] * c1[1] * c2[2]) - (c3[0] * c2[1] * c1[2]) -
172 // (c2[0] * c1[1] * c3[2]) - (c1[0] * c3[1] * c2[2]);
173 // };
175 // auto rayPlaneIntersection =
176 // [&determinant](ACG::Vec3f const &s1, ACG::Vec3f const &s2,
177 // ACG::Vec3f const &p, ACG::Vec3f const &q,
178 // ACG::Vec3f const &r, float &alpha, float &beta) {
179 // /// implementation of cramers rule to compute closed form
180 // solution for
181 // /// plane ray intersections (closed form solution) problematic
182 // if ray
183 // /// points away or converges to parallel to plane
184 // /// s1 and s2 are two points that define the ray (origin &
185 // origin+dir)
186 // /// p,q,r span a triangle that defines the plane
187 // /// alpha & beta are return params for the barycentric
188 // coordinates
189 // /// alpha belongs to p, beta belongs to q
190 // const auto denom = determinant(s1, r - p, r - q);
191 // alpha = determinant(s1, r - s2, r - q) / denom;
192 // beta = determinant(s1, r - p, r - s2) / denom;
193 // };
194
196 auto rayPlaneIntersection = [](ACG::Vec3d const &origin,
197 ACG::Vec3d const &dir, ACG::Vec3d const &p,
198 ACG::Vec3d const &q, ACG::Vec3d const &r,
199 double &alpha, double &beta) -> bool {
200 {
201 ACG::Vec3d edge1, edge2, tvec, pvec, qvec;
202 double det, inv_det;
203
204 edge1 = q - p;
205 edge2 = r - p;
206 pvec = dir % edge2;
207 det = edge1 | pvec;
208
209 constexpr double EPSILON = std::numeric_limits<double>::epsilon() * 1e2;
210 if (det > -EPSILON && det < EPSILON) {
211 std::cerr << "det within eps!" << std::endl;
212 return false;
213 }
214 inv_det = 1.f / det;
215 tvec = origin - p;
216 alpha = (tvec | pvec) * inv_det;
217 qvec = tvec % edge1;
218 beta = (dir | qvec) * inv_det;
219
223 if (alpha < 0.0 || alpha > 1.0)
224 return false;
225 if (beta < 0.0 || alpha + beta > 1.0)
226 return false;
227
228 return true;
229 }
230 };
231
232 if (PluginFunctions::pickMode() == PLANE)
234 if (PluginFunctions::pickMode() == PLANE_RESIZE) {
235 switch (_event->type()) {
236 case QEvent::MouseButtonPress: {
237 ACG::Vec3d sourcePoint3D;
238 size_t node_idx, target_idx;
240 _event->pos(), node_idx, target_idx,
241 &sourcePoint3D)) {
242 BaseObjectData *obj = nullptr;
243 if (PluginFunctions::getPickedObject(node_idx, obj)) {
244 // is picked object Plane?
246 lastObjId_ = obj->id();
247
248 if (curPlane_) {
249 origPlane_ = curPlane_->plane();
250 QPoint position = _event->pos();
251
252 viewCoord_ = ACG::Vec3d(position.x(), PluginFunctions::viewerProperties().glState().context_height() - position.y(), .5);
253
255 ACG::Vec3d origin;
257 viewCoord_[0], viewCoord_[1], origin, viewDirection_);
258
260 {
261 const auto center = curPlane_->plane().position;
262 const auto xdir = curPlane_->plane().xDirection / 2.;
263 const auto ydir = curPlane_->plane().yDirection / 2.;
264 const auto p0 = center + xdir + ydir;
265 const auto p1 = center + xdir - ydir;
266 const auto p2 = center - xdir - ydir;
267 double u = -1, v = -1, w = -1;
268 rayPlaneIntersection(origin, viewDirection_, p0, p1, p2, u, v);
269 w = 1 - (u + v);
270 wMouseDownPosition_ = (w * p0) + (u * p1) + (v * p2);
271 }
272
273 pickedCorner_ = target_idx;
274
275 // We hit a corner so start dragging
276 dragging_ = true;
277 }
278 }
279 }
280
281 break;
282 }
283
284 case QEvent::MouseMove: {
285
286 if (dragging_ && curPlane_) {
287
288 QPoint position = _event->pos();
289
291 auto plane = origPlane_;
292
296 const auto viewCoord = ACG::Vec3d(
297 position.x(),
299 position.y(),
300 .5);
301
302 {
303 ACG::Vec3d origin;
304 ACG::Vec3d dir;
306 viewCoord[0], viewCoord[1], origin, dir);
308 double u = -3.1415926539, v = -3.1415926539;
309 const auto center = plane.position;
310 const auto xdir = plane.xDirection / 2.;
311 const auto ydir = plane.yDirection / 2.;
312 const auto p0 = ACG::Vec3d{center + xdir + ydir};
313 const auto p1 = ACG::Vec3d{center + xdir - ydir};
314 const auto p2 = ACG::Vec3d{center - xdir - ydir};
318
320 rayPlaneIntersection(origin, dir, p0, p1, p2, u, v);
321 const auto w = 1. - (u + v);
322 wCurrMousePos_ = (w * p0) + (u * p1) + (v * p2);
323 }
324
327
328 // const auto wMouseDownPosition = wMouseDownPosition_ - plane.position;
329 if (pickedCorner_ != 0)
330 {
331 OpenMesh::Vec3d wMouseDownPosition;
332 const auto center = plane.position;
333 const auto xdir = plane.xDirection / 2.;
334 const auto ydir = plane.yDirection / 2.;
335 // Hit the plane 0 corner
336 if (pickedCorner_ == 1)
337 wMouseDownPosition = (center - xdir - ydir);
338 // Hit the plane x corner
339 if (pickedCorner_ == 2)
340 wMouseDownPosition = center + xdir - ydir;
341 // Hit the plane xy corner
342 if (pickedCorner_ == 3)
343 wMouseDownPosition = center + xdir + ydir;
344 // Hit the plane y
345 if (pickedCorner_ == 4)
346 wMouseDownPosition = center - xdir + ydir;
347 wMouseDownPosition -= plane.position;
348
350 const auto wCurrMousePos = wCurrMousePos_ - plane.position;
351
353 const auto worldSpaceUpdate = (wCurrMousePos - wMouseDownPosition) / 2.;
355
356 const auto distClickOrigin = wMouseDownPosition;
357
359 auto scale = (worldSpaceUpdate + distClickOrigin) / distClickOrigin;
360 if (!std::isinf(scale[2]) && !std::isnan(scale[2]))
361 scale[2] = 1;
362
363 const auto xscale = scale[0] * plane.xDirection.length();
364 const auto yscale = scale[1] * plane.yDirection.length();
365 plane.setSize(xscale, yscale);
368 plane.position += worldSpaceUpdate;
369 }
370 else
371 {
372 if (ortho_)
373 {
375 const auto viewDiff = (viewCoord - viewCoord_) / 1000;
376 const auto sign = (viewDiff[0] / std::fabs(viewDiff[0]));
377 plane.position += plane.normal * sign * viewDiff.length();
378 }
379 else
380 {
383 const auto wMouseDownPosition = wMouseDownPosition_ - plane.position;
384 const auto wCurrMousePos = wCurrMousePos_ - plane.position;
385 const auto worldSpaceUpdate = (wCurrMousePos - wMouseDownPosition) / 2.;
386 plane.position += worldSpaceUpdate * 2;
387 }
388 }
389
390 curPlane_->plane() = plane;
391
393 if (lastObjId_ > 0)
394 emit updatedObject(lastObjId_, UPDATE_GEOMETRY);
395 else
396 emit log(LOGERR, tr("COULD NOT UPDATE OBJECT"));
397 }
398
399 break;
400 }
401 case QEvent::MouseButtonRelease: {
402
403 // Stop dragging operation reset all properties
404 dragging_ = false;
405 pickedCorner_ = 0;
407 curPlane_ = nullptr;
408 lastObjId_ = -1;
409
410 break;
411 }
412 default:
413 break;
414 }
415 }
416}
417
418/*******************************************************************************
419 PlanePlugin Implementation
420 *******************************************************************************/
421
422//------------------------------------------------------------------------------
423
428 PluginFunctions::actionMode(Viewer::PickingMode);
430}
431
432//------------------------------------------------------------------------------
433
435 // get object
436 BaseObjectData *obj;
437
438 if (PluginFunctions::getPickedObject(createPlane_->getNode(), obj)) {
439 if (obj == nullptr) {
440 emit log(LOGERR, "Unable to get object");
441 return;
442 }
443
444 // generate a plane object
445 int planeId = -1;
446
447 // add new plane
448 emit addEmptyObject(DATA_PLANE, planeId);
449
450 // get current planeobject
451 BaseObjectData *planeObj;
452 PluginFunctions::getObject(planeId, planeObj);
453
454 // get plane object
455 PlaneObject *currentPlane = PluginFunctions::planeObject(planeObj);
456
457 ACG::Vec3d point = createPlane_->getSourcePoint();
458 ACG::Vec3d normal = createPlane_->getNormal();
459
460 ACG::Vec3d center;
461 double radius;
462
463 if (getIntersectionParams(*obj, center, radius)) {
464 radius = 1.5 * radius;
465 radius = std::min(radius, PluginFunctions::sceneRadius());
466
467 currentPlane->plane().setPlane(center, normal);
468 currentPlane->plane().setSize(2.0 * radius, 2.0 * radius);
469
470 } else {
471 std::cerr << "unable to get intersection params" << std::endl;
472 currentPlane->plane().setPlane(point, normal);
473 currentPlane->plane().setSize(PluginFunctions::sceneRadius(),
475 }
476
477 currentPlane->planeNode()->show();
478
479 emit updatedObject(planeId, UPDATE_ALL);
480 }
481}
482
483//------------------------------------------------------------------------------
484
485bool PlanePlugin::getIntersectionParams(BaseObjectData &_obj,
486 ACG::Vec3d &_center, double &_radius) {
487 ACG::Vec3d point = createPlane_->getSourcePoint();
488 ACG::Vec3d normal = createPlane_->getNormal();
489
490 // get the intersection points
491 std::vector<ACG::Vec3d> linePoints;
492 bool closed = false;
493 if (_obj.dataType(DATA_TRIANGLE_MESH)) {
494 TriMesh *mesh = PluginFunctions::triMesh(&_obj);
495
496 if (mesh == nullptr)
497 return false;
498
499 // get a edge of the mesh that is cut by the plane
500 TriMesh::EdgeHandle eh = getCuttedEdge(*mesh, normal, point);
501
502 if (!eh.is_valid())
503 return false;
504
505 TriMesh::FaceHandle fh = mesh->face_handle(mesh->halfedge_handle(eh, 0));
506
507 if (!fh.is_valid())
508 fh = mesh->face_handle(mesh->halfedge_handle(eh, 1));
509
510 // get all intersection points
511 linePoints = getIntersectionPoints(mesh, fh.idx(), normal, point, closed);
512
513 } else {
514 PolyMesh *mesh = PluginFunctions::polyMesh(&_obj);
515
516 if (mesh == nullptr)
517 return false;
518
519 // get a edge of the mesh that is cut by the plane
520 PolyMesh::EdgeHandle eh = getCuttedEdge(*mesh, normal, point);
521
522 if (!eh.is_valid())
523 return false;
524
525 PolyMesh::FaceHandle fh = mesh->face_handle(mesh->halfedge_handle(eh, 0));
526
527 if (!fh.is_valid())
528 fh = mesh->face_handle(mesh->halfedge_handle(eh, 1));
529
530 // get all intersection points
531 linePoints = getIntersectionPoints(mesh, fh.idx(), normal, point, closed);
532 }
533
534 if (linePoints.empty())
535 return false;
536
537 _center = ACG::Vec3d(0.0, 0.0, 0.0);
538
539 for (uint i = 0; i < linePoints.size(); i++)
540 _center += linePoints[i];
541
542 _center /= (double)linePoints.size();
543
544 _radius = 0;
545
546 for (uint i = 0; i < linePoints.size(); i++) {
547 double dist = (_center - linePoints[i]).norm();
548
549 if (dist > _radius)
550 _radius = dist;
551 }
552 return true;
553}
554
555//------------------------------------------------------------------------------
@ LOGERR
#define DATA_PLANE
Definition: Plane.hh:58
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
int context_height() const
get gl context height
Definition: GLState.hh:855
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
Definition: GLState.cc:930
void setPlane(const ACG::Vec3d &_position, const ACG::Vec3d &_xDirection, const ACG::Vec3d &)
Set plane.
Definition: PlaneType.cc:48
void setSize(double _xDirection, double _yDirection)
Set plane size.
Definition: PlaneType.cc:100
void show()
Show node: set status to Active.
Definition: BaseNode.hh:407
bool dataType(DataType _type) const
Definition: BaseObject.cc:219
int id() const
Definition: BaseObject.cc:188
Kernel::EdgeHandle EdgeHandle
Scalar type.
Definition: PolyMeshT.hh:138
Kernel::FaceHandle FaceHandle
Scalar type.
Definition: PolyMeshT.hh:139
static constexpr size_t size()
returns dimension of the vector
Definition: Vector11T.hh:107
auto length() const -> decltype(std::declval< VectorT< S, DIM > >().norm())
compute squared euclidean norm
Definition: Vector11T.hh:443
PlaneNode * planeNode()
Get the scenegraph Node.
Definition: PlaneObject.cc:181
bool dragging_
additional information
Definition: PlanePlugin.hh:181
void slotCreatePlaneTriggered()
Create a plane node when position/normal have been drawn.
Definition: PlanePlugin.cc:434
ACG::Vec3d viewCoord_
additional information
Definition: PlanePlugin.hh:190
PlanePlugin()
Constructor.
Definition: PlanePlugin.cc:59
PlaneObject * curPlane_
additional information
Definition: PlanePlugin.hh:193
bool ortho_
additional information
Definition: PlanePlugin.hh:182
ACG::Vec3d wCurrMousePos_
additional information
Definition: PlanePlugin.hh:189
void slotKeyReleaseEvent(QKeyEvent *_event)
a keyRelease event occured
Definition: PlanePlugin.cc:148
int pickedCorner_
additional information
Definition: PlanePlugin.hh:185
QAction * planeNodeAction_
additional information
Definition: PlanePlugin.hh:177
ACG::Vec3d wMouseDownPosition_
additional information
Definition: PlanePlugin.hh:188
void slotPickModeChanged(const std::string &_mode)
the pickMode changed
Definition: PlanePlugin.cc:123
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
MeshT::EdgeHandle getCuttedEdge(MeshT &_mesh, ACG::Vec3d &_planeNormal, ACG::Vec3d &_planePoint)
get an edge of the mesh that is cut by the plane
QtPlaneSelect * createPlane_
additional information
Definition: PlanePlugin.hh:175
void setDescriptions()
set scripting slot descriptions
void slotCreatePlaneNode()
Plane Node Button.
Definition: PlanePlugin.cc:427
Plane origPlane_
additional information
Definition: PlanePlugin.hh:195
void slotKeyEvent(QKeyEvent *_event)
a key event occured
Definition: PlanePlugin.cc:135
void initializePlugin()
Initialize the plugin.
Definition: PlanePlugin.cc:73
int lastObjId_
additional information
Definition: PlanePlugin.hh:197
void slotMouseEvent(QMouseEvent *_event)
a mouse event occured
Definition: PlanePlugin.cc:166
ACG::Vec3d viewDirection_
additional information
Definition: PlanePlugin.hh:191
void pluginsInitialized()
Second initialization phase.
Definition: PlanePlugin.cc:86
void slotMouseEvent(QMouseEvent *_event)
ACG::GLState & glState()
Get the glState of the Viewer.
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
@ PICK_ANYTHING
pick any of the prior targets (should be implemented for all nodes)
Definition: PickTarget.hh:84
VectorT< double, 3 > Vec3d
Definition: VectorT.hh:121
double sceneRadius()
Returns the current scene radius from the active examiner widget.
Viewer::ViewerProperties & viewerProperties(int _id)
Get the viewer properties Use this functions to get basic viewer properties such as backgroundcolor o...
PlaneObject * planeObject(BaseObjectData *_object)
Cast an BaseObject to a PlaneObject if possible.
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const std::string pickMode()
Get the current Picking mode.
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
Viewer::ActionMode actionMode()
Get the current Action mode.