FreeCAD

Форк
0
/
QGIViewDimension.cpp 
2745 строк · 106.2 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk>                 *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24

25
#ifdef FC_OS_WIN32
26
# define _USE_MATH_DEFINES //resolves Windows & M_PI issues
27
#endif
28

29
#ifndef _PreComp_
30
# include <cmath>
31

32
# include <QGraphicsScene>
33
# include <QGraphicsSceneMouseEvent>
34
# include <QPaintDevice>
35
# include <QPainter>
36
# include <QPainterPath>
37
# include <QSvgGenerator>
38
#endif
39

40
#include <App/Application.h>
41
#include <Base/Console.h>
42
#include <Base/Parameter.h>
43
#include <Base/UnitsApi.h>
44
#include <Gui/Command.h>
45
#include <Mod/TechDraw/App/DrawUtil.h>
46
#include <Mod/TechDraw/App/DrawViewDimension.h>
47
#include <Mod/TechDraw/App/DrawViewPart.h>
48
#include <Mod/TechDraw/App/Geometry.h>
49

50
#include "QGIViewDimension.h"
51
#include "PreferencesGui.h"
52
#include "QGIArrow.h"
53
#include "QGIDimLines.h"
54
#include "QGIVertex.h"
55
#include "QGCustomSvg.h"
56
#include "ViewProviderDimension.h"
57
#include "ZVALUE.h"
58

59

60
#define NORMAL 0
61
#define PRE 1
62
#define SEL 2
63

64

65
//TODO: hide the Qt coord system (+y down).
66

67
using namespace TechDraw;
68
using namespace TechDrawGui;
69

70
enum SnapMode
71
{
72
    NoSnap,
73
    VerticalSnap,
74
    HorizontalSnap
75
};
76

77
enum DragState
78
{
79
    NoDrag,
80
    DragStarted,
81
    Dragging
82
};
83

84

85
QGIDatumLabel::QGIDatumLabel() : m_dragState(NoDrag)
86
{
87
    verticalSep = false;
88
    posX = 0;
89
    posY = 0;
90

91
    parent = nullptr;
92

93
    setCacheMode(QGraphicsItem::NoCache);
94
    setFlag(ItemSendsGeometryChanges, true);
95
    setFlag(ItemIsMovable, true);
96
    setFlag(ItemIsSelectable, true);
97
    setAcceptHoverEvents(true);
98
    setFiltersChildEvents(true);
99

100
    m_dimText = new QGCustomText();
101
    m_dimText->setTightBounding(true);
102
    m_dimText->setParentItem(this);
103
    m_tolTextOver = new QGCustomText();
104
    m_tolTextOver->setTightBounding(true);
105
    m_tolTextOver->setParentItem(this);
106
    m_tolTextUnder = new QGCustomText();
107
    m_tolTextUnder->setTightBounding(true);
108
    m_tolTextUnder->setParentItem(this);
109
    m_unitText = new QGCustomText();
110
    m_unitText->setTightBounding(true);
111
    m_unitText->setParentItem(this);
112

113
    m_ctrl = false;
114

115
    m_isFramed = false;
116
    m_lineWidth = Rez::guiX(0.5);
117
}
118

119
QVariant QGIDatumLabel::itemChange(GraphicsItemChange change, const QVariant& value)
120
{
121
    if (change == ItemSelectedHasChanged && scene()) {
122
        if (isSelected()) {
123
            setPrettySel();
124
        }
125
        else {
126
            setPrettyNormal();
127
            if (m_dragState == Dragging) {
128
                //stop the drag if we are no longer selected.
129
                m_dragState = NoDrag;
130
                Q_EMIT dragFinished();
131
            }
132
        }
133
    }
134
    else if (change == ItemPositionHasChanged && scene()) {
135
        setLabelCenter();
136
        m_dragState = Dragging;
137
        Q_EMIT dragging(m_ctrl);
138
    }
139

140
    return QGraphicsItem::itemChange(change, value);
141
}
142

143
void QGIDatumLabel::mousePressEvent(QGraphicsSceneMouseEvent* event)
144
{
145
    if (event->modifiers() & Qt::ControlModifier) {
146
        m_ctrl = true;
147
    }
148

149
    QGraphicsItem::mousePressEvent(event);
150
}
151

152
void QGIDatumLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
153
{
154
    //    Base::Console().Message("QGIDL::mouseReleaseEvent()\n");
155
    m_ctrl = false;
156
    if (m_dragState == Dragging) {
157
        m_dragState = NoDrag;
158
        Q_EMIT dragFinished();
159
    }
160

161
    QGraphicsItem::mouseReleaseEvent(event);
162
}
163

164
void QGIDatumLabel::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
165
{
166
    QGIViewDimension* qgivDimension = dynamic_cast<QGIViewDimension*>(parentItem());
167
    if (!qgivDimension) {
168
        qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No parent item";
169
        return;
170
    }
171

172
    auto ViewProvider = dynamic_cast<ViewProviderDimension*>(
173
        qgivDimension->getViewProvider(qgivDimension->getViewObject()));
174
    if (!ViewProvider) {
175
        qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No valid view provider";
176
        return;
177
    }
178

179
    ViewProvider->startDefaultEditMode();
180
    QGraphicsItem::mouseDoubleClickEvent(event);
181
}
182

183
void QGIDatumLabel::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
184
{
185
    Q_EMIT hover(true);
186
    if (!isSelected()) {
187
        setPrettyPre();
188
    }
189
    else {
190
        setPrettySel();
191
    }
192
    QGraphicsItem::hoverEnterEvent(event);
193
}
194

195
void QGIDatumLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
196
{
197
    Q_EMIT hover(false);
198
    if (!isSelected()) {
199
        setPrettyNormal();
200
    }
201
    else {
202
        setPrettySel();
203
    }
204

205
    QGraphicsItem::hoverLeaveEvent(event);
206
}
207

208
QRectF QGIDatumLabel::boundingRect() const
209
{
210
    QRectF result = childrenBoundingRect();
211
    result.adjust(-m_lineWidth * 4.0, 0.0, 0.0, 0.0);
212
    return result;
213
}
214

215
void QGIDatumLabel::paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
216
                          QWidget* widget)
217
{
218
    Q_UNUSED(widget);
219
    Q_UNUSED(painter);
220
    QStyleOptionGraphicsItem myOption(*option);
221
    myOption.state &= ~QStyle::State_Selected;
222

223
    //    painter->setPen(Qt::blue);
224
    //    painter->drawRect(boundingRect());          //good for debugging
225

226
    if (m_isFramed) {
227
        QPen prevPen = painter->pen();
228
        QPen framePen(prevPen);
229

230
        framePen.setWidthF(m_lineWidth);
231
        framePen.setColor(m_dimText->defaultTextColor());
232

233
        painter->setPen(framePen);
234
        painter->drawRect(boundingRect());
235
        painter->setPen(prevPen);
236
    }
237
}
238

239
void QGIDatumLabel::setPosFromCenter(const double& xCenter, const double& yCenter)
240
{
241
    prepareGeometryChange();
242
    QGIViewDimension* qgivd = dynamic_cast<QGIViewDimension*>(parentItem());
243
    if (!qgivd) {
244
        return;
245
    }
246
    const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(qgivd->getViewObject()));
247
    if (!dim) {
248
        return;
249
    }
250

251
    //set label's Qt position(top, left) given boundingRect center point
252
    setPos(xCenter - m_dimText->boundingRect().width() / 2.,
253
           yCenter - m_dimText->boundingRect().height() / 2.);
254

255
    QString uText = m_unitText->toPlainText();
256
    if ((uText.size() > 0) && (uText.at(0) != QChar::fromLatin1(' '))) {
257
        QString vText = m_dimText->toPlainText();
258
        vText = vText + uText;
259
        m_dimText->setPlainText(vText);
260
        m_unitText->setPlainText(QString());
261
    }
262

263
    QRectF labelBox = m_dimText->boundingRect();
264
    double right = labelBox.right();
265
    double top = labelBox.top();
266
    double bottom = labelBox.bottom();
267
    double middle = (top + bottom) / 2.0;
268

269
    //set unit position
270
    QRectF unitBox = m_unitText->boundingRect();
271
    double unitWidth = unitBox.width();
272
    double unitRight = right + unitWidth;
273
    // Set the m_unitText font *baseline* at same height as the m_dimText font baseline
274
    m_unitText->setPos(right, 0.0);
275

276
    //set tolerance position
277
    QRectF overBox = m_tolTextOver->boundingRect();
278
    double tolLeft  = unitRight;
279

280
    // Adjust for difference in tight and original bounding box sizes, note the y-coord down system
281
    QPointF tol_adj = m_tolTextOver->tightBoundingAdjust();
282
    m_tolTextOver->justifyLeftAt(tolLeft + tol_adj.x(), middle - tol_adj.y(), false);
283
    tol_adj = m_tolTextUnder->tightBoundingAdjust();
284
    m_tolTextUnder->justifyLeftAt(tolLeft + tol_adj.x(), middle + overBox.height() - tol_adj.y(),
285
                                   false);
286
}
287

288
void QGIDatumLabel::setLabelCenter()
289
{
290
    //save label's bRect center (posX, posY) given Qt position (top, left)
291
    posX = x() + m_dimText->boundingRect().width() / 2.;
292
    posY = y() + m_dimText->boundingRect().height() / 2.;
293
}
294

295
void QGIDatumLabel::setFont(QFont font)
296
{
297
    prepareGeometryChange();
298
    m_dimText->setFont(font);
299
    m_unitText->setFont(font);
300
    QFont tFont(font);
301
    double fontSize = font.pixelSize();
302
    double tolAdj = getTolAdjust();
303
    tFont.setPixelSize((int)(fontSize * tolAdj));
304
    m_tolTextOver->setFont(tFont);
305
    m_tolTextUnder->setFont(tFont);
306
}
307

308
void QGIDatumLabel::setDimString(QString text)
309
{
310
    prepareGeometryChange();
311
    m_dimText->setPlainText(text);
312
}
313

314
void QGIDatumLabel::setDimString(QString text, qreal maxWidth)
315
{
316
    prepareGeometryChange();
317
    m_dimText->setPlainText(text);
318
    m_dimText->setTextWidth(maxWidth);
319
}
320

321
void QGIDatumLabel::setToleranceString()
322
{
323
    prepareGeometryChange();
324
    QGIViewDimension* qgivd = dynamic_cast<QGIViewDimension*>(parentItem());
325
    if (!qgivd) {
326
        return;
327
    }
328
    const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(qgivd->getViewObject()));
329
    if (!dim) {
330
        return;
331
        // don't show if both are zero or if EqualTolerance is true
332
    }
333
    else if (!dim->hasOverUnderTolerance() || dim->EqualTolerance.getValue()
334
             || dim->TheoreticalExact.getValue()) {
335
        m_tolTextOver->hide();
336
        m_tolTextUnder->hide();
337
        // we must explicitly empty the text otherwise the frame drawn for
338
        // TheoreticalExact would be as wide as necessary for the text
339
        m_tolTextOver->setPlainText(QString());
340
        m_tolTextUnder->setPlainText(QString());
341
        return;
342
    }
343

344
    std::pair<std::string, std::string> labelTexts, unitTexts;
345

346
    if (dim->ArbitraryTolerances.getValue()) {
347
        labelTexts = dim->getFormattedToleranceValues(1);//copy tolerance spec
348
        unitTexts.first = "";
349
        unitTexts.second = "";
350
    }
351
    else {
352
        if (dim->isMultiValueSchema()) {
353
            labelTexts = dim->getFormattedToleranceValues(0);//don't format multis
354
            unitTexts.first = "";
355
            unitTexts.second = "";
356
        }
357
        else {
358
            labelTexts = dim->getFormattedToleranceValues(1);// prefix value [unit] postfix
359
            unitTexts = dim->getFormattedToleranceValues(2); //just the unit
360
        }
361
    }
362

363
    if (labelTexts.first.empty()) {
364
        m_tolTextUnder->hide();
365
    }
366
    else {
367
        m_tolTextUnder->setPlainText(QString::fromUtf8(labelTexts.first.c_str()));
368
        m_tolTextUnder->show();
369
    }
370
    if (labelTexts.second.empty()) {
371
        m_tolTextOver->hide();
372
    }
373
    else {
374
        m_tolTextOver->setPlainText(QString::fromUtf8(labelTexts.second.c_str()));
375
        m_tolTextOver->show();
376
    }
377

378
    return;
379
}
380

381
void QGIDatumLabel::setUnitString(QString text)
382
{
383
    prepareGeometryChange();
384
    if (text.isEmpty()) {
385
        m_unitText->hide();
386
    }
387
    else {
388
        m_unitText->setPlainText(text);
389
        m_unitText->show();
390
    }
391
}
392

393

394
int QGIDatumLabel::getPrecision()
395
{
396
    if (Preferences::useGlobalDecimals()) {
397
        return Base::UnitsApi::getDecimals();
398
    }
399
    return Preferences::getPreferenceGroup("Dimensions")->GetInt("AltDecimals", 2);
400
}
401

402
double QGIDatumLabel::getTolAdjust()
403
{
404
    return Preferences::getPreferenceGroup("Dimensions")->GetFloat("TolSizeAdjust", 0.50);
405
}
406

407

408
void QGIDatumLabel::setPrettySel()
409
{
410
    //    Base::Console().Message("QGIDL::setPrettySel()\n");
411
    m_dimText->setPrettySel();
412
    m_tolTextOver->setPrettySel();
413
    m_tolTextUnder->setPrettySel();
414
    m_unitText->setPrettySel();
415
    Q_EMIT setPretty(SEL);
416
}
417

418
void QGIDatumLabel::setPrettyPre()
419
{
420
    //    Base::Console().Message("QGIDL::setPrettyPre()\n");
421
    m_dimText->setPrettyPre();
422
    m_tolTextOver->setPrettyPre();
423
    m_tolTextUnder->setPrettyPre();
424
    m_unitText->setPrettyPre();
425
    Q_EMIT setPretty(PRE);
426
}
427

428
void QGIDatumLabel::setPrettyNormal()
429
{
430
    //    Base::Console().Message("QGIDL::setPrettyNormal()\n");
431
    m_dimText->setPrettyNormal();
432
    m_tolTextOver->setPrettyNormal();
433
    m_tolTextUnder->setPrettyNormal();
434
    m_unitText->setPrettyNormal();
435
    Q_EMIT setPretty(NORMAL);
436
}
437

