PolyLinePlugin.cc 81.4 KB
Newer Older
1 2 3 4 5 6 7 8 9
//=============================================================================
//
//  CLASS PolyLinePlugin - IMPLEMENTATION
//
//=============================================================================


//== INCLUDES =================================================================

10

11 12 13 14 15 16 17 18
#include "PolyLinePlugin.hh"

#include <iostream>
#include <ACG/GL/GLState.hh>
#include <QStringList>
#include <ACG/Scenegraph/ManipulatorNode.hh>

#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
19
#include <OpenFlipper/BasePlugin/PluginFunctionsViewControls.hh>
20 21 22

#include <OpenFlipper/common/GlobalOptions.hh>

23 24
#include <ACG/Geometry/bsp/BSPImplT.hh>

25 26 27 28 29 30
#if QT_VERSION >= 0x050000 
#include <QtWidgets>
#else
#include <QtGui>
#endif

31 32
namespace {

Jan Möbius's avatar
Jan Möbius committed
33 34
class GlutPrimitiveNode : public ACG::SceneGraph::GlutPrimitiveNode {

35
	public:
Jan Möbius's avatar
Jan Möbius committed
36

37 38
		GlutPrimitiveNode(PolyLineObject* L, std::string name, int _index = -1)
			: ACG::SceneGraph::GlutPrimitiveNode(ACG::SceneGraph::GlutPrimitiveNode::SPHERE, L->manipulatorNode(), name)
39
		{
40
			index = _index;
41 42
			line = L;
		}
Jan Möbius's avatar
Jan Möbius committed
43

44
	public:
Jan Möbius's avatar
Jan Möbius committed
45

46
		PolyLineObject* line;
47
		int index;
Jan Möbius's avatar
Jan Möbius committed
48

49 50
};

51
class GlutLineNode : public ACG::SceneGraph::LineNode {
Jan Möbius's avatar
Jan Möbius committed
52

53
	public:
Jan Möbius's avatar
Jan Möbius committed
54

55 56 57 58 59
		GlutLineNode(PolyLineObject* L, std::string name)
			: ACG::SceneGraph::LineNode(LineSegmentsMode, L->manipulatorNode(), name)
		{
			line = L;
		}
Jan Möbius's avatar
Jan Möbius committed
60

61 62
	public:
		PolyLineObject* line;
Jan Möbius's avatar
Jan Möbius committed
63

64
};
65 66 67

}

68 69 70 71 72 73 74 75 76 77 78 79

//== IMPLEMENTATION ==========================================================

/// default constructor
PolyLinePlugin::PolyLinePlugin() :
        tool_(0),
        polyLineAction_(0),
        toolBarActions_(0),
        toolbar_(0),
        pickToolbar_(0),
        pickToolBarActions_(0),
        insertAction_(0),
80
        insertCircleAction_(0),
81
        insertSplineAction_(0),
82 83 84 85 86 87 88 89 90 91
        deleteAction_(0),
        moveAction_(0),
        smartMoveAction_(0),
        mergeAction_(0),
        splitAction_(0),
        cutAction_(0),
        cur_insert_id_(-1),
        cur_polyline_obj_(0),
        cur_move_id_(-1),
        move_point_ref_(0),
92
        create_point_ref_(0),
93
        createCircle_CurrSelIndex_(-1),
94
        createCircle_LastSelIndex_(-1),
95
        moveCircle_SelNode_(0),
96 97
        moveCircle_IsLocked(false),
        moveCircle_IsFloating(false),
98 99 100 101
        copyPaste_Action_(0),
        copyPaste_ObjectId_(-1),
        copyPaste_ActionType_(-1),
        copyPaste_NewObjectId_(-1),
102 103 104 105 106
        createSpline_CurrSelIndex_(-1),
        createSpline_LastSelIndex_(-1),
        moveBezSpline_SelNode_(0),
        moveBezSpline_SelIndex_(-1),
        moveBezSpline_SelSubIndex_(0),
107 108 109
        cur_merge_id_(-1),
        smart_move_timer_(0),
        cur_smart_move_obj_(0),
110
        planeSelect_(0)
111 112 113 114 115 116 117 118
{
}


