FreeCAD

Форк
0
/
EditableDatumLabel.cpp 
403 строки · 11.9 Кб
1
/// SPDX-License-Identifier: LGPL-2.1-or-later
2
/****************************************************************************
3
 *                                                                          *
4
 *   Copyright (c) 2023 Ondsel <development@ondsel.com>                     *
5
 *                                                                          *
6
 *   This file is part of FreeCAD.                                          *
7
 *                                                                          *
8
 *   FreeCAD is free software: you can redistribute it and/or modify it     *
9
 *   under the terms of the GNU Lesser General Public License as            *
10
 *   published by the Free Software Foundation, either version 2.1 of the   *
11
 *   License, or (at your option) any later version.                        *
12
 *                                                                          *
13
 *   FreeCAD is distributed in the hope that it will be useful, but         *
14
 *   WITHOUT ANY WARRANTY; without even the implied warranty of             *
15
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU       *
16
 *   Lesser General Public License for more details.                        *
17
 *                                                                          *
18
 *   You should have received a copy of the GNU Lesser General Public       *
19
 *   License along with FreeCAD. If not, see                                *
20
 *   <https://www.gnu.org/licenses/>.                                       *
21
 *                                                                          *
22
 ***************************************************************************/
23

24
#include "PreCompiled.h"
25
#ifndef _PreComp_
26
# include <Inventor/sensors/SoNodeSensor.h>
27
# include <Inventor/nodes/SoAnnotation.h>
28
# include <Inventor/nodes/SoOrthographicCamera.h>
29
# include <Inventor/nodes/SoTransform.h>
30
#endif // _PreComp_
31

32
#include <Gui/Application.h>
33
#include <Gui/View3DInventor.h>
34
#include <Gui/View3DInventorViewer.h>
35

36
#include "EditableDatumLabel.h"
37

38

39

40
using namespace Gui;
41

42

43
struct NodeData {
44
    EditableDatumLabel* label;
45
};
46

47
EditableDatumLabel::EditableDatumLabel(View3DInventorViewer* view,
48
                                       const Base::Placement& plc,
49
                                       SbColor color,
50
                                       bool autoDistance,
51
                                       bool avoidMouseCursor)
52
    : isSet(false)
53
    , autoDistance(autoDistance)
54
    , autoDistanceReverse(false)
55
    , avoidMouseCursor(avoidMouseCursor)
56
    , value(0.0)
57
    , viewer(view)
58
    , spinBox(nullptr)
59
    , cameraSensor(nullptr)
60
    , function(Function::Positioning)
61
{
62
    // NOLINTBEGIN
63
    root = new SoAnnotation;
64
    root->ref();
65
    root->renderCaching = SoSeparator::OFF;
66

67
    transform = new SoTransform();
68
    transform->ref();
69
    root->addChild(transform);
70

71
    label = new SoDatumLabel();
72
    label->ref();
73
    label->string = " ";
74
    label->textColor = color;
75
    label->size.setValue(17);
76
    label->lineWidth = 2.0;
77
    label->useAntialiasing = false;
78
    label->datumtype = SoDatumLabel::DISTANCE;
79
    label->param1 = 0.;
80
    label->param2 = 0.;
81
    label->param3 = 0.;
82
    if (autoDistance) {
83
        setLabelRecommendedDistance();
84
    }
85
    root->addChild(label);
86

87
    setPlacement(plc);
88
    // NOLINTEND
89
}
90

91
EditableDatumLabel::~EditableDatumLabel()
92
{
93
    deactivate();
94
    transform->unref();
95
    root->unref();
96
    label->unref();
97
}
98

99
void EditableDatumLabel::activate()
100
{
101
    if (!viewer || isActive()) {
102
        return;
103
    }
104

105
    static_cast<SoSeparator*>(viewer->getSceneGraph())->addChild(root); // NOLINT
106

107
    //track camera movements to update spinbox position.
108
    auto info = new NodeData{ this };
109
    cameraSensor = new SoNodeSensor([](void* data, SoSensor* sensor) {
110
        Q_UNUSED(sensor);
111
        auto info = static_cast<NodeData*>(data);
112
        info->label->positionSpinbox();
113
        if (info->label->autoDistance) {
114
            info->label->setLabelRecommendedDistance();
115
        }
116
    }, info);
117
    cameraSensor->attach(viewer->getCamera());
118
}
119

120
void EditableDatumLabel::deactivate()
121
{
122
    stopEdit();
123

124
    if (cameraSensor) {
125
        auto data = static_cast<NodeData*>(cameraSensor->getData());
126
        delete data;
127
        cameraSensor->detach();
128
        delete cameraSensor;
129
        cameraSensor = nullptr;
130
    }
131

132
    if (viewer) {
133
        static_cast<SoSeparator*>(viewer->getSceneGraph())->removeChild(root); // NOLINT
134
    }
135
}
136

