54 #include "PolyLinePlugin.hh" 56 #include <ACG/Scenegraph/ManipulatorNode.hh> 57 #include <ACG/Scenegraph/LineNode.hh> 58 #include <ACG/Scenegraph/GlutPrimitiveNode.hh> 59 #include <ACG/QtScenegraph/QtTranslationManipulatorNode.hh> 67 GlutPrimitiveNode(
PolyLineObject* L, std::string name,
int _index = -1)
86 :
ACG::
SceneGraph::LineNode(LineSegmentsMode, L->manipulatorNode(), name)
108 pickToolBarActions_(0),
110 insertCircleAction_(0),
111 insertSplineAction_(0),
118 cutMultipleAction_(0),
120 cur_polyline_obj_(0),
123 create_point_ref_(0),
124 createCircle_CurrSelIndex_(-1),
125 createCircle_LastSelIndex_(-1),
126 moveCircle_SelNode_(0),
127 moveCircle_IsLocked(false),
128 moveCircle_IsFloating(false),
129 copyPaste_Action_(0),
130 copyPaste_ObjectId_(-1),
131 copyPaste_ActionType_(-1),
132 copyPaste_NewObjectId_(-1),
133 createSpline_CurrSelIndex_(-1),
134 createSpline_LastSelIndex_(-1),
135 moveBezSpline_SelNode_(0),
136 moveBezSpline_SelIndex_(-1),
137 moveBezSpline_SelSubIndex_(0),
139 smart_move_timer_(0),
140 cur_smart_move_obj_(0),
152 QSize size(100, 100);
154 tool_->setObjectName(
"PolyLineToolbar");
157 connect(
tool_->pb_subdivide,SIGNAL(clicked() ),
this,SLOT(slot_subdivide()));
158 connect(
tool_->subdivide_relative,SIGNAL(toggled(
bool) ),
this,SLOT(slot_subdivide_percent(
bool)));
159 connect(
tool_->pb_decimate,SIGNAL(clicked() ),
this,SLOT(slot_decimate()));
160 connect(
tool_->decimate_relative,SIGNAL(toggled(
bool) ),
this,SLOT(slot_decimate_percent(
bool)));
161 #ifdef EXTENDED_POLY_LINE 162 connect(
tool_->pb_resample_on_edges,SIGNAL(clicked() ),
this,SLOT(slot_resample_on_edges()));
164 tool_->pb_resample_on_edges->setDisabled(
true);
166 connect(
tool_->pb_smooth,SIGNAL(clicked() ),
this,SLOT(slot_smooth()));
167 connect(
tool_->pb_smooth,SIGNAL(clicked() ),
this,SIGNAL(updateView()));
168 connect(
tool_->pb_project,SIGNAL(clicked() ),
this,SLOT(slot_project()));
169 connect(
tool_->pb_project,SIGNAL(clicked() ),
this,SIGNAL(updateView()));
170 connect(
tool_->pb_smooth_project,SIGNAL(clicked() ),
this,SLOT(slot_smooth_project()));
171 connect(
tool_->pb_smooth_project,SIGNAL(clicked() ),
this,SIGNAL(updateView()));
173 connect(
tool_->rb_insert, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
174 connect(
tool_->rb_InsertCircle, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
175 connect(
tool_->rb_InsertSpline, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
176 connect(
tool_->rb_delete, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
177 connect(
tool_->rb_move, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
178 connect(
tool_->rb_smart_move, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
179 connect(
tool_->rb_merge, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
180 connect(
tool_->rb_split, SIGNAL( clicked() ),
this, SLOT( slotEditModeChanged() ));
182 connect(
tool_->sb_CirclePointNum, SIGNAL(valueChanged(
int)),
this, SLOT(slot_setCirclePointNum(
int)));
183 connect(
tool_->sb_SplineSegNum, SIGNAL(valueChanged(
int)),
this, SLOT(slot_setSplinePointNum(
int)));
185 tool_->rb_insert->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_insert.png") );
186 tool_->rb_InsertCircle->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_circle.png") );
187 tool_->rb_InsertSpline->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_bezier.png") );
188 tool_->rb_delete->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_delete.png") );
189 tool_->rb_move->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_move.png") );
190 tool_->rb_smart_move->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_move.png") );
191 tool_->rb_merge->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_merge.png") );
192 tool_->rb_split->setIcon( QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"polyline_split.png") );
199 QIcon* toolIcon =
new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"cut_polyline.png");
200 emit addToolbox( tr(
"PolyLine") ,
tool_, toolIcon );
209 slotMouseEvent( QMouseEvent* _event )
211 copyPaste_LastMouse = _event->pos();
214 if (_event->modifiers() & (Qt::ControlModifier))
218 && _event->button() != Qt::RightButton) {
225 case PL_INSERTCIRCLE:
226 me_insertCircle(_event);
229 case PL_INSERTSPLINE:
230 me_insertSpline(_event);
250 me_smart_move(_event);
254 me_copyPasteMouse(_event);
265 void PolyLinePlugin::slotKeyEvent(QKeyEvent* event) {
266 switch (event->key()) {
269 if(mode() == PL_INSERT && cur_polyline_obj_ && cur_insert_id_ != -1) {
272 if (event->modifiers() & (Qt::ShiftModifier))
278 cur_polyline_obj_ = 0;
279 create_point_ref_ = 0;
281 clearStatusMessage();
283 else if(mode() == PL_INSERTSPLINE) {
296 slotPickModeChanged(
const std::string& _mode)
299 cutAction_->setChecked( _mode == CREATE_CUT_POLYLINE );
312 emit addHiddenPickMode(
"PolyLine");
313 emit setPickModeMouseTracking(
"PolyLine",
true);
314 emit addHiddenPickMode( CREATE_CUT_POLYLINE );
315 emit addHiddenPickMode( CREATE_CUT_POLYLINES );
317 emit registerKey(Qt::Key_Return, Qt::NoModifier, tr(
"Terminate creation of poly line."),
true);
318 emit registerKey(Qt::Key_Return, Qt::ShiftModifier, tr(
"Terminate creation of poly line and create loop."),
true);
321 toolbar_ =
new QToolBar(tr(
"PolyLine Editing"));
322 toolbar_->setObjectName(
"PolyLineEditingToolbar");
330 polyLineAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_insert.png") );
335 cutAction_ =
new QAction(tr(
"&Create polyline at intersection with plane"),
this);
337 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."));
338 cutAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"cut_polyline.png") );
343 cutMultipleAction_ =
new QAction(tr(
"&Create polylines at intersection with plane"),
this);
345 cutMultipleAction_->setStatusTip(tr(
"Create polylines by specifying a plane with which the object is then intersected. The polylines will be created at the intersection."));
346 cutMultipleAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"cut_polylines.png") );
356 pickToolbar_->setObjectName(
"PolyLine_Editing_Toolbar");
357 pickToolbar_->setAttribute(Qt::WA_AlwaysShowToolTips,
true);
364 "Use <Doubleclick> to finish the line.\n" 365 "Hold <Shift> to close line on finish."));
366 insertAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_insert.png") );
373 "<Click> to select the center.\n" 374 "Drag to specify the radius."));
376 insertCircleAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_circle.png") );
383 "<Click> to select the points."));
385 insertSplineAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_bezier.png") );
390 deleteAction_->setStatusTip(tr(
"Delete a complete PolyLine."));
391 deleteAction_->setToolTip(tr(
"Delete a complete PolyLine.\n" 392 "<Click> on the lines you want to delete."));
393 deleteAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_delete.png") );
400 smartMoveAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_move.png") );
407 "Drag one endpoint of a PolyLine to the EndPoint of another one.\n"));
408 mergeAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_merge.png") );
413 splitAction_->setStatusTip(tr(
"Split a PolyLine at a given point."));
414 splitAction_->setToolTip(tr(
"Split a PolyLine at a given point.\n" 415 "<Click> on the vertex where you want to split the PolyLine and drag it away."));
416 splitAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_split.png") );
423 moveAction_->setStatusTip(tr(
"Move a Vertex of a PolyLine"));
424 moveAction_->setToolTip(tr(
"Move a single Vertex of a PolyLine."));
425 moveAction_->setIcon(QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+
"polyline_move.png") );
434 smart_move_timer_ =
new QTimer(
this);
435 connect(smart_move_timer_, SIGNAL(timeout()),
this, SLOT(slot_smart_move_timer()));
440 connect(
planeSelect_, SIGNAL( updateViewProxy( ) ),
this, SIGNAL( updateView() ) );
441 connect(
planeSelect_, SIGNAL( nodeVisChangedProxy(
int) ),
this, SIGNAL(nodeVisibilityChanged(
int) ) );
444 copyPaste_Action_ =
new QAction(
"Duplicate", 0);
445 connect(copyPaste_Action_,SIGNAL(triggered() ),
this,SLOT(slot_duplicate()));
484 emit log(
"Cutting object " + object->
name());
487 emit log(
"Only Meshes are supported at the moment ");
498 QString command =
"generatePolyLineFromCut(" + QString::number(object->
id()) +
",Vector(" 499 + QString::number(point[0]) +
"," + QString::number(point[1]) +
"," + QString::number(point[2]) +
"),Vector(" 500 + QString::number(normal[0]) +
"," + QString::number(normal[1]) +
"," + QString::number(normal[2]) +
"));";
501 emit scriptInfo(command);
506 if (o_it->id() !=
object->id()) {
518 QString command =
"generatePolyLinesFromCut(" + QString::number(object->
id()) +
",Vector(" 519 + QString::number(point[0]) +
"," + QString::number(point[1]) +
"," + QString::number(point[2]) +
"),Vector(" 520 + QString::number(normal[0]) +
"," + QString::number(normal[1]) +
"," + QString::number(normal[2]) +
"));";
521 emit scriptInfo(command);
526 if (o_it->id() !=
object->id()) {
531 for (
unsigned int i = 0 ; i < objectIds.size() ; ++i ) {
533 if ( objectIds[i] != -1)
549 double max_length =
tool_->dsb_subdivide->value();
552 if (max_length == 0.0)
571 void PolyLinePlugin::slot_subdivide_percent(
bool _checked) {
575 double total_length = 0;
586 double v = total_length / double(n_active_pl);
591 if (n_active_pl > 0) {
592 double val_new =
tool_->dsb_subdivide->value() / v;
593 tool_->dsb_subdivide->setValue(val_new);
595 emit log(
LOGWARN,
"Could not find any active polyline!");
600 if (n_active_pl > 0) {
601 double val_new =
tool_->dsb_subdivide->value() * v;
602 tool_->dsb_subdivide->setValue(val_new);
604 emit log(
LOGWARN,
"Could not find any active polyline!");
618 double min_length =
tool_->dsb_decimate->value();
635 void PolyLinePlugin::slot_decimate_percent(
bool _checked)
640 double total_length = 0;
651 double v = total_length / double(n_active_pl);
656 if (n_active_pl > 0) {
657 double val_new =
tool_->dsb_subdivide->value() / v;
658 tool_->dsb_decimate->setValue(val_new);
660 emit log(
LOGWARN,
"Could not find any active polyline!");
665 if (n_active_pl > 0) {
666 double val_new =
tool_->dsb_subdivide->value() * v;
667 tool_->dsb_decimate->setValue(val_new);
669 emit log(
LOGWARN,
"Could not find any active polyline!");
677 #ifdef EXTENDED_POLY_LINE 681 slot_resample_on_edges()
685 unsigned int n_targetMeshes(0);
689 if( n_targetMeshes != 1)
691 std::cerr <<
"Warning: resample_on_edges needs exactly 1 target mesh! Otherwise no operation is performed! \n";
715 std::cerr <<
"resample " << o_it2->
name().toStdString() << std::endl;
739 for(
int i=0; i<
tool_->sb_smooth_iter->value(); ++i)
740 if(
tool_->rb_smooth_c0->isChecked())
744 if(
tool_->rb_smooth_c1->isChecked())
748 if(
tool_->rb_smooth_c2->isChecked())
767 for (
int i = 0; i <
tool_->sb_smooth_iter->value(); ++i)
768 if (
tool_->rb_smooth_c0->isChecked())
771 else if (
tool_->rb_smooth_c1->isChecked())
774 else if (
tool_->rb_smooth_c2->isChecked())
790 std::vector<TriMesh*> meshes;
791 std::vector<OpenMeshTriangleBSPT<TriMesh>*> bsps;
800 meshes.push_back(tmesh_obj->
mesh());
827 std::vector<TriMesh*> meshes;
828 std::vector<OpenMeshTriangleBSPT<TriMesh>*> bsps;
838 meshes.push_back(tmesh_obj->
mesh());
855 slot_smooth_project()
857 int smooth_project_iter =
tool_->sb_smooth_project_iter->value();
859 for (
int i = 0, j = 1; i < smooth_project_iter; ++i, ++j) {
878 int smooth_project_iter =
tool_->sb_smooth_project_iter->value();
880 for (
int i = 0, j = 1; i < smooth_project_iter; ++i, ++j) {
897 slot_smart_move_timer()
899 int smooth_project_iter =
tool_->sb_smooth_project_iter->value();
901 if (smooth_project_iter)
902 slot_smooth_project(cur_smart_move_obj_);
904 slot_smooth(cur_smart_move_obj_);
912 PolyLinePlugin::slotObjectUpdated(
int _identifier,
const UpdateType &_type )
919 GlutPrimitiveNode* H = 0, *C = 0;
922 if(circleData && !C) {
923 createCircle_createUI(_identifier);
925 else if(splineData && !H) {
926 createSpline_createUI(_identifier);
936 if(copyPaste_ObjectId_ != -1 && copyPaste_ActionType_ != -1)
937 return PL_COPY_PASTE;
941 if(
tool_->rb_insert->isChecked() )
return PL_INSERT;
942 if(
tool_->rb_InsertCircle->isChecked() )
return PL_INSERTCIRCLE;
943 if(
tool_->rb_InsertSpline->isChecked() )
return PL_INSERTSPLINE;
944 else if(
tool_->rb_delete->isChecked() )
return PL_DELETE;
945 else if(
tool_->rb_move->isChecked() )
return PL_MOVE;
946 else if(
tool_->rb_split->isChecked() )
return PL_SPLIT;
947 else if(
tool_->rb_merge->isChecked() )
return PL_MERGE;
948 else if(
tool_->rb_smart_move->isChecked())
return PL_SMART_MOVE;
958 me_insert( QMouseEvent* _event )
960 if (_event->type() == QEvent::MouseMove) {
962 if (create_point_ref_) {
964 size_t node_idx, target_idx;
967 *create_point_ref_ = (PolyLine::Point) hit_point;
977 size_t node_idx, target_idx;
981 switch (_event->type()) {
983 case QEvent::MouseButtonPress: {
1003 cur_polyline_obj_->
line()->
add_point((PolyLine::Point) hit_point);
1005 emit showStatusMessage(tr(
"Double click/Enter to terminate poly line. Use with shift to create loop."));
1009 cur_polyline_obj_->
line()->
add_point((PolyLine::Point) hit_point);
1010 create_point_ref_ = &cur_polyline_obj_->
line()->
points().back();
1019 case QEvent::MouseButtonDblClick: {
1021 if (_event->modifiers() & (Qt::ShiftModifier)) {
1034 cur_insert_id_ = -1;
1035 cur_polyline_obj_ = 0;
1036 create_point_ref_ = 0;
1038 clearStatusMessage();
1051 double getRad(
int meshIdx)
1056 ACG::Vec3d bbMin( FLT_MAX, FLT_MAX, FLT_MAX);
1057 ACG::Vec3d bbMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
1059 return 0.005*(bbMax-bbMin).norm();
1066 void PolyLinePlugin::
1067 createCircle_createUI(
int _polyLineObjectID)
1076 double rad = getRad(lineData->circleMeshIndex_);
1078 GlutPrimitiveNode* handle0 =
new GlutPrimitiveNode(lineObject,
"N_Handle0");
1079 handle0->get_primitive(0).color =
ACG::Vec4f(1,0,0,1);
1080 handle0->set_size(rad);
1082 handle0->enablePicking(
true);
1083 handle0->set_position(lineData->circleCenter_ + lineData->circleMainAxis_ * lineData->circleMainRadius_);
1087 GlutPrimitiveNode* handle1 =
new GlutPrimitiveNode(lineObject,
"N_Handle1");
1088 handle1->get_primitive(0).color =
ACG::Vec4f(0,1,0,1);
1089 handle1->set_size(rad);
1091 handle1->enablePicking(
true);
1092 handle1->set_position(lineData->circleCenter_ + lineData->circleSideAxis_ * lineData->circleSideRadius_);
1096 GlutPrimitiveNode* cenNode =
new GlutPrimitiveNode(lineObject,
"N_Center");
1097 cenNode->get_primitive(0).color =
ACG::Vec4f(0,0,1,1);
1098 cenNode->set_size(rad);
1100 cenNode->enablePicking(
true);
1101 cenNode->set_position(lineData->circleCenter_);
1105 emit updatedObject(_polyLineObjectID,
UPDATE_ALL);
1110 void PolyLinePlugin::
1111 me_insertCircle(QMouseEvent* _event)
1114 TriMesh::FaceHandle fh;
1115 TriMesh::VertexHandle vh;
1117 if(!pick_triangle_mesh(_event->pos(), mesh, fh, vh, hit_point) && _event->type() != QEvent::MouseButtonRelease)
1126 const ACG::Vec3d n = circleData->circleNormal_, x0 = circleData->circleCenter_;
1127 const double t = ((n | x0) - (n | hit_point)) / n.
sqrnorm();
1128 const ACG::Vec3d onPlane = hit_point + t * n, d = onPlane - x0;
1130 circleData->circleMainAxis_ = (onPlane - x0).normalize();
1131 circleData->circleSideRadius_ = circleData->circleMainRadius_ = d.norm();
1132 circleData->circleSideAxis_ = (circleData->circleMainAxis_ % n).normalize();
1148 if(!mesh->
mesh()->has_face_normals())
1149 mesh->
mesh()->request_face_normals();
1159 else if(_event->type() == QEvent::MouseButtonRelease ) {
1167 createCircle_CurrSelIndex_ = -1;
1173 void PolyLinePlugin::
1174 createSpline_createUI(
int _polyLineObjectID)
1185 GlutLineNode* lineN =
new GlutLineNode(lineObject,
"N_Line");
1187 lineN->enablePicking(
false);
1191 for(
unsigned int i = 0; i < splineData->points_.size(); i++) {
1192 GlutPrimitiveNode* handle0 =
new GlutPrimitiveNode(lineObject,
"N_Control", i);
1193 handle0->get_primitive(0).color =
ACG::Vec4f(0,1,0,1);
1194 handle0->set_size(rad);
1196 handle0->set_position(splineData->points_[i].position);
1198 handle0->enablePicking(
true);
1202 for(
unsigned int i = 0; i < splineData->handles_.size(); i++) {
1204 const ACG::Vec3d hndlPos = splineData->handles_[i], ctrlPos = control.position;
1206 GlutPrimitiveNode* handle0 =
new GlutPrimitiveNode(lineObject,
"N_Handle", i);
1207 handle0->get_primitive(0).color =
ACG::Vec4f(0,0,1,1);
1208 handle0->set_size(rad * 0.75);
1210 handle0->enablePicking(
true);
1211 handle0->set_position(hndlPos);
1215 GlutLineNode* lineN;
1217 lineN->add_line(ctrlPos, hndlPos);
1223 emit updatedObject(_polyLineObjectID,
UPDATE_ALL);
1242 ACG::Vec3d bbMin( FLT_MAX, FLT_MAX, FLT_MAX);
1243 ACG::Vec3d bbMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
1249 GlutPrimitiveNode* control = 0;
1251 for(
unsigned int i = 0; i < splineData->points_.size(); i++) {
1253 control->enablePicking(
true);
1256 for(
unsigned int i = 0; i < splineData->handles_.size(); i++) {
1258 const ACG::Vec3d hndlPos = splineData->handles_[i], ctrlPos = control.position;
1260 GlutPrimitiveNode* handle0 =
new GlutPrimitiveNode(lineObject,
"N_Handle", i);
1261 handle0->get_primitive(0).color =
ACG::Vec4f(0,0,1,1);
1262 handle0->set_size(0.004*sizeBB.
norm());
1264 handle0->enablePicking(
true);
1265 handle0->set_position(hndlPos);
1269 GlutLineNode* lineN;
1271 lineN->add_line(ctrlPos, hndlPos);
1282 void PolyLinePlugin::
1283 me_insertSpline(QMouseEvent* _event)
1287 TriMesh::FaceHandle fh;
1288 TriMesh::VertexHandle vh;
1291 if(!pick_triangle_mesh(_event->pos(), mesh, fh, vh, hit_point))
1294 ACG::Vec3d bbMin( FLT_MAX, FLT_MAX, FLT_MAX);
1295 ACG::Vec3d bbMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);
1299 if(!mesh->
mesh()->has_face_normals())
1300 mesh->
mesh()->request_face_normals();
1304 if(_event->type() == QEvent::MouseButtonPress) {
1324 GlutLineNode* lineN =
new GlutLineNode(newLine,
"N_Line");
1326 lineN->enablePicking(
false);
1331 ACG::Vec3d insert_Point = hit_point + nor * 0.003 * sizeBB.
norm();
1338 GlutPrimitiveNode* handle0 =
new GlutPrimitiveNode(lineObject,
"N_Control", splineData->points_.size());
1339 handle0->get_primitive(0).color =
ACG::Vec4f(0,1,0,1);
1340 handle0->set_size(0.005*sizeBB.
norm());
1342 handle0->set_position(insert_Point);
1344 handle0->enablePicking(
false);
1351 if(_event->type() == QEvent::MouseButtonDblClick) {
1360 me_delete( QMouseEvent* _event )
1363 if (_event->type() == QEvent::MouseButtonPress) {
1365 size_t node_idx, target_idx;
1375 emit deleteObject(obj->
id());
1391 if(moveCircle_SelNode_)
1394 if(moveCircle_SelNode_)
1399 _node_idx = obj->
id();
1405 if(!_moveCircle_SelNode_->
name().compare(
"N_Center"))
1407 _lineData->circleNormal_ = _nor;
1408 _lineData->circleCenter_ = _hit_point;
1409 _lineData->circleSideAxis_ = (_lineData->circleMainAxis_ % _lineData->circleNormal_).normalize();
1410 _lineData->circleMainAxis_ = (_lineData->circleNormal_ % _lineData->circleSideAxis_).normalize();
1414 const double cr = (_onPlane - _lineData->circleCenter_).norm();
1415 const ACG::Vec3d axisa = (_onPlane - _lineData->circleCenter_).normalize();
1416 if (!_moveCircle_SelNode_->
name().compare(
"N_Handle0")) {
1417 const ACG::Vec3d axisb = (axisa % _lineData->circleNormal_).normalize();
1418 _lineData->circleMainRadius_ = cr;
1421 _lineData->circleSideRadius_ = cr;
1423 _lineData->circleMainAxis_ = axisa;
1424 _lineData->circleSideAxis_ = axisb;
1426 const ACG::Vec3d axisb = (_lineData->circleNormal_ % axisa).normalize();
1427 _lineData->circleSideRadius_ = cr;
1430 _lineData->circleMainRadius_ = cr;
1432 _lineData->circleSideAxis_ = axisa;
1433 _lineData->circleMainAxis_ = axisb;
1441 me_move( QMouseEvent* _event )
1443 if((_event->modifiers() & Qt::ShiftModifier) != Qt::ShiftModifier && moveCircle_IsLocked)
1444 moveCircle_IsLocked = moveCircle_IsFloating =
false;
1447 if (_event->type() == QEvent::MouseButtonPress) {
1449 size_t node_idx, target_idx;
1454 GlutPrimitiveNode* glutNode =
dynamic_cast<GlutPrimitiveNode*
>(node);
1483 cur_move_id_ = cur_pol->
id();
1485 move_point_ref_ = &(cur_pol->
line()->
point(target_idx));
1492 if (_event->type() == QEvent::MouseMove){
1500 bool hasHit = me_GetMeshHit(_event,
moveCircle_SelNode_, hit_point, lineData->circleMeshIndex_, target_idx);
1501 if(lineData->circleMeshIndex_ == std::numeric_limits<unsigned int>::max())
return;
1502 if(!moveCircle_IsLocked && hasHit) {
1503 moveCircle_IsFloating =
false;
1504 ACG::Vec3d x0 = lineData->circleCenter_, n = lineData->circleNormal_;
1506 double t = ((n | x0) - (n | onMesh)) / n.
sqrnorm();
1512 me_UpdateCircleData(onMesh, onPlane, mesh->
mesh()->normal(mesh->
mesh()->face_handle(target_idx)),
moveCircle_SelNode_, lineData, (_event->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier);
1515 if((_event->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier) {
1516 moveCircle_IsLocked =
true;
1517 moveCircle_LastHitNor_ = lineData->circleNormal_;
1523 moveCircle_IsFloating =
true;
1532 : lineData->circleNormal_;
1533 const double t = ((x0 | n) - (cameraPos | n)) / (cameraDir | n);
1534 const ACG::Vec3d onPlane = cameraPos + cameraDir * t;
1536 me_UpdateCircleData(onPlane, onPlane, n,
moveCircle_SelNode_, lineData, (_event->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier);
1550 if(lineData->
meshIndex_ == std::numeric_limits<unsigned int>::max())
1556 ACG::Vec3d onMesh = hit_point, onMeshNor = mesh->
mesh()->normal(mesh->
mesh()->face_handle(target_idx));
1560 lineData->points_[controlIndex].position = onMesh;
1561 lineData->points_[controlIndex].normal = onMeshNor;
1563 int handleIndex = 2 * controlIndex - 1;
1564 ACG::Vec3d dir = lineData->handles_[handleIndex] - oldPos, side = dir % onMeshNor, forw = (onMeshNor % side).normalize() * dir.
norm();
1566 lineData->handles_[handleIndex] = point;
1568 if(controlIndex != ((
int)lineData->points_.size() - 1)) {
1569 int handleIndex = 2 * controlIndex;
1570 ACG::Vec3d dir = lineData->handles_[handleIndex] - oldPos, side = dir % onMeshNor, forw = (onMeshNor % side).normalize() * dir.
norm();
1572 lineData->handles_[handleIndex] = point;
1583 double t = ((control.normal | control.position) - (control.normal | cameraPos)) / (control.normal | cameraDir);
1584 ACG::Vec3d onPlane = cameraPos + t * cameraDir;
1586 lineData->handles_[handleIndex] = onPlane;
1587 if(handleIndex % 2 == 1 && handleIndex != ((
int)lineData->handles_.size() - 1)) {
1588 double dist = (lineData->handles_[handleIndex + 1] - control.position).norm();
1589 if(_event->modifiers() & Qt::ShiftModifier)
1590 dist = (onPlane - control.position).norm();
1591 ACG::Vec3d dir = -(onPlane - control.position).normalize();
1592 lineData->handles_[handleIndex + 1] = control.position + dir * dist;
1594 if(handleIndex % 2 == 0 && handleIndex) {
1595 double dist = (lineData->handles_[handleIndex - 1] - control.position).norm();
1596 if(_event->modifiers() & Qt::ShiftModifier)
1597 dist = (onPlane - control.position).norm();
1598 ACG::Vec3d dir = -(onPlane - control.position).normalize();
1599 lineData->handles_[handleIndex - 1] = control.position + dir * dist;
1602 GlutLineNode* lineN;
1608 else if (move_point_ref_ != 0) {
1610 size_t node_idx, target_idx;
1612 (*move_point_ref_) = (PolyLine::Point) hit_point;
1620 if (_event->type() == QEvent::MouseButtonRelease) {
1621 if((_event->modifiers() & Qt::ShiftModifier) != Qt::ShiftModifier) {
1624 moveCircle_IsLocked = moveCircle_IsFloating =
false;
1628 moveCircle_IsLocked =
false;
1633 move_point_ref_ = 0;
1645 me_split( QMouseEvent* _event )
1648 if (_event->type() == QEvent::MouseButtonPress) {
1650 move_point_ref_ = 0;
1652 size_t node_idx, target_idx;
1674 cur_move_id_ = cur_pol->
id();
1705 cur_move_id_ = cur_pol->
id();
1721 if (_event->type() == QEvent::MouseMove)
1722 if (move_point_ref_ != 0) {
1724 size_t node_idx, target_idx;
1728 (*move_point_ref_) = (PolyLine::Point) hit_point;
1737 if (_event->type() == QEvent::MouseButtonRelease) {
1739 if (cur_move_id_ != -1)
1742 move_point_ref_ = 0;
1753 me_merge( QMouseEvent* _event )
1756 if (_event->type() == QEvent::MouseButtonPress) {
1758 move_point_ref_ = 0;
1761 size_t node_idx, target_idx;
1776 if (target_idx == cur_pol->
line()->
n_vertices() - 1 || target_idx == 0) {
1777 if (target_idx == 0) {
1783 cur_merge_id_ = cur_pol->
id();
1796 if (_event->type() == QEvent::MouseMove && cur_merge_id_ != -1)
1797 if (move_point_ref_ != 0) {
1798 size_t node_idx, target_idx;
1802 (*move_point_ref_) = (PolyLine::Point) hit_point;
1811 if (_event->type() == QEvent::MouseButtonRelease && cur_merge_id_ != -1) {
1812 PolyLine::Point p_save;
1815 if (move_point_ref_ != 0) {
1827 move_point_ref_ = 0;
1830 size_t node_idx, target_idx;
1839 PluginFunctions::invalidatePickCaches();
1841 bool merged =
false;
1854 if ( target_idx < second_pol->line()->n_vertices() ) {
1857 unsigned int second_idx = target_idx;
1862 bool inv_first(
false), inv_second(
false);
1865 if (first_idx == 0) {
1877 if (first_idx == first_pol->
line()->
n_vertices() - 1 && second_idx == 0) {
1879 if (first_pol->
id() == second_pol->
id()) {
1896 emit deleteObject(second_pol->
id());
1907 PluginFunctions::invalidatePickCaches();
1920 if ( target_idx < second_pol->line()->n_vertices() ) {
1923 unsigned int second_idx = target_idx;
1929 if (first_idx == 0) {
1938 if (first_pol->
id() == second_pol->
id() && first_idx == first_pol->
line()->
n_vertices() - 1 && second_idx == 0) {
1963 move_point_ref_ = 0;
1975 me_smart_move( QMouseEvent* _event )
1978 if (_event->type() == QEvent::MouseButtonPress) {
1980 size_t node_idx, target_idx;
1993 cur_polyline_obj_ = cur_pol;
2000 cur_smart_move_obj_ = cur_pol;
2001 if (cur_pol->
line()->vertex_selections_available()) {
2002 if (!(_event->modifiers() & (Qt::ShiftModifier)))
2003 cur_pol->
line()->vertex_selection(target_idx) =
true;
2005 cur_pol->
line()->vertex_selection(target_idx) =
false;
2013 if (!(_event->modifiers() & (Qt::ShiftModifier)))
2014 smart_move_timer_->start(20);
2021 if( _event->type() == QEvent::MouseButtonRelease)
2023 smart_move_timer_->stop();
2024 cur_smart_move_obj_ = NULL;
2036 slotEditModeChanged()
2049 GlutPrimitiveNode* H0, *H1, *C;
2053 ACG::Vec3d h0 = circleData->circleCenter_ + circleData->circleMainAxis_ * circleData->circleMainRadius_,
2054 h1 = circleData->circleCenter_ + circleData->circleSideAxis_ * circleData->circleSideRadius_;
2056 C->set_position(circleData->circleCenter_);
2067 slot_setCirclePointNum(
int i)
2078 slot_setSplinePointNum(
int i)
2091 tool_->rb_insert->setChecked(
true);
2093 tool_->rb_InsertCircle->setChecked(
true);
2095 tool_->rb_InsertSpline->setChecked(
true);
2097 tool_->rb_delete->setChecked(
true);
2099 tool_->rb_move->setChecked(
true);
2101 tool_->rb_smart_move->setChecked(
true);
2103 tool_->rb_merge->setChecked(
true);
2105 tool_->rb_split->setChecked(
true);
2125 slotEnablePickMode(QString _name)
2132 if(_name ==
"INSERT")
2133 tool_->rb_insert->setChecked(
true);
2134 else if(_name ==
"DELETE")
2135 tool_->rb_delete->setChecked(
true);
2136 else if(_name ==
"MOVE")
2137 tool_->rb_move->setChecked(
true);
2138 else if(_name ==
"SPLIT")
2139 tool_->rb_split->setChecked(
true);
2140 else if(_name ==
"MERGE")
2141 tool_->rb_merge->setChecked(
true);
2142 else if( _name ==
"SMART_MOVE")
2143 tool_->rb_smart_move->setChecked(
true);
2151 pick_triangle_mesh( QPoint mPos,
2155 _fh = TriMesh::FaceHandle (-1);
2156 _vh = TriMesh::VertexHandle(-1);
2158 size_t target_idx = 0, node_idx = 0;
2176 _fh = m.face_handle(target_idx);
2179 TriMesh::VertexHandle closest = *fv_it;
2180 float shortest_distance = (m.point(closest) - hit_point).sqrnorm();
2183 if ( (m.point(*fv_it ) - hit_point).sqrnorm() < shortest_distance ) {
2184 shortest_distance = (m.point(*fv_it ) - hit_point).sqrnorm();
2189 if ( (m.point(*fv_it ) - hit_point).sqrnorm() < shortest_distance ) {
2196 _hitPoint = hit_point;
2209 slotUpdateContextMenu(
int objectId)
2211 copyPaste_ObjectId_ = objectId;
2212 copyPaste_Action_->setVisible(
pickToolbar_->isVisible());
2217 me_copyPasteMouse(QMouseEvent* _event)
2224 copyPaste_ObjectId_ = copyPaste_ActionType_ - 1;
2227 size_t target_idx = 0, node_idx = 0;
2230 if(copyPaste_ActionType_ == 1) {
2240 GlutPrimitiveNode* glutNode =
dynamic_cast<GlutPrimitiveNode*
>(node);
2242 me_GetMeshHit(_event, glutNode, hit_point, circleData->circleMeshIndex_, target_idx);
2244 circleData-> circleCenter_ = hit_point;
2248 else if(oldSplineData) {
2249 for(
int i = 0; i < (int)splineData->points_.size(); i++) {
2250 ACG::Vec3d onMeshNor, oldPos = splineData->points_[i].position;
2252 splineData->points_[i].position = onMesh;
2253 splineData->points_[i].normal = onMeshNor;
2255 int handleIndex = 2 * i - 1;
2256 ACG::Vec3d dir = splineData->handles_[handleIndex] - oldPos, side = dir % onMeshNor, forw = (onMeshNor % side).normalize() * dir.
norm();
2258 splineData->handles_[handleIndex] = point;
2260 if(i != ((
int)splineData->points_.size() - 1)) {
2261 int handleIndex = 2 * i;
2262 ACG::Vec3d dir = splineData->handles_[handleIndex] - oldPos, side = dir % onMeshNor, forw = (onMeshNor % side).normalize() * dir.
norm();
2264 splineData->handles_[handleIndex] = point;
2267 GlutLineNode* lineN;
2275 newObj->
line()->
point(i) = hit_point + copyPaste_RelativePoints_[i];
2280 if(_event->type() == QEvent::MouseButtonPress) {
2281 copyPaste_ActionType_ = copyPaste_ObjectId_ = -1;
2284 else if(copyPaste_ActionType_ == 2) {
2298 size_t target_idx = 0, node_idx = 0;
2301 QPoint mPos = copyPaste_LastMouse;
2303 copyPaste_ActionType_ = 1;
2317 copyPaste_RelativePoints_.clear();
2322 createCircle_createUI(new_line_id);
2324 else if(splineData) {
2327 createSpline_createUI(new_line_id);
2330 for(
size_t i = 0; i < newData->points_.size(); i++)
2331 copyPaste_RelativePoints_.push_back(newData->points_[i].position - hit_point);
2338 copyPaste_RelativePoints_.push_back(obj->
line()->
point(i) - hit_point);
2342 copyPaste_NewObjectId_ = new_line_id;
2353 copyPaste_ActionType_ = 2;
void append(const PolyLineT< PointT > &_pl)
Append second polyline _pl to this one.
void updatePolyBezierHandles(PolyLineObject *_lineObject, ACG::SceneGraph::LineNode *_line)
Updates all the handles on the PolyBezier.
void add_point(const Point &_p)
Append a point to the polyline.
DrawMode WIREFRAME
draw wireframe
void slotTriggerCutPlaneSelect()
Generate PolyLine after the cutPlane has been drawn.
Namespace providing different geometric functions concerning angles.
void updatePolyEllipse(PolyLineObject *_lineObject, unsigned int _pointCount)
Generates points for the ellipse.
void smooth_uniform_laplace2()
Squared laplacian smoothing.
Point & point(unsigned int _i)
Get a point of the polyline.
int moveBezSpline_SelSubIndex_
The index of the control or handle being moved.
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
BaseObject * childExists(int _objectId)
Check if the element exists in the subtree of this element.
void delete_point(int _idx)
Delete point at _idx.
DrawMode POINTS_SHADED
draw shaded points (requires point normals)
bool scenegraphPick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
Execute picking operation on scenegraph.
Point & back()
Get last point of the polyline ( no range check!!!)
IdList generatePolyLinesFromCut(int _objectId, Vector _planePoint, Vector _planeNormal)
Generates a polyLine of a plane intersection.
pick any of the prior targets (should be implemented for all nodes)
QString name()
Name of the Plugin.
void enablePicking(bool _enable)
ACG::SceneGraph::GlutPrimitiveNode * moveCircle_SelNode_
The handle which is being dragged.
size_t n_vertices() const
Get number of vertices.
auto norm() const -> decltype(std::sqrt(std::declval< VectorT< S, DIM >>().sqrnorm()))
compute euclidean norm
void project_to_mesh(const MeshT &_mesh, SpatialSearchT *_ssearch=0)
Project polyline points to nearest surface points (use spatial search!!!)
decltype(std::declval< S >() *std::declval< S >()) sqrnorm() const
compute squared euclidean norm
MaterialNode * materialNode()
get a pointer to the materialnode
bool getPickedObject(const size_t _node_idx, BaseObjectData *&_object)
Get the picked mesh.
int moveBezSpline_SelIndex_
The object which is being moved.
void set_vertex_radius(const Scalar _r)
set ball-radius of vertices
QAction * cutAction_
Called by pick Toolbar.
void set_closed(const bool _c)
Set if the polyline should be closed and therefore forms a loop.
std::string name() const
Returns: name of node (needs not be unique)
OMTriangleBSP * requestTriangleBsp()
bool getAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
get an addition node from the object
ACG::Vec3d getPointOnMesh(PolyLineBezierSplineData *_SplineData, ACG::Vec3d _point, ACG::Vec3d *_nor=0)
Returns the nearest point on the mesh or if none could be found the input.
Scalar length() const
Compute the length of the polyline (in future cached method)
VectorT< float, 4 > Vec4f
QAction * polyLineAction_
Called by Toolbar to enable pick mode.
PolyLineToolbarWidget * tool_
Widget for Toolbox.
PolyLine * line()
return a pointer to the line
QAction * smartMoveAction_
Called by pick Toolbar.
Kernel::FaceVertexIter FaceVertexIter
Circulator.
bool dataType(DataType _type) const
int createCircle_LastSelIndex_
Use this one to mark the last index to update the number of points.
QToolBar * toolbar_
Called by Toolbar to enable pick mode.
MeshT * mesh()
return a pointer to the mesh
const QStringList TARGET_OBJECTS("target")
Iterable object range.
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
PolyLinePlugin()
default constructor
void slotScissorButton()
Scissor Button was hit.
void smooth_uniform_laplace3()
Cubic laplacian smoothing.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
QAction * insertAction_
Called by pick Toolbar.
bool addAdditionalNode(NodeT *_node, QString _pluginName, QString _nodeName, int _id=0)
add an additional node to the object
ACG::SceneGraph::BaseNode * getRootNode()
Get the root node for data objects.
int createSpline_CurrSelIndex_
The index of the currently created spline.
void addInterpolatePoint(ACG::Vec3d _position, ACG::Vec3d _normal)
Adds a point to the end of the list and inserts control points.
void slotScissorLinesButton()
Scissor Button for multiple polylines was hit.
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
Type for a MeshObject containing a triangle mesh.
QString name() const
return the name of the object. The name defaults to NONAME if unset.
ACG::Vec3d createCircle_getHit(PolyLineCircleData *_circleData, ACG::Vec3d _hit_point)
Returns point on mesh or point on the normal plane.
ACG::SceneGraph::GlutPrimitiveNode * moveBezSpline_SelNode_
The handle which is being dragged.
BaseObject *& objectRoot()
Get the root of the object structure.
EditMode
Edit Mode of PolyLinePlugin.
void slotPickToolbarAction(QAction *_action)
Called by pick Toolbar.
Viewer::ViewerProperties & viewerProperties(int _id)
Get the viewer properties Use this functions to get basic viewer properties such as backgroundcolor o...
QAction * moveAction_
Called by pick Toolbar.
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(1)<< 2)
Geometry updated.
VectorT< double, 3 > Vec3d
bool finishSpline()
If possible calculates handles.
void enablePicking(bool _enable)
Enable or disable picking for this Object.
InterpolatePoint & getInterpolatePoint(unsigned int _handleIndex)
Retrieves the interpolate point based on the handle.
QActionGroup * pickToolBarActions_
Called by pick Toolbar.
QAction * cutMultipleAction_
Called by pick Toolbar.
QAction * insertSplineAction_
Called by pick Toolbar.
void get_viewport(int &_left, int &_bottom, int &_width, int &_height) const
get viewport
ACG::SceneGraph::PolyLineNodeT< PolyLine > * lineNode()
Get the scenegraph Node.
const Vec3d get_position(int _idx=0) const
get position
ACG::Vec3d moveCircle_LastHitPos_
The last valid hit on the mesh.
void set_random_color()
Generates a random color and sets it.
void setObjectData(QString _dataName, PerObjectData *_data)
size_t meshIndex_
Index of the corresponding mesh.
QAction * deleteAction_
Called by pick Toolbar.
void viewing_ray(int _x, int _y, Vec3d &_origin, Vec3d &_direction) const
QtPlaneSelect * planeSelect_
Plane selection tool.
void resize(unsigned int _n)
Resize current polyline.
void collapse(Scalar _smallest)
Collapse polyline.
void smooth_uniform_laplace()
Laplacian smoothing.
void subdivide(Scalar _largest)
Subdivide polyline.
void updateHandles(PolyLineObject *_lineObject)
Updates the center, forward and side handle of the Poly ellipse.
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
int createCircle_CurrSelIndex_
The object which is being modified(created, dragged)
std::vector< Point > & points()
Get all points of the polyline.
QActionGroup * toolBarActions_
Called by Toolbar to enable pick mode.
DrawMode SOLID_FLAT_SHADED
draw flat shaded faces (requires face normals)
int generatePolyLineFromCut(int _objectId, Vector _planePoint, Vector _planeNormal, int _polyLineId=-1)
Generates a polyLine of a plane intersection.
QAction * splitAction_
Called by pick Toolbar.
ACG::GLState & glState()
Get the glState of the Viewer.
void setObjectDrawMode(const ACG::SceneGraph::DrawModes::DrawMode &_mode, const bool &_force=false)
Set the draw mode for the object.
picks faces (should be implemented for all nodes)
const UpdateType UPDATE_TOPOLOGY(UpdateTypeSet(1)<< 3)
Topology updated.
void updatePolyBezierSpline(PolyLineObject *_lineObject, unsigned int _pointsPerSegment)
Generates points for the spline, updates handles.
bool is_closed() const
Check if the polyline is marked as closed.
QAction * mergeAction_
Called by pick Toolbar.
Viewer::ActionMode actionMode()
Get the current Action mode.
int createSpline_LastSelIndex_
Use this one to mark the last index to update the number of points.
void set_position(const Vec3d &_p, int _idx=0)
set position
virtual bool picked(uint _node_idx)
detect if the node has been picked
QAction * insertCircleAction_
Called by pick Toolbar.
void viewingDirection(const ACG::Vec3d &_dir, const ACG::Vec3d &_up, int _viewer)
Set the viewing direction.
void split(unsigned int _split_idx, PolyLineT< PointT > &_new_pl)
Split closed polyline at vertex with index _split_idx.
PerObjectData * objectData(QString _dataName)
Returns the object data pointer.
double sceneRadius()
Returns the current scene radius from the active examiner widget.
#define DATA_TRIANGLE_MESH
void slotSetPolyLineMode(QAction *_action)
Called by Toolbar to enable pick mode.
void invert()
Invert polyline that first vertex becomes last.
QToolBar * pickToolbar_
Called by pick Toolbar.
The Menu will be shown when an object was picked.
picks verices (may not be implemented for all nodes)
PolyLineObject * polyLineObject(BaseObjectData *_object)
Cast an BaseObject to a PolyLineObject if possible.
Scalar vertex_radius() const
get ball-radius of vertices
const std::string pickMode()
Get the current Picking mode.
void boundingBox(ACG::Vec3d &_bbMin, typename ACG::Vec3d &_bbMax)
Get the BoundingBox of this object.
void split_closed(unsigned int _split_idx)
Split closed polyline at vertex with index _split_idx.