void
PolyLinePlugin::
initializePlugin()
{
Jan Möbius's avatar
Jan Möbius committed
119
  // Initialize the Toolbox
120 121 122
  tool_ = new PolyLineToolbarWidget();
  QSize size(100, 100);
  tool_->resize(size);
Jan Möbius's avatar
Jan Möbius committed
123
  tool_->setObjectName("PolyLineToolbar");
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

  // connect signals->slots
  connect(tool_->pb_subdivide,SIGNAL(clicked() ),this,SLOT(slot_subdivide()));
  connect(tool_->subdivide_relative,SIGNAL(toggled(bool) ),this,SLOT(slot_subdivide_percent(bool)));
  connect(tool_->pb_decimate,SIGNAL(clicked() ),this,SLOT(slot_decimate()));
  connect(tool_->decimate_relative,SIGNAL(toggled(bool) ),this,SLOT(slot_decimate_percent(bool)));
#ifdef EXTENDED_POLY_LINE
  connect(tool_->pb_resample_on_edges,SIGNAL(clicked() ),this,SLOT(slot_resample_on_edges()));
#else
  tool_->pb_resample_on_edges->setDisabled(true);
#endif
  connect(tool_->pb_smooth,SIGNAL(clicked() ),this,SLOT(slot_smooth()));
  connect(tool_->pb_smooth,SIGNAL(clicked() ),this,SIGNAL(updateView()));
  connect(tool_->pb_project,SIGNAL(clicked() ),this,SLOT(slot_project()));
  connect(tool_->pb_project,SIGNAL(clicked() ),this,SIGNAL(updateView()));
  connect(tool_->pb_smooth_project,SIGNAL(clicked() ),this,SLOT(slot_smooth_project()));
  connect(tool_->pb_smooth_project,SIGNAL(clicked() ),this,SIGNAL(updateView()));

  connect(tool_->rb_insert, SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
143
  connect(tool_->rb_InsertCircle, SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
144
  connect(tool_->rb_InsertSpline, SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
145 146 147 148 149 150
  connect(tool_->rb_delete, SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
  connect(tool_->rb_move,   SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
  connect(tool_->rb_smart_move, SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
  connect(tool_->rb_merge,  SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));
  connect(tool_->rb_split,  SIGNAL( clicked() ), this, SLOT( slotEditModeChanged() ));

151
  connect(tool_->sb_CirclePointNum, SIGNAL(valueChanged(int)), this, SLOT(slot_setCirclePointNum(int)));
152
  connect(tool_->sb_SplineSegNum, SIGNAL(valueChanged(int)), this, SLOT(slot_setSplinePointNum(int)));
153 154
  //add icons
  tool_->rb_insert->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_insert.png") );
155
  tool_->rb_InsertCircle->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_circle.png") );
156
  tool_->rb_InsertSpline->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_bezier.png") );
157 158 159 160 161 162 163 164
  tool_->rb_delete->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_delete.png") );
  tool_->rb_move->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_move.png") );
  tool_->rb_smart_move->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_move.png") );
  tool_->rb_merge->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_merge.png") );
  tool_->rb_split->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_split.png") );
//   tool_->rb_smooth_c0->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_insert.svg") );
//   tool_->rb_smooth_c1->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_c1.svg") );
//   tool_->rb_smooth_c2->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "polyline_c2.svg") );
165 166 167 168 169


  // Add the toolbox with the given icon
  QIcon* toolIcon = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cut_polyline.png");
  emit addToolbox( tr("PolyLine") , tool_, toolIcon );
170 171 172 173 174 175 176 177 178 179
}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slotMouseEvent( QMouseEvent* _event )
{
180 181
  copyPaste_LastMouse = _event->pos();

182
  // control modifier is reserved for selcting target
Jan Möbius's avatar
Jan Möbius committed
183
  if (_event->modifiers() & (Qt::ControlModifier))
184 185
    return;

Jan Möbius's avatar
Jan Möbius committed
186 187
  if (PluginFunctions::pickMode() == ("PolyLine") && PluginFunctions::actionMode() == Viewer::PickingMode
      && _event->button() != Qt::RightButton) {
188
    // handle mouse events depending on current mode
Jan Möbius's avatar
Jan Möbius committed
189
    switch (mode()) {
190
      case PL_INSERT:
Jan Möbius's avatar
Jan Möbius committed
191 192
        me_insert(_event);
        break;
193

194 195 196 197
      case PL_INSERTCIRCLE:
        me_insertCircle(_event);
        break;

198 199 200 201
      case PL_INSERTSPLINE:
        me_insertSpline(_event);
        break;

202
      case PL_DELETE:
Jan Möbius's avatar
Jan Möbius committed
203 204
        me_delete(_event);
        break;
205 206

      case PL_MOVE:
Jan Möbius's avatar
Jan Möbius committed
207 208
        me_move(_event);
        break;
209 210

      case PL_SPLIT:
Jan Möbius's avatar
Jan Möbius committed
211 212
        me_split(_event);
        break;
213 214

      case PL_MERGE:
Jan Möbius's avatar
Jan Möbius committed
215 216
        me_merge(_event);
        break;
217 218

      case PL_SMART_MOVE:
Jan Möbius's avatar
Jan Möbius committed
219 220
        me_smart_move(_event);
        break;
221

222 223 224 225
      case PL_COPY_PASTE:
          me_copyPasteMouse(_event);
        break;

226
      default:
Jan Möbius's avatar
Jan Möbius committed
227
        break;
228
    }
Jan Möbius's avatar
Jan Möbius committed
229 230
  } else if (PluginFunctions::pickMode() == CREATE_CUT_POLYLINE) {
    planeSelect_->slotMouseEvent(_event);
231 232 233
  }
}

234 235 236
void PolyLinePlugin::slotKeyEvent(QKeyEvent* event) {
    switch (event->key()) {
        case Qt::Key_Return:
237 238 239
            if (PluginFunctions::pickMode() == ("PolyLine") && PluginFunctions::actionMode() == Viewer::PickingMode) {
                if(mode() == PL_INSERT && cur_polyline_obj_ && cur_insert_id_ != -1) {
                    cur_polyline_obj_->line()->delete_point(cur_polyline_obj_->line()->n_vertices() - 1);
240

241 242
                    if (event->modifiers() & (Qt::ShiftModifier))
                      cur_polyline_obj_->line()->set_closed(true);
243

244
                    emit updatedObject(cur_insert_id_, UPDATE_GEOMETRY | UPDATE_TOPOLOGY);
245

246 247 248
                    cur_insert_id_ = -1;
                    cur_polyline_obj_ = 0;
                    create_point_ref_ = 0;
249

250 251 252 253 254
                    clearStatusMessage();
                }
                else if(mode() == PL_INSERTSPLINE) {
                    finishSpline();
                }
255 256 257 258 259 260
            }
            break;
        default:
            break;
    }
}
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
//-----------------------------------------------------------------------------

void
PolyLinePlugin::
slotPickModeChanged( const std::string& _mode)
{
  polyLineAction_->setChecked(_mode == "PolyLine");
  cutAction_->setChecked( _mode == CREATE_CUT_POLYLINE );
}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
pluginsInitialized()
{
Jan Möbius's avatar
Jan Möbius committed
279 280
  // Add the required Picking modes (Hidden, controlled only by the buttons)
  emit addHiddenPickMode("PolyLine");
281
  emit setPickModeMouseTracking("PolyLine", true);
Jan Möbius's avatar
Jan Möbius committed
282
  emit addHiddenPickMode( CREATE_CUT_POLYLINE );
283 284 285

  emit registerKey(Qt::Key_Return, Qt::NoModifier, tr("Terminate creation of poly line."), true);
  emit registerKey(Qt::Key_Return, Qt::ShiftModifier, tr("Terminate creation of poly line and create loop."), true);
286
  
Jan Möbius's avatar
Jan Möbius committed
287
  // TOOLBAR
288
  toolbar_ = new QToolBar(tr("PolyLine Editing"));
Jan Möbius's avatar
Jan Möbius committed
289 290
  toolbar_->setObjectName("PolyLineEditingToolbar");

291 292 293
  
  toolBarActions_ = new QActionGroup(toolbar_);
  
Jan Möbius's avatar
Jan Möbius committed
294
  // icon which enables the Modeling toolbar
295 296 297 298 299 300
  polyLineAction_ = new QAction(tr("Edit PolyLines"), toolBarActions_);
  polyLineAction_->setStatusTip(tr("Edit and create PolyLines."));
  polyLineAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_insert.png") );
  polyLineAction_->setCheckable(true);
  toolbar_->addAction(polyLineAction_);
  
Jan Möbius's avatar
Jan Möbius committed
301
  // icon for polyline cutting of objects
302 303 304 305 306 307 308 309 310 311 312 313 314
  cutAction_ = new QAction(tr("&Create polyline at intersection with plane"), this);
  cutAction_->setCheckable( true );
  cutAction_->setStatusTip(tr("Create a polyline by specifying a plane with which the object is then intersected. The polyline will be created at the intersection."));
  cutAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cut_polyline.png") );
  connect(cutAction_, SIGNAL(triggered()), this, SLOT(slotScissorButton()) );
  toolbar_->addAction(cutAction_);
  
  connect(toolBarActions_, SIGNAL(triggered(QAction*)), this, SLOT(slotSetPolyLineMode(QAction*)) );
  
  emit addToolbar(toolbar_);
  
  // Pick Toolbar
  pickToolbar_ = new QToolBar(tr("PolyLine Editing"));
Jan Möbius's avatar
Jan Möbius committed
315
  pickToolbar_->setObjectName("PolyLine_Editing_Toolbar");
316 317 318 319 320 321 322 323 324 325 326 327 328
  pickToolbar_->setAttribute(Qt::WA_AlwaysShowToolTips, true);
  pickToolBarActions_ = new QActionGroup(pickToolbar_);
  pickToolBarActions_->setExclusive (true);
  
  insertAction_ = new QAction(tr("Create PolyLine"), pickToolBarActions_);
  insertAction_->setStatusTip(tr("Create a new PolyLine."));
  insertAction_->setToolTip(tr("Create a new PolyLine.\n"
                               "Use <Doubleclick> to finish the line.\n"
                               "Hold <Shift> to close line on finish."));
  insertAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_insert.png") );
  insertAction_->setCheckable(true);
  pickToolbar_->addAction(insertAction_);
  
329 330 331 332 333 334 335 336 337 338
  insertCircleAction_ = new QAction(tr("Create PolyCircle"), pickToolBarActions_);
  insertCircleAction_->setStatusTip(tr("Create a new PolyCircle."));
  insertCircleAction_->setToolTip(tr("Create a new PolyCircle.\n"
		  	  	  	  	  	  	  	 "<Click> to select the center.\n"
		  	  	  	  	  	  	  	 "Drag to specify the radius."));

  insertCircleAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_circle.png") );
  insertCircleAction_->setCheckable(true);
  pickToolbar_->addAction(insertCircleAction_);

339 340 341 342 343
  insertSplineAction_ = new QAction(tr("Create PolySpline"), pickToolBarActions_);
  insertSplineAction_->setStatusTip(tr("Create a new PolySpline."));
  insertSplineAction_->setToolTip(tr("Create a new PolySpline.\n"
		  	  	  	  	  	  	  	 "<Click> to select the points."));

344
  insertSplineAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_bezier.png") );
345 346 347
  insertSplineAction_->setCheckable(true);
  pickToolbar_->addAction(insertSplineAction_);

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
  deleteAction_ = new QAction(tr("Delete PolyLine"), pickToolBarActions_);
  deleteAction_->setStatusTip(tr("Delete a complete PolyLine."));
  deleteAction_->setToolTip(tr( "Delete a complete PolyLine.\n"
                                "<Click> on the lines you want to delete."));
  deleteAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_delete.png") );
  deleteAction_->setCheckable(true);
  pickToolbar_->addAction(deleteAction_);  

  smartMoveAction_ = new QAction(tr("Move PolyLine"), pickToolBarActions_);
  smartMoveAction_->setStatusTip(tr("Move the PolyLine."));
  smartMoveAction_->setToolTip(tr("Move the polyline."));
  smartMoveAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_move.png") );
  smartMoveAction_->setCheckable(true);
  pickToolbar_->addAction(smartMoveAction_);
  
  mergeAction_ = new QAction(tr("Merge PolyLines"), pickToolBarActions_);
  mergeAction_->setStatusTip(tr("Merge two PolyLines."));
  mergeAction_->setToolTip(tr("Merge two PolyLines.\n"
                              "Drag one endpoint of a PolyLine to the EndPoint of another one.\n"));
  mergeAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_merge.png") );
  mergeAction_->setCheckable(true);
  pickToolbar_->addAction(mergeAction_);  
  
  splitAction_ = new QAction(tr("Split a PolyLine"), pickToolBarActions_);
  splitAction_->setStatusTip(tr("Split a PolyLine at a given point."));
  splitAction_->setToolTip(tr("Split a PolyLine at a given point.\n"
                              "<Click> on the vertex where you want to split the PolyLine and drag it away."));
  splitAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_split.png") );
  splitAction_->setCheckable(true);
  pickToolbar_->addAction(splitAction_);  
  
  pickToolbar_->addSeparator ();
  
  moveAction_ = new QAction(tr("Move PolyLine Vertex"), pickToolBarActions_);
  moveAction_->setStatusTip(tr("Move a Vertex of a PolyLine"));
  moveAction_->setToolTip(tr("Move a single Vertex of a PolyLine."));
  moveAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"polyline_move.png") );
  moveAction_->setCheckable(true);
  pickToolbar_->addAction(moveAction_);
  
  emit setPickModeToolbar ("PolyLine", pickToolbar_);
  
  connect(pickToolBarActions_, SIGNAL(triggered(QAction*)), this, SLOT(slotPickToolbarAction(QAction*)) );

  // construct timer
  smart_move_timer_ = new QTimer(this);
  connect(smart_move_timer_, SIGNAL(timeout()), this, SLOT(slot_smart_move_timer()));
  
Jan Möbius's avatar
Jan Möbius committed
396
  // Instantiate plane select object
397 398 399
  planeSelect_ = new QtPlaneSelect( PluginFunctions::viewerProperties().glState() );
  connect( planeSelect_, SIGNAL( signalTriggerCut( ) ), this, SLOT( slotTriggerCutPlaneSelect() ) );
  connect( planeSelect_, SIGNAL( updateViewProxy( ) ), this, SIGNAL( updateView() ) );
400
  connect( planeSelect_, SIGNAL( nodeVisChangedProxy(int) ), this, SIGNAL(nodeVisibilityChanged(int) ) );
401 402 403 404 405

  //create copy paste action in context menu
  copyPaste_Action_ = new QAction("Duplicate", 0);
  connect(copyPaste_Action_,SIGNAL(triggered() ),this,SLOT(slot_duplicate()));
  emit addContextMenuItem(copyPaste_Action_ , DATA_POLY_LINE , CONTEXTOBJECTMENU );
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
}

//------------------------------------------------------------------------------

/** \brief Scissor Button was hit
*
*/
void PolyLinePlugin::slotScissorButton( )
{
    PluginFunctions::actionMode( Viewer::PickingMode );
    PluginFunctions::pickMode( CREATE_CUT_POLYLINE );
}

//-----------------------------------------------------------------------------

/** \brief Generate PolyLine after the cutPlane has been drawn
*
*/
void PolyLinePlugin::slotTriggerCutPlaneSelect( )
{
    using ACG::SceneGraph::LineNode;
Jan Möbius's avatar
Jan Möbius committed
427 428 429 430 431 432 433 434 435

  // Iterate over all selected objects
  BaseObjectData* object;
  if (PluginFunctions::getPickedObject(planeSelect_->getNode(), object)) {
    emit log("Cutting object " + object->name());

    if (!(object->dataType(DATA_TRIANGLE_MESH) || object->dataType(DATA_POLY_MESH))) {
      emit log("Only Meshes are supported at the moment ");
      return;
436
    }
Jan Möbius's avatar
Jan Möbius committed
437 438 439 440

    ACG::Vec3d point = planeSelect_->getSourcePoint();
    ACG::Vec3d normal = planeSelect_->getNormal();

441
    int objectId = generatePolyLineFromCut(object->id(), point, normal);
Jan Möbius's avatar
Jan Möbius committed
442 443 444 445 446 447 448 449

    QString command = "generatePolyLineFromCut(" + QString::number(object->id()) + ",Vector("
        + QString::number(point[0]) + "," + QString::number(point[1]) + "," + QString::number(point[2]) + "),Vector("
        + QString::number(normal[0]) + "," + QString::number(normal[1]) + "," + QString::number(normal[2]) + "));";
    emit scriptInfo(command);

    //remove all other targets
    for (PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,
450
         DataType(DATA_TRIANGLE_MESH | DATA_POLY_MESH)); o_it != PluginFunctions::objectsEnd(); ++o_it) {
Jan Möbius's avatar
Jan Möbius committed
451 452 453
      if (o_it->id() != object->id()) {
        o_it->target(false);
      }
454 455 456 457 458 459
    }

    // If we successfully created the polyline, we can inform the core about it.
    if ( objectId != -1)
      emit updatedObject(objectId,UPDATE_ALL);

Jan Möbius's avatar
Jan Möbius committed
460 461
  }

462 463 464 465 466 467 468 469 470 471
}

//-----------------------------------------------------------------------------

void
PolyLinePlugin::
slot_subdivide()
{
  // get subdivision value
  double max_length = tool_->dsb_subdivide->value();
Jan Möbius's avatar
Jan Möbius committed
472

473
  // safety catch
Jan Möbius's avatar
Jan Möbius committed
474
  if (max_length == 0.0)
475 476 477
    max_length = 1e-4;

  // iterate over all target polylines
Jan Möbius's avatar
Jan Möbius committed
478
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE);
479

Jan Möbius's avatar
Jan Möbius committed
480
  for (; o_it != PluginFunctions::objectsEnd(); ++o_it) {
481
    // subdivide polyline
Jan Möbius's avatar
Jan Möbius committed
482
    PluginFunctions::polyLineObject(*o_it)->line()->subdivide(max_length);
483 484 485 486 487 488 489 490 491 492 493 494
  }

  // update
  emit updateView();
}


//-----------------------------------------------------------------------------


void PolyLinePlugin::slot_subdivide_percent(bool _checked) {

Jan Möbius's avatar
Jan Möbius committed
495 496 497
  // Get subdivision value
  int n_active_pl = 0;
  double total_length = 0;
498

Jan Möbius's avatar
Jan Möbius committed
499 500
  // Iterate over all target polylines
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE);
501

Jan Möbius's avatar
Jan Möbius committed
502 503 504 505 506
  for (; o_it != PluginFunctions::objectsEnd(); ++o_it) {
    // Add polyline length
    total_length += PluginFunctions::polyLineObject(*o_it)->line()->length();
    ++n_active_pl;
  }
507

Jan Möbius's avatar
Jan Möbius committed
508
  double v = total_length / double(n_active_pl);
509

Jan Möbius's avatar
Jan Möbius committed
510
  if (_checked) {
511

Jan Möbius's avatar
Jan Möbius committed
512 513 514 515
    // Compute current absolute length to relative portion
    if (n_active_pl > 0) {
      double val_new = tool_->dsb_subdivide->value() / v;
      tool_->dsb_subdivide->setValue(val_new);
516
    } else {
Jan Möbius's avatar
Jan Möbius committed
517 518 519
      emit log(LOGWARN, "Could not find any active polyline!");
    }
  } else {
520

Jan Möbius's avatar
Jan Möbius committed
521 522 523 524 525 526
    // Compute relative portion to absolute value
    if (n_active_pl > 0) {
      double val_new = tool_->dsb_subdivide->value() * v;
      tool_->dsb_subdivide->setValue(val_new);
    } else {
      emit log(LOGWARN, "Could not find any active polyline!");
527
    }
Jan Möbius's avatar
Jan Möbius committed
528
  }
529 530 531 532 533 534 535 536 537 538 539 540 541 542
}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slot_decimate()
{
  // get subdivision value
  double min_length = tool_->dsb_decimate->value();

  // iterate over all target polylines
Jan Möbius's avatar
Jan Möbius committed
543
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE);
544

