12 #include <QFileDialog>
14 #include <QIntValidator>
24 #include <qcombobox.h>
50 ccCompass::ccCompass(QObject* parent)
63 ccCompass::~ccCompass() {
95 m_mapDlg->setLowerButton->setEnabled(
false);
96 m_mapDlg->setUpperButton->setEnabled(
false);
97 m_mapDlg->setInteriorButton->setEnabled(
false);
98 m_mapDlg->selectionLabel->setEnabled(
false);
99 m_mapDlg->selectionLabel->setText(tr(
"No Selection"));
102 for (
ccHObject* obj : selectedEntities) {
106 bool interior =
false;
125 m_mapDlg->setLowerButton->setEnabled(
true);
126 m_mapDlg->setUpperButton->setEnabled(
true);
127 m_mapDlg->setInteriorButton->setEnabled(
true);
129 m_mapDlg->selectionLabel->setEnabled(
true);
153 QList<QAction*> ccCompass::getActions() {
166 connect(
m_action, &QAction::triggered,
this,
203 ccCompassDlg::connect(
m_dlg->closeButton, SIGNAL(clicked()),
this,
205 ccCompassDlg::connect(
m_dlg->acceptButton, SIGNAL(clicked()),
this,
207 ccCompassDlg::connect(
m_dlg->saveButton, SIGNAL(clicked()),
this,
209 ccCompassDlg::connect(
m_dlg->undoButton, SIGNAL(clicked()),
this,
211 ccCompassDlg::connect(
m_dlg->infoButton, SIGNAL(clicked()),
this,
215 ccCompassDlg::connect(
m_dlg->mapMode, SIGNAL(clicked()),
this,
217 ccCompassDlg::connect(
m_dlg->compassMode, SIGNAL(clicked()),
this,
221 ccCompassDlg::connect(
m_dlg->pickModeButton, SIGNAL(clicked()),
this,
223 ccCompassDlg::connect(
m_dlg->pairModeButton, SIGNAL(clicked()),
this,
225 ccCompassDlg::connect(
m_dlg->planeModeButton, SIGNAL(clicked()),
this,
227 ccCompassDlg::connect(
m_dlg->traceModeButton, SIGNAL(clicked()),
this,
240 ccCompassDlg::connect(
m_dlg->
m_follows, SIGNAL(triggered()),
this,
250 SIGNAL(triggered()),
this,
268 ccCompassDlg::connect(
m_dlg->
m_toSVG, SIGNAL(triggered()),
this,
288 SIGNAL(triggered()),
this,
290 ccCompassDlg::connect(
m_mapDlg->setInteriorButton, SIGNAL(clicked()),
292 ccCompassDlg::connect(
m_mapDlg->setUpperButton, SIGNAL(clicked()),
this,
294 ccCompassDlg::connect(
m_mapDlg->setLowerButton, SIGNAL(clicked()),
this,
319 prg.
setInfo(tr(
"Converting Compass types..."));
323 std::vector<int> originals;
324 std::vector<ccHObject*> replacements;
327 for (
unsigned i = 0; i < nChildren; i++) {
328 prg.setValue(
static_cast<int>((50 * i) / nChildren));
334 for (
size_t i = 0; i < originals.size(); i++) {
335 prg.setValue(
static_cast<int>(50 + (50 * i) / originals.size()));
338 ccHObject* replacement = replacements[i];
363 m_app->
addToDB(replacement,
false,
false,
false,
false);
376 std::vector<int>* originals,
377 std::vector<ccHObject*>* replacements) {
399 replacements->push_back(geoObj);
415 replacements->push_back(plane);
424 replacements->push_back(sneCloud);
439 replacements->push_back(trace);
447 replacements->push_back(lin);
455 replacements->push_back(t);
466 replacements->push_back(n);
474 replacements->push_back(n);
486 "ACloudViewer window. Abort!"),
563 "hub. Measurement aborted."),
570 "mechanism. Stop it first"),
596 "GeoObject to digitize to."),
606 "GeoObject to digitize to."),
614 tr(
"[ccCompass] Warning: Could not retrieve valid "
615 "mapping region for the active GeoObject."),
630 tr(
"measurements")) {
642 ->
getName() == tr(
"measurements")) {
651 if (measurement_group) {
657 if (!measurement_group) {
658 measurement_group =
new ccHObject(tr(
"measurements"));
660 m_app->
addToDB(measurement_group,
false,
true,
false,
false);
663 return measurement_group;
691 if (parentNode ==
nullptr)
711 tr(
"[Item picking] Shit's fubar (Picked point is not in "
712 "pickable entities DB?)!"));
731 if (
event->type() == QEvent::MouseButtonDblClick) {
732 QMouseEvent* mouseEvent =
static_cast<QMouseEvent*
>(
event);
733 if (mouseEvent->buttons() == Qt::RightButton) {
761 return object->hasMetaData(tr(
"ccCompassType"));
793 m_dlg->pairModeButton->setChecked(
false);
794 m_dlg->planeModeButton->setChecked(
false);
795 m_dlg->traceModeButton->setChecked(
false);
796 m_dlg->pickModeButton->setChecked(
false);
797 m_dlg->extraModeButton->setChecked(
false);
798 m_dlg->undoButton->setEnabled(
false);
799 m_dlg->acceptButton->setEnabled(
false);
802 if (autoRestartPicking) {
821 m_dlg->undoButton->setEnabled(
false);
822 m_dlg->pairModeButton->setChecked(
true);
840 m_dlg->planeModeButton->setChecked(
true);
857 m_dlg->traceModeButton->setChecked(
true);
859 m_dlg->acceptButton->setEnabled(
true);
873 m_dlg->pickModeButton->setChecked(
true);
874 m_dlg->undoButton->setEnabled(
false);
875 m_dlg->acceptButton->setEnabled(
false);
888 m_dlg->extraModeButton->setChecked(
true);
890 m_dlg->acceptButton->setEnabled(
false);
907 m_dlg->extraModeButton->setChecked(
true);
909 m_dlg->acceptButton->setEnabled(
true);
933 m_dlg->undoButton->setEnabled(
false);
934 m_dlg->acceptButton->setEnabled(
false);
964 m_dlg->extraModeButton->setChecked(
true);
966 m_dlg->acceptButton->setEnabled(
false);
973 std::vector<ccGeoObject*> objs;
988 tr(
"[Compass] Select several GeoObjects to merge."),
998 for (
int i = 1; i < objs.size(); i++) {
1009 objs[i]->removeChild(interior);
1010 objs[i]->removeChild(upper);
1011 objs[i]->removeChild(lower);
1012 objs[i]->getParent()->removeChild(objs[i]);
1025 tr(
"[Compass] Merged selected GeoObjects to ") + dest->
getName(),
1059 for (
unsigned p = 0; p < t->
size(); p++) {
1067 if (
points->size() > 0) {
1071 map.insert(
"RMS", rms);
1077 "generate sensible fit plane."),
1097 for (
unsigned p = 0; p < t->
size(); p++) {
1106 if (
points->size() > 0) {
1110 map.insert(
"RMS", rms);
1116 "to generate sensible fit plane."),
1132 std::vector<ccHObject*> garbage;
1133 for (ccHObject::Container::iterator it = planes.begin(); it != planes.end();
1152 garbage.push_back((*it));
1159 for (
unsigned c = 0; c < (*it)->getChildrenNumber(); c++) {
1175 (*it)->detachChild(t);
1182 garbage.push_back((*it));
1191 for (
int i = 0; i < garbage.size(); i++) {
1192 garbage[i]->getParent()->removeChild(garbage[i]);
1197 inline double prior(
double phi,
double theta,
double nx,
double ny,
double nz) {
1206 double alpha = acos(nx * sin(phi) * cos(theta) +
1207 ny * cos(phi) * cos(theta) - nz * sin(theta));
1217 double detX =
X.m_values[0][0] * ((
X.m_values[1][1] *
X.m_values[2][2]) -
1218 (
X.m_values[2][1] *
X.m_values[1][2])) -
1219 X.m_values[0][1] * (
X.m_values[1][0] *
X.m_values[2][2] -
1220 X.m_values[2][0] *
X.m_values[1][2]) +
1221 X.m_values[0][2] * (
X.m_values[1][0] *
X.m_values[2][1] -
1222 X.m_values[2][0] *
X.m_values[1][1]);
1224 return (nobserved - 4.0) * 0.5 * log(detX) -
1225 (nobserved * 3. / 2.) *
1228 ((3.0 / 2.0) * log(
M_PI) + lgamma(nobserved / 2.0) +
1229 lgamma((nobserved / 2.0) - 0.5) +
1230 lgamma((nobserved / 2.0) - 1.0));
1250 e[0][2] = sin(phi) * cos(theta);
1251 e[1][2] = cos(phi) * cos(theta);
1252 e[2][2] = -sin(theta);
1255 e[0][1] = sin(phi) * sin(theta) * sin(alpha) - cos(phi) * cos(alpha);
1256 e[1][1] = sin(phi) * cos(alpha) + sin(theta) * cos(phi) * sin(alpha);
1257 e[2][1] = sin(alpha) * cos(theta);
1259 e[0][0] = e[1][2] * e[2][1] - e[2][2] * e[1][1];
1260 e[1][0] = e[2][2] * e[0][1] - e[0][2] * e[2][1];
1261 e[2][0] = e[0][2] * e[1][1] - e[1][2] * e[0][1];
1264 double D = e1 * e2 * e3;
1275 i[0][0] = e1 * e[0][0] * e[0][0] + e2 * e[0][1] * e[0][1] +
1276 e3 * e[0][2] * e[0][2];
1277 i[1][1] = e1 * e[1][0] * e[1][0] + e2 * e[1][1] * e[1][1] +
1278 e3 * e[1][2] * e[1][2];
1279 i[2][2] = e1 * e[2][0] * e[2][0] + e2 * e[2][1] * e[2][1] +
1280 e3 * e[2][2] * e[2][2];
1281 i[0][1] = e1 * e[0][0] * e[1][0] + e2 * e[0][1] * e[1][1] +
1282 e3 * e[0][2] * e[1][2];
1283 i[0][2] = e1 * e[0][0] * e[2][0] + e2 * e[0][1] * e[2][1] +
1284 e3 * e[0][2] * e[2][2];
1285 i[1][2] = e1 * e[1][0] * e[2][0] + e2 * e[1][1] * e[2][1] +
1286 e3 * e[1][2] * e[2][2];
1289 double trIX = (i[0][0] *
X.m_values[0][0] + i[0][1] *
X.m_values[1][0] +
1290 i[0][2] *
X.m_values[2][0]) +
1291 (i[0][1] *
X.m_values[0][1] + i[1][1] *
X.m_values[1][1] +
1292 i[1][2] *
X.m_values[2][1]) +
1293 (i[0][2] *
X.m_values[0][2] + i[1][2] *
X.m_values[1][2] +
1294 i[2][2] *
X.m_values[2][2]);
1297 return lsf - 0.5 * (trIX + nobserved * log(D));
1317 QVBoxLayout* vbox =
new QVBoxLayout();
1318 QLabel minSizeLabel(tr(
"Minimum trace size (points):"));
1319 QLineEdit minSizeText(QString::number(
minsize));
1320 minSizeText.setValidator(
1322 QLabel maxSizeLabel(tr(
"Maximum trace size (points):"));
1323 QLineEdit maxSizeText(QString::number(
maxsize));
1324 maxSizeText.setValidator(
1326 QLabel dofLabel(tr(
"Wishart Degrees of Freedom:"));
1327 QLineEdit dofText(QString::number(
dof));
1329 QLabel likPowerLabel(tr(
"Likelihood power:"));
1330 QLineEdit likPowerText(QString::number(
likPower));
1331 likPowerText.setValidator(
1333 QLabel calcThickLabel(tr(
"Calculate thickness:"));
1334 QCheckBox calcThickChk(tr(
"Calculate thickness"));
1336 QLabel distanceLabel(tr(
"Distance cutoff (m):"));
1337 QLineEdit distanceText(QString::number(
tcDistance));
1338 distanceText.setValidator(
1340 QLabel sampleLabel(tr(
"Samples:"));
1341 QLineEdit sampleText(QString::number(
oversample));
1342 sampleText.setValidator(
1343 new QIntValidator(1, 10000));
1345 QLabel strideLabel(tr(
"MCMC Stride (radians):"));
1346 QLineEdit strideText(QString::number(
stride));
1347 strideText.setValidator(
new QDoubleValidator(0.0000001, 0.5, 6));
1350 minSizeText.setToolTip(
1351 tr(
"The minimum size of the normal-estimation window."));
1352 maxSizeText.setToolTip(
1353 tr(
"The maximum size of the normal-estimation window."));
1355 tr(
"Sets the degrees of freedom parameter for the Wishart "
1356 "distribution. Due to non-independent data/errors in traces, "
1357 "this should be low (~10). Higher give more confident results - "
1359 distanceText.setToolTip(
1360 tr(
"The furthest distance to search for points on the opposite "
1361 "surface of a GeoObject during thickness calculations."));
1362 sampleText.setToolTip(
1363 tr(
"Sample n orientation estimates at each point in each trace to "
1364 "quantify uncertainty."));
1365 likPowerText.setToolTip(tr(
1366 "Fudge factor to change the balance between the prior and "
1367 "likelihood functions. Advanced use only - see docs for details."));
1368 strideText.setToolTip(
1369 tr(
"Standard deviation of the normal distribution used to "
1370 "calculate monte-carlo jumps during sampling. Larger numbers "
1371 "sample more widely but are slower to run."));
1373 QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
1374 QObject::connect(&buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept()));
1375 QObject::connect(&buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject()));
1377 vbox->addWidget(&minSizeLabel);
1378 vbox->addWidget(&minSizeText);
1379 vbox->addWidget(&maxSizeLabel);
1380 vbox->addWidget(&maxSizeText);
1381 vbox->addWidget(&dofLabel);
1382 vbox->addWidget(&dofText);
1383 vbox->addWidget(&likPowerLabel);
1384 vbox->addWidget(&likPowerText);
1385 vbox->addWidget(&sampleLabel);
1386 vbox->addWidget(&sampleText);
1387 vbox->addWidget(&strideLabel);
1388 vbox->addWidget(&strideText);
1389 vbox->addWidget(&calcThickLabel);
1390 vbox->addWidget(&calcThickChk);
1391 vbox->addWidget(&distanceLabel);
1392 vbox->addWidget(&distanceText);
1393 vbox->addWidget(&buttonBox);
1395 dlg.setLayout(vbox);
1399 if (
result == QDialog::Rejected) {
1404 minsize = minSizeText.text().toInt();
1405 maxsize = maxSizeText.text().toInt();
1406 dof = dofText.text().toInt();
1411 likPower = likPowerText.text().toDouble();
1413 stride = strideText.text().toDouble();
1422 "than minsize? Get your shit together..."),
1428 "may take a while..."),
1438 std::vector<double> eigValues;
1439 bool hasNormals =
true;
1440 bool broken =
false;
1445 prg.
setInfo(tr(
"Gathering data..."));
1450 std::vector<std::array<ccHObject*, 2>>
1452 std::vector<ccPointCloud*> pinchClouds;
1467 bool foundGeoObject =
false;
1473 foundGeoObject =
true;
1477 std::array<ccHObject*, 2> data = {
1486 datasets.push_back(data);
1494 pinchClouds.push_back(cloud);
1519 if (foundGeoObject) {
1540 std::array<ccHObject*, 2> data = {t,
nullptr};
1541 datasets.push_back(data);
1542 pinchClouds.push_back(
1550 if (datasets.empty()) {
1552 tr(
"[ccCompass] No GeoObjects or Traces could be found to "
1553 "estimate structure normals for. Please select some!"),
1558 for (
int _d = 0; _d < datasets.size(); _d++) {
1561 tr(
"Processing %1 of %2 datasets: Calculating fit planes...")
1563 .arg(datasets.size()));
1570 std::array<ccHObject*, 2> regions = datasets[_d];
1588 for (
unsigned r = 0; r < 2; r++) {
1589 if (regions[r] ==
nullptr) {
1597 objs.push_back(regions[r]);
1615 points[r]->reserveTheNormsTable();
1617 points[r]->setGlobalScale(
1622 for (
unsigned p = 0; p < t->
size(); p++) {
1635 tr(
"[ccCompass] Warning: Region %1 contains less than "
1636 "minsize points. Region ignored.")
1637 .arg(regions[r]->getUniqueID()),
1641 regions[r] =
nullptr;
1654 if (longAxis ==
nullptr) {
1657 tr(
"[ccCompass] Warning: Could not compute eigensystem "
1658 "for region %1. Region ignored.")
1659 .arg(regions[r]->getUniqueID()),
1665 std::vector<unsigned> pid;
1667 std::vector<double>
dist, px,
py, pz, nx, ny, nz;
1672 px.push_back(
points[r]->getPoint(0)->x);
1673 py.push_back(
points[r]->getPoint(0)->y);
1674 pz.push_back(
points[r]->getPoint(0)->z);
1675 nx.push_back(
points[r]->getPointNormal(0).x);
1676 ny.push_back(
points[r]->getPointNormal(0).y);
1677 nz.push_back(
points[r]->getPointNormal(0).z);
1679 for (
unsigned p = 0; p <
points[r]->size(); p++) {
1681 d =
points[r]->getPoint(p)->dot(*longAxis);
1688 px.push_back(
points[r]->getPoint(p)->x);
1689 py.push_back(
points[r]->getPoint(p)->y);
1690 pz.push_back(
points[r]->getPoint(p)->z);
1691 nx.push_back(
points[r]->getPointNormal(p).x);
1692 ny.push_back(
points[r]->getPointNormal(p).y);
1693 nz.push_back(
points[r]->getPointNormal(p).z);
1696 for (
int n = 0; n <
dist.size(); n++) {
1707 dist.insert(
dist.begin() + iid, d);
1708 pid.insert(pid.begin() + iid, p);
1709 px.insert(px.begin() + iid,
points[r]->getPoint(p)->x);
1710 py.insert(
py.begin() + iid,
points[r]->getPoint(p)->y);
1711 pz.insert(pz.begin() + iid,
points[r]->getPoint(p)->z);
1712 nx.insert(nx.begin() + iid,
points[r]->getPointNormal(p).x);
1713 ny.insert(ny.begin() + iid,
points[r]->getPointNormal(p).y);
1714 nz.insert(nz.begin() + iid,
points[r]->getPointNormal(p).z);
1722 std::vector<bool> breaks(
1729 unsigned char level = oct->findBestLevelForAGivenPopulationPerCell(
1734 for (
unsigned p = 0; p < pinchNodes->
size(); p++) {
1736 nCloud->
clear(
false);
1737 oct->findPointNeighbourhood(pinchNodes->
getPoint(p), nCloud, 1,
1748 double mnx, mny, mnz, lpd, lsf, phi, theta, alpha, len;
1751 std::vector<double> bestPd(
1753 std::numeric_limits<double>::
1756 std::vector<double> bestPhi(px.size(), 0);
1757 std::vector<double> bestTheta(px.size(), 0);
1758 std::vector<double> bestAlpha(px.size(), 0);
1759 std::vector<double> bestE1(px.size(), 0);
1760 std::vector<double> bestE2(px.size(), 0);
1761 std::vector<double> bestE3(px.size(), 0);
1762 std::vector<cloudViewer::SquareMatrixd> bestX(
1765 std::vector<CCVector3> sne(
1769 std::vector<int>
start(px.size(),
1771 std::vector<int> end(px.size(),
1773 std::vector<int> segmentID(
1775 std::vector<CCVector3>
normal(
1781 if (
abs(nx[0]) <= 0.000001 &&
abs(ny[0]) <= 0.0000001 &&
1782 abs(nz[0]) <= 0.00000001)
1786 tr(
"[ccCompass] Warning: Cannot compensate for "
1787 "outcrop-surface bias as point cloud has no "
1788 "normals. Structure normal estimates may be "
1789 "misleading or incorrect."),
1799 for (
unsigned _min = 0; _min < px.size() -
minsize; _min++) {
1802 static_cast<float>(px.size() -
minsize));
1807 for (
int i = 0; i < pinchClouds.size(); i++) {
1808 delete pinchClouds[i];
1814 for (
unsigned _max = _min +
minsize;
1819 n = _max - _min + 1;
1833 for (
unsigned p = _min; p <= _max; p++) {
1842 if (breaks[pid[p]]) {
1859 len = sqrt(mnx * mnx + mny * mny +
1874 for (
unsigned p = _min; p <= _max; p++) {
1875 X.m_values[0][0] += (px[p] - cx) * (px[p] - cx);
1876 X.m_values[1][1] += (
py[p] - cy) * (
py[p] - cy);
1877 X.m_values[2][2] += (pz[p] - cz) * (pz[p] - cz);
1878 X.m_values[0][1] += (px[p] - cx) * (
py[p] - cy);
1879 X.m_values[0][2] += (px[p] - cx) * (pz[p] - cz);
1880 X.m_values[1][2] += (
py[p] - cy) * (pz[p] - cz);
1885 cov.
m_values[0][0] =
X.m_values[0][0] / n;
1886 cov.
m_values[1][1] =
X.m_values[1][1] / n;
1887 cov.
m_values[2][2] =
X.m_values[2][2] / n;
1888 cov.
m_values[0][1] =
X.m_values[0][1] / n;
1889 cov.
m_values[0][2] =
X.m_values[0][2] / n;
1890 cov.
m_values[1][2] =
X.m_values[1][2] / n;
1903 X.m_values[1][0] =
X.m_values[0][1];
1905 X.m_values[2][0] =
X.m_values[0][2];
1907 X.m_values[2][1] =
X.m_values[1][2];
1912 cov, eigVectors, eigValues,
true);
1947 while (phi > 2 *
M_PI) {
1953 alpha = asin(eigVectors.
m_values[2][1] /
1961 while (alpha >
M_PI) {
1969 eigValues[0], eigValues[1],
1974 lpd += log(
prior(phi, theta, mnx, mny, mnz));
1980 for (
unsigned p = _min; p <= _max; p++) {
1981 if (lpd > bestPd[p])
1985 bestTheta[p] = theta;
1986 bestAlpha[p] = alpha;
1987 bestE1[p] = eigValues[0];
1988 bestE2[p] = eigValues[1];
1989 bestE3[p] = eigValues[2];
1996 _max *
static_cast<int>(px.size()) + _min;
2008 tr(
"[ccCompass] Warning: Region %1 contains no valid "
2009 "points (PinchNodes break the trace into small "
2010 "segments?). Region ignored.")
2011 .arg(regions[r]->getUniqueID()),
2015 regions[r] =
nullptr;
2022 points[r]->setName(
"SNE");
2042 weightSF->reserve(px.size());
2043 startSF->reserve(px.size());
2044 endSF->reserve(px.size());
2045 idSF->reserve(px.size());
2046 trend->reserve(px.size());
2047 plunge->reserve(px.size());
2048 pointID->reserve(px.size());
2051 for (
unsigned p = 0; p < px.size(); p++) {
2052 points[r]->setPointNormal(pid[p], sne[p]);
2053 weightSF->
setValue(pid[p], bestPd[p]);
2056 idSF->
setValue(pid[p], segmentID[p]);
2072 points[r]->setCurrentDisplayedScalarField(0);
2076 regions[r]->addChild(
points[r]);
2087 samples[r]->
setName(
"SNE_Samples");
2089 points[r]->getGlobalScale());
2094 static_cast<unsigned int>(px.size() *
oversample));
2125 std::random_device rd;
2126 std::default_random_engine generator(rd());
2127 std::normal_distribution<double> N(
2129 std::uniform_real_distribution<double> U(0.0, 1.0);
2132 for (
unsigned p = 0; p < px.size(); p++) {
2134 prg.
setInfo(tr(
"Processing %1 of %2 datasets: Sampling "
2137 .arg(datasets.size()));
2138 prg.
update(100.0f * p /
static_cast<float>(px.size()));
2142 for (
int i = 0; i < pinchClouds.size(); i++) {
2143 delete pinchClouds[i];
2144 if (samples[0] !=
nullptr) {
2147 if (samples[1] !=
nullptr) {
2155 if (bestX[p].m_values ==
nullptr) {
2165 double lpdCurrent = bestPd[p];
2166 double phi = bestPhi[p];
2167 double theta = bestTheta[p];
2168 double alpha = bestAlpha[p];
2169 double e1 = bestE1[p];
2170 double e2 = bestE2[p];
2171 double e3 = bestE3[p];
2172 double _phi, _theta, _alpha;
2179 _phi = phi + N(generator);
2180 _theta = theta + N(generator);
2181 _alpha = alpha + N(generator);
2190 lpdProposed += log(
prior(_phi, _theta,
normal[p].x,
2195 if (log(U(generator)) <= lpdProposed - lpdCurrent) {
2203 cos(phi) * cos(theta), -sin(theta));
2218 lpdCurrent = lpdProposed;
2224 tr(
"[ccCompass] Warning - MCMC sampler "
2225 "failed so sampling will be "
2238 weightSF->shrink_to_fit();
2239 startSF->shrink_to_fit();
2240 idSF->shrink_to_fit();
2241 trend->shrink_to_fit();
2242 plunge->shrink_to_fit();
2243 pointID->shrink_to_fit();
2256 samples[r]->
showSF(
true);
2259 regions[r]->addChild(samples[r]);
2260 m_app->
addToDB(samples[r],
false,
false,
false,
false);
2266 if (regions[0] !=
nullptr && regions[1] !=
nullptr &&
2272 prg.
setInfo(tr(
"Processing %1 of %2 datasets: Estimating "
2275 .arg(datasets.size()));
2276 for (
int r = 0; r < 2; r++) {
2284 points[r]->setCurrentDisplayedScalarField(
2285 points[r]->getScalarFieldIndexByName(
"Thickness"));
2291 if (samples[r] !=
nullptr) {
2293 samples[r]->addScalarField(
2295 thickSF_sample->reserve(samples[r]->
size());
2297 samples[r]->getScalarFieldIndexByName(
2300 samples[r]->getScalarFieldIndexByName(
2302 samples[r]->
showSF(
true);
2317 unsigned char level =
2318 oct->findBestLevelForAGivenNeighbourhoodSizeExtraction(
2325 for (
unsigned p = 0; p <
points[r]->size(); p++) {
2340 for (
int i = 0; i < pinchClouds.size(); i++) {
2341 delete pinchClouds[i];
2349 oct->findPointNeighbourhood(
points[r]->getPoint(p),
2350 nCloud, 1, level, d);
2357 if (samples[r] !=
nullptr) {
2358 for (
unsigned s = 0; s < samples[r]->
size();
2374 pEq[0] =
points[r]->getPointNormal(p).x;
2375 pEq[1] =
points[r]->getPointNormal(p).y;
2376 pEq[2] =
points[r]->getPointNormal(p).z;
2377 pEq[3] =
points[r]->getPoint(p)->dot(
2378 points[r]->getPointNormal(p));
2390 points[r]->setPointNormal(
2391 p,
points[r]->getPointNormal(p) * (d /
abs(d)));
2395 if (samples[r] !=
nullptr) {
2396 for (
unsigned s = 0; s < samples[r]->
size(); s++) {
2406 samples[r]->getPointNormal(s));
2412 s, samples[r]->getPointNormal(s) *
2421 if (thickSF_sample !=
nullptr) {
2430 for (
int i = 0; i < pinchClouds.size(); i++) {
2431 delete pinchClouds[i];
2437 tr(
"[ccCompass] Structure normal estimation complete."),
2453 std::vector<ccPolyline*> lines;
2457 lines.push_back(
static_cast<ccPolyline*
>(o));
2466 lines.push_back(
static_cast<ccPolyline*
>(c));
2473 maxx = std::numeric_limits<float>::lowest();
2475 maxy = std::numeric_limits<float>::lowest();
2477 maxz = std::numeric_limits<float>::lowest();
2479 if (lines.empty()) {
2481 "to compute estimate strain with."),
2489 if (poly->size() > 0)
2491 poly->getBoundingBox(bbMin, bbMax);
2505 QVBoxLayout* vbox =
new QVBoxLayout();
2506 QLabel boxSizeLabel(tr(
"Voxel Size:"));
2507 QLineEdit boxSizeText(QString::number(
binSize));
2508 boxSizeText.setValidator(
new QDoubleValidator(
2510 QCheckBox externalSNEChk(tr(
"Use external SNE:"));
2512 QCheckBox buildBlocksChk(tr(
"Build graphics:"));
2514 QLabel exagLabel(tr(
"Shape exaggeration factor:"));
2515 QLineEdit exagText(QString::number(
exag));
2516 boxSizeText.setValidator(
new QDoubleValidator(
2519 boxSizeText.setToolTip(
2520 tr(
"The voxel size for computing strain. This should be large "
2521 "enough that most boxes contain SNEs."));
2522 externalSNEChk.setToolTip(
2523 tr(
"Use SNE orientation estimates for outside the current cell if "
2524 "none are avaliable within it."));
2525 buildBlocksChk.setToolTip(
2526 tr(
"Build graphic strain ellipses and grid domains. Useful for "
2528 exagText.setToolTip(
2529 tr(
"Exaggerate the shape of strain ellipses for easier "
2532 QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
2533 QObject::connect(&buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept()));
2534 QObject::connect(&buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject()));
2535 vbox->addWidget(&boxSizeLabel);
2536 vbox->addWidget(&boxSizeText);
2537 vbox->addWidget(&buildBlocksChk);
2538 vbox->addWidget(&exagLabel);
2539 vbox->addWidget(&exagText);
2540 vbox->addWidget(&externalSNEChk);
2541 vbox->addWidget(&buttonBox);
2542 dlg.setLayout(vbox);
2546 if (
result == QDialog::Rejected) {
2551 binSize = boxSizeText.text().toDouble();
2554 exag = exagText.text().toDouble();
2576 int nx = (maxx - minx) /
binSize;
2577 int ny = (maxy - miny) /
binSize;
2578 int nz = (maxz - minz) /
binSize;
2583 prg.
setInfo(tr(
"Gathering GeoObjects..."));
2584 std::vector<std::unordered_set<ccGeoObject*>> geoObjectBins(
2585 nx * ny * nz, std::unordered_set<ccGeoObject*>());
2586 for (
int i = 0; i < lines.size(); i++) {
2587 prg.
update(100.0 * i /
float(lines.size()));
2594 for (
unsigned p = 0; p < lines[i]->size(); p++) {
2601 int idx = x + nx * (y + ny * z);
2604 if (idx < geoObjectBins.size()) {
2605 geoObjectBins[idx].insert(g);
2608 const QString message =
2609 tr(
"[ccCompass] Error: cell %1 is outside of mesh "
2610 "bounds (with total size = %2 [%3,%4,%5]).")
2612 .arg(geoObjectBins.size())
2628 std::vector<int> nStructures(nx * ny * nz,
2631 std::vector<int> nIgnored(
2635 std::vector<ccPointCloud*> dataInCell(nx * ny * nz,
nullptr);
2646 std::vector<cloudViewer::SquareMatrixd> F(
2651 prg.
setInfo(tr(
"Calculating strain tensors..."));
2652 for (
int x = 0; x < nx; x++) {
2653 for (
int y = 0; y < ny; y++) {
2654 for (
int z = 0; z < nz; z++) {
2655 int idx = x + nx * (y + ny * z);
2656 prg.
update((100.0f * idx) / (nx * ny * nz));
2665 dataInCell[idx]->setName(tr(
"DataInCell"));
2667 dataInCell[idx]->addScalarField(thickness);
2673 double average_thickness = 0.0;
2678 int n_lower = 0, n_upper = 0;
2689 if (thickSF != -1) {
2702 for (
unsigned p = 0; p < s->
size(); p++) {
2710 int _idx = _x + nx * (_y + ny * _z);
2716 if (average_direction.
norm2() ==
2718 average_direction =
normal;
2721 average_direction) <
2727 average_direction +=
2730 average_direction +=
normal;
2732 average_thickness +=
2745 dataInCell[idx]->reserve(1);
2746 thickness->reserve(1);
2750 dataInCell[idx]->addPoint(V);
2752 ->reserveTheNormsTable();
2753 dataInCell[idx]->addNorm(
2762 ->setCurrentDisplayedScalarField(
2764 dataInCell[idx]->showSF(
true);
2773 if (n_upper == 0 && n_lower == 0) {
2780 average_thickness /= (n_lower + n_upper);
2813 (
binSize + average_thickness) /
2824 F[idx] = F_increment * F[idx];
2830 gl16[12] = minx + (x + 0.5) *
binSize;
2831 gl16[13] = miny + (y + 0.5) *
binSize;
2832 gl16[14] = minz + (z + 0.5) *
binSize;
2838 &gl,
"BlockStrain");
2839 dataInCell[idx]->addChild(box);
2854 lines[0]->getGlobalScale());
2858 points->setGlobalShift(lines[0]->getGlobalShift());
2860 points->reserve(validCells);
2864 points->addScalarField(nValidSF);
2865 points->addScalarField(nIgnoredSF);
2866 points->addScalarField(JSF);
2867 nValidSF->reserve(validCells);
2868 nIgnoredSF->reserve(validCells);
2869 JSF->reserve(validCells);
2871 for (
int i = 0; i < 3; i++) {
2872 for (
int j = 0; j < 3; j++) {
2877 eSF[i][j]->reserve(validCells);
2878 points->addScalarField(eSF[i][j]);
2885 ellipses =
new ccHObject(tr(
"Ellipses"));
2887 points->addChild(ellipses);
2889 points->addChild(blocks);
2894 for (
int x = 0; x < nx; x++) {
2895 for (
int y = 0; y < ny; y++) {
2896 for (
int z = 0; z < nz; z++) {
2897 int idx = x + nx * (y + ny * z);
2898 if (nStructures[idx] > 0) {
2902 minz + ((z + 0.5) *
binSize));
2909 std::vector<double> eigValues;
2912 B, eigVectors, eigValues,
true);
2917 U_local.
setValue(0, 0, sqrt(eigValues[0]));
2918 U_local.
setValue(1, 1, sqrt(eigValues[1]));
2919 U_local.
setValue(2, 2, sqrt(eigValues[2]));
2922 (U_local * eigVectors);
2926 double J = eigValues[0] * eigValues[1] *
2931 for (
int i = 0; i < 3; i++) {
2932 for (
int j = 0; j < 3; j++) {
2942 F[idx], eigVectors, eigValues,
2951 0, 0, pow(eigValues[0] / eigValues[1],
exag));
2953 1, 1, pow(eigValues[1] / eigValues[1],
exag));
2955 2, 2, pow(eigValues[2] / eigValues[1],
exag));
2958 transMat = eigVectors *
2974 QVariantMap* map =
new QVariantMap();
2975 map->insert(
"Exx", U.
getValue(0, 0) - 1.0);
2976 map->insert(
"Exy", U.
getValue(0, 1));
2977 map->insert(
"Exz", U.
getValue(0, 2));
2978 map->insert(
"Eyx", U.
getValue(1, 0));
2979 map->insert(
"Eyy", U.
getValue(1, 1) - 1.0);
2980 map->insert(
"Eyz", U.
getValue(1, 2));
2981 map->insert(
"Ezx", U.
getValue(2, 0));
2982 map->insert(
"Ezy", U.
getValue(2, 1));
2983 map->insert(
"Ezz", U.
getValue(2, 2) - 1.0);
2984 map->insert(
"J", J);
3001 delete dataInCell[idx];
3011 for (
int i = 0; i < 3; i++) {
3012 for (
int j = 0; j < 3; j++) {
3020 points->setCurrentDisplayedScalarField(0);
3037 std::vector<ccPolyline*> lines;
3038 std::vector<ccSNECloud*> sne;
3042 lines.push_back(
static_cast<ccPolyline*
>(o));
3070 lines.push_back(
static_cast<ccPolyline*
>(c));
3075 if (lines.empty()) {
3077 "found to compute P21."),
3086 if (outcrop ==
nullptr) {
3087 outcrop =
dynamic_cast<ccPointCloud*
>(p->getAssociatedCloud());
3091 if (outcrop != p->getAssociatedCloud()) {
3093 tr(
"[ccCompass] Error - cannot calculate P21 intensity for "
3094 "structures digitised from different point clouds."),
3109 weight->reserve(p->size());
3110 for (
unsigned i = 0; i < p->size(); i++) {
3123 QVBoxLayout* vbox =
new QVBoxLayout();
3124 QLabel boxSizeLabel(tr(
"Search Radius:"));
3125 QLineEdit boxSizeText(QString::number(
searchR));
3126 boxSizeText.setValidator(
new QDoubleValidator(
3128 QLabel subsampleLabel(tr(
"Subsample:"));
3129 QLineEdit subsampleText(QString::number(
subsample));
3130 boxSizeText.setValidator(
3133 boxSizeText.setToolTip(
3134 tr(
"The search radius used to define the region to compute P21 "
3136 subsampleText.setToolTip(
3137 tr(
"Only sample P21 on the each n'th point in the original outcrop "
3138 "model (decreases calculation time)."));
3140 QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
3141 QObject::connect(&buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept()));
3142 QObject::connect(&buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject()));
3143 vbox->addWidget(&boxSizeLabel);
3144 vbox->addWidget(&boxSizeText);
3145 vbox->addWidget(&subsampleLabel);
3146 vbox->addWidget(&subsampleText);
3147 vbox->addWidget(&buttonBox);
3148 dlg.setLayout(vbox);
3152 if (
result == QDialog::Rejected) {
3157 searchR = boxSizeText.text().toDouble();
3158 subsample = subsampleText.text().toUInt();
3160 "search radius of of %1.")
3186 P21->reserve(outputCloud->
size());
3195 unsigned char trace_level =
3196 trace_oct->findBestLevelForAGivenNeighbourhoodSizeExtraction(
3205 prg.
setInfo(tr(
"Sampling structures..."));
3210 for (
unsigned p = 0; p < outputCloud->
size(); p++) {
3212 prg.
update(100.0 * p /
static_cast<float>(outputCloud->
size()));
3221 trace_oct->getPointsInSphericalNeighbourhood(
3227 for (
int i = 0; i < region.size(); i++) {
3228 sum += weight->
getValue(region[i].pointIndex);
3237 prg.
setInfo(tr(
"Calculating patch areas..."));
3240 for (
unsigned p = 0; p < outputCloud->
size(); p++) {
3246 prg.
update(100.0 * p /
static_cast<float>(outputCloud->
size()));
3255 n_outcrop = outcrop_oct->getPointsInSphericalNeighbourhood(
3268 outputCloud->
showSF(
true);
3276 std::vector<ccGeoObject*> objs;
3277 std::vector<ccPolyline*> lines;
3287 lines.push_back(
static_cast<ccPolyline*
>(o));
3303 lines.push_back(
static_cast<ccPolyline*
>(c));
3318 tr(
"ConvertedLines"));
3328 for (
int i = 0; i < nRegions; i++) {
3335 for (ccHObject::Container::const_iterator it = poly.begin();
3336 it != poly.end(); it++) {
3344 for (
unsigned int p = 0; p < t->
size(); p++) {
3352 if (
points->size() > 0) {
3354 points->setCurrentDisplayedScalarField(sfid);
3361 "converted - none found."),
3371 tr(
"ConvertedLines"));
3379 sf->reserve(
points->size() + t->size());
3380 for (
unsigned p = 0; p < t->size(); p++) {
3381 points->addPoint(*t->getPoint(p));
3385 if (
points->size() > 0) {
3387 points->setCurrentDisplayedScalarField(sfid);
3402 if (selection.empty()) {
3408 std::vector<ccGeoObject*> geoObjs;
3416 geoObjs.push_back(g);
3425 int matchingChars = 0;
3429 if (obj->getName().contains(
3432 if (g->getName().size() > matchingChars) {
3433 matchingChars = g->
getName().size();
3445 if (obj->getName().contains(
"upper")) {
3448 }
else if (obj->getName().contains(
"lower")) {
3457 obj->setSelected(
false);
3462 "found that matches %1.")
3463 .arg(obj->getName()),
3517 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
3534 object->showNameIn3D(checked);
3538 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
3560 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
3579 m_dlg->mapMode->setChecked(
true);
3580 m_dlg->compassMode->setChecked(
false);
3597 m_dlg->mapMode->setChecked(
false);
3598 m_dlg->compassMode->setChecked(
true);
3613 if (
name.contains(
"_")) {
3614 number =
name.split(
"_")[1].toInt();
3618 name += QString::asprintf(
"_%d", number);
3622 tr(
"GeoObject Name:"), QLineEdit::Normal,
3634 tr(
"interpretation")) {
3639 for (
unsigned c = 0;
3645 ->
getName() == tr(
"interpretation")) {
3659 if (!interp_group) {
3660 interp_group =
new ccHObject(tr(
"interpretation"));
3662 m_app->
addToDB(interp_group,
false,
true,
false,
false);
3667 interp_group->
addChild(newGeoObject);
3668 m_app->
addToDB(newGeoObject,
false,
true,
false,
false);
3681 m_mapDlg->setInteriorButton->setChecked(
true);
3682 m_mapDlg->setUpperButton->setChecked(
false);
3683 m_mapDlg->setLowerButton->setChecked(
false);
3690 m_mapDlg->setInteriorButton->setChecked(
false);
3691 m_mapDlg->setUpperButton->setChecked(
true);
3692 m_mapDlg->setLowerButton->setChecked(
false);
3699 m_mapDlg->setInteriorButton->setChecked(
false);
3700 m_mapDlg->setUpperButton->setChecked(
false);
3701 m_mapDlg->setLowerButton->setChecked(
true);
3711 tr(
"Please select a point cloud containing your field data "
3712 "(this can be loaded from a text file)"),
3719 tr(
"Please select a point cloud containing your field data "
3720 "(this can be loaded from a text file)"),
3730 QVBoxLayout* vbox =
new QVBoxLayout();
3731 QLabel dipLabel(tr(
"Dip Field:"));
3732 QLabel dipDirLabel(tr(
"Dip-Direction Field:"));
3733 QLabel sizeLabel(tr(
"Plane Size"));
3736 QLineEdit planeSize(
"2.0");
3737 planeSize.setValidator(
3750 QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
3751 QObject::connect(&buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept()));
3752 QObject::connect(&buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject()));
3754 vbox->addWidget(&dipLabel);
3755 vbox->addWidget(&dipCombo);
3756 vbox->addWidget(&dipDirLabel);
3757 vbox->addWidget(&dipDirCombo);
3758 vbox->addWidget(&buttonBox);
3759 vbox->addWidget(&sizeLabel);
3760 vbox->addWidget(&planeSize);
3761 dlg.setLayout(vbox);
3765 if (
result == QDialog::Rejected) {
3771 dipCombo.currentText().toStdString().c_str());
3773 dipDirCombo.currentText().toStdString().c_str());
3774 double size = planeSize.text().toDouble();
3775 if (dipSF == dipDirSF) {
3777 "must be different!"),
3783 for (
unsigned p = 0; p < cld->
size(); p++) {
3791 .arg(
static_cast<int>(dip), 2, 10, QChar(
'0'))
3792 .arg(
static_cast<int>(dipdir), 3, 10, QChar(
'0')));
3804 bool needToApplyTrans =
false;
3805 bool needToApplyRot =
false;
3808 std::numeric_limits<PointCoordinateType>::epsilon());
3809 needToApplyTrans = needToApplyRot || ((C - Cd).norm2d() != 0);
3811 if (needToApplyTrans) {
3813 needToApplyTrans =
true;
3815 if (needToApplyRot) {
3819 PC_ONE - std::numeric_limits<PointCoordinateType>::epsilon()) {
3828 rotation = rotZ * rotX;
3833 trans = rotation * trans;
3835 if (needToApplyTrans) {
3838 if (needToApplyRot || needToApplyTrans) {
3856 tr(
"Please select a point cloud containing your field data "
3857 "(this can be loaded from a text file)"),
3864 tr(
"Please select a point cloud containing your field data "
3865 "(this can be loaded from a text file)"),
3875 QVBoxLayout* vbox =
new QVBoxLayout();
3876 QLabel dipLabel(tr(
"Trend Field:"));
3877 QLabel dipDirLabel(tr(
"Plunge Field:"));
3878 QLabel sizeLabel(tr(
"Display Length"));
3881 QLineEdit planeSize(
"2.0");
3882 planeSize.setValidator(
3891 QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
3892 QObject::connect(&buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept()));
3893 QObject::connect(&buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject()));
3895 vbox->addWidget(&dipLabel);
3896 vbox->addWidget(&dipCombo);
3897 vbox->addWidget(&dipDirLabel);
3898 vbox->addWidget(&dipDirCombo);
3899 vbox->addWidget(&buttonBox);
3900 vbox->addWidget(&sizeLabel);
3901 vbox->addWidget(&planeSize);
3902 dlg.setLayout(vbox);
3906 if (
result == QDialog::Rejected) {
3912 dipCombo.currentText().toStdString().c_str());
3914 dipDirCombo.currentText().toStdString().c_str());
3915 double size = planeSize.text().toDouble();
3916 if (dipSF == dipDirSF) {
3918 tr(
"Error: Trend and plunge scalar fields must be different!"),
3924 for (
unsigned p = 0; p < cld->
size(); p++) {
3945 points->setName(tr(
"verts"));
3953 lineation->
setName(QStringLiteral(
"%1->%2")
3954 .arg(qRound(plunge))
3955 .arg(qRound(trend)));
3966 QString
filename = QFileDialog::getSaveFileName(
3967 m_dlg, tr(
"SVG Output file"),
"", tr(
"SVG files (*.svg)"));
3973 if (QFileInfo(
filename).suffix() !=
"svg") {
3978 std::vector<ccHObject*>
3986 hidden.push_back(o);
4003 bu.open(QIODevice::WriteOnly);
4010 if (svg_file.open(QIODevice::WriteOnly)) {
4011 QTextStream svg_stream(&svg_file);
4020 svg_stream << QString::asprintf(
"<svg width=\"%d\" height=\"%d\">",
4025 svg_stream << QString::asprintf(
4026 "<image height = \"%d\" width = \"%d\" "
4027 "xlink:href = \"data:image/png;base64,",
4046 "polylines to .svg file.")
4052 ".svg - no polylines found!"),
4074 *out <<
"<polyline fill=\"none\" stroke=\"black\" points=\"";
4079 if (
params.perspective) {
4088 for (
unsigned i = 0; i < line->
size(); i++) {
4094 params.project(P, coords2D);
4097 *out << QString::asprintf(
4098 "%.3f,%.3f ", coords2D.
x * zoom,
4110 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
4120 QString
filename = QFileDialog::getSaveFileName(
4121 m_dlg, tr(
"Output file"),
"",
4122 tr(
"CSV files (*.csv *.txt);XML (*.xml)"));
4130 if (fi.suffix() ==
"xml") {
4141 int thicknesses = 0;
4149 QString baseName = fi.absolutePath() +
"/" + fi.completeBaseName();
4150 QString ext = fi.suffix();
4151 if (!ext.isEmpty()) {
4154 QString plane_fn = baseName +
"_planes" + ext;
4155 QString trace_fn = baseName +
"_traces" + ext;
4156 QString lineation_fn = baseName +
"_lineations" + ext;
4157 QString thickness_fn = baseName +
"_thickness" + ext;
4160 QFile plane_file(plane_fn);
4161 QFile trace_file(trace_fn);
4162 QFile lineation_file(lineation_fn);
4163 QFile thickness_file(thickness_fn);
4166 if (plane_file.open(QIODevice::WriteOnly) &&
4167 trace_file.open(QIODevice::WriteOnly) &&
4168 lineation_file.open(QIODevice::WriteOnly) &&
4169 thickness_file.open(QIODevice::WriteOnly)) {
4171 QTextStream plane_stream(&plane_file);
4172 QTextStream trace_stream(&trace_file);
4173 QTextStream lineation_stream(&lineation_file);
4174 QTextStream thickness_stream(&thickness_file);
4177 plane_stream <<
"Name,Strike,Dip,Dip_Dir,Cx,Cy,Cz,Nx,Ny,Nz,Sample_"
4178 "Radius,RMS,Gx,Gy,Gz,Length"
4180 trace_stream <<
"Name,Trace_id,Point_id,Start_x,Start_y,Start_z,End_x,"
4181 "End_y,End_z,Cost,Cost_Mode"
4183 lineation_stream <<
"Name,Sx,Sy,Sz,Ex,Ey,Ez,Trend,Plunge,Length"
4185 thickness_stream <<
"Name,Sx,Sy,Sz,Ex,Ey,Ez,Trend,Plunge,Thickness"
4203 plane_stream.flush();
4205 trace_stream.flush();
4207 lineation_stream.flush();
4208 lineation_file.close();
4209 thickness_stream.flush();
4210 thickness_file.close();
4215 tr(
"[ccCompass] Successfully exported plane data."),
4220 plane_file.remove();
4224 tr(
"[ccCompass] Successfully exported trace data."),
4229 trace_file.remove();
4233 tr(
"[ccCompass] Successfully exported lineation data."),
4238 lineation_file.remove();
4242 tr(
"[ccCompass] Successfully exported thickness data."),
4247 thickness_file.remove();
4251 "ensure CC has write access to this location."),
4259 const QString& parentName) {
4262 if (parentName.isEmpty()) {
4265 name = QString(
"%1.%2").arg(parentName, object->
getName());
4277 std::vector<ccHObject*> clouds;
4280 unsigned int npoints = 0;
4283 if (npoints <= p->
size()) {
4284 npoints = p->
size();
4298 *out <<
name <<
",";
4299 *out <<
object->getMetaData(
"Strike").toString() <<
","
4300 <<
object->getMetaData(
"Dip").toString() <<
","
4301 <<
object->getMetaData(
"DipDir").toString() <<
",";
4302 *out <<
object->getMetaData(
"Cx").toString() <<
","
4303 <<
object->getMetaData(
"Cy").toString() <<
","
4304 <<
object->getMetaData(
"Cz").toString() <<
",";
4305 *out <<
object->getMetaData(
"Nx").toString() <<
","
4306 <<
object->getMetaData(
"Ny").toString() <<
","
4307 <<
object->getMetaData(
"Nz").toString() <<
",";
4308 *out <<
object->getMetaData(
"Radius").toString() <<
","
4309 <<
object->getMetaData(
"RMS").toString() <<
",";
4311 if (ss !=
nullptr) {
4315 *out << G.
x <<
"," << G.
y <<
"," << G.
z <<
",";
4332 if (N.
z < 0.0) N *= -1.0;
4335 float strike, dip, dipdir;
4340 *out <<
name <<
",";
4341 *out << strike <<
"," << dip <<
"," << dipdir
4343 *out << L.
x <<
"," << L.
y <<
"," << L.
z <<
",";
4344 *out << N.
x <<
"," << N.
y <<
"," << N.
z <<
",";
4351 if (ss !=
nullptr) {
4362 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
4372 const QString& parentName) {
4375 if (parentName.isEmpty()) {
4378 name = QString(
"%1.%2").arg(parentName, object->
getName());
4390 int tID =
object->getUniqueID();
4391 if (p->
size() >= 2) {
4396 for (
unsigned i = 1; i < p->
size(); i++) {
4409 *out <<
name <<
",";
4411 *out << i - 1 <<
",";
4418 *out << cost <<
",";
4426 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
4436 const QString& parentName,
4440 if (parentName.isEmpty()) {
4443 name = QString(
"%1.%2").arg(parentName, object->
getName());
4448 if (((thicknesses ==
false) &&
4450 ((thicknesses ==
true) &&
4454 *out <<
name <<
",";
4455 *out <<
object->getMetaData(
"Sx").toString() <<
","
4456 <<
object->getMetaData(
"Sy").toString() <<
","
4457 <<
object->getMetaData(
"Sz").toString() <<
",";
4458 *out <<
object->getMetaData(
"Ex").toString() <<
","
4459 <<
object->getMetaData(
"Ey").toString() <<
","
4460 <<
object->getMetaData(
"Ez").toString() <<
",";
4461 *out <<
object->getMetaData(
"Trend").toString() <<
","
4462 <<
object->getMetaData(
"Plunge").toString() <<
","
4468 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
4480 if (file.open(QIODevice::WriteOnly))
4483 QXmlStreamWriter xmlWriter(&file);
4485 xmlWriter.setAutoFormatting(
true);
4486 xmlWriter.writeStartDocument();
4504 xmlWriter.writeEndDocument();
4511 tr(
"[ccCompass] Successfully exported data-tree to xml."),
4515 "ensure CC has write access to this location."),
4528 out->writeStartElement(
"GEO_OBJECT");
4531 out->writeStartElement(
"PLANE");
4534 out->writeStartElement(
"TRACE");
4537 out->writeStartElement(
"THICKNESS");
4539 out->writeStartElement(
"SNE");
4542 out->writeStartElement(
"LINEATION");
4544 out->writeStartElement(
"CLOUD");
4548 out->writeStartElement(
"POLYLINE");
4551 out->writeStartElement(
4560 out->writeAttribute(
"name", object->
getName());
4561 out->writeAttribute(
"id", QString::asprintf(
"%d", object->
getUniqueID()));
4564 for (QMap<QString, QVariant>::const_iterator it =
4566 it != object->
metaData().end(); it++) {
4567 out->writeTextElement(it.key(), it.value().toString());
4575 out->writeTextElement(
4586 for (QMap<QString, QVariant>::const_iterator it =
4588 it != temp->
metaData().end(); it++) {
4589 out->writeTextElement(it.key(), it.value().toString());
4603 trace =
static_cast<ccTrace*
>(object);
4606 QString x, y, z, nx, ny, nz, cost, wIDs, w_local_ids;
4613 bool hasNormals =
false;
4615 if (poly->
size() >= 2) {
4617 for (
unsigned i = 1; i < poly->
size(); i++) {
4623 x += QString::asprintf(
"%f,", p1.
x);
4624 y += QString::asprintf(
"%f,", p1.
y);
4625 z += QString::asprintf(
"%f,", p1.
z);
4632 cost += QString::asprintf(
"%d,", c);
4636 nx += QString::asprintf(
"%f,", n1.
x);
4637 ny += QString::asprintf(
"%f,", n1.
y);
4638 nz += QString::asprintf(
"%f,", n1.
z);
4639 if (!hasNormals && !(n1.
x == 0 && n1.
y == 0 && n1.
z == 0)) {
4648 x += QString::asprintf(
"%f", p2.
x);
4649 y += QString::asprintf(
"%f", p2.
y);
4650 z += QString::asprintf(
"%f", p2.
z);
4653 nx += QString::asprintf(
"%f", n2.
x);
4654 ny += QString::asprintf(
"%f", n2.
y);
4655 nz += QString::asprintf(
"%f", n2.
z);
4666 wIDs += QString::asprintf(
"%d,", trace->
getWaypoint(w));
4676 for (; i < trace->
size(); i++) {
4683 w_local_ids += QString::asprintf(
"%d,", i);
4687 out->writeStartElement(
"POINTS");
4688 out->writeAttribute(
"count", QString::asprintf(
"%d", poly->
size()));
4691 out->writeAttribute(
"normals",
"True");
4693 out->writeAttribute(
"normals",
"False");
4696 out->writeTextElement(
"x", x);
4697 out->writeTextElement(
"y", y);
4698 out->writeTextElement(
"z", z);
4701 out->writeTextElement(
"nx", nx);
4702 out->writeTextElement(
"ny", ny);
4703 out->writeTextElement(
"nz", nz);
4708 out->writeTextElement(
"cost", cost);
4709 out->writeTextElement(
"control_point_cloud_ids", wIDs);
4710 out->writeTextElement(
"control_point_local_ids", w_local_ids);
4714 out->writeEndElement();
4721 out->writeTextElement(
"GLOBAL_SCALE",
4723 out->writeTextElement(
4725 out->writeTextElement(
4727 out->writeTextElement(
4733 out->writeStartElement(
"POINTS");
4734 out->writeAttribute(
"count",
4735 QString::asprintf(
"%d", cloud->
size()));
4738 QString x, y, z, nx, ny, nz, thickness, weight, trend, plunge;
4748 for (
unsigned p = 0; p < cloud->
size(); p++) {
4749 x += QString::asprintf(
"%f,", cloud->
getPoint(p)->
x);
4750 y += QString::asprintf(
"%f,", cloud->
getPoint(p)->
y);
4751 z += QString::asprintf(
"%f,", cloud->
getPoint(p)->
z);
4755 weight += QString::asprintf(
"%f,", wSF->
getValue(p));
4756 trend += QString::asprintf(
"%f,", trendSF->
getValue(p));
4757 plunge += QString::asprintf(
"%f,", plungeSF->
getValue(p));
4762 thickness += QString::asprintf(
"%f,", tSF->
getValue(p));
4767 out->writeTextElement(
"x", x);
4768 out->writeTextElement(
"y", y);
4769 out->writeTextElement(
"z", z);
4770 out->writeTextElement(
"nx", nx);
4771 out->writeTextElement(
"ny", ny);
4772 out->writeTextElement(
"nz", nz);
4773 out->writeTextElement(
"weight", weight);
4774 out->writeTextElement(
"trend", trend);
4775 out->writeTextElement(
"plunge", plunge);
4776 if (tSF !=
nullptr) {
4777 out->writeTextElement(
"thickness", thickness);
4781 out->writeEndElement();
4786 for (
unsigned i = 0; i <
object->getChildrenNumber(); i++) {
4791 out->writeEndElement();
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
cmdLineReadable * params[]
static unsigned subsample
static bool buildGraphics
double prior(double phi, double theta, double nx, double ny, double nz)
static unsigned int oversample
double logWishart(cloudViewer::SquareMatrixd X, int nobserved, double phi, double theta, double alpha, double e1, double e2, double e3, double lsf)
static unsigned int maxsize
static unsigned int minsize
static bool useExternalSNE
double logWishSF(cloudViewer::SquareMatrixd X, int nobserved)
static bool calcThickness
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool ComputeEigenValuesAndVectors(const SquareMatrix &matrix, SquareMatrix &eigenVectors, EigenValues &eigenValues, bool absoluteValues=true, unsigned maxIterationCount=50)
Computes eigen vectors (and values) with the Jacobian method.
static bool SortEigenValuesAndVectors(SquareMatrix &eigenVectors, EigenValues &eigenValues)
void normalize()
Sets vector norm to unity.
Type dot(const Vector3Tpl &v) const
Dot product.
Type norm2() const
Returns vector square norm.
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
QAction * m_fitPlaneToGeoObject
QAction * m_measure_thickness_twoPoint
QAction * m_distributeSelection
QAction * m_measure_thickness
QAction * m_estimateNormals
QAction * m_mergeSelected
QAction * m_estimateStrain
QAction * m_loadLineations
QAction * m_loadFoliations
QAction * m_recalculateFitPlanes
void toggleStipple(bool checked)
void cleanupBeforeToolChange(bool autoRestartPicking=true)
void addGeoObject(bool singleSurface=false)
virtual bool eventFilter(QObject *obj, QEvent *event) override
void estimateStructureNormals()
ccFitPlaneTool * m_fitPlaneTool
void recurseStipple(ccHObject *object, bool checked)
bool madeByMe(ccHObject *object)
void pointPicked(ccHObject *entity, unsigned itemIdx, int x, int y, const CCVector3 &P)
void toggleLabels(bool checked)
bool stopMeasuring(bool finalStop=false)
int writeToXML(const QString &filename)
ccHObject * getInsertPoint()
void distributeSelection()
ccLineationTool * m_lineationTool
void recalculateFitPlanes()
ccTopologyTool * m_topologyTool
void recurseLabels(ccHObject *object, bool checked)
void hideAllPointClouds(ccHObject *o)
int writeObjectXML(ccHObject *object, QXmlStreamWriter *out)
std::vector< int > m_hiddenObjects
void recurseNormals(ccHObject *object, bool checked)
int writeTracesSVG(ccHObject *object, QTextStream *out, int height, float zoom)
void toggleNormals(bool checked)
ccGeoObject * m_geoObject
int writeLineations(ccHObject *object, QTextStream *out, const QString &parentName=QString(), bool thickness=false)
void convertToPointCloud()
ccThicknessTool * m_thicknessTool
int writeTraces(ccHObject *object, QTextStream *out, const QString &parentName=QString())
ccPinchNodeTool * m_pinchNodeTool
QString m_lastGeoObjectName
virtual void onItemPicked(const ccPickingListener::PickedItem &pi) override
ccTraceTool * m_traceTool
void fitPlaneToGeoObject()
void recalculateSelectedTraces()
int writePlanes(ccHObject *object, QTextStream *out, const QString &parentName=QString())
virtual QString getName() const override
Returns (short) name (for menu entry, etc.)
virtual QString getDescription() const override
Returns long name/description (for tooltip, etc.)
virtual QIcon getIcon() const override
Returns icon.
virtual bool isVisible() const
Returns whether entity is visible or not.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showNameIn3D(bool state)
Sets whether name should be displayed in 3D.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
static ccFitPlane * Fit(cloudViewer::GenericIndexedCloudPersist *cloud, double *rms)
static bool isFitPlane(ccHObject *object)
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
static ccGLMatrixTpl< float > FromToRotation(const Vector3Tpl< float > &from, const Vector3Tpl< float > &to)
Creates a transformation matrix that rotates a vector to another.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
void initFromParameters(T alpha_rad, const Vector3Tpl< T > &axis3D, const Vector3Tpl< T > &t3D)
Inits transformation from a rotation axis, an angle and a translation.
Float version of ccGLMatrixTpl.
virtual void showWired(bool state)
Sets whether mesh should be displayed as a wire or with plain facets.
void enableStippling(bool state)
Enables polygon stippling.
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
Generic primitive interface.
virtual void setColor(const ecvColor::Rgb &col)
Sets primitive color (shortcut)
virtual ccGLMatrix & getTransformation()
Returns the transformation that is currently applied to the vertices.
void setActive(bool active)
static ccGeoObject * getGeoObjectParent(ccHObject *object)
static bool isGeoObjectInterior(ccHObject *object)
static const int INTERIOR
static const int UPPER_BOUNDARY
static int getGeoObjectRegion(ccHObject *object)
ccHObject * getRegion(int mappingRegion)
static bool isGeoObjectLower(ccHObject *object)
static bool isGeoObject(ccHObject *object)
static bool isSingleSurfaceGeoObject(ccHObject *object)
static const int LOWER_BOUNDARY
static bool isGeoObjectUpper(ccHObject *object)
Hierarchical CLOUDVIEWER Object.
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
void transferChildren(ccHObject &newParent, bool forceFatherDependent=false)
Transfer all children to another parent.
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
unsigned getChildrenNumber() const
Returns the number of children.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
void detachAllChildren()
Removes a specific child.
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
void updateMetadata() override
static bool isLineation(ccHObject *obj)
QAction * m_create_geoObject
QAction * m_create_geoObjectSS
static CCVector3 ConvertDipAndDipDirToNormal(PointCoordinateType dip_deg, PointCoordinateType dipDir_deg, bool upward=true)
static void ConvertNormalToStrikeAndDip(const CCVector3 &N, PointCoordinateType &strike_deg, PointCoordinateType &dip_deg)
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
static bool isNote(ccHObject *obj)
const QVariantMap & metaData() const
Returns meta-data map (const only)
virtual QString getName() const
Returns object name.
virtual unsigned getUniqueID() const
Returns object unique ID.
void setMetaData(const QString &key, const QVariant &data)
Sets a meta-data element.
bool isA(CV_CLASS_ENUM type) const
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
virtual void setName(const QString &name)
Sets object name.
virtual void setEnabled(bool state)
Sets the "enabled" property.
virtual bool isEnabled() const
Returns whether the object is enabled or not.
bool isKindOf(CV_CLASS_ENUM type) const
QSharedPointer< ccOctree > Shared
Shared pointer.
virtual void stop(bool accepted)
Stops process/dialog.
virtual bool start()
Starts process.
virtual bool linkWith(QWidget *win)
Links the overlay dialog with a MDI window.
void removeListener(ccPickingListener *listener, bool autoStopPickingIfLast=true)
Removes a listener.
bool addListener(ccPickingListener *listener, bool exclusive=false, bool autoStartPicking=true, ecvDisplayTools::PICKING_MODE mode=ecvDisplayTools::POINT_OR_TRIANGLE_PICKING)
Adds a listener.
static bool isPinchNode(ccHObject *obj)
void showNormalVector(bool state)
Show normal vector.
CCVector3 getCenter() const
Returns the center.
CCVector3 getNormal() const override
Returns the entity normal.
void setXWidth(PointCoordinateType w, bool autoUpdate=true)
Sets 'X' width.
PointCoordinateType getXWidth() const
Returns 'X' width.
PointCoordinateType getYWidth() const
Returns 'Y' width.
void setYWidth(PointCoordinateType h, bool autoUpdate=true)
Sets 'Y' width.
virtual bool start()
Starts the plugin.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
void normalsHaveChanged()
Notify a modification of normals display parameters or contents.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
void clear() override
Clears the entity from all its points and features.
void setPointNormal(size_t pointIndex, const CCVector3 &N)
Sets a particular point normal (shortcut)
void shrinkToFit()
Removes unused capacity.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
static bool isPointPair(ccHObject *object)
void setWidth(PointCoordinateType width)
Sets the width of the line.
static bool isSNECloud(ccHObject *obj)
A scalar field associated to display-related parameters.
void computeMinAndMax() override
Determines the min and max values.
virtual void setGlobalScale(double scale)
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
Standard ECV plugin interface.
ecvMainAppInterface * m_app
Main application interface.
static bool isThickness(ccHObject *obj)
static const int EQUIVALENCE
static const int YOUNGER_THAN
static const int IMMEDIATELY_FOLLOWS
ccFitPlane * fitPlane(int surface_effect_tolerance=10, float min_planarity=0.75f)
size_t waypoint_count() const
static bool isTrace(ccHObject *object)
CCVector3f getPointNormal(int pointIdx)
int getSegmentCost(int p1, int p2)
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
const CCVector3 * getLSPlaneX()
Returns best interpolating plane (Least-square) 'X' base vector.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
void setCurrentOutScalarField(int index)
Sets the OUTPUT scalar field.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
void setCurrentScalarField(int index)
Sets both the INPUT & OUTPUT scalar field.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
ScalarType getPointScalarValue(unsigned pointIndex) const override
const CCVector3 * getPoint(unsigned index) const override
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
virtual void clear(bool releaseMemory=false)
Clears the cloud.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
A simple scalar field (to be associated to a point cloud)
virtual void computeMinAndMax()
Determines the min and max values.
void addElement(ScalarType value)
ScalarType & getValue(std::size_t index)
void setValue(std::size_t index, ScalarType value)
void toIdentity()
Sets matrix to identity.
Scalar ** m_values
The matrix rows.
void clear()
Sets all elements to 0.
SquareMatrixTpl transposed() const
Returns the transposed version of this matrix.
void setValue(unsigned row, unsigned column, Scalar value)
Sets a particular matrix value.
void toGlMatrix(float M16f[]) const
Converts a 3*3 or 4*4 matrix to an OpenGL-style float matrix (float[16])
Scalar getValue(unsigned row, unsigned column) const
Returns a particular matrix value.
virtual void updateUI()=0
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual QWidget * getActiveWindow()=0
virtual void updateOverlayDialogsPlacement()=0
Forces the update of all registered MDI 'overlay' dialogs.
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
virtual const ccHObject::Container & getSelectedEntities() const =0
Returns currently selected entities ("read only")
virtual void setSelectedInDB(ccHObject *obj, bool selected)=0
Selects or unselects an entity (in db tree)
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
virtual void removeFromDB(ccHObject *obj, bool autoDelete=true)=0
Removes an entity from main db tree.
virtual ccPickingHub * pickingHub()
virtual void registerOverlayDialog(ccOverlayDialog *dlg, Qt::Corner pos)=0
Registers a MDI area 'overlay' dialog.
virtual void unregisterOverlayDialog(ccOverlayDialog *dlg)=0
Unregisters a MDI area 'overlay' dialog.
Graphical progress indicator (thread-safe)
virtual void stop() override
Notifies the fact that the process has ended.
virtual void update(float percent) override
Notifies the algorithm progress.
virtual void start() override
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual bool isCancelRequested() override
Checks if the process should be canceled.
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
__host__ __device__ float dot(float2 a, float2 b)
__host__ __device__ float2 fabs(float2 v)
__host__ __device__ int2 abs(int2 v)
static double dist(double x1, double y1, double x2, double y2)
QTextStream & endl(QTextStream &stream)
float DegreesToRadians(int degrees)
Convert degrees to radians.
constexpr Rgb blue(0, 0, MAX)
OpenGL camera parameters.