137
void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool visibleToMouse)
138
{
139
    if (isInEdit()) {
140
        return;
141
    }
142

143
    QWidget* mdi = viewer->parentWidget();
144

145
    label->string = " ";
146

147
    spinBox = new QuantitySpinBox(mdi);
148
    spinBox->setUnit(Base::Unit::Length);
149
    spinBox->setMinimum(-INT_MAX);
150
    spinBox->setMaximum(INT_MAX);
151
    spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
152
    spinBox->setKeyboardTracking(false);
153
    spinBox->setFocusPolicy(Qt::ClickFocus); // prevent passing focus with tab.
154
    if (eventFilteringObj) {
155
        spinBox->installEventFilter(eventFilteringObj);
156
    }
157

158
    if (!visibleToMouse) {
159
        setSpinboxVisibleToMouse(visibleToMouse);
160
    }
161

162
    spinBox->show();
163
    setSpinboxValue(val);
164
    //Note: adjustSize apparently uses the Min/Max values to set the size. So if we don't set them to INT_MAX, the spinbox are much too big.
165
    spinBox->adjustSize();
166
    setFocusToSpinbox();
167

168
    connect(spinBox, qOverload<double>(&QuantitySpinBox::valueChanged),
169
        this, [this](double value) {
170
        this->isSet = true;
171
        this->value = value;
172
        Q_EMIT this->valueChanged(value);
173
    });
174
}
175

176
void EditableDatumLabel::stopEdit()
177
{
178
    if (spinBox) {
179
        // write the spinbox value in the label.
180
        Base::Quantity quantity = spinBox->value();
181

182
        double factor{};
183
        QString unitStr;
184
        QString valueStr;
185
        valueStr = quantity.getUserString(factor, unitStr);
186
        label->string = SbString(valueStr.toUtf8().constData());
187

188
        spinBox->deleteLater();
189
        spinBox = nullptr;
190
    }
191
}
192

193
bool EditableDatumLabel::isActive() const
194
{
195
    return cameraSensor != nullptr;
196
}
197

198
bool EditableDatumLabel::isInEdit() const
199
{
200
    return spinBox != nullptr;
201
}
202

203

204
double EditableDatumLabel::getValue() const
205
{
206
    // We use value rather than spinBox->rawValue() in case edit stopped.
207
    return value;
208
}
209

210
void EditableDatumLabel::setSpinboxValue(double val, const Base::Unit& unit)
211
{
212
    if (!spinBox) {
213
        Base::Console().DeveloperWarning("EditableDatumLabel::setSpinboxValue", "Spinbox doesn't exist in");
214
        return;
215
    }
216

217
    QSignalBlocker block(spinBox);
218
    spinBox->setValue(Base::Quantity(val, unit));
219
    value = val;
220
    positionSpinbox();
221

222
    if (spinBox->hasFocus()) {
223
        spinBox->selectNumber();
224
    }
225
}
226

227
void EditableDatumLabel::setFocusToSpinbox()
228
{
229
    if (!spinBox) {
230
        Base::Console().DeveloperWarning("EditableDatumLabel::setFocusToSpinbox", "Spinbox doesn't exist in");
231
        return;
232
    }
233
    if (!spinBox->hasFocus()) {
234
        spinBox->setFocus();
235
        spinBox->selectNumber();
236
    }
237
}
238

239
void EditableDatumLabel::positionSpinbox()
240
{
241
    if (!spinBox) {
242
        return;
243
    }
244

245
    if (spinBox->hasFocus()) {
246
        spinBox->raise();
247
    }
248

249
    QSize wSize = spinBox->size();
250
    QSize vSize = viewer->size();
251
    QPoint pxCoord = viewer->toQPoint(viewer->getPointOnViewport(getTextCenterPoint()));
252

253
    int posX = std::min(std::max(pxCoord.x() - wSize.width() / 2, 0), vSize.width() - wSize.width());
254
    int posY = std::min(std::max(pxCoord.y() - wSize.height() / 2, 0), vSize.height() - wSize.height());
255

256
    if (avoidMouseCursor) {
257
        QPoint cursorPos = viewer->mapFromGlobal(QCursor::pos());
258
        int margin = static_cast<int>(wSize.height() * 0.7); // NOLINT
259
        if ((cursorPos.x() > posX - margin && cursorPos.x() < posX + wSize.width() + margin)
260
            && (cursorPos.y() > posY - margin && cursorPos.y() < posY + wSize.height() + margin)) {
261
            posY = cursorPos.y() + ((cursorPos.y() > pxCoord.y()) ? - wSize.height() - margin : margin);
262
        }
263
    }
264

265
    pxCoord.setX(posX);
266
    pxCoord.setY(posY);
267
    spinBox->move(pxCoord);
268
}
269