Jan Möbius's avatar
Jan Möbius committed
545
  for (; o_it != PluginFunctions::objectsEnd(); ++o_it) {
546
    // decimate polyline
Jan Möbius's avatar
Jan Möbius committed
547
    PluginFunctions::polyLineObject(*o_it)->line()->collapse(min_length);
548 549 550 551 552 553 554 555 556
  }

  // update
  emit updateView();
}

//-----------------------------------------------------------------------------


Jan Möbius's avatar
Jan Möbius committed
557 558
void PolyLinePlugin::slot_decimate_percent(bool _checked)
{
559

Jan Möbius's avatar
Jan Möbius committed
560 561 562
  // Get decimation value
  int n_active_pl = 0;
  double total_length = 0;
563

Jan Möbius's avatar
Jan Möbius committed
564 565
  // Iterate over all target polylines
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE);
566

Jan Möbius's avatar
Jan Möbius committed
567 568 569 570 571
  for (; o_it != PluginFunctions::objectsEnd(); ++o_it) {
    // Add polyline length
    total_length += PluginFunctions::polyLineObject(*o_it)->line()->length();
    ++n_active_pl;
  }
572

Jan Möbius's avatar
Jan Möbius committed
573
  double v = total_length / double(n_active_pl);
574

Jan Möbius's avatar
Jan Möbius committed
575
  if (_checked) {
576

Jan Möbius's avatar
Jan Möbius committed
577 578 579 580
    // Compute current absolute length to relative portion
    if (n_active_pl > 0) {
      double val_new = tool_->dsb_subdivide->value() / v;
      tool_->dsb_decimate->setValue(val_new);
581
    } else {
Jan Möbius's avatar
Jan Möbius committed
582 583 584
      emit log(LOGWARN, "Could not find any active polyline!");
    }
  } else {
585

Jan Möbius's avatar
Jan Möbius committed
586 587 588 589 590 591
    // Compute relative portion to absolute value
    if (n_active_pl > 0) {
      double val_new = tool_->dsb_subdivide->value() * v;
      tool_->dsb_decimate->setValue(val_new);
    } else {
      emit log(LOGWARN, "Could not find any active polyline!");
592
    }
Jan Möbius's avatar
Jan Möbius committed
593
  }
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
}