438
void QGIDatumLabel::setColor(QColor color)
439
{
440
    //    Base::Console().Message("QGIDL::setColor(%s)\n", qPrintable(c.name()));
441
    m_colNormal = color;
442
    m_dimText->setColor(m_colNormal);
443
    m_tolTextOver->setColor(m_colNormal);
444
    m_tolTextUnder->setColor(m_colNormal);
445
    m_unitText->setColor(m_colNormal);
446
}
447

448
//**************************************************************
449
QGIViewDimension::QGIViewDimension() : dvDimension(nullptr), hasHover(false), m_lineWidth(0.0)
450
{
451
    setHandlesChildEvents(false);
452
    setFlag(QGraphicsItem::ItemIsMovable, false);
453
    setFlag(QGraphicsItem::ItemIsSelectable, false);
454
    setAcceptHoverEvents(false);
455
    setCacheMode(QGraphicsItem::NoCache);
456

457
    datumLabel = new QGIDatumLabel();
458
    datumLabel->setQDim(this);
459

460
    addToGroup(datumLabel);
461

462
    dimLines = new QGIDimLines();
463
    addToGroup(dimLines);
464

465
    aHead1 = new QGIArrow();
466
    addToGroup(aHead1);
467

468
    aHead2 = new QGIArrow();
469
    addToGroup(aHead2);
470

471
    datumLabel->setZValue(ZVALUE::DIMENSION);
472
    aHead1->setZValue(ZVALUE::DIMENSION);
473
    aHead2->setZValue(ZVALUE::DIMENSION);
474
    dimLines->setZValue(ZVALUE::DIMENSION);
475
    dimLines->setStyle(Qt::SolidLine);
476

477
    // connecting the needed slots and signals
478
    QObject::connect(datumLabel, &QGIDatumLabel::dragging, this, &QGIViewDimension::datumLabelDragged);
479

480
    QObject::connect(datumLabel, &QGIDatumLabel::dragFinished, this, &QGIViewDimension::datumLabelDragFinished);
481

482
    QObject::connect(datumLabel, &QGIDatumLabel::selected, this, &QGIViewDimension::select);
483

484
    QObject::connect(datumLabel, &QGIDatumLabel::hover, this, &QGIViewDimension::hover);
485

486
    QObject::connect(datumLabel, &QGIDatumLabel::setPretty, this, &QGIViewDimension::onPrettyChanged);
487

488
    setZValue(ZVALUE::DIMENSION);//note: this won't paint dimensions over another View if it stacks
489
                                 //above this Dimension's parent view.   need Layers?
490
    hideFrame();
491

492
    m_refFlag = new QGCustomSvg();
493
    m_refFlag->setParentItem(this);
494
    m_refFlag->load(QString::fromUtf8(":/icons/TechDraw_RefError.svg"));
495
    m_refFlag->setZValue(ZVALUE::LOCK);
496
    m_refFlag->hide();
497
}
498

499
QVariant QGIViewDimension::itemChange(GraphicsItemChange change, const QVariant& value)
500
{
501
    if (change == ItemSelectedHasChanged && scene()) {
502
        if (isSelected()) {
503
            datumLabel->setSelected(true);
504
        }
505
        else {
506
            datumLabel->setSelected(false);
507
        }
508
        draw();
509
        return value;
510
    }
511
    if(change == ItemPositionChange && scene()) {
512
        // QGIVDimension doesn't really change position the way other views do.
513
        // If we call QGIView::itemChange it will set the position to (0,0) instead of
514
        // using the label's position, and the Dimension will be in the wrong place.
515
        // QGIVBalloon behaves the same way.
516
        return QGraphicsItem::itemChange(change, value);
517
    }
518
    return QGIView::itemChange(change, value);
519
}
520

521
bool QGIViewDimension::getGroupSelection()
522
{
523
    return datumLabel->isSelected();
524
}
525

526
//Set selection state for this and its children
527
void QGIViewDimension::setGroupSelection(bool isSelected)
528
{
529
    //    Base::Console().Message("QGIVD::setGroupSelection(%d)\n", b);
530
    setSelected(isSelected);
531
    datumLabel->setSelected(isSelected);
532
    dimLines->setSelected(isSelected);
533
    aHead1->setSelected(isSelected);
534
    aHead2->setSelected(isSelected);
535
}
536

537
void QGIViewDimension::select(bool state)
538
{
539
    setSelected(state);
540
    draw();
541
}
542

543
//surrogate for hover enter (true), hover leave (false) events
544
void QGIViewDimension::hover(bool state)
545
{
546
    hasHover = state;
547
    draw();
548
}
549

550
void QGIViewDimension::setViewPartFeature(TechDraw::DrawViewDimension* obj)
551
{
552
    //    Base::Console().Message("QGIVD::setViewPartFeature()\n");
553
    if (!obj) {
554
        return;
555
    }
556

557
    setViewFeature(static_cast<TechDraw::DrawView*>(obj));
558
    dvDimension = obj;
559

560
    // Set the QGIGroup Properties based on the DrawView
561
    float x = Rez::guiX(obj->X.getValue());
562
    float y = Rez::guiX(-obj->Y.getValue());
563

564
    datumLabel->setPosFromCenter(x, y);
565

566
    setNormalColorAll();
567
    setPrettyNormal();
568

569
    updateDim();
570
    draw();
571
}
572

573
void QGIViewDimension::setNormalColorAll()
574
{
575
    QColor qc = prefNormalColor();
576
    datumLabel->setColor(qc);
577
    dimLines->setNormalColor(qc);
578
    aHead1->setNormalColor(qc);
579
    aHead1->setFillColor(qc);
580
    aHead2->setNormalColor(qc);
581
    aHead2->setFillColor(qc);
582
}
583

584
//QGIViewDimension does not behave the same as other QGIView derived classes
585
//and so mouse events need to be ignored.  Only the QGIDatumLabel mouse events are relevant.
586
void QGIViewDimension::mousePressEvent(QGraphicsSceneMouseEvent* event)
587
{
588
    //    Base::Console().Message("QGIVD::mousePressEvent() - %s\n", getViewName());
589
    QGraphicsItem::mousePressEvent(event);
590
}
591

592
void QGIViewDimension::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
593
{
594
    QGraphicsItem::mouseMoveEvent(event);
595
}
596

597
void QGIViewDimension::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
598
{
599
    //    Base::Console().Message("QGIVDim::mouseReleaseEvent() - %s\n", getViewName());
600
    QGraphicsItem::mouseReleaseEvent(event);
601
}
602

603
void QGIViewDimension::updateView(bool update)
604
{
605
    Q_UNUSED(update);
606
    auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject()));
607
    if (!dim) {
608
        return;
609
    }
610

611
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
612
    if (!vp) {
613
        return;
614
    }
615

616
    if (update || dim->X.isTouched() || dim->Y.isTouched()) {
617
        float x = Rez::guiX(dim->X.getValue());
618
        float y = Rez::guiX(dim->Y.getValue());
619
        datumLabel->setPosFromCenter(x, -y);
620
        updateDim();
621
    }
622
    else if (vp->Fontsize.isTouched() || vp->Font.isTouched()) {
623
        updateDim();
624
    }
625
    else if (vp->LineWidth.isTouched()) {
626
        m_lineWidth = vp->LineWidth.getValue();
627
        updateDim();
628
    }
629
    else {
630
        updateDim();
631
    }
632

633
    if (dim->hasGoodReferences()) {
634
        m_refFlag->hide();
635
    } else {
636
        m_refFlag->centerAt(datumLabel->pos() + datumLabel->boundingRect().center());
637
        m_refFlag->show();
638
    }
639

640
    draw();
641
}
642

643
void QGIViewDimension::updateDim()
644
{
645
    const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject()));
646
    if (!dim) {
647
        return;
648
    }
649
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
650
    if (!vp) {
651
        return;
652
    }
653

654
    QString labelText =
655
        QString::fromUtf8(dim->getFormattedDimensionValue(1).c_str());// pre value [unit] post
656
    if (dim->isMultiValueSchema()) {
657
        labelText =
658
            QString::fromUtf8(dim->getFormattedDimensionValue(0).c_str());//don't format multis
659
    }
660

661
    QFont font = datumLabel->getFont();
662
    font.setFamily(QString::fromUtf8(vp->Font.getValue()));
663
    int fontSize = QGIView::exactFontSize(vp->Font.getValue(), vp->Fontsize.getValue());
664
    font.setPixelSize(fontSize);
665
    datumLabel->setFont(font);
666

667
    prepareGeometryChange();
668
    datumLabel->setDimString(labelText);
669
    datumLabel->setToleranceString();
670
    datumLabel->setPosFromCenter(datumLabel->X(), datumLabel->Y());
671

672
    datumLabel->setFramed(dim->TheoreticalExact.getValue());
673
    datumLabel->setLineWidth(m_lineWidth);
674
}
675

676
void QGIViewDimension::datumLabelDragged(bool ctrl)
677
{
678
    Q_UNUSED(ctrl);
679
    draw();
680
}
681

682
void QGIViewDimension::datumLabelDragFinished()
683
{
684
    auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject()));
685

686
    if (!dim) {
687
        return;
688
    }
689

690
    double x = Rez::appX(datumLabel->X()), y = Rez::appX(datumLabel->Y());
691
    Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Drag Dimension"));
692
    Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f",
693
                            dim->getNameInDocument(), x);
694
    Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f",
695
                            dim->getNameInDocument(), -y);
696
    Gui::Command::commitCommand();
697
}
698

699
//this is for formatting and finding centers, not display
700
QString QGIViewDimension::getLabelText()
701
{
702
    QString first = datumLabel->getDimText()->toPlainText();
703
    QString second = datumLabel->getTolTextOver()->toPlainText();
704
    QString third = datumLabel->getTolTextUnder()->toPlainText();
705
    if (second.length() > third.length()) {
706
        return first + second;
707
    }
708
    else {
709
        return first + third;
710
    }
711
}
712

713
void QGIViewDimension::draw()
714
{
715
    prepareGeometryChange();
716
    if (!isVisible()) {
717
        return;
718
    }
719

720
    TechDraw::DrawViewDimension* dim = dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject());
721
    if (!dim ||//nothing to draw, don't try
722
        !dim->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())
723
        || !dim->has2DReferences()) {
724
        datumLabel->hide();
725
        hide();
726
        return;
727
    }
728

729
    const TechDraw::DrawViewPart* refObj = dim->getViewPart();
730
    if (!refObj) {
731
        return;
732
    }
733
    if (!refObj->hasGeometry()) {//nothing to draw yet (restoring)
734
        datumLabel->hide();
735
        hide();
736
        return;
737
    }
738

739
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
740
    if (!vp) {
741
        datumLabel->show();
742
        show();
743
        return;
744
    }
745

746
    m_lineWidth = Rez::guiX(vp->LineWidth.getValue());
747
    datumLabel->setRotation(0.0);
748
    datumLabel->show();
749

750
    resetArrows();
751
    show();
752

753
    if (vp->RenderingExtent.getValue() > ViewProviderDimension::REND_EXTENT_NONE) {
754
        // We are expected to draw something, not just display the value
755
        const char* dimType = dim->Type.getValueAsString();
756

757
        if (strcmp(dimType, "Distance") == 0 || strcmp(dimType, "DistanceX") == 0
758
            || strcmp(dimType, "DistanceY") == 0) {
759
            drawDistance(dim, vp);
760
        }
761
        else if (strcmp(dimType, "Diameter") == 0) {
762
            drawDiameter(dim, vp);
763
        }
764
        else if (strcmp(dimType, "Radius") == 0) {
765
            drawRadius(dim, vp);
766
        }
767
        else if (strcmp(dimType, "Angle") == 0 || strcmp(dimType, "Angle3Pt") == 0) {
768
            drawAngle(dim, vp);
769
        }
770
        else {
771
            Base::Console().Error("QGIVD::draw - this DimensionType is unknown: %s\n", dimType);
772
        }
773
    }
774
    else {
775
        // No dimension lines are drawn, the arrows are hidden
776
        dimLines->setPath(QPainterPath());
777
        drawArrows(0, nullptr, nullptr, false);
778
    }
779

780
    // reset the colors
781
    if (hasHover && !datumLabel->isSelected()) {
782
        setPrettyPre();
783
    }
784
    else if (datumLabel->isSelected()) {
785
        setPrettySel();
786
    }
787
    else {
788
        setPrettyNormal();
789
    }
790

791
    update();
792
    if (parentItem()) {
793
        //TODO: parent redraw still required with new frame/label??
794
        parentItem()->update();
795
    }
796
}
797

798
double QGIViewDimension::getAnglePlacementFactor(double testAngle, double endAngle,
799
                                                 double startRotation)
800
{
801
    if (startRotation > 0.0) {
802
        startRotation = -startRotation;
803
        endAngle -= startRotation;
804
        if (endAngle > M_PI) {
805
            endAngle -= M_2PI;
806
        }
807
    }
808

809
    if (testAngle > endAngle) {
810
        testAngle -= M_2PI;
811
    }
812

813
    if (testAngle >= endAngle + startRotation) {
814
        return +1.0;
815
    }
816

817
    testAngle += M_PI;
818
    if (testAngle > endAngle) {
819
        testAngle -= M_2PI;
820
    }
821

822
    if (testAngle >= endAngle + startRotation) {
823
        return -1.0;
824
    }
825

826
    return 0.0;
827
}
828

829
int QGIViewDimension::compareAngleStraightness(double straightAngle, double leftAngle,
830
                                               double rightAngle, double leftStrikeFactor,
831
                                               double rightStrikeFactor)
832
{
833
    double leftDelta = DrawUtil::angleComposition(M_PI, straightAngle - leftAngle);
834
    double rightDelta = DrawUtil::angleComposition(rightAngle, -straightAngle);
835

836
    if (fabs(leftDelta - rightDelta) <= Precision::Confusion()) {
837
        return 0;
838
    }
839

840
    if (leftStrikeFactor == rightStrikeFactor) {
841
        double leftBend = fabs(leftDelta);
842
        double rightBend = fabs(rightDelta);
843

844
        return DrawUtil::sgn(leftBend - rightBend);
845
    }
846

847
    return rightStrikeFactor > leftStrikeFactor ? -1 : +1;
848
}
849

850
double QGIViewDimension::getIsoStandardLinePlacement(double labelAngle)
851
{
852
    // According to ISO 129-1 Standard Figure 23, the bordering angle is 1/2 PI, resp. -1/2 PI
853
    return labelAngle < -M_PI / 2.0 || labelAngle > +M_PI / 2.0 ? +1.0 : -1.0;
854
}
855

