Files
create/src/Mod/TechDraw/Gui/QGIViewDimension.cpp
2019-08-21 19:27:12 -04:00

2040 lines
78 KiB
C++

/***************************************************************************
* Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <BRep_Builder.hxx>
#include <TopoDS_Compound.hxx>
# include <TopoDS_Shape.hxx>
# include <TopoDS_Edge.hxx>
# include <TopoDS.hxx>
# include <BRepAdaptor_Curve.hxx>
# include <Precision.hxx>
# include <QGraphicsScene>
# include <QGraphicsSceneMouseEvent>
# include <QPainter>
# include <QPaintDevice>
# include <QSvgGenerator>
# include <cmath>
#endif
#include <App/Application.h>
#include <App/Material.h>
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Parameter.h>
#include <Base/UnitsApi.h>
#include <Gui/Command.h>
#include <Mod/Part/App/PartFeature.h>
#include <Mod/TechDraw/App/DrawViewDimension.h>
#include <Mod/TechDraw/App/DrawViewPart.h>
#include <Mod/TechDraw/App/DrawUtil.h>
#include <Mod/TechDraw/App/Geometry.h>
#include "Rez.h"
#include "ZVALUE.h"
#include "QGCustomLabel.h"
#include "QGCustomBorder.h"
#include "QGCustomText.h"
#include "QGICaption.h"
#include "QGCustomImage.h"
#include "QGIArrow.h"
#include "QGIDimLines.h"
#include "QGIViewDimension.h"
#include "ViewProviderDimension.h"
#include "DrawGuiUtil.h"
#ifndef M_2PI
#define M_2PI ((M_PI) * 2.0)
#endif
//TODO: hide the Qt coord system (+y down).
using namespace TechDraw;
using namespace TechDrawGui;
enum SnapMode{
NoSnap,
VerticalSnap,
HorizontalSnap
};
QGIDatumLabel::QGIDatumLabel()
{
posX = 0;
posY = 0;
setCacheMode(QGraphicsItem::NoCache);
setFlag(ItemSendsGeometryChanges, true);
setFlag(ItemIsMovable, true);
setFlag(ItemIsSelectable, true);
setAcceptHoverEvents(true);
m_dimText = new QGCustomText();
m_dimText->setParentItem(this);
m_tolText = new QGCustomText();
m_tolText->setParentItem(this);
m_ctrl = false;
hasHover = false;
}
QVariant QGIDatumLabel::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemSelectedHasChanged && scene()) {
if(isSelected()) {
Q_EMIT selected(true);
setPrettySel();
} else {
Q_EMIT selected(false);
setPrettyNormal();
}
update();
} else if(change == ItemPositionHasChanged && scene()) {
setLabelCenter();
Q_EMIT dragging(m_ctrl);
}
return QGraphicsItem::itemChange(change, value);
}
void QGIDatumLabel::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
if(event->modifiers() & Qt::ControlModifier) {
m_ctrl = true;
}
if(scene() && this == scene()->mouseGrabberItem()) {
Q_EMIT dragFinished();
}
QGraphicsItem::mousePressEvent(event);
}
void QGIDatumLabel::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsItem::mouseMoveEvent(event);
}
void QGIDatumLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
m_ctrl = false;
if(scene() && this == scene()->mouseGrabberItem()) {
Q_EMIT dragFinished();
}
QGraphicsItem::mouseReleaseEvent(event);
}
void QGIDatumLabel::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_EMIT hover(true);
hasHover = true;
if (!isSelected()) {
setPrettyPre();
}
QGraphicsItem::hoverEnterEvent(event);
}
void QGIDatumLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
QGIView *view = dynamic_cast<QGIView *> (parentItem());
assert(view != 0);
Q_UNUSED(view);
Q_EMIT hover(false);
hasHover = false;
if (!isSelected()) {
setPrettyNormal();
}
QGraphicsItem::hoverLeaveEvent(event);
}
QRectF QGIDatumLabel::boundingRect() const
{
return childrenBoundingRect();
}
void QGIDatumLabel::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
Q_UNUSED(painter);
QStyleOptionGraphicsItem myOption(*option);
myOption.state &= ~QStyle::State_Selected;
//painter->drawRect(boundingRect()); //good for debugging
}
void QGIDatumLabel::setPosFromCenter(const double &xCenter, const double &yCenter)
{
//set label's Qt position(top,left) given boundingRect center point
setPos(xCenter - m_dimText->boundingRect().width() / 2., yCenter - m_dimText->boundingRect().height() / 2.);
//set tolerance position
QRectF labelBox = m_dimText->boundingRect();
double right = labelBox.right();
double top = labelBox.top();
m_tolText->setPos(right,top);
}
void QGIDatumLabel::setLabelCenter()
{
//save label's bRect center (posX,posY) given Qt position (top,left)
posX = x() + m_dimText->boundingRect().width() / 2.;
posY = y() + m_dimText->boundingRect().height() / 2.;
}
void QGIDatumLabel::setFont(QFont f)
{
m_dimText->setFont(f);
QFont tFont(f);
double fontSize = f.pixelSize();
double tolAdj = getTolAdjust();
tFont.setPixelSize((int) (fontSize * tolAdj));
m_tolText->setFont(tFont);
}
void QGIDatumLabel::setDimString(QString t)
{
prepareGeometryChange();
m_dimText->setPlainText(t);
}
void QGIDatumLabel::setDimString(QString t, qreal maxWidth)
{
prepareGeometryChange();
m_dimText->setPlainText(t);
m_dimText->setTextWidth(maxWidth);
}
void QGIDatumLabel::setTolString()
{
prepareGeometryChange();
QGIViewDimension* qgivd = dynamic_cast<QGIViewDimension*>(parentItem());
if( qgivd == nullptr ) {
return; //tarfu
}
const auto dim( dynamic_cast<TechDraw::DrawViewDimension *>(qgivd->getViewObject()) );
if( dim == nullptr ) {
return;
} else if (!dim->hasTolerance()) {
return;
}
double overTol = dim->OverTolerance.getValue();
double underTol = dim->UnderTolerance.getValue();
int precision = getPrecision();
QString overFormat = QString::number(overTol,'f', precision);
QString underFormat = QString::number(underTol,'f',precision);
QString html = QString::fromUtf8("<div>%1 <br/>%2 </div>");
html = html.arg(overFormat).arg(underFormat);
m_tolText->setHtml(html);
return;
}
int QGIDatumLabel::getPrecision(void)
{
int precision;
bool global = false;
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
global = hGrp->GetBool("UseGlobalDecimals", true);
if (global) {
precision = Base::UnitsApi::getDecimals();
} else {
precision = hGrp->GetInt("AltDecimals", 2);
}
return precision;
}
double QGIDatumLabel::getTolAdjust(void)
{
double adjust;
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
adjust = hGrp->GetFloat("TolSizeAdjust", 0.50);
return adjust;
}
void QGIDatumLabel::setPrettySel(void)
{
m_dimText->setPrettySel();
m_tolText->setPrettySel();
}
void QGIDatumLabel::setPrettyPre(void)
{
m_dimText->setPrettyPre();
m_tolText->setPrettyPre();
}
void QGIDatumLabel::setPrettyNormal(void)
{
m_dimText->setPrettyNormal();
m_tolText->setPrettyNormal();
}
void QGIDatumLabel::setColor(QColor c)
{
m_colNormal = c;
m_dimText->setColor(m_colNormal);
m_tolText->setColor(m_colNormal);
}
//**************************************************************
QGIViewDimension::QGIViewDimension() :
hasHover(false),
m_lineWidth(0.0),
m_obtuse(false)
{
setHandlesChildEvents(false);
setFlag(QGraphicsItem::ItemIsMovable, false);
setFlag(QGraphicsItem::ItemIsSelectable, false);
// setAcceptHoverEvents(true);
setAcceptHoverEvents(false);
setCacheMode(QGraphicsItem::NoCache);
datumLabel = new QGIDatumLabel();
addToGroup(datumLabel);
datumLabel->setColor(getNormalColor());
datumLabel->setPrettyNormal();
dimLines = new QGIDimLines();
addToGroup(dimLines);
aHead1 = new QGIArrow();
addToGroup(aHead1);
aHead2 = new QGIArrow();
addToGroup(aHead2);
datumLabel->setZValue(ZVALUE::DIMENSION);
dimLines->setZValue(ZVALUE::DIMENSION);
aHead1->setZValue(ZVALUE::DIMENSION);
aHead2->setZValue(ZVALUE::DIMENSION);
//centerMark = new QGICMark();
//addToGroup(centerMark);
// connecting the needed slots and signals
QObject::connect(
datumLabel, SIGNAL(dragging(bool)),
this , SLOT (datumLabelDragged(bool)));
QObject::connect(
datumLabel, SIGNAL(dragFinished()),
this , SLOT (datumLabelDragFinished()));
QObject::connect(
datumLabel, SIGNAL(selected(bool)),
this , SLOT (select(bool)));
QObject::connect(
datumLabel, SIGNAL(hover(bool)),
this , SLOT (hover(bool)));
dimLines->setStyle(Qt::SolidLine);
setZValue(ZVALUE::DIMENSION); //note: this won't paint dimensions over another View if it stacks
//above this Dimension's parent view. need Layers?
m_label->hide();
m_border->hide();
m_caption->hide();
m_lock->hide();
setPrettyNormal();
}
QVariant QGIViewDimension::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemSelectedHasChanged && scene()) {
if(isSelected()) {
setSelected(false);
datumLabel->setSelected(true);
} else {
datumLabel->setSelected(false);
}
draw();
}
return QGIView::itemChange(change, value);
}
void QGIViewDimension::select(bool state)
{
// Base::Console().Message("QGIVD::select(%d)\n", state);
if (state) {
setPrettySel();
} else {
setPrettyNormal();
}
draw();
}
//surrogate for hover enter (true), hover leave (false) events
void QGIViewDimension::hover(bool state)
{
hasHover = state;
if (state) {
if (datumLabel->isSelected()) {
setPrettySel();
} else {
setPrettyPre(); //have hover, not selected -> preselect
}
} else {
if (datumLabel->isSelected()) {
setPrettySel();
} else {
setPrettyNormal();
}
}
draw();
}
void QGIViewDimension::setViewPartFeature(TechDraw::DrawViewDimension *obj)
{
if(obj == 0)
return;
setViewFeature(static_cast<TechDraw::DrawView *>(obj));
// Set the QGIGroup Properties based on the DrawView
float x = Rez::guiX(obj->X.getValue());
float y = Rez::guiX(-obj->Y.getValue());
datumLabel->setPosFromCenter(x, y);
updateDim();
draw();
}
void QGIViewDimension::updateView(bool update)
{
Q_UNUSED(update);
auto dim( dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject()) );
if( dim == nullptr )
return;
auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
if (update||
dim->X.isTouched() ||
dim->Y.isTouched()) {
float x = Rez::guiX(dim->X.getValue());
float y = Rez::guiX(dim->Y.getValue());
datumLabel->setPosFromCenter(x,-y);
updateDim();
}
else if(vp->Fontsize.isTouched() ||
vp->Font.isTouched()) {
updateDim();
} else if (vp->LineWidth.isTouched()) { //never happens!!
m_lineWidth = vp->LineWidth.getValue();
updateDim();
} else {
updateDim();
}
draw();
}
void QGIViewDimension::updateDim(bool obtuse)
{
(void) obtuse;
const auto dim( dynamic_cast<TechDraw::DrawViewDimension *>(getViewObject()) );
if( dim == nullptr ) {
return;
}
auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
QString labelText = QString::fromUtf8(dim->getFormatedValue(m_obtuse).c_str());
QFont font = datumLabel->getFont();
font.setFamily(QString::fromUtf8(vp->Font.getValue()));
font.setPixelSize(calculateFontPixelSize(vp->Fontsize.getValue()));
datumLabel->setFont(font);
prepareGeometryChange();
datumLabel->setDimString(labelText);
datumLabel->setTolString();
datumLabel->setPosFromCenter(datumLabel->X(),datumLabel->Y());
}
//this is for formatting and finding centers, not display
QString QGIViewDimension::getLabelText(void)
{
QString result;
QString first = datumLabel->getDimText()->toPlainText();
QString second = datumLabel->getTolText()->toPlainText();
result = first + second;
return result;
}
void QGIViewDimension::datumLabelDragged(bool ctrl)
{
Q_UNUSED(ctrl);
draw();
}
void QGIViewDimension::datumLabelDragFinished()
{
auto dim( dynamic_cast<TechDraw::DrawViewDimension *>(getViewObject()) );
if( dim == nullptr ) {
return;
}
double x = Rez::appX(datumLabel->X()),
y = Rez::appX(datumLabel->Y());
Gui::Command::openCommand("Drag Dimension");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.X = %f", dim->getNameInDocument(), x);
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Y = %f", dim->getNameInDocument(), -y);
Gui::Command::commitCommand();
}
void QGIViewDimension::draw()
{
if (!isVisible()) {
return;
}
datumLabel->show();
show();
TechDraw::DrawViewDimension *dim = dynamic_cast<TechDraw::DrawViewDimension *>(getViewObject());
if((!dim) || //nothing to draw, don't try
(!dim->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())) ||
(!dim->has2DReferences()) ) {
datumLabel->hide();
hide();
return;
}
const TechDraw::DrawViewPart *refObj = dim->getViewPart();
if(!refObj->hasGeometry()) { //nothing to draw yet (restoring)
datumLabel->hide();
hide();
return;
}
auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
m_colNormal = getNormalColor();
datumLabel->setColor(m_colNormal);
m_lineWidth = Rez::guiX(vp->LineWidth.getValue());
float margin = Rez::guiX(5.f);
QString labelText = getLabelText();
Base::Vector3d lblCenter(datumLabel->X(), datumLabel->Y(), 0); //already Qt gui coords
const char *dimType = dim->Type.getValueAsString();
datumLabel->show();
show();
if (strcmp(dimType, "Distance") == 0 ||
strcmp(dimType, "DistanceX") == 0 ||
strcmp(dimType, "DistanceY") == 0) {
Base::Vector3d stdUp(0.0,1.0,0.0);
Base::Vector3d stdLeft(-1.0,0.0,0.0);
pointPair pts = dim->getLinearPoints();
Base::Vector3d startDist, endDist, midDist; //start/end/mid points of distance line
startDist = Rez::guiX(pts.first);
endDist = Rez::guiX(pts.second);
if (startDist.y < endDist.y) { //always measure bottom to top
Base::Vector3d temp = startDist;
startDist = endDist;
endDist = temp;
}
Base::Vector3d vecDist = (endDist - startDist);
// +/- aligned method
// dimension text legible from bottom or right
// text outside arrows (not between) iso convention (asme convention)
// text to left of vertical dims
// text above horizontal dims
Base::Vector3d dirDist, normDist; //direction/normal vectors of distance line
Base::Vector3d dirExt;
Base::Vector3d dirDim, normDim;
Base::Vector3d dirIso;
dirDist = vecDist;
dirDist.Normalize();
normDist = Base::Vector3d (-dirDist.y,dirDist.x, 0); //normal to distance direction
//toward dimension line??? how to tell?
if (strcmp(dimType, "Distance") == 0 ) {
//distance and dimension lines parallel
dirDim = dirDist;
normDim = normDist;
} else if (strcmp(dimType, "DistanceX") == 0 ) {
//distance and dimension lines not (necessarily) parallel
dirDim = Base::Vector3d ( ((endDist.x - startDist.x >= FLT_EPSILON) ? 1 : -1) , 0, 0);
normDim = Base::Vector3d (-dirDim.y,dirDim.x, 0);
} else if (strcmp(dimType, "DistanceY") == 0 ) {
//distance and dimension lines not (necessarily) parallel
dirDim = Base::Vector3d (0, 1, 0);
normDim = Base::Vector3d (-1, 0, 0);
}
Base::Vector3d adjustDir = dirDim; //adjust line lengths for arrowheads
//for ortho drawing extension lines are para to normDim, perp to dirDist
dirExt = normDim; //dirExt is para or anti-parallel to real extension direction
dirIso = normDim;
if (refObj->isIso()) {
//is this dimension an iso dimension? ie points +/-isoX,+/-isoY,+/-isoZ
dirIso = findIsoDir(dirDist);
dirExt = findIsoExt(dirIso);
}
dirExt.Normalize();
// Get magnitude of angle between dimension line and horizontal
// to determine rotation of dimension text ("aligned" convention. alt is "unidirectional")
//note qt y axis is reversed! and angles are CW!
double textRotAngle = atan2(-dirDim.y,dirDim.x);
if (textRotAngle < 0.0) {
textRotAngle = 2 * M_PI + textRotAngle; //map to +ve textRotAngle
}
//orient text right side up
double angleFiddle = M_PI / 10.0; // 18 => 10*, 12 => 15*, 10 => 18*, ...
if ((textRotAngle > M_PI_2 + angleFiddle) && // > 100CW
(textRotAngle <= M_PI)) { // < 180CW -> Q2
textRotAngle += M_PI; // flip CW
} else if ((textRotAngle > M_PI) && // > 180CW
(textRotAngle <= 1.5*M_PI - angleFiddle)) { // < 260CW -> Q3
textRotAngle -= M_PI; // flip CCW
}
Base::Vector3d textNorm = normDim;
if (strcmp(dimType, "DistanceX") == 0 ) {
textNorm = stdUp; //always above
} else if (strcmp(dimType, "DistanceY") == 0 ) {
textNorm = stdLeft; //left of dimLine
} else if (std::abs(dirDist.x) < FLT_EPSILON) { //this is horizontal dim line
textNorm = stdLeft; //left of dimLine
} else if (std::abs(dirDist.y) < FLT_EPSILON) { //this is vertical dim line
textNorm = stdLeft; //left of dimLine
}
// +/- pos of startDist vs endDist for vert/horiz Dims
// distStartDelta sb zero for normal dims
float distStartDelta = vecDist.Dot(normDim); // component of distance vector in dim line direction
Base::Vector3d startDistAdj = startDist + normDim * distStartDelta;
double textOffset = getDefaultTextVerticalOffset();
QPointF qFigure = boundingRect().center();
Base::Vector3d figureCenter(qFigure.x(),qFigure.y(),0.0);
QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect());
lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0);
//find actual extension direction vector
Base::Vector3d startIntercept = DrawUtil::Intersect2d(startDist, dirExt,
lblCenter,dirDim);
Base::Vector3d dirExtActual = (startIntercept - startDist);
dirExtActual.Normalize();
midDist = (startDistAdj + endDist) / 2.0; //middle point of distance line
//fauxCenter is where the getDimText() would be if it was on the dimLine
Base::Vector3d fauxCenter;
if (strcmp(dimType, "Distance") == 0 ) { //oblique line
textRotAngle = -textRotAngle; // flip text 180*
fauxCenter = lblCenter + textOffset * dirExtActual;
double slope;
if (DrawUtil::fpCompare(dirDist.x, 0.0)) {
slope = std::numeric_limits<float>::max(); //vertical line
} else {
slope = fabs(dirDist.y / dirDist.x);
}
double deg = atan(slope) * 180.0 / M_PI;
if (deg > (90.0 - (angleFiddle * 180.0/M_PI))) { //mostly vertical +/- 10*
//dim text reads bottom to top on left side of dim line
if (lblCenter.x > fauxCenter.x) { //label is to right of dimline
fauxCenter = lblCenter - textOffset*dirExtActual; //move dim line closer to figure
}
} else { //mostly horizontal
//dim text reads left to right above dim line
if (lblCenter.y > fauxCenter.y) { //label is below dimline
fauxCenter = lblCenter - textOffset*dirExtActual; //move dim line closer to figure
}
}
} else if (strcmp(dimType, "DistanceX") == 0 ) {
fauxCenter = lblCenter + textOffset * textNorm;
} else if (strcmp(dimType, "DistanceY") == 0 ) {
textRotAngle = - textRotAngle;
fauxCenter = lblCenter - textOffset * textNorm;
}
//intersection of extension lines and dimension line
startIntercept = DrawUtil::Intersect2d(startDist, dirExt,
fauxCenter,dirDim);
Base::Vector3d endIntercept = DrawUtil::Intersect2d(endDist, dirExt,
fauxCenter,dirDim);
dirExtActual = (startIntercept - startDist);
dirExtActual.Normalize();
margin = Rez::guiX(2.f);
float scaler = 1.;
Base::Vector3d extStartEnd = startIntercept + dirExtActual * (margin * scaler);
Base::Vector3d extEndEnd = endIntercept + dirExtActual * (margin * scaler);
//case 1: inner placement: text between extensions & fits. arros point out from inside (default)
//case 2: inner placement2: text too big to fit. arrows point in from outside
//case 3: outer placement: text is outside extensions. arrows point in, 1 arrow tracks getDimText()
Base::Vector3d dim1Tip = startIntercept;
Base::Vector3d dim1Tail = fauxCenter;
Base::Vector3d dim2Tip = endIntercept;
Base::Vector3d dim2Tail = fauxCenter;
Base::Vector3d a1Dir = -dirDim;
Base::Vector3d a2Dir = dirDim;
if (strcmp(dimType, "DistanceY") == 0 ) {
adjustDir = -adjustDir;
a1Dir = Base::Vector3d(0,1,0);
a2Dir = Base::Vector3d(0,-1,0);
}
double dimSpan = (extEndEnd - extStartEnd).Length(); //distance between extension lines
double fauxToDim1 = (fauxCenter - dim1Tip).Length(); //label to arrow #1
double fauxToDim2 = (fauxCenter - dim2Tip).Length(); //label to end #2
double tailLength = Rez::guiX(10.f) * scaler;
//case2 - innerPlacement && text > span
double lblWidth = datumLabel->boundingRect().width();
if ((DrawUtil::isBetween(fauxCenter, dim1Tip, dim2Tip)) &&
(lblWidth > dimSpan) ) {
adjustDir = -adjustDir;
if (strcmp(dimType, "DistanceY") == 0 ) {
a1Dir = Base::Vector3d(0,-1,0);
a2Dir = Base::Vector3d(0,1,0);
dim1Tail = dim1Tip + dirDim * tailLength;
dim2Tail = dim2Tip - dirDim * tailLength;
} else {
dim1Tail = dim1Tip - tailLength * dirDim;
dim2Tail = dim2Tip + tailLength * dirDim;
a1Dir = dirDim;
a2Dir = -dirDim;
}
}
if (!DrawUtil::isBetween(fauxCenter, dim1Tip, dim2Tip)) {
//case3 - outerPlacement
adjustDir = -adjustDir;
if (strcmp(dimType, "DistanceY") == 0 ) {
a1Dir = Base::Vector3d(0,-1,0);
a2Dir = Base::Vector3d(0,1,0);
} else {
a1Dir = dirDim;
a2Dir = -dirDim;
}
if (fauxToDim1 < fauxToDim2) {
dim1Tail = fauxCenter;
dim2Tail = dim2Tip - tailLength*a2Dir;
} else {
dim1Tail = dim1Tip - tailLength*a1Dir;
dim2Tail = fauxCenter;
}
}
// Extension lines
QPainterPath path;
Base::Vector3d gap = startDist + dirExtActual * (margin * scaler); //space ext line a bit
path.moveTo(gap.x, gap.y);
path.lineTo(extStartEnd.x, extStartEnd.y);
gap = endDist + dirExtActual * (margin * scaler);
path.moveTo(gap.x, gap.y);
path.lineTo(extEndEnd.x, extEndEnd.y);
//Dimension lines (arrow shafts)
adjustDir.Normalize();
double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(),
QGIArrow::getPrefArrowSize()));
Base::Vector3d adjustedTip = dim1Tip + adjustDir * dimLineAdjust;
path.moveTo(adjustedTip.x, adjustedTip.y);
path.lineTo(dim1Tail.x, dim1Tail.y);
adjustedTip = dim2Tip - adjustDir * dimLineAdjust;
path.moveTo(adjustedTip.x, adjustedTip.y);
path.lineTo(dim2Tail.x, dim2Tail.y);
dimLines->setPath(path);
// Note Bounding Box size is not the same width or height as text (only used for finding center)
double bbX = datumLabel->boundingRect().width();
double bbY = datumLabel->boundingRect().height();
datumLabel->setTransformOriginPoint(bbX / 2, bbY /2);
double angleOption = 0.0; //put lblText angle adjustments here
datumLabel->setRotation((textRotAngle * 180 / M_PI) + angleOption);
if (strcmp(dimType, "DistanceY") == 0 ) {
datumLabel->setRotation(-90.0 + angleOption);
}
aHead1->setDirMode(true);
aHead2->setDirMode(true);
if (vp->FlipArrowheads.getValue()) {
aHead1->setDirection(a1Dir * -1.0);
aHead2->setDirection(a2Dir * -1.0);
} else {
aHead1->setDirection(a1Dir);
aHead2->setDirection(a2Dir);
}
aHead1->setStyle(QGIArrow::getPrefArrowStyle());
aHead1->setSize(QGIArrow::getPrefArrowSize());
aHead1->draw();
aHead2->setStyle(QGIArrow::getPrefArrowStyle());
aHead2->setSize(QGIArrow::getPrefArrowSize());
aHead2->draw();
aHead1->setPos(dim1Tip.x, dim1Tip.y);
aHead2->setPos(dim2Tip.x, dim2Tip.y);
aHead1->setDirMode(false);
aHead2->setDirMode(false);
} else if(strcmp(dimType, "Diameter") == 0) {
// terminology: Dimension Text, Dimension Line(s), Extension Lines, Arrowheads
Base::Vector3d arrow1Tip, arrow2Tip;
Base::Vector3d arrow1Tail, arrow2Tail;
Base::Vector3d arrow1Dir,arrow2Dir;
double radius;
Base::Vector3d pointOnCurve,curveCenter;
Base::Vector3d startExt1,endExt1,startExt2,endExt2;
arcPoints pts = dim->getArcPoints();
bool isArc = pts.isArc;
radius = Rez::guiX(pts.radius);
curveCenter = Rez::guiX(pts.center);
pointOnCurve = Rez::guiX(pts.onCurve.first);
Base::Vector3d startDist,endDist,dirDim;
startDist = Rez::guiX(pts.onCurve.first);
endDist = Rez::guiX(pts.onCurve.second);
dirDim = endDist - startDist;
if (fabs(dirDim.Length()) < Precision::Confusion()) {
dirDim = Base::Vector3d(1.0,0.0,0.0);
}
dirDim.Normalize();
Base::Vector3d adjustDir = dirDim; //adjust line lengths for arrowheads
Base::Vector3d fauxCenter = lblCenter;
//default arrow endpoints
double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(),
QGIArrow::getPrefArrowSize()));
arrow1Tip = curveCenter - dirDim * radius;
Base::Vector3d adjustedTip1 = arrow1Tip - adjustDir * dimLineAdjust;
arrow2Tip = curveCenter + dirDim * radius;
Base::Vector3d adjustedTip2 = arrow2Tip + adjustDir * dimLineAdjust;
arrow1Tail = curveCenter;
arrow2Tail = curveCenter;
double gapMargin = Rez::guiX(4.f);
margin = Rez::guiX(2.f);
float scaler = 1.;
double tip = (margin * scaler);
double gap = (gapMargin * scaler); //sb % of radius?
//offset of dimLine from getDimText()
double horizOffset = getDefaultTextHorizontalOffset(lblCenter.x > curveCenter.x ? -1.0 : +1.0);
double vertOffset = getDefaultTextVerticalOffset();
bool outerPlacement = false;
if ((lblCenter-curveCenter).Length() > radius) { //label is outside circle
outerPlacement = true;
}
// // Note Bounding Box size is not the same width or height as text (only used for finding center)
float bbX = datumLabel->boundingRect().width();
float bbY = datumLabel->boundingRect().height();
datumLabel->setTransformOriginPoint(bbX / 2, bbY /2);
int posMode = NoSnap;
bool isLeader = false;
QPainterPath path;
if(outerPlacement) {
Base::Vector3d v = (lblCenter - curveCenter);
double angle = atan2(v.y, v.x);
double tolerance = 15.0; //deg
tolerance *= M_PI / 180;
if( (angle > -tolerance && angle < tolerance) || //angle = 0 or 180 (+/- 15)
(angle > (M_PI - tolerance) || angle < (-M_PI + tolerance)) ) { //dim line is Horizontal
posMode = HorizontalSnap;
} else if( (angle < ( M_PI / 2. + tolerance) && angle > ( M_PI / 2. - tolerance)) || //angle 90/270
(angle < (-M_PI / 2. + tolerance) && angle > (-M_PI / 2. - tolerance)) ) { //Vertical
posMode = VerticalSnap;
}
//NOTE: VerticalSnap and Horizontal snap are confusing names.
// VerticalSnap => dim line is horizontal, dim text is directly above/below center
// HorizontalSnap => dim line is vertical, dim text is directly left/right of center.
if(posMode == VerticalSnap) {
QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect());
lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0);
if (lblCenter.y > curveCenter.y) {
fauxCenter = Base::Vector3d(lblCenter.x,lblCenter.y + vertOffset,lblCenter.z);
} else {
fauxCenter = Base::Vector3d(lblCenter.x,lblCenter.y + vertOffset,lblCenter.z);
tip = -tip;
gap = -gap;
}
arrow1Tip.x = curveCenter.x - radius; //to left, on circle cl
arrow1Tip.y = fauxCenter.y;
arrow1Tail.x = curveCenter.x;
arrow1Tail.y = arrow1Tip.y;
arrow1Dir = (arrow1Tip - arrow1Tail).Normalize();
adjustedTip1.x = arrow1Tip.x + dimLineAdjust;
adjustedTip1.y = arrow1Tip.y;
path.moveTo(arrow1Tail.x, arrow1Tail.y);
path.lineTo(adjustedTip1.x, adjustedTip1.y);
arrow2Tip.x = curveCenter.x + radius;
arrow2Tip.y = fauxCenter.y;
arrow2Tail.x = curveCenter.x;
arrow2Tail.y = arrow2Tip.y;
arrow2Dir = (arrow2Tip - arrow2Tail).Normalize();
adjustedTip2.x = arrow2Tip.x - dimLineAdjust;
adjustedTip2.y = arrow2Tip.y;
path.moveTo(arrow2Tail.x, arrow2Tail.y);
path.lineTo(adjustedTip2.x, adjustedTip2.y);
startExt1.x = curveCenter.x - radius;
startExt1.y = curveCenter.y + gap;
endExt1.x = startExt1.x;
endExt1.y = fauxCenter.y + tip;
startExt2.x = curveCenter.x + radius;
startExt2.y = curveCenter.y + gap;
endExt2.x = startExt2.x;
endExt2.y = fauxCenter.y + tip;
path.moveTo(startExt1.x, startExt1.y);
path.lineTo(endExt1.x, endExt1.y);
path.moveTo(startExt2.x, startExt2.y);
path.lineTo(endExt2.x, endExt2.y);
datumLabel->setRotation(0.0);
} else if(posMode == HorizontalSnap) {
QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect());
lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0);
if (lblCenter.x > curveCenter.x) { //label right
// fauxCenter = Base::Vector3d(lblCenter.x - horizOffset,lblCenter.y,lblCenter.z); //uniform convention
fauxCenter = Base::Vector3d(lblCenter.x + vertOffset,lblCenter.y,lblCenter.z); //aligned convention
} else { //label left
tip = -tip;
gap = -gap;
// fauxCenter = Base::Vector3d(lblCenter.x + horizOffset,lblCenter.y,lblCenter.z); //uniform
fauxCenter = Base::Vector3d(lblCenter.x + vertOffset,lblCenter.y,lblCenter.z); //aligned
}
arrow1Tip.x = fauxCenter.x;
arrow1Tip.y = curveCenter.y - radius;
arrow1Tail.x = arrow1Tip.x;
arrow1Tail.y = curveCenter.y;
arrow1Dir = (arrow1Tip - arrow1Tail).Normalize();
adjustedTip1.x = arrow1Tip.x;
adjustedTip1.y = arrow1Tip.y + dimLineAdjust;
path.moveTo(arrow1Tail.x, arrow1Tail.y);
path.lineTo(adjustedTip1.x, adjustedTip1.y);
arrow2Tip.x = fauxCenter.x;
arrow2Tip.y = curveCenter.y + radius;
arrow2Tail.x = arrow2Tip.x;
arrow2Tail.y = curveCenter.y;
arrow2Dir = (arrow2Tip - arrow2Tail).Normalize();
adjustedTip2.x = arrow2Tip.x;
adjustedTip2.y = arrow2Tip.y - dimLineAdjust;
path.moveTo(arrow2Tail.x, arrow2Tail.y);
path.lineTo(adjustedTip2.x, adjustedTip2.y);
startExt1.x = curveCenter.x + gap;
startExt1.y = curveCenter.y - radius;
endExt1.x = fauxCenter.x + tip;
endExt1.y = startExt1.y;
startExt2.x = curveCenter.x + gap;
startExt2.y = curveCenter.y + radius;
endExt2.x = fauxCenter.x + tip;
endExt2.y = startExt2.y;
path.moveTo(startExt1.x, startExt1.y);
path.lineTo(endExt1.x, endExt1.y);
path.moveTo(startExt2.x, startExt2.y);
path.lineTo(endExt2.x, endExt2.y);
datumLabel->setRotation(-90.0); //aligned convention
// datumLabel->setRotation(0.0); //unidirectional convention
} else { //outer placement, NoSnap (leader type)
QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect());
lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0);
isLeader = true;
arrow1Tail = lblCenter;
arrow1Tail.x += horizOffset;
Base::Vector3d kinkPoint = arrow1Tail;
kinkPoint.x += (lblCenter.x < curveCenter.x) ? margin : - margin;
arrow1Tip = curveCenter + (kinkPoint - curveCenter).Normalize() * radius;
adjustedTip1 = arrow1Tip + (kinkPoint - curveCenter).Normalize() * dimLineAdjust;
path.moveTo(arrow1Tail.x, arrow1Tail.y);
path.lineTo(kinkPoint.x, kinkPoint.y);
path.lineTo(adjustedTip1.x, adjustedTip1.y);
arrow1Dir = (arrow1Tip - kinkPoint).Normalize();
datumLabel->setRotation(0.);
}
} else { //NOT outerplacement ie dimLines are inside circle
double vertOffset = getDefaultTextVerticalOffset();
Base::Vector3d dirLabel = lblCenter - curveCenter;
if (dirLabel.Length() < Precision::Confusion()) {
dirLabel = Base::Vector3d(-1.0, 0.0, 0.0);
}
double labelAngle = 0.0;
if (vertOffset < dirLabel.Length()) {
double lineShiftAngle = asin(vertOffset/dirLabel.Length());
labelAngle = atan2(dirLabel.y, dirLabel.x);
if (labelAngle > 0.25*M_PI) {
labelAngle -= M_PI + lineShiftAngle;
}
else if (labelAngle < -0.75*M_PI) {
labelAngle += M_PI - lineShiftAngle;
}
else {
labelAngle += lineShiftAngle;
}
}
// Set the angle of the dimension text
datumLabel->setRotation(labelAngle*180/M_PI);
dirDim = Base::Vector3d(cos(labelAngle), sin(labelAngle), 0.0);
arrow1Tip = curveCenter - dirDim * radius;
adjustedTip1 = arrow1Tip + dirDim * dimLineAdjust;
arrow1Tail = curveCenter;
arrow1Dir = (arrow1Tip - arrow1Tail).Normalize();
arrow2Tip = curveCenter + dirDim * radius;
adjustedTip2 = arrow2Tip - dirDim * dimLineAdjust;
arrow2Tail = curveCenter;
arrow2Dir = (arrow2Tip - arrow2Tail).Normalize();
path.moveTo(arrow1Tail.x, arrow1Tail.y);
path.lineTo(adjustedTip1.x, adjustedTip1.y);
path.moveTo(arrow2Tail.x, arrow2Tail.y);
path.lineTo(adjustedTip2.x, adjustedTip2.y);
}
aHead1->setStyle(QGIArrow::getPrefArrowStyle());
aHead1->setSize(QGIArrow::getPrefArrowSize());
if (!isLeader) {
aHead2->setStyle(QGIArrow::getPrefArrowStyle());
aHead2->setSize(QGIArrow::getPrefArrowSize());
}
//handle partial arc weird cases
//TODO: does anybody dimension Arcs with Diameter? doesn't Radius make more sense?
Base::Vector3d dLineStart;
Base::Vector3d kinkPoint;
margin = Rez::guiX(5.f);
double kinkLength = Rez::guiX(5.0); //sb % of horizontal dist(lblCenter,curveCenter)???
QPainterPath arcPath;
if (isArc) {
arrow1Tail = lblCenter;
arrow1Tail.x += horizOffset;
Base::Vector3d kinkPoint = arrow1Tail;
kinkPoint.x += (lblCenter.x < curveCenter.x) ? margin : - margin;
if (lblCenter.x > curveCenter.x) { // label to right of vert c/l
dLineStart = Base::Vector3d(lblCenter.x + horizOffset,lblCenter.y,lblCenter.z);
kinkPoint = Base::Vector3d(dLineStart.x - kinkLength,dLineStart.y,dLineStart.z);
} else {
tip = -tip;
gap = -gap;
dLineStart = Base::Vector3d(lblCenter.x + horizOffset,lblCenter.y,lblCenter.z);
kinkPoint = Base::Vector3d(dLineStart.x + kinkLength,dLineStart.y,dLineStart.z);
}
dirDim = (kinkPoint - curveCenter).Normalize();
pointOnCurve = curveCenter + (dirDim * radius);
pointOnCurve = Rez::guiX(pts.midArc);
adjustedTip1 = pointOnCurve + (dirDim * dimLineAdjust);
arcPath.moveTo(dLineStart.x,dLineStart.y);
arcPath.lineTo(kinkPoint.x,kinkPoint.y);
arcPath.lineTo(adjustedTip1.x,adjustedTip1.y);
arrow1Dir = (arrow1Tip - kinkPoint).Normalize();
datumLabel->setRotation(0.);
}
dimLines->setPath(path);
if (isArc) {
dimLines->setPath(arcPath);
aHead1->setPos(pointOnCurve.x, pointOnCurve.y);
aHead1->setDirMode(true);
aHead1->setDirection(arrow1Dir);
aHead1->draw();
aHead1->show();
aHead2->hide();
} else if(outerPlacement) {
if(posMode > NoSnap) { // Horizontal or Vertical snap
aHead1->setPos(arrow1Tip.x, arrow1Tip.y);
aHead1->setDirMode(true);
aHead1->setDirection(arrow1Dir);
aHead1->draw();
aHead1->show();
aHead2->setPos(arrow2Tip.x, arrow2Tip.y);
aHead2->setDirMode(true);
aHead2->setDirection(arrow2Dir);
aHead2->draw();
aHead2->show();
} else { //leader stye
aHead1->setPos(arrow1Tip.x, arrow1Tip.y);
aHead1->setDirMode(true);
aHead1->setDirection(arrow1Dir);
aHead1->draw();
aHead1->show();
aHead2->hide(); //only 1 arrowhead for NoSnap + outerplacement (ie a leader)
}
} else { //inner placement
aHead1->setPos(arrow1Tip.x, arrow1Tip.y);
aHead1->setDirMode(true);
aHead1->setDirection(arrow1Dir);
aHead1->draw();
aHead1->show();
aHead2->setPos(arrow2Tip.x, arrow2Tip.y);
aHead2->setDirMode(true);
aHead2->setDirection(arrow2Dir);
aHead2->draw();
aHead2->show();
}
// code for centerMark being attribute of Dim instead of View
// if (dim->CentreLines.getValue()) {
// curveCenterMark->setPos(curveCenter.x,curveCenter.y);
// centerMark->show();
// dim->getViewPart()->addVertex(curveCenter,true);
// }
} else if(strcmp(dimType, "Radius") == 0) {
drawRadius(dim, vp);
} else if( (strcmp(dimType, "Angle") == 0) ||
(strcmp(dimType, "Angle3Pt") == 0)) {
anglePoints pts = dim->getAnglePoints();
Base::Vector3d X(1.0,0.0,0.0);
Base::Vector3d vertex = Rez::guiX(pts.vertex);
Base::Vector3d legEnd0 = Rez::guiX(pts.ends.first);
Base::Vector3d legEnd1 = Rez::guiX(pts.ends.second);
Base::Vector3d dir0 = legEnd0 - vertex;
Base::Vector3d dir1 = legEnd1 - vertex;
Base::Vector3d d0 = dir0;
d0.Normalize();
Base::Vector3d d1 = dir1;
d1.Normalize();
Base::Vector3d leg0 = dir0;
Base::Vector3d leg1 = dir1;
// Qt y coordinates are flipped
dir0.y *= -1.;
dir1.y *= -1.;
double insideAngle = dir0.GetAngle(dir1); // [0,PI]
double outsideAngle = 2*M_PI - insideAngle; // [PI,2PI]
Base::Vector3d tempCross = d0.Cross(d1);
double insideDir = tempCross.z;
bool ccwInner = true;
if (insideDir > 0.0) {
ccwInner = false;
}
//TODO: figure out the math for adjusting the arc so the tip doesn't overlap the arrowhead.
// double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(),
// QGIArrow::getPrefArrowSize()));
QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect());
lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0);
Base::Vector3d labelVec = (lblCenter - vertex); //dir from label to vertex
double textOffset = getDefaultTextVerticalOffset();
double radius = labelVec.Length() - textOffset;
QRectF arcRect(vertex.x - radius, vertex.y - radius, 2. * radius, 2. * radius);
Base::Vector3d ar0Pos = vertex + d0 * radius;
// Base::Vector3d adjustedTip0 = ar0Pos - d0 * dimLineAdjust;
Base::Vector3d ar1Pos = vertex + d1 * radius;
// Base::Vector3d adjustedTip1 = ar1Pos - d1 * dimLineAdjust;
double startangle = atan2(dir0.y,dir0.x);
if (startangle < 0) {
startangle += 2.0 * M_PI;
}
double endangle = atan2(dir1.y,dir1.x);
if (endangle < 0) {
endangle += 2.0 * M_PI;
}
Base::Vector3d startExt0 = legEnd0;
Base::Vector3d startExt1 = legEnd1;
// add an offset from the ends
double offsetFudge = 5.0;
startExt0 += d0 * offsetFudge;
startExt1 += d1 * offsetFudge;
// Draw the path
QPainterPath path;
// Only draw extension lines if outside arc
double extFudge = 5.0;
if(radius > (startExt0-vertex).Length()) {
path.moveTo(startExt0.x, startExt0.y);
Base::Vector3d endExt0 = ar0Pos + d0*Rez::guiX(extFudge);
path.lineTo(endExt0.x, endExt0.y);
}
if(radius > (startExt1-vertex).Length()) {
path.moveTo(startExt1.x, startExt1.y);
Base::Vector3d endExt1 = ar1Pos + d1*Rez::guiX(extFudge);
path.lineTo(endExt1.x, endExt1.y);
}
// Treat zero as positive to be consistent for horizontal lines
if(std::abs(startangle) < FLT_EPSILON)
startangle = 0;
if(std::abs(endangle) < FLT_EPSILON)
endangle = 0;
//https://stackoverflow.com/questions/13640931/how-to-determine-if-a-vector-is-between-two-other-vectors
bool isOutside = true;
if ( ((d0.Cross(labelVec)).Dot(d0.Cross(d1)) >= 0.0) &&
((d1.Cross(labelVec)).Dot(d1.Cross(d0)) >= 0.0) ) {
isOutside = false;
}
//dim line (arc) calculation is very different here.
//TODO: make arc a bit shorter to not overlap arrowheads. ends +/- x degrees.
path.arcMoveTo(arcRect, startangle * 180 / M_PI);
double actualSweep = 0.0;
m_obtuse = false;
if(isOutside) {
m_obtuse = true;
if (ccwInner) { //inner is ccw so outer is cw and sweep is -ve
actualSweep = -outsideAngle;
} else { //inner is cw so outer is ccw and sweep is +ve
actualSweep = outsideAngle;
}
} else {
if (ccwInner) { //inner is ccw and sweep is +ve
actualSweep = insideAngle;
} else { //inner is cw and sweep is -ve
actualSweep = -insideAngle;
}
}
path.arcTo(arcRect, startangle * 180 / M_PI, actualSweep*180.0/M_PI);
dimLines->setPath(path);
//NOTE: arrowheads are dirMode(false)
aHead1->setFlipped(true);
aHead1->setStyle(QGIArrow::getPrefArrowStyle());
aHead1->setSize(QGIArrow::getPrefArrowSize());
aHead1->draw();
aHead2->setStyle(QGIArrow::getPrefArrowStyle());
aHead2->setSize(QGIArrow::getPrefArrowSize());
aHead2->draw();
aHead1->setPos(ar0Pos.x,ar0Pos.y );
aHead2->setPos(ar1Pos.x,ar1Pos.y );
Base::Vector3d norm1 = leg0;
Base::Vector3d norm2 = leg1;
Base::Vector3d avg = (norm1 + norm2) / 2.; //midline of legs
norm1 = norm1.ProjectToLine(avg, norm1);
norm2 = norm2.ProjectToLine(avg, norm2);
float ar0angle = atan2(-norm1.y, -norm1.x) * 180 / M_PI;
float ar1angle = atan2(norm2.y, norm2.x) * 180 / M_PI;
if(isOutside) {
aHead1->setRotation(ar0angle + 180.);
aHead2->setRotation(ar1angle + 180.);
} else {
aHead1->setRotation(ar0angle);
aHead2->setRotation(ar1angle);
}
// Set the angle of the dimension text
Base::Vector3d labelNorm(-labelVec.y, labelVec.x, 0.);
double lAngle = atan2(labelNorm.y, labelNorm.x);
if (lAngle < 0.0) {
lAngle = 2 * M_PI + lAngle; //map to +ve lAngle
}
//orient text right side up
double angleFiddle = M_PI / 10.0; // 18 => 10*, 12 => 15*, 10 => 18*, ...
if ((lAngle > M_PI_2 + angleFiddle) && // > 100CW
(lAngle <= M_PI)) { // < 180CW -> Q2
lAngle += M_PI; // flip CW
} else if ((lAngle > M_PI) && // > 180CW
(lAngle <= 1.5*M_PI - angleFiddle)) { // < 260CW -> Q3
lAngle -= M_PI; // flip CCW
}
// //if label is more/less vertical, make it vertical
// if (lAngle > M_PI_2+M_PI/12) { // label norm angle > 90 + 15 = 105
// lAngle -= M_PI; // lAngle - 180 Flip
// } else if (lAngle <= -M_PI_2+M_PI/12) { // < -90 + 15 = - 85
// lAngle += M_PI; // langle + 180 Flip
// }
float bbX = datumLabel->boundingRect().width();
float bbY = datumLabel->boundingRect().height();
datumLabel->setTransformOriginPoint(bbX / 2., bbY /2.);
datumLabel->setRotation(lAngle * 180 / M_PI);
} //endif Distance/Diameter/Radius/Angle
//this is already handled in select() and hover()
// // redraw the Dimension and the parent View
// if (datumLabel->hasHover && !datumLabel->isSelected()) {
// setPrettyPre();
// } else if (datumLabel->isSelected()) {
// setPrettySel();
// } else {
// setPrettyNormal();
// }
update();
if (parentItem()) {
//TODO: parent redraw still required with new frame/label??
parentItem()->update();
} else {
Base::Console().Log("INFO - QGIVD::draw - no parent to update\n");
}
}
double QGIViewDimension::getIsoStandardLinePlacement(double labelAngle)
{
// According to ISO 129-1 Standard Figure 23, the bordering angle is 2/3 PI, resp. -1/3 PI
// As Qt Y axis points downwards, all signs are flipped
return labelAngle > +M_PI/3.0 || labelAngle < -2.0*M_PI/3.0
? -1.0 : +1.0;
}
double QGIViewDimension::computeLineAndLabelAngles(Base::Vector2d lineTarget, Base::Vector2d labelCenter,
double lineLabelDistance, double &lineAngle, double &labelAngle)
{
// By default horizontal line and no label rotation
lineAngle = 0.0;
labelAngle = 0.0;
Base::Vector2d rawDirection(labelCenter - lineTarget);
double rawDistance = rawDirection.Length();
if (rawDistance < Precision::Confusion()) { // Almost single point, can't tell
return 0.0;
}
double rawAngle = atan2(rawDirection.y, rawDirection.x);
lineAngle = rawAngle;
// If we are too close to the line origin, no further adjustments
if (lineLabelDistance >= rawDistance) {
return 0.0;
}
// Rotate the line by angle between the label rectangle center and label bottom side center
double devAngle = getIsoStandardLinePlacement(rawAngle)*asin(lineLabelDistance/rawDistance);
lineAngle = addAngles(lineAngle, devAngle);
labelAngle = devAngle > 0.0 ? lineAngle : addAngles(lineAngle, M_PI);
return devAngle;
}
bool QGIViewDimension::computeLineRectangleExitPoint(const QRectF &rectangle, Base::Vector2d targetPoint,
Base::Vector2d &exitPoint) {
if (targetPoint.x > rectangle.left() && targetPoint.x < rectangle.right()
&& targetPoint.y > rectangle.top() && targetPoint.y < rectangle.bottom()) {
// Target point is inside the rectangle - no crossing at all
return false;
}
Base::Vector2d lineOrigin(rectangle.center().x(), rectangle.center().y());
Base::Vector2d direction = targetPoint - lineOrigin;
if (fabs(direction.y) >= Precision::Confusion()) {
// The line is not parallel with X axis
exitPoint.y = direction.y < 0 ? rectangle.top() : rectangle.bottom();
exitPoint.x = lineOrigin.x + direction.x*(exitPoint.y - lineOrigin.y)/direction.y;
if (exitPoint.x >= rectangle.left() && exitPoint.x <= rectangle.right()) {
return true;
}
}
if (fabs(direction.x) >= Precision::Confusion()) {
// The line is not parallel with Y axis
exitPoint.x = direction.x < 0 ? rectangle.left() : rectangle.right();
exitPoint.y = lineOrigin.y + direction.y*(exitPoint.x - lineOrigin.x)/direction.x;
if (exitPoint.y >= rectangle.top() && exitPoint.y <= rectangle.bottom()) {
return true;
}
}
return false;
}
Base::Vector2d QGIViewDimension::computeLineOriginPoint(Base::Vector2d lineTarget, double projectedLabelDistance,
double lineAngle, double labelWidth, double direction) const
{
return lineTarget + (projectedLabelDistance + direction*(0.5*labelWidth + getDefaultReferenceLineOverhang()))
*Base::Vector2d(cos(lineAngle), sin(lineAngle));
}
Base::Vector2d QGIViewDimension::getIsoJointPoint(Base::Vector2d labelCenter, double width, double dir) const
{
return Base::Vector2d(labelCenter.x + dir*(width*0.5 + getDefaultReferenceLineOverhang()),
labelCenter.y + getDefaultTextVerticalOffset());
}
Base::Vector2d QGIViewDimension::getAsmeJointPoint(Base::Vector2d labelCenter, double width, double dir) const
{
return Base::Vector2d(labelCenter.x + dir*(width*0.5 + getDefaultHorizontalLeaderLength()),
labelCenter.y + TextOffsetFudge);
}
void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const
{
// Preferred terminology according to ISO 129-1 for Radius:
// Dimensional Value, Leader Line, Reference Line, Terminator
QPainterPath radiusPath;
datumLabel->setRotation(0.0);
aHead1->setRotation(0.0);
aHead1->setFlipped(false);
QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect());
Base::Vector2d labelCenter = Base::Vector2d(mappedRect.center().x(), mappedRect.center().y());
arcPoints curvePoints = dimension->getArcPoints();
Base::Vector2d curveCenter = Rez::guiX(curvePoints.center, true);
double mappedRadius = Rez::guiX(curvePoints.radius);
double centerDistance = (labelCenter - curveCenter).Length();
double arcStartAngle;
double arcEndAngle;
bool arcClockwise;
if (curvePoints.isArc) {
arcStartAngle = atan2(curvePoints.arcEnds.first.y - curvePoints.center.y,
curvePoints.arcEnds.first.x - curvePoints.center.x);
arcEndAngle = atan2(curvePoints.arcEnds.second.y - curvePoints.center.y,
curvePoints.arcEnds.second.x - curvePoints.center.x);
arcClockwise = !curvePoints.arcCW;
}
else { // A circle arc covers the whole plane
arcStartAngle = -M_PI;
arcEndAngle = +M_PI;
arcClockwise = false;
}
double labelAngle = 0.0;
Base::Vector2d arcPoint;
double lineAngle;
int standardStyle = viewProvider->StandardAndStyle.getValue();
if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_LEVELLED
|| standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) {
// The dimensional value text must stay horizontal
Base::Vector2d leftJoint, rightJoint;
if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_LEVELLED) {
leftJoint = getIsoJointPoint(labelCenter, mappedRect.width(), -1.0);
rightJoint = getIsoJointPoint(labelCenter, mappedRect.width(), +1.0);
}
else {
leftJoint = getAsmeJointPoint(labelCenter, mappedRect.width(), -1.0);
rightJoint = getAsmeJointPoint(labelCenter, mappedRect.width(), +1.0);
}
double leftAngle = atan2(leftJoint.y - curveCenter.y, leftJoint.x - curveCenter.x);
double rightAngle = atan2(rightJoint.y - curveCenter.y, rightJoint.x - curveCenter.x);
int leftPosition = classifyPointToArcPosition((leftJoint - curveCenter).Length(),
leftAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise);
int rightPosition = classifyPointToArcPosition((rightJoint - curveCenter).Length(),
rightAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise);
Base::Vector2d originPoint;
Base::Vector2d jointPoint;
Base::Vector2d targetPoint;
if (leftPosition <= OPPOSITE_SECTOR || rightPosition <= OPPOSITE_SECTOR) {
// At least from one of the reference line sides can run the leader line
// perpendicularly to the arc, i.e. in direction to the center
if (leftPosition <= OPPOSITE_SECTOR && rightPosition <= OPPOSITE_SECTOR) {
// Both are acceptable, so choose the more convenient one
double leftBend = leftPosition == INNER_SECTOR ? M_PI - fabs(leftAngle) : fabs(leftAngle);
double rightBend = rightPosition == INNER_SECTOR ? fabs(rightAngle) : M_PI - fabs(rightAngle);
// If right leader line bends less or does not cross the dimensional value,
// use it by marking left point as outlayer
if (leftBend <= M_PI_2 || rightBend <= M_PI_2
|| standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) {
// Either at least one line does not cross the text, or it is an ASME connection
// (vertically centered), which behaves the same going up or down
if (rightBend < leftBend) {
leftPosition = COMPLEMENT_SECTOR;
}
}
else { // ISO connection, but crosses the value - try to find the one pointing down (if exists)
bool leftDown = leftPosition == INNER_SECTOR ? leftAngle > 0.0 : leftAngle < 0.0;
bool rightDown = rightPosition == INNER_SECTOR ? rightAngle > 0.0 : rightAngle < 0.0;
if (leftDown == rightDown) { // Both lines go downwards or upwards
if (rightBend < leftBend) {
leftPosition = COMPLEMENT_SECTOR;
}
}
else if (rightDown) {
leftPosition = COMPLEMENT_SECTOR;
}
}
}
int resultPosition;
if (leftPosition <= OPPOSITE_SECTOR) {
if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) {
originPoint = Base::Vector2d(labelCenter.x + getDefaultTextHorizontalOffset(-1.0),
labelCenter.y + TextOffsetFudge);
}
else {
originPoint = rightJoint;
}
jointPoint = leftJoint;
lineAngle = leftAngle;
resultPosition = leftPosition;
}
else {
if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) {
originPoint = Base::Vector2d(labelCenter.x + getDefaultTextHorizontalOffset(+1.0),
labelCenter.y + TextOffsetFudge);
}
else {
originPoint = leftJoint;
}
jointPoint = rightJoint;
lineAngle = rightAngle;
resultPosition = rightPosition;
}
switch (resultPosition) {
case INNER_SECTOR:
arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
targetPoint = arcPoint;
break;
case OUTER_SECTOR:
arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
// If desired, extend the target point to the center
if (viewProvider->ExtendToCenter.getValue()) {
targetPoint = curveCenter;
if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_LEVELLED) {
aHead1->flip();
}
}
else {
targetPoint = arcPoint;
aHead1->flip();
}
break;
case OPPOSITE_SECTOR:
arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
targetPoint = arcPoint;
aHead1->flip();
break;
}
}
else { // Both joint points lay outside the vertical angles
arcPoint = Rez::guiX(curvePoints.midArc, true);
if (labelCenter.x >= arcPoint.x) { // Place the dimensional value right
if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) {
originPoint = Base::Vector2d(mappedRect.left(), labelCenter.y);
}
else {
originPoint = rightJoint;
}
jointPoint = leftJoint;
}
else { // Place the dimensional value left
if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) {
originPoint = Base::Vector2d(mappedRect.right(), labelCenter.y);
}
else {
originPoint = leftJoint;
}
jointPoint = rightJoint;
}
targetPoint = arcPoint;
lineAngle = atan2(targetPoint.y - jointPoint.y, targetPoint.x - jointPoint.x);
}
radiusPath.moveTo(originPoint.x, originPoint.y);
radiusPath.lineTo(jointPoint.x, jointPoint.y);
radiusPath.lineTo(targetPoint.x, targetPoint.y);
}
else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) {
// We may rotate the label so no reference line is needed
double devAngle = computeLineAndLabelAngles(curveCenter, labelCenter,
getDefaultTextVerticalOffset(), lineAngle, labelAngle);
// Correct the label center distance projected on the leader line
centerDistance *= cos(devAngle);
Base::Vector2d originPoint;
Base::Vector2d targetPoint;
switch (classifyPointToArcPosition(centerDistance, lineAngle, mappedRadius,
arcStartAngle, arcEndAngle, arcClockwise)) {
case INNER_SECTOR: {
// The label is placed within the arc sector angle, there's always point
// on the arc where the leader line can cross it perpendicularly
arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
if (viewProvider->ExtendToCenter.getValue()) { // Start in the very center
originPoint = curveCenter;
}
else { // Start on the label side closer to the center
originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle,
mappedRect.width(), -1.0);
}
targetPoint = arcPoint;
break;
}
case OUTER_SECTOR: {
// Same situation as when on the inner side of sector
arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
aHead1->flip();
originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle,
mappedRect.width(), +1.0);
// If leader line shall not be extended to the center, start on the arc projection
targetPoint = viewProvider->ExtendToCenter.getValue() ? curveCenter : arcPoint;
break;
}
case OPPOSITE_SECTOR: {
// If the label is placed within the vertically opposite angle of the arc sector,
// the leader line passing through the arc center can mark a point on the arc
arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
aHead1->flip();
originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle,
mappedRect.width(), +1.0);
targetPoint = arcPoint;
break;
}
default: {
// Label outside both arc wedges
arcPoint = Rez::guiX(curvePoints.midArc, true);
aHead1->flip();
devAngle = computeLineAndLabelAngles(arcPoint, labelCenter,
getDefaultTextVerticalOffset(), lineAngle, labelAngle);
centerDistance = (labelCenter - arcPoint).Length()*cos(devAngle);
originPoint = computeLineOriginPoint(arcPoint, centerDistance, lineAngle,
mappedRect.width(), +1.0);
targetPoint = arcPoint;
break;
}
}
// Draw only the leader line from start point to end point
radiusPath.moveTo(originPoint.x, originPoint.y);
radiusPath.lineTo(targetPoint.x, targetPoint.y);
}
else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) {
// Text must remain horizontal, but it may split the leader line
Base::Vector2d lineDirection(labelCenter - curveCenter);
lineAngle = atan2(lineDirection.y, lineDirection.x);
Base::Vector2d exitPoint;
switch (classifyPointToArcPosition(centerDistance, lineAngle, mappedRadius,
arcStartAngle, arcEndAngle, arcClockwise)) {
case INNER_SECTOR: {
// The label is placed within the arc sector angle, there's always point
// on the arc where the leader line can cross it perpendicularly
arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) {
radiusPath.moveTo(exitPoint.x, exitPoint.y);
radiusPath.lineTo(arcPoint.x, arcPoint.y);
}
if (viewProvider->ExtendToCenter.getValue()
&& computeLineRectangleExitPoint(mappedRect, curveCenter, exitPoint)) {
radiusPath.moveTo(exitPoint.x, exitPoint.y);
radiusPath.lineTo(curveCenter.x, curveCenter.y);
}
break;
}
case OUTER_SECTOR: {
// Same situation as when on the inner side of sector
arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
Base::Vector2d targetPoint(viewProvider->ExtendToCenter.getValue()
? curveCenter : arcPoint);
if (computeLineRectangleExitPoint(mappedRect, targetPoint, exitPoint)) {
radiusPath.moveTo(exitPoint.x, exitPoint.y);
radiusPath.lineTo(targetPoint.x, targetPoint.y);
}
if (!viewProvider->ExtendToCenter.getValue()) {
aHead1->flip();
}
break;
}
case OPPOSITE_SECTOR: {
// If the label is placed within the vertically opposite angle of the arc sector,
// the leader line passing through the arc center can mark a point on the arc
arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle));
if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) {
radiusPath.moveTo(exitPoint.x, exitPoint.y);
radiusPath.lineTo(arcPoint.x, arcPoint.y);
}
aHead1->flip();
break;
}
default: {
// Label outside both arc wedges
arcPoint = Rez::guiX(curvePoints.midArc, true);
lineDirection = labelCenter - arcPoint;
lineAngle = atan2(lineDirection.y, lineDirection.x);
if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) {
radiusPath.moveTo(exitPoint.x, exitPoint.y);
radiusPath.lineTo(arcPoint.x, arcPoint.y);
}
aHead1->flip();
break;
}
}
}
else {
Base::Console().Error("QGIVD::drawRadius - this Standard&Style is not supported: %d\n", standardStyle);
return;
}
datumLabel->setTransformOriginPoint(datumLabel->boundingRect().width()*0.5,
datumLabel->boundingRect().height()*0.5);
datumLabel->setRotation(labelAngle*180.0/M_PI);
dimLines->setPath(radiusPath);
aHead1->setPos(arcPoint.x, arcPoint.y);
aHead1->setDirMode(true);
aHead1->setDirection(lineAngle);
if (viewProvider->FlipArrowheads.getValue()) {
aHead1->flip();
}
aHead1->setStyle(QGIArrow::getPrefArrowStyle());
aHead1->setSize(QGIArrow::getPrefArrowSize());
aHead1->draw();
aHead1->show();
aHead2->hide();
}
QColor QGIViewDimension::getNormalColor()
{
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
App::Color fcColor;
fcColor.setPackedValue(hGrp->GetUnsigned("Color", 0x00000000));
m_colNormal = fcColor.asValue<QColor>();
auto dim( dynamic_cast<TechDraw::DrawViewDimension*>(getViewObject()) );
if( dim == nullptr )
return m_colNormal;
auto vp = static_cast<ViewProviderDimension*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return m_colNormal;
}
m_colNormal = vp->Color.getValue().asValue<QColor>();
return m_colNormal;
}
//! find the closest isometric axis given an ortho vector
Base::Vector3d QGIViewDimension::findIsoDir(Base::Vector3d ortho)
{
std::vector<Base::Vector3d> isoDirs = { Base::Vector3d(0.866,0.5,0.0), //iso X
Base::Vector3d(-0.866,-0.5,0.0), //iso -X
Base::Vector3d(-0.866,0.5,0.0), //iso -Y?
Base::Vector3d(0.866,-0.5,0.0), //iso +Y?
Base::Vector3d(0.0,-1.0,0.0), //iso -Z
Base::Vector3d(0.0,1.0,0.0) }; //iso Z
std::vector<double> angles;
for (auto& iso: isoDirs) {
angles.push_back(ortho.GetAngle(iso));
}
int idx = 0;
double min = angles[0];
for (int i = 1; i < 6; i++) {
if (angles[i] < min) {
idx = i;
min = angles[i];
}
}
return isoDirs[idx];
}
//! find the iso extension direction corresponding to an iso dist direction
Base::Vector3d QGIViewDimension::findIsoExt(Base::Vector3d dir)
{
Base::Vector3d dirExt(1,0,0);
Base::Vector3d isoX(0.866,0.5,0.0); //iso X
Base::Vector3d isoXr(-0.866,-0.5,0.0); //iso -X
Base::Vector3d isoY(-0.866,0.5,0.0); //iso -Y?
Base::Vector3d isoYr(0.866,-0.5,0.0); //iso +Y?
Base::Vector3d isoZ(0.0,1.0,0.0); //iso Z
Base::Vector3d isoZr(0.0,-1.0,0.0); //iso -Z
if (dir.IsEqual(isoX, FLT_EPSILON)) {
dirExt = isoY;
} else if (dir.IsEqual(-isoX, FLT_EPSILON)) {
dirExt = -isoY;
} else if (dir.IsEqual(isoY, FLT_EPSILON)) {
dirExt = isoZ;
} else if (dir.IsEqual(-isoY, FLT_EPSILON)) {
dirExt = -isoZ;
} else if (dir.IsEqual(isoZ, FLT_EPSILON)) {
dirExt = isoX;
} else if (dir.IsEqual(-isoZ, FLT_EPSILON)) {
dirExt = -isoX;
} else { //tarfu
Base::Console().Message("QGIVD::findIsoExt - %s - input is not iso axis\n", getViewObject()->getNameInDocument());
}
return dirExt;
}
void QGIViewDimension::setPrettyPre(void)
{
aHead1->setPrettyPre();
aHead2->setPrettyPre();
dimLines->setPrettyPre();
}
void QGIViewDimension::setPrettySel(void)
{
aHead1->setPrettySel();
aHead2->setPrettySel();
dimLines->setPrettySel();
}
void QGIViewDimension::setPrettyNormal(void)
{
aHead1->setPrettyNormal();
aHead2->setPrettyNormal();
dimLines->setPrettyNormal();
}
void QGIViewDimension::drawBorder(void)
{
//Dimensions have no border!
// Base::Console().Message("TRACE - QGIViewDimension::drawBorder - doing nothing!\n");
}
const double QGIViewDimension::TextOffsetFudge = 2.0;
double QGIViewDimension::getDefaultTextHorizontalOffset(double direction) const
{
return direction*(datumLabel->boundingRect().width()*0.5 + TextOffsetFudge*2.0);
}
double QGIViewDimension::getDefaultTextVerticalOffset() const
{
TechDraw::DrawViewDimension *dim = dynamic_cast<TechDraw::DrawViewDimension *>(getViewObject());
ViewProviderDimension *vp = static_cast<ViewProviderDimension *>(getViewProvider(getViewObject()));
double textMult = 1.0;
if (dim->hasTolerance()) {
textMult += datumLabel->getTolAdjust();
}
return textMult*Rez::guiX(vp->Fontsize.getValue()) + TextOffsetFudge;
}
double QGIViewDimension::getDefaultReferenceLineOverhang() const
{
return 2.0*TextOffsetFudge;
}
double QGIViewDimension::getDefaultHorizontalLeaderLength() const
{
QFontMetrics fontMetrics(datumLabel->getFont());
return 1.5*fontMetrics.width(QChar::fromLatin1('M'));
}
bool QGIViewDimension::angleWithinSector(double testAngle, double startAngle, double endAngle, bool clockwise)
{
if (clockwise) {
std::swap(startAngle, endAngle);
}
if (endAngle < startAngle) {
endAngle += M_2PI;
}
if (testAngle < startAngle) {
testAngle += M_2PI;
}
return testAngle <= endAngle;
}
double QGIViewDimension::addAngles(double angle1, double angle2)
{
angle1 += angle2;
if (angle2 >= 0.0) {
if (angle1 > +M_PI) angle1 -= M_2PI;
return angle1;
}
else {
if (angle1 < -M_PI) angle1 += M_2PI;
return angle1;
}
}
int QGIViewDimension::classifyPointToArcPosition(double pointDistance, double pointAngle,
double radius, double startAngle, double endAngle, bool clockwise)
{
if (angleWithinSector(pointAngle, startAngle, endAngle, clockwise)) {
return pointDistance > radius ? OUTER_SECTOR : INNER_SECTOR;
}
if (angleWithinSector(addAngles(pointAngle, M_PI), startAngle, endAngle, clockwise)) {
return OPPOSITE_SECTOR;
}
return COMPLEMENT_SECTOR;
}
//frame, border, caption are never shown in QGIVD, so shouldn't be in bRect
QRectF QGIViewDimension::boundingRect() const
{
QRectF labelRect = mapFromItem(datumLabel, datumLabel->boundingRect()).boundingRect();
QRectF linesRect = mapFromItem(dimLines, dimLines->boundingRect()).boundingRect();
QRectF aHead1Rect = mapFromItem(aHead1, aHead1->boundingRect()).boundingRect();
QRectF aHead2Rect = mapFromItem(aHead2, aHead2->boundingRect()).boundingRect();
QRectF result(labelRect);
result = result.united(linesRect);
result = result.united(aHead1Rect);
result = result.united(aHead2Rect);
return result;
}
void QGIViewDimension::paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) {
QStyleOptionGraphicsItem myOption(*option);
myOption.state &= ~QStyle::State_Selected;
QPaintDevice* hw = painter->device();
QSvgGenerator* svg = dynamic_cast<QSvgGenerator*>(hw);
setPens();
//double arrowSaveWidth = aHead1->getWidth();
if (svg) {
setSvgPens();
} else {
setPens();
}
// painter->drawRect(boundingRect()); //good for debugging
// QGIView::paint (painter, &myOption, widget);
QGraphicsItemGroup::paint(painter, &myOption, widget);
setPens();
}
void QGIViewDimension::setSvgPens(void)
{
double svgLineFactor = 3.0; //magic number. should be a setting somewhere.
dimLines->setWidth(m_lineWidth/svgLineFactor);
aHead1->setWidth(aHead1->getWidth()/svgLineFactor);
aHead2->setWidth(aHead2->getWidth()/svgLineFactor);
}
void QGIViewDimension::setPens(void)
{
dimLines->setWidth(m_lineWidth);
aHead1->setWidth(m_lineWidth);
aHead2->setWidth(m_lineWidth);
}
#include <Mod/TechDraw/Gui/moc_QGIViewDimension.cpp>