46 computeUnitVector(
const T *coordOrig,
const T *coordDest, T *coordVect);
50static void rotate(T *ptToRotate,
const T *center,
double angle);
56 rotatePolygon(std::vector<T> &coords, T *centerCoords,
const double angle);
60 std::vector<size_t> &res,
107 template <
typename T>
110 const std::vector<T> &inputMatrix,
124 template <
typename T>
125 bool getPrevNextEdges(
const std::vector<size_t> &idsPtsPolygon,
133 template <
typename T>
134 T rotateMergingCompsBest(
const std::vector<size_t> &hull1,
135 const std::vector<size_t> &hull2,
136 const std::vector<size_t> &comp1,
137 const std::vector<size_t> &comp2,
140 const std::vector<T> &distMatrix,
143 size_t angularSampleNb);
145 template <
typename T>
146 bool computeConvexHull(T *allCoords,
147 const std::vector<size_t> &compPtsIds,
148 std::vector<size_t> &idsInHull)
const;
182 template <
typename T>
185 const std::vector<T> &inputMatrix,
190#ifdef TTK_ENABLE_QHULL
193 "Qhull was enabled but it is not installed or was not found.");
194 this->printWrn(
"Defaulting to Boost support instead.");
199 this->
printMsg(
"Using Boost for convex hulls.");
200 this->printWrn(
"Bugs have been reported in Boost's implementation.");
201 this->printWrn(
"Consider enabling Qhull instead.");
204 if(AngularSampleNb < 2) {
205 this->printWrn(
"The number of angular samples set is less than 2.");
206 this->printWrn(
"Setting it to 2 (minimum possible value).");
207 this->AngularSampleNb = 2;
212 if(std::is_same<T, double>::value) {
214 }
else if(std::is_same<T, float>::value) {
217 printErr(
"Error, template type must be either double or float.\n");
222 this->
printMsg(
"Input data: " + std::to_string(n) +
" points.");
224 this->
printMsg(
"Input data: " + std::to_string(n) +
" points ("
225 + std::to_string(inputMatrix.size() / n)
229 std::vector<T> computedDistMatrix(n * n);
231 size_t dim = inputMatrix.size() / n;
232 if(n * dim != inputMatrix.size()) {
233 this->printErr(
"Error, the coordinates input matrix has invalid size.");
237#ifdef TTK_ENABLE_OPENMP
238#pragma omp parallel for num_threads(this->threadNumber_) \
239 shared(computedDistMatrix)
241 for(
size_t i1 = 0; i1 < n; i1++) {
242 for(
size_t i2 = i1; i2 < n; i2++) {
243 T dist = ttk::Geometry::distance<T>(
244 &inputMatrix[dim * i1], &inputMatrix[dim * i2], dim);
245 computedDistMatrix[i1 * n + i2] = dist;
246 computedDistMatrix[i2 * n + i1] = dist;
250 if(inputMatrix.size() != n * n) {
251 this->printErr(
"Invalid size for the distance matrix.");
256 std::vector<double> edgesMSTBefore,
258 edgesMSTBefore.reserve(n);
259 const std::vector<T> &distMatrix
260 = isDistMat ? inputMatrix : computedDistMatrix;
262 if(insertionTime !=
nullptr)
263 for(
size_t i = 0; i < n; i++)
264 insertionTime[i] = -1;
267 using heapType = std::pair<T, std::pair<size_t, size_t>>;
268 std::priority_queue<heapType, std::vector<heapType>, std::greater<heapType>>
270 if(this->Strategy == STRATEGY::KRUSKAL) {
271 for(
size_t u1 = 0; u1 < n; u1++) {
272 for(
size_t u2 = u1 + 1; u2 < n; u2++) {
274 std::make_pair(distMatrix[u1 * n + u2], std::make_pair(u1, u2)));
277 }
else if(this->Strategy == STRATEGY::PRIM) {
278 for(
size_t u1 = 0; u1 < n; u1++) {
279 edgeHeap.push(std::make_pair(distMatrix[u1], std::make_pair(0, u1)));
282 this->printErr(
"Invalid stategy for the MST: only Kruskal (0) or Prim "
283 "(1) algorithm can be used.");
288 std::map<UnionFind *, std::vector<size_t>> ufToComp;
289 std::vector<UnionFind> ufVector(n);
290 for(
size_t i = 0; i < n; i++) {
291 ufToComp[&ufVector[i]].emplace_back(i);
294 for(
size_t i = 0; i < 2 * n; i++) {
298 T finalDistortion = 0;
300 size_t nbEdgesMerged = 0;
301 double MSTWeight = 0;
305 if(this->Strategy == STRATEGY::PRIM) {
306 for(
size_t i = 1; i < n; i++) {
310 while(nbEdgesMerged != n - 1) {
311 if(edgeHeap.empty()) {
312 this->printErr(
"Error building the MST. Aborting.");
315 const auto &elt = edgeHeap.top();
316 T edgeCost = elt.first;
317 size_t u = elt.second.first;
318 size_t v = elt.second.second;
328 MSTWeight += edgeCost;
330 if(insertionTime !=
nullptr) {
331 if(insertionTime[u] == -1) {
332 insertionTime[u] = nbEdgesMerged;
334 if(insertionTime[v] == -1) {
335 insertionTime[v] = nbEdgesMerged;
338 edgesMSTBefore.push_back(edgeCost);
339 std::vector<size_t> &compU = ufToComp[reprU], &compV = ufToComp[reprV];
340 size_t idSmall = compU.size() < compV.size() ? u : v;
341 size_t idBig = idSmall == u ? v : u;
342 std::vector<size_t> &compSmall = idSmall == u ? compU : compV;
343 std::vector<size_t> &compBig = idSmall == u ? compV : compU;
344 size_t nBig = compBig.size();
345 size_t nSmall = compSmall.size();
346 std::vector<size_t> idsInHullSmall, idsInHullBig;
350 bool statusHull =
true;
352 = statusHull | computeConvexHull(outputCoords, compBig, idsInHullBig);
353 statusHull = statusHull
354 | computeConvexHull(outputCoords, compSmall, idsInHullSmall);
356 this->printErr(
"Aborting.");
360 size_t idChosenBig = idsInHullBig[0], idChosenSmall = idsInHullSmall[0];
364 for(
size_t vert : idsInHullBig) {
365 T dist = distMatrix[vert * n + idBig];
366 if(dist < distMatrix[idChosenBig * n + idBig]) {
371 for(
size_t vert : idsInHullSmall) {
372 T dist = distMatrix[vert * n + idSmall];
373 if(dist < distMatrix[idChosenSmall * n + idSmall]) {
374 idChosenSmall = vert;
378 size_t sizeBigHull = idsInHullBig.size(),
379 sizeSmallHull = idsInHullSmall.size();
380 std::vector<T> coordsBigHull(sizeBigHull * 2),
381 coordsSmallHull(sizeSmallHull * 2);
382 for(
size_t iHull = 0; iHull < sizeBigHull; iHull++) {
383 size_t vert = idsInHullBig[iHull];
384 coordsBigHull[iHull * 2] = outputCoords[vert * 2];
385 coordsBigHull[iHull * 2 + 1] = outputCoords[vert * 2 + 1];
387 for(
size_t iHull = 0; iHull < sizeSmallHull; iHull++) {
388 size_t vert = idsInHullSmall[iHull];
389 coordsSmallHull[iHull * 2] = outputCoords[vert * 2];
390 coordsSmallHull[iHull * 2 + 1] = outputCoords[vert * 2 + 1];
394 T coordPrevBig[2], coordPostBig[2];
395 T coordPrevSmall[2], coordPostSmall[2];
396 if(!getPrevNextEdges(idsInHullSmall, idChosenSmall, outputCoords,
397 coordPrevSmall, coordPostSmall)) {
400 if(!getPrevNextEdges(idsInHullBig, idChosenBig, outputCoords,
401 coordPrevBig, coordPostBig)) {
404 T coordPtSmall[2] = {
405 outputCoords[2 * idChosenSmall], outputCoords[2 * idChosenSmall + 1]};
407 = {outputCoords[2 * idChosenBig], outputCoords[2 * idChosenBig + 1]};
411 coordPtSmall, coordPrevSmall, coordPostSmall);
413 coordPtBig, coordPrevBig, coordPostBig);
414 if(angleSmall -
M_PI > EpsilonDBL || angleBig -
M_PI > EpsilonDBL) {
415 this->printErr(
"Error, angle out of bound (greater than pi).");
417 T coordscenterBig[2] = {coordPrevBig[0], coordPrevBig[1]};
418 T coordscenterSmall[2] = {coordPrevSmall[0], coordPrevSmall[1]};
420 rotate(coordscenterSmall, coordPtSmall, angleSmall / 2);
421 rotate(coordscenterBig, coordPtBig, angleBig / 2);
423 T unitcenterBigVect[2], unitcenterSmallVect[2];
425 &outputCoords[idChosenBig * 2], coordscenterBig, unitcenterBigVect);
426 computeUnitVector(&outputCoords[idChosenSmall * 2], coordscenterSmall,
427 unitcenterSmallVect);
431 T goalCoordChosenSmall[2]
432 = {outputCoords[idChosenBig * 2] - edgeCost * unitcenterBigVect[0],
433 outputCoords[idChosenBig * 2 + 1] - edgeCost * unitcenterBigVect[1]};
434 if(sizeBigHull == 1) {
435 goalCoordChosenSmall[0] = outputCoords[idChosenBig * 2] + edgeCost;
436 goalCoordChosenSmall[1] = outputCoords[idChosenBig * 2 + 1];
438 T smallCompMoveVect[2]
439 = {goalCoordChosenSmall[0] - outputCoords[idChosenSmall * 2],
440 goalCoordChosenSmall[1] - outputCoords[idChosenSmall * 2 + 1]};
443 &outputCoords[idChosenSmall * 2], coordscenterSmall);
444 T preFinalPosBarySmall[2] = {coordscenterSmall[0] + smallCompMoveVect[0],
445 coordscenterSmall[1] + smallCompMoveVect[1]};
446 T finalPosBarySmall[2]
447 = {goalCoordChosenSmall[0] - unitcenterBigVect[0] * distBaryPointSmall,
448 goalCoordChosenSmall[1] - unitcenterBigVect[1] * distBaryPointSmall};
453 for(
size_t curIdSmall : compSmall) {
454 outputCoords[curIdSmall * 2] += smallCompMoveVect[0];
455 outputCoords[curIdSmall * 2 + 1] += smallCompMoveVect[1];
459 goalCoordChosenSmall, preFinalPosBarySmall, finalPosBarySmall);
460 if(nSmall > 1 && std::isfinite(rotationAngle)) {
462 &outputCoords[curIdSmall * 2], goalCoordChosenSmall, rotationAngle);
470 finalDistortion = rotateMergingCompsBest(
471 idsInHullSmall, idsInHullBig, compSmall, compBig, idChosenSmall,
472 idChosenBig, distMatrix, outputCoords, n, this->AngularSampleNb);
473 if(finalDistortion < -1) {
479 &outputCoords[2 * idChosenSmall], &outputCoords[2 * idChosenBig]);
480 if(fabs(finalDist - edgeCost) > Epsilon) {
482 "The distance we set is too far from the goal distance.");
486 UnionFind *otherRepr = (unionRepr == reprU) ? reprV : reprU;
487 if(unionRepr != reprU && unionRepr != reprV) {
488 printErr(
"Error with the union find module results. Aborting.");
491 std::vector<size_t> &unionSet = ufToComp[unionRepr];
492 std::vector<size_t> &otherSet = ufToComp[otherRepr];
494 unionSet.insert(unionSet.end(), otherSet.begin(), otherSet.end());
496 ufToComp.erase(otherRepr);
498 if(this->Strategy == STRATEGY::PRIM) {
500 for(
size_t uToDo : stillToDo) {
501 edgeHeap.push(std::make_pair(
502 distMatrix[v * n + uToDo], std::make_pair(v, uToDo)));
506 std::stringstream ssDistortion;
507 ssDistortion << std::scientific << 2 * finalDistortion;
508 this->
printMsg(
"Non normalized distance matrix distortion: "
509 + ssDistortion.str());
511 "(for normalized distortion values, use distanceMatrixDistorsion).");
513 if(this->errorConvexHull) {
515 "Warning, somme convex hull computations were wrong, so the projection "
516 "may be less optimal than it could have been. If you are using Boost, "
517 "consider switching to Qhull to avoid this.");
519 this->
printMsg(
"The minimum spanning tree has weight "
520 + std::to_string(MSTWeight) +
".");
526 edgesMSTAfter.reserve(n);
527 this->
printMsg(
"Checking the new minimum spanning tree.");
528 std::priority_queue<heapType, std::vector<heapType>,
529 std::greater<heapType>>
531 if(this->Strategy == STRATEGY::KRUSKAL) {
532 for(
size_t u1 = 0; u1 < n; u1++) {
533 for(
size_t u2 = u1 + 1; u2 < n; u2++) {
536 &outputCoords[2 * u1], &outputCoords[2 * u2]),
537 std::make_pair(u1, u2)));
540 }
else if(this->Strategy == STRATEGY::PRIM) {
541 for(
size_t u1 = 0; u1 < n; u1++) {
542 edgeHeapAfter.push(std::make_pair(
544 std::make_pair(0, u1)));
548 std::map<UnionFind *, std::vector<size_t>> ufToCompAfter;
549 std::vector<UnionFind> ufVectorAfter(n);
550 std::vector<UnionFind *> ufPtrVectorAfter(n);
551 for(
size_t i = 0; i < n; i++) {
552 ufPtrVectorAfter[i] = &ufVectorAfter[i];
553 ufToCompAfter[ufPtrVectorAfter[i]].push_back(i);
558 for(
size_t i = 1; i < n; i++)
560 while(nbEdgesMerged != n - 1) {
561 if(edgeHeapAfter.empty()) {
562 this->printErr(
"Error building the MST for the check. Aborting.");
565 const auto &elt = edgeHeapAfter.top();
566 T edgeCost = elt.first;
567 size_t u = elt.second.first;
568 size_t v = elt.second.second;
577 UnionFind *otherRepr = (unionRepr == reprU) ? reprV : reprU;
578 if(unionRepr != reprU && unionRepr != reprV) {
579 printErr(
"Error with the union find module results. Aborting.");
583 std::vector<size_t> &unionSet = ufToCompAfter[unionRepr];
584 std::vector<size_t> &otherSet = ufToCompAfter[otherRepr];
586 unionSet.insert(unionSet.end(), otherSet.begin(), otherSet.end());
588 ufPtrVectorAfter[u] = unionRepr;
589 ufPtrVectorAfter[v] = unionRepr;
591 ufToCompAfter.erase(otherRepr);
592 if(this->Strategy == STRATEGY::PRIM) {
594 for(
size_t uToDo : stillToDo) {
597 &outputCoords[2 * v], &outputCoords[2 * uToDo]),
598 std::make_pair(v, uToDo)));
602 edgesMSTAfter.push_back(edgeCost);
605 const T epsilonCheck = Epsilon * 5;
606 double sumDiff = 0, maxDiff = 0;
607 size_t nbProblematic = 0;
608 if(this->Strategy == STRATEGY::PRIM) {
609 sort(edgesMSTBefore.begin(), edgesMSTBefore.end());
610 sort(edgesMSTAfter.begin(), edgesMSTAfter.end());
612 for(
size_t i = 0; i < edgesMSTBefore.size(); i++) {
613 if(fabs(edgesMSTBefore[i] - edgesMSTAfter[i]) >= epsilonCheck) {
615 double diff = fabs(edgesMSTBefore[i] - edgesMSTAfter[i]);
617 maxDiff = std::max(diff, maxDiff);
620 if(nbProblematic != 0) {
621 this->printWrn(
"Error on " + std::to_string(nbProblematic)
622 +
" edge(s) of the MST.");
624 std::stringstream ssMax, ssAvg;
625 ssMax << std::scientific << maxDiff;
626 ssAvg << std::scientific << sumDiff / nbProblematic;
627 this->printWrn(
"The maximum error is " + ssMax.str()
628 +
" and the average (on the number of errors) error is "
629 + ssAvg.str() +
".");
640 template <
typename T>
641 bool TopoMap::getPrevNextEdges(
const std::vector<size_t> &idsPtsPolygon,
645 T *coordPost)
const {
646 size_t n = idsPtsPolygon.size();
647 size_t iPtPrev = 0, iPtPost = 0;
650 for(
size_t i = 0; i < n; i++) {
651 if(idsPtsPolygon[i] == idCenter) {
653 iPtPost = idsPtsPolygon[(i + 1) % n];
654 iPtPrev = idsPtsPolygon[(i + n - 1) % n];
659 printErr(
"Error, we could not find the edges incident to the point we "
660 "chose for the rotation of the component. Aborting.");
664 coordPrev[0] = allCoords[2 * iPtPrev];
665 coordPrev[1] = allCoords[2 * iPtPrev + 1];
666 coordPost[0] = allCoords[2 * iPtPost];
667 coordPost[1] = allCoords[2 * iPtPost + 1];
672 template <
typename T>
673 T TopoMap::rotateMergingCompsBest(
const std::vector<size_t> &hull1,
674 const std::vector<size_t> &hull2,
675 const std::vector<size_t> &comp1,
676 const std::vector<size_t> &comp2,
679 const std::vector<T> &distMatrix,
682 size_t angularSampleNb) {
684 T shortestDistPossible
686 T coordPt1[2] = {allCoords[2 * iPt1], allCoords[2 * iPt1 + 1]};
687 T coordPt2[2] = {allCoords[2 * iPt2], allCoords[2 * iPt2 + 1]};
688 size_t comp1Size = comp1.size(), comp2Size = comp2.size();
690 T coordPrev1[2], coordPost1[2];
691 T coordPrev2[2], coordPost2[2];
692 getPrevNextEdges(hull1, iPt1, allCoords, coordPrev1, coordPost1);
693 getPrevNextEdges(hull2, iPt2, allCoords, coordPrev2, coordPost2);
699 if(angle1 -
M_PI > EpsilonDBL || angle2 -
M_PI > EpsilonDBL) {
700 this->
printErr(
"One angle of the convex hull is greater than pi. "
701 "Convexity error, aborting.");
704 T coordBissect1[2] = {coordPrev1[0], coordPrev1[1]};
705 T coordBissect2[2] = {coordPrev2[0], coordPrev2[1]};
706 rotate(coordBissect1, coordPt1, angle1 / 2);
707 rotate(coordBissect2, coordPt2, angle2 / 2);
710 double semiAngle1 = comp1Size == 1 ?
M_PI / 2 : angle1 / 2;
711 double semiAngle2 = comp2Size == 1 ?
M_PI / 2 : angle2 / 2;
713 double angleMax1 =
M_PI / 2 - semiAngle1, angleMin1 = -angleMax1;
714 double angleMax2 =
M_PI / 2 - semiAngle2, angleMin2 = -angleMax2;
715 double step1 = (angleMax1 - angleMin1) / angularSampleNb,
716 step2 = (angleMax2 - angleMin2) / angularSampleNb;
717 double bestAnglePair[2] = {0, 0};
720 std::vector<std::vector<T>> origDistMatrix(comp1Size);
721 for(
size_t i = 0; i < comp1Size; i++) {
722 origDistMatrix[i].resize(comp2Size);
723 for(
size_t j = 0; j < comp2Size; j++) {
724 origDistMatrix[i][j] = distMatrix[comp1[i] * n + comp2[j]];
727 std::vector<T> initialCoords1(2 * comp1Size), initialCoords2(2 * comp2Size);
728 for(
size_t i = 0; i < comp1.size(); i++) {
729 size_t iPt = comp1[i];
730 initialCoords1[2 * i] = allCoords[2 * iPt];
731 initialCoords1[2 * i + 1] = allCoords[2 * iPt + 1];
733 for(
size_t i = 0; i < comp2.size(); i++) {
734 size_t iPt = comp2[i];
735 initialCoords2[2 * i] = allCoords[2 * iPt];
736 initialCoords2[2 * i + 1] = allCoords[2 * iPt + 1];
739 size_t nbIter1 = std::isfinite(step1) ? angularSampleNb + 1 : 1;
740 size_t nbIter2 = std::isfinite(step2) ? angularSampleNb + 1 : 1;
742 if(step1 * nbIter1 < 0.001) {
745 if(step2 * nbIter2 < 0.001) {
749 bool errorDuringLoop =
false;
750 bool foundValidAngle =
false;
751#ifdef TTK_ENABLE_OPENMP
752#pragma omp parallel for num_threads(this->threadNumber_) shared(allCoords)
755 for(
size_t i1 = 0; i1 < nbIter1; i1++) {
756 std::vector<T> coords1Test(2 * comp1Size), coords2Test(2 * comp2Size);
757 coords1Test = initialCoords1;
758 double testAngle1 = angleMin1 + step1 * i1;
759 rotatePolygon(coords1Test, coordPt1, testAngle1);
763 for(
size_t i2 = 0; i2 < nbIter2; i2++) {
764 bool curAngleError =
false;
765 coords2Test = initialCoords2;
766 double testAngle2 = angleMin2 + i2 * step2;
767 rotatePolygon(coords2Test, coordPt2, testAngle2);
772 for(
size_t i = 0; i < comp1Size; i++) {
773 T coordARotate[2] = {coords1Test[2 * i], coords1Test[2 * i + 1]};
774 for(
size_t j = 0; j < comp2Size; j++) {
775 T coordBRotate[2] = {coords2Test[2 * j], coords2Test[2 * j + 1]};
777 curScore += (newDist - origDistMatrix[i][j])
778 * (newDist - origDistMatrix[i][j]);
779 if(newDist + Epsilon < shortestDistPossible) {
780 curAngleError =
true;
781 errorDuringLoop =
true;
793#ifdef TTK_ENABLE_OPENMP
799 foundValidAngle =
true;
802 if(curScore < bestScore
803 || (curScore == bestScore
804 && (testAngle1 < bestAnglePair[0]
805 || (testAngle1 == bestAnglePair[0]
806 && testAngle2 < bestAnglePair[1])))) {
807 bestScore = curScore;
808 bestAnglePair[0] = std::isfinite(testAngle1) ? testAngle1 : 0;
809 bestAnglePair[1] = std::isfinite(testAngle2) ? testAngle2 : 0;
816 if(!foundValidAngle) {
818 "No valid angle was found, due to errors in the convex hull "
819 "computation. Please consider using Qhull instead of Boost. Aborting.");
824 for(
size_t i1 : comp1) {
825 rotate(&allCoords[2 * i1], coordPt1, bestAnglePair[0]);
827 for(
size_t i2 : comp2) {
828 rotate(&allCoords[2 * i2], coordPt2, bestAnglePair[1]);
831 if(errorDuringLoop) {
838 template <
typename T>
839 bool TopoMap::computeConvexHull(T *allCoords,
840 const std::vector<size_t> &compPtsIds,
841 std::vector<size_t> &idsInHull)
const {
842 size_t nbPoint = compPtsIds.size();
843 std::vector<double> compCoords(2 * nbPoint);
844 for(
size_t i = 0; i < nbPoint; i++) {
845 size_t vertId = compPtsIds[i];
846 compCoords[2 * i] =
static_cast<double>(allCoords[2 * vertId]);
847 compCoords[2 * i + 1] =
static_cast<double>(allCoords[2 * vertId + 1]);
851 idsInHull.push_back(compPtsIds[0]);
856 idsInHull.push_back(compPtsIds[1]);
865 double dirVect[2] = {0, 0};
866 bool areColinear =
true;
868 size_t idFirstDistinct = 0;
869 while(idFirstDistinct < nbPoint && fabs(dirVect[0]) < Epsilon
870 && fabs(dirVect[1]) < Epsilon) {
872 dirVect[0] = compCoords[2 * idFirstDistinct] - compCoords[0];
873 dirVect[1] = compCoords[2 * idFirstDistinct + 1] - compCoords[1];
874 if(fabs(dirVect[0]) < Epsilon) {
877 if(fabs(dirVect[1]) < Epsilon) {
883 int idMins[2] = {compCoords[0] < compCoords[2] ? 0 : 1,
884 compCoords[1] < compCoords[3] ? 0 : 1};
885 int idMaxs[2] = {compCoords[0] > compCoords[2] ? 0 : 1,
886 compCoords[1] > compCoords[3] ? 0 : 1};
888 const double *pt0 = &compCoords[0];
889 const double *ptDistinct = &compCoords[2 * idFirstDistinct];
891 for(
size_t iPt = idFirstDistinct + 1; iPt < nbPoint; iPt++) {
892 double curVect[2] = {compCoords[2 * iPt] - compCoords[0],
893 compCoords[2 * iPt + 1] - compCoords[1]};
894 if(fabs(curVect[0]) < Epsilon && fabs(curVect[1]) < Epsilon) {
897 const double *ptCur = &compCoords[2 * iPt];
899 pt0, ptDistinct, ptCur, EpsilonDBL)) {
905 if(compCoords[2 * iPt] < compCoords[2 * idMins[0]]) {
908 if(compCoords[2 * iPt + 1] < compCoords[2 * idMins[1]]) {
911 if(compCoords[2 * iPt] > compCoords[2 * idMaxs[0]]) {
914 if(compCoords[2 * iPt + 1] > compCoords[2 * idMaxs[1]]) {
920 if(fabs(dirVect[0]) > Epsilon) {
921 idsInHull.push_back(compPtsIds[idMins[0]]);
922 idsInHull.push_back(compPtsIds[idMaxs[0]]);
924 idsInHull.push_back(compPtsIds[idMins[1]]);
925 idsInHull.push_back(compPtsIds[idMaxs[1]]);
943 double sumX = 0, sumY = 0;
944 for(
size_t u : idsInHull) {
945 sumX += compCoords[2 * u];
946 sumY += compCoords[2 * u + 1];
948 double bary[2] = {sumX / idsInHull.size(), sumY / idsInHull.size()};
949 double baryRight[2] = {bary[0] + 2, bary[1]};
950 std::vector<std::pair<double, size_t>> ptsToSort;
951 ptsToSort.reserve(idsInHull.size());
952 for(
size_t u : idsInHull) {
953 const double curPt[2] = {compCoords[2 * u], compCoords[2 * u + 1]};
956 ptsToSort.emplace_back(std::make_pair(-curAngle, u));
961 sort(ptsToSort.begin(), ptsToSort.end());
962 for(
size_t i = 0; i < ptsToSort.size(); i++)
963 idsInHull[i] = ptsToSort[i].second;
967 for(
size_t &x : idsInHull) {
976 computeUnitVector(
const T *coordOrig,
const T *coordDest, T *coordVect) {
977 T tmp[2] = {coordDest[0] - coordOrig[0], coordDest[1] - coordOrig[1]};
978 T dist = sqrt(tmp[0] * tmp[0] + tmp[1] * tmp[1]);
983 coordVect[0] = tmp[0] / dist;
984 coordVect[1] = tmp[1] / dist;
989static void rotate(T *ptToRotate,
const T *center,
double angle) {
990 const double &xCtr = center[0], &yCtr = center[1];
991 T &xPt = ptToRotate[0], &yPt = ptToRotate[1];
992 const double dx = xPt - xCtr, dy = yPt - yCtr;
993 xPt = dx * cos(angle) - dy * sin(angle) + xCtr;
994 yPt = dx * sin(angle) + dy * cos(angle) + yCtr;
999 rotatePolygon(std::vector<T> &coords, T *centerCoords,
const double angle) {
1000 double xCenter = centerCoords[0], yCenter = centerCoords[1];
1001 size_t nbPoint = coords.size() / 2;
1002 for(
size_t iPt = 0; iPt < nbPoint; iPt++) {
1003 T &x = coords[iPt * 2], &y = coords[iPt * 2 + 1];
1005 xNew = (x - xCenter) * cos(angle) - (y - yCenter) * sin(angle) + xCenter;
1006 yNew = (y - yCenter) * cos(angle) + (x - xCenter) * sin(angle) + yCenter;
bool computeConvexHull_aux(const std::vector< double > &coords, std::vector< size_t > &res, std::string &errMsg)
bool computeConvexHull_aux(const std::vector< double > &coords, std::vector< size_t > &res, std::string &errMsg)
Minimalist debugging class.
void setDebugMsgPrefix(const std::string &prefix)
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
int execute(T *outputCoords, int *insertionTime, const std::vector< T > &inputMatrix, bool isDistMat, size_t n)
Computes the TopoMap projection.
TopoMap(size_t angularSampleNb, bool checkMST, STRATEGY strategy)
Union Find implementation for connectivity tracking.
static UnionFind * makeUnion(UnionFind *uf0, UnionFind *uf1)
double angle2DUndirected(const T *vA, const T *vB, const T *vC)
bool isTriangleColinear2D(const T *pptA, const T *pptB, const T *pptC, const T tolerance)
T distance2D(const T *p0, const T *p1)
T powInt(const T val, const int n)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)