//-----------------------------------------------------------------------------

#ifdef EXTENDED_POLY_LINE

void
PolyLinePlugin::
slot_resample_on_edges()
{
  // count target meshes
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_TRIANGLE_MESH );
  unsigned int n_targetMeshes(0);
  for(; o_it != PluginFunctions::objectsEnd(); ++o_it)
    ++n_targetMeshes;

  if( n_targetMeshes != 1)
  {
    std::cerr << "Warning: resample_on_edges needs exactly 1 target mesh! Otherwise no operation is performed! \n";
    return;
  }

  // take first target mesh
  o_it = PluginFunctions::ObjectIterator(PluginFunctions::TARGET_OBJECTS, DATA_TRIANGLE_MESH );

  if(o_it != PluginFunctions::objectsEnd())
  {
    // get pointer to TriMeshObject
    TriMeshObject* tmesh_obj = PluginFunctions::triMeshObject(*o_it);

    // get pointer to mesh
    TriMesh* mesh = tmesh_obj->mesh();

    // get pointer to triangle bsp
    OpenMeshTriangleBSPT< TriMesh >* tbsp = tmesh_obj->requestTriangleBsp();
    //    OpenMeshTriangleBSPT< TriMesh >* tbsp = 0;

    // iterate over all target polylines
    PluginFunctions::ObjectIterator o_it2(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE );

Jan Möbius's avatar
Jan Möbius committed
635
    for (; o_it2 != PluginFunctions::objectsEnd(); ++o_it2)
636
    {
637
      std::cerr << "resample " << o_it2->name().toStdString() << std::endl;
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
      // decimate polyline
      PluginFunctions::polyLineObject(*o_it2)->line()->resample_on_mesh_edges( *mesh, tbsp );
    }

    // update
    emit updateView();
  }
}

#endif

//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slot_smooth()
{
  // iterate over all target polylines
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE );

  for (  ; o_it != PluginFunctions::objectsEnd(); ++o_it)
  {
    for( int i=0; i<tool_->sb_smooth_iter->value(); ++i)
      if( tool_->rb_smooth_c0->isChecked())
	// smooth polyline C0
	PluginFunctions::polyLineObject(*o_it)->line()->smooth_uniform_laplace();
      else
	if( tool_->rb_smooth_c1->isChecked())
	  // smooth polyline C1
	  PluginFunctions::polyLineObject(*o_it)->line()->smooth_uniform_laplace2();
	else
	  if( tool_->rb_smooth_c2->isChecked())
	    // smooth polyline C2
	    PluginFunctions::polyLineObject(*o_it)->line()->smooth_uniform_laplace3();

    emit updatedObject( o_it->id(), UPDATE_GEOMETRY);
  }

}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slot_smooth( PolyLineObject*& _pol)
{
  // if polyline object is valid
Jan Möbius's avatar
Jan Möbius committed
688 689 690
  if (_pol) {
    for (int i = 0; i < tool_->sb_smooth_iter->value(); ++i)
      if (tool_->rb_smooth_c0->isChecked())
691 692
        // smooth polyline C0
        _pol->line()->smooth_uniform_laplace();
Jan Möbius's avatar
Jan Möbius committed
693 694 695 696 697 698 699 700
      else if (tool_->rb_smooth_c1->isChecked())
        // smooth polyline C1
        _pol->line()->smooth_uniform_laplace2();
      else if (tool_->rb_smooth_c2->isChecked())
        // smooth polyline C2
        _pol->line()->smooth_uniform_laplace3();

    emit updatedObject(_pol->id(), UPDATE_GEOMETRY);
701 702 703 704 705 706 707 708 709 710 711
  }
}

//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slot_project()
{
  // create meshe and bsp vectors
Jan Möbius's avatar
Jan Möbius committed
712 713
  std::vector<TriMesh*> meshes;
  std::vector<OpenMeshTriangleBSPT<TriMesh>*> bsps;
714

Jan Möbius's avatar
Jan Möbius committed
715
  PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_TRIANGLE_MESH);
716

Jan Möbius's avatar
Jan Möbius committed
717
  for (; o_it != PluginFunctions::objectsEnd(); ++o_it) {
718 719 720 721
    // get pointer to TriMeshObject
    TriMeshObject* tmesh_obj = PluginFunctions::triMeshObject(*o_it);

    // save meshes and bsps
Jan Möbius's avatar
Jan Möbius committed
722
    meshes.push_back(tmesh_obj->mesh());
723
    //    bsps.push_back(0);
Jan Möbius's avatar
Jan Möbius committed
724
    bsps.push_back(tmesh_obj->requestTriangleBsp());
725 726 727
  }

  // iterate over all target polylines
Jan Möbius's avatar
Jan Möbius committed
728
  PluginFunctions::ObjectIterator o_it2(PluginFunctions::TARGET_OBJECTS, DATA_POLY_LINE);
729

Jan Möbius's avatar
Jan Möbius committed
730
  for (; o_it2 != PluginFunctions::objectsEnd(); ++o_it2) {
731
    // project polyline
Jan Möbius's avatar
Jan Möbius committed
732 733
    PluginFunctions::polyLineObject(*o_it2)->line()->project_to_mesh(meshes, &bsps);
    emit updatedObject(o_it2->id(), UPDATE_GEOMETRY);
734 735 736 737 738 739 740 741 742 743 744 745
  }
}


//-----------------------------------------------------------------------------


void 
PolyLinePlugin::
slot_project( PolyLineObject*& _pol)
{
  // check if polyline ok
Jan Möbius's avatar
Jan Möbius committed
746 747
  if (_pol) {

748
    // create meshes and bsp vectors
Jan Möbius's avatar
Jan Möbius committed
749 750
    std::vector<TriMesh*> meshes;
    std::vector<OpenMeshTriangleBSPT<TriMesh>*> bsps;
751

Jan Möbius's avatar
Jan Möbius committed
752 753 754
    PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DATA_TRIANGLE_MESH);

    for (; o_it != PluginFunctions::objectsEnd(); ++o_it) {
755 756 757 758 759

      // get pointer to TriMeshObject
      TriMeshObject* tmesh_obj = PluginFunctions::triMeshObject(*o_it);

      // save meshes and bsps
Jan Möbius's avatar
Jan Möbius committed
760
      meshes.push_back(tmesh_obj->mesh());
761
      //    bsps.push_back(0);
Jan Möbius's avatar
Jan Möbius committed
762
      bsps.push_back(tmesh_obj->requestTriangleBsp());
763 764 765
    }

    // only project given PolyLine
Jan Möbius's avatar
Jan Möbius committed
766
    _pol->line()->project_to_mesh(meshes, &bsps);
767 768 769 770 771 772 773 774 775 776 777 778 779 780
  }

}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slot_smooth_project()
{
  int smooth_project_iter = tool_->sb_smooth_project_iter->value();

Jan Möbius's avatar
Jan Möbius committed
781 782
  for (int i = 0, j = 1; i < smooth_project_iter; ++i, ++j) {

783 784 785 786 787
    // n smoothing steps
    slot_smooth();

    // projection step
    slot_project();
Jan Möbius's avatar
Jan Möbius committed
788

789 790 791 792 793 794 795 796 797
  }
}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
Jan Möbius's avatar
Jan Möbius committed
798
slot_smooth_project(PolyLineObject*& _pol)
799 800 801
{
  int smooth_project_iter = tool_->sb_smooth_project_iter->value();

Jan Möbius's avatar
Jan Möbius committed
802 803
  for (int i = 0, j = 1; i < smooth_project_iter; ++i, ++j) {

804
    // n smoothing steps
Jan Möbius's avatar
Jan Möbius committed
805
    slot_smooth(_pol);
806 807

    // projection step
Jan Möbius's avatar
Jan Möbius committed
808 809
    slot_project(_pol);

810 811 812 813 814 815 816 817 818 819 820 821 822
  }
}


