@@ -45,8 +45,7 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) :
4545 _numPoints(0 ),
4646 _settingsAction(this , " Settings" ),
4747 _primaryToolbarAction(this , " Primary Toolbar" ),
48- _secondaryToolbarAction(this , " Secondary Toolbar" ),
49- _selectPointsTimer()
48+ _secondaryToolbarAction(this , " Secondary Toolbar" )
5049{
5150 setObjectName (" Scatterplot" );
5251
@@ -214,18 +213,28 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) :
214213 return dropRegions;
215214 });
216215
217- /*
218- _selectPointsTimer.setSingleShot(true);
216+ auto & selectionAction = _settingsAction.getSelectionAction ();
219217
220- connect(&_selectPointsTimer, &QTimer::timeout, this, [this]() -> void {
221- if (_selectPointsTimer.isActive())
222- _selectPointsTimer.start(LAZY_UPDATE_INTERVAL);
223- else {
224- _selectPointsTimer.stop();
225- selectPoints();
226- }
218+ getSamplerAction ().initialize (this , &selectionAction.getPixelSelectionAction (), &selectionAction.getSamplerPixelSelectionAction ());
219+ getSamplerAction ().setTooltipGeneratorFunction ([this ](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString {
220+ QStringList localPointIndices, globalPointIndices;
221+
222+ for (const auto & localPointIndex : toolTipContext[" LocalPointIndices" ].toList ())
223+ localPointIndices << QString::number (localPointIndex.toInt ());
224+
225+ for (const auto & globalPointIndex : toolTipContext[" GlobalPointIndices" ].toList ())
226+ globalPointIndices << QString::number (globalPointIndex.toInt ());
227+
228+ if (localPointIndices.isEmpty ())
229+ return {};
230+
231+ return QString (" <table> \
232+ <tr> \
233+ <td><b>Point ID's: </b></td> \
234+ <td>%1</td> \
235+ </tr> \
236+ </table>" ).arg (globalPointIndices.join (" , " ));
227237 });
228- */
229238}
230239
231240ScatterplotPlugin::~ScatterplotPlugin ()
@@ -247,26 +256,26 @@ void ScatterplotPlugin::init()
247256 // Update the data when the scatter plot widget is initialized
248257 connect (_scatterPlotWidget, &ScatterplotWidget::initialized, this , &ScatterplotPlugin::updateData);
249258
250- // Update the selection when the pixel selection tool selected area changed
251259 connect (&_scatterPlotWidget->getPixelSelectionTool (), &PixelSelectionTool::areaChanged, [this ]() {
252260 if (_scatterPlotWidget->getPixelSelectionTool ().isNotifyDuringSelection ()) {
253- // _selectPointsTimer.start(LAZY_UPDATE_INTERVAL);
254261 selectPoints ();
255262 }
256263 });
257264
258- // Update the selection when the pixel selection process ended
259265 connect (&_scatterPlotWidget->getPixelSelectionTool (), &PixelSelectionTool::ended, [this ]() {
260266 if (_scatterPlotWidget->getPixelSelectionTool ().isNotifyDuringSelection ())
261267 return ;
262268
263- // _selectPointsTimer.start(LAZY_UPDATE_INTERVAL);
264269 selectPoints ();
265270 });
266271
272+ connect (&getSamplerAction (), &ViewPluginSamplerAction::sampleContextRequested, this , &ScatterplotPlugin::samplePoints);
273+
267274 connect (&_positionDataset, &Dataset<Points>::changed, this , &ScatterplotPlugin::positionDatasetChanged);
268275 connect (&_positionDataset, &Dataset<Points>::dataChanged, this , &ScatterplotPlugin::updateData);
269276 connect (&_positionDataset, &Dataset<Points>::dataSelectionChanged, this , &ScatterplotPlugin::updateSelection);
277+
278+ _scatterPlotWidget->installEventFilter (this );
270279}
271280
272281void ScatterplotPlugin::loadData (const Datasets& datasets)
@@ -299,28 +308,21 @@ void ScatterplotPlugin::createSubset(const bool& fromSourceData /*= false*/, con
299308
300309void ScatterplotPlugin::selectPoints ()
301310{
311+ auto & pixelSelectionTool = _scatterPlotWidget->getPixelSelectionTool ();
312+
302313 // Only proceed with a valid points position dataset and when the pixel selection tool is active
303- if (!_positionDataset.isValid () || !_scatterPlotWidget-> getPixelSelectionTool () .isActive () || _scatterPlotWidget->isNavigating ())
314+ if (!_positionDataset.isValid () || !pixelSelectionTool .isActive () || _scatterPlotWidget->isNavigating ())
304315 return ;
305316
306- // qDebug() << _positionDataset->getGuiName() << "selectPoints";
307-
308- // Get binary selection area image from the pixel selection tool
309- auto selectionAreaImage = _scatterPlotWidget->getPixelSelectionTool ().getAreaPixmap ().toImage ();
310-
311- // Get smart pointer to the position selection dataset
312- auto selectionSet = _positionDataset->getSelection <Points>();
317+ auto selectionAreaImage = pixelSelectionTool.getAreaPixmap ().toImage ();
318+ auto selectionSet = _positionDataset->getSelection <Points>();
313319
314- // Create vector for target selection indices
315320 std::vector<std::uint32_t > targetSelectionIndices;
316321
317- // Reserve space for the indices
318322 targetSelectionIndices.reserve (_positionDataset->getNumPoints ());
319323
320- // Mapping from local to global indices
321324 std::vector<std::uint32_t > localGlobalIndices;
322325
323- // Get global indices from the position dataset
324326 _positionDataset->getGlobalIndices (localGlobalIndices);
325327
326328 auto & zoomRectangleAction = _scatterPlotWidget->getNavigationAction ().getZoomRectangleAction ();
@@ -333,24 +335,18 @@ void ScatterplotPlugin::selectPoints()
333335 QPointF uvNormalized = {};
334336 QPoint uv = {};
335337
336- // Loop over all points and establish whether they are selected or not
337- for (std::uint32_t i = 0 ; i < _positions.size (); i++) {
338- uvNormalized = QPointF ((_positions[i].x - zoomRectangleAction.getLeft ()) / zoomRectangleAction.getWidth (), (zoomRectangleAction.getTop () - _positions[i].y ) / zoomRectangleAction.getHeight ());
338+ for (std::uint32_t localPointIndex = 0 ; localPointIndex < _positions.size (); localPointIndex++) {
339+ uvNormalized = QPointF ((_positions[localPointIndex].x - zoomRectangleAction.getLeft ()) / zoomRectangleAction.getWidth (), (zoomRectangleAction.getTop () - _positions[localPointIndex].y ) / zoomRectangleAction.getHeight ());
339340 uv = uvOffset + QPoint (uvNormalized.x () * size, uvNormalized.y () * size);
340341
341- if (uv.x () >= selectionAreaImage.width () || uv.x () < 0 ||
342- uv.y () >= selectionAreaImage.height () || uv.y () < 0 )
342+ if (uv.x () >= selectionAreaImage.width () || uv.x () < 0 || uv.y () >= selectionAreaImage.height () || uv.y () < 0 )
343343 continue ;
344344
345- // Add point if the corresponding pixel selection is on
346345 if (selectionAreaImage.pixelColor (uv).alpha () > 0 )
347- targetSelectionIndices.push_back (localGlobalIndices[i ]);
346+ targetSelectionIndices.push_back (localGlobalIndices[localPointIndex ]);
348347 }
349348
350- // Selection should be subtracted when the selection process was aborted by the user (e.g. by pressing the escape key)
351- const auto selectionModifier = _scatterPlotWidget->getPixelSelectionTool ().isAborted () ? PixelSelectionModifierType::Subtract : _scatterPlotWidget->getPixelSelectionTool ().getModifier ();
352-
353- switch (selectionModifier)
349+ switch (const auto selectionModifier = pixelSelectionTool.isAborted () ? PixelSelectionModifierType::Subtract : pixelSelectionTool.getModifier ())
354350 {
355351 case PixelSelectionModifierType::Replace:
356352 break ;
@@ -367,30 +363,29 @@ void ScatterplotPlugin::selectPoints()
367363 switch (selectionModifier)
368364 {
369365 // Add points to the current selection
370- case PixelSelectionModifierType::Add:
371- {
372- // Add indices to the set
373- for (const auto & targetIndex : targetSelectionIndices)
374- set.insert (targetIndex);
366+ case PixelSelectionModifierType::Add:
367+ {
368+ // Add indices to the set
369+ for (const auto & targetIndex : targetSelectionIndices)
370+ set.insert (targetIndex);
375371
376- break ;
377- }
372+ break ;
373+ }
378374
379- // Remove points from the current selection
380- case PixelSelectionModifierType::Subtract:
381- {
382- // Remove indices from the set
383- for (const auto & targetIndex : targetSelectionIndices)
384- set.remove (targetIndex);
375+ // Remove points from the current selection
376+ case PixelSelectionModifierType::Subtract:
377+ {
378+ // Remove indices from the set
379+ for (const auto & targetIndex : targetSelectionIndices)
380+ set.remove (targetIndex);
385381
386- break ;
387- }
382+ break ;
383+ }
388384
389- default :
390- break ;
385+ case PixelSelectionModifierType::Replace :
386+ break ;
391387 }
392388
393- // Convert set back to vector
394389 targetSelectionIndices = std::vector<std::uint32_t >(set.begin (), set.end ());
395390
396391 break ;
@@ -405,6 +400,105 @@ void ScatterplotPlugin::selectPoints()
405400 events ().notifyDatasetDataSelectionChanged (_positionDataset->getSourceDataset <Points>());
406401}
407402
403+ void ScatterplotPlugin::samplePoints ()
404+ {
405+ auto & samplerPixelSelectionTool = _scatterPlotWidget->getSamplerPixelSelectionTool ();
406+
407+ if (!_positionDataset.isValid () || _scatterPlotWidget->isNavigating ())
408+ return ;
409+
410+ auto selectionAreaImage = samplerPixelSelectionTool.getAreaPixmap ().toImage ();
411+
412+ auto selectionSet = _positionDataset->getSelection <Points>();
413+
414+ std::vector<std::uint32_t > targetSelectionIndices;
415+
416+ targetSelectionIndices.reserve (_positionDataset->getNumPoints ());
417+
418+ std::vector<std::uint32_t > localGlobalIndices;
419+
420+ _positionDataset->getGlobalIndices (localGlobalIndices);
421+
422+ auto & zoomRectangleAction = _scatterPlotWidget->getNavigationAction ().getZoomRectangleAction ();
423+
424+ const auto width = selectionAreaImage.width ();
425+ const auto height = selectionAreaImage.height ();
426+ const auto size = width < height ? width : height;
427+ const auto uvOffset = QPoint ((selectionAreaImage.width () - size) / 2 .0f , (selectionAreaImage.height () - size) / 2 .0f );
428+
429+ QPointF pointUvNormalized;
430+
431+ QPoint pointUv, mouseUv = _scatterPlotWidget->mapFromGlobal (QCursor::pos ());
432+
433+ std::vector<char > focusHighlights (_positions.size ());
434+
435+ std::vector<std::pair<float , std::uint32_t >> sampledPoints;
436+
437+ for (std::uint32_t localPointIndex = 0 ; localPointIndex < _positions.size (); localPointIndex++) {
438+ pointUvNormalized = QPointF ((_positions[localPointIndex].x - zoomRectangleAction.getLeft ()) / zoomRectangleAction.getWidth (), (zoomRectangleAction.getTop () - _positions[localPointIndex].y ) / zoomRectangleAction.getHeight ());
439+ pointUv = uvOffset + QPoint (pointUvNormalized.x () * size, pointUvNormalized.y () * size);
440+
441+ if (pointUv.x () >= selectionAreaImage.width () || pointUv.x () < 0 || pointUv.y () >= selectionAreaImage.height () || pointUv.y () < 0 )
442+ continue ;
443+
444+ if (selectionAreaImage.pixelColor (pointUv).alpha () > 0 ) {
445+ const auto sample = std::pair<float , std::uint32_t >((QVector2D (mouseUv) - QVector2D (pointUv)).length (), localPointIndex);
446+
447+ sampledPoints.emplace_back (sample);
448+ }
449+ }
450+
451+ QVariantList localPointIndices, globalPointIndices, distances;
452+
453+ localPointIndices.reserve (static_cast <std::int32_t >(sampledPoints.size ()));
454+ globalPointIndices.reserve (static_cast <std::int32_t >(sampledPoints.size ()));
455+ distances.reserve (static_cast <std::int32_t >(sampledPoints.size ()));
456+
457+ std::int32_t numberOfPoints = 0 ;
458+
459+ std::sort (sampledPoints.begin (), sampledPoints.end (), [](const auto & sampleA, const auto & sampleB) -> bool {
460+ return sampleB.first > sampleA.first ;
461+ });
462+
463+ for (const auto & sampledPoint : sampledPoints) {
464+ if (getSamplerAction ().getRestrictNumberOfElementsAction ().isChecked () && numberOfPoints >= getSamplerAction ().getMaximumNumberOfElementsAction ().getValue ())
465+ break ;
466+
467+ const auto & distance = sampledPoint.first ;
468+ const auto & localPointIndex = sampledPoint.second ;
469+ const auto & globalPointIndex = localGlobalIndices[localPointIndex];
470+
471+ distances << distance;
472+ localPointIndices << localPointIndex;
473+ globalPointIndices << globalPointIndex;
474+
475+ focusHighlights[localPointIndex] = true ;
476+
477+ numberOfPoints++;
478+ }
479+
480+ if (getSamplerAction ().getHighlightFocusedElementsAction ().isChecked ())
481+ const_cast <PointRenderer&>(_scatterPlotWidget->getPointRenderer ()).setFocusHighlights (focusHighlights, static_cast <std::int32_t >(focusHighlights.size ()));
482+
483+ _scatterPlotWidget->update ();
484+
485+ auto & coloringAction = _settingsAction.getColoringAction ();
486+
487+ getSamplerAction ().setSampleContext ({
488+ { " PositionDatasetID" , _positionDataset.getDatasetId () },
489+ { " ColorDatasetID" , _settingsAction.getColoringAction ().getCurrentColorDataset ().getDatasetId () },
490+ { " LocalPointIndices" , localPointIndices },
491+ { " GlobalPointIndices" , globalPointIndices },
492+ { " Distances" , distances },
493+ { " ColorBy" , coloringAction.getColorByAction ().getCurrentText () },
494+ { " ConstantColor" , coloringAction.getConstantColorAction ().getColor () },
495+ { " ColorMap1D" , coloringAction.getColorMap1DAction ().getColorMapImage () },
496+ { " ColorMap2D" , coloringAction.getColorMap2DAction ().getColorMapImage () },
497+ { " ColorDimensionIndex" , coloringAction.getDimensionAction ().getCurrentDimensionAction ().getCurrentIndex () },
498+ { " RenderMode" , _settingsAction.getRenderModeAction ().getCurrentText () }
499+ });
500+ }
501+
408502Dataset<Points>& ScatterplotPlugin::getPositionDataset ()
409503{
410504 return _positionDataset;
0 commit comments