270
SbVec3f EditableDatumLabel::getTextCenterPoint() const
271
{
272
    //Here we need the 3d point and not the 2d point as are the SoLabel points.
273
    // First we get the 2D point (on the sketch/image plane) of the middle of the text label.
274
    SbVec3f point2D = label->getLabelTextCenter();
275
    // Get the translation and rotation values from the transform
276
    SbVec3f translation = transform->translation.getValue();
277
    SbRotation rotation = transform->rotation.getValue();
278

279
    // Calculate the inverse transformation
280
    SbVec3f invTranslation = -translation;
281
    SbRotation invRotation = rotation.inverse();
282

283
    // Transform the 2D coordinates to 3D
284
    // Plane form
285
    SbVec3f RX(1, 0, 0);
286
    SbVec3f RY(0, 1, 0);
287

288
    // move to position of Sketch
289
    invRotation.multVec(RX, RX);
290
    invRotation.multVec(RY, RY);
291
    invRotation.multVec(invTranslation, invTranslation);
292

293
    // we use invTranslation as the Base because in setPlacement we set transform->translation using
294
    // placement.getPosition() to fix the Zoffset. But this applies the X & Y translation too.
295
    Base::Vector3d pos(invTranslation[0], invTranslation[1], invTranslation[2]);
296
    Base::Vector3d RXb(RX[0], RX[1], RX[2]);
297
    Base::Vector3d RYb(RY[0], RY[1], RY[2]);
298
    Base::Vector3d P2D(point2D[0], point2D[1], point2D[2]);
299
    P2D.TransformToCoordinateSystem(pos, RXb, RYb);
300

301
    return {float(P2D.x), float(P2D.y), float(P2D.z)};
302
}
303

304
void EditableDatumLabel::setPlacement(const Base::Placement& plc)
305
{
306
    double x{}, y{}, z{}, w{}; // NOLINT
307
    plc.getRotation().getValue(x, y, z, w);
308
    transform->rotation.setValue(x, y, z, w); // NOLINT
309

310
    Base::Vector3d pos = plc.getPosition();
311
    transform->translation.setValue(float(pos.x), float(pos.y), float(pos.z));
312

313
    Base::Vector3d RN(0, 0, 1);
314
    RN = plc.getRotation().multVec(RN);
315
    label->norm.setValue(SbVec3f(float(RN.x), float(RN.y), float(RN.z)));
316
}
317

318
// NOLINTNEXTLINE
319
void EditableDatumLabel::setColor(SbColor color)
320
{
321
    label->textColor = color;
322
}
323

324
void EditableDatumLabel::setFocus()
325
{
326
    if (spinBox) {
327
        spinBox->selectNumber();
328
    }
329
}
330

331
void EditableDatumLabel::setPoints(SbVec3f p1, SbVec3f p2)
332
{
333
    label->setPoints(p1, p2);
334
    //TODO: here the position of the spinbox is not going to be center of p1, p2 because the point given by getTextCenterPoint
335
    // is not updated yet. it will be only on redraw so it is actually positioning on previous position.
336

337
    positionSpinbox();
338
    if (autoDistance) {
339
        setLabelRecommendedDistance();
340
    }
341
}
342

343
void EditableDatumLabel::setPoints(Base::Vector3d p1, Base::Vector3d p2)
344
{
345
    setPoints(SbVec3f(float(p1.x), float(p1.y), float(p1.z)),
346
              SbVec3f(float(p2.x), float(p2.y), float(p2.z)));
347
}
348

349
// NOLINTNEXTLINE
350
void EditableDatumLabel::setLabelType(SoDatumLabel::Type type, Function funct)
351
{
352
    label->datumtype = type;
353
    function = funct;
354
}
355

356
// NOLINTNEXTLINE
357
void EditableDatumLabel::setLabelDistance(double val)
358
{
359
    label->param1 = float(val);
360
}
361

362
// NOLINTNEXTLINE
363
void EditableDatumLabel::setLabelStartAngle(double val)
364
{
365
    label->param2 = float(val);
366
}
367

368
// NOLINTNEXTLINE
369
void EditableDatumLabel::setLabelRange(double val)
370
{
371
    label->param3 = float(val);
372
}
373

374
void EditableDatumLabel::setLabelRecommendedDistance()
375
{
376
    // Takes the 3d view size, and set the label distance to a % of that, such that the distance does not depend on the zoom level.
377
    float width = -1.;
378
    float length = -1.;
379
    viewer->getDimensions(width, length);
380

381
    if (width == -1. || length == -1.) {
382
        return;
383
    }
384

385
    label->param1 = (autoDistanceReverse ? -1.0F : 1.0F) * (width + length) * 0.03F; // NOLINT
386
}
387

388
void EditableDatumLabel::setLabelAutoDistanceReverse(bool val)
389
{
390
    autoDistanceReverse = val;
391
}
392

393
void EditableDatumLabel::setSpinboxVisibleToMouse(bool val)
394
{
395
    spinBox->setAttribute(Qt::WA_TransparentForMouseEvents, !val);
396
}
397

398
EditableDatumLabel::Function EditableDatumLabel::getFunction()
399
{
400
    return function;
401
}
402

403
#include "moc_EditableDatumLabel.cpp" // NOLINT
404

405

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.