856
Base::Vector2d QGIViewDimension::getIsoRefOutsetPoint(const Base::BoundBox2d& labelRectangle,
857
                                                      bool right) const
858
{
859
    return Base::Vector2d(right ? labelRectangle.MinX - getDefaultIsoReferenceLineOverhang()
860
                                : labelRectangle.MaxX + getDefaultIsoReferenceLineOverhang(),
861
                          labelRectangle.MinY - getIsoDimensionLineSpacing());
862
}
863

864
Base::Vector2d QGIViewDimension::getIsoRefJointPoint(const Base::BoundBox2d& labelRectangle,
865
                                                     bool right) const
866
{
867
    return getIsoRefOutsetPoint(labelRectangle, !right);
868
}
869

870
Base::Vector2d QGIViewDimension::getAsmeRefOutsetPoint(const Base::BoundBox2d& labelRectangle,
871
                                                       bool right) const
872
{
873
    return Base::Vector2d(right ? labelRectangle.MaxX : labelRectangle.MinX,
874
                          labelRectangle.GetCenter().y);
875
}
876

877
Base::Vector2d QGIViewDimension::getAsmeRefJointPoint(const Base::BoundBox2d& labelRectangle,
878
                                                      bool right) const
879
{
880
    return Base::Vector2d(right ? labelRectangle.MaxX + getDefaultAsmeHorizontalLeaderLength()
881
                                : labelRectangle.MinX - getDefaultAsmeHorizontalLeaderLength(),
882
                          labelRectangle.GetCenter().y);
883
}
884

885
//find intersection of line L (through linePoint at angle lineAngle) and a line perpendicular to L
886
//passing through perpendicularPoint
887
//tricky vector algebra note:
888
//a*b is the magnitude of the projection of a onto b
889
//so we project a vector linePoint-perpendicularPoint onto unit vector in lineAngle direction giving
890
//the distance from linePoint to intersection, then make a displacement vector and add it to linePoint
891
Base::Vector2d QGIViewDimension::computePerpendicularIntersection(
892
    const Base::Vector2d& linePoint, const Base::Vector2d& perpendicularPoint, double lineAngle)
893
{
894
    return linePoint
895
        + Base::Vector2d::FromPolar((perpendicularPoint - linePoint)
896
                                        * Base::Vector2d::FromPolar(1.0, lineAngle),
897
                                    lineAngle);
898
}
899

900
//calculate the end points of 1 extension line
901
//originPoint - a point on the distance line (end point)
902
//linePoint - point on dimension line that is perpendicular projection of distance line point
903
//            onto dimension line
904
//1 extension line end point is the return value
905
//1 extension line end point is modified parameter startPoint
906
Base::Vector2d QGIViewDimension::computeExtensionLinePoints(const Base::Vector2d& originPoint,
907
                                                            const Base::Vector2d& linePoint,
908
                                                            double hintAngle, double overhangSize,
909
                                                            double gapSize,
910
                                                            Base::Vector2d& startPoint)
911
{
912
    Base::Vector2d direction(linePoint - originPoint);
913
    double rawLength = direction.Length();
914

915
    if (rawLength <= Precision::Confusion()) {
916
        direction = Base::Vector2d::FromPolar(1.0, hintAngle);
917
    }
918
    else {
919
        direction = direction / rawLength;
920
    }
921

922
    if (overhangSize > rawLength - gapSize) {
923
        // The extension line would be smaller than extension line overhang, keep it at least so long
924
        startPoint = linePoint - overhangSize * direction;
925
    }
926
    else {
927
        startPoint = linePoint - (rawLength - gapSize) * direction;
928
    }
929

930
    return linePoint + overhangSize * direction;
931
}
932

933
double QGIViewDimension::computeLineAndLabelAngles(const Base::Vector2d& rotationCenter,
934
                                                   const Base::Vector2d& labelCenter,
935
                                                   double lineLabelDistance, double& lineAngle,
936
                                                   double& labelAngle)
937
{
938
    // By default horizontal line and no label rotation
939
    lineAngle = 0.0;
940
    labelAngle = 0.0;
941

942
    Base::Vector2d rawDirection(labelCenter - rotationCenter);
943
    double rawDistance = rawDirection.Length();
944
    if (rawDistance <= Precision::Confusion()) {// Almost single point, can't tell
945
        return 0.0;
946
    }
947

948
    double rawAngle = rawDirection.Angle();
949
    lineAngle = rawAngle;
950

951
    // If we are too close to the line origin, no further adjustments
952
    if (lineLabelDistance >= rawDistance) {
953
        return 0.0;
954
    }
955

956
    // Rotate the line by angle between the label rectangle center and label bottom side center
957
    double devAngle = getIsoStandardLinePlacement(rawAngle) * asin(lineLabelDistance / rawDistance);
958
    lineAngle = DrawUtil::angleComposition(lineAngle, devAngle);
959

960
    labelAngle = devAngle < 0.0 ? lineAngle : DrawUtil::angleComposition(lineAngle, M_PI);
961

962
    return devAngle;
963
}
964

965
double
966
QGIViewDimension::computeLineStrikeFactor(const Base::BoundBox2d& labelRectangle,
967
                                          const Base::Vector2d& lineOrigin, double lineAngle,
968
                                          const std::vector<std::pair<double, bool>>& drawMarking)
969
{
970
    if (drawMarking.size() < 2) {
971
        return 0.0;
972
    }
973

974
    std::vector<Base::Vector2d> intersectionPoints;
975
    unsigned int startIndex = 0;
976
    unsigned int currentIndex = 1;
977

978
    while (currentIndex < drawMarking.size()) {
979
        if (drawMarking[currentIndex].second != drawMarking[startIndex].second) {
980
            if (drawMarking[startIndex].second) {
981
                double segmentBase = drawMarking[startIndex].first;
982
                double segmentLength = drawMarking[currentIndex].first - segmentBase;
983

984
                DrawUtil::findLineSegmentRectangleIntersections(lineOrigin, lineAngle, segmentBase,
985
                                                                segmentLength, labelRectangle,
986
                                                                intersectionPoints);
987
            }
988

989
            startIndex = currentIndex;
990
        }
991

992
        ++currentIndex;
993
    }
994

995
    return intersectionPoints.size() >= 2 ? 1.0 : 0.0;
996
}
997

998
double
999
QGIViewDimension::computeArcStrikeFactor(const Base::BoundBox2d& labelRectangle,
1000
                                         const Base::Vector2d& arcCenter, double arcRadius,
1001
                                         const std::vector<std::pair<double, bool>>& drawMarking)
1002
{
1003
    if (drawMarking.empty()) {
1004
        return 0.0;
1005
    }
1006

1007
    unsigned int entryIndex = 0;
1008
    while (entryIndex < drawMarking.size() && drawMarking[entryIndex].second) {
1009
        ++entryIndex;
1010
    }
1011

1012
    std::vector<Base::Vector2d> intersectionPoints;
1013

1014
    if (entryIndex >= drawMarking.size()) {
1015
        DrawUtil::findCircleRectangleIntersections(arcCenter, arcRadius, labelRectangle,
1016
                                                   intersectionPoints);
1017
    }
1018
    else {
1019
        unsigned int startIndex = entryIndex;
1020
        unsigned int currentIndex = entryIndex;
1021
        do {
1022
            ++currentIndex;
1023
            currentIndex %= drawMarking.size();
1024

1025
            if (drawMarking[currentIndex].second != drawMarking[startIndex].second) {
1026
                if (drawMarking[startIndex].second) {
1027
                    double arcAngle = drawMarking[startIndex].first;
1028
                    double arcRotation = drawMarking[currentIndex].first - arcAngle;
1029
                    if (arcRotation < 0.0) {
1030
                        arcRotation += M_2PI;
1031
                    }
1032

1033
                    DrawUtil::findCircularArcRectangleIntersections(arcCenter, arcRadius, arcAngle,
1034
                                                                    arcRotation, labelRectangle,
1035
                                                                    intersectionPoints);
1036
                }
1037

1038
                startIndex = currentIndex;
1039
            }
1040
        } while (currentIndex != entryIndex);
1041
    }
1042

1043
    return intersectionPoints.size() >= 2 ? 1.0 : 0.0;
1044
}
1045

1046
double QGIViewDimension::normalizeStartPosition(double& startPosition, double& lineAngle)
1047
{
1048
    if (startPosition > 0.0) {
1049
        startPosition = -startPosition;
1050
        lineAngle += M_PI;
1051
        return -1.0;
1052
    }
1053

1054
    return +1.0;
1055
}
1056

1057
double QGIViewDimension::normalizeStartRotation(double& startRotation)
1058
{
1059
    if (copysign(1.0, startRotation) > 0.0) {
1060
        startRotation = -startRotation;
1061
        return -1.0;
1062
    }
1063

1064
    return 1.0;
1065
}
1066

1067
bool QGIViewDimension::constructDimensionLine(
1068
    const Base::Vector2d& targetPoint, double lineAngle, double startPosition, double jointPosition,
1069
    const Base::BoundBox2d& labelRectangle, int arrowCount, int standardStyle, bool flipArrows,
1070
    std::vector<std::pair<double, bool>>& outputMarking) const
1071
{
1072
    // The start position > 0 is not expected, the caller must handle this
1073
    if (startPosition > 0.0) {
1074
        Base::Console().Error(
1075
            "QGIVD::constructDimLine - Start Position must not be positive! Received: %f\n",
1076
            startPosition);
1077
        return false;
1078
    }
1079

1080
    double labelBorder = 0.0;
1081
    if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
1082
        labelBorder = labelRectangle.Width() * 0.5 + getDefaultIsoReferenceLineOverhang();
1083
    }
1084
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1085
        std::vector<Base::Vector2d> intersectionPoints;
1086
        DrawUtil::findLineRectangleIntersections(targetPoint, lineAngle, labelRectangle,
1087
                                                 intersectionPoints);
1088

1089
        if (intersectionPoints.size() >= 2) {
1090
            labelBorder = (intersectionPoints[0] - labelRectangle.GetCenter()).Length();
1091
        }
1092
    }
1093

1094
    bool autoFlipArrows = false;
1095
    if (jointPosition + labelBorder > 0.0) {
1096
        // If label sticks out, extend the dimension line beyond the end point (0.0)
1097
        DrawUtil::intervalMarkLinear(outputMarking, 0.0, jointPosition + labelBorder, true);
1098
        autoFlipArrows = true;
1099
    }
1100

1101
    if (jointPosition - labelBorder < startPosition) {
1102
        DrawUtil::intervalMarkLinear(outputMarking, startPosition,
1103
                                     jointPosition - labelBorder - startPosition, true);
1104

1105
        // For only one arrow and zero width line skip flipping, it already points correctly
1106
        if (arrowCount > 1 || startPosition < 0.0) {
1107
            autoFlipArrows = true;
1108
        }
1109
    }
1110

1111
    flipArrows ^= autoFlipArrows;
1112
    if (!flipArrows
1113
        || (standardStyle != ViewProviderDimension::STD_STYLE_ASME_INLINED
1114
            && standardStyle != ViewProviderDimension::STD_STYLE_ASME_REFERENCING)) {
1115
        // If arrows point outside, or ASME standard is not followed,
1116
        // add the line part between start and end
1117
        DrawUtil::intervalMarkLinear(outputMarking, 0.0, startPosition, true);
1118
    }
1119

1120
    // For ASME Inlined, cut out the part of line occupied by the value
1121
    if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1122
        DrawUtil::intervalMarkLinear(outputMarking, jointPosition - labelBorder, labelBorder * 2.0,
1123
                                     false);
1124
    }
1125

1126
    // Add the arrow tails - these are drawn always
1127
    double placementFactor = flipArrows ? +1.0 : -1.0;
1128
    DrawUtil::intervalMarkLinear(outputMarking, 0.0, placementFactor * getDefaultArrowTailLength(),
1129
                                 true);
1130
    if (arrowCount > 1) {
1131
        DrawUtil::intervalMarkLinear(outputMarking, startPosition,
1132
                                     -placementFactor * getDefaultArrowTailLength(), true);
1133
    }
1134

1135
    return flipArrows;
1136
}
1137

1138
bool QGIViewDimension::constructDimensionArc(
1139
    const Base::Vector2d& arcCenter, double arcRadius, double endAngle, double startRotation,
1140
    double handednessFactor, double jointRotation, const Base::BoundBox2d& labelRectangle,
1141
    int arrowCount, int standardStyle, bool flipArrows,
1142
    std::vector<std::pair<double, bool>>& outputMarking) const
1143
{
1144
    // The start rotation > 0 is not expected, the caller must handle this
1145
    if (startRotation > 0.0) {
1146
        Base::Console().Error(
1147
            "QGIVD::constructDimArc - Start Rotation must not be positive! Received: %f\n",
1148
            startRotation);
1149
        return false;
1150
    }
1151

1152
    double startDelta = 0.0;
1153
    double endDelta = 0.0;
1154
    if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
1155
        double borderRadius = (labelRectangle.GetCenter() - arcCenter).Length();
1156

1157
        if (borderRadius > arcRadius) {
1158
            borderRadius = arcRadius + getIsoDimensionLineSpacing();
1159
        }
1160
        else if (borderRadius < arcRadius) {
1161
            borderRadius = arcRadius - getIsoDimensionLineSpacing();
1162
        }
1163

1164
        // ISO oriented labels are symmetrical along their center axis
1165
        startDelta = atan((labelRectangle.Width() * 0.5 + getDefaultIsoReferenceLineOverhang())
1166
                          / borderRadius);
1167
        endDelta = startDelta;
1168
    }
1169
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1170
        std::vector<Base::Vector2d> intersectionPoints;
1171

1172
        DrawUtil::findCircleRectangleIntersections(arcCenter, arcRadius, labelRectangle,
1173
                                                   intersectionPoints);
1174

1175
        // We do not want to handle other cases than 2 intersection points - if so, act as if there were none
