ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
dxfProfilesExporter.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "dxfProfilesExporter.h"
9 
10 // CV_DB_LIB
11 #include <ecvPolyline.h>
12 
13 // ECV_plugins
14 #include <ecvMainAppInterface.h>
15 
16 // DXF lib
17 #ifdef CV_DXF_SUPPORT
18 #include <dl_dxf.h>
19 #endif
20 
22 #ifdef CV_DXF_SUPPORT
23  return true;
24 #else
25  return false;
26 #endif
27 }
28 
29 // constants
30 static const char PROFILE_LAYER[] = "theoretical_profile";
31 static const char LEGEND_LAYER[] = "legend";
32 static const char VERT_PROFILE_LAYER[] = "vertical_profile_%1";
33 static const char HORIZ_PROFILE_LAYER[] = "horizontal_profile_%1";
34 static const int s_lineWidth = 5;
35 
36 static const double c_textHeight_mm = 2.5;
37 static const double c_textMargin_mm = 2.0;
38 static const double c_pageHeight_mm = 297.0;
39 static const double c_pageWidth_mm = 210.0;
40 static const double c_pageMargin_mm = 10.0;
41 static const double c_profileMargin_mm = 50.0;
42 
43 struct VertStepData {
44  double height;
45  double radius_th;
46  double deviation;
47 };
48 
50  const QSharedPointer<DistanceMapGenerationTool::Map>& map,
52  QString filename,
53  unsigned angularStepCount,
54  double heightStep,
55  double heightShift,
56  const Parameters& params,
57  ecvMainAppInterface* app /*=0*/) {
58 #ifdef CV_DXF_SUPPORT
60  assert(2.0 * c_profileMargin_mm <
62 
63  if (!map || !profile || angularStepCount == 0 || heightStep <= 0) {
64  // invalid parameters
65  return false;
66  }
67 
68  // Theoretical profile bounding box
69  CCVector3 profileBBMin, profileBBMax;
70  profile->getAssociatedCloud()->getBoundingBox(profileBBMin, profileBBMax);
71  // Mix with the map's boundaries along 'Y'
72  double yMin = std::max(map->yMin,
73  static_cast<double>(profileBBMin.y) + heightShift);
74  double yMax = std::min(map->yMin + map->ySteps * map->yStep,
75  static_cast<double>(profileBBMax.y) + heightShift);
76  const double ySpan = yMax - yMin;
77  // For the 'X' dimension, it's easier to stick with the th. profile
78  const double xMin = profileBBMin.x;
79  // const double xMax = profileBBMax.x;
80  const double xSpan = profileBBMax.x - profileBBMin.x;
81 
82  if (xSpan == 0.0 && ySpan == 0.0) {
83  if (app)
84  app->dispToConsole(QString("Internal error: null profile?!"),
86  return false;
87  }
88 
89  DL_Dxf dxf;
90  DL_WriterA* dw = dxf.out(qPrintable(filename), DL_VERSION_R12);
91  if (!dw) {
92  if (app)
93  app->dispToConsole(QString("Failed to open '%1' file for writing!")
94  .arg(filename),
96  return false;
97  }
98 
99  // write header
100  dxf.writeHeader(*dw);
101 
102  // add dimensions
103  dw->dxfString(9, "$INSBASE");
104  dw->dxfReal(10, 0.0);
105  dw->dxfReal(20, 0.0);
106  dw->dxfReal(30, 0.0);
107  dw->dxfString(9, "$EXTMIN");
108  dw->dxfReal(10, 0.0);
109  dw->dxfReal(20, 0.0);
110  dw->dxfReal(30, 0.0);
111  dw->dxfString(9, "$EXTMAX");
112  dw->dxfReal(10, c_pageWidth_mm);
113  dw->dxfReal(20, c_pageHeight_mm);
114  dw->dxfReal(30, 0.0);
115  dw->dxfString(9, "$LIMMIN");
116  dw->dxfReal(10, 0.0);
117  dw->dxfReal(20, 0.0);
118  dw->dxfString(9, "$LIMMAX");
119  dw->dxfReal(10, c_pageWidth_mm);
120  dw->dxfReal(20, c_pageHeight_mm);
121 
122  // close header
123  dw->sectionEnd();
124 
125  // Opening the Tables Section
126  dw->sectionTables();
127  // Writing the Viewports
128  dxf.writeVPort(*dw);
129 
130  // Writing the Linetypes (all by default)
131  {
132  dw->tableLinetypes(3);
133  dxf.writeLinetype(*dw,
134  DL_LinetypeData("BYBLOCK", "BYBLOCK", 0, 0, 0.0));
135  dxf.writeLinetype(*dw,
136  DL_LinetypeData("BYLAYER", "BYLAYER", 0, 0, 0.0));
137  dxf.writeLinetype(
138  *dw, DL_LinetypeData("CONTINUOUS", "Continuous", 0, 0, 0.0));
139  dw->tableEnd();
140  }
141 
142  // Writing the Layers
143  dw->tableLayers(angularStepCount + 3);
144  QStringList profileNames;
145  {
146  // default layer
147  dxf.writeLayer(*dw, DL_LayerData("0", 0),
148  DL_Attributes(std::string(""), // leave empty
149  DL_Codes::black, // default color
150  100, // default width (in 1/100 mm)
151  "CONTINUOUS", // default line style
152  1.0 // linetypeScale
153  ));
154 
155  // theoretical profile layer
156  dxf.writeLayer(*dw, DL_LayerData(PROFILE_LAYER, 0),
157  DL_Attributes(std::string(""), DL_Codes::red,
158  s_lineWidth, "CONTINUOUS", 1.0));
159 
160  // legend layer
161  dxf.writeLayer(*dw, DL_LayerData(LEGEND_LAYER, 0),
162  DL_Attributes(std::string(""), DL_Codes::black,
163  s_lineWidth, "CONTINUOUS", 1.0));
164 
165  // vert. profile layers
166  for (unsigned i = 0; i < angularStepCount; ++i) {
167  // default layer name
168  QString layerName =
169  QString(VERT_PROFILE_LAYER).arg(i + 1, 3, 10, QChar('0'));
170  // but we use the profile title if we have one!
171  // DGM: nope, as it may be longer than 31 characters (R14 limit)
172  // if (params.profileTitles.size() >=
173  // static_cast<int>(angularStepCount))
174  //{
175  // layerName = params.profileTitles[i];
176  // layerName.replace(QChar(' '),QChar('_'));
177  // }
178 
179  profileNames << layerName;
180  dxf.writeLayer(
181  *dw,
182  DL_LayerData(qPrintable(layerName),
183  0), // DGM: warning, toStdString doesn't
184  // preserve "local" characters
185  DL_Attributes(std::string(""),
186  i == 0 ? DL_Codes::green
187  : -DL_Codes::green, // invisible if
188  // negative!
189  s_lineWidth, "CONTINUOUS", 1.0));
190  }
191  }
192  dw->tableEnd();
193 
194  // Writing Various Other Tables
195  // dxf.writeStyle(*dw); //DXFLIB V2.5
196  dw->tableStyle(1);
197  dxf.writeStyle(*dw, DL_StyleData("Standard", 0, 0.0, 0.75, 0.0, 0, 2.5,
198  "txt", "")); // DXFLIB V3.3
199  dw->tableEnd();
200 
201  dxf.writeView(*dw);
202  dxf.writeUcs(*dw);
203 
204  dw->tableAppid(1);
205  dw->tableAppidEntry(0x12);
206  dw->dxfString(2, "ACAD");
207  dw->dxfInt(70, 0);
208  dw->tableEnd();
209 
210  // Writing Dimension Styles
211  dxf.writeDimStyle(*dw,
212  /*arrowSize*/ 1,
213  /*extensionLineExtension*/ 1,
214  /*extensionLineOffset*/ 1,
215  /*dimensionGap*/ 1,
216  /*dimensionTextSize*/ 1);
217 
218  // Writing Block Records
219  dxf.writeBlockRecord(*dw);
220  // dxf.writeBlockRecord(*dw, "myblock1");
221  // dxf.writeBlockRecord(*dw, "myblock2");
222  dw->tableEnd();
223 
224  // Ending the Tables Section
225  dw->sectionEnd();
226 
227  // Writing the Blocks Section
228  {
229  dw->sectionBlocks();
230 
231  dxf.writeBlock(*dw, DL_BlockData("*Model_Space", 0, 0.0, 0.0, 0.0));
232  dxf.writeEndBlock(*dw, "*Model_Space");
233 
234  dxf.writeBlock(*dw, DL_BlockData("*Paper_Space", 0, 0.0, 0.0, 0.0));
235  dxf.writeEndBlock(*dw, "*Paper_Space");
236 
237  dxf.writeBlock(*dw, DL_BlockData("*Paper_Space0", 0, 0.0, 0.0, 0.0));
238  dxf.writeEndBlock(*dw, "*Paper_Space0");
239 
240  dw->sectionEnd();
241  }
242 
243  // Writing the Entities Section
244  {
245  dw->sectionEntities();
246 
247  // we make the profile fit in the middle of the page (21.0 x 29.7 cm)
248  double scale = 1.0;
249  if (xSpan == 0) {
250  assert(ySpan != 0);
251  scale = (c_pageHeight_mm - 2.0 * c_profileMargin_mm) / ySpan;
252  } else if (ySpan == 0) {
253  assert(xSpan != 0);
254  scale = (c_pageWidth_mm - 2.0 * c_profileMargin_mm) / xSpan;
255  } else {
256  scale = std::min(
257  (c_pageWidth_mm - 2.0 * c_profileMargin_mm) / xSpan,
258  (c_pageHeight_mm - 2.0 * c_profileMargin_mm) / ySpan);
259  }
260 
261  // min corner of profile area
262  const double x0 = (c_pageWidth_mm - xSpan * scale) / 2.0;
263  const double y0 = (c_pageHeight_mm - ySpan * scale) / 2.0;
264 
265  // write theoretical profile (polyline)
266  {
267  unsigned vertexCount = profile->size();
268  dxf.writePolyline(
269  *dw,
270  DL_PolylineData(static_cast<int>(vertexCount), 0, 0, 0),
271  DL_Attributes(PROFILE_LAYER, DL_Codes::bylayer, -1,
272  "BYLAYER", 1.0));
273 
274  for (unsigned i = 0; i < vertexCount; ++i) {
275  const CCVector3* P = profile->getPoint(i);
276  dxf.writeVertex(*dw, DL_VertexData(x0 + (P->x - xMin) * scale,
277  y0 + (P->y + heightShift -
278  yMin) * scale,
279  0.0));
280  }
281 
282  dxf.writePolylineEnd(*dw);
283  }
284 
285  // write legend
286  {
287  DL_Attributes DefaultLegendMaterial(LEGEND_LAYER, DL_Codes::bylayer,
288  -1, "BYLAYER", 1.0);
289 
290  // write page contour
291  {
292  dxf.writePolyline(*dw, DL_PolylineData(4, 0, 0, 1),
293  DefaultLegendMaterial);
294 
295  dxf.writeVertex(*dw, DL_VertexData(c_pageMargin_mm,
296  c_pageMargin_mm, 0.0));
297  dxf.writeVertex(
298  *dw,
299  DL_VertexData(c_pageMargin_mm,
301  dxf.writeVertex(
302  *dw,
303  DL_VertexData(c_pageWidth_mm - c_pageMargin_mm,
305  dxf.writeVertex(*dw,
306  DL_VertexData(c_pageWidth_mm - c_pageMargin_mm,
307  c_pageMargin_mm, 0.0));
308 
309  dxf.writePolylineEnd(*dw);
310  }
311 
312  double xLegend = c_pageMargin_mm + 2.0 * c_textHeight_mm;
313  double yLegend = c_pageMargin_mm + 2.0 * c_textHeight_mm;
314  const double legendWidth_mm = 20.0;
315 
316  // deviation magnification factor
317  QString magnifyStr = QString::number(params.devMagnifyCoef);
318  dxf.writeText(
319  *dw,
320  DL_TextData(xLegend, yLegend, 0.0, xLegend, yLegend, 0.0,
321  c_textHeight_mm, 1.0, 0, 0, 0,
322  qPrintable(QString("Deviation magnification "
323  "factor: ") +
324  magnifyStr),
325  "STANDARD",
326  0.0), // DGM: warning, toStdString doesn't
327  // preserve "local" characters
328  DefaultLegendMaterial);
329 
330  // next line
331  yLegend += c_textHeight_mm * 2.0;
332 
333  // units
334  dxf.writeText(*dw,
335  DL_TextData(xLegend, yLegend, 0.0, xLegend, yLegend,
336  0.0, c_textHeight_mm, 1.0, 0, 0, 0,
337  qPrintable(QString("Deviation units: ") +
338  params.scaledDevUnits),
339  "STANDARD", 0.0),
340  DefaultLegendMaterial);
341 
342  // next line
343  yLegend += c_textHeight_mm * 2.0;
344 
345  // true profile line (red)
346  dxf.writeLine(*dw,
347  DL_LineData(xLegend, yLegend, 0,
348  xLegend + legendWidth_mm, yLegend, 0.0),
349  DL_Attributes(LEGEND_LAYER, DL_Codes::green, -1,
350  "BYLAYER", 1.0));
351 
352  dxf.writeText(
353  *dw,
354  DL_TextData(xLegend + legendWidth_mm + c_textMargin_mm,
355  yLegend, 0.0,
356  xLegend + legendWidth_mm + c_textMargin_mm,
357  yLegend, 0.0, c_textHeight_mm, 1.0, 0, 0, 0,
358  qPrintable(params.legendRealProfileTitle),
359  "STANDARD",
360  0.0), // DGM: warning, toStdString doesn't
361  // preserve "local" characters
362  DefaultLegendMaterial);
363 
364  // next line
365  yLegend += c_textHeight_mm * 2.0;
366 
367  // theoretical profile line (red)
368  dxf.writeLine(*dw,
369  DL_LineData(xLegend, yLegend, 0,
370  xLegend + legendWidth_mm, yLegend, 0.0),
371  DL_Attributes(LEGEND_LAYER, DL_Codes::red, -1,
372  "BYLAYER", 1.0));
373 
374  dxf.writeText(
375  *dw,
376  DL_TextData(xLegend + legendWidth_mm + c_textMargin_mm,
377  yLegend, 0.0,
378  xLegend + legendWidth_mm + c_textMargin_mm,
379  yLegend, 0.0, c_textHeight_mm, 1.0, 0, 0, 0,
380  qPrintable(params.legendTheoProfileTitle),
381  "STANDARD",
382  0.0), // DGM: warning, toStdString doesn't
383  // preserve "local" characters
384  DefaultLegendMaterial);
385  }
386 
387  // write vertical profiles
388  for (unsigned angleStep = 0; angleStep < angularStepCount;
389  ++angleStep) {
390  std::vector<VertStepData> polySteps;
391  try {
392  polySteps.reserve(map->ySteps);
393  } catch (const std::bad_alloc&) {
394  // not enough memory
395  dw->dxfEOF();
396  dw->close();
397  delete dw;
398  return false;
399  }
400 
401  unsigned iMap = static_cast<unsigned>(
402  static_cast<double>(angleStep * map->xSteps) /
403  static_cast<double>(angularStepCount));
404  for (unsigned jMap = 0; jMap < map->ySteps; ++jMap) {
406  map->at(iMap + jMap * map->xSteps);
407 
408  VertStepData step;
409  step.height =
410  map->yMin + static_cast<double>(jMap) * map->yStep;
411 
412  if (step.height >= yMin && step.height <= yMax) {
413  // find corresponding radius
414  bool found = false;
415  for (unsigned i = 1; i < profile->size(); ++i) {
416  const CCVector3* A = profile->getPoint(i - 1);
417  const CCVector3* B = profile->getPoint(i);
418 
419  double alpha = static_cast<double>(
420  (step.height - A->y - heightShift) /
421  (B->y - A->y));
422  if (alpha >= 0.0 && alpha <= 1.0) {
423  // we deduce the right radius by linear
424  // interpolation
425  step.radius_th = A->x + alpha * (B->x - A->x);
426  found = true;
427  break;
428  }
429  }
430 
431  if (found) {
432  step.deviation = cell.count ? cell.value : 0.0;
433  polySteps.push_back(step);
434  }
435  }
436  }
437 
438  const DL_Attributes DefaultMaterial(
439  qPrintable(profileNames[angleStep]), DL_Codes::bylayer, -1,
440  "BYLAYER", 1.0); // DGM: warning, toStdString doesn't
441  // preserve "local" characters
442  const DL_Attributes GrayMaterial(
443  qPrintable(profileNames[angleStep]), DL_Codes::l_gray, -1,
444  "", 1.0);
445 
446  // write layer title
447  if (static_cast<int>(angleStep) < params.profileTitles.size()) {
448  const QString& title = params.profileTitles[angleStep];
449 
450  CCVector3d Ptop(c_pageWidth_mm / 2.0,
451  y0 + ySpan * scale + c_profileMargin_mm / 2.0,
452  0.0);
453 
454  dxf.writeText(
455  *dw,
456  DL_TextData(Ptop.x, Ptop.y, Ptop.z, Ptop.x, Ptop.y,
457  Ptop.z, c_textHeight_mm, 1.0, 0, 1, 0,
458  qPrintable(title), "STANDARD",
459  0.0), // DGM: warning, toStdString doesn't
460  // preserve "local" characters
461  GrayMaterial);
462  }
463 
464  // write corresponding polyline
465  {
466  dxf.writePolyline(
467  *dw,
468  DL_PolylineData(static_cast<int>(polySteps.size()), 0,
469  0, 0),
470  DefaultMaterial);
471 
472  for (size_t i = 0; i < polySteps.size(); ++i) {
473  const VertStepData& step = polySteps[i];
474  dxf.writeVertex(
475  *dw,
476  DL_VertexData(x0 + (step.radius_th +
477  step.deviation *
478  params.devMagnifyCoef -
479  xMin) * scale,
480  y0 + (step.height - yMin) * scale,
481  0.0));
482  }
483 
484  dxf.writePolylineEnd(*dw);
485  }
486 
487  // write corresponding 'deviation bars' and labels
488  CCVector3d pageShift(x0 - xMin * scale, y0 - yMin * scale, 0.0);
489  {
490  size_t lastStep = 0;
491  for (size_t i = 0; i < polySteps.size(); ++i) {
492  const VertStepData& step = polySteps[i];
493  bool displayIt = (i == 0 || i + 1 == polySteps.size());
494  if (!displayIt) {
495  double dh = polySteps[i].height -
496  polySteps[lastStep].height;
497  double next_dh = polySteps[i + 1].height -
498  polySteps[lastStep].height;
499  if (dh >= heightStep ||
500  (next_dh > heightStep &&
501  fabs(dh - heightStep) <
502  fabs(next_dh - heightStep))) {
503  displayIt = true;
504  }
505  }
506 
507  if (displayIt) {
508  CCVector3d Pheight(step.radius_th, step.height, 0.0);
509  CCVector3d Pdev(
510  step.radius_th +
511  step.deviation * params.devMagnifyCoef,
512  step.height, 0.0);
513 
514  // page scaling
515  Pheight = pageShift + Pheight * scale;
516  Pdev = pageShift + Pdev * scale;
517 
518  // deviation bar
519  dxf.writeLine(
520  *dw,
521  DL_LineData(Pheight.x, Pheight.y, Pheight.z,
522  Pdev.x, Pdev.y, Pdev.z),
523  GrayMaterial);
524 
525  // labels
526 
527  // Horizontal justification: 0 = Left , 1 = Center, 2 =
528  // Right
529  int hJustification = 0;
530  // Vertical justification: 0 = Baseline, 1 = Bottom, 2 =
531  // Middle, 3 = Top
532  int vJustification = 2;
533 
534  if (step.deviation < 0.0) {
535  // deviation label on the left
536  Pdev.x -= 2.0 * c_textMargin_mm;
537  // opposite for the height label
538  Pheight.x += c_textMargin_mm;
539  hJustification = 2; // Right
540  } else {
541  // deviation label on the right
542  Pdev.x += 2.0 * c_textMargin_mm;
543  // opposite for the height label
544  Pheight.x -= c_textMargin_mm;
545  hJustification = 0; // Left
546  }
547 
548  QString devText =
549  QString::number(polySteps[i].deviation *
550  params.devLabelMultCoef,
551  'f', params.precision);
552  dxf.writeText(
553  *dw,
554  DL_TextData(Pdev.x, Pdev.y, Pdev.z, Pdev.x,
555  Pdev.y, Pdev.z, c_textHeight_mm,
556  1.0, 0, hJustification,
557  vJustification, qPrintable(devText),
558  "STANDARD",
559  0.0), // DGM: warning, toStdString
560  // doesn't preserve "local"
561  // characters
562  DefaultMaterial);
563 
564  QString heightText = QString::number(
565  polySteps[i].height, 'f', params.precision);
566  dxf.writeText(
567  *dw,
568  DL_TextData(Pheight.x, Pheight.y, Pheight.z,
569  Pheight.x, Pheight.y, Pheight.z,
570  c_textHeight_mm, 1.0, 0,
571  2 - hJustification, vJustification,
572  qPrintable(heightText), "STANDARD",
573  0.0),
574  GrayMaterial);
575 
576  lastStep = i;
577  }
578  }
579  }
580  }
581 
582  dw->sectionEnd();
583  }
584 
585  // Writing the Objects Section
586  dxf.writeObjects(*dw);
587  dxf.writeObjectsEnd(*dw);
588 
589  // Ending and Closing the File
590  dw->dxfEOF();
591  dw->close();
592  delete dw;
593  dw = 0;
594 
595  return true;
596 
597 #else
598  return false;
599 #endif
600 }
601 
603  double angle_rad;
604  double deviation;
605 };
606 
608  const QSharedPointer<DistanceMapGenerationTool::Map>& map,
610  QString filename,
611  unsigned heightStepCount,
612  double heightShift,
613  double angularStep_rad,
614  double radToUnitConvFactor,
615  QString angleUnit,
616  const Parameters& params,
617  ecvMainAppInterface* app /*= 0*/) {
618 #ifdef CV_DXF_SUPPORT
620  assert(2.0 * c_profileMargin_mm <
622 
623  if (!map || !profile || heightStepCount == 0 || angularStep_rad <= 0) {
624  // invalid parameters
625  return false;
626  }
627 
628  // Theoretical profile bounding box
629  CCVector3 profileBBMin, profileBBMax;
630  profile->getAssociatedCloud()->getBoundingBox(profileBBMin, profileBBMax);
631  // Mix with the map's boundaries along 'Y'
632  double yMin = std::max(
633  map->yMin + 0.5 * map->xStep, // central height of first row
634  static_cast<double>(profileBBMin.y) + heightShift);
635  double yMax = std::min(
636  map->yMin + (static_cast<double>(map->ySteps) - 0.5) *
637  map->yStep, // central height of last row
638  static_cast<double>(profileBBMax.y) + heightShift);
639  const double ySpan = yMax - yMin;
640 
641  // For the 'X' dimension, it's easier to stick with the th. profile
642  // const double xMin = profileBBMin.x;
643  const double xMax = profileBBMax.x;
644  // shortcut for clarity
645  const double& maxRadius = xMax;
646 
647  if (ySpan == 0.0) {
648  if (app)
649  app->dispToConsole(QString("Internal error: null profile?!"),
651  return false;
652  }
653 
654  DL_Dxf dxf;
655  DL_WriterA* dw = dxf.out(qPrintable(filename), DL_VERSION_R12);
656  if (!dw) {
657  if (app)
658  app->dispToConsole(QString("Failed to open '%1' file for writing!")
659  .arg(filename),
661  return false;
662  }
663 
664  // write header
665  dxf.writeHeader(*dw);
666 
667  // add dimensions
668  dw->dxfString(9, "$INSBASE");
669  dw->dxfReal(10, 0.0);
670  dw->dxfReal(20, 0.0);
671  dw->dxfReal(30, 0.0);
672  dw->dxfString(9, "$EXTMIN");
673  dw->dxfReal(10, 0.0);
674  dw->dxfReal(20, 0.0);
675  dw->dxfReal(30, 0.0);
676  dw->dxfString(9, "$EXTMAX");
677  dw->dxfReal(10, c_pageWidth_mm);
678  dw->dxfReal(20, c_pageHeight_mm);
679  dw->dxfReal(30, 0.0);
680  dw->dxfString(9, "$LIMMIN");
681  dw->dxfReal(10, 0.0);
682  dw->dxfReal(20, 0.0);
683  dw->dxfString(9, "$LIMMAX");
684  dw->dxfReal(10, c_pageWidth_mm);
685  dw->dxfReal(20, c_pageHeight_mm);
686 
687  // close header
688  dw->sectionEnd();
689 
690  // Opening the Tables Section
691  dw->sectionTables();
692  // Writing the Viewports
693  dxf.writeVPort(*dw);
694 
695  // Writing the Linetypes (all by default)
696  {
697  dw->tableLinetypes(3);
698  dxf.writeLinetype(*dw,
699  DL_LinetypeData("BYBLOCK", "BYBLOCK", 0, 0, 0.0));
700  dxf.writeLinetype(*dw,
701  DL_LinetypeData("BYLAYER", "BYLAYER", 0, 0, 0.0));
702  dxf.writeLinetype(
703  *dw, DL_LinetypeData("CONTINUOUS", "Continuous", 0, 0, 0.0));
704  dw->tableEnd();
705  }
706 
707  // Writing the Layers
708  dw->tableLayers(heightStepCount + 2);
709  QStringList profileNames;
710  {
711  // default layer
712  dxf.writeLayer(*dw, DL_LayerData("0", 0),
713  DL_Attributes(std::string(""), // leave empty
714  DL_Codes::black, // default color
715  100, // default width (in 1/100 mm)
716  "CONTINUOUS", // default line style
717  1.0 // linetypeScale
718  ));
719 
720  // legend layer
721  dxf.writeLayer(*dw, DL_LayerData(LEGEND_LAYER, 0),
722  DL_Attributes(std::string(""), DL_Codes::black,
723  s_lineWidth, "CONTINUOUS", 1.0));
724 
725  // horiz. profile layers
726  for (unsigned i = 0; i < heightStepCount; ++i) {
727  // default profile name
728  QString layerName =
729  QString(HORIZ_PROFILE_LAYER).arg(i + 1, 3, 10, QChar('0'));
730  // but we use the profile title if we have one!
731  // DGM: nope, as it may be longer than 31 characters (R14 limit)
732  // if (params.profileTitles.size() == 1)
733  //{
734  // //profile height
735  // double height = yMin + static_cast<double>(i) /
736  // static_cast<double>(heightStepCount-1) * ySpan; layerName =
737  // QString(params.profileTitles[0]).arg(height,0,'f',params.precision);
738  // layerName.replace(QChar(' '),QChar('_'));
739  // }
740 
741  profileNames << layerName;
742  dxf.writeLayer(
743  *dw,
744  DL_LayerData(qPrintable(layerName),
745  0), // DGM: warning, toStdString doesn't
746  // preserve "local" characters
747  DL_Attributes(std::string(""),
748  i == 0 ? DL_Codes::green
749  : -DL_Codes::green, // invisible if
750  // negative!
751  s_lineWidth, "CONTINUOUS", 1.0));
752  }
753  }
754  dw->tableEnd();
755 
756  // Writing Various Other Tables
757  // dxf.writeStyle(*dw); //DXFLIB V2.5
758  dw->tableStyle(1);
759  dxf.writeStyle(*dw, DL_StyleData("Standard", 0, 0.0, 0.75, 0.0, 0, 2.5,
760  "txt", "")); // DXFLIB V3.3
761  dw->tableEnd();
762 
763  dxf.writeView(*dw);
764  dxf.writeUcs(*dw);
765 
766  dw->tableAppid(1);
767  dw->tableAppidEntry(0x12);
768  dw->dxfString(2, "ACAD");
769  dw->dxfInt(70, 0);
770  dw->tableEnd();
771 
772  // Writing Dimension Styles
773  dxf.writeDimStyle(*dw,
774  /*arrowSize*/ 1,
775  /*extensionLineExtension*/ 1,
776  /*extensionLineOffset*/ 1,
777  /*dimensionGap*/ 1,
778  /*dimensionTextSize*/ 1);
779 
780  // Writing Block Records
781  dxf.writeBlockRecord(*dw);
782  dw->tableEnd();
783 
784  // Ending the Tables Section
785  dw->sectionEnd();
786 
787  // Writing the Blocks Section
788  {
789  dw->sectionBlocks();
790 
791  dxf.writeBlock(*dw, DL_BlockData("*Model_Space", 0, 0.0, 0.0, 0.0));
792  dxf.writeEndBlock(*dw, "*Model_Space");
793 
794  dxf.writeBlock(*dw, DL_BlockData("*Paper_Space", 0, 0.0, 0.0, 0.0));
795  dxf.writeEndBlock(*dw, "*Paper_Space");
796 
797  dxf.writeBlock(*dw, DL_BlockData("*Paper_Space0", 0, 0.0, 0.0, 0.0));
798  dxf.writeEndBlock(*dw, "*Paper_Space0");
799 
800  dw->sectionEnd();
801  }
802 
803  // Writing the Entities Section
804  {
805  dw->sectionEntities();
806 
807  // we make the profile fit in the middle of the page (21.0 x 29.7 cm)
808  double scale = std::min(
809  (c_pageWidth_mm - 2.0 * c_profileMargin_mm) / (2.0 * maxRadius),
811  (2.0 * maxRadius));
812 
813  // min corner of profile area
814  // const double x0 = (c_pageWidth_mm - 2.0 * maxRadius*scale) / 2.0;
815  const double y0 = (c_pageHeight_mm - 2.0 * maxRadius * scale) / 2.0;
816  // center of profile area
817  const double xc = c_pageWidth_mm / 2.0;
818  const double yc = c_pageHeight_mm / 2.0;
819 
820  // write legend
821  {
822  DL_Attributes DefaultLegendMaterial(LEGEND_LAYER, DL_Codes::bylayer,
823  -1, "BYLAYER", 1.0);
824 
825  // write page contour
826  {
827  dxf.writePolyline(*dw, DL_PolylineData(4, 0, 0, 1),
828  DefaultLegendMaterial);
829 
830  dxf.writeVertex(*dw, DL_VertexData(c_pageMargin_mm,
831  c_pageMargin_mm, 0.0));
832  dxf.writeVertex(
833  *dw,
834  DL_VertexData(c_pageMargin_mm,
836  dxf.writeVertex(
837  *dw,
838  DL_VertexData(c_pageWidth_mm - c_pageMargin_mm,
840  dxf.writeVertex(*dw,
841  DL_VertexData(c_pageWidth_mm - c_pageMargin_mm,
842  c_pageMargin_mm, 0.0));
843 
844  dxf.writePolylineEnd(*dw);
845  }
846 
847  double xLegend = c_pageMargin_mm + 2.0 * c_textHeight_mm;
848  double yLegend = c_pageMargin_mm + 2.0 * c_textHeight_mm;
849  const double legendWidth_mm = 20.0;
850 
851  // Y axis
852  double axisTip = maxRadius * scale + 5.0;
853  dxf.writeLine(*dw, DL_LineData(xc, yc, 0.0, xc, yc - axisTip, 0.0),
854  DefaultLegendMaterial);
855 
856  // Y axis tip as triangle
857  {
858  double axisTipSize = 3.0;
859 
860  dxf.writePolyline(
861  *dw, DL_PolylineData(3, 0, 0, 1), // closed polyline!
862  DefaultLegendMaterial);
863  dxf.writeVertex(
864  *dw,
865  DL_VertexData(xc, yc - (axisTip + axisTipSize), 0.0));
866  dxf.writeVertex(*dw, DL_VertexData(xc - axisTipSize / 2.0,
867  yc - axisTip, 0.0));
868  dxf.writeVertex(*dw, DL_VertexData(xc + axisTipSize / 2.0,
869  yc - axisTip, 0.0));
870  dxf.writePolylineEnd(*dw);
871 
872  dxf.writeText(
873  *dw,
874  DL_TextData(xc, yc - (axisTip + 2.0 * axisTipSize), 0.0,
875  xc, yc - (axisTip + 2.0 * axisTipSize), 0.0,
876  c_textHeight_mm, 1.0, 0, 1, 3, "Y",
877  "STANDARD", 0.0),
878  DefaultLegendMaterial);
879  }
880 
881  // deviation magnification factor
882  QString magnifyStr = QString::number(params.devMagnifyCoef);
883  dxf.writeText(
884  *dw,
885  DL_TextData(xLegend, yLegend, 0.0, xLegend, yLegend, 0.0,
886  c_textHeight_mm, 1.0, 0, 0, 0,
887  qPrintable(QString("Deviation magnification "
888  "factor: ") +
889  magnifyStr),
890  "STANDARD",
891  0.0), // DGM: warning, toStdString doesn't
892  // preserve "local" characters
893  DefaultLegendMaterial);
894 
895  // next line
896  yLegend += c_textHeight_mm * 2.0;
897 
898  // units
899  dxf.writeText(
900  *dw,
901  DL_TextData(xLegend, yLegend, 0.0, xLegend, yLegend, 0.0,
902  c_textHeight_mm, 1.0, 0, 0, 0,
903  qPrintable(QString("Deviation units: ") +
904  params.scaledDevUnits),
905  "STANDARD",
906  0.0), // DGM: warning, toStdString doesn't
907  // preserve "local" characters
908  DefaultLegendMaterial);
909 
910  // next line
911  yLegend += c_textHeight_mm * 2.0;
912 
913  // true profile line (red)
914  dxf.writeLine(*dw,
915  DL_LineData(xLegend, yLegend, 0,
916  xLegend + legendWidth_mm, yLegend, 0.0),
917  DL_Attributes(LEGEND_LAYER, DL_Codes::green, -1,
918  "BYLAYER", 1.0));
919 
920  dxf.writeText(
921  *dw,
922  DL_TextData(xLegend + legendWidth_mm + c_textMargin_mm,
923  yLegend, 0.0,
924  xLegend + legendWidth_mm + c_textMargin_mm,
925  yLegend, 0.0, c_textHeight_mm, 1.0, 0, 0, 0,
926  qPrintable(params.legendRealProfileTitle),
927  "STANDARD", 0.0),
928  DefaultLegendMaterial);
929 
930  // next line
931  yLegend += c_textHeight_mm * 2.0;
932 
933  // theoretical profile line (red)
934  dxf.writeLine(*dw,
935  DL_LineData(xLegend, yLegend, 0,
936  xLegend + legendWidth_mm, yLegend, 0.0),
937  DL_Attributes(LEGEND_LAYER, DL_Codes::red, -1,
938  "BYLAYER", 1.0));
939 
940  dxf.writeText(
941  *dw,
942  DL_TextData(xLegend + legendWidth_mm + c_textMargin_mm,
943  yLegend, 0.0,
944  xLegend + legendWidth_mm + c_textMargin_mm,
945  yLegend, 0.0, c_textHeight_mm, 1.0, 0, 0, 0,
946  qPrintable(params.legendTheoProfileTitle),
947  "STANDARD", 0.0),
948  DefaultLegendMaterial);
949  }
950 
951  // profile values (fixed size: one per angular step of the input grid)
952  std::vector<HorizStepData> polySteps;
953  try {
954  polySteps.resize(map->xSteps);
955  } catch (const std::bad_alloc&) {
956  // not engouh memory
957  dw->dxfEOF();
958  dw->close();
959  delete dw;
960  return false;
961  }
962 
963  // write horizontal profiles
964  for (unsigned heightStep = 0; heightStep < heightStepCount;
965  ++heightStep) {
966  // profile height
967  double height =
968  yMin + static_cast<double>(heightStep) /
969  static_cast<double>(heightStepCount - 1) *
970  ySpan;
971 
972  // corresponding index in map
973  if (height < map->yMin ||
974  height >= map->yMin + static_cast<double>(map->ySteps) *
975  map->yStep) {
976  assert(false); // we have computed yMin and yMax so that those
977  // values are totally included inside the map's
978  // boundaries...
979  continue;
980  }
981  unsigned jMap =
982  static_cast<unsigned>((height - map->yMin) / map->yStep);
983  assert(jMap < map->ySteps);
984 
985  // find corresponding radius
986  double currentRadius = 0.0;
987  {
988  bool found = false;
989  for (unsigned i = 1; i < profile->size(); ++i) {
990  const CCVector3* A = profile->getPoint(i - 1);
991  const CCVector3* B = profile->getPoint(i);
992 
993  double alpha = static_cast<double>(
994  (height - A->y - heightShift) / (B->y - A->y));
995  if (alpha >= 0.0 && alpha <= 1.0) {
996  // we deduce the right radius by linear interpolation
997  currentRadius = A->x + alpha * (B->x - A->x);
998  found = true;
999  break;
1000  }
1001  }
1002 
1003  if (!found) {
1004  assert(false); // we have computed yMin and yMax so that
1005  // 'height' is totally included inside the
1006  // profile's boundaries...
1007  continue;
1008  }
1009  }
1010 
1011  const QString& currentLayer = profileNames[heightStep];
1012  const DL_Attributes DefaultMaterial(
1013  qPrintable(currentLayer), DL_Codes::bylayer, -1, "BYLAYER",
1014  1.0); // DGM: warning, toStdString doesn't preserve "local"
1015  // characters
1016  const DL_Attributes GrayMaterial(qPrintable(currentLayer),
1017  DL_Codes::l_gray, -1, "", 1.0);
1018 
1019  // write layer title
1020  if (params.profileTitles.size() == 1) {
1021  QString title = QString(params.profileTitles[0])
1022  .arg(height, 0, 'f', params.precision);
1023 
1024  CCVector3d Ptop(
1025  xc,
1026  y0 + 2.0 * maxRadius * scale + c_profileMargin_mm / 2.0,
1027  0.0);
1028 
1029  dxf.writeText(
1030  *dw,
1031  DL_TextData(Ptop.x, Ptop.y, Ptop.z, Ptop.x, Ptop.y,
1032  Ptop.z, c_textHeight_mm, 1.0, 0, 1, 0,
1033  qPrintable(title), "STANDARD",
1034  0.0), // DGM: warning, toStdString doesn't
1035  // preserve "local" characters
1036  GrayMaterial);
1037  }
1038 
1039  // write theoretical profile (polyline = circle)
1040  {
1041  dxf.writeCircle(
1042  *dw, DL_CircleData(xc, yc, 0.0, currentRadius * scale),
1043  DL_Attributes(qPrintable(currentLayer), DL_Codes::red,
1044  -1, "BYLAYER", 1.0));
1045  }
1046 
1047  assert(polySteps.size() == map->xSteps);
1048  {
1050  &map->at(jMap * map->xSteps);
1051  for (unsigned iMap = 0; iMap < map->xSteps; ++iMap, ++cell) {
1052  HorizStepData step;
1053  step.angle_rad = 2.0 * M_PI * static_cast<double>(iMap) /
1054  static_cast<double>(map->xSteps);
1055  step.deviation = cell->count ? cell->value : 0.0;
1056  polySteps[iMap] = step;
1057  }
1058  }
1059 
1060  // profile "direction"
1061  double cwSign = map->counterclockwise ? -1.0 : 1.0;
1062 
1063  CCVector3d pageShift(xc, yc, 0.0);
1064 
1065  // write profile polyline
1066  {
1067  dxf.writePolyline(
1068  *dw,
1069  DL_PolylineData(static_cast<int>(polySteps.size()), 0,
1070  0, 1), // closed shape!
1071  DefaultMaterial);
1072 
1073  for (size_t i = 0; i < polySteps.size(); ++i) {
1074  const HorizStepData& step = polySteps[i];
1075  double radius = currentRadius +
1076  step.deviation * params.devMagnifyCoef;
1077  dxf.writeVertex(
1078  *dw,
1079  DL_VertexData(pageShift.x - (cwSign * radius *
1080  sin(step.angle_rad)) *
1081  scale,
1082  pageShift.y - (radius *
1083  cos(step.angle_rad)) *
1084  scale,
1085  0.0));
1086  }
1087 
1088  dxf.writePolylineEnd(*dw);
1089  }
1090 
1091  // write corresponding 'deviation bars' and labels
1092  {
1093  size_t lastStep = 0;
1094  for (size_t i = 0; i < polySteps.size(); ++i) {
1095  const HorizStepData& step = polySteps[i];
1096  bool displayIt =
1097  (i ==
1098  0 /*|| i+1 == polySteps.size()*/); // warning:
1099  // cycle
1100  if (i != 0) {
1101  double dAngle = polySteps[i].angle_rad -
1102  polySteps[lastStep].angle_rad;
1103  double next_dAngle =
1104  (i + 1 == polySteps.size()
1105  ? polySteps[0].angle_rad + 2.0 * M_PI
1106  : polySteps[i + 1].angle_rad) -
1107  polySteps[lastStep].angle_rad;
1108  if (dAngle >= angularStep_rad ||
1109  (next_dAngle > angularStep_rad &&
1110  fabs(dAngle - angularStep_rad) <
1111  fabs(next_dAngle - angularStep_rad))) {
1112  displayIt = true;
1113  }
1114  }
1115 
1116  if (displayIt && i != 0) // we skip 0 as we always display
1117  // a vertical axis!
1118  {
1119  CCVector3d relativePos(-cwSign * sin(step.angle_rad),
1120  -cos(step.angle_rad), 0.0);
1121  CCVector3d Pangle = relativePos * currentRadius;
1122  CCVector3d Pdev =
1123  relativePos *
1124  (currentRadius +
1125  step.deviation * params.devMagnifyCoef);
1126 
1127  // page scaling
1128  Pangle = pageShift + Pangle * scale;
1129  Pdev = pageShift + Pdev * scale;
1130 
1131  // deviation bar
1132  dxf.writeLine(*dw,
1133  DL_LineData(Pangle.x, Pangle.y, Pangle.z,
1134  Pdev.x, Pdev.y, Pdev.z),
1135  GrayMaterial);
1136 
1137  // labels
1138 
1139  // justification depends on the current angle
1140  const double c_angleMargin_rad =
1141  5.0 / 180.0 * M_PI; // margin: 5 degrees
1142  const double c_margin = sin(c_angleMargin_rad);
1143 
1144  // Horizontal justification: 0 = Left , 1 = Center, 2 =
1145  // Right
1146  int hJustification = 1;
1147  if (relativePos.x < -c_margin) // point on the left
1148  hJustification = 2; // Right
1149  else if (relativePos.x > c_margin) // point on the
1150  // right
1151  hJustification = 0; // Left
1152 
1153  // Vertical justification: 0 = Baseline, 1 = Bottom, 2 =
1154  // Middle, 3 = Top
1155  int vJustification = 2;
1156  if (relativePos.y <
1157  -c_margin) // point in the lower part
1158  vJustification = 3; // Top
1159  else if (relativePos.y >
1160  c_margin) // point in the upper part
1161  vJustification = 1; // Bottom
1162 
1163  int hJustificationDev = hJustification;
1164  int hJustificationAng = hJustification;
1165  int vJustificationDev = vJustification;
1166  int vJustificationAng = vJustification;
1167 
1168  // negative deviation: profile is inside the circle
1169  // - deviation is displayed insde
1170  // - angle is displayed outside
1171  if (step.deviation < 0.0) {
1172  Pdev -= relativePos * 2.0 * c_textMargin_mm;
1173  Pangle += relativePos * c_textMargin_mm;
1174  // invert dev. text justification
1175  if (hJustificationDev != 1)
1176  hJustificationDev =
1177  2 - hJustificationDev; // 0 <-> 2
1178  if (vJustificationDev != 2)
1179  vJustificationDev =
1180  4 - vJustificationDev; // 1 <-> 3
1181  } else {
1182  Pdev += relativePos * 2.0 * c_textMargin_mm;
1183  Pangle -= relativePos * c_textMargin_mm;
1184  // invert ang. text justification
1185  if (hJustificationAng != 1)
1186  hJustificationAng =
1187  2 - hJustificationAng; // 0 <-> 2
1188  if (vJustificationAng != 2)
1189  vJustificationAng =
1190  4 - vJustificationAng; // 1 <-> 3
1191  }
1192 
1193  QString devText =
1194  QString::number(polySteps[i].deviation *
1195  params.devLabelMultCoef,
1196  'f', params.precision);
1197  dxf.writeText(
1198  *dw,
1199  DL_TextData(
1200  Pdev.x, Pdev.y, Pdev.z, Pdev.x, Pdev.y,
1201  Pdev.z, c_textHeight_mm, 1.0, 0,
1202  hJustificationDev, vJustificationDev,
1203  qPrintable(devText), "STANDARD", 0.0),
1204  DefaultMaterial);
1205 
1206  QString angleText =
1207  QString::number(polySteps[i].angle_rad *
1208  radToUnitConvFactor,
1209  'f', params.precision) +
1210  angleUnit;
1211  dxf.writeText(*dw,
1212  DL_TextData(Pangle.x, Pangle.y, Pangle.z,
1213  Pangle.x, Pangle.y, Pangle.z,
1214  c_textHeight_mm, 1.0, 0,
1215  hJustificationAng,
1216  vJustificationAng,
1217  qPrintable(angleText),
1218  "STANDARD", 0.0),
1219  GrayMaterial);
1220 
1221  lastStep = i;
1222  }
1223  }
1224  }
1225  }
1226 
1227  dw->sectionEnd();
1228  }
1229 
1230  // Writing the Objects Section
1231  dxf.writeObjects(*dw);
1232  dxf.writeObjectsEnd(*dw);
1233 
1234  // Ending and Closing the File
1235  dw->dxfEOF();
1236  dw->close();
1237  delete dw;
1238  dw = 0;
1239 
1240  return true;
1241 
1242 #else
1243  return false;
1244 #endif
1245 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
std::string filename
int height
CloudViewerScene::LightingProfile profile
cmdLineReadable * params[]
static bool SaveHorizontalProfiles(const QSharedPointer< DistanceMapGenerationTool::Map > &map, ccPolyline *profile, QString filename, unsigned heightStepCount, double heightShift, double angularStep_rad, double radToUnitConvFactor, QString angleUnit, const Parameters &params, ecvMainAppInterface *app=0)
static bool SaveVerticalProfiles(const QSharedPointer< DistanceMapGenerationTool::Map > &map, ccPolyline *profile, QString filename, unsigned angularStepCount, double heightStep, double heightShift, const Parameters &params, ecvMainAppInterface *app=0)
static bool IsEnabled()
Returns whether DXF support is enabled or not.
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Colored polyline.
Definition: ecvPolyline.h:24
Main application interface (for plugins)
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
__host__ __device__ float2 fabs(float2 v)
Definition: cutil_math.h:1254
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
static const double c_textHeight_mm
static const double c_textMargin_mm
static const double c_profileMargin_mm
static const char HORIZ_PROFILE_LAYER[]
static const char LEGEND_LAYER[]
static const double c_pageHeight_mm
static const double c_pageWidth_mm
static const double c_pageMargin_mm
static const char PROFILE_LAYER[]
static const char VERT_PROFILE_LAYER[]
static const int s_lineWidth
constexpr Rgb black(0, 0, 0)
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb green(0, MAX, 0)
unsigned count
Number of values projected in this cell (for average computation)