//-----------------------------------------------------------------------------


void
PolyLinePlugin::
slot_smart_move_timer()
{
  int smooth_project_iter = tool_->sb_smooth_project_iter->value();

Jan Möbius's avatar
Jan Möbius committed
823 824 825 826
  if (smooth_project_iter)
    slot_smooth_project(cur_smart_move_obj_);
  else // only smooth
    slot_smooth(cur_smart_move_obj_);
827 828 829 830

  emit updateView();
}

831
//-----------------------------------------------------------------------------
832

833
void
834
PolyLinePlugin::slotObjectUpdated( int _identifier, const UpdateType &_type )
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
{
    PolyLineObject* lineObject = 0;
    if(!PluginFunctions::getObject(_identifier, lineObject))
        return;
    PolyLineCircleData* circleData = dynamic_cast<PolyLineCircleData*>(lineObject->objectData(CIRCLE_DATA));
    PolyLineBezierSplineData* splineData = dynamic_cast<PolyLineBezierSplineData*>(lineObject->objectData(BEZSPLINE_DATA));
    GlutPrimitiveNode* H = 0, *C = 0;
    lineObject->getAdditionalNode(C,  name(), "circle");
    lineObject->getAdditionalNode(H, name(), "control", 0);
    if(circleData && !C) {
        createCircle_createUI(_identifier);
    }
    else if(splineData && !H) {
        createSpline_createUI(_identifier);
    }
}
851

852 853
//-----------------------------------------------------------------------------

854 855 856 857
PolyLinePlugin::EditMode
PolyLinePlugin::
mode()
{
858 859 860
  if(copyPaste_ObjectId_ != -1 && copyPaste_ActionType_ != -1)//no matter what tool is selected
      return PL_COPY_PASTE;

861 862 863
  if(tool_ )
  {
    if( tool_->rb_insert->isChecked()         ) return PL_INSERT;
864
    if( tool_->rb_InsertCircle->isChecked()	  ) return PL_INSERTCIRCLE;
865
    if( tool_->rb_InsertSpline->isChecked()	  ) return PL_INSERTSPLINE;
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
    else if( tool_->rb_delete->isChecked()    ) return PL_DELETE;
    else if( tool_->rb_move->isChecked()      ) return PL_MOVE;
    else if( tool_->rb_split->isChecked()     ) return PL_SPLIT;
    else if( tool_->rb_merge->isChecked()     ) return PL_MERGE;
    else if( tool_->rb_smart_move->isChecked()) return PL_SMART_MOVE;
  }

  return PL_NONE;
}

//-----------------------------------------------------------------------------

void
PolyLinePlugin::
me_insert( QMouseEvent* _event )
{
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
    if (_event->type() == QEvent::MouseMove) {
        if (create_point_ref_) {
            // Pick position
            unsigned int node_idx, target_idx;
            ACG::Vec3d hit_point;
            if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(), node_idx, target_idx, &hit_point)) {
                *create_point_ref_ = (PolyLine::Point) hit_point;

                // update
                emit updatedObject(cur_insert_id_, UPDATE_GEOMETRY);
            }
        }
        return;
    }

897
  // Pick position
Jan Möbius's avatar
Jan Möbius committed
898 899 900
  unsigned int node_idx, target_idx;
  ACG::Vec3d hit_point;
  if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(), node_idx, target_idx, &hit_point)) {
901

Jan Möbius's avatar
Jan Möbius committed
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
    switch (_event->type()) {
      // insert new point
      case QEvent::MouseButtonPress: {
        // new Polyline?
        if (cur_insert_id_ == -1 || !PluginFunctions::objectRoot()->childExists(cur_insert_id_)) {
          // add new polyline
          emit addEmptyObject(DATA_POLY_LINE, cur_insert_id_);

          // get current polylineobject
          BaseObjectData *obj = 0;
          PluginFunctions::getObject(cur_insert_id_, obj);
          // default: set as target
          obj->target(true);
          // get polyline object
          cur_polyline_obj_ = PluginFunctions::polyLineObject(obj);

          cur_polyline_obj_->materialNode()->set_random_color();
919

920
          cur_polyline_obj_->line()->set_vertex_radius(PluginFunctions::sceneRadius()*0.012);
921 922

          cur_polyline_obj_->lineNode()->drawMode(ACG::SceneGraph::DrawModes::DrawMode::getFromDescription("Points (as Spheres)") | ACG::SceneGraph::DrawModes::WIREFRAME);
923

924 925 926
          cur_polyline_obj_->line()->add_point((PolyLine::Point) hit_point);

          emit showStatusMessage(tr("Double click/Enter to terminate poly line. Use with shift to create loop."));
Jan Möbius's avatar
Jan Möbius committed
927
        }
928

Jan Möbius's avatar
Jan Möbius committed
929 930
        // add new point
        cur_polyline_obj_->line()->add_point((PolyLine::Point) hit_point);
931
        create_point_ref_ = &cur_polyline_obj_->line()->points().back();
932

Jan Möbius's avatar
Jan Möbius committed
933 934
        // update
        emit updatedObject(cur_insert_id_, UPDATE_GEOMETRY | UPDATE_TOPOLOGY);
935

Jan Möbius's avatar
Jan Möbius committed
936 937
        break;
      }
938

Jan Möbius's avatar
Jan Möbius committed
939 940 941 942 943 944
      // finish polyline
      case QEvent::MouseButtonDblClick: {
        // close polyline
        if (_event->modifiers() & (Qt::ShiftModifier)) {
          cur_polyline_obj_->line()->set_closed(true);
        }
945

946 947 948 949
        if (cur_polyline_obj_->line()->n_vertices() >= 2) {
            const PolyLine::Point &p1 = cur_polyline_obj_->line()->point(cur_polyline_obj_->line()->n_vertices() - 1),
                    &p2 = cur_polyline_obj_->line()->point(cur_polyline_obj_->line()->n_vertices() - 2);

950
            cur_polyline_obj_->line()->delete_point(cur_polyline_obj_->line()->n_vertices() - 1);
951 952
        }

Jan Möbius's avatar
Jan Möbius committed
953 954
        // update
        emit updatedObject(cur_insert_id_, UPDATE_GEOMETRY | UPDATE_TOPOLOGY);
955

Jan Möbius's avatar
Jan Möbius committed
956 957 958
        // reset current variables
        cur_insert_id_ = -1;
        cur_polyline_obj_ = 0;
959 960 961
        create_point_ref_ = 0;

        clearStatusMessage();
962

Jan Möbius's avatar
Jan Möbius committed
963
        break;
964 965 966
      }

      default:
Jan Möbius's avatar
Jan Möbius committed
967
        break;
968 969 970 971 972 973
    }
  }
}

//-----------------------------------------------------------------------------

974
double getRad(int meshIdx)
975 976
{
    TriMeshObject* mesh;
977
    if(PluginFunctions::getObject(meshIdx, mesh)) {
978 979 980 981
        //use the size of the mesh to compute the handle radii
        ACG::Vec3d bbMin( FLT_MAX, FLT_MAX, FLT_MAX);
        ACG::Vec3d bbMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
        mesh->boundingBox(bbMin, bbMax);
982
        return 0.005*(bbMax-bbMin).norm();
983 984
    }
    else {
985
        return PluginFunctions::sceneRadius()*0.01;//use double of the above cause it is the radius
986
    }
987 988 989 990 991 992 993 994 995 996 997 998 999
}