1176
        if (intersectionPoints.size() == 2) {
1177
            double zeroAngle = (labelRectangle.GetCenter() - arcCenter).Angle();
1178

1179
            startDelta =
1180
                DrawUtil::angleDifference(zeroAngle, (intersectionPoints[0] - arcCenter).Angle());
1181
            endDelta =
1182
                DrawUtil::angleDifference(zeroAngle, (intersectionPoints[1] - arcCenter).Angle());
1183

1184
            // End delta is the angle in the end point direction, start delta in the opposite
1185
            // Keep orientation and the computation sign in sync
1186
            if ((endDelta < 0.0) == (handednessFactor < 0.0)) {
1187
                std::swap(startDelta, endDelta);
1188
            }
1189

1190
            startDelta = fabs(startDelta);
1191
            endDelta = fabs(endDelta);
1192
        }
1193
    }
1194

1195
    bool autoFlipArrows = false;
1196
    if (jointRotation + endDelta > 0.0) {
1197
        // If label exceeds end angle ray, extend the dimension arc and flip arrows
1198
        DrawUtil::intervalMarkCircular(outputMarking, endAngle,
1199
                                       handednessFactor * (jointRotation + endDelta), true);
1200
        autoFlipArrows = true;
1201
    }
1202

1203
    if (jointRotation - startDelta < startRotation) {
1204
        DrawUtil::intervalMarkCircular(
1205
            outputMarking, endAngle + handednessFactor * startRotation,
1206
            handednessFactor * (jointRotation - startDelta - startRotation), true);
1207

1208
        // For only one arrow and zero width line skip flipping, it already points correctly
1209
        if (arrowCount > 1 || startRotation < 0.0) {
1210
            autoFlipArrows = true;
1211
        }
1212
    }
1213

1214
    flipArrows ^= autoFlipArrows;
1215
    if (!flipArrows
1216
        || (standardStyle != ViewProviderDimension::STD_STYLE_ASME_INLINED
1217
            && standardStyle != ViewProviderDimension::STD_STYLE_ASME_REFERENCING)) {
1218
        // If arrows point outside, or ASME standard is not followed,
1219
        // add the arc part between start and end
1220
        DrawUtil::intervalMarkCircular(outputMarking, endAngle, handednessFactor * startRotation,
1221
                                       true);
1222
    }
1223

1224
    // For ASME Inlined, cut out the part of arc occupied by the value
1225
    if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1226
        DrawUtil::intervalMarkCircular(outputMarking,
1227
                                       endAngle + handednessFactor * (jointRotation - startDelta),
1228
                                       handednessFactor * (startDelta + endDelta), false);
1229
    }
1230

1231
    // Add the arrow tails - these are drawn always
1232
    double tailDelta =
1233
        arcRadius >= Precision::Confusion() ? getDefaultArrowTailLength() / arcRadius : M_PI_4;
1234
    double placementFactor = flipArrows ? +1.0 : -1.0;
1235

1236
    DrawUtil::intervalMarkCircular(outputMarking, endAngle,
1237
                                   placementFactor * handednessFactor * tailDelta, true);
1238
    if (arrowCount > 1) {
1239
        DrawUtil::intervalMarkCircular(outputMarking, endAngle + handednessFactor * startRotation,
1240
                                       -placementFactor * handednessFactor * tailDelta, true);
1241
    }
1242

1243
    return flipArrows;
1244
}
1245

1246
void QGIViewDimension::resetArrows() const
1247
{
1248
    aHead1->setDirMode(true);
1249
    aHead1->setRotation(0.0);
1250
    aHead1->setFlipped(false);
1251

1252
    aHead2->setDirMode(true);
1253
    aHead2->setRotation(0.0);
1254
    aHead2->setFlipped(false);
1255
}
1256

1257
void QGIViewDimension::drawArrows(int count, const Base::Vector2d positions[], double angles[],
1258
                                  bool flipped) const
1259
{
1260
    const int arrowCount = 2;
1261
    QGIArrow* arrows[arrowCount] = {aHead1, aHead2};
1262

1263
    arrowPositionsToFeature(positions);
1264

1265
    for (int i = 0; i < arrowCount; ++i) {
1266
        QGIArrow* arrow = arrows[i];
1267

1268
        if (positions && angles) {
1269
            arrow->setPos(toQtGui(positions[i]));
1270
            arrow->setDirection(toQtRad(angles[i]));
1271
        }
1272

1273
        if (i >= count) {
1274
            arrow->hide();
1275
            continue;
1276
        }
1277

1278
        arrow->setStyle(QGIArrow::getPrefArrowStyle());
1279
        auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
1280
        auto arrowSize = vp->Arrowsize.getValue();
1281
        arrow->setSize(arrowSize);
1282
        arrow->setFlipped(flipped);
1283

1284
        if (QGIArrow::getPrefArrowStyle() != 7) {// if not "None"
1285
            arrow->draw();
1286
            arrow->show();
1287
        }
1288
        else {
1289
            arrow->hide();
1290
        }
1291
    }
1292
}
1293

1294
void QGIViewDimension::arrowPositionsToFeature(const Base::Vector2d positions[]) const
1295
{
1296
    auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject()));
1297
    if (!dim) {
1298
        return;
1299
    }
1300

1301
    dim->saveArrowPositions(positions);
1302
}
1303

1304
void QGIViewDimension::drawSingleLine(QPainterPath& painterPath, const Base::Vector2d& lineOrigin,
1305
                                      double lineAngle, double startPosition,
1306
                                      double endPosition) const
1307
{
1308
    if (endPosition == startPosition) {
1309
        return;
1310
    }
1311

1312
    painterPath.moveTo(toQtGui(lineOrigin + Base::Vector2d::FromPolar(startPosition, lineAngle)));
1313
    painterPath.lineTo(toQtGui(lineOrigin + Base::Vector2d::FromPolar(endPosition, lineAngle)));
1314
}
1315

1316

1317
//adds line segments to painterPath from lineOrigin along lineAngle
1318
//segment length is determined by drawMarking entries
1319
void QGIViewDimension::drawMultiLine(QPainterPath& painterPath, const Base::Vector2d& lineOrigin,
1320
                                     double lineAngle,
1321
                                     const std::vector<std::pair<double, bool>>& drawMarking) const
1322
{
1323
    if (drawMarking.size() < 2) {
1324
        return;
1325
    }
1326

1327
    unsigned int startIndex = 0;
1328
    unsigned int currentIndex = 1;
1329
    while (currentIndex < drawMarking.size()) {
1330
        if (drawMarking[currentIndex].second != drawMarking[startIndex].second) {
1331
            if (drawMarking[startIndex].second) {
1332
                drawSingleLine(painterPath, lineOrigin, lineAngle, drawMarking[startIndex].first,
1333
                               drawMarking[currentIndex].first);
1334
            }
1335

1336
            startIndex = currentIndex;
1337
        }
1338

1339
        ++currentIndex;
1340
    }
1341
}
1342

1343
void QGIViewDimension::drawSingleArc(QPainterPath& painterPath, const Base::Vector2d& arcCenter,
1344
                                     double arcRadius, double startAngle, double endAngle) const
1345
{
1346
    if (endAngle == startAngle) {
1347
        return;
1348
    }
1349
    if (endAngle < startAngle) {
1350
        endAngle += M_2PI;
1351
    }
1352

1353
    QRectF qtArcRectangle(
1354
        toQtGui(Base::BoundBox2d(arcCenter.x - arcRadius, arcCenter.y - arcRadius,
1355
                                 arcCenter.x + arcRadius, arcCenter.y + arcRadius)));
1356

1357
    // In arc drawing are for some reason Qt's angles counterclockwise as in our computations...
1358
    painterPath.arcMoveTo(qtArcRectangle, toDeg(startAngle));
1359
    painterPath.arcTo(qtArcRectangle, toDeg(startAngle), toDeg(endAngle - startAngle));
1360
}
1361

1362
void QGIViewDimension::drawMultiArc(QPainterPath& painterPath, const Base::Vector2d& arcCenter,
1363
                                    double arcRadius,
1364
                                    const std::vector<std::pair<double, bool>>& drawMarking) const
1365
{
1366
    if (drawMarking.empty()) {
1367
        return;
1368
    }
1369

1370
    unsigned int entryIndex = 0;
1371
    while (entryIndex < drawMarking.size() && drawMarking[entryIndex].second) {
1372
        ++entryIndex;
1373
    }
1374

1375
    if (entryIndex >= drawMarking.size()) {
1376
        drawSingleArc(painterPath, arcCenter, arcRadius, 0, M_2PI);
1377
        return;
1378
    }
1379

1380
    unsigned int startIndex = entryIndex;
1381
    unsigned int currentIndex = entryIndex;
1382
    do {
1383
        ++currentIndex;
1384
        currentIndex %= drawMarking.size();
1385

1386
        if (drawMarking[currentIndex].second != drawMarking[startIndex].second) {
1387
            if (drawMarking[startIndex].second) {
1388
                drawSingleArc(painterPath, arcCenter, arcRadius, drawMarking[startIndex].first,
1389
                              drawMarking[currentIndex].first);
1390
            }
1391

1392
            startIndex = currentIndex;
1393
        }
1394
    } while (currentIndex != entryIndex);
1395
}
1396

1397

1398
//adds dimension line to painterPath
1399
//dimension line starts at targetPoint and continues for a distance (startPosition?) along lineAngle
1400
//jointPosition - distance of reference line from 1 extension line??
1401
//lineAngle - Clockwise angle of distance line with horizontal
1402
void QGIViewDimension::drawDimensionLine(QPainterPath& painterPath,
1403
                                         const Base::Vector2d& targetPoint, double lineAngle,
1404
                                         double startPosition, double jointPosition,
1405
                                         const Base::BoundBox2d& labelRectangle, int arrowCount,
1406
                                         int standardStyle, bool flipArrows) const
1407
{
1408
    // Keep the convention start position <= 0
1409
    jointPosition *= normalizeStartPosition(startPosition, lineAngle);
1410

1411
    std::vector<std::pair<double, bool>> drawMarks;
1412
    flipArrows =
1413
        constructDimensionLine(targetPoint, lineAngle, startPosition, jointPosition, labelRectangle,
1414
                               arrowCount, standardStyle, flipArrows, drawMarks);
1415

1416
    drawMultiLine(painterPath, targetPoint, lineAngle, drawMarks);
1417

1418
    Base::Vector2d arrowPositions[2];
1419
    arrowPositions[0] = targetPoint;
1420
    arrowPositions[1] = targetPoint + Base::Vector2d::FromPolar(startPosition, lineAngle);
1421

1422
    double arrowAngles[2];
1423
    arrowAngles[0] = lineAngle;
1424
    arrowAngles[1] = lineAngle + M_PI;
1425

1426
    drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows);
1427
}
1428

1429
void QGIViewDimension::drawDimensionArc(QPainterPath& painterPath, const Base::Vector2d& arcCenter,
1430
                                        double arcRadius, double endAngle, double startRotation,
1431
                                        double jointAngle, const Base::BoundBox2d& labelRectangle,
1432
                                        int arrowCount, int standardStyle, bool flipArrows) const
1433
{
1434
    // Keep the convention start rotation <= 0
1435
    double handednessFactor = normalizeStartRotation(startRotation);
1436

1437
    // Split the rest of 2PI minus the angle and assign joint offset so > 0 is closer to end arc side
1438
    double jointRotation = handednessFactor * (jointAngle - endAngle);
1439
    if (fabs(jointRotation - startRotation * 0.5) > M_PI) {
1440
        jointRotation += jointRotation < 0.0 ? +M_2PI : -M_2PI;
1441
    }
1442

1443
    std::vector<std::pair<double, bool>> drawMarks;
1444
    flipArrows = constructDimensionArc(arcCenter, arcRadius, endAngle, startRotation,
1445
                                       handednessFactor, jointRotation, labelRectangle, arrowCount,
1446
                                       standardStyle, flipArrows, drawMarks);
1447

1448
    drawMultiArc(painterPath, arcCenter, arcRadius, drawMarks);
1449

1450
    Base::Vector2d arrowPositions[2];
1451
    arrowPositions[0] = arcCenter + Base::Vector2d::FromPolar(arcRadius, endAngle);
1452
    arrowPositions[1] = arcCenter
1453
        + Base::Vector2d::FromPolar(arcRadius, endAngle + handednessFactor * startRotation);
1454

1455
    double arrowAngles[2];
1456
    arrowAngles[0] = endAngle + handednessFactor * M_PI_2;
1457
    arrowAngles[1] = endAngle + handednessFactor * (startRotation - M_PI_2);
1458

1459
    drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows);
1460
}
1461

1462
//draw any of 3 distance dimension types
1463
//startPoint, endPoint - ends of actual distance line
1464
//lineAngle - angle of actual line with horizontal
1465
//target points - projection of reference line ends on to extension line
1466
//startCross & endCross - real intersection of extension lines and dimension line
1467
//dimension line - main annotation line
1468
//reference line - line under dimension text in referenced styles
1469
//joint points - ends of reference line
1470
void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d& startPoint,
1471
                                             const Base::Vector2d& endPoint, double lineAngle,
1472
                                             const Base::BoundBox2d& labelRectangle,
1473
                                             int standardStyle, int renderExtent,
1474
                                             bool flipArrows) const
1475
{
1476
    QPainterPath distancePath;
1477

1478
    Base::Vector2d labelCenter(labelRectangle.GetCenter());
1479
    double labelAngle = 0.0;
1480

1481
    //startCross and endCross are points where extension line intersects dimension line
1482
    Base::Vector2d startCross;
1483
    Base::Vector2d endCross;
1484
    int arrowCount = renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL
1485
            || renderExtent == ViewProviderDimension::REND_EXTENT_CONFINED
1486
        ? 2
1487
        : 1;
1488

1489
    if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1490
        || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) {
1491
        // The dimensional value text must stay horizontal in these styles
1492

1493
        //jointPoints are the ends of the reference line
1494
        Base::Vector2d jointPoints[2];
1495

1496
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) {
1497
            jointPoints[0] = getIsoRefJointPoint(labelRectangle, false);
1498
            jointPoints[1] = getIsoRefJointPoint(labelRectangle, true);
1499
        }
1500
        else {
1501
            jointPoints[0] = getAsmeRefJointPoint(labelRectangle, false);
1502
            jointPoints[1] = getAsmeRefJointPoint(labelRectangle, true);
1503
        }
1504

1505
        //targetPoints are the projection of reference line endpoints onto endPoint's extension line
1506
        Base::Vector2d targetPoints[2];
1507
        targetPoints[0] = computePerpendicularIntersection(jointPoints[0], endPoint, lineAngle);
1508
        targetPoints[1] = computePerpendicularIntersection(jointPoints[1], endPoint, lineAngle);
1509

1510
        // Compute and normalize (i.e. make < 0) the start position
