ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvGlobalShiftManager.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 
9 
11 
12 // CV_DB_LIB
13 #include <ecvHObject.h>
14 
15 // System
16 #include <assert.h>
17 #include <string.h>
18 
21 
22 // semi-persistent settings
23 static std::vector<ecvGlobalShiftManager::ShiftInfo> s_lastInfoBuffer;
24 
26  double scale,
27  bool preserve /*=true*/) {
28  if (scale == 1.0 && shift.norm2d() == 0) {
29  // default shift and scale are ignored
30  return;
31  }
32 
33  for (const ecvGlobalShiftManager::ShiftInfo& shiftInfo : s_lastInfoBuffer) {
34  if (shiftInfo.scale == scale &&
35  (shiftInfo.shift - shift).norm2d() == 0) {
36  // we already know this one
37  return;
38  }
39  }
40 
41  ecvGlobalShiftManager::ShiftInfo info("Last input");
42  info.scale = scale;
43  info.shift = shift;
44  info.preserve = preserve;
45  if (!s_lastInfoBuffer.empty()) {
46  info.name += QString(" (%1)").arg(s_lastInfoBuffer.size());
47  }
48  s_lastInfoBuffer.emplace_back(info);
49 }
50 
52  if (s_lastInfoBuffer.empty()) {
53  return false;
54  }
55  info = s_lastInfoBuffer.back();
56  return true;
57 }
58 
59 bool ecvGlobalShiftManager::GetLast(std::vector<ShiftInfo>& infos) {
60  try {
61  infos = s_lastInfoBuffer;
62  } catch (const std::bad_alloc&) {
63  CVLog::Warning("[ecvGlobalShiftManager::GetLast] Not enough memory");
64  return false;
65  }
66 
67  return true;
68 }
69 
71  return NeedShift(P.x) || NeedShift(P.y) || NeedShift(P.z);
72 }
73 
75  return fabs(d) >= MAX_COORDINATE_ABS_VALUE;
76 }
77 
79  return fabs(d) >= MAX_DIAGONAL_LENGTH;
80 }
81 
83  double diagonal,
84  Mode mode,
85  bool useInputCoordinatesShiftIfPossible,
86  CCVector3d& coordinatesShift,
87  bool* preserveCoordinateShift,
88  double* coordinatesScale,
89  bool* applyAll /*=0*/) {
90  assert(diagonal >= 0);
91  if (preserveCoordinateShift) {
92  *preserveCoordinateShift = true;
93  }
94  if (applyAll) {
95  *applyAll = false;
96  }
97 
98  // default scale
99  double scale =
100  (coordinatesScale ? std::max(*coordinatesScale, ZERO_TOLERANCE_D)
101  : 1.0);
102 
103  bool needShift = NeedShift(P);
104  bool needRescale = NeedRescale(diagonal);
105 
106  // if we can't display a dialog and no usable shift is specified, there's
107  // nothing we can do...
108  if (mode == NO_DIALOG && !useInputCoordinatesShiftIfPossible) {
109  coordinatesShift = CCVector3d(0, 0, 0);
110  if (coordinatesScale) {
111  *coordinatesScale = 1.0;
112  }
113  if (preserveCoordinateShift) {
114  *preserveCoordinateShift = false;
115  }
116 
117  if (needShift || needRescale) {
119  "[ecvGlobalShiftManager] Entity has very big coordinates: "
120  "original accuracy may be lost! (you should apply a Global "
121  "Shift or Scale)");
122  }
123 
124  return false;
125  }
126 
127  // is shift necessary?
128  if (needShift || useInputCoordinatesShiftIfPossible || needRescale ||
129  mode == ALWAYS_DISPLAY_DIALOG) {
130  // shift information already provided? (typically from a previous
131  // entity)
132  if (useInputCoordinatesShiftIfPossible &&
133  mode != ALWAYS_DISPLAY_DIALOG) {
134  if (mode == NO_DIALOG // either we are in non interactive mode
135  // (which means that shift is 'forced' by
136  // caller)
137  || (!NeedShift(P * scale + coordinatesShift) &&
138  !NeedRescale(diagonal *
139  scale)) // or we are in interactive mode and
140  // existing shift is pertinent
141  ) {
142  // have we already stored shift info?
143  if (mode == NO_DIALOG_AUTO_SHIFT && !s_lastInfoBuffer.empty()) {
144  // in "auto shift" mode, we may want to use it (to
145  // synchronize multiple clouds!)
146  for (const ecvGlobalShiftManager::ShiftInfo& shiftInfo :
148  if (!NeedShift(P * shiftInfo.scale + shiftInfo.shift) &&
149  !NeedRescale(diagonal * shiftInfo.scale)) {
150  coordinatesShift = shiftInfo.shift;
151  if (coordinatesScale) {
152  *coordinatesScale = shiftInfo.scale;
153  }
154  if (preserveCoordinateShift) {
155  *preserveCoordinateShift = shiftInfo.preserve;
156  }
157  return true;
158  }
159  }
160  }
161 
162  // save info for next time
163  if (coordinatesShift.norm2() != 0 || scale != 1.0) {
164  StoreShift(coordinatesShift, scale);
165  }
166  // user should use the provided shift information
167  return true;
168  }
169  //--> otherwise we (should) ask for a better one
170  }
171 
172  // let's deduce the right values (AUTO mode)
173  if (mode == NO_DIALOG_AUTO_SHIFT) {
174  // guess best shift & scale info from input point/diagonal
175  coordinatesShift = CCVector3d(0, 0, 0);
176  scale = 1.0;
177  if (needShift) {
178  coordinatesShift = BestShift(P);
179  if (preserveCoordinateShift) {
180  *preserveCoordinateShift = true;
181  }
182  }
183  if (coordinatesScale && needRescale) {
184  *coordinatesScale = BestScale(diagonal);
185  if (preserveCoordinateShift) {
186  *preserveCoordinateShift = true;
187  }
188 
189  // save info for next time
190  scale = *coordinatesScale;
191  }
192 
193  // save info for next time
194  StoreShift(coordinatesShift, 1.0, true);
195  return true;
196  }
197 
198  // otherwise let's ask the user for those values
199  ecvShiftAndScaleCloudDlg sasDlg(P, diagonal);
200  if (!applyAll) {
201  sasDlg.showApplyAllButton(false);
202  }
203  if (!coordinatesScale) {
204  sasDlg.showScaleItems(false);
205  }
206 
207  scale = 1.0;
208  CCVector3d shift(0, 0, 0);
209  if (useInputCoordinatesShiftIfPossible) {
210  // shift on load already provided? (typically from a previous file)
211  shift = coordinatesShift;
212  if (coordinatesScale) {
213  scale = *coordinatesScale;
214  }
215  if (mode != ALWAYS_DISPLAY_DIALOG) {
216  sasDlg.showWarning(true); // if we are here, it means that the
217  // provided shift isn't concordant
218  }
219  } else {
220  // guess best shift & scale info from input point/diagonal
221  if (needShift) {
222  shift = BestShift(P);
223  }
224  if (needRescale) {
225  scale = BestScale(diagonal);
226  }
227  }
228 
229  // add "suggested" entry
230  int index = sasDlg.addShiftInfo(ShiftInfo("Suggested", shift, scale));
231  sasDlg.setCurrentProfile(index);
232  // add "last" entry (if available)
233  if (!s_lastInfoBuffer.empty()) {
235 
236  // use the very last one for preserve or not preserve
237  sasDlg.setPreserveShiftOnSave(s_lastInfoBuffer.back().preserve);
238  }
239  sasDlg.showPreserveShiftOnSave(preserveCoordinateShift != nullptr);
240  // add entries from file (if any)
241  sasDlg.addFileInfo();
242 
243  // automatically make the first available shift that works
244  //(different than the suggested one) active
245  {
246  for (size_t i = static_cast<size_t>(std::max(0, index + 1));
247  i < sasDlg.infoCount(); ++i) {
248  ShiftInfo info;
249  if (sasDlg.getInfo(i, info)) {
250  // check if they work
251  if (!NeedShift((CCVector3d(P) + info.shift) * info.scale) &&
252  !NeedRescale(diagonal * info.scale)) {
253  sasDlg.setCurrentProfile(static_cast<int>(i));
254  break;
255  }
256  }
257  }
258  }
259  sasDlg.showTitle(needShift || needRescale);
260  if (sasDlg.exec()) {
261  // save info for next time
262  StoreShift(sasDlg.getShift(), sasDlg.getScale(),
263  sasDlg.preserveShiftOnSave());
264 
265  coordinatesShift = sasDlg.getShift();
266  if (coordinatesScale) {
267  *coordinatesScale = sasDlg.getScale();
268  }
269  if (preserveCoordinateShift) {
270  *preserveCoordinateShift = sasDlg.preserveShiftOnSave();
271  }
272  if (applyAll) {
273  *applyAll = sasDlg.applyAll();
274  }
275 
276  return true;
277  }
278  }
279 
280  coordinatesShift = CCVector3d(0, 0, 0);
281  if (coordinatesScale) {
282  *coordinatesScale = 1.0;
283  }
284  if (preserveCoordinateShift) {
285  *preserveCoordinateShift = false;
286  }
287 
288  return false;
289 }
290 
292  if (!NeedShift(P)) {
293  return CCVector3d(0, 0, 0);
294  }
295 
296  CCVector3d shift(fabs(P[0]) >= MAX_COORDINATE_ABS_VALUE ? -P[0] : 0,
297  fabs(P[1]) >= MAX_COORDINATE_ABS_VALUE ? -P[1] : 0,
298  fabs(P[2]) >= MAX_COORDINATE_ABS_VALUE ? -P[2] : 0);
299 
300  // round-off to the nearest hundred
301  shift.x = static_cast<int>(shift.x / 100) * 100.0;
302  shift.y = static_cast<int>(shift.y / 100) * 100.0;
303  shift.z = static_cast<int>(shift.z / 100) * 100.0;
304 
305  return shift;
306 }
307 
309  return d < MAX_DIAGONAL_LENGTH
310  ? 1.0
311  : pow(10.0, -static_cast<double>(
312  ceil(log(d / MAX_DIAGONAL_LENGTH))));
313 }
constexpr double ZERO_TOLERANCE_D
Definition: CVConst.h:49
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Definition: CVGeom.h:804
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:417
double norm2d() const
Returns vector square norm (forces double precision output)
Definition: CVGeom.h:419
static void StoreShift(const CCVector3d &shift, double scale, bool preserve=true)
Adds a new shift / scale couple.
static bool Handle(const CCVector3d &P, double diagonal, Mode mode, bool useInputCoordinatesShiftIfPossible, CCVector3d &coordinatesShift, bool *preserveCoordinateShift, double *coordinatesScale, bool *applyAll=0)
static double BestScale(double d)
static bool NeedShift(const CCVector3d &P)
Returns whether a particular point (coordinates) is too big or not.
static CCVector3d BestShift(const CCVector3d &P)
Suggests a shift for a given point expressed in global coordinate space.
static bool NeedRescale(double d)
Returns whether a particular dimension (e.g. diagonal) is too big or not.
Mode
Strategy to handle coordinates shift/scale.
static bool GetLast(ShiftInfo &info)
static double MAX_COORDINATE_ABS_VALUE
Dialog for selection of cloud center.
bool preserveShiftOnSave() const
Returns whether the global shift should be preserved or not.
void setPreserveShiftOnSave(bool state)
Sets whether the global shift should be preserved or not.
CCVector3d getShift() const
Returns shift.
void setCurrentProfile(int index)
Sets the current combo-box entry (profile)
size_t infoCount() const
Returns the number of info currently stored.
void showWarning(bool state)
Whether to show or not the warning about non pertinent shift information.
int addShiftInfo(const ecvGlobalShiftManager::ShiftInfo &info)
Adds shift info to the combox.
void showApplyAllButton(bool state)
Whether to show the 'Apply all' button or not.
void showScaleItems(bool state)
Whether to show dialog items related to scale.
void showTitle(bool state)
Whether to show or not the title.
bool applyAll() const
Whether shift should be applied to all files.
bool addFileInfo()
Adds information from default file (if any)
void showPreserveShiftOnSave(bool state)
Whether to show or not the 'Preserve shift on save' checkbox.
double getScale() const
Returns scale.
bool getInfo(size_t index, ecvGlobalShiftManager::ShiftInfo &info) const
Returns a given input info.
static std::vector< ecvGlobalShiftManager::ShiftInfo > s_lastInfoBuffer
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89