void PolyLinePlugin::
createCircle_createUI(int _polyLineObjectID)
{
    PolyLineObject* lineObject;
    if (!PluginFunctions::getObject(_polyLineObjectID, lineObject) || !lineObject->objectData(CIRCLE_DATA))
        return;
    PolyLineCircleData* lineData = dynamic_cast<PolyLineCircleData*>(lineObject->objectData(CIRCLE_DATA));

    lineObject->setObjectDrawMode(ACG::SceneGraph::DrawModes::WIREFRAME);

    double rad = getRad(lineData->circleMeshIndex_);
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027

    GlutPrimitiveNode* handle0 = new GlutPrimitiveNode(lineObject, "N_Handle0");
    handle0->get_primitive(0).color = ACG::Vec4f(1,0,0,1);
    handle0->set_size(rad);
    handle0->show();
    handle0->enablePicking(true);
    handle0->set_position(lineData->circleCenter_ + lineData->circleMainAxis_ * lineData->circleMainRadius_);
    lineObject->addAdditionalNode(handle0, name(), "handle0");
    handle0->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);

    GlutPrimitiveNode* handle1 = new GlutPrimitiveNode(lineObject, "N_Handle1");
    handle1->get_primitive(0).color = ACG::Vec4f(0,1,0,1);
    handle1->set_size(rad);
    handle1->show();
    handle1->enablePicking(true);
    handle1->set_position(lineData->circleCenter_ + lineData->circleSideAxis_ * lineData->circleSideRadius_);
    lineObject->addAdditionalNode(handle1, name(), "handle1");
    handle1->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);

    GlutPrimitiveNode* cenNode = new GlutPrimitiveNode(lineObject, "N_Center");
    cenNode->get_primitive(0).color = ACG::Vec4f(0,0,1,1);
    cenNode->set_size(rad);
    cenNode->show();
    cenNode->enablePicking(true);
    cenNode->set_position(lineData->circleCenter_);
    lineObject->addAdditionalNode(cenNode, name(), "circle");
    cenNode->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);

1028
    emit updatedObject(_polyLineObjectID, UPDATE_ALL);
1029 1030
}

1031 1032
//-----------------------------------------------------------------------------

1033 1034 1035 1036 1037 1038 1039
void PolyLinePlugin::
me_insertCircle(QMouseEvent* _event)
{
	TriMeshObject* mesh;
	TriMesh::FaceHandle fh;
	TriMesh::VertexHandle vh;
	ACG::Vec3d hit_point;
1040 1041
	if(!pick_triangle_mesh(_event->pos(), mesh, fh, vh, hit_point) && _event->type() != QEvent::MouseButtonRelease)
	    return;//can't generate a circle in empty space
1042 1043

	if(_event->type() == QEvent::MouseMove && createCircle_CurrSelIndex_ != -1) {
Jan Möbius's avatar
Jan Möbius committed
1044
		PolyLineObject* lineObject = 0;
1045
		if(!PluginFunctions::getObject(createCircle_CurrSelIndex_, lineObject) || !lineObject->objectData(CIRCLE_DATA))
1046
			return;
1047
        PolyLineCircleData* circleData = dynamic_cast<PolyLineCircleData*>(lineObject->objectData(CIRCLE_DATA));
1048

1049
		const ACG::Vec3d n = circleData->circleNormal_, x0 = circleData->circleCenter_;
Jan Möbius's avatar
Jan Möbius committed
1050
		const double t = ((n | x0) - (n | hit_point)) / n.sqrnorm();
1051 1052
		const ACG::Vec3d onPlane = hit_point + t * n,  d = onPlane - x0;

1053 1054 1055 1056
        circleData->circleMainAxis_ = (onPlane - x0).normalize();
        circleData->circleSideRadius_  = circleData->circleMainRadius_ = d.norm();
        circleData->circleSideAxis_ = (circleData->circleMainAxis_ % n).normalize();

Jan Möbius's avatar
Jan Möbius committed
1057 1058
		updatePolyEllipse(lineObject, tool_->sb_CirclePointNum->value());
		updateHandles(lineObject);
1059
	}
1060 1061 1062
	else if(_event->type() == QEvent::MouseButtonPress && createCircle_CurrSelIndex_ == -1) {
		int new_line_id;
	    emit addEmptyObject(DATA_POLY_LINE, new_line_id);
1063
		BaseObjectData *obj = 0;
1064
		PluginFunctions::getObject(new_line_id, obj);
1065
		obj->target(true);
1066 1067
		PolyLineObject* newLine = PluginFunctions::polyLineObject(obj);
		newLine->materialNode()->set_random_color();
1068

1069 1070
		newLine->line()->set_vertex_radius(PluginFunctions::sceneRadius()*0.012);

1071 1072
		if(!mesh->mesh()->has_face_normals())
			mesh->mesh()->request_face_normals();
1073 1074 1075

        PolyLineCircleData* lineData = new PolyLineCircleData(hit_point, mesh->mesh()->normal(fh), ACG::Vec3d(), ACG::Vec3d(), 0, 0, mesh->id());
        newLine->setObjectData(CIRCLE_DATA, lineData);
Jan Möbius's avatar
Jan Möbius committed
1076
        newLine->setObjectDrawMode(ACG::SceneGraph::DrawModes::WIREFRAME);
1077 1078

		emit updatedObject(new_line_id, UPDATE_GEOMETRY | UPDATE_TOPOLOGY);
Jan Möbius's avatar
Jan Möbius committed
1079
		createCircle_LastSelIndex_ = createCircle_CurrSelIndex_ = newLine->id();
1080
		createSpline_LastSelIndex_ = -1;
1081
	}
1082 1083 1084 1085 1086 1087 1088 1089
	else if(_event->type() == QEvent::MouseButtonRelease ) {
	    PolyLineObject* lineObject;
		if(PluginFunctions::getObject(createCircle_CurrSelIndex_, lineObject))
		{
            ACG::SceneGraph::GlutPrimitiveNode* N;
            if(lineObject->getAdditionalNode(N, name(), "handle0"))
                N->enablePicking(true);
		}
1090
		createCircle_CurrSelIndex_ = -1;
1091 1092 1093
	}
}

1094
//-------------------------------------------OpenFLipper/BasePlugin/----------------------------------
1095

1096 1097 1098 1099 1100 1101 1102 1103
void PolyLinePlugin::
createSpline_createUI(int _polyLineObjectID)
{
    PolyLineObject* lineObject;
    if (!PluginFunctions::getObject(_polyLineObjectID, lineObject) || !lineObject->objectData(BEZSPLINE_DATA))
        return;
    PolyLineBezierSplineData* splineData = dynamic_cast<PolyLineBezierSplineData*>(lineObject->objectData(BEZSPLINE_DATA));

1104 1105 1106
    lineObject->setObjectDrawMode(ACG::SceneGraph::DrawModes::WIREFRAME);

    double rad = getRad(splineData->meshIndex_);
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135

    GlutLineNode* lineN = new GlutLineNode(lineObject, "N_Line");
    lineN->show();
    lineN->enablePicking(false);
    lineObject->addAdditionalNode(lineN, name(), "line");
    lineN->drawMode(ACG::SceneGraph::DrawModes::WIREFRAME);

    for(unsigned int i = 0; i < splineData->points_.size(); i++) {
        GlutPrimitiveNode* handle0 = new GlutPrimitiveNode(lineObject, "N_Control", i);
        handle0->get_primitive(0).color = ACG::Vec4f(0,1,0,1);
        handle0->set_size(rad);
        handle0->show();
        handle0->set_position(splineData->points_[i].position);
        handle0->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);
        handle0->enablePicking(true);
        lineObject->addAdditionalNode(handle0, name(), "control", i);
    }

    for(unsigned int i = 0; i < splineData->handles_.size(); i++) {
        const PolyLineBezierSplineData::InterpolatePoint& control = splineData->getInterpolatePoint(i);
        const ACG::Vec3d hndlPos = splineData->handles_[i], ctrlPos = control.position;

        GlutPrimitiveNode* handle0 = new GlutPrimitiveNode(lineObject, "N_Handle", i);
        handle0->get_primitive(0).color = ACG::Vec4f(0,0,1,1);
        handle0->set_size(rad * 0.75);
        handle0->show();
        handle0->enablePicking(true);
        handle0->set_position(hndlPos);
        lineObject->addAdditionalNode(handle0, name(), "handle", i);
1136
        handle0->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);