1511
        Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
1512
        double startPosition =
1513
            arrowCount > 1 ? lineDirection * (startPoint - targetPoints[0]) : 0.0;
1514
        lineDirection *= normalizeStartPosition(startPosition, lineAngle);
1515

1516
        // Find the positions where the reference line attaches to the dimension line
1517
        //jointPoints are the ends of the reference line
1518
        //targetPoints - projection of reference line on to extension line
1519
        //jointPositions - displacement of jointPoints from ext line
1520
        double jointPositions[2];
1521
        jointPositions[0] = lineDirection * (jointPoints[0] - targetPoints[0]);
1522
        jointPositions[1] = lineDirection * (jointPoints[1] - targetPoints[1]);
1523

1524
        // Orient the leader line angle correctly towards the target point
1525
        double angles[2];
1526
        angles[0] =
1527
            jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle;
1528
        angles[1] =
1529
            jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle;
1530

1531
        // Select the placement, where the label is not obscured by the leader line
1532
        // or (if both behave the same) the one that  bends the reference line less
1533
        double strikeFactors[2];
1534

1535
        std::vector<std::pair<double, bool>> lineMarking;
1536
        constructDimensionLine(targetPoints[0], lineAngle, startPosition, jointPositions[0],
1537
                               labelRectangle, arrowCount, standardStyle, flipArrows, lineMarking);
1538
        strikeFactors[0] =
1539
            computeLineStrikeFactor(labelRectangle, targetPoints[0], lineAngle, lineMarking);
1540

1541
        lineMarking.clear();
1542
        constructDimensionLine(targetPoints[1], lineAngle, startPosition, jointPositions[1],
1543
                               labelRectangle, arrowCount, standardStyle, flipArrows, lineMarking);
1544
        strikeFactors[1] =
1545
            computeLineStrikeFactor(labelRectangle, targetPoints[1], lineAngle, lineMarking);
1546

1547
        int selected =
1548
            compareAngleStraightness(0.0, angles[0], angles[1], strikeFactors[0], strikeFactors[1]);
1549
        if (selected == 0) {
1550
            // Select the side closer, so the label is on the outer side of the dimension line
1551
            Base::Vector2d perpendicularDir(lineDirection.Perpendicular());
1552
            if (fabs((jointPoints[0] - endPoint) * perpendicularDir)
1553
                > fabs((jointPoints[1] - endPoint) * perpendicularDir)) {
1554
                selected = 1;
1555
            }
1556
        }
1557
        else if (selected < 0) {
1558
            selected = 0;
1559
        }
1560

1561
        //find points where extension lines meet dimension line
1562
        endCross = targetPoints[selected];
1563
        startCross = targetPoints[selected] + Base::Vector2d::FromPolar(startPosition, lineAngle);
1564

1565
        drawDimensionLine(distancePath, endCross, lineAngle, startPosition,
1566
                          jointPositions[selected], labelRectangle, arrowCount, standardStyle,
1567
                          flipArrows);
1568

1569
        Base::Vector2d outsetPoint(
1570
            standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1571
                ? getIsoRefOutsetPoint(labelRectangle, selected == 1)// 0 = left, 1 = right
1572
                : getAsmeRefOutsetPoint(labelRectangle, selected == 1));
1573

1574
        //add the reference line to the QPainterPath
1575
        distancePath.moveTo(toQtGui(outsetPoint));
1576
        distancePath.lineTo(toQtGui(jointPoints[selected]));
1577
    }
1578

1579
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
1580
        // We may rotate the label so no leader and reference lines are needed
1581
        double placementFactor = getIsoStandardLinePlacement(lineAngle);
1582
        labelAngle =
1583
            placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle;
1584

1585
        // Find out the projection of label center on the line with given angle
1586
        Base::Vector2d labelProjection(
1587
            labelCenter
1588
            + Base::Vector2d::FromPolar(
1589
                placementFactor
1590
                    * (labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing()),
1591
                lineAngle + M_PI_2));
1592

1593
        // Compute the dimensional line start and end crossings with (virtual) extension lines
1594
        //check for isometric direction and if iso compute non-perpendicular intersection of dim line and ext lines
1595
        Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
1596
        startCross = computePerpendicularIntersection(labelProjection, startPoint, lineAngle);
1597
        endCross = computePerpendicularIntersection(labelProjection, endPoint, lineAngle);
1598

1599
        // Find linear coefficients of crossings
1600
        double startPosition = arrowCount > 1 ? lineDirection * (startCross - endCross) : 0.0;
1601
        double labelPosition = lineDirection * (labelProjection - endCross);
1602

1603
        drawDimensionLine(distancePath, endCross, lineAngle, startPosition, labelPosition,
1604
                          labelRectangle, arrowCount, standardStyle, flipArrows);
1605
    }
1606

1607
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1608
        // Text must remain horizontal, but it may split the leader line
1609
        Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
1610
        startCross = computePerpendicularIntersection(labelCenter, startPoint, lineAngle);
1611
        endCross = computePerpendicularIntersection(labelCenter, endPoint, lineAngle);
1612

1613
        // Find linear coefficients of crossings
1614
        double startPosition = arrowCount > 1 ? lineDirection * (startCross - endCross) : 0.0;
1615
        double labelPosition = lineDirection * (labelCenter - endCross);
1616

1617
        drawDimensionLine(distancePath, endCross, lineAngle, startPosition, labelPosition,
1618
                          labelRectangle, arrowCount, standardStyle, flipArrows);
1619
    }
1620
    else {
1621
        Base::Console().Error(
1622
            "QGIVD::drawDistanceExecutive - this Standard&Style is not supported: %d\n",
1623
            standardStyle);
1624
        arrowCount = 0;
1625
    }
1626

1627
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
1628
    assert(vp);
1629

1630
    if (arrowCount > 0 && renderExtent >= ViewProviderDimension::REND_EXTENT_REDUCED) {
1631
        double gapSize = 0.0;
1632
        if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING
1633
            || standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1634
            double factor = vp->GapFactorASME.getValue();
1635
            gapSize = Rez::appX(m_lineWidth * factor);
1636
        }
1637
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1638
            || standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
1639
            double factor = vp->GapFactorISO.getValue();
1640
            gapSize = Rez::appX(m_lineWidth * factor);
1641
        }
1642

1643
        Base::Vector2d extensionOrigin;
1644
        Base::Vector2d extensionTarget(computeExtensionLinePoints(
1645
            endPoint, endCross, lineAngle + M_PI_2, getDefaultExtensionLineOverhang(), gapSize,
1646
            extensionOrigin));
1647
        //draw 1st extension line
1648
        distancePath.moveTo(toQtGui(extensionOrigin));
1649
        distancePath.lineTo(toQtGui(extensionTarget));
1650

1651
        if (arrowCount > 1) {
1652
            extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + M_PI_2,
1653
                                                         getDefaultExtensionLineOverhang(), gapSize,
1654
                                                         extensionOrigin);
1655
            //draw second extension line
1656
            distancePath.moveTo(toQtGui(extensionOrigin));
1657
            distancePath.lineTo(toQtGui(extensionTarget));
1658
        }
1659
    }
1660

1661
    datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center());
1662
    datumLabel->setRotation(toQtDeg(labelAngle));
1663

1664
    dimLines->setPath(distancePath);
1665
}
1666

1667
//draw any of 3 distance dimension types with user override of dimension and extension line directions
1668
//startPoint, endPoint - ends of actual distance line
1669
//lineAngle - desired angle of dimension line with horizontal
1670
//extensionAngle - desired angle of extension lines with horizontal
1671
void QGIViewDimension::drawDistanceOverride(const Base::Vector2d& startPoint,
1672
                                            const Base::Vector2d& endPoint, double lineAngle,
1673
                                            const Base::BoundBox2d& labelRectangle,
1674
                                            int standardStyle, int renderExtent, bool flipArrows,
1675
                                            double extensionAngle) const
1676
{
1677
    QPainterPath distancePath;
1678

1679
    Base::Vector2d labelCenter(labelRectangle.GetCenter());
1680
    double labelAngle = 0.0;
1681

1682
    //startCross and endCross are points where extension lines intersect dimension line
1683
    Base::Vector2d startCross;
1684
    Base::Vector2d endCross;
1685
    Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
1686
    Base::Vector2d extensionDirection(Base::Vector2d::FromPolar(1.0, extensionAngle));
1687

1688
    int arrowCount = renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL
1689
            || renderExtent == ViewProviderDimension::REND_EXTENT_CONFINED
1690
        ? 2
1691
        : 1;
1692

1693
    if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1694
        || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) {
1695
        // The dimensional value text must stay horizontal in these styles
1696

1697
        //refEndPoints are the ends of the reference line
1698
        Base::Vector2d refEndPoints[2];
1699

1700
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) {
1701
            refEndPoints[0] = getIsoRefJointPoint(labelRectangle, false);
1702
            refEndPoints[1] = getIsoRefJointPoint(labelRectangle, true);
1703
        }
1704
        else {
1705
            refEndPoints[0] = getAsmeRefJointPoint(labelRectangle, false);
1706
            refEndPoints[1] = getAsmeRefJointPoint(labelRectangle, true);
1707
        }
1708

1709
        //targetPoints are the projection of reference line endpoints onto endPoint's extension line
1710
        Base::Vector2d targetPoints[2];
1711
        targetPoints[0] =
1712
            DrawUtil::Intersect2d(refEndPoints[0], lineDirection, endPoint, extensionDirection);
1713
        targetPoints[1] =
1714
            DrawUtil::Intersect2d(refEndPoints[1], lineDirection, endPoint, extensionDirection);
1715
        Base::Vector2d pointOnStartExtension =
1716
            DrawUtil::Intersect2d(endPoint, lineDirection, startPoint, extensionDirection);
1717
        double startPosition =
1718
            arrowCount > 1 ? lineDirection * (pointOnStartExtension - endPoint) : 0.0;
1719

1720
        // Compute and normalize (i.e. make < 0) the start position
1721
        lineDirection *= normalizeStartPosition(startPosition, lineAngle);
1722

1723
        // Find the positions where the reference line attaches to the dimension line
1724
        //refEndPoints are the ends of the reference line
1725
        //targetPoints - projection of reference line on to extension line
1726
        //jointPositions - displacement of refEndPoints from extension line
1727
        double jointPositions[2];
1728
        jointPositions[0] = lineDirection * (refEndPoints[0] - targetPoints[0]);
1729
        jointPositions[1] = lineDirection * (refEndPoints[1] - targetPoints[1]);
1730

1731
        // Orient the leader line angle correctly towards the target point
1732
        double angles[2];
1733
        angles[0] =
1734
            jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle;
1735
        angles[1] =
1736
            jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle;
1737

1738
        // Select the placement, where the label is not obscured by the leader line
1739
        // or (if both behave the same) the one that  bends the reference line less
1740
        double strikeFactors[2];
1741

1742
        std::vector<std::pair<double, bool>> lineMarking;
1743
        constructDimensionLine(targetPoints[0], lineAngle, startPosition, jointPositions[0],
1744
                               labelRectangle, arrowCount, standardStyle, flipArrows, lineMarking);
1745
        strikeFactors[0] =
1746
            computeLineStrikeFactor(labelRectangle, targetPoints[0], lineAngle, lineMarking);
1747

1748
        lineMarking.clear();
1749
        constructDimensionLine(targetPoints[1], lineAngle, startPosition, jointPositions[1],
1750
                               labelRectangle, arrowCount, standardStyle, flipArrows, lineMarking);
1751
        strikeFactors[1] =
1752
            computeLineStrikeFactor(labelRectangle, targetPoints[1], lineAngle, lineMarking);
1753

1754
        int selected =
1755
            compareAngleStraightness(0.0, angles[0], angles[1], strikeFactors[0], strikeFactors[1]);
1756
        if (selected == 0) {
1757
            // Select the side closer, so the label is on the outer side of the dimension line
1758
            Base::Vector2d perpendicularDir(lineDirection.Perpendicular());
1759
            if (fabs((refEndPoints[0] - endPoint) * perpendicularDir)
1760
                > fabs((refEndPoints[1] - endPoint) * perpendicularDir)) {
1761
                selected = 1;
1762
            }
1763
        }
1764
        else if (selected < 0) {
1765
            selected = 0;
1766
        }
1767

1768
        //find points where extension lines meet dimension line
1769
        Base::Vector2d pointOnDimLine(refEndPoints[selected].x, refEndPoints[selected].y);
1770
        startCross =
1771
            DrawUtil::Intersect2d(startPoint, extensionDirection, pointOnDimLine, lineDirection);
1772
        endCross =
1773
            DrawUtil::Intersect2d(endPoint, extensionDirection, pointOnDimLine, lineDirection);
1774

1775
        drawDimensionLine(distancePath, endCross, lineAngle, startPosition,
1776
                          jointPositions[selected], labelRectangle, arrowCount, standardStyle,
1777
                          flipArrows);
1778

1779
        Base::Vector2d outsetPoint(
1780
            standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1781
                ? getIsoRefOutsetPoint(labelRectangle, selected == 1)// 0 = left, 1 = right
1782
                : getAsmeRefOutsetPoint(labelRectangle, selected == 1));
1783

1784
        //add the reference line to the QPainterPath
1785
        distancePath.moveTo(toQtGui(outsetPoint));
1786
        distancePath.lineTo(toQtGui(refEndPoints[selected]));
1787
    }
1788

1789
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
1790
        // We may rotate the label so no leader and reference lines are needed
1791
        double placementFactor = getIsoStandardLinePlacement(lineAngle);
1792
        labelAngle =
1793
            placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle;
1794

1795
        // Find out the projection of label center on the line with given angle
1796
        Base::Vector2d labelProjection(
1797
            labelCenter
1798
            + Base::Vector2d::FromPolar(
1799
                placementFactor
1800
                    * (labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing()),
1801
                lineAngle + M_PI_2));
1802

1803
        // Compute the dimensional line start and end crossings with (virtual) extension lines
1804
        startCross =
1805
            DrawUtil::Intersect2d(startPoint, extensionDirection, labelProjection, lineDirection);
1806
        endCross =
1807
            DrawUtil::Intersect2d(endPoint, extensionDirection, labelProjection, lineDirection);
1808

1809
        // Find linear coefficients of crossings
1810
        double startPosition = arrowCount > 1 ? lineDirection * (startCross - endCross) : 0.0;
1811
        double labelPosition = lineDirection * (labelProjection - endCross);
1812

1813
        drawDimensionLine(distancePath, endCross, lineAngle, startPosition, labelPosition,
1814
                          labelRectangle, arrowCount, standardStyle, flipArrows);
1815
    }
1816

1817
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1818
        // Text must remain horizontal, but it may split the leader line
1819
        startCross =
1820
            DrawUtil::Intersect2d(startPoint, extensionDirection, labelCenter, lineDirection);
1821
        endCross = DrawUtil::Intersect2d(endPoint, extensionDirection, labelCenter, lineDirection);
1822

1823
        // Find linear coefficients of crossings
1824
        double startPosition = arrowCount > 1 ? lineDirection * (startCross - endCross) : 0.0;
1825
        double labelPosition = lineDirection * (labelCenter - endCross);
1826

1827
        drawDimensionLine(distancePath, endCross, lineAngle, startPosition, labelPosition,
1828
                          labelRectangle, arrowCount, standardStyle, flipArrows);
1829
    }
1830
    else {
1831
        Base::Console().Error(
1832
            "QGIVD::drawDistanceExecutive - this Standard&Style is not supported: %d\n",
1833
            standardStyle);
1834
        arrowCount = 0;
1835
    }
1836

1837
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
1838
    assert(vp);
1839

1840
    if (arrowCount > 0 && renderExtent >= ViewProviderDimension::REND_EXTENT_REDUCED) {
1841
        double gapSize = 0.0;
1842
        if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING
1843
            || standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
1844
            double factor = vp->GapFactorASME.getValue();
1845
            gapSize = Rez::appX(m_lineWidth * factor);
1846
        }
1847
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1848
            || standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
1849
            double factor = vp->GapFactorISO.getValue();
1850
            gapSize = Rez::appX(m_lineWidth * factor);
1851
        }
1852

1853

1854
        Base::Vector2d extensionOrigin;
1855
        Base::Vector2d extensionTarget(computeExtensionLinePoints(
1856
            endPoint, endCross, lineAngle + M_PI_2, getDefaultExtensionLineOverhang(), gapSize,
1857
            extensionOrigin));
1858
        //draw 1st extension line
1859
        distancePath.moveTo(toQtGui(extensionOrigin));
1860
        distancePath.lineTo(toQtGui(extensionTarget));
1861

1862
        if (arrowCount > 1) {
1863
            extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + M_PI_2,
1864
                                                         getDefaultExtensionLineOverhang(), gapSize,
1865
                                                         extensionOrigin);
1866
            //draw second extension line
1867
            distancePath.moveTo(toQtGui(extensionOrigin));
1868
            distancePath.lineTo(toQtGui(extensionTarget));
1869
        }
1870
    }
1871

1872
    datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center());
1873
    datumLabel->setRotation(toQtDeg(labelAngle));
1874

1875
    dimLines->setPath(distancePath);
1876
}
1877

1878
void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint,
1879
                                           const Base::Vector2d& midPoint, double radius,
1880
                                           double endAngle, double startRotation,
1881
                                           const Base::BoundBox2d& labelRectangle,
1882
                                           double centerOverhang, int standardStyle,
1883
                                           int renderExtent, bool flipArrow) const
1884
{
1885
    QPainterPath radiusPath;
1886

1887
    Base::Vector2d labelCenter(labelRectangle.GetCenter());
1888
    double labelAngle = 0.0;
1889

1890
    if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1891
        || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) {
1892
        // The dimensional value text must stay horizontal
1893
        Base::Vector2d jointDirections[2];
1894

1895
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) {
1896
            jointDirections[0] = getIsoRefJointPoint(labelRectangle, false) - centerPoint;
1897
            jointDirections[1] = getIsoRefJointPoint(labelRectangle, true) - centerPoint;
1898
        }
1899
        else {
1900
            jointDirections[0] = getAsmeRefJointPoint(labelRectangle, false) - centerPoint;
1901
            jointDirections[1] = getAsmeRefJointPoint(labelRectangle, true) - centerPoint;
1902
        }
1903

1904
        double lineAngles[2];
1905
        lineAngles[0] = jointDirections[0].Angle();
1906
        lineAngles[1] = jointDirections[1].Angle();
1907

1908
        // Are there points on the arc, where line from center intersects it perpendicularly?
1909
        double angleFactors[2];
1910
        angleFactors[0] = getAnglePlacementFactor(lineAngles[0], endAngle, startRotation);
1911
        angleFactors[1] = getAnglePlacementFactor(lineAngles[1], endAngle, startRotation);
1912

1913
        // Orient the leader line angle correctly towards the point on arc
1914
        if (angleFactors[0] < 0.0) {
1915
            lineAngles[0] = DrawUtil::angleComposition(lineAngles[0], M_PI);
1916
        }
1917
        if (angleFactors[1] < 0.0) {
1918
            lineAngles[1] = DrawUtil::angleComposition(lineAngles[1], M_PI);
1919
        }
1920

1921
        // Find the positions where the reference line attaches to the dimension line
1922
        double jointPositions[2];
1923
        jointPositions[0] = angleFactors[0] * jointDirections[0].Length() - radius;
1924
        jointPositions[1] = angleFactors[1] * jointDirections[1].Length() - radius;
1925

1926
        Base::Vector2d targetPoints[2];
1927
        targetPoints[0] = centerPoint + Base::Vector2d::FromPolar(radius, lineAngles[0]);
1928
        targetPoints[1] = centerPoint + Base::Vector2d::FromPolar(radius, lineAngles[1]);
1929

1930
        Base::Vector2d arcPoint;
1931
        int selected = 0;
1932
        if (angleFactors[0] || angleFactors[1]) {
1933
            // At least from one of the reference line sides can run the leader line
1934
            // perpendicularly to the arc, i.e. in direction to the center
1935
            if (angleFactors[0] && angleFactors[1]) {
1936
                // Both are acceptable, so choose the more convenient one
1937
                double strikeFactors[2] = {0.0, 0.0};
1938

1939
                if (renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL) {
1940
                    std::vector<std::pair<double, bool>> lineMarking;
1941
                    constructDimensionLine(targetPoints[0], lineAngles[0], -radius,
1942
                                           jointPositions[0], labelRectangle, 1, standardStyle,
1943
                                           flipArrow, lineMarking);
1944
                    strikeFactors[0] = computeLineStrikeFactor(labelRectangle, targetPoints[0],
1945
                                                               lineAngles[0], lineMarking);
1946

1947
                    lineMarking.clear();
1948
                    constructDimensionLine(targetPoints[1], lineAngles[1], -radius,
1949
                                           jointPositions[1], labelRectangle, 1, standardStyle,
1950
                                           flipArrow, lineMarking);
1951
                    strikeFactors[1] = computeLineStrikeFactor(labelRectangle, targetPoints[1],
1952
                                                               lineAngles[1], lineMarking);
1953
                }
1954

1955
                if (compareAngleStraightness(
1956
                        0.0,
1957
                        jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], M_PI)
1958
                                                : lineAngles[0],
1959
                        jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], M_PI)
1960
                                                : lineAngles[1],
1961
                        strikeFactors[0], strikeFactors[1])
1962
                    > 0) {
1963
                    selected = 1;
1964
                }
1965
            }
1966
            else if (angleFactors[1]) {
1967
                selected = 1;
1968
            }
1969

1970
            arcPoint = targetPoints[selected];
1971
        }
1972
        else {//  Both joint points lay outside the vertical angles
1973
            arcPoint = midPoint;
1974

1975
            if (labelCenter.x < arcPoint.x) {// Place the dimensional value left
1976
                selected = 1;
1977
            }
1978

1979
            Base::Vector2d lineDirection(arcPoint - centerPoint - jointDirections[selected]);
1980
            lineAngles[selected] = lineDirection.Angle();
1981
            jointPositions[selected] = -lineDirection.Length();
1982
        }
1983

1984
        drawDimensionLine(radiusPath, arcPoint, lineAngles[selected],
1985
                          // If not reduced rendering and at least in one arc wedge, draw to center
1986
                          (angleFactors[0] || angleFactors[1])
1987
                                  && renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL
1988
                              ? -radius - centerOverhang
1989
                              : 0.0,
1990
                          jointPositions[selected], labelRectangle, 1, standardStyle, flipArrow);
1991

1992
        Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
1993
                                       ? getIsoRefOutsetPoint(labelRectangle, selected == 1)
1994
                                       : getAsmeRefOutsetPoint(labelRectangle, selected == 1));
1995

1996
        radiusPath.moveTo(toQtGui(outsetPoint));
1997
        radiusPath.lineTo(toQtGui(centerPoint + jointDirections[selected]));
1998
    }
1999
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
2000
        // We may rotate the label so no reference line is needed
2001
        double lineAngle;
2002
        double devAngle = computeLineAndLabelAngles(centerPoint, labelCenter,
2003
                                                    labelRectangle.Height() * 0.5
2004
                                                        + getIsoDimensionLineSpacing(),
2005
                                                    lineAngle, labelAngle);
2006

2007
        // Is there point on the arc, where line from center intersects it perpendicularly?
2008
        double angleFactor = getAnglePlacementFactor(lineAngle, endAngle, startRotation);
2009
        if (angleFactor < 0.0) {
2010
            lineAngle = DrawUtil::angleComposition(lineAngle, M_PI);
2011
        }
2012

2013
        Base::Vector2d arcPoint;
2014
        double labelPosition;
2015
        if (angleFactor) {
2016
            arcPoint = centerPoint + Base::Vector2d::FromPolar(radius, lineAngle);
2017

2018
            // Correct the label center distance projected on the leader line and subtract radius
2019
            labelPosition =
2020
                angleFactor * cos(devAngle) * ((labelCenter - centerPoint).Length()) - radius;
2021
        }
2022
        else {
2023
            // Leader line outside both arc wedges
2024
            arcPoint = midPoint;
2025

2026
            devAngle = computeLineAndLabelAngles(arcPoint, labelCenter,
2027
                                                 labelRectangle.Height() * 0.5
2028
                                                     + getIsoDimensionLineSpacing(),
2029
                                                 lineAngle, labelAngle);
2030
            lineAngle = DrawUtil::angleComposition(lineAngle, M_PI);
2031

2032
            labelPosition = -cos(devAngle) * ((labelCenter - arcPoint).Length());
2033
        }
2034

2035
        drawDimensionLine(radiusPath, arcPoint, lineAngle,
2036
                          // If not reduced rendering and at least in one arc wedge, draw to center
2037
                          angleFactor && renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL
2038
                              ? -radius - centerOverhang
2039
                              : 0.0,
2040
                          labelPosition, labelRectangle, 1, standardStyle, flipArrow);
2041
    }
2042
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
2043
        // Text must remain horizontal, but it may split the leader line
2044
        Base::Vector2d labelDirection(labelCenter - centerPoint);
2045
        double lineAngle = labelDirection.Angle();
2046

2047
        // Is there point on the arc, where line from center intersects it perpendicularly?
2048
        double angleFactor = getAnglePlacementFactor(lineAngle, endAngle, startRotation);
2049
        if (angleFactor < 0) {
2050
            lineAngle = DrawUtil::angleComposition(lineAngle, M_PI);
2051
        }
2052

2053
        Base::Vector2d arcPoint;
2054
        double labelPosition;
2055
        if (angleFactor) {
2056
            arcPoint = centerPoint + Base::Vector2d::FromPolar(radius, lineAngle);
2057

2058
            labelPosition = angleFactor * labelDirection.Length() - radius;
2059
        }
2060
        else {
2061
            // Leader line outside both arc wedges
2062
            arcPoint = midPoint;
2063

2064
            labelDirection = arcPoint - labelCenter;
2065
            lineAngle = labelDirection.Angle();
2066
            labelPosition = -labelDirection.Length();
2067
        }
2068

2069
        drawDimensionLine(radiusPath, arcPoint, lineAngle,
2070
                          // If not reduced rendering and at least in one arc wedge, draw to center
2071
                          angleFactor && renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL
2072
                              ? -radius - centerOverhang
2073
                              : 0.0,
2074
                          labelPosition, labelRectangle, 1, standardStyle, flipArrow);
2075
    }
2076
    else {
2077
        Base::Console().Error(
2078
            "QGIVD::drawRadiusExecutive - this Standard&Style is not supported: %d\n",
2079
            standardStyle);
2080
    }
2081

2082
    datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center());
2083
    datumLabel->setRotation(toQtDeg(labelAngle));
2084

2085
    dimLines->setPath(radiusPath);
2086
}
2087

2088
void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension,
2089
                                    ViewProviderDimension* viewProvider) const
2090
{
2091
    Base::BoundBox2d labelRectangle(
2092
        fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect())));
2093

2094
    pointPair linePoints = dimension->getLinearPoints();
2095
    const char* dimensionType = dimension->Type.getValueAsString();
2096

2097
    double lineAngle;
2098
    if (strcmp(dimensionType, "DistanceX") == 0) {
2099
        lineAngle = 0.0;
2100
    }
2101
    else if (strcmp(dimensionType, "DistanceY") == 0) {
2102
        lineAngle = M_PI_2;
2103
    }
2104
    else {
2105
        lineAngle = (fromQtApp(linePoints.second()) - fromQtApp(linePoints.first())).Angle();
2106
    }
2107

2108
    int standardStyle = viewProvider->StandardAndStyle.getValue();
2109
    int renderExtent = viewProvider->RenderingExtent.getValue();
2110
    bool flipArrows = viewProvider->FlipArrowheads.getValue();
2111

2112

2113
    if (dimension->AngleOverride.getValue()) {
2114
        drawDistanceOverride(fromQtApp(linePoints.first()), fromQtApp(linePoints.second()),
2115
                             dimension->LineAngle.getValue() * M_PI / 180.0, labelRectangle,
2116
                             standardStyle, renderExtent, flipArrows,
2117
                             dimension->ExtensionAngle.getValue() * M_PI / 180.0);
2118
    }
2119
    else {
2120
        drawDistanceExecutive(fromQtApp(linePoints.extensionLineFirst()), fromQtApp(linePoints.extensionLineSecond()),
2121
                              lineAngle, labelRectangle, standardStyle, renderExtent, flipArrows);
2122
    }
2123
}
2124

2125
void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension* dimension,
2126
                                  ViewProviderDimension* viewProvider) const
2127
{
2128
    Base::BoundBox2d labelRectangle(
2129
        fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect())));
2130
    arcPoints curvePoints = dimension->getArcPoints();