1137 1138 1139 1140 1141 1142 1143 1144 1145

        GlutLineNode* lineN;
        if(lineObject->getAdditionalNode(lineN,  name(), "line")) {
            lineN->add_line(ctrlPos, hndlPos);
            lineN->add_color(ACG::Vec4f(1,0,0,1));
        }
    }
    updatePolyBezierSpline(lineObject, tool_->sb_SplineSegNum->value());

1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
    emit updatedObject(_polyLineObjectID, UPDATE_ALL);
}

void
PolyLinePlugin::
finishSpline()
{
    PolyLineObject* lineObject = 0;

    if(!PluginFunctions::getObject(createSpline_CurrSelIndex_, lineObject))
        return;

Jan Möbius's avatar
Jan Möbius committed
1158

1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
    PolyLineBezierSplineData* splineData = dynamic_cast<PolyLineBezierSplineData*>(lineObject->objectData(BEZSPLINE_DATA));

    TriMeshObject* mesh;
    if(!PluginFunctions::getObject(splineData->meshIndex_, mesh))
        return;

    ACG::Vec3d bbMin( FLT_MAX, FLT_MAX, FLT_MAX);
    ACG::Vec3d bbMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
    mesh->boundingBox(bbMin, bbMax);
    const ACG::Vec3d sizeBB((bbMax-bbMin));

    if(splineData->finishSpline()) {
Jan Möbius's avatar
Jan Möbius committed
1171 1172 1173

        GlutPrimitiveNode* control = 0;

1174 1175 1176 1177
        for(unsigned int i = 0; i < splineData->points_.size(); i++) {
            lineObject->getAdditionalNode(control, name(), "control", i);
            control->enablePicking(true);
        }
Jan Möbius's avatar
Jan Möbius committed
1178

1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
        for(unsigned int i = 0; i < splineData->handles_.size(); i++) {
            const PolyLineBezierSplineData::InterpolatePoint& control = splineData->getInterpolatePoint(i);
            const ACG::Vec3d hndlPos = splineData->handles_[i], ctrlPos = control.position;

            GlutPrimitiveNode* handle0 = new GlutPrimitiveNode(lineObject, "N_Handle", i);
            handle0->get_primitive(0).color = ACG::Vec4f(0,0,1,1);
            handle0->set_size(0.004*sizeBB.norm());
            handle0->show();
            handle0->enablePicking(true);
            handle0->set_position(hndlPos);
            lineObject->addAdditionalNode(handle0, name(), "handle", i);
            handle0->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);

            GlutLineNode* lineN;
            if(lineObject->getAdditionalNode(lineN,  name(), "line")) {
                lineN->add_line(ctrlPos, hndlPos);
                lineN->add_color(ACG::Vec4f(1,0,0,1));
            }

            emit updatedObject(createSpline_CurrSelIndex_, UPDATE_ALL);
        }
        updatePolyBezierSpline(lineObject, tool_->sb_SplineSegNum->value());
        createSpline_CurrSelIndex_ = -1;
    }
1203 1204
}

1205 1206 1207
void PolyLinePlugin::
me_insertSpline(QMouseEvent* _event)
{
Jan Möbius's avatar
Jan Möbius committed
1208

1209 1210 1211 1212
	TriMeshObject* mesh;
	TriMesh::FaceHandle fh;
	TriMesh::VertexHandle vh;
	ACG::Vec3d hit_point;
Jan Möbius's avatar
Jan Möbius committed
1213

1214 1215
	if(!pick_triangle_mesh(_event->pos(), mesh, fh, vh, hit_point))
		return;//can't generate a circle in empty space
Jan Möbius's avatar
Jan Möbius committed
1216

1217 1218 1219 1220
    ACG::Vec3d bbMin( FLT_MAX, FLT_MAX, FLT_MAX);
    ACG::Vec3d bbMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
    mesh->boundingBox(bbMin, bbMax);
    const ACG::Vec3d sizeBB((bbMax-bbMin));
Jan Möbius's avatar
Jan Möbius committed
1221

1222 1223
	if(!mesh->mesh()->has_face_normals())
		mesh->mesh()->request_face_normals();
Jan Möbius's avatar
Jan Möbius committed
1224

1225
	ACG::Vec3d nor = mesh->mesh()->normal(fh);
Jan Möbius's avatar
Jan Möbius committed
1226

1227
	if(_event->type() == QEvent::MouseButtonPress) {
Jan Möbius's avatar
Jan Möbius committed
1228

Jan Möbius's avatar
Jan Möbius committed
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
	  if(createSpline_CurrSelIndex_ == -1) {

	    emit addEmptyObject(DATA_POLY_LINE, createSpline_CurrSelIndex_);
	    createSpline_LastSelIndex_ = createSpline_CurrSelIndex_;
	    createCircle_LastSelIndex_ = -1;
	    BaseObjectData *obj = 0;
	    PluginFunctions::getObject(createSpline_CurrSelIndex_, obj);
	    obj->target(true);
	    PolyLineObject* newLine = PluginFunctions::polyLineObject(obj);
	    newLine->materialNode()->set_random_color();
1239 1240
	    newLine->line()->set_vertex_radius(PluginFunctions::sceneRadius()*0.012);

Jan Möbius's avatar
Jan Möbius committed
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
	    newLine->lineNode()->drawMode(ACG::SceneGraph::DrawModes::WIREFRAME);
	    emit updatedObject(createSpline_CurrSelIndex_, UPDATE_GEOMETRY | UPDATE_TOPOLOGY);

	    PolyLineBezierSplineData* lineData = new PolyLineBezierSplineData(mesh->id());
	    newLine->setObjectData(BEZSPLINE_DATA, lineData);

	    GlutLineNode* lineN = new GlutLineNode(newLine, "N_Line");
	    lineN->show();
	    lineN->enablePicking(false);
	    newLine->addAdditionalNode(lineN, name(), "line");
	    lineN->drawMode(ACG::SceneGraph::DrawModes::WIREFRAME);
	  }

	  ACG::Vec3d insert_Point = hit_point + nor * 0.003 * sizeBB.norm();
	  PolyLineObject* lineObject = 0;

	  if(!PluginFunctions::getObject(createSpline_CurrSelIndex_, lineObject))
	    return;

	  PolyLineBezierSplineData* splineData = dynamic_cast<PolyLineBezierSplineData*>(lineObject->objectData(BEZSPLINE_DATA));
	  GlutPrimitiveNode* handle0 = new GlutPrimitiveNode(lineObject, "N_Control", splineData->points_.size());
	  handle0->get_primitive(0).color = ACG::Vec4f(0,1,0,1);
	  handle0->set_size(0.005*sizeBB.norm());
	  handle0->show();
	  handle0->set_position(insert_Point);
	  handle0->drawMode(ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED);
1267 1268
	  handle0->enablePicking(false);
	  lineObject->addAdditionalNode(handle0, name(), "control", splineData->points_.size());
1269

1270 1271
	  emit updatedObject(createSpline_CurrSelIndex_, UPDATE_ALL);
	  splineData->addInterpolatePoint(insert_Point, nor);
Jan Möbius's avatar
Jan Möbius committed
1272

1273 1274
	}
	if(_event->type() == QEvent::MouseButtonDblClick) {
1275
	    finishSpline();
1276 1277 1278 1279 1280
	}
}

//-----------------------------------------------------------------------------

1281 1282 1283 1284 1285
void
PolyLinePlugin::
me_delete( QMouseEvent* _event )
{
  // MousePress ?
Jan Möbius's avatar
Jan Möbius committed
1286 1287 1288 1289
  if (_event->type() == QEvent::MouseButtonPress) {

    unsigned int node_idx, target_idx;
    ACG::Vec3d hit_point;
1290
    // pick
Jan Möbius's avatar
Jan Möbius committed
1291 1292
    if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_ANYTHING, _event->pos(), node_idx, target_idx, &hit_point)) {

1293
      BaseObjectData* obj = 0;
Jan Möbius's avatar
Jan Möbius committed
1294 1295

      if (PluginFunctions::getPickedObject(node_idx, obj))
1296
        // is picked object polyline?
Jan Möbius's avatar
Jan Möbius committed
1297
        if (PluginFunctions::polyLineObject(obj)) {
1298 1299 1300
          emit deleteObject(obj->id());
        }
    }