2131

2132
    double endAngle;
2133
    double startRotation;
2134
    if (curvePoints.isArc) {
2135
        endAngle =
2136
            (fromQtApp(curvePoints.arcEnds.second()) - fromQtApp(curvePoints.center)).Angle();
2137
        startRotation =
2138
            (fromQtApp(curvePoints.arcEnds.first()) - fromQtApp(curvePoints.center)).Angle()
2139
            - endAngle;
2140

2141
        if (startRotation != 0.0 && ((startRotation > 0.0) != curvePoints.arcCW)) {
2142
            startRotation += curvePoints.arcCW ? +M_2PI : -M_2PI;
2143
        }
2144
    }
2145
    else {// A circle arc covers the whole plane
2146
        endAngle = M_PI;
2147
        startRotation = -M_2PI;
2148
    }
2149

2150
    drawRadiusExecutive(
2151
        fromQtApp(curvePoints.center), fromQtApp(curvePoints.midArc), curvePoints.radius, endAngle,
2152
        startRotation, labelRectangle, 0.0, viewProvider->StandardAndStyle.getValue(),
2153
        viewProvider->RenderingExtent.getValue(), viewProvider->FlipArrowheads.getValue());
2154
}
2155

2156
void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension* dimension,
2157
                                    ViewProviderDimension* viewProvider) const
2158
{
2159
    Base::BoundBox2d labelRectangle(
2160
        fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect())));
2161
    Base::Vector2d labelCenter(labelRectangle.GetCenter());
2162

2163
    arcPoints curvePoints = dimension->getArcPoints();
2164

2165
    Base::Vector2d curveCenter(fromQtApp(curvePoints.center));
2166
    double curveRadius = curvePoints.radius;
2167

2168
    int standardStyle = viewProvider->StandardAndStyle.getValue();
2169
    int renderExtent = viewProvider->RenderingExtent.getValue();
2170
    bool flipArrows = viewProvider->FlipArrowheads.getValue();
2171

2172
    if (renderExtent == ViewProviderDimension::REND_EXTENT_NORMAL) {
2173
        // Draw diameter as one line crossing center touching two opposite circle points
2174
        QPainterPath diameterPath;
2175
        double labelAngle = 0.0;
2176

2177
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
2178
            || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) {
2179
            // The dimensional value text must stay horizontal
2180
            Base::Vector2d jointDirections[2];
2181

2182
            if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) {
2183
                jointDirections[0] = getIsoRefJointPoint(labelRectangle, false) - curveCenter;
2184
                jointDirections[1] = getIsoRefJointPoint(labelRectangle, true) - curveCenter;
2185
            }
2186
            else {
2187
                jointDirections[0] = getAsmeRefJointPoint(labelRectangle, false) - curveCenter;
2188
                jointDirections[1] = getAsmeRefJointPoint(labelRectangle, true) - curveCenter;
2189
            }
2190

2191
            // Find the angles of lines from the center
2192
            double lineAngles[2];
2193
            lineAngles[0] = jointDirections[0].Angle();
2194
            lineAngles[1] = jointDirections[1].Angle();
2195

2196
            Base::Vector2d targetPoints[2];
2197
            targetPoints[0] = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngles[0]);
2198
            targetPoints[1] = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngles[1]);
2199

2200
            // Find the positions where the reference line attaches to the dimension line
2201
            double jointPositions[2];
2202
            jointPositions[0] = jointDirections[0].Length() - curveRadius;
2203
            jointPositions[1] = jointDirections[1].Length() - curveRadius;
2204

2205
            // Select the placement, where the label is not obscured by the leader line
2206
            double strikeFactors[2];
2207

2208
            std::vector<std::pair<double, bool>> lineMarking;
2209
            constructDimensionLine(targetPoints[0], lineAngles[0], -curveRadius * 2.0,
2210
                                   jointPositions[0], labelRectangle, 2, standardStyle, flipArrows,
2211
                                   lineMarking);
2212
            strikeFactors[0] = computeLineStrikeFactor(labelRectangle, targetPoints[0],
2213
                                                       lineAngles[0], lineMarking);
2214

2215
            lineMarking.clear();
2216
            constructDimensionLine(targetPoints[1], lineAngles[1], -curveRadius * 2.0,
2217
                                   jointPositions[1], labelRectangle, 2, standardStyle, flipArrows,
2218
                                   lineMarking);
2219
            strikeFactors[1] = computeLineStrikeFactor(labelRectangle, targetPoints[1],
2220
                                                       lineAngles[1], lineMarking);
2221

2222
            int selected = 0;
2223
            if (compareAngleStraightness(
2224
                    0.0,
2225
                    jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], M_PI)
2226
                                            : lineAngles[0],
2227
                    jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], M_PI)
2228
                                            : lineAngles[1],
2229
                    strikeFactors[0], strikeFactors[1])
2230
                > 0) {
2231
                selected = 1;
2232
            }
2233

2234
            drawDimensionLine(diameterPath,
2235
                              curveCenter
2236
                                  + Base::Vector2d::FromPolar(curveRadius, lineAngles[selected]),
2237
                              lineAngles[selected], -curveRadius * 2.0, jointPositions[selected],
2238
                              labelRectangle, 2, standardStyle, flipArrows);
2239

2240
            Base::Vector2d outsetPoint(standardStyle
2241
                                               == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
2242
                                           ? getIsoRefOutsetPoint(labelRectangle, selected == 1)
2243
                                           : getAsmeRefOutsetPoint(labelRectangle, selected == 1));
2244

2245
            diameterPath.moveTo(toQtGui(outsetPoint));
2246
            diameterPath.lineTo(toQtGui(curveCenter + jointDirections[selected]));
2247
        }
2248
        else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
2249
            // We may rotate the label so no reference line is needed
2250
            double lineAngle;
2251
            double devAngle = computeLineAndLabelAngles(curveCenter, labelCenter,
2252
                                                        labelRectangle.Height() * 0.5
2253
                                                            + getIsoDimensionLineSpacing(),
2254
                                                        lineAngle, labelAngle);
2255

2256
            // Correct the label center distance projected on the leader line and subtract radius
2257
            double labelPosition =
2258
                cos(devAngle) * ((labelCenter - curveCenter).Length()) - curveRadius;
2259

2260
            drawDimensionLine(diameterPath,
2261
                              curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle),
2262
                              lineAngle, -curveRadius * 2.0, labelPosition, labelRectangle, 2,
2263
                              standardStyle, flipArrows);
2264
        }
2265
        else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
2266
            // Text must remain horizontal, but it may split the leader line
2267
            double lineAngle = (labelCenter - curveCenter).Angle();
2268
            //Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
2269

2270
            drawDimensionLine(
2271
                diameterPath, curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle),
2272
                lineAngle, -curveRadius * 2.0, (labelCenter - curveCenter).Length() - curveRadius,
2273
                labelRectangle, 2, standardStyle, flipArrows);
2274
        }
2275
        else {
2276
            Base::Console().Error("QGIVD::drawRadius - this Standard&Style is not supported: %d\n",
2277
                                  standardStyle);
2278
        }
2279

2280
        datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center());
2281
        datumLabel->setRotation(toQtDeg(labelAngle));
2282

2283
        dimLines->setPath(diameterPath);
2284
    }
2285
    else if (renderExtent >= ViewProviderDimension::REND_EXTENT_EXPANDED) {
2286
        double lineAngle = (labelCenter - curveCenter).Angle();
2287
        Base::Vector2d startPoint(curveCenter);
2288
        Base::Vector2d endPoint(curveCenter);
2289

2290
        if ((lineAngle >= M_PI_4 && lineAngle <= 3.0 * M_PI_4)
2291
            || (lineAngle <= -M_PI_4 && lineAngle >= -3.0 * M_PI_4)) {
2292
            // Horizontal dimension line
2293
            startPoint.x -= curveRadius;
2294
            endPoint.x += curveRadius;
2295
            lineAngle = 0.0;
2296
        }
2297
        else {// Vertical dimension line
2298
            startPoint.y -= curveRadius;
2299
            endPoint.y += curveRadius;
2300
            lineAngle = M_PI_2;
2301
        }
2302

2303
        //        lineAngle = DrawUtil::angleComposition((labelCenter - curveCenter).Angle(), +M_PI_2);
2304
        //        startPoint = curveCenter - Base::Vector2d::FromPolar(curveRadius, lineAngle);
2305
        //        endPoint = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle);
2306

2307
        drawDistanceExecutive(startPoint, endPoint, lineAngle, labelRectangle, standardStyle,
2308
                              ViewProviderDimension::REND_EXTENT_NORMAL, flipArrows);
2309
    }
2310
    else if (renderExtent <= ViewProviderDimension::REND_EXTENT_REDUCED) {
2311
        renderExtent = renderExtent <= ViewProviderDimension::REND_EXTENT_CONFINED
2312
            ? ViewProviderDimension::REND_EXTENT_REDUCED
2313
            : ViewProviderDimension::REND_EXTENT_NORMAL;
2314

2315
        drawRadiusExecutive(curveCenter, Rez::guiX(curvePoints.midArc, true), curveRadius, M_PI,
2316
                            -M_2PI, labelRectangle, getDefaultExtensionLineOverhang(),
2317
                            standardStyle, renderExtent, flipArrows);
2318
    }
2319
}
2320

2321
void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension,
2322
                                 ViewProviderDimension* viewProvider) const
2323
{
2324
    QPainterPath anglePath;
2325

2326
    Base::BoundBox2d labelRectangle(
2327
        fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect())));
2328
    Base::Vector2d labelCenter(labelRectangle.GetCenter());
2329
    double labelAngle = 0.0;
2330

2331
    anglePoints anglePoints = dimension->getAnglePoints();
2332

2333
    Base::Vector2d angleVertex = fromQtApp(anglePoints.vertex());
2334
    Base::Vector2d startPoint = fromQtApp(anglePoints.first());
2335
    Base::Vector2d endPoint = fromQtApp(anglePoints.second());
2336

2337
    double endAngle = (endPoint - angleVertex).Angle();
2338
    double startAngle = (startPoint - angleVertex).Angle();
2339
    double arcRadius;
2340

2341
    int standardStyle = viewProvider->StandardAndStyle.getValue();
2342
    int renderExtent = viewProvider->RenderingExtent.getValue();
2343
    bool flipArrows = viewProvider->FlipArrowheads.getValue();
2344

2345
    int arrowCount = renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL
2346
            || renderExtent == ViewProviderDimension::REND_EXTENT_CONFINED
2347
        ? 2
2348
        : 1;
2349

2350
    // Inverted dimensions display reflex angles (fi > PI), regular ones oblique angles (fi <= PI/2)
2351
    double startRotation =
2352
        DrawUtil::angleDifference(startAngle, endAngle, dimension->Inverted.getValue());
2353
    if (arrowCount < 2) {
2354
        // For single arrow, the effective angle span is 0, but still we need to know
2355
        // the angle orientation. Floating point positive/negative zero comes to rescue...
2356
        startRotation = copysign(0.0, startRotation);
2357
    }
2358

2359
    if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
2360
        || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) {
2361
        // The dimensional value text must stay horizontal
2362
        Base::Vector2d jointDirections[2];
2363

2364
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) {
2365
            jointDirections[0] = getIsoRefJointPoint(labelRectangle, false) - angleVertex;
2366
            jointDirections[1] = getIsoRefJointPoint(labelRectangle, true) - angleVertex;
2367
        }
2368
        else {
2369
            jointDirections[0] = getAsmeRefJointPoint(labelRectangle, false) - angleVertex;
2370
            jointDirections[1] = getAsmeRefJointPoint(labelRectangle, true) - angleVertex;
2371
        }
2372

2373
        // Get radiuses of the angle dimension arcs
2374
        double arcRadii[2];
2375
        arcRadii[0] = jointDirections[0].Length();
2376
        arcRadii[1] = jointDirections[1].Length();
2377

2378
        // Compute the reference line joint angles
2379
        double jointAngles[2];
2380
        jointAngles[0] = jointDirections[0].Angle();
2381
        jointAngles[1] = jointDirections[1].Angle();
2382

2383
        double handednessFactor = normalizeStartRotation(startRotation);
2384
        double jointRotations[2];
2385
        jointRotations[0] = handednessFactor * (jointAngles[0] - endAngle);
2386
        jointRotations[1] = handednessFactor * (jointAngles[1] - endAngle);
2387

2388
        // Compare the offset with half of the rest of 2PI minus the angle and eventually fix the values
2389
        if (fabs(jointRotations[0] - startRotation * 0.5) > M_PI) {
2390
            jointRotations[0] += jointRotations[0] < 0.0 ? +M_2PI : -M_2PI;
2391
        }
2392
        if (fabs(jointRotations[1] - startRotation * 0.5) > M_PI) {
2393
            jointRotations[1] += jointRotations[1] < 0.0 ? +M_2PI : -M_2PI;
2394
        }
2395

2396
        // Compute the strike factors so we can choose the placement where value is not obscured by dimensional arc
2397
        double strikeFactors[2];
2398

2399
        std::vector<std::pair<double, bool>> arcMarking;
2400
        constructDimensionArc(angleVertex, arcRadii[0], endAngle, startRotation, handednessFactor,
2401
                              jointRotations[0], labelRectangle, arrowCount, standardStyle,
2402
                              flipArrows, arcMarking);
2403
        strikeFactors[0] =
2404
            computeArcStrikeFactor(labelRectangle, angleVertex, arcRadii[0], arcMarking);
2405

2406
        arcMarking.clear();
2407
        constructDimensionArc(angleVertex, arcRadii[1], endAngle, startRotation, handednessFactor,
2408
                              jointRotations[1], labelRectangle, arrowCount, standardStyle,
2409
                              flipArrows, arcMarking);
2410
        strikeFactors[1] =
2411
            computeArcStrikeFactor(labelRectangle, angleVertex, arcRadii[1], arcMarking);
2412

2413
        int selected = 0;
2414
        if (compareAngleStraightness(
2415
                0.0,
2416
                DrawUtil::angleComposition(
2417
                    jointAngles[0], handednessFactor * jointRotations[0] > 0.0 ? -M_PI_2 : +M_PI_2),
2418
                DrawUtil::angleComposition(
2419
                    jointAngles[1], handednessFactor * jointRotations[1] > 0.0 ? -M_PI_2 : +M_PI_2),
2420
                strikeFactors[0], strikeFactors[1])
2421
            > 0) {
2422
            selected = 1;
2423
        }
2424

2425
        arcRadius = arcRadii[selected];
2426
        startRotation = copysign(startRotation, -handednessFactor);
2427

2428
        drawDimensionArc(anglePath, angleVertex, arcRadius, endAngle, startRotation,
2429
                         jointAngles[selected], labelRectangle, arrowCount, standardStyle,
2430
                         flipArrows);
2431

2432
        Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
2433
                                       ? getIsoRefOutsetPoint(labelRectangle, selected == 1)
2434
                                       : getAsmeRefOutsetPoint(labelRectangle, selected == 1));
2435

2436
        anglePath.moveTo(toQtGui(outsetPoint));
2437
        anglePath.lineTo(toQtGui(angleVertex + jointDirections[selected]));
2438
    }
2439
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
2440
        // We may rotate the label so no leader and reference lines are needed
2441
        Base::Vector2d labelDirection(labelCenter - angleVertex);
2442
        double radiusAngle = labelDirection.Angle();
2443

2444
        labelAngle = DrawUtil::angleComposition(radiusAngle, M_PI_2);
2445
        double placementFactor = getIsoStandardLinePlacement(labelAngle);
2446
        labelAngle =
2447
            placementFactor > 0.0 ? DrawUtil::angleComposition(labelAngle, M_PI) : labelAngle;
2448

2449
        arcRadius = labelDirection.Length()
2450
            - placementFactor
2451
                * (labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing());
2452
        if (arcRadius < 0.0) {
2453
            arcRadius = labelDirection.Length();
2454
        }
2455

2456
        drawDimensionArc(anglePath, angleVertex, arcRadius, endAngle, startRotation, radiusAngle,
2457
                         labelRectangle, arrowCount, standardStyle, flipArrows);
2458
    }
2459
    else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
2460
        // Text must remain horizontal, but it may split the leader line
2461
        Base::Vector2d labelDirection(labelCenter - angleVertex);
2462
        arcRadius = labelDirection.Length();
2463

2464
        drawDimensionArc(anglePath, angleVertex, arcRadius, endAngle, startRotation,
2465
                         labelDirection.Angle(), labelRectangle, arrowCount, standardStyle,
2466
                         flipArrows);
2467
    }
2468
    else {
2469
        Base::Console().Error("QGIVD::drawAngle - this Standard&Style is not supported: %d\n",
2470
                              standardStyle);
2471
        arrowCount = 0;
2472
    }
2473

2474
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
2475
    assert(vp);
2476

2477
    if (arrowCount > 0 && renderExtent >= ViewProviderDimension::REND_EXTENT_REDUCED) {
2478
        double gapSize = 0.0;
2479
        if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING
2480
            || standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
2481
            double factor = vp->GapFactorASME.getValue();
2482
            gapSize = Rez::appX(m_lineWidth * factor);
2483
        }
2484
        if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING
2485
            || standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
2486
            double factor = vp->GapFactorISO.getValue();
2487
            gapSize = Rez::appX(m_lineWidth * factor);
2488
        }
2489

2490

2491
        Base::Vector2d extensionOrigin;
2492
        Base::Vector2d extensionTarget(computeExtensionLinePoints(
2493
            endPoint, angleVertex + Base::Vector2d::FromPolar(arcRadius, endAngle), endAngle,
2494
            getDefaultExtensionLineOverhang(), gapSize, extensionOrigin));
2495
        anglePath.moveTo(toQtGui(extensionOrigin));
2496
        anglePath.lineTo(toQtGui(extensionTarget));
2497

2498
        if (arrowCount > 1) {
2499
            extensionTarget = computeExtensionLinePoints(
2500
                startPoint, angleVertex + Base::Vector2d::FromPolar(arcRadius, startAngle),
2501
                startAngle, getDefaultExtensionLineOverhang(), gapSize, extensionOrigin);
2502
            anglePath.moveTo(toQtGui(extensionOrigin));
2503
            anglePath.lineTo(toQtGui(extensionTarget));
2504
        }
2505
    }
2506

2507
    datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center());
2508
    datumLabel->setRotation(toQtDeg(labelAngle));
2509

2510
    dimLines->setPath(anglePath);
2511
}
2512

2513
QColor QGIViewDimension::prefNormalColor()
2514
{
2515
    setNormalColor(PreferencesGui::getAccessibleQColor(PreferencesGui::dimQColor()));
2516
    ViewProviderDimension* vpDim = nullptr;
2517
    Gui::ViewProvider* vp = getViewProvider(getDimFeat());
2518
    if (vp) {
2519
        vpDim = dynamic_cast<ViewProviderDimension*>(vp);
2520
        if (vpDim) {
2521
            App::Color fcColor = vpDim->Color.getValue();
2522
            fcColor = Preferences::getAccessibleColor(fcColor);
2523
            setNormalColor(fcColor.asValue<QColor>());
2524
        }
2525
    }
2526
    return getNormalColor();
2527
}
2528

2529
//! find the closest isometric axis given an ortho vector
2530
Base::Vector3d QGIViewDimension::findIsoDir(Base::Vector3d ortho) const
2531
{
2532
    std::vector<Base::Vector3d> isoDirs = {Base::Vector3d(0.866, 0.5, 0.0),  //iso X
2533
                                           Base::Vector3d(-0.866, -0.5, 0.0),//iso -X
2534
                                           Base::Vector3d(-0.866, 0.5, 0.0), //iso -Y?
2535
                                           Base::Vector3d(0.866, -0.5, 0.0), //iso +Y?
2536
                                           Base::Vector3d(0.0, -1.0, 0.0),   //iso -Z
2537
                                           Base::Vector3d(0.0, 1.0, 0.0)};   //iso Z
2538
    std::vector<double> angles;
2539
    for (auto& iso : isoDirs) {
2540
        angles.push_back(ortho.GetAngle(iso));
2541
    }
2542
    int idx = 0;
2543
    double min = angles[0];
2544
    for (int i = 1; i < 6; i++) {
2545
        if (angles[i] < min) {
2546
            idx = i;
2547
            min = angles[i];
2548
        }
2549
    }
2550
    return isoDirs[idx];
2551
}
2552

2553
//! find the iso extension direction corresponding to an iso dist direction
2554
Base::Vector3d QGIViewDimension::findIsoExt(Base::Vector3d dir) const
2555
{
2556
    Base::Vector3d isoX(0.866, 0.5, 0.0);   //iso X
2557
    Base::Vector3d isoXr(-0.866, -0.5, 0.0);//iso -X
2558
    Base::Vector3d isoY(-0.866, 0.5, 0.0);  //iso -Y?
2559
    Base::Vector3d isoYr(0.866, -0.5, 0.0); //iso +Y?
2560
    Base::Vector3d isoZ(0.0, 1.0, 0.0);     //iso Z
2561
    Base::Vector3d isoZr(0.0, -1.0, 0.0);   //iso -Z
2562
    if (dir.IsEqual(isoX, FLT_EPSILON)) {
2563
        return isoY;
2564
    }
2565
    else if (dir.IsEqual(-isoX, FLT_EPSILON)) {
2566
        return -isoY;
2567
    }
2568
    else if (dir.IsEqual(isoY, FLT_EPSILON)) {
2569
        return isoZ;
2570
    }
2571
    else if (dir.IsEqual(-isoY, FLT_EPSILON)) {
2572
        return -isoZ;
2573
    }
2574
    else if (dir.IsEqual(isoZ, FLT_EPSILON)) {
2575
        return isoX;
2576
    }
2577
    else if (dir.IsEqual(-isoZ, FLT_EPSILON)) {
2578
        return -isoX;
2579
    }
2580

2581
    //tarfu
2582
    Base::Console().Message("QGIVD::findIsoExt - %s - input is not iso axis\n",
2583
                            getViewObject()->getNameInDocument());
2584
    return Base::Vector3d(1, 0, 0);
2585
}
2586

2587
void QGIViewDimension::onPrettyChanged(int state)
2588
{
2589
    //    Base::Console().Message("QGIVD::onPrettyChange(%d)\n", state);
2590
    if (state == NORMAL) {
2591
        setPrettyNormal();
2592
    }
2593
    else if (state == PRE) {
2594
        setPrettyPre();
2595
    }
2596
    else {//if state = SEL
2597
        setPrettySel();
2598
    }
2599
}
2600

2601
void QGIViewDimension::setPrettyPre()
2602
{
2603
    aHead1->setPrettyPre();
2604
    aHead2->setPrettyPre();
2605
    dimLines->setPrettyPre();
2606
}
2607

2608
void QGIViewDimension::setPrettySel()
2609
{
2610
    aHead1->setPrettySel();
2611
    aHead2->setPrettySel();
2612
    dimLines->setPrettySel();
2613
}
2614

2615
void QGIViewDimension::setPrettyNormal()
2616
{
2617
    aHead1->setPrettyNormal();
2618
    aHead2->setPrettyNormal();
2619
    dimLines->setPrettyNormal();
2620
}
2621

2622
void QGIViewDimension::drawBorder()
2623
{
2624
    //Dimensions have no border!
2625
    //    Base::Console().Message("TRACE - QGIViewDimension::drawBorder - doing nothing!\n");
2626
}
2627

2628
double QGIViewDimension::getDefaultExtensionLineOverhang() const
2629
{
2630
    // 8x Line Width according to ISO 129-1 Standard section 5.4, not specified by ASME Y14.5M
2631
    return Rez::appX(m_lineWidth * 8.0);
2632
}
2633

2634
double QGIViewDimension::getDefaultArrowTailLength() const
2635
{
2636
    // Arrow length shall be equal to font height and both ISO and ASME seem
2637
    // to have arrow tail twice the arrow length, so let's make it twice arrow size
2638
    auto arrowSize = PreferencesGui::dimArrowSize();
2639
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
2640
    if (vp) {
2641
        arrowSize = vp->Arrowsize.getValue();
2642

2643
    }
2644
    return  arrowSize * 2.0;
2645
}
2646

2647
double QGIViewDimension::getDefaultIsoDimensionLineSpacing() const
2648
{
2649
    // Not specified directly, but seems to be 2x Line Width according to ISO 129-1 Annex A
2650
    return Rez::appX(m_lineWidth * 2.0);
2651
}
2652

2653
// Returns the line spacing for ISO dimension based on the user inputted factor
2654
double QGIViewDimension::getIsoDimensionLineSpacing() const
2655
{
2656
    auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
2657
    return Rez::appX(m_lineWidth * vp->LineSpacingFactorISO.getValue());
2658
}
2659

2660
double QGIViewDimension::getDefaultIsoReferenceLineOverhang() const
2661
{
2662
    // Not specified directly but seems to be exactly Line Width according to ISO 129-1 Annex A
2663
    return Rez::appX(m_lineWidth * 1.0);
2664
}
2665

2666
double QGIViewDimension::getDefaultAsmeHorizontalLeaderLength() const
2667
{
2668
    // Not specified by ASME Y14.5M, this is a best guess
2669
    return Rez::appX(m_lineWidth * 12);
2670
}
2671

2672
//frame, border, caption are never shown in QGIVD, so shouldn't be in bRect
2673
QRectF QGIViewDimension::boundingRect() const
2674
{
2675
    QRectF labelRect = mapFromItem(datumLabel, datumLabel->boundingRect()).boundingRect();
2676
    QRectF linesRect = mapFromItem(dimLines, dimLines->boundingRect()).boundingRect();
2677
    QRectF aHead1Rect = mapFromItem(aHead1, aHead1->boundingRect()).boundingRect();
2678
    QRectF aHead2Rect = mapFromItem(aHead2, aHead2->boundingRect()).boundingRect();
2679
    QRectF result(labelRect);
2680
    result = result.united(linesRect);
2681
    result = result.united(aHead1Rect);
2682
    result = result.united(aHead2Rect);
2683
    return result;
2684
}
2685

2686
void QGIViewDimension::paint(QPainter* painter, const QStyleOptionGraphicsItem* option,
2687
                             QWidget* widget)
2688
{
2689
    QStyleOptionGraphicsItem myOption(*option);
2690
    myOption.state &= ~QStyle::State_Selected;
2691

2692
    QPaintDevice* hw = painter->device();
2693
    QSvgGenerator* svg = dynamic_cast<QSvgGenerator*>(hw);
2694
    setPens();
2695
    //double arrowSaveWidth = aHead1->getWidth();
2696
    if (svg) {
2697
        setSvgPens();
2698
    }
2699
    else {
2700
        setPens();
2701
    }
2702

2703
    //    painter->setPen(Qt::red);
2704
    //    painter->drawRect(boundingRect());          //good for debugging
2705

2706
    //    QGIView::paint (painter, &myOption, widget);
2707
    QGraphicsItemGroup::paint(painter, &myOption, widget);
2708
    setPens();
2709
}
2710

2711
void QGIViewDimension::setSvgPens()
2712
{
2713
    double svgLineFactor = 3.0;//magic number.  should be a setting somewhere.
2714
    dimLines->setWidth(m_lineWidth / svgLineFactor);
2715
    aHead1->setWidth(aHead1->getWidth() / svgLineFactor);
2716
    aHead2->setWidth(aHead2->getWidth() / svgLineFactor);
2717
}
2718

2719
void QGIViewDimension::setPens()
2720
{
2721
    dimLines->setWidth(m_lineWidth);
2722
    aHead1->setWidth(m_lineWidth);
2723
    aHead2->setWidth(m_lineWidth);
2724
}
2725

2726
double QGIViewDimension::toDeg(double angle) { return angle * 180 / M_PI; }
2727

2728
double QGIViewDimension::toQtRad(double angle) { return -angle; }
2729

2730
double QGIViewDimension::toQtDeg(double angle) { return -angle * 180.0 / M_PI; }
2731

2732
void QGIViewDimension::makeMarkC(double xPos, double yPos, QColor color) const
2733
{
2734
    QGIVertex* vItem = new QGIVertex(-1);
2735
    vItem->setParentItem(const_cast<QGIViewDimension*>(this));
2736
    vItem->setPos(xPos, yPos);
2737
    vItem->setWidth(2.0);
2738
    vItem->setRadius(20.0);
2739
    vItem->setNormalColor(color);
2740
    vItem->setFillColor(color);
2741
    vItem->setPrettyNormal();
2742
    vItem->setZValue(ZVALUE::VERTEX);
2743
}
2744

2745
#include <Mod/TechDraw/Gui/moc_QGIViewDimension.cpp>
2746

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

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

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

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