Jan Möbius's avatar
Jan Möbius committed
1301

1302
  }
Jan Möbius's avatar
Jan Möbius committed
1303

1304 1305 1306 1307 1308
}


//-----------------------------------------------------------------------------

1309 1310 1311 1312 1313
namespace {

bool me_GetMeshHit(QMouseEvent* _event, ACG::SceneGraph::GlutPrimitiveNode* moveCircle_SelNode_, ACG::Vec3d& _hit_point, unsigned int& _node_idx, unsigned int& _targetIdx)
{
    unsigned int ndx;
1314 1315
    if(moveCircle_SelNode_)
        moveCircle_SelNode_->enablePicking(false);
1316
    bool hasHit = PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_FACE, _event->pos(), ndx, _targetIdx, &_hit_point);
1317 1318
    if(moveCircle_SelNode_)
        moveCircle_SelNode_->enablePicking(true);
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
    BaseObjectData* obj;
    //if there is no current mesh use the newly found
    if(hasHit && PluginFunctions::getPickedObject(ndx, obj) && _node_idx == std::numeric_limits<unsigned int>::max())
        _node_idx = obj->id();
    return hasHit;
}

void me_UpdateCircleData(ACG::Vec3d _hit_point, ACG::Vec3d _onPlane, ACG::Vec3d _nor, ACG::SceneGraph::GlutPrimitiveNode* _moveCircle_SelNode_, PolyLineCircleData* _lineData, bool _isShift)
{
    if(!_moveCircle_SelNode_->name().compare("N_Center"))
    {
         _lineData->circleNormal_ = _nor;
         _lineData->circleCenter_ = _hit_point;
         _lineData->circleSideAxis_ = (_lineData->circleMainAxis_ % _lineData->circleNormal_).normalize();
         _lineData->circleMainAxis_ = (_lineData->circleNormal_ % _lineData->circleSideAxis_).normalize();
    }
    else
    {
        const double cr = (_onPlane - _lineData->circleCenter_).norm();
        const ACG::Vec3d axisa = (_onPlane - _lineData->circleCenter_).normalize();
        if (!_moveCircle_SelNode_->name().compare("N_Handle0")) {
            const ACG::Vec3d axisb = (axisa % _lineData->circleNormal_).normalize();
            _lineData->circleMainRadius_ = cr;

            if (_isShift)
                _lineData->circleSideRadius_ = cr;

            _lineData->circleMainAxis_ = axisa;
            _lineData->circleSideAxis_ = axisb;
        } else {
            const ACG::Vec3d axisb = (_lineData->circleNormal_ % axisa).normalize();
            _lineData->circleSideRadius_ = cr;

            if (_isShift)
                _lineData->circleMainRadius_ = cr;

            _lineData->circleSideAxis_ = axisa;
            _lineData->circleMainAxis_ = axisb;
        }
    }
}
}
1361 1362 1363 1364 1365

void
PolyLinePlugin::
me_move( QMouseEvent* _event )
{
1366
  if((_event->modifiers() & Qt::ShiftModifier) != Qt::ShiftModifier && moveCircle_IsLocked)//alt was pressed but it isn't anymore
1367 1368
        moveCircle_IsLocked = moveCircle_IsFloating = false;

1369
  // MousePress ? -> get reference point
Jan Möbius's avatar
Jan Möbius committed
1370 1371 1372 1373
  if (_event->type() == QEvent::MouseButtonPress) {

    unsigned int node_idx, target_idx;
    ACG::Vec3d hit_point;
1374
    // this is for picking the handles on a circle or spline
1375
    if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_ANYTHING, _event->pos(), node_idx, target_idx, &hit_point)) {
1376
      BaseNode* node = find_node( PluginFunctions::getRootNode(), node_idx );
1377
      GlutPrimitiveNode* glutNode = dynamic_cast<GlutPrimitiveNode*>(node);
Jan Möbius's avatar
Jan Möbius committed
1378

1379
      if(glutNode) {
Jan Möbius's avatar
Jan Möbius committed
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
        PolyLineCircleData* circleData = dynamic_cast<PolyLineCircleData*>(glutNode->line->objectData(CIRCLE_DATA));
        PolyLineBezierSplineData* splineData = dynamic_cast<PolyLineBezierSplineData*>(glutNode->line->objectData(BEZSPLINE_DATA));
        if(circleData) {
          moveCircle_SelNode_        = glutNode;
          createCircle_LastSelIndex_ = createCircle_CurrSelIndex_ = glutNode->line->id();
        }
        if(splineData) {
          moveBezSpline_SelNode_ = glutNode;
          createSpline_LastSelIndex_ = moveBezSpline_SelIndex_ = glutNode->line->id();
          moveBezSpline_SelSubIndex_ = glutNode->index;
        }
1391
      }
1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
    }
    //this is for picking the normal poly lines
    if (PluginFunctions::scenegraphPick(ACG::SceneGraph::PICK_VERTEX, _event->pos(), node_idx, target_idx, &hit_point)) {
        BaseObjectData* obj = 0;
        if (PluginFunctions::getPickedObject(node_idx, obj)) {
          // is picked object polyline?
          PolyLineObject* cur_pol = PluginFunctions::polyLineObject(obj);
          if (cur_pol && !cur_pol->objectData(CIRCLE_DATA) && !cur_pol->objectData(BEZSPLINE_DATA)) {//no vertex dragging on circles!

            // Check if we got a line segment or a vertex
            if ( target_idx >= cur_pol->line()->n_vertices() )
              return;
1404

1405 1406
            // save references
            cur_move_id_ = cur_pol->id();
1407

1408 1409
            move_point_ref_ = &(cur_pol->line()->point(target_idx));
          }
Jan Möbius's avatar
Jan Möbius committed
1410
        }
1411 1412 1413 1414
    }
  }

  // Move ? -> move reference point
1415
  if (_event->type() == QEvent::MouseMove){
1416
    if (moveCircle_SelNode_) {
Jan Möbius's avatar
Jan Möbius committed
1417
        PolyLineObject* lineObject;
1418 1419
        if (!PluginFunctions::getObject(createCircle_CurrSelIndex_, lineObject) || !lineObject->objectData(CIRCLE_DATA))
            return;
Jan Möbius's avatar
Jan Möbius committed
1420
        PolyLineCircleData* lineData = dynamic_cast<PolyLineCircleData*>(lineObject->objectData(CIRCLE_DATA));
1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
        ACG::Vec3d hit_point;
        unsigned int target_idx;
        bool hasHit = me_GetMeshHit(_event, moveCircle_SelNode_, hit_point, lineData->circleMeshIndex_, target_idx);
        if(lineData->circleMeshIndex_ == std::numeric_limits<unsigned int>::max()) return;
        if(!moveCircle_IsLocked && hasHit) {
            moveCircle_IsFloating = false;
            ACG::Vec3d x0 = lineData->circleCenter_, n = lineData->circleNormal_;
            ACG::Vec3d onMesh = hit_point;
            double t = ((n | x0) - (n | onMesh)) / n.sqrnorm();
            ACG::Vec3d onPlane = onMesh + t * n;
            TriMeshObject* mesh;
            if (!PluginFunctions::getObject(lineData->circleMeshIndex_, mesh))
                return;

            me_UpdateCircleData(onMesh, onPlane, mesh->mesh()->normal(mesh->mesh()->face_handle(target_idx)), moveCircle_SelNode_, lineData, (_event->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier);
            if (!moveCircle_SelNode_->name().compare("N_Center")) {
                moveCircle_LastHitPos_ = onMesh;
1438
                if((_event->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier) {
1439 1440 1441 1442
                    moveCircle_IsLocked = true;
                    moveCircle_LastHitNor_ = lineData->circleNormal_;
                }
            }
1443
        }
1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
        else
        {
            moveCircle_IsFloating = true;
            ACG::Vec3d cameraPos, cameraDir;
            int l,b,w,h;
            PluginFunctions::viewerProperties(0).glState().get_viewport(l,b,w,h);
            PluginFunctions::viewerProperties(0).glState().viewing_ray(_event->pos().x(), h - 1 - _event->pos().y(), cameraPos, cameraDir);
            //intersect the camera ray with a plane located at moveCircle_LastHitPos_ facing moveCircle_LastHitNor_
            const ACG::Vec3d x0 = !moveCircle_SelNode_->name().compare("N_Center") ? moveCircle_LastHitPos_<