diff --git a/src/Mod/TechDraw/App/AppTechDrawPy.cpp b/src/Mod/TechDraw/App/AppTechDrawPy.cpp index 3330332779..a8051489e9 100644 --- a/src/Mod/TechDraw/App/AppTechDrawPy.cpp +++ b/src/Mod/TechDraw/App/AppTechDrawPy.cpp @@ -62,6 +62,7 @@ #include "DrawUtil.h" #include "DrawViewAnnotation.h" #include "DrawViewDimension.h" +#include "DimensionGeometry.h" #include "DrawViewPart.h" #include "DrawViewPartPy.h" #include "EdgeWalker.h" @@ -725,13 +726,13 @@ private: Base::Vector3d textLocn(dvd->X.getValue() + parentX, dvd->Y.getValue() + parentY, 0.0); Base::Vector3d lineLocn(dvd->X.getValue() + parentX, dvd->Y.getValue() + parentY, 0.0); pointPair pts = dvd->getLinearPoints(); - Base::Vector3d dimLine = pts.first - pts.second; + Base::Vector3d dimLine = pts.first() - pts.second(); Base::Vector3d norm(-dimLine.y, dimLine.x, 0.0); norm.Normalize(); lineLocn = lineLocn + (norm * gap); - Base::Vector3d extLine1Start = Base::Vector3d(pts.first.x, -pts.first.y, 0.0) + + Base::Vector3d extLine1Start = Base::Vector3d(pts.first().x, - pts.first().y, 0.0) + Base::Vector3d(parentX, parentY, 0.0); - Base::Vector3d extLine2Start = Base::Vector3d(pts.second.x, -pts.second.y, 0.0) + + Base::Vector3d extLine2Start = Base::Vector3d(pts.second().x, - pts.second().y, 0.0) + Base::Vector3d(parentX, parentY, 0.0); if (dvd->Type.isValue("DistanceX") ) { type = 1; @@ -743,12 +744,12 @@ private: Base::Vector3d textLocn(dvd->X.getValue() + parentX, dvd->Y.getValue() + parentY, 0.0); Base::Vector3d lineLocn(dvd->X.getValue() + parentX, dvd->Y.getValue() + parentY, 0.0); anglePoints pts = dvd->getAnglePoints(); - Base::Vector3d end1 = pts.ends.first; + Base::Vector3d end1 = pts.first(); end1.y = -end1.y; - Base::Vector3d end2 = pts.ends.second; + Base::Vector3d end2 = pts.second(); end2.y = -end2.y; - Base::Vector3d apex = pts.vertex; + Base::Vector3d apex = pts.vertex(); apex.y = -apex.y; apex = apex + parentPos; @@ -766,7 +767,7 @@ private: Base::Vector3d center = pts.center; center.y = -center.y; center = center + parentPos; - Base::Vector3d lineDir = (arrowPts.first - arrowPts.second).Normalize(); + Base::Vector3d lineDir = (arrowPts.first() - arrowPts.second()).Normalize(); Base::Vector3d arcPoint = center + lineDir * pts.radius; writer.exportRadialDim(center, textLocn, arcPoint, dimText); } else if(dvd->Type.isValue("Diameter")){ @@ -776,7 +777,7 @@ private: Base::Vector3d center = pts.center; center.y = -center.y; center = center + parentPos; - Base::Vector3d lineDir = (arrowPts.first - arrowPts.second).Normalize(); + Base::Vector3d lineDir = (arrowPts.first() - arrowPts.second()).Normalize(); Base::Vector3d end1 = center + lineDir * pts.radius; Base::Vector3d end2 = center - lineDir * pts.radius; writer.exportDiametricDim(textLocn, end1, end2, dimText); diff --git a/src/Mod/TechDraw/App/CMakeLists.txt b/src/Mod/TechDraw/App/CMakeLists.txt index a74b6ce93a..8eb26da5f9 100644 --- a/src/Mod/TechDraw/App/CMakeLists.txt +++ b/src/Mod/TechDraw/App/CMakeLists.txt @@ -108,6 +108,12 @@ SET(Draw_SRCS DrawViewDimExtent.h LandmarkDimension.cpp LandmarkDimension.h + DimensionGeometry.cpp + DimensionGeometry.h + DimensionReferences.cpp + DimensionReferences.h + DimensionFormatter.cpp + DimensionFormatter.h DrawViewBalloon.cpp DrawViewBalloon.h DrawViewSection.cpp diff --git a/src/Mod/TechDraw/App/DimensionFormatter.cpp b/src/Mod/TechDraw/App/DimensionFormatter.cpp new file mode 100644 index 0000000000..d03143c7cc --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionFormatter.cpp @@ -0,0 +1,408 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan * + * * + * 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" +#include +#ifndef _PreComp_ +#endif + +#include +#include + +#include "Preferences.h" +#include "DrawViewDimension.h" +#include "DimensionFormatter.h" + +using namespace TechDraw; + +bool DimensionFormatter::isMultiValueSchema() const +{ + bool angularMeasure = (m_dimension->Type.isValue("Angle") || + m_dimension->Type.isValue("Angle3Pt")); + + if (Base::UnitsApi::isMultiUnitAngle() && + angularMeasure) { + return true; + } else if (Base::UnitsApi::isMultiUnitLength() && + !angularMeasure) { + return true; + } + return false; +} + +//partial = 0 return the unaltered user string from the Units subsystem +//partial = 1 return value formatted according to the format spec and preferences for +// useAltDecimals and showUnits +//partial = 2 return only the unit of measure +std::string DimensionFormatter::formatValue(qreal value, + QString qFormatSpec, + int partial, + bool isDim) +{ + bool angularMeasure = false; + QLocale loc; + + Base::Quantity asQuantity; + asQuantity.setValue(value); + if ( (m_dimension->Type.isValue("Angle")) || + (m_dimension->Type.isValue("Angle3Pt")) ) { + angularMeasure = true; + asQuantity.setUnit(Base::Unit::Angle); + } else { + asQuantity.setUnit(Base::Unit::Length); + } + + QString qUserString = asQuantity.getUserString(); // this handles mm to inch/km/parsec etc + // and decimal positions but won't give more than + // Global_Decimals precision + + //get formatSpec prefix/suffix/specifier + QStringList qsl = getPrefixSuffixSpec(qFormatSpec); + QString formatPrefix = qsl[0]; //FormatSpec prefix + QString formatSuffix = qsl[1]; //FormatSpec suffix + QString formatSpecifier = qsl[2]; //FormatSpec specifier + + QString qMultiValueStr; + QString qBasicUnit = Base::Tools::fromStdString(Base::UnitsApi::getBasicLengthUnit()); + + QString formattedValue; + if (isMultiValueSchema() && partial == 0) { + //handle multi value schemes (yd/ft/in, dms, etc). don't even try to use Alt Decimals or hide units + qMultiValueStr = formatPrefix + qUserString + formatSuffix; + return qMultiValueStr.toStdString(); + } else { + //not multivalue schema + if (formatSpecifier.isEmpty()) { + Base::Console().Warning("Warning - no numeric format in Format Spec %s - %s\n", + qPrintable(qFormatSpec), m_dimension->getNameInDocument()); + return Base::Tools::toStdString(qFormatSpec); + } + + // for older TD drawings the formatSpecifier "%g" was used, but the number of decimals was + // neverheless limited. To keep old drawings, we limit the number of decimals too + // if the TD preferences option to use the global decimal number is set + // the formatSpecifier can have a prefix and/or suffix + if (m_dimension->useDecimals() && formatSpecifier.contains(QString::fromLatin1("%g"), Qt::CaseInsensitive)) { + int globalPrecision = Base::UnitsApi::getDecimals(); + // change formatSpecifier to e.g. "%.2f" + QString newSpecifier = QString::fromStdString("%." + std::to_string(globalPrecision) + "f"); + formatSpecifier.replace(QString::fromLatin1("%g"), newSpecifier, Qt::CaseInsensitive); + } + + // since we are not using a multiValueSchema, we know that angles are in '°' and for + // lengths we can get the unit of measure from UnitsApi::getBasicLengthUnit. + + // TODO: check the weird schemas (MKS, Imperial1)that report different UoM + // for different values + + // get value in the base unit with default decimals + // for the conversion we use the same method as in DlgUnitsCalculator::valueChanged + // get the conversion factor for the unit + // the result is now just val / convertValue because val is always in the base unit + // don't do this for angular values since they are not in the BaseLengthUnit + double userVal; + if (angularMeasure) { + userVal = asQuantity.getValue(); + qBasicUnit = QString::fromUtf8("°"); + } else { + double convertValue = Base::Quantity::parse(QString::fromLatin1("1") + qBasicUnit).getValue(); + userVal = asQuantity.getValue() / convertValue; + } + + if (isTooSmall(userVal, formatSpecifier)) { + Base::Console().Warning("Dimension value is too small for format specifier: %s\n", qPrintable(formatSpecifier)); + } + + formattedValue = formatValueToSpec(userVal, formatSpecifier); + + // replace decimal sign if necessary + QChar dp = QChar::fromLatin1('.'); + if (loc.decimalPoint() != dp) { + formattedValue.replace(dp, loc.decimalPoint()); + } + } + + //formattedValue is now in formatSpec format with local decimal separator + std::string formattedValueString = formattedValue.toStdString(); + if (partial == 0) { //prefix + unit subsystem string + suffix + return Base::Tools::toStdString(formatPrefix) + + Base::Tools::toStdString(qUserString) + + Base::Tools::toStdString(formatSuffix); + } else if (partial == 1) { // prefix number[unit] suffix + if (angularMeasure) { + //always insert unit after value + return Base::Tools::toStdString(formatPrefix) + + formattedValueString + "°" + + Base::Tools::toStdString(formatSuffix); + } else if (m_dimension->showUnits()){ + if (isDim && m_dimension->haveTolerance()) { + //unit will be included in tolerance so don't repeat it here + return Base::Tools::toStdString(formatPrefix) + + formattedValueString + + Base::Tools::toStdString(formatSuffix); + } else { + //no tolerance, so we need to include unit + return Base::Tools::toStdString(formatPrefix) + + formattedValueString + " " + + Base::Tools::toStdString(qBasicUnit) + + Base::Tools::toStdString(formatSuffix); + } + } else { + //showUnits is false + return Base::Tools::toStdString(formatPrefix) + + formattedValueString + + Base::Tools::toStdString(formatSuffix); + } + } else if (partial == 2) { // just the unit + if (angularMeasure) { + return Base::Tools::toStdString(qBasicUnit); + } else if (m_dimension->showUnits()) { + return Base::Tools::toStdString(qBasicUnit); + } else { + return ""; + } + } + + return formattedValueString; +} + +std::string DimensionFormatter::getFormattedToleranceValue(int partial) +{ + QString FormatSpec = QString::fromUtf8(m_dimension->FormatSpecOverTolerance.getStrValue().data()); + QString ToleranceString; + + if (m_dimension->ArbitraryTolerances.getValue()) + ToleranceString = FormatSpec; + else + ToleranceString = QString::fromUtf8(formatValue(m_dimension->OverTolerance.getValue(), + FormatSpec, + partial, + false).c_str()); + + return ToleranceString.toStdString(); +} + +//get over and under tolerances +std::pair DimensionFormatter::getFormattedToleranceValues(int partial) +{ + QString underFormatSpec = QString::fromUtf8(m_dimension->FormatSpecUnderTolerance.getStrValue().data()); + QString overFormatSpec = QString::fromUtf8(m_dimension->FormatSpecOverTolerance.getStrValue().data()); + std::pair tolerances; + QString underTolerance, overTolerance; + + if (m_dimension->ArbitraryTolerances.getValue()) { + underTolerance = underFormatSpec; + overTolerance = overFormatSpec; + } else { + if (DrawUtil::fpCompare(m_dimension->UnderTolerance.getValue(), 0.0)) { + underTolerance = QString::fromUtf8(formatValue(m_dimension->UnderTolerance.getValue(), + QString::fromUtf8("%.0f"), + partial, + false).c_str()); + } + else { + underTolerance = QString::fromUtf8(formatValue(m_dimension->UnderTolerance.getValue(), + underFormatSpec, + partial, + false).c_str()); + } + if (DrawUtil::fpCompare(m_dimension->OverTolerance.getValue(), 0.0)) { + overTolerance = QString::fromUtf8(formatValue(m_dimension->OverTolerance.getValue(), + QString::fromUtf8("%.0f"), + partial, + false).c_str()); + } + else { + overTolerance = QString::fromUtf8(formatValue(m_dimension->OverTolerance.getValue(), + overFormatSpec, + partial, + false).c_str()); + } + } + + tolerances.first = underTolerance.toStdString(); + tolerances.second = overTolerance.toStdString(); + + return tolerances; +} + +//partial = 2 unit only +std::string DimensionFormatter::getFormattedDimensionValue(int partial) +{ + QString qFormatSpec = QString::fromUtf8(m_dimension->FormatSpec.getStrValue().data()); + + if ( (m_dimension->Arbitrary.getValue() && !m_dimension->EqualTolerance.getValue()) + || (m_dimension->Arbitrary.getValue() && m_dimension->TheoreticalExact.getValue()) ) { + return m_dimension->FormatSpec.getStrValue(); + } + + if (m_dimension->Arbitrary.getValue()) { + return m_dimension->FormatSpec.getStrValue(); + } + + // if there is an equal over-/undertolerance (so only 1 tolerance to show with +/-) and + // not theoretically exact (which has no tolerance), and + // tolerance has been specified, ie + // (OverTolerance != 0.0 (so a tolerance has been specified) or + // ArbitraryTolerances are specified) + // concatenate the tolerance to dimension + if (m_dimension->EqualTolerance.getValue() && + !m_dimension->TheoreticalExact.getValue() && + (!DrawUtil::fpCompare(m_dimension->OverTolerance.getValue(), 0.0) || + m_dimension->ArbitraryTolerances.getValue())) { + QString labelText = QString::fromUtf8(formatValue(m_dimension->getDimValue(), + qFormatSpec, + 1, + true).c_str()); //just the number pref/spec[unit]/suf + QString unitText = QString::fromUtf8(formatValue(m_dimension->getDimValue(), + qFormatSpec, + 2, + false).c_str()); //just the unit + QString tolerance = QString::fromStdString(getFormattedToleranceValue(1).c_str()); + + // tolerance might start with a plus sign that we don't want, so cut it off + // note plus sign is not at pos = 0! + QRegularExpression plus(QString::fromUtf8("^\\s*\\+")); + tolerance.remove(plus); + + return (labelText + + QString::fromUtf8(" \xC2\xB1 ") + // +/- symbol + tolerance).toStdString(); + + if (partial == 2) { + return unitText.toStdString(); + } + + return ""; + } + + //tolerance not specified, so just format dimension value? + std::string formattedValue = formatValue(m_dimension->getDimValue(), qFormatSpec, partial, true); + + return formattedValue; +} + +// format the value using the formatSpec. Also, handle the non-standard format- +// specifier '%w', which has the following rules: works as %f, but no trailing zeros +QString DimensionFormatter::formatValueToSpec(double value, QString formatSpecifier) +{ + QString formattedValue; + if (formatSpecifier.contains(QRegularExpression(QStringLiteral("%.*[wW]")))) { + QString fs = formatSpecifier; + fs.replace(QRegularExpression(QStringLiteral("%(.*)w")), QStringLiteral("%\\1f")); + fs.replace(QRegularExpression(QStringLiteral("%(.*)W")), QStringLiteral("%\\1F")); + formattedValue = QString::asprintf(Base::Tools::toStdString(fs).c_str(), value); + // First, try to cut trailing zeros, if AFTER decimal dot there are nonzero numbers + // Second, try to cut also decimal dot and zeros, if there are just zeros after it + formattedValue.replace(QRegularExpression(QStringLiteral("([0-9][0-9]*\\.[0-9]*[1-9])00*$")), QStringLiteral("\\1")); + formattedValue.replace(QRegularExpression(QStringLiteral("([0-9][0-9]*)\\.0*$")), QStringLiteral("\\1")); + } else { + formattedValue = QString::asprintf(Base::Tools::toStdString(formatSpecifier).c_str(), value); + } + + return formattedValue; +} + +QStringList DimensionFormatter::getPrefixSuffixSpec(QString fSpec) +{ + QStringList result; + //find the %x.y tag in FormatSpec + QRegularExpression rxFormat(QStringLiteral("%[+-]?[0-9]*\\.*[0-9]*[aefgwAEFGW]")); //printf double format spec + QRegularExpressionMatch rxMatch; + int pos = fSpec.indexOf(rxFormat, 0, &rxMatch); + if (pos != -1) { + QString match = rxMatch.captured(0); //entire capture of rx + QString formatPrefix = fSpec.left(pos); + result.append(formatPrefix); + QString formatSuffix = fSpec.right(fSpec.size() - pos - match.size()); + result.append(formatSuffix); + result.append(match); + } else { //printf format not found! + Base::Console().Warning("Warning - no numeric format in formatSpec %s - %s\n", + qPrintable(fSpec), m_dimension->getNameInDocument()); + result.append(QString()); + result.append(QString()); + result.append(fSpec); + } + return result; +} + +std::string DimensionFormatter::getDefaultFormatSpec(bool isToleranceFormat) const +{ + std::string prefFormat = Preferences::formatSpec(); + QString formatSpec; + QString qPrefix; + if (prefFormat.empty()) { + QString format1 = Base::Tools::fromStdString("%."); + QString format2 = Base::Tools::fromStdString("f"); + int precision; + if (m_dimension->useDecimals()) { + precision = Base::UnitsApi::getDecimals(); + } else { + precision = Preferences::altDecimals(); + } + QString formatPrecision = QString::number(precision); + + std::string prefix = m_dimension->getPrefixForDimType(); + + if (!prefix.empty()) { + qPrefix = QString::fromUtf8(prefix.data(), prefix.size()); + } + + formatSpec = qPrefix + format1 + formatPrecision + format2; + } else { + + std::string prefix = m_dimension->getPrefixForDimType(); + qPrefix = QString::fromUtf8(prefix.data(), prefix.size()); + formatSpec = qPrefix + QString::fromStdString(prefFormat); + + } + + if (isToleranceFormat) { + formatSpec.replace(QString::fromUtf8("%"), QString::fromUtf8("%+")); + } + + return Base::Tools::toStdString(formatSpec); +} + +//true if value is too small to display using formatSpec +bool DimensionFormatter::isTooSmall(double value, QString formatSpec) +{ + if (DU::fpCompare(value, 0.0)) { + //zero values always fit, so it isn't too small + return false; + } + + QRegularExpression rxFormat(QStringLiteral("%[+-]?[0-9]*\\.*([0-9]*)[aefgwAEFGW]")); //printf double format spec + QRegularExpressionMatch rxMatch = rxFormat.match(formatSpec); + if (rxMatch.hasMatch()) { + QString decimalGroup = rxMatch.captured(1); + int factor = decimalGroup.toInt(); + double minValue = pow(10.0, -factor); + if (value < minValue) { + return true; + } + } else { + Base::Console().Warning("Failed to parse dimension format spec\n"); + } + return false; +} diff --git a/src/Mod/TechDraw/App/DimensionFormatter.h b/src/Mod/TechDraw/App/DimensionFormatter.h new file mode 100644 index 0000000000..c0a63341df --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionFormatter.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef DIMENSIONFORMATTER_H +#define DIMENSIONFORMATTER_H + +#include + +#include + +namespace TechDraw { + +class TechDrawExport DimensionFormatter { +public: + DimensionFormatter() {} + DimensionFormatter(DrawViewDimension* dim) { m_dimension = dim; } + ~DimensionFormatter() = default; + + void setDimension(DrawViewDimension* dim) { m_dimension = dim; } + bool isMultiValueSchema() const; + std::string formatValue(qreal value, + QString qFormatSpec, + int partial, + bool isDim); + std::string getFormattedToleranceValue(int partial); + std::pair getFormattedToleranceValues(int partial); + std::string getFormattedDimensionValue(int partial); + QStringList getPrefixSuffixSpec(QString fSpec); + std::string getDefaultFormatSpec(bool isToleranceFormat) const; + bool isTooSmall(double value, QString formatSpec); + QString formatValueToSpec(double value, QString formatSpecifier); + +private: + DrawViewDimension* m_dimension; +}; + +} //end namespace TechDraw +#endif diff --git a/src/Mod/TechDraw/App/DimensionGeometry.cpp b/src/Mod/TechDraw/App/DimensionGeometry.cpp new file mode 100644 index 0000000000..eab1c154ed --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionGeometry.cpp @@ -0,0 +1,251 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan * + * * + * 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 +#include +#include +#endif + +#include + +#include "DrawViewPart.h" +#include "DrawUtil.h" + +#include "DimensionGeometry.h" + +using namespace TechDraw; +using DU = DrawUtil; + +pointPair::pointPair(const pointPair& pp) +{ + first(pp.first()); + second(pp.second()); +} + +void pointPair::move(Base::Vector3d offset) +{ + m_first = m_first - offset; + m_second = m_second - offset; +} + +void pointPair::project(DrawViewPart* dvp) +{ + Base::Vector3d normal = DrawUtil::toVector3d(dvp->getProjectionCS().Direction()); + Base::Vector3d stdOrigin(0.0, 0.0, 0.0); + m_first = m_first.ProjectToPlane(stdOrigin, normal) * dvp->getScale(); + m_second = m_second.ProjectToPlane(stdOrigin, normal) * dvp->getScale(); +} + +void pointPair::mapToPage(DrawViewPart* dvp) +{ + gp_Trsf xOXYZ; + gp_Ax3 OXYZ; + xOXYZ.SetTransformation(OXYZ, gp_Ax3(dvp->getProjectionCS())); + + gp_Vec gvFirst = DU::togp_Vec(m_first).Transformed(xOXYZ); + m_first = DU::toVector3d(gvFirst); + gp_Vec gvSecond = DU::togp_Vec(m_second).Transformed(xOXYZ); + m_second = DU::toVector3d(gvSecond); +} + +void pointPair::invertY() +{ + m_first = DU::invertY(m_first); + m_second = DU::invertY(m_second); +} + +void pointPair::dump(std::string text) const +{ + Base::Console().Message("pointPair - %s\n", text.c_str()); + Base::Console().Message("pointPair - first: %s second: %s\n", + DU::formatVector(first()).c_str(), + DU::formatVector(second()).c_str()); +} + +anglePoints::anglePoints() +{ + m_ends.first(Base::Vector3d(0.0, 0.0, 0.0)); + m_ends.second(Base::Vector3d(0.0, 0.0, 0.0)); + m_vertex = Base::Vector3d(0.0, 0.0, 0.0); +} + +anglePoints::anglePoints(const anglePoints& ap) + : m_ends(ap.ends()), + m_vertex(ap.vertex()) +{ + +} + +anglePoints& anglePoints::operator= (const anglePoints& ap) +{ + m_ends = ap.ends(); + m_vertex = ap.vertex(); + return *this; +} + +void anglePoints::move(Base::Vector3d offset) +{ + m_ends.move(offset); + m_vertex = m_vertex - offset; +} + +void anglePoints::project(DrawViewPart* dvp) +{ + Base::Vector3d normal = DrawUtil::toVector3d(dvp->getProjectionCS().Direction()); + Base::Vector3d stdOrigin(0.0, 0.0, 0.0); + m_ends.project(dvp); + m_vertex = m_vertex.ProjectToPlane(stdOrigin, normal) * dvp->getScale(); +} + +void anglePoints::mapToPage(DrawViewPart* dvp) +{ + m_ends.mapToPage(dvp); + + gp_Trsf xOXYZ; + gp_Ax3 OXYZ; + xOXYZ.SetTransformation(OXYZ, gp_Ax3(dvp->getProjectionCS())); + gp_Vec gvVertex = DU::togp_Vec(m_vertex).Transformed(xOXYZ); + m_vertex = DU::toVector3d(gvVertex); +} + +void anglePoints::invertY() +{ + m_ends.invertY(); + m_vertex = DU::invertY(m_vertex); +} + +void anglePoints::dump(std::string text) const +{ + Base::Console().Message("anglePoints - %s\n", text.c_str()); + Base::Console().Message("anglePoints - ends - first: %s second: %s\n", + DU::formatVector(first()).c_str(), + DU::formatVector(second()).c_str()); + Base::Console().Message("anglePoints - vertex: %s\n", + DU::formatVector(vertex()).c_str()); +} + +arcPoints::arcPoints() +{ + isArc = false; + radius = 0.0; + center = Base::Vector3d(0.0, 0.0, 0.0); + onCurve.first(Base::Vector3d(0.0, 0.0, 0.0)); + onCurve.second(Base::Vector3d(0.0, 0.0, 0.0)); + arcEnds.first(Base::Vector3d(0.0, 0.0, 0.0)); + arcEnds.second(Base::Vector3d(0.0, 0.0, 0.0)); + midArc = Base::Vector3d(0.0, 0.0, 0.0); + arcCW = false; +} + +arcPoints::arcPoints(const arcPoints& ap) + : isArc(ap.isArc) + , radius(ap.radius) + , center(ap.center) + , onCurve(ap.onCurve) + , arcEnds(ap.arcEnds) + , midArc(ap.midArc) + , arcCW(ap.arcCW) +{ + +} + +arcPoints& arcPoints::operator= (const arcPoints& ap) +{ + isArc = ap.isArc; + radius = ap.radius; + center = ap.center; + onCurve = ap.onCurve; + arcEnds = ap.arcEnds; + midArc = ap.midArc; + arcCW = ap.arcCW; + return *this; +} + +void arcPoints::move(Base::Vector3d offset) +{ + center = center - offset; + onCurve.first(onCurve.first() - offset); + onCurve.second(onCurve.second() - offset); + arcEnds.first(arcEnds.first() - offset); + arcEnds.second(arcEnds.second() - offset); + midArc = midArc - offset; +} + +void arcPoints::project(DrawViewPart* dvp) +{ + radius = radius * dvp->getScale(); + Base::Vector3d normal = DrawUtil::toVector3d(dvp->getProjectionCS().Direction()); + Base::Vector3d stdOrigin(0.0, 0.0, 0.0); + center = center.ProjectToPlane(stdOrigin, normal) * dvp->getScale(); + onCurve.first(onCurve.first().ProjectToPlane(stdOrigin, normal) * dvp->getScale()); + onCurve.second(onCurve.second().ProjectToPlane(stdOrigin, normal) * dvp->getScale()); + arcEnds.first(arcEnds.first().ProjectToPlane(stdOrigin, normal) * dvp->getScale()); + arcEnds.second(arcEnds.second().ProjectToPlane(stdOrigin, normal) * dvp->getScale()); + midArc = midArc.ProjectToPlane(stdOrigin, normal) * dvp->getScale(); +} + +void arcPoints::mapToPage(DrawViewPart* dvp) +{ + gp_Trsf xOXYZ; + gp_Ax3 OXYZ; + xOXYZ.SetTransformation(OXYZ, gp_Ax3(dvp->getProjectionCS())); + + gp_Vec gvCenter = DU::togp_Vec(center).Transformed(xOXYZ); + center = DU::toVector3d(gvCenter); + gp_Vec gvOnCurve1 = DU::togp_Vec(onCurve.first()).Transformed(xOXYZ); + onCurve.first(DU::toVector3d(gvOnCurve1)); + gp_Vec gvOnCurve2 = DU::togp_Vec(onCurve.second()).Transformed(xOXYZ); + onCurve.second(DU::toVector3d(gvOnCurve2)); + gp_Vec gvArcEnds1 = DU::togp_Vec(arcEnds.first()).Transformed(xOXYZ); + arcEnds.first(DU::toVector3d(gvArcEnds1)); + gp_Vec gvArcEnds2 = DU::togp_Vec(arcEnds.second()).Transformed(xOXYZ); + arcEnds.second(DU::toVector3d(gvArcEnds2)); + gp_Vec gvMidArc = DU::togp_Vec(midArc).Transformed(xOXYZ); + midArc = DU::toVector3d(gvMidArc); +} + +void arcPoints::invertY() +{ + center = DU::invertY(center); + onCurve.invertY(); + arcEnds.invertY(); + midArc = DU::invertY(midArc); +} + +void arcPoints::dump(std::string text) const +{ + Base::Console().Message("arcPoints - %s\n", text.c_str()); + Base::Console().Message("arcPoints - radius: %.3f center: %s\n", radius, DrawUtil::formatVector(center).c_str()); + Base::Console().Message("arcPoints - isArc: %d arcCW: %d\n", isArc, arcCW); + Base::Console().Message("arcPoints - onCurve: %s %s\n", + DrawUtil::formatVector(onCurve.first()).c_str(), + DrawUtil::formatVector(onCurve.second()).c_str()); + Base::Console().Message("arcPoints - arcEnds: %s %s\n", + DrawUtil::formatVector(arcEnds.first()).c_str(), + DrawUtil::formatVector(arcEnds.second()).c_str()); + Base::Console().Message("arcPoints - midArc: %s\n", + DrawUtil::formatVector(midArc).c_str()); +} + diff --git a/src/Mod/TechDraw/App/DimensionGeometry.h b/src/Mod/TechDraw/App/DimensionGeometry.h new file mode 100644 index 0000000000..8c2f7615d4 --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionGeometry.h @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef TECHDRAW_DIMENSIONGEOMETRY_h_ +#define TECHDRAW_DIMENSIONGEOMETRY_h_ + +#include + +#include + +#include + +namespace TechDraw +{ +class DrawViewPart; + +//a convenient container for linear dimension end points +class TechDrawExport pointPair +{ +public: + pointPair() = default; + pointPair(Base::Vector3d point0, Base::Vector3d point1) { m_first = point0; m_second = point1; } + pointPair(const pointPair& pp); + + pointPair& operator= (const pointPair& pp) { + m_first = pp.first(); + m_second = pp.second(); + return *this; + } + + Base::Vector3d first() const { return m_first; } + void first(Base::Vector3d newFirst) { m_first = newFirst; } + Base::Vector3d second() const { return m_second; } + void second(Base::Vector3d newSecond) { m_second = newSecond; } + + void move(Base::Vector3d offset); + void project(DrawViewPart* dvp); + void mapToPage(DrawViewPart* dvp); + void invertY(); + void dump(std::string text) const; + +private: + Base::Vector3d m_first; + Base::Vector3d m_second; +}; + +//a convenient container for angular dimension points +class TechDrawExport anglePoints +{ +public: + anglePoints(); + anglePoints(Base::Vector3d apex, Base::Vector3d point0, Base::Vector3d point1) { + vertex(apex); first(point0); second(point1); } + anglePoints(const anglePoints& ap); + + anglePoints& operator= (const anglePoints& ap); + + pointPair ends() const { return m_ends; } + void ends(pointPair newEnds) { m_ends = newEnds; } + Base::Vector3d first() const { return m_ends.first(); } + void first(Base::Vector3d newFirst) { m_ends.first(newFirst); } + Base::Vector3d second() const { return m_ends.second(); } + void second(Base::Vector3d newSecond) { m_ends.second(newSecond); } + Base::Vector3d vertex() const { return m_vertex; } + void vertex(Base::Vector3d newVertex) { m_vertex = newVertex; } + + void move(Base::Vector3d offset); + void project(DrawViewPart* dvp); + void mapToPage(DrawViewPart* dvp); + void invertY(); + void dump(std::string text) const; + +private: + pointPair m_ends; + Base::Vector3d m_vertex; +}; + +//a convenient container for diameter or radius dimension points +class TechDrawExport arcPoints +{ +public: + arcPoints(); + arcPoints(const arcPoints& ap); + + arcPoints& operator= (const arcPoints& ap); + + void move(Base::Vector3d offset); + void project(DrawViewPart* dvp); + void mapToPage(DrawViewPart* dvp); + void invertY(); + void dump(std::string text) const; + +//TODO: setters and getters + bool isArc; + double radius; + Base::Vector3d center; + pointPair onCurve; + pointPair arcEnds; + Base::Vector3d midArc; + bool arcCW; +}; + +} //end namespace TechDraw + +#endif diff --git a/src/Mod/TechDraw/App/DimensionReferences.cpp b/src/Mod/TechDraw/App/DimensionReferences.cpp new file mode 100644 index 0000000000..703ec054d7 --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionReferences.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan + +#include +#include +#include + +#include "DrawViewPart.h" +#include "DrawUtil.h" +#include "Geometry.h" +#include "DimensionReferences.h" + +using namespace TechDraw; + +TopoDS_Shape ReferenceEntry::getGeometry() const +{ + if ( getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) ) { + TechDraw::DrawViewPart* dvp = static_cast(getObject()); + std::string gType = geomType(); + if (gType == "Vertex") { + auto vgeom = dvp->getVertex(getSubName()); + return vgeom->occVertex; + } else if (gType == "Edge") { + auto egeom = dvp->getEdge(getSubName()); + return egeom->occEdge; + } else if (gType == "Face") { + auto fgeom = dvp->getFace(getSubName()); + return fgeom->toOccFace(); + } + return TopoDS_Shape(); + } + + Part::TopoShape shape = Part::Feature::getTopoShape(getObject()); + App::GeoFeature* geoFeat = dynamic_cast(getObject()); + if (geoFeat) { + shape.setPlacement(geoFeat->globalPlacement()); + } + + if (getSubName().empty()) { + return shape.getShape(); + } + return shape.getSubShape(getSubName().c_str()); +} + +std::string ReferenceEntry::geomType() const +{ + return DrawUtil::getGeomTypeFromName(getSubName()); +} + +bool ReferenceEntry::isWholeObject() const +{ + return getSubName().empty(); +} diff --git a/src/Mod/TechDraw/App/DimensionReferences.h b/src/Mod/TechDraw/App/DimensionReferences.h new file mode 100644 index 0000000000..34292a597e --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionReferences.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan + +#include +#include + +#include + +namespace App +{ +class DocumentObject; +} + +namespace TechDraw +{ + +//a convenient way of handling object+subName references +class TechDrawExport ReferenceEntry +{ +public: + ReferenceEntry( App::DocumentObject* docObject, std::string subName ) { + setObject(docObject); + setSubName(subName); + } + ReferenceEntry(const ReferenceEntry& other) { + setObject(other.getObject()); + setSubName(other.getSubName()); + } + ~ReferenceEntry() = default; + + App::DocumentObject* getObject() const { return m_object; } + void setObject(App::DocumentObject* docObj) { m_object = docObj; } + std::string getSubName() const { return m_subName; } + void setSubName(std::string subName) { m_subName = subName; } + TopoDS_Shape getGeometry() const; + std::string geomType() const; + bool isWholeObject() const; + +private: + bool is3d(); + App::DocumentObject* m_object; + std::string m_subName; +}; + +using ReferenceVector = std::vector; + +} // end namespace + +#endif //TECHDRAW_DIMENSIONREFERENCES_H diff --git a/src/Mod/TechDraw/App/DrawDimHelper.cpp b/src/Mod/TechDraw/App/DrawDimHelper.cpp index 8268c7ba4c..21c85ca3f2 100644 --- a/src/Mod/TechDraw/App/DrawDimHelper.cpp +++ b/src/Mod/TechDraw/App/DrawDimHelper.cpp @@ -23,16 +23,17 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include +#include +#include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include #endif #include @@ -43,6 +44,7 @@ #include "DrawDimHelper.h" #include "Geometry.h" +#include "GeometryObject.h" #include "Cosmetic.h" #include "DrawPage.h" #include "DrawUtil.h" @@ -57,9 +59,6 @@ using namespace TechDraw; -//All this OCC math is being done on edges(&vertices) that have been through the center/scale/mirror process. - -//TODO: this needs to be exposed to Python void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector edgeNames, int direction) @@ -67,7 +66,6 @@ void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, // Base::Console().Message("DDH::makeExtentDim() - dvp: %s edgeNames: %d\n", // dvp->Label.getValue(), edgeNames.size()); if (!dvp) { -// Base::Console().Message("DDH::makeExtentDim - dvp: %X\n", dvp); return; } @@ -78,90 +76,132 @@ void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, dimNum = 1; } - std::pair endPoints = minMax(dvp, - edgeNames, - direction); - Base::Vector3d refMin = endPoints.first / dvp->getScale(); //unscale from geometry - Base::Vector3d refMax = endPoints.second / dvp->getScale(); + TechDraw::DrawPage* page = dvp->findParentPage(); + std::string pageName = page->getNameInDocument(); - //pause recomputes - dvp->getDocument()->setStatus(App::Document::Status::SkipRecompute, true); - - DrawViewDimension* distDim = makeDistDim(dvp, dimType, refMin, refMax, true); - std::string dimName = distDim->getNameInDocument(); + App::Document* doc = dvp->getDocument(); + std::string dimName = doc->getUniqueObjectName("DimExtent"); + Base::Interpreter().runStringArg("App.activeDocument().addObject('TechDraw::DrawViewDimExtent', '%s')", + dimName.c_str()); + Base::Interpreter().runStringArg("App.activeDocument().%s.Type = '%s'", + dimName.c_str(), dimType.c_str()); Base::Interpreter().runStringArg("App.activeDocument().%s.DirExtent = %d", dimName.c_str(), dimNum); - DrawViewDimExtent* extDim = dynamic_cast(distDim); - extDim->Source.setValue(dvp, edgeNames); - std::vector subElements = extDim->References2D.getSubValues(); - std::vector cvTags; - std::string tag0; - std::string tag1; - TechDraw::VertexPtr v0; - TechDraw::VertexPtr v1; - if (subElements.size() > 1) { - int idx0 = DrawUtil::getIndexFromName(subElements[0]); - int idx1 = DrawUtil::getIndexFromName(subElements[1]); - v0 = dvp->getProjVertexByIndex(idx0); - v1 = dvp->getProjVertexByIndex(idx1); - if (v0 && !v0->cosmeticTag.empty()) { - tag0 = v0->cosmeticTag; - } - if (v1 && !v1->cosmeticTag.empty()) { - tag1 = v1->cosmeticTag; - } - cvTags.push_back(tag0); - cvTags.push_back(tag1); - extDim->CosmeticTags.setValues(cvTags); + TechDraw::DrawViewDimExtent* dimExt = dynamic_cast(doc->getObject(dimName.c_str())); + if (!dimExt) { + throw Base::TypeError("Dim extent not found"); } + dimExt->Source.setValue(dvp, edgeNames); + ReferenceVector newRefs; + if (edgeNames.empty()) { + ReferenceEntry emptyRef(dvp, std::string()); + newRefs.push_back(emptyRef); + } else { + for (auto& edge : edgeNames) { + ReferenceEntry ref(dvp, edge); + newRefs.push_back(ref); + } + } + dimExt->setReferences2d(newRefs); - //continue recomputes - dvp->getDocument()->setStatus(App::Document::Status::SkipRecompute, false); - extDim->recomputeFeature(); + Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", + pageName.c_str(), dimName.c_str()); + + dimExt->recomputeFeature(); } +void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, + ReferenceVector references, + int direction) +{ +// Base::Console().Message("DDH::makeExtentDim3d() - dvp: %s references: %d\n", +// dvp->Label.getValue(), references.size()); + if (!dvp) { + return; + } + + std::string dimType = "DistanceX"; + int dimNum = 0; + if (direction == VERTICAL) { + dimType = "DistanceY"; + dimNum = 1; + } + + TechDraw::DrawPage* page = dvp->findParentPage(); + std::string pageName = page->getNameInDocument(); + + App::Document* doc = dvp->getDocument(); + std::string dimName = doc->getUniqueObjectName("DimExtent"); + Base::Interpreter().runStringArg("App.activeDocument().addObject('TechDraw::DrawViewDimExtent', '%s')", + dimName.c_str()); + Base::Interpreter().runStringArg("App.activeDocument().%s.Type = '%s'", + dimName.c_str(), dimType.c_str()); + Base::Interpreter().runStringArg("App.activeDocument().%s.DirExtent = %d", + dimName.c_str(), dimNum); + + TechDraw::DrawViewDimExtent* dimExt = dynamic_cast(doc->getObject(dimName.c_str())); + if (!dimExt) { + throw Base::TypeError("Dim extent not found"); + } + + dimExt->Source.setValue(dvp); + + std::vector objs3d; + std::vector subs3d; + for (auto& ref : references) { + objs3d.push_back(ref.getObject()); + subs3d.push_back(ref.getSubName()); + } + dimExt->Source3d.setValues(objs3d, subs3d); + + ReferenceVector newRefs2d; + ReferenceEntry emptyRef(dvp, std::string()); + newRefs2d.push_back(emptyRef); + dimExt->setReferences2d(newRefs2d); + + dimExt->setReferences3d(references); + + Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", + pageName.c_str(), dimName.c_str()); + + dimExt->recomputeFeature(); +} std::pair DrawDimHelper::minMax(DrawViewPart* dvp, std::vector edgeNames, int direction) { -// Base::Console().Message("DDH::minMax()\n"); +// Base::Console().Message("DDH::minMax() - edgeName: %d\n", edgeNames.size()); std::pair result; Base::Vector3d refMin; Base::Vector3d refMax; - gp_Pnt stdOrg(0.0, 0.0, 0.0); - gp_Dir stdZ(0.0, 0.0, 1.0); - gp_Dir stdX(1.0, 0.0, 0.0); - gp_Ax3 projAx3(stdOrg, stdZ, stdX); - gp_Pln projPlane(projAx3); // OZX + gp_Ax3 projAx3; // OXYZ + gp_Pln projPlane(projAx3); - BaseGeomPtrVector bgList; - if (!edgeNames.empty()) { + BaseGeomPtrVector edgeGeomList; + if (!edgeNames.empty() && !edgeNames.front().empty()) { + //we have edge names and the first one isn't null for (auto& n: edgeNames) { - if (!n.empty()) { - std::string geomType = DrawUtil::getGeomTypeFromName(n); - if (!n.empty() && (geomType == "Edge")) { - int i = DrawUtil::getIndexFromName(n); - BaseGeomPtr bg = dvp->getGeomByIndex(i); - if (bg) { - bgList.push_back(bg); - } + std::string geomType = DrawUtil::getGeomTypeFromName(n); + if (geomType == "Edge") { + int i = DrawUtil::getIndexFromName(n); + BaseGeomPtr bg = dvp->getGeomByIndex(i); + if (bg) { + edgeGeomList.push_back(bg); } } } + } else { + edgeGeomList = dvp->getEdgeGeometry(); //do the whole View } - BaseGeomPtrVector selEdges = bgList; - if (selEdges.empty()) { - selEdges = dvp->getEdgeGeometry(); //do the whole View - } Bnd_Box edgeBbx; edgeBbx.SetGap(1.0); //make the box a bit bigger std::vector inEdges; - for (auto& bg: selEdges) { + for (auto& bg: edgeGeomList) { inEdges.push_back(bg->occEdge); BRepBndLib::Add(bg->occEdge, edgeBbx); } @@ -242,6 +282,94 @@ gp_Pnt DrawDimHelper::findClosestPoint(std::vector inEdges, return nearPoint; } +std::pair DrawDimHelper::minMax3d(DrawViewPart* dvp, + ReferenceVector references, + int direction) +{ +// Base::Console().Message("DDH::minMax3d() - references: %d\n", references.size()); + std::pair result; + Base::Vector3d refMin; + Base::Vector3d refMax; + + gp_Ax3 projAx3; //OXYZ + gp_Pln projPlane(projAx3); + + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + for (auto& ref : references) { + builder.Add(comp, ref.getGeometry()); + } + Base::Vector3d centroid = dvp->getOriginalCentroid(); + TopoDS_Shape centeredShape = //this result is a throw away. We will work with comp. + DrawViewPart::centerScaleRotate(dvp, comp, centroid); + + //project the selected 3d shapes in the dvp's coord system + TechDraw::GeometryObjectPtr go(std::make_shared(std::string(), nullptr)); + go->setIsoCount(0); + go->isPerspective(false); + go->usePolygonHLR(false); + go->projectShape(comp, dvp->getProjectionCS()); + auto edges = go->getEdgeGeometry(); + + Bnd_Box shapeBbx; + shapeBbx.SetGap(1.0); //make the box a bit bigger + + std::vector inEdges; + for (auto& bg: edges) { + inEdges.push_back(bg->occEdge); + BRepBndLib::Add(bg->occEdge, shapeBbx); + } + + //from here on this is the same as 2d method + double minX, minY, minZ, maxX, maxY, maxZ; + shapeBbx.Get(minX, minY, minZ, maxX, maxY, maxZ); + double xMid = (maxX + minX) / 2.0; + double yMid = (maxY + minY) / 2.0; + + gp_Pnt rightMid(maxX, yMid, 0.0); + gp_Pnt leftMid(minX, yMid, 0.0); + gp_Pnt topMid(xMid, maxY, 0.0); + gp_Pnt bottomMid(xMid, minY, 0.0); + + gp_Dir xDir(1.0, 0.0, 0.0); + gp_Dir yDir(0.0, 1.0, 0.0); + + if (direction == HORIZONTAL) { + Handle(Geom_Line) lineLeft = new Geom_Line(leftMid, yDir); + BRepBuilderAPI_MakeEdge mkEdgeLeft(lineLeft); + TopoDS_Edge edgeLeft = mkEdgeLeft.Edge(); + gp_Pnt leftPoint = findClosestPoint(inEdges, + edgeLeft); + Handle(Geom_Line) lineRight = new Geom_Line(rightMid, yDir); + BRepBuilderAPI_MakeEdge mkEdgeRight(lineRight); + TopoDS_Edge edgeRight = mkEdgeRight.Edge(); + gp_Pnt rightPoint = findClosestPoint(inEdges, + edgeRight); + + refMin = Base::Vector3d(leftPoint.X(), leftPoint.Y(), 0.0); + refMax = Base::Vector3d(rightPoint.X(), rightPoint.Y(), 0.0); + + } else if (direction == VERTICAL) { + Handle(Geom_Line) lineBottom = new Geom_Line(bottomMid, xDir); + BRepBuilderAPI_MakeEdge mkEdgeBottom(lineBottom); + TopoDS_Edge edgeBottom = mkEdgeBottom.Edge(); + gp_Pnt bottomPoint = findClosestPoint(inEdges, + edgeBottom); + Handle(Geom_Line) lineTop = new Geom_Line(topMid, xDir); + BRepBuilderAPI_MakeEdge mkEdgeTop(lineTop); + TopoDS_Edge edgeTop = mkEdgeTop.Edge(); + gp_Pnt topPoint = findClosestPoint(inEdges, + edgeTop); + refMin = Base::Vector3d(bottomPoint.X(), bottomPoint.Y(), 0.0); + refMax = Base::Vector3d(topPoint.X(), topPoint.Y(), 0.0); + } + + result.first = refMin; + result.second = refMax; + return result; +} + DrawViewDimension* DrawDimHelper::makeDistDim(DrawViewPart* dvp, std::string dimType, Base::Vector3d inMin, //is this scaled or unscaled?? @@ -309,7 +437,6 @@ DrawViewDimension* DrawDimHelper::makeDistDim(DrawViewPart* dvp, Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", pageName.c_str(), dimName.c_str()); - dvp->requestPaint(); return dim; } diff --git a/src/Mod/TechDraw/App/DrawDimHelper.h b/src/Mod/TechDraw/App/DrawDimHelper.h index 7786e0acb0..3a3fcf9de4 100644 --- a/src/Mod/TechDraw/App/DrawDimHelper.h +++ b/src/Mod/TechDraw/App/DrawDimHelper.h @@ -31,7 +31,7 @@ #include #include - +#include namespace TechDraw { @@ -45,6 +45,10 @@ class TechDrawExport DrawDimHelper { static void makeExtentDim(DrawViewPart* dvp, std::vector edgeNames, int direction); + static void makeExtentDim3d(DrawViewPart* dvp, + ReferenceVector references, + int direction); + static gp_Pnt findClosestPoint(std::vector inEdges, TopoDS_Edge& boundary); @@ -58,6 +62,9 @@ class TechDrawExport DrawDimHelper { static std::pair minMax(DrawViewPart* dvp, std::vector edgeNames, int direction); + static std::pair minMax3d(DrawViewPart* dvp, + ReferenceVector references, + int direction); }; } //end namespace TechDraw diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index 617b3fc805..9c8950cf20 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -345,6 +346,25 @@ std::pair DrawUtil::boxIntersect2d(Base::Vector3 return result; } +//find the apparent intersection of 2 3d curves. We are only interested in curves that are lines, so we will have either 0 or 1 +//apparent intersection. The intersection is "apparent" because the curve's progenator is a trimmed curve (line segment) +bool DrawUtil::apparentIntersection(const Handle(Geom_Curve) curve1, + const Handle(Geom_Curve) curve2, + Base::Vector3d& result) +{ + GeomAPI_ExtremaCurveCurve intersector(curve1, curve2); + if (intersector.NbExtrema() == 0 || + intersector.LowerDistance() > EWTOLERANCE ) { + //no intersection + return false; + } + //for our purposes, only one intersection point is required. + gp_Pnt p1, p2; + intersector.Points(1, p1, p2); + result = toVector3d(p1); + return true; +} + Base::Vector3d DrawUtil::vertex2Vector(const TopoDS_Vertex& v) { gp_Pnt gp = BRep_Tool::Pnt(v); diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h index f11a153981..ee1e098344 100644 --- a/src/Mod/TechDraw/App/DrawUtil.h +++ b/src/Mod/TechDraw/App/DrawUtil.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,12 @@ class TechDrawExport DrawUtil { Base::Vector3d dir, double xRange, double yRange) ; + static bool apparentIntersection(const Handle(Geom_Curve) curve1, + const Handle(Geom_Curve) curve2, + Base::Vector3d& result); + + + static Base::Vector3d vertex2Vector(const TopoDS_Vertex& v); static std::string formatVector(const Base::Vector3d& v); diff --git a/src/Mod/TechDraw/App/DrawViewDimExtent.cpp b/src/Mod/TechDraw/App/DrawViewDimExtent.cpp index 5b2f8432b0..25179c824b 100644 --- a/src/Mod/TechDraw/App/DrawViewDimExtent.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimExtent.cpp @@ -33,11 +33,9 @@ #include // generated from DrawViewDimExtentPy.xml #include "DrawViewDimExtent.h" -#include "Cosmetic.h" #include "DrawDimHelper.h" #include "DrawViewPart.h" - using namespace TechDraw; //=========================================================================== @@ -49,36 +47,19 @@ PROPERTY_SOURCE(TechDraw::DrawViewDimExtent, TechDraw::DrawViewDimension) DrawViewDimExtent::DrawViewDimExtent(void) { App::PropertyLinkSubList Source; //DrawViewPart & SubElements(Edges) - //Cosmetic End points are stored in DVD::References2d - App::PropertyLinkSubList Source3d; //Part::Feature & SubElements TBI + App::PropertyLinkSubList Source3d; //Part::Feature(s) & SubElements - ADD_PROPERTY_TYPE(Source, (nullptr, nullptr), "", (App::PropertyType)(App::Prop_Output), "View (Edges) to dimension"); + ADD_PROPERTY_TYPE(Source, (nullptr, nullptr), "", (App::PropertyType)(App::Prop_Output), "View containing the dimension"); Source.setScope(App::LinkScope::Global); - ADD_PROPERTY_TYPE(Source3d, (nullptr, nullptr), "", (App::PropertyType)(App::Prop_Output), "View (Edges) to dimension"); //TBI + + //Source3d is a candidate for deprecation as References3D contains the same information + ADD_PROPERTY_TYPE(Source3d, (nullptr, nullptr), "", (App::PropertyType)(App::Prop_Output), "3d geometry to be dimensioned"); Source3d.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(DirExtent ,(0), "", App::Prop_Output, "Horizontal / Vertical"); + //CosmeticTags is a candidate for deprecation ADD_PROPERTY_TYPE(CosmeticTags ,(""), "", App::Prop_Output, "Id of cosmetic endpoints"); - //hide the properties the user can't edit in the property editor - Source3d.setStatus(App::Property::Hidden, true); //TBI - -} - -void DrawViewDimExtent::onChanged(const App::Property* prop) -{ - if (!isRestoring()) { - if (prop == &Source) { -// Base::Console().Message("DVDE::onChanged - Source: %X\n", Source.getValue()); - //recalculate the points? - } - } - DrawViewDimension::onChanged(prop); -} - -short DrawViewDimExtent::mustExecute() const -{ - return DrawViewDimension::mustExecute(); } App::DocumentObjectExecReturn *DrawViewDimExtent::execute(void) @@ -87,137 +68,81 @@ App::DocumentObjectExecReturn *DrawViewDimExtent::execute(void) if (!keepUpdated()) { return App::DocumentObject::StdReturn; } - App::DocumentObject* docObj = Source.getValue(); if (!docObj) return App::DocumentObject::StdReturn; DrawViewPart* dvp = dynamic_cast(docObj); - if (!dvp) + if (!dvp) return App::DocumentObject::StdReturn; - double tolerance = 0.00001; - std::vector edgeNames = getSubNames(); - int direction = DirExtent.getValue(); + ReferenceVector references = getEffectiveReferences(); - std::pair endPoints = - DrawDimHelper::minMax(dvp, - edgeNames, - direction); - Base::Vector3d refMin = endPoints.first; - Base::Vector3d refMax = endPoints.second; + resetLinear(); + resetAngular(); + resetArc(); - std::vector cTags = CosmeticTags.getValues(); - if (cTags.size() <= 1) { - //not ready yet. - return DrawView::execute(); - } - - TechDraw::VertexPtr v0 = dvp->getProjVertexByCosTag(cTags[0]); - TechDraw::VertexPtr v1 = dvp->getProjVertexByCosTag(cTags[1]); - if (!v0 || !v1) { - //either not ready yet or something has gone wrong - return DrawView::execute(); - } - - double length00 = (v0->pnt - refMin).Length(); - double length11 = (v1->pnt - refMax).Length(); - double length01 = (v0->pnt - refMax).Length(); - double length10 = (v1->pnt - refMin).Length(); - - if ((length00 >= tolerance || length11 >= tolerance) && - (length01 >= tolerance || length10 >= tolerance)) { // Something has changed - //update GV - v0->pnt = refMin; - v1->pnt = refMax; -// v0->occVertex = ??? -// v1->occVertex = ??? - //update CV - double scale = dvp->getScale(); - CosmeticVertex* cvTemp = dvp->getCosmeticVertex(cTags[0]); - cvTemp->permaPoint = refMin / scale; - cvTemp = dvp->getCosmeticVertex(cTags[1]); - cvTemp->permaPoint = refMax / scale; + if ( Type.isValue("Distance") || + Type.isValue("DistanceX") || + Type.isValue("DistanceY") ) { + setLinearPoints(getPointsExtent(references)); } overrideKeepUpdated(false); - return DrawViewDimension::execute(); -} - -//getSubValues returns a garbage 1st entry if there are no subelements. -std::vector DrawViewDimExtent::getSubNames(void) -{ - std::vector edgeNames = Source.getSubValues(); -// Base::Console().Message("DVDE::getSubNames - edgeNames: %d\n", edgeNames.size()); - if (edgeNames.empty() || - edgeNames[0].empty()) { - return std::vector(); //garbage first entry - nop - } - return edgeNames; -} - -pointPair DrawViewDimExtent::getPointsTwoVerts() -{ -// Base::Console().Message("DVDE::getPointsTwoVerts() - %s\n", getNameInDocument()); - pointPair errorValue( - Base::Vector3d(0.0, 0.0, 0.0), - Base::Vector3d(0.0, 0.0, 0.0) - ); - - TechDraw::DrawViewPart* dvp = getViewPart(); - if (!dvp) { - return errorValue; - } - - std::vector cTags = CosmeticTags.getValues(); - if (cTags.size() < 2) { -// Base::Console().Message("DVDE::getPointsTwoVerts - not enough tags!\n"); - return errorValue; - } - - TechDraw::VertexPtr v0 = dvp->getProjVertexByCosTag(cTags[0]); - TechDraw::VertexPtr v1 = dvp->getProjVertexByCosTag(cTags[1]); - if (!v0 || !v1) - return errorValue; - - return pointPair(v0->pnt, v1->pnt); + return DrawView::execute(); } //! validate 2D references - only checks if the target exists bool DrawViewDimExtent::checkReferences2D() const { -// Base::Console().Message("DVDE::checkReFerences2d() - %s\n", getNameInDocument()); - TechDraw::DrawViewPart* dvp = getViewPart(); - if (!dvp) { +// Base::Console().Message("DVDE::checkReferences2d() - %s\n", getNameInDocument()); + const std::vector &objects = References2D.getValues(); + if (objects.empty()) { return false; } - std::vector cTags = CosmeticTags.getValues(); - if (cTags.size() < 2) { - //still building this dimension, so treat as valid? + const std::vector &subElements = References2D.getSubValues(); + //extent dims are the only dims allowed to have no subElements + if (subElements.empty() || subElements.front().empty()) { return true; } - CosmeticVertex* cv0 = dvp->getCosmeticVertex(cTags[0]); - CosmeticVertex* cv1 = dvp->getCosmeticVertex(cTags[1]); - if (!cv0 || !cv1) - return false; - - return true; + //if we have an object and a non-empty subelement list, then extent dims are the same as other dims + return DrawViewDimension::checkReferences2D(); } - -void DrawViewDimExtent::unsetupObject() +pointPair DrawViewDimExtent::getPointsExtent(ReferenceVector references) { -// bool isRemoving = testStatus(App::ObjectStatus::Remove); -// Base::Console().Message("DVDE::unsetupObject - isRemove: %d status: %X\n", -// isRemoving, getStatus()); - TechDraw::DrawViewPart* dvp = getViewPart(); +// Base::Console().Message("DVD::getPointsExtent() - %s\n", getNameInDocument()); + App::DocumentObject* refObject = references.front().getObject(); + int direction = DirExtent.getValue(); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { + auto dvp = static_cast(refObject); - std::vector cTags = CosmeticTags.getValues(); - dvp->removeCosmeticVertex(cTags); - DrawViewDimension::unsetupObject(); + std::vector edgeNames; //empty list means we are using all the edges + if (!references.at(0).getSubName().empty()) { + //this is the usual case of selected edges in a dvp + for (auto& ref : references) { + if (ref.getSubName().empty()) { + continue; + } + std::string geomType = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomType == "Edge") { + edgeNames.push_back(ref.getSubName()); + } + } + } + std::pair endPoints = + DrawDimHelper::minMax(dvp, + edgeNames, + direction); + return pointPair(endPoints.first, endPoints.second); + } - //dvp probably needs recomp/repaint here. - dvp->enforceRecompute(); + //this is a 3d reference + std::pair endPoints = + DrawDimHelper::minMax3d(getViewPart(), + references, + direction); + return pointPair(endPoints.first, endPoints.second); } PyObject *DrawViewDimExtent::getPyObject(void) diff --git a/src/Mod/TechDraw/App/DrawViewDimExtent.h b/src/Mod/TechDraw/App/DrawViewDimExtent.h index 28d8535adc..562a4e83c2 100644 --- a/src/Mod/TechDraw/App/DrawViewDimExtent.h +++ b/src/Mod/TechDraw/App/DrawViewDimExtent.h @@ -47,23 +47,17 @@ public: //Cosmetic End points are stored in DVD::References2d App::PropertyLinkSubList Source3d; //Part::Feature & SubElements TBI App::PropertyInteger DirExtent; //Horizontal, Vertical, TBD - App::PropertyStringList CosmeticTags; //id of cosmetic end points. + App::PropertyStringList CosmeticTags; //id of cosmetic end points. obsolete! App::DocumentObjectExecReturn *execute() override; - short mustExecute() const override; - void unsetupObject() override; - bool checkReferences2D() const override; - int getRefType() const override { return twoVertex; } - pointPair getLinearPoints() override { return getPointsTwoVerts(); } + int getRefType() const override { return extent; } - //return PyObject as DrawViewDimExtentPy PyObject *getPyObject() override; protected: - void onChanged(const App::Property* prop) override; - std::vector getSubNames(); - pointPair getPointsTwoVerts() override; + virtual pointPair getPointsExtent(ReferenceVector references); + bool checkReferences2D() const override; private: }; diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index aecea1d530..79f9cd8d0a 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) 2013 Luke Parry * + * 2022 WandererFan * * * * This file is part of the FreeCAD CAx development system. * * * @@ -23,20 +24,27 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include #include +#include +#include #include #include +#include +#include #endif #include @@ -47,16 +55,19 @@ #include #include #include +//#include + #include // generated from DrawViewDimensionPy.xml #include "DrawViewDimension.h" +#include "DimensionFormatter.h" #include "DrawUtil.h" #include "DrawViewPart.h" #include "Geometry.h" #include "Preferences.h" - using namespace TechDraw; +using DU = DrawUtil; //=========================================================================== // DrawViewDimension @@ -64,20 +75,6 @@ using namespace TechDraw; PROPERTY_SOURCE(TechDraw::DrawViewDimension, TechDraw::DrawView) -namespace { - // keep this enum synchronized with TypeEnums - enum DrawViewType { - Distance, - DistanceX, - DistanceY, - DistanceZ, - Radius, - Diameter, - Angle, - Angle3Pt - }; -} - const char* DrawViewDimension::TypeEnums[]= {"Distance", "DistanceX", "DistanceY", @@ -99,6 +96,9 @@ static const App::PropertyQuantityConstraint::Constraints PositiveConstraint = { DrawViewDimension::DrawViewDimension() { + //create the formatter since it will be needed to set default property values + m_formatter = new DimensionFormatter(this); + ADD_PROPERTY_TYPE(References2D, (nullptr, nullptr), "", (App::Prop_None), "Projected Geometry References"); References2D.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(References3D, (nullptr, nullptr), "", (App::Prop_None), "3D Geometry References"); @@ -152,140 +152,137 @@ DrawViewDimension::DrawViewDimension() resetAngular(); resetArc(); m_hasGeometry = false; + m_formatter = new DimensionFormatter(this); } DrawViewDimension::~DrawViewDimension() { delete measurement; measurement = nullptr; + delete m_formatter; } void DrawViewDimension::resetLinear() { - m_linearPoints.first = Base::Vector3d(0, 0,0); - m_linearPoints.second = Base::Vector3d(0, 0,0); + m_linearPoints.first(Base::Vector3d(0, 0, 0)); + m_linearPoints.second(Base::Vector3d(0, 0, 0)); } void DrawViewDimension::resetAngular() { - m_anglePoints.ends.first = Base::Vector3d(0, 0,0); - m_anglePoints.ends.second = Base::Vector3d(0, 0,0); - m_anglePoints.vertex = Base::Vector3d(0, 0,0); + m_anglePoints.first(Base::Vector3d(0, 0, 0)); + m_anglePoints.second(Base::Vector3d(0, 0, 0)); + m_anglePoints.vertex(Base::Vector3d(0, 0,0)); } void DrawViewDimension::resetArc() { m_arcPoints.isArc = false; - m_arcPoints.center = Base::Vector3d(0, 0,0); - m_arcPoints.onCurve.first = Base::Vector3d(0, 0,0); - m_arcPoints.onCurve.second = Base::Vector3d(0, 0,0); - m_arcPoints.arcEnds.first = Base::Vector3d(0, 0,0); - m_arcPoints.arcEnds.second = Base::Vector3d(0, 0,0); - m_arcPoints.midArc = Base::Vector3d(0, 0,0); + m_arcPoints.center = Base::Vector3d(0, 0, 0); + m_arcPoints.onCurve.first(Base::Vector3d(0, 0, 0)); + m_arcPoints.onCurve.second(Base::Vector3d(0, 0, 0)); + m_arcPoints.arcEnds.first(Base::Vector3d(0, 0, 0)); + m_arcPoints.arcEnds.second(Base::Vector3d(0, 0, 0)); + m_arcPoints.midArc = Base::Vector3d(0, 0, 0); m_arcPoints.arcCW = false; } void DrawViewDimension::onChanged(const App::Property* prop) { - if (!isRestoring()) { - if (prop == &MeasureType) { - if (MeasureType.isValue("True") && !measurement->has3DReferences()) { - Base::Console().Warning("%s has no 3D References but is Type: True\n", getNameInDocument()); - MeasureType.setValue("Projected"); - } - return; + if (prop == &References3D) { + //have to rebuild the Measurement object + clear3DMeasurements(); //Measurement object + if (!(References3D.getValues()).empty()) { + setAll3DMeasurement(); } - else if (prop == &References3D) { //have to rebuild the Measurement object -// Base::Console().Message("DVD::onChanged - References3D\n"); - clear3DMeasurements(); //Measurement object - if (!(References3D.getValues()).empty()) { - setAll3DMeasurement(); - } else if (MeasureType.isValue("True")) { //empty 3dRefs, but True - MeasureType.touch(); //run MeasureType logic for this case - } - return; - } - else if (prop == &Type) { //why?? - FormatSpec.setValue(getDefaultFormatSpec().c_str()); + } - DrawViewType type = static_cast(Type.getValue()); - if (type == DrawViewType::Angle || type == DrawViewType::Angle3Pt) { - OverTolerance.setUnit(Base::Unit::Angle); - UnderTolerance.setUnit(Base::Unit::Angle); - } - else { - OverTolerance.setUnit(Base::Unit::Length); - UnderTolerance.setUnit(Base::Unit::Length); - } - return; + if (isRestoring()) { + DrawView::onChanged(prop); + return; + } + + if (prop == &References3D) { //have to rebuild the Measurement object + clear3DMeasurements(); //Measurement object + if (!(References3D.getValues()).empty()) { + setAll3DMeasurement(); + } else if (MeasureType.isValue("True")) { //empty 3dRefs, but True + MeasureType.touch(); //run MeasureType logic for this case } - else if (prop == &TheoreticalExact) { - // if TheoreticalExact disable tolerances and set them to zero - if (TheoreticalExact.getValue()) { + return; + } else if (prop == &Type) { //why?? + FormatSpec.setValue(getDefaultFormatSpec().c_str()); + + DimensionType type = static_cast(Type.getValue()); + if (type == DimensionType::Angle || type == DimensionType::Angle3Pt) { + OverTolerance.setUnit(Base::Unit::Angle); + UnderTolerance.setUnit(Base::Unit::Angle); + } + else { + OverTolerance.setUnit(Base::Unit::Length); + UnderTolerance.setUnit(Base::Unit::Length); + } + return; + } else if (prop == &TheoreticalExact) { + // if TheoreticalExact disable tolerances and set them to zero + if (TheoreticalExact.getValue()) { + OverTolerance.setValue(0.0); + UnderTolerance.setValue(0.0); + OverTolerance.setReadOnly(true); + UnderTolerance.setReadOnly(true); + FormatSpecOverTolerance.setReadOnly(true); + FormatSpecUnderTolerance.setReadOnly(true); + ArbitraryTolerances.setValue(false); + ArbitraryTolerances.setReadOnly(true); + } else { + OverTolerance.setReadOnly(false); + FormatSpecOverTolerance.setReadOnly(false); + ArbitraryTolerances.setReadOnly(false); + if (!EqualTolerance.getValue()) { + UnderTolerance.setReadOnly(false); + FormatSpecUnderTolerance.setReadOnly(false); + } + } + return; + } else if (prop == &EqualTolerance) { + // if EqualTolerance set negated overtolerance for untertolerance + // then also the OverTolerance must be positive + if (EqualTolerance.getValue()) { + // if OverTolerance is negative or zero, first set it to zero + if (OverTolerance.getValue() < 0) { OverTolerance.setValue(0.0); - UnderTolerance.setValue(0.0); - OverTolerance.setReadOnly(true); - UnderTolerance.setReadOnly(true); - FormatSpecOverTolerance.setReadOnly(true); - FormatSpecUnderTolerance.setReadOnly(true); - ArbitraryTolerances.setValue(false); - ArbitraryTolerances.setReadOnly(true); } - else { - OverTolerance.setReadOnly(false); - FormatSpecOverTolerance.setReadOnly(false); - ArbitraryTolerances.setReadOnly(false); - if (!EqualTolerance.getValue()) { - UnderTolerance.setReadOnly(false); - FormatSpecUnderTolerance.setReadOnly(false); - } + OverTolerance.setConstraints(&PositiveConstraint); + UnderTolerance.setValue(-1.0 * OverTolerance.getValue()); + UnderTolerance.setUnit(OverTolerance.getUnit()); + UnderTolerance.setReadOnly(true); + FormatSpecUnderTolerance.setValue(FormatSpecOverTolerance.getValue()); + FormatSpecUnderTolerance.setReadOnly(true); + } else { + OverTolerance.setConstraints(&ToleranceConstraint); + if (!TheoreticalExact.getValue()) { + UnderTolerance.setReadOnly(false); + FormatSpecUnderTolerance.setReadOnly(false); } - return; } - else if (prop == &EqualTolerance) { - // if EqualTolerance set negated overtolerance for untertolerance - // then also the OverTolerance must be positive - if (EqualTolerance.getValue()) { - // if OverTolerance is negative or zero, first set it to zero - if (OverTolerance.getValue() < 0) { - OverTolerance.setValue(0.0); - } - OverTolerance.setConstraints(&PositiveConstraint); - UnderTolerance.setValue(-1.0 * OverTolerance.getValue()); - UnderTolerance.setUnit(OverTolerance.getUnit()); - UnderTolerance.setReadOnly(true); - FormatSpecUnderTolerance.setValue(FormatSpecOverTolerance.getValue()); - FormatSpecUnderTolerance.setReadOnly(true); - } - else { - OverTolerance.setConstraints(&ToleranceConstraint); - if (!TheoreticalExact.getValue()) { - UnderTolerance.setReadOnly(false); - FormatSpecUnderTolerance.setReadOnly(false); - } - } - return; + return; + } else if (prop == &OverTolerance) { += // if EqualTolerance set negated overtolerance for untertolerance + if (EqualTolerance.getValue()) { + UnderTolerance.setValue(-1.0 * OverTolerance.getValue()); + UnderTolerance.setUnit(OverTolerance.getUnit()); } - else if (prop == &OverTolerance) { - // if EqualTolerance set negated overtolerance for untertolerance - if (EqualTolerance.getValue()) { - UnderTolerance.setValue(-1.0 * OverTolerance.getValue()); - UnderTolerance.setUnit(OverTolerance.getUnit()); - } - return; + return; + } else if (prop == &FormatSpecOverTolerance) { + if (!ArbitraryTolerances.getValue()) { + FormatSpecUnderTolerance.setValue(FormatSpecOverTolerance.getValue()); } - else if (prop == &FormatSpecOverTolerance) { - if (!ArbitraryTolerances.getValue()) { - FormatSpecUnderTolerance.setValue(FormatSpecOverTolerance.getValue()); - } - return; - } - else if (prop == &FormatSpecUnderTolerance) { - if (!ArbitraryTolerances.getValue()) { - FormatSpecOverTolerance.setValue(FormatSpecUnderTolerance.getValue()); - } - return; + return; + } else if (prop == &FormatSpecUnderTolerance) { + if (!ArbitraryTolerances.getValue()) { + FormatSpecOverTolerance.setValue(FormatSpecUnderTolerance.getValue()); } + return; } DrawView::onChanged(prop); @@ -306,8 +303,8 @@ void DrawViewDimension::onDocumentRestored() setAll3DMeasurement(); } - DrawViewType type = static_cast(Type.getValue()); - if (type == DrawViewType::Angle || type == DrawViewType::Angle3Pt) { + DimensionType type = static_cast(Type.getValue()); + if (type == DimensionType::Angle || type == DimensionType::Angle3Pt) { OverTolerance.setUnit(Base::Unit::Angle); UnderTolerance.setUnit(Base::Unit::Angle); } @@ -344,7 +341,6 @@ void DrawViewDimension::handleChangedPropertyType(Base::XMLReader &reader, const } } - short DrawViewDimension::mustExecute() const { if (!isRestoring()) { @@ -368,28 +364,24 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute() if (!dvp) return App::DocumentObject::StdReturn; - if (!has2DReferences()) { //too soon? - if (isRestoring() || - getDocument()->testStatus(App::Document::Status::Restoring)) { - return App::DocumentObject::StdReturn; - } else { - Base::Console().Warning("%s has no 2D References\n", getNameInDocument()); - } + if (!has2DReferences() && !has3DReferences()) { + //no references, can't do anything return App::DocumentObject::StdReturn; } - //can't do anything until Source has geometry - if (!getViewPart()->hasGeometry()) { //happens when loading saved document + if (!getViewPart()->hasGeometry()) { + //can't do anything until Source has geometry return App::DocumentObject::StdReturn; } - //now we can check if Reference2ds have valid targets. - if (!checkReferences2D()) { - Base::Console().Warning("%s has invalid 2D References\n", getNameInDocument()); + if (References3D.getValues().empty() && + !checkReferences2D()) { + Base::Console().Warning("DVD::execute - %s has invalid 2D References\n", getNameInDocument()); return App::DocumentObject::StdReturn; } - const std::vector &subElements = References2D.getSubValues(); + //we have either or both valid References3D and References2D + ReferenceVector references = getEffectiveReferences(); resetLinear(); resetAngular(); @@ -399,313 +391,32 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute() Type.isValue("DistanceX") || Type.isValue("DistanceY") ) { if (getRefType() == oneEdge) { - m_linearPoints = getPointsOneEdge(); + m_linearPoints = getPointsOneEdge(references); } else if (getRefType() == twoEdge) { - m_linearPoints = getPointsTwoEdges(); + m_linearPoints = getPointsTwoEdges(references); } else if (getRefType() == twoVertex) { - m_linearPoints = getPointsTwoVerts(); + m_linearPoints = getPointsTwoVerts(references); } else if (getRefType() == vertexEdge) { - m_linearPoints = getPointsEdgeVert(); + m_linearPoints = getPointsEdgeVert(references); } m_hasGeometry = true; } else if (Type.isValue("Radius")){ - int idx = DrawUtil::getIndexFromName(subElements[0]); - TechDraw::BaseGeomPtr base = getViewPart()->getGeomByIndex(idx); - TechDraw::CirclePtr circle; - arcPoints pts; - pts.center = Base::Vector3d(0.0, 0.0, 0.0); - pts.radius = 0.0; - if ( (base && base->geomType == TechDraw::GeomType::CIRCLE) || - (base && base->geomType == TechDraw::GeomType::ARCOFCIRCLE)) { - circle = std::static_pointer_cast (base); - pts.center = Base::Vector3d(circle->center.x, circle->center.y, 0.0); - pts.radius = circle->radius; - if (base->geomType == TechDraw::GeomType::ARCOFCIRCLE) { -// TechDraw::AOCPtr aoc = std::static_pointer_cast (circle); - TechDraw::AOCPtr aoc = std::static_pointer_cast (base); - pts.isArc = true; - pts.onCurve.first = Base::Vector3d(aoc->midPnt.x, aoc->midPnt.y, 0.0); - pts.midArc = Base::Vector3d(aoc->midPnt.x, aoc->midPnt.y, 0.0); - pts.arcEnds.first = Base::Vector3d(aoc->startPnt.x, aoc->startPnt.y, 0.0); - pts.arcEnds.second = Base::Vector3d(aoc->endPnt.x, aoc->endPnt.y, 0.0); - pts.arcCW = aoc->cw; - } else { - pts.isArc = false; - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * circle->radius; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * circle->radius; //arbitrary point on edge - } - } else if ((base && base->geomType == TechDraw::GeomType::ELLIPSE) || - (base && base->geomType == TechDraw::GeomType::ARCOFELLIPSE)) { - TechDraw::EllipsePtr ellipse; - ellipse = std::static_pointer_cast (base); - if (ellipse->closed()) { - double r1 = ellipse->minor; - double r2 = ellipse->major; - double rAvg = (r1 + r2) / 2.0; - pts.center = Base::Vector3d(ellipse->center.x, - ellipse->center.y, - 0.0); - pts.radius = rAvg; - pts.isArc = false; - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * rAvg; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * rAvg; //arbitrary point on edge - } else { - TechDraw::AOEPtr aoe = std::static_pointer_cast (base); - double r1 = aoe->minor; - double r2 = aoe->major; - double rAvg = (r1 + r2) / 2.0; - pts.isArc = true; - pts.center = Base::Vector3d(aoe->center.x, - aoe->center.y, - 0.0); - pts.radius = rAvg; - pts.arcEnds.first = Base::Vector3d(aoe->startPnt.x, aoe->startPnt.y, 0.0); - pts.arcEnds.second = Base::Vector3d(aoe->endPnt.x, aoe->endPnt.y, 0.0); - pts.midArc = Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0); - pts.arcCW = aoe->cw; - pts.onCurve.first = Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0); - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * rAvg; //arbitrary point on edge - } - } else if (base && base->geomType == TechDraw::GeomType::BSPLINE) { - TechDraw::BSplinePtr spline; - spline = std::static_pointer_cast (base); - if (spline->isCircle()) { - bool circ, arc; - double rad; - Base::Vector3d center; - spline->getCircleParms(circ, rad, center, arc); - pts.center = Base::Vector3d(center.x, center.y, 0.0); - pts.radius = rad; - pts.arcEnds.first = Base::Vector3d(spline->startPnt.x, spline->startPnt.y, 0.0); - pts.arcEnds.second = Base::Vector3d(spline->endPnt.x, spline->endPnt.y, 0.0); - pts.midArc = Base::Vector3d(spline->midPnt.x, spline->midPnt.y, 0.0); - pts.isArc = arc; - pts.arcCW = spline->cw; - if (arc) { - pts.onCurve.first = Base::Vector3d(spline->midPnt.x, spline->midPnt.y, 0.0); - } else { - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * rad; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * rad; //arbitrary point on edge - } - } else { - //fubar - can't have non-circular spline as target of Radius dimension - Base::Console().Error("Dimension %s refers to invalid BSpline\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - } else { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - m_arcPoints = pts; + m_arcPoints = getArcParameters(references); m_hasGeometry = true; } else if (Type.isValue("Diameter")){ - int idx = DrawUtil::getIndexFromName(subElements[0]); - TechDraw::BaseGeomPtr base = getViewPart()->getGeomByIndex(idx); - TechDraw::CirclePtr circle; - arcPoints pts; - pts.center = Base::Vector3d(0.0, 0.0, 0.0); - pts.radius = 0.0; - if ((base && base->geomType == TechDraw::GeomType::CIRCLE) || - (base && base->geomType == TechDraw::GeomType::ARCOFCIRCLE)) { - circle = std::static_pointer_cast (base); - pts.center = Base::Vector3d(circle->center.x, circle->center.y, 0.0); - pts.radius = circle->radius; - if (base->geomType == TechDraw::GeomType::ARCOFCIRCLE) { - TechDraw::AOCPtr aoc = std::static_pointer_cast (circle); - pts.isArc = true; - pts.onCurve.first = Base::Vector3d(aoc->midPnt.x, aoc->midPnt.y, 0.0); - pts.midArc = Base::Vector3d(aoc->midPnt.x, aoc->midPnt.y, 0.0); - pts.arcEnds.first = Base::Vector3d(aoc->startPnt.x, aoc->startPnt.y, 0.0); - pts.arcEnds.second = Base::Vector3d(aoc->endPnt.x, aoc->endPnt.y, 0.0); - pts.arcCW = aoc->cw; - } else { - pts.isArc = false; - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * circle->radius; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * circle->radius; //arbitrary point on edge - } - } else if ( (base && base->geomType == TechDraw::GeomType::ELLIPSE) || - (base && base->geomType == TechDraw::GeomType::ARCOFELLIPSE) ) { - TechDraw::EllipsePtr ellipse = std::static_pointer_cast (base); - if (ellipse->closed()) { - double r1 = ellipse->minor; - double r2 = ellipse->major; - double rAvg = (r1 + r2) / 2.0; - pts.center = Base::Vector3d(ellipse->center.x, - ellipse->center.y, - 0.0); - pts.radius = rAvg; - pts.isArc = false; - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * rAvg; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * rAvg; //arbitrary point on edge - } else { - TechDraw::AOEPtr aoe = std::static_pointer_cast (base); - double r1 = aoe->minor; - double r2 = aoe->major; - double rAvg = (r1 + r2) / 2.0; - pts.isArc = true; - pts.center = Base::Vector3d(aoe->center.x, - aoe->center.y, - 0.0); - pts.radius = rAvg; - pts.arcEnds.first = Base::Vector3d(aoe->startPnt.x, aoe->startPnt.y, 0.0); - pts.arcEnds.second = Base::Vector3d(aoe->endPnt.x, aoe->endPnt.y, 0.0); - pts.midArc = Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0); - pts.arcCW = aoe->cw; - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * rAvg; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * rAvg; //arbitrary point on edge - } - } else if (base && base->geomType == TechDraw::GeomType::BSPLINE) { - TechDraw::BSplinePtr spline = std::static_pointer_cast (base); - if (spline->isCircle()) { - bool circ, arc; - double rad; - Base::Vector3d center; - spline->getCircleParms(circ, rad, center, arc); - pts.center = Base::Vector3d(center.x, center.y, 0.0); - pts.radius = rad; - pts.arcEnds.first = Base::Vector3d(spline->startPnt.x, spline->startPnt.y, 0.0); - pts.arcEnds.second = Base::Vector3d(spline->endPnt.x, spline->endPnt.y, 0.0); - pts.midArc = Base::Vector3d(spline->midPnt.x, spline->midPnt.y, 0.0); - pts.isArc = arc; - pts.arcCW = spline->cw; - if (arc) { - pts.onCurve.first = Base::Vector3d(spline->midPnt.x, spline->midPnt.y, 0.0); - } else { - pts.onCurve.first = pts.center + Base::Vector3d(1, 0,0) * rad; //arbitrary point on edge - pts.onCurve.second = pts.center + Base::Vector3d(-1, 0,0) * rad; //arbitrary point on edge - } - } else { - //fubar - can't have non-circular spline as target of Diameter dimension - Base::Console().Error("%s: can not make a Circle from this BSpline edge\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - } else { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - m_arcPoints = pts; + m_arcPoints = getArcParameters(references); m_hasGeometry = true; } else if (Type.isValue("Angle")){ if (getRefType() != twoEdge) { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; + throw Base::RuntimeError("Angle dimension has non-edge references"); } - int idx0 = DrawUtil::getIndexFromName(subElements[0]); - int idx1 = DrawUtil::getIndexFromName(subElements[1]); - TechDraw::BaseGeomPtr edge0 = getViewPart()->getGeomByIndex(idx0); - TechDraw::BaseGeomPtr edge1 = getViewPart()->getGeomByIndex(idx1); - TechDraw::GenericPtr gen0; - TechDraw::GenericPtr gen1; - if (edge0 && edge0->geomType == TechDraw::GeomType::GENERIC) { - gen0 = std::static_pointer_cast(edge0); - } else { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - if (edge1 && edge1->geomType == TechDraw::GeomType::GENERIC) { - gen1 = std::static_pointer_cast(edge1); - } else { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - - anglePoints pts; - Base::Vector3d apex = gen0->apparentInter(gen1); - Base::Vector3d extPoint0, extPoint1; - Base::Vector3d farPoint0, farPoint1; - //pick the end of gen0 farthest from the apex - if ((gen0->getStartPoint() - apex).Length() > - (gen0->getEndPoint() - apex).Length()) { - farPoint0 = gen0->getStartPoint(); - } else { - farPoint0 = gen0->getEndPoint(); - } - //pick the end of gen1 farthest from the apex - if ((gen1->getStartPoint() - apex).Length() > - (gen1->getEndPoint() - apex).Length()) { - farPoint1 = gen1->getStartPoint(); - } else { - farPoint1 = gen1->getEndPoint(); - } - - Base::Vector3d l0Dir = (gen0->getStartPoint() - gen0->getEndPoint()).Normalize(); - Base::Vector3d l1Dir = (gen1->getStartPoint() - gen1->getEndPoint()).Normalize(); - extPoint0 = farPoint0; - extPoint1 = farPoint0.Perpendicular(apex, l1Dir); - - //special case for parallel/anti-parallel edges avoids nan - //special case for perpendicular edges avoids nan - double dot = l0Dir.Dot(l1Dir); - Base::Vector3d cross = l0Dir.Cross(l1Dir); - if (DrawUtil::fpCompare(cross.Length(), 0.0)) { - //parallel edges - extPoint1 = farPoint1; - } else if (DrawUtil::fpCompare(dot, 0.0)) { - //perpendicular edges - extPoint1 = farPoint1; - } else { - //skew edges - //project farthest points onto opposite edge - Base::Vector3d projFar0OnL1 = farPoint0.Perpendicular(apex, l1Dir); - Base::Vector3d projFar1OnL0 = farPoint1.Perpendicular(apex, l0Dir); - if (DrawUtil::isBetween(projFar0OnL1, - gen1->getStartPoint(), - gen1->getEndPoint())) { - extPoint0 = farPoint0; - extPoint1 = projFar0OnL1; - } else if (DrawUtil::isBetween(projFar1OnL0, - gen0->getStartPoint(), - gen0->getEndPoint())) { - extPoint0 = projFar1OnL0; - extPoint1 = farPoint1; - } else { - //extPoints are both outside range of respective edges which will - //create the supplementary angle instead of the desired - //angle. Force extPoints to midpoints of edges to get - //viable dimension. - extPoint0 = (gen0->getStartPoint() + gen0->getEndPoint()) / 2.0; - if (extPoint0.IsEqual(apex, Precision::Confusion())) { - //pathological case. can't make dimension from only 2 points. - extPoint0 = farPoint0; - } - extPoint1 = (gen1->getStartPoint() + gen1->getEndPoint()) / 2.0; - if (extPoint1.IsEqual(apex, Precision::Confusion())) { - //pathological case. can't make dimension from only 2 points. - extPoint1 = farPoint1; - } - } - } - - pts.ends.first = extPoint0; - pts.ends.second = extPoint1; - pts.vertex = apex; - m_anglePoints = pts; + m_anglePoints = getAnglePointsTwoEdges(references); m_hasGeometry = true; } else if (Type.isValue("Angle3Pt")){ if (getRefType() != threeVertex) { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; + throw Base::RuntimeError("3 point angle dimension has non-vertex references"); } - int idx0 = DrawUtil::getIndexFromName(subElements[0]); - int idx1 = DrawUtil::getIndexFromName(subElements[1]); - int idx2 = DrawUtil::getIndexFromName(subElements[2]); - - TechDraw::VertexPtr vert0 = getViewPart()->getProjVertexByIndex(idx0); - TechDraw::VertexPtr vert1 = getViewPart()->getProjVertexByIndex(idx1); - TechDraw::VertexPtr vert2 = getViewPart()->getProjVertexByIndex(idx2); - if (!vert0 || !vert1 || !vert2) { - Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); - return App::DocumentObject::StdReturn; - } - - anglePoints pts; - Base::Vector3d apex = vert1->point(); - Base::Vector3d extPoint0 = vert0->point(); - Base::Vector3d extPoint2 = vert2->point(); - pts.ends.first = extPoint0; - pts.ends.second = extPoint2; - pts.vertex = apex; - m_anglePoints = pts; + m_anglePoints = getAnglePointsThreeVerts(references); m_hasGeometry = true; } @@ -713,45 +424,10 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute() return DrawView::execute(); } +////TODO: schema not report their multiValue status bool DrawViewDimension::isMultiValueSchema() const { - bool angularMeasure = (Type.isValue("Angle") || Type.isValue("Angle3Pt")); - - Base::UnitSystem uniSys = Base::UnitsApi::getSchema(); - if (uniSys == Base::UnitSystem::ImperialBuilding && - !angularMeasure) { - return true; - } else if (uniSys == Base::UnitSystem::ImperialCivil && - !angularMeasure) { - return true; - } - return false; -} - -std::string DrawViewDimension::getBaseLengthUnit(Base::UnitSystem system) -{ - switch (system) { - case Base::UnitSystem::SI1: - return "mm"; - case Base::UnitSystem::SI2: - return "m"; - case Base::UnitSystem::Imperial1: - return "in"; - case Base::UnitSystem::ImperialDecimal: - return "in"; - case Base::UnitSystem::Centimeters: - return "cm"; - case Base::UnitSystem::ImperialBuilding: - return "ft"; - case Base::UnitSystem::MmMin: - return "mm"; - case Base::UnitSystem::ImperialCivil: - return "ft"; - case Base::UnitSystem::FemMilliMeterNewton: - return "mm"; - default: - return "Unknown schema"; - } + return m_formatter->isMultiValueSchema(); } std::string DrawViewDimension::formatValue(qreal value, @@ -759,203 +435,10 @@ std::string DrawViewDimension::formatValue(qreal value, int partial, bool isDim) { - QString qUserStringUnits; - QString formattedValue; - bool angularMeasure = false; - QLocale loc; - - Base::Quantity asQuantity; - asQuantity.setValue(value); - if ( (Type.isValue("Angle")) || - (Type.isValue("Angle3Pt")) ) { - angularMeasure = true; - asQuantity.setUnit(Base::Unit::Angle); - } else { - asQuantity.setUnit(Base::Unit::Length); - } - - QString qUserString = asQuantity.getUserString(); // this handles mm to inch/km/parsec etc - // and decimal positions but won't give more than - // Global_Decimals precision - - //units api: get schema to figure out if this is multi-value schema(Imperial1, ImperialBuilding, etc) - //if it is multi-unit schema, don't even try to use Alt Decimals - Base::UnitSystem unitSystem = Base::UnitsApi::getSchema(); - - // we need to know what length unit is used by the scheme - std::string BaseLengthUnit = getBaseLengthUnit(unitSystem); - - //get formatSpec prefix/suffix/specifier - QStringList qsl = getPrefixSuffixSpec(qFormatSpec); - QString formatPrefix = qsl[0]; //FormatSpec prefix - QString formatSuffix = qsl[1]; //FormatSpec suffix - QString formatSpecifier = qsl[2]; //FormatSpec specifier - - //handle multi value schemes (yd/ft/in, dms, etc) - std::string genPrefix = getPrefix(); //general prefix - diameter, radius, etc - QString qMultiValueStr; - QString qGenPrefix = QString::fromUtf8(genPrefix.data(), genPrefix.size()); - if ((unitSystem == Base::UnitSystem::ImperialCivil) && angularMeasure) { - QString dispMinute = QString::fromUtf8("\'"); - QString dispSecond = QString::fromUtf8("\""); - QString schemeMinute = QString::fromUtf8("M"); - QString schemeSecond = QString::fromUtf8("S"); - QString displaySub = qUserString.replace(schemeMinute, dispMinute); - displaySub = displaySub.replace(schemeSecond, dispSecond); - qMultiValueStr = displaySub; - if (!genPrefix.empty()) { - // prefix + 48*30'30" + suffix - qMultiValueStr = formatPrefix + qGenPrefix + displaySub + formatSuffix; - } - formattedValue = qMultiValueStr; - } else if (isMultiValueSchema()) { - qMultiValueStr = qUserString; - if (!genPrefix.empty()) { - //qUserString from Quantity includes units - prefix + R + nnn ft + suffix - qMultiValueStr = formatPrefix + qUserString + formatSuffix; - } - return qMultiValueStr.toStdString(); - } else { //not multivalue schema - if (formatSpecifier.isEmpty()) { - Base::Console().Warning("Warning - no numeric format in Format Spec %s - %s\n", - qPrintable(qFormatSpec), getNameInDocument()); - return Base::Tools::toStdString(qFormatSpec); - } - - // for older TD drawings the formatSpecifier "%g" was used, but the number of decimals was - // neverheless limited. To keep old drawings, we limit the number of decimals too - // if the TD preferences option to use the global decimal number is set - // the formatSpecifier can have a prefix and/or suffix - if (useDecimals() && formatSpecifier.contains(QString::fromLatin1("%g"), Qt::CaseInsensitive)) { - int globalPrecision = Base::UnitsApi::getDecimals(); - // change formatSpecifier to e.g. "%.2f" - QString newSpecifier = QString::fromStdString("%." + std::to_string(globalPrecision) + "f"); - formatSpecifier.replace(QString::fromLatin1("%g"), newSpecifier, Qt::CaseInsensitive); - } - - // qUserString is the value + unit with default decimals, so extract the unit - // we cannot just use unit.getString() because this would convert '°' to 'deg' - QRegularExpression rxUnits(QString::fromUtf8(" \\D*$")); // space + any non digits at end of string - QRegularExpressionMatch rxMatch; - int pos = qUserString.indexOf(rxUnits, 0, &rxMatch); - if (pos != -1) { - qUserStringUnits = rxMatch.captured(0); // entire capture - non numerics at end of qUserString - } - - // get value in the base unit with default decimals - // for the conversion we use the same method as in DlgUnitsCalculator::valueChanged - // get the conversion factor for the unit - double convertValue = Base::Quantity::parse(QString::fromLatin1("1") + QString::fromStdString(BaseLengthUnit)).getValue(); - // the result is now just val / convertValue because val is always in the base unit - // don't do this for angular values since they are not in the BaseLengthUnit - double userVal; - if (!angularMeasure) { - userVal = asQuantity.getValue() / convertValue; - // since we converted to the BaseLengthUnit we must assure it is also used for qUserStringUnits - qUserStringUnits = QChar::fromLatin1(' ') + QString::fromStdString(BaseLengthUnit); - } - else { - userVal = asQuantity.getValue(); - } - - // we reformat the value - // the user can overwrite the decimal settings, so we must in every case use the formatSpecifier - // the default is: if useDecimals(), then formatSpecifier = global decimals, otherwise it is %.2f - // Also, handle the new non-standard format-specifier '%w', which has the following rules: works as %f, but no trailing zeros - if (formatSpecifier.contains(QRegularExpression(QStringLiteral("%.*[wW]")))) { - QString fs = formatSpecifier; - fs.replace(QRegularExpression(QStringLiteral("%(.*)w")), QStringLiteral("%\\1f")); - fs.replace(QRegularExpression(QStringLiteral("%(.*)W")), QStringLiteral("%\\1F")); - formattedValue = QString::asprintf(Base::Tools::toStdString(fs).c_str(), userVal); - // First, try to cut trailing zeros, if AFTER decimal dot there are nonzero numbers - // Second, try to cut also decimal dot and zeros, if there are just zeros after it - formattedValue.replace(QRegularExpression(QStringLiteral("([0-9][0-9]*\\.[0-9]*[1-9])00*$")), QStringLiteral("\\1")); - formattedValue.replace(QRegularExpression(QStringLiteral("([0-9][0-9]*)\\.0*$")), QStringLiteral("\\1")); - } else { - formattedValue = QString::asprintf(Base::Tools::toStdString(formatSpecifier).c_str(), userVal); - } - - // if abs(1 - userVal / formattedValue) > 0.1 we know that we make an error greater than 10% - // then we need more digits - if (abs(userVal - formattedValue.toDouble()) > 0.1 * abs(userVal)) { - int i = 1; - do { // increase decimals step by step until error is < 10 % - formattedValue = QLocale().toString(userVal, 'f', i); - ++i; - } while (abs(userVal - loc.toDouble(formattedValue)) > 0.1 * abs(userVal)); - // We purposely don't reset the formatSpecifier. - // Why "%.1f" is overwritten for a value of e.g. "0.001" is obvious, - // moreover such cases only occurs when - // changing unit schemes on existing drawings. Moreover a typical case is that - // you accidentally used e.g. a building scheme, see your mistake and go back - // then you would end up with e.g. "%.5f" and must manually correct this. - } - - // replace decimal sign if necessary - QChar dp = QChar::fromLatin1('.'); - if (loc.decimalPoint() != dp) { - formattedValue.replace(dp, loc.decimalPoint()); - } - } - - - std::string formattedValueString = formattedValue.toStdString(); - - if (partial == 0) { //full text for multi-value schemas - return Base::Tools::toStdString(formatPrefix) + - Base::Tools::toStdString(qMultiValueStr) + - Base::Tools::toStdString(formatSuffix) + - Base::Tools::toStdString(qUserStringUnits); - } - else if (partial == 1) { // prefix number[unit] suffix - // remove space between dimension and ° (U+00B0) - // other units need 1 space for readability - if ( angularMeasure && - !qUserStringUnits.contains(QString::fromLatin1("deg")) ) { - QRegularExpression space(QString::fromUtf8("\\s")); - qUserStringUnits.remove(space); - } - if (angularMeasure) { - //always insert unit after value - return Base::Tools::toStdString(formatPrefix) + - formattedValueString + - Base::Tools::toStdString(qUserStringUnits) + - Base::Tools::toStdString(formatSuffix); - } else if (showUnits()){ - if (isDim && haveTolerance()) { - //unit will be included in tolerance so don't repeat it here - return Base::Tools::toStdString(formatPrefix) + - formattedValueString + - Base::Tools::toStdString(formatSuffix); - } else { - //no tolerance, so we need to include unit - return Base::Tools::toStdString(formatPrefix) + - formattedValueString + - Base::Tools::toStdString(qUserStringUnits) + - Base::Tools::toStdString(formatSuffix); - } - } else { - return Base::Tools::toStdString(formatPrefix) + - formattedValueString + - Base::Tools::toStdString(formatSuffix); - } - } - else if (partial == 2) { // just the unit - if (angularMeasure) { - // remove leading space from unit if unit is not "deg" - if ( !qUserStringUnits.contains(QString::fromLatin1("deg")) ) { - QRegularExpression space(QString::fromUtf8("\\s")); - qUserStringUnits.remove(space); - } - return Base::Tools::toStdString(qUserStringUnits); - } else if (showUnits()) { - return Base::Tools::toStdString(qUserStringUnits); - } else { - return ""; - } - } - - return formattedValueString; + return m_formatter->formatValue(value, + qFormatSpec, + partial, + isDim); } bool DrawViewDimension::haveTolerance() @@ -972,156 +455,33 @@ bool DrawViewDimension::haveTolerance() std::string DrawViewDimension::getFormattedToleranceValue(int partial) { - QString FormatSpec = QString::fromUtf8(FormatSpecOverTolerance.getStrValue().data()); - QString ToleranceString; - - if (ArbitraryTolerances.getValue()) - ToleranceString = FormatSpec; - else - ToleranceString = QString::fromUtf8(formatValue(OverTolerance.getValue(), - FormatSpec, - partial, - false).c_str()); - - return ToleranceString.toStdString(); + return m_formatter->getFormattedToleranceValue(partial); } -//get over and under tolerances +////get over and under tolerances std::pair DrawViewDimension::getFormattedToleranceValues(int partial) { - QString underFormatSpec = QString::fromUtf8(FormatSpecUnderTolerance.getStrValue().data()); - QString overFormatSpec = QString::fromUtf8(FormatSpecOverTolerance.getStrValue().data()); - std::pair tolerances; - QString underTolerance, overTolerance; - - if (ArbitraryTolerances.getValue()) { - underTolerance = underFormatSpec; - overTolerance = overFormatSpec; - } else { - if (DrawUtil::fpCompare(UnderTolerance.getValue(), 0.0)) { - underTolerance = QString::fromUtf8(formatValue(UnderTolerance.getValue(), - QString::fromUtf8("%.0f"), - partial, - false).c_str()); - } - else { - underTolerance = QString::fromUtf8(formatValue(UnderTolerance.getValue(), - underFormatSpec, - partial, - false).c_str()); - } - if (DrawUtil::fpCompare(OverTolerance.getValue(), 0.0)) { - overTolerance = QString::fromUtf8(formatValue(OverTolerance.getValue(), - QString::fromUtf8("%.0f"), - partial, - false).c_str()); - } - else { - overTolerance = QString::fromUtf8(formatValue(OverTolerance.getValue(), - overFormatSpec, - partial, - false).c_str()); - } - } - - tolerances.first = underTolerance.toStdString(); - tolerances.second = overTolerance.toStdString(); - - return tolerances; + return m_formatter->getFormattedToleranceValues(partial); } -//partial = 2 unit only +////partial = 2 unit only std::string DrawViewDimension::getFormattedDimensionValue(int partial) { - QString qFormatSpec = QString::fromUtf8(FormatSpec.getStrValue().data()); - - if ( (Arbitrary.getValue() && !EqualTolerance.getValue()) - || (Arbitrary.getValue() && TheoreticalExact.getValue()) ) { - return FormatSpec.getStrValue(); - } - - if (Arbitrary.getValue()) { - return FormatSpec.getStrValue(); - } - - // if there is an equal over-/undertolerance (so only 1 tolerance to show with +/-) and - // not theoretically exact (which has no tolerance), and - // tolerance has been specified, ie - // (OverTolerance != 0.0 (so a tolerance has been specified) or - // ArbitraryTolerances are specified) - // concatenate the tolerance to dimension - if (EqualTolerance.getValue() && - !TheoreticalExact.getValue() && - (!DrawUtil::fpCompare(OverTolerance.getValue(), 0.0) || ArbitraryTolerances.getValue())) { - QString labelText = QString::fromUtf8(formatValue(getDimValue(), - qFormatSpec, - 1, - true).c_str()); //just the number pref/spec[unit]/suf - QString unitText = QString::fromUtf8(formatValue(getDimValue(), - qFormatSpec, - 2, - false).c_str()); //just the unit - QString tolerance = QString::fromStdString(getFormattedToleranceValue(1).c_str()); - - // tolerance might start with a plus sign that we don't want, so cut it off - // note plus sign is not at pos = 0! - QRegularExpression plus(QString::fromUtf8("^\\s*\\+")); - tolerance.remove(plus); - - return (labelText + - QString::fromUtf8(" \xC2\xB1 ") + // +/- symbol - tolerance).toStdString(); - - if (partial == 2) { - return unitText.toStdString(); - } - - return ""; - } - - //tolerance not specified, so just format dimension value? - std::string formattedValue = formatValue(getDimValue(), qFormatSpec, partial, true); - - return formattedValue; + return m_formatter->getFormattedDimensionValue(partial); } QStringList DrawViewDimension::getPrefixSuffixSpec(QString fSpec) { - QStringList result; - //find the %x.y tag in FormatSpec - QRegularExpression rxFormat(QStringLiteral("%[+-]?[0-9]*\\.*[0-9]*[aefgwAEFGW]")); //printf double format spec - QRegularExpressionMatch rxMatch; - int pos = fSpec.indexOf(rxFormat, 0, &rxMatch); - if (pos != -1) { - QString match = rxMatch.captured(0); //entire capture of rx - QString formatPrefix = fSpec.left(pos); - result.append(formatPrefix); - QString formatSuffix = fSpec.right(fSpec.size() - pos - match.size()); - result.append(formatSuffix); - result.append(match); - } else { //printf format not found! - Base::Console().Warning("Warning - no numeric format in formatSpec %s - %s\n", - qPrintable(fSpec), getNameInDocument()); - result.append(QString()); - result.append(QString()); - result.append(fSpec); - } - return result; + return m_formatter->getPrefixSuffixSpec(fSpec); } - //!NOTE: this returns the Dimension value in internal units (ie mm)!!!! double DrawViewDimension::getDimValue() { // Base::Console().Message("DVD::getDimValue()\n"); double result = 0.0; - if (!has2DReferences()) { //too soon? - if (isRestoring() || - getDocument()->testStatus(App::Document::Status::Restoring)) { - return result; - } else { - Base::Console().Warning("%s has no 2D References\n", getNameInDocument()); - } + if (!has2DReferences() && !has3DReferences()) { + //nothing to measure return result; } if (!getViewPart()) @@ -1144,7 +504,8 @@ double DrawViewDimension::getDimValue() result = measurement->radius(); } else if (Type.isValue("Diameter")){ result = 2.0 * measurement->radius(); - } else if (Type.isValue("Angle")){ + } else if (Type.isValue("Angle") || + Type.isValue("Angle3Pt") ) { result = measurement->angle(); } else { //tarfu throw Base::ValueError("getDimValue() - Unknown Dimension Type (3)"); @@ -1159,7 +520,7 @@ double DrawViewDimension::getDimValue() Type.isValue("DistanceX") || Type.isValue("DistanceY") ) { pointPair pts = getLinearPoints(); - Base::Vector3d dimVec = pts.first - pts.second; + Base::Vector3d dimVec = pts.first() - pts.second(); if (Type.isValue("Distance")) { result = dimVec.Length() / getViewPart()->getScale(); } else if (Type.isValue("DistanceX")) { @@ -1167,24 +528,20 @@ double DrawViewDimension::getDimValue() } else { result = fabs(dimVec.y) / getViewPart()->getScale(); } - } else if (Type.isValue("Radius")){ arcPoints pts = m_arcPoints; result = pts.radius / getViewPart()->getScale(); //Projected BaseGeom is scaled for drawing - } else if (Type.isValue("Diameter")){ arcPoints pts = m_arcPoints; result = (pts.radius * 2.0) / getViewPart()->getScale(); //Projected BaseGeom is scaled for drawing - } else if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) { //same as case "Angle"? anglePoints pts = m_anglePoints; - Base::Vector3d vertex = pts.vertex; - Base::Vector3d leg0 = pts.ends.first - vertex; - Base::Vector3d leg1 = pts.ends.second - vertex; + Base::Vector3d vertex = pts.vertex(); + Base::Vector3d leg0 = pts.first() - vertex; + Base::Vector3d leg1 = pts.second() - vertex; double legAngle = leg0.GetAngle(leg1) * 180.0 / M_PI; result = legAngle; - } } @@ -1200,83 +557,539 @@ double DrawViewDimension::getDimValue() return result; } -pointPair DrawViewDimension::getPointsOneEdge() +pointPair DrawViewDimension::getPointsOneEdge(ReferenceVector references) { -// Base::Console().Message("DVD::getPointsOneEdge() - %s\n", getNameInDocument()); - const std::vector &subElements = References2D.getSubValues(); - - //TODO: Check for straight line Edge? - int idx = DrawUtil::getIndexFromName(subElements[0]); - TechDraw::BaseGeomPtr geom = getViewPart()->getGeomByIndex(idx); - if (!geom || geom->geomType != TechDraw::GeomType::GENERIC) { - Base::Console().Error("Error: DVD - %s - 2D references are corrupt (1)\n", getNameInDocument()); - return pointPair(); +// Base::Console().Message("DVD::getPointsOneEdge()\n"); + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement = DrawUtil::getIndexFromName(references.front().getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //TODO: Notify if not straight line Edge? + //this is a 2d object (a DVP + subelements) + TechDraw::BaseGeomPtr geom = getViewPart()->getGeomByIndex(iSubelement); + if (!geom || geom->geomType != TechDraw::GeomType::GENERIC) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + TechDraw::GenericPtr generic = std::static_pointer_cast(geom); + return { generic->points[0], generic->points[1] }; } - TechDraw::GenericPtr gen = std::static_pointer_cast(geom); - return pointPair(gen->points[0], gen->points[1]); + //this is a 3d object + //get the endpoints of the edge in the DVP's coordinates + Base::Vector3d edgeEnd0, edgeEnd1; + TopoDS_Shape geometry = references.front().getGeometry(); + if (geometry.IsNull() || + geometry.ShapeType() != TopAbs_EDGE) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + const TopoDS_Edge& edge = TopoDS::Edge(geometry); + gp_Pnt gEnd0 = BRep_Tool::Pnt(TopExp::FirstVertex(edge)); + gp_Pnt gEnd1 = BRep_Tool::Pnt(TopExp::LastVertex(edge)); + gp_Pnt gCentroid = DrawUtil::togp_Pnt(getViewPart()->getOriginalCentroid()); + gEnd0 = gp_Pnt(gEnd0.XYZ() - gCentroid.XYZ()); + gEnd1 = gp_Pnt(gEnd1.XYZ() - gCentroid.XYZ()); + //project points onto paperplane, centered, scaled, rotated and inverted + edgeEnd0 = getViewPart()->projectPoint(DrawUtil::toVector3d(gEnd0)) * getViewPart()->getScale(); + edgeEnd1 = getViewPart()->projectPoint(DrawUtil::toVector3d(gEnd1)) * getViewPart()->getScale(); + + return { edgeEnd0, edgeEnd1 }; } -pointPair DrawViewDimension::getPointsTwoEdges() +pointPair DrawViewDimension::getPointsTwoEdges(ReferenceVector references) { // Base::Console().Message("DVD::getPointsTwoEdges() - %s\n", getNameInDocument()); - pointPair result; - const std::vector &subElements = References2D.getSubValues(); - - int idx0 = DrawUtil::getIndexFromName(subElements[0]); - int idx1 = DrawUtil::getIndexFromName(subElements[1]); - TechDraw::BaseGeomPtr geom0 = getViewPart()->getGeomByIndex(idx0); - TechDraw::BaseGeomPtr geom1 = getViewPart()->getGeomByIndex(idx1); - if (!geom0 || !geom1) { - Base::Console().Error("Error: DVD - %s - 2D references are corrupt (2)\n", getNameInDocument()); - return result; + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement0 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + int iSubelement1 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //this is a 2d object (a DVP + subelements) + TechDraw::BaseGeomPtr geom0 = getViewPart()->getGeomByIndex(iSubelement0); + TechDraw::BaseGeomPtr geom1 = getViewPart()->getGeomByIndex(iSubelement1); + if (!geom0 || !geom1) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + return closestPoints(geom0->occEdge, geom1->occEdge); } - result = closestPoints(geom0->occEdge, geom1->occEdge); - return result; + + //this is a 3d object + TopoDS_Shape geometry0 = references.at(0).getGeometry(); + TopoDS_Shape geometry1 = references.at(1).getGeometry(); + if (geometry0.IsNull() || geometry1.IsNull() || + geometry0.ShapeType() != TopAbs_EDGE || geometry1.ShapeType() != TopAbs_EDGE) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + + pointPair pts = closestPoints(geometry0, geometry1); + pts.move(getViewPart()->getOriginalCentroid()); + pts.project(getViewPart()); + pts.mapToPage(getViewPart()); + pts.invertY(); + return pts; } -pointPair DrawViewDimension::getPointsTwoVerts() +pointPair DrawViewDimension::getPointsTwoVerts(ReferenceVector references) { // Base::Console().Message("DVD::getPointsTwoVerts() - %s\n", getNameInDocument()); - pointPair result; - const std::vector &subElements = References2D.getSubValues(); - - int idx0 = DrawUtil::getIndexFromName(subElements[0]); - int idx1 = DrawUtil::getIndexFromName(subElements[1]); - TechDraw::VertexPtr v0 = getViewPart()->getProjVertexByIndex(idx0); - TechDraw::VertexPtr v1 = getViewPart()->getProjVertexByIndex(idx1); - if (!v0 || !v1) { - Base::Console().Error("Error: DVD - %s - 2D references are corrupt (3)\n", getNameInDocument()); - return result; + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement0 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //this is a 2d object (a DVP + subelements) + TechDraw::VertexPtr v0 = getViewPart()->getProjVertexByIndex(iSubelement0); + TechDraw::VertexPtr v1 = getViewPart()->getProjVertexByIndex(iSubelement1); + if (!v0 || !v1) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + return { v0->pnt, v1->pnt }; } - result.first = v0->pnt; - result.second = v1->pnt; - return result; + + //this is a 3d object + TopoDS_Shape geometry0 = references.at(0).getGeometry(); + TopoDS_Shape geometry1 = references.at(1).getGeometry(); + if (geometry0.IsNull() || geometry1.IsNull() || + geometry0.ShapeType() != TopAbs_VERTEX || geometry1.ShapeType() != TopAbs_VERTEX) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + const TopoDS_Vertex& vertex0 = TopoDS::Vertex(geometry0); + const TopoDS_Vertex& vertex1 = TopoDS::Vertex(geometry1); + gp_Pnt gPoint0 = BRep_Tool::Pnt(vertex0); + gp_Pnt gPoint1 = BRep_Tool::Pnt(vertex1); + gp_Pnt gCentroid = DrawUtil::togp_Pnt(getViewPart()->getOriginalCentroid()); + gPoint0 = gp_Pnt(gPoint0.XYZ() - gCentroid.XYZ()); + gPoint1 = gp_Pnt(gPoint1.XYZ() - gCentroid.XYZ()); + //project points onto paperplane, centered, scaled, rotated and inverted + Base::Vector3d vPoint0 = getViewPart()->projectPoint(DrawUtil::toVector3d(gPoint0)) * getViewPart()->getScale(); + Base::Vector3d vPoint1 = getViewPart()->projectPoint(DrawUtil::toVector3d(gPoint1)) * getViewPart()->getScale(); + return { vPoint0, vPoint1 }; } -pointPair DrawViewDimension::getPointsEdgeVert() +pointPair DrawViewDimension::getPointsEdgeVert(ReferenceVector references) { - pointPair result; - const std::vector &subElements = References2D.getSubValues(); - int idx0 = DrawUtil::getIndexFromName(subElements[0]); - int idx1 = DrawUtil::getIndexFromName(subElements[1]); - TechDraw::BaseGeomPtr e; - TechDraw::VertexPtr v; - if (DrawUtil::getGeomTypeFromName(subElements[0]) == "Edge") { - e = getViewPart()->getGeomByIndex(idx0); - v = getViewPart()->getProjVertexByIndex(idx1); +// Base::Console().Message("DVD::getPointsEdgeVert() - %s\n", getNameInDocument()); + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement0 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + int iSubelement1 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //this is a 2d object (a DVP + subelements) + TechDraw::BaseGeomPtr edge; + TechDraw::VertexPtr vertex; + if (DrawUtil::getGeomTypeFromName(references.at(0).getSubName()) == "Edge") { + edge = getViewPart()->getGeomByIndex(iSubelement0); + vertex = getViewPart()->getProjVertexByIndex(iSubelement1); + } else { + edge = getViewPart()->getGeomByIndex(iSubelement1); + vertex = getViewPart()->getProjVertexByIndex(iSubelement0); + } + if (!vertex || !edge) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + return closestPoints(edge->occEdge, vertex->occVertex); + } + + //this is a 3d object + TopoDS_Shape geometry0 = references.at(0).getGeometry(); + TopoDS_Shape geometry1 = references.at(1).getGeometry(); + if (geometry0.IsNull() || geometry1.IsNull() || + geometry0.ShapeType() != TopAbs_VERTEX || geometry1.ShapeType() != TopAbs_VERTEX) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + + pointPair pts = closestPoints(geometry0, geometry1); + pts.move(getViewPart()->getOriginalCentroid()); + pts.project(getViewPart()); + pts.mapToPage(getViewPart()); + pts.invertY(); + return pts; +} + +arcPoints DrawViewDimension::getArcParameters(ReferenceVector references) +{ +// Base::Console().Message("DVD::getArcParameters()\n"); + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement = DrawUtil::getIndexFromName(references.front().getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //this is a 2d object (a DVP + subelements) + TechDraw::BaseGeomPtr geom = getViewPart()->getGeomByIndex(iSubelement); + if (!geom) { + Base::Console().Error("DVD - %s - 2D references are corrupt (1)\n",getNameInDocument()); + return arcPoints(); + } + return arcPointsFromBaseGeom(getViewPart()->getGeomByIndex(iSubelement)); + } + + //this is a 3d reference + TopoDS_Shape geometry = references.front().getGeometry(); + if (geometry.IsNull() || + geometry.ShapeType() != TopAbs_EDGE) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + const TopoDS_Edge& edge = TopoDS::Edge(geometry); + arcPoints pts = arcPointsFromEdge(edge); + pts.move(getViewPart()->getOriginalCentroid()); + pts.project(getViewPart()); + pts.mapToPage(getViewPart()); + pts.invertY(); + return pts; +} + +arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base) +{ + TechDraw::CirclePtr circle; + arcPoints pts; + pts.center = Base::Vector3d(0.0, 0.0, 0.0); + pts.radius = 0.0; + if ((base && base->geomType == TechDraw::GeomType::CIRCLE) || + (base && base->geomType == TechDraw::GeomType::ARCOFCIRCLE)) { + circle = std::static_pointer_cast (base); + pts.center = Base::Vector3d(circle->center.x, circle->center.y, 0.0); + pts.radius = circle->radius; + if (base->geomType == TechDraw::GeomType::ARCOFCIRCLE) { + TechDraw::AOCPtr aoc = std::static_pointer_cast (circle); + pts.isArc = true; + pts.onCurve.first(Base::Vector3d(aoc->midPnt.x, aoc->midPnt.y, 0.0)); + pts.midArc = Base::Vector3d(aoc->midPnt.x, aoc->midPnt.y, 0.0); + pts.arcEnds.first(Base::Vector3d(aoc->startPnt.x, aoc->startPnt.y, 0.0)); + pts.arcEnds.second(Base::Vector3d(aoc->endPnt.x, aoc->endPnt.y, 0.0)); + pts.arcCW = aoc->cw; + } else { + pts.isArc = false; + pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * circle->radius); //arbitrary point on edge + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * circle->radius); //arbitrary point on edge + } + } else if ( (base && base->geomType == TechDraw::GeomType::ELLIPSE) || + (base && base->geomType == TechDraw::GeomType::ARCOFELLIPSE) ) { + TechDraw::EllipsePtr ellipse = std::static_pointer_cast (base); + if (ellipse->closed()) { + double r1 = ellipse->minor; + double r2 = ellipse->major; + double rAvg = (r1 + r2) / 2.0; + pts.center = Base::Vector3d(ellipse->center.x, + ellipse->center.y, + 0.0); + pts.radius = rAvg; + pts.isArc = false; + pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * rAvg); //arbitrary point on edge + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * rAvg); //arbitrary point on edge + } else { + TechDraw::AOEPtr aoe = std::static_pointer_cast (base); + double r1 = aoe->minor; + double r2 = aoe->major; + double rAvg = (r1 + r2) / 2.0; + pts.isArc = true; + pts.center = Base::Vector3d(aoe->center.x, + aoe->center.y, + 0.0); + pts.radius = rAvg; + pts.arcEnds.first(Base::Vector3d(aoe->startPnt.x, aoe->startPnt.y, 0.0)); + pts.arcEnds.second(Base::Vector3d(aoe->endPnt.x, aoe->endPnt.y, 0.0)); + pts.midArc = Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0); + pts.arcCW = aoe->cw; + pts.onCurve.first(Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0)); //for radius +// pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * rAvg); //for diameter + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * rAvg); //arbitrary point on edge + } + } else if (base && base->geomType == TechDraw::GeomType::BSPLINE) { + TechDraw::BSplinePtr spline = std::static_pointer_cast (base); + if (spline->isCircle()) { + bool arc; + double rad; + Base::Vector3d center; + //bool circ = + GeometryUtils::getCircleParms(spline->occEdge, rad, center, arc); + pts.center = Base::Vector3d(center.x, center.y, 0.0); + pts.radius = rad; + pts.arcEnds.first(Base::Vector3d(spline->startPnt.x, spline->startPnt.y, 0.0)); + pts.arcEnds.second(Base::Vector3d(spline->endPnt.x, spline->endPnt.y, 0.0)); + pts.midArc = Base::Vector3d(spline->midPnt.x, spline->midPnt.y, 0.0); + pts.isArc = arc; + pts.arcCW = spline->cw; + if (arc) { + pts.onCurve.first(Base::Vector3d(spline->midPnt.x, spline->midPnt.y, 0.0)); + } else { + pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * rad); //arbitrary point on edge + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * rad); //arbitrary point on edge + } + } else { + //fubar - can't have non-circular spline as target of Diameter dimension, but this is already + //checked, so something has gone badly wrong. + Base::Console().Error("%s: can not make a Circle from this BSpline edge\n", getNameInDocument()); + throw Base::RuntimeError("Bad BSpline geometry for arc dimension"); + } } else { - e = getViewPart()->getGeomByIndex(idx1); - v = getViewPart()->getProjVertexByIndex(idx0); + Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n", getNameInDocument()); + throw Base::RuntimeError("Bad geometry for arc dimension"); } - if (!v || !e) { - Base::Console().Error("Error: DVD - %s - 2D references are corrupt (4)\n", getNameInDocument()); - return result; + return pts; +} + +arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) +{ + arcPoints pts; + pts.isArc = !BRep_Tool::IsClosed(occEdge); + pts.arcCW = false; + BRepAdaptor_Curve adapt(occEdge); + double pFirst = adapt.FirstParameter(); + double pLast = adapt.LastParameter(); + double pMid = (pFirst + pLast) / 2.0; + BRepLProp_CLProps props(adapt, pFirst, 0, Precision::Confusion()); + pts.arcEnds.first(DU::toVector3d(props.Value())); + props.SetParameter(pLast); + pts.arcEnds.second(DU::toVector3d(props.Value())); + props.SetParameter(pMid); + pts.onCurve.first(DU::toVector3d(props.Value())); + pts.midArc = DU::toVector3d(props.Value()); + + if (adapt.GetType() == GeomAbs_Circle) { + gp_Circ circle = adapt.Circle(); + pts.center = DU::toVector3d(circle.Location()); + pts.radius = circle.Radius(); + if (pts.isArc) { + //part of circle + gp_Ax1 axis = circle.Axis(); + gp_Vec startVec = DU::togp_Vec(pts.arcEnds.first() - pts.center); + gp_Vec endVec = DU::togp_Vec(pts.arcEnds.second() - pts.center); + double angle = startVec.AngleWithRef(endVec, axis.Direction().XYZ()); + pts.arcCW = (angle < 0.0); + } else { + //full circle + pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * pts.radius); //arbitrary point on edge + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * pts.radius); //arbitrary point on edge + } + } else if (adapt.GetType() == GeomAbs_Ellipse) { + gp_Elips ellipse = adapt.Ellipse(); + pts.center = DU::toVector3d(ellipse.Location()); + pts.radius = (ellipse.MajorRadius() + ellipse.MinorRadius()) / 2.0; + if (pts.isArc) { + //part of ellipse + gp_Ax1 axis = ellipse.Axis(); + gp_Vec startVec = DU::togp_Vec(pts.arcEnds.first() - pts.center); + gp_Vec endVec = DU::togp_Vec(pts.arcEnds.second() - pts.center); + double angle = startVec.AngleWithRef(endVec, axis.Direction().XYZ()); + pts.arcCW = (angle < 0.0); + } else { + //full ellipse + pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * pts.radius); //arbitrary point on edge + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * pts.radius); //arbitrary point on edge + } + } else if (adapt.GetType() == GeomAbs_BSplineCurve) { + if (GeometryUtils::isCircle(occEdge)) { + bool isArc(false); + TopoDS_Edge circleEdge = GeometryUtils::asCircle(occEdge, isArc); + pts.isArc = isArc; + BRepAdaptor_Curve adaptCircle(circleEdge); + if (adaptCircle.GetType() != GeomAbs_Circle) { + throw Base::RuntimeError("failed to get circle from bspline"); + } + gp_Circ circle = adapt.Circle(); + //TODO: same code as above. reuse opportunity. + pts.center = DU::toVector3d(circle.Location()); + pts.radius = circle.Radius(); + if (pts.isArc) { + //part of circle + gp_Ax1 axis = circle.Axis(); + gp_Vec startVec = DU::togp_Vec(pts.arcEnds.first() - pts.center); + gp_Vec endVec = DU::togp_Vec(pts.arcEnds.second() - pts.center); + double angle = startVec.AngleWithRef(endVec, axis.Direction().XYZ()); + pts.arcCW = (angle < 0.0); + } else { + //full circle + pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * pts.radius); //arbitrary point on edge + pts.onCurve.second(pts.center + Base::Vector3d(-1, 0,0) * pts.radius); //arbitrary point on edge + } + } else { + throw Base::RuntimeError("failed to make circle from bspline"); + } + } else { + throw Base::RuntimeError("can not get arc points from this edge"); } - result = closestPoints(e->occEdge, v->occVertex); + return pts; +} - return result; +anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references) +{ +// Base::Console().Message("DVD::getAnglePointsTwoEdges() - %s\n", getNameInDocument()); + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement0 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //this is a 2d object (a DVP + subelements) + TechDraw::BaseGeomPtr geom0 = getViewPart()->getGeomByIndex(iSubelement0); + TechDraw::BaseGeomPtr geom1 = getViewPart()->getGeomByIndex(iSubelement1); + if (!geom0 || !geom1) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + if (!geom0 || geom0->geomType != TechDraw::GeomType::GENERIC) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + if (!geom1 || geom1->geomType != TechDraw::GeomType::GENERIC) { + throw Base::RuntimeError("Missing geometry for dimension"); + } + TechDraw::GenericPtr generic0 = std::static_pointer_cast(geom0); + TechDraw::GenericPtr generic1 = std::static_pointer_cast(geom1); + Base::Vector3d apex = generic0->apparentInter(generic1); + Base::Vector3d farPoint0, farPoint1; + //pick the end of generic0 farthest from the apex + if ((generic0->getStartPoint() - apex).Length() > + (generic0->getEndPoint() - apex).Length()) { + farPoint0 = generic0->getStartPoint(); + } else { + farPoint0 = generic0->getEndPoint(); + } + //pick the end of generic1 farthest from the apex + if ((generic1->getStartPoint() - apex).Length() > + (generic1->getEndPoint() - apex).Length()) { + farPoint1 = generic1->getStartPoint(); + } else { + farPoint1 = generic1->getEndPoint(); + } + Base::Vector3d leg0Dir = (generic0->getStartPoint() - generic0->getEndPoint()).Normalize(); + Base::Vector3d leg1Dir = (generic1->getStartPoint() - generic1->getEndPoint()).Normalize(); + if (DU::fpCompare(fabs(leg0Dir.Dot(leg1Dir)), 1.0)) { + //legs of the angle are parallel. + throw Base::RuntimeError("Can not make angle from parallel edges"); + } + Base::Vector3d extenPoint0 = farPoint0; //extension line points + Base::Vector3d extenPoint1 = farPoint1; + if (DU::fpCompare(fabs(leg0Dir.Dot(leg1Dir)), 0.0)) { + //legs of angle are perpendicular farPoints will do + } else { + //legs of the angle are skew + //project farthest points onto opposite edge + Base::Vector3d projFar0OnLeg1 = farPoint0.Perpendicular(apex, leg1Dir); + Base::Vector3d projFar1OnLeg0 = farPoint1.Perpendicular(apex, leg0Dir); + if (DrawUtil::isBetween(projFar0OnLeg1, + generic1->getStartPoint(), + generic1->getEndPoint())) { + extenPoint1 = projFar0OnLeg1; + } else if (DrawUtil::isBetween(projFar1OnLeg0, + generic0->getStartPoint(), + generic0->getEndPoint())) { + extenPoint0 = projFar1OnLeg0; + } + } + + anglePoints pts; + pts.first(extenPoint0); + pts.second(extenPoint1); + pts.vertex(apex); + return pts; + } + + //this is a 3d object + TopoDS_Shape geometry0 = references.at(0).getGeometry(); + TopoDS_Shape geometry1 = references.at(1).getGeometry(); + if (geometry0.IsNull() || geometry1.IsNull() || + geometry0.ShapeType() != TopAbs_EDGE || geometry1.ShapeType() != TopAbs_EDGE) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + TopoDS_Edge edge0 = TopoDS::Edge(geometry0); + BRepAdaptor_Curve adapt0(edge0); + auto curve0 = adapt0.Curve().Curve(); + TopoDS_Edge edge1 = TopoDS::Edge(geometry1); + BRepAdaptor_Curve adapt1(edge1); + auto curve1 = adapt1.Curve().Curve(); + Base::Vector3d apex; + if (!DU::apparentIntersection(curve0, curve1, apex)) { + //no intersection + throw Base::RuntimeError("Edges for angle dimension can not intersect"); + } + gp_Pnt gStart0 = BRep_Tool::Pnt(TopExp::FirstVertex(edge0)); + gp_Pnt gEnd0 = BRep_Tool::Pnt(TopExp::LastVertex(edge0)); + gp_Pnt gFar0 = gEnd0; + if (gStart0.Distance(DU::togp_Pnt(apex)) > gEnd0.Distance(DU::togp_Pnt(apex)) ) { + gFar0 = gStart0; + } + gp_Pnt gStart1 = BRep_Tool::Pnt(TopExp::FirstVertex(edge1)); + gp_Pnt gEnd1 = BRep_Tool::Pnt(TopExp::LastVertex(edge1)); + gp_Pnt gFar1 = gEnd1; + if (gStart1.Distance(DU::togp_Pnt(apex)) > gEnd1.Distance(DU::togp_Pnt(apex)) ) { + gFar1 = gStart1; + } + Base::Vector3d farPoint0 = DU::toVector3d(gFar0); + Base::Vector3d farPoint1 = DU::toVector3d(gFar1); + Base::Vector3d leg0Dir = DU::toVector3d(gFar0) - apex; + Base::Vector3d leg1Dir = DU::toVector3d(gFar1) - apex; + if (DU::fpCompare(fabs(leg0Dir.Dot(leg1Dir)), 1.0)) { + //legs of the angle are parallel. + throw Base::RuntimeError("Can not make angle from parallel edges"); + } + Base::Vector3d extenPoint0 = DU::toVector3d(gFar0); //extension line points + Base::Vector3d extenPoint1 = DU::toVector3d(gFar1); + if (!DU::fpCompare(fabs(leg0Dir.Dot(leg1Dir)), 0.0)) { + //legs of the angle are skew + //project farthest points onto opposite edge + Base::Vector3d projFar0OnLeg1 = farPoint0.Perpendicular(apex, leg1Dir); + Base::Vector3d projFar1OnLeg0 = farPoint1.Perpendicular(apex, leg0Dir); + if (DrawUtil::isBetween(projFar0OnLeg1, + DU::toVector3d(gStart0), + DU::toVector3d(gEnd0)) ) { + extenPoint1 = projFar0OnLeg1; + } else if (DrawUtil::isBetween(projFar1OnLeg0, + DU::toVector3d(gStart1), + DU::toVector3d(gEnd1)) ) { + + extenPoint0 = projFar1OnLeg0; + } + } + + anglePoints pts(apex, extenPoint0, extenPoint1); + pts.move(getViewPart()->getOriginalCentroid()); + pts.project(getViewPart()); + pts.mapToPage(getViewPart()); + pts.invertY(); + return pts; +} + +//TODO: this makes assumptions about the order of references (p - v - p). is this checked somewhere? +anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector references) +{ +// Base::Console().Message("DVD::getAnglePointsThreeVerts() - %s\n", getNameInDocument()); + if (references.size() < 3) { + throw Base::RuntimeError("Not enough references to make angle dimension"); + } + App::DocumentObject* refObject = references.front().getObject(); + int iSubelement0 = DrawUtil::getIndexFromName(references.at(0).getSubName()); + int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); + int iSubelement2 = DrawUtil::getIndexFromName(references.at(2).getSubName()); + if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !references.at(0).getSubName().empty()) { + //this is a 2d object (a DVP + subelements) + TechDraw::VertexPtr vert0 = getViewPart()->getProjVertexByIndex(iSubelement0); + TechDraw::VertexPtr vert1 = getViewPart()->getProjVertexByIndex(iSubelement1); + TechDraw::VertexPtr vert2 = getViewPart()->getProjVertexByIndex(iSubelement2); + if (!vert0 || !vert1 || !vert2) { + throw Base::RuntimeError("References for three point angle dimension are not vertices"); + } + anglePoints pts(vert1->point(), vert0->point(), vert2->point()); + return pts; + } + + //this is a 3d object + TopoDS_Shape geometry0 = references.at(0).getGeometry(); + TopoDS_Shape geometry1 = references.at(1).getGeometry(); + TopoDS_Shape geometry2 = references.at(2).getGeometry(); + if (geometry0.IsNull() || geometry1.IsNull() || geometry2.IsNull() || + geometry0.ShapeType() != TopAbs_VERTEX || geometry1.ShapeType() != TopAbs_VERTEX || geometry2.ShapeType() != TopAbs_VERTEX) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + TopoDS_Vertex vertex0 = TopoDS::Vertex(geometry0); + gp_Pnt point0 = BRep_Tool::Pnt(vertex0); + TopoDS_Vertex vertex1 = TopoDS::Vertex(geometry1); + gp_Pnt point1 = BRep_Tool::Pnt(vertex1); + TopoDS_Vertex vertex2 = TopoDS::Vertex(geometry2); + gp_Pnt point2 = BRep_Tool::Pnt(vertex2); + anglePoints pts(DU::toVector3d(point1), DU::toVector3d(point0), DU::toVector3d(point2)); + pts.move(getViewPart()->getOriginalCentroid()); + pts.project(getViewPart()); + pts.mapToPage(getViewPart()); + pts.invertY(); + return pts; } DrawViewPart* DrawViewDimension::getViewPart() const @@ -1287,11 +1100,64 @@ DrawViewPart* DrawViewDimension::getViewPart() const return dynamic_cast(References2D.getValues().at(0)); } -int DrawViewDimension::getRefType() const + +//return the references controlling this dimension. 3d references are used when available +//otherwise 2d references are returned. no checking is performed. Result is pairs of (object, subName) +ReferenceVector DrawViewDimension::getEffectiveReferences() const { - return getRefTypeSubElements(References2D.getSubValues()); + const std::vector& objects3d = References3D.getValues(); + const std::vector& subElements3d = References3D.getSubValues(); + const std::vector& objects = References2D.getValues(); + const std::vector& subElements = References2D.getSubValues(); + ReferenceVector effectiveRefs; + if (!objects3d.empty()) { + //use 3d references by preference + int refCount = objects3d.size(); + for (int i = 0; i < refCount; i++) { + ReferenceEntry ref(objects3d.at(i), std::string(subElements3d.at(i))); + effectiveRefs.push_back(ref); + } + } else { + //use 2d references if necessary + int refCount = objects.size(); + for (int i = 0; i < refCount; i++) { + ReferenceEntry ref(objects.at(i), subElements.at(i)); + effectiveRefs.push_back(ref); + } + } + return effectiveRefs; } +//what configuration of references do we have - Vertex-Vertex, Edge-Vertex, Edge, ... +int DrawViewDimension::getRefType() const +{ + if (isExtentDim()) { + return RefType::extent; + } + + ReferenceVector refs = getEffectiveReferences(); + std::vector subNames; + + //std::vector subNames = getEffectiveSubNames(); //??? + for (auto& ref : refs) { + if (ref.getSubName().empty()) { + //skip this one + continue; + } + subNames.push_back(ref.getSubName()); + } + + if (subNames.empty()) { + //something went wrong, there were no subNames. + Base::Console().Message("DVD::getRefType - %s - there are no subNames.\n", getNameInDocument()); + return 0; + } + + return getRefTypeSubElements(subNames); +} + +//TODO: Gui/DimensionValidators.cpp has almost the same code +//decide what the reference configuration is by examining the names of the sub elements int DrawViewDimension::getRefTypeSubElements(const std::vector &subElements) { int refType = invalidRef; @@ -1314,19 +1180,24 @@ int DrawViewDimension::getRefTypeSubElements(const std::vector &sub //! validate 2D references - only checks if the target exists bool DrawViewDimension::checkReferences2D() const { -// Base::Console().Message("DVD::checkReFerences2d() - %s\n", getNameInDocument()); +// Base::Console().Message("DVD::checkReferences2d() - %s\n", getNameInDocument()); const std::vector &objects = References2D.getValues(); if (objects.empty()) { - Base::Console().Log("DVD::checkReferences2d() - %s - objects empty!\n", getNameInDocument()); return false; } const std::vector &subElements = References2D.getSubValues(); if (subElements.empty()) { - Base::Console().Log("DVD::checkRegerences2d() - %s - subelements empty!\n", getNameInDocument()); + //must have at least 1 null string entry to balance DVP return false; } + if (subElements.front().empty() && + !References3D.getValues().empty()) { + //this is (probably) a dim with 3d refs + return true; + } + for (auto& s: subElements) { if (s.empty()) { return false; @@ -1360,17 +1231,52 @@ pointPair DrawViewDimension::closestPoints(TopoDS_Shape s1, int count = extss.NbSolution(); if (count != 0) { gp_Pnt p = extss.PointOnShape1(1); - result.first = Base::Vector3d(p.X(), p.Y(), p.Z()); + result.first(Base::Vector3d(p.X(), p.Y(), p.Z())); p = extss.PointOnShape2(1); - result.second = Base::Vector3d(p.X(), p.Y(), p.Z()); + result.second(Base::Vector3d(p.X(), p.Y(), p.Z())); } //TODO: else { explode } return result; } +//set the reference property from a reference vector +void DrawViewDimension::setReferences2d(ReferenceVector refs) +{ + std::vector objects; + std::vector subNames; + if ( objects.size() != subNames.size() ) { + throw Base::IndexError("DVD::setReferences2d - objects and subNames do not match."); + } + + for ( size_t iRef = 0; iRef < refs.size(); iRef++) { + objects.push_back(refs.at(iRef).getObject()); + subNames.push_back(refs.at(iRef).getSubName()); + } + + References2D.setValues(objects, subNames); +} + +//set the reference property from a reference vector +void DrawViewDimension::setReferences3d(ReferenceVector refs) +{ + std::vector objects; + std::vector subNames; + if ( objects.size() != subNames.size() ) { + throw Base::IndexError("DVD::setReferences3d - objects and subNames do not match."); + } + + for ( size_t iRef = 0; iRef < refs.size(); iRef++) { + objects.push_back(refs.at(iRef).getObject()); + subNames.push_back(refs.at(iRef).getSubName()); + } + + References3D.setValues(objects, subNames); +} + //!add Dimension 3D references to measurement void DrawViewDimension::setAll3DMeasurement() { +// Base::Console().Message("DVD::setAll3dMeasurement()\n"); measurement->clear(); const std::vector &Objs = References3D.getValues(); const std::vector &Subs = References3D.getSubValues(); @@ -1458,12 +1364,12 @@ bool DrawViewDimension::leaderIntersectsArc(Base::Vector3d s, Base::Vector3d poi void DrawViewDimension::saveArrowPositions(const Base::Vector2d positions[]) { if (!positions) { - m_arrowPositions.first = Base::Vector3d(0.0, 0.0, 0.0); - m_arrowPositions.second = Base::Vector3d(0.0, 0.0, 0.0); + m_arrowPositions.first(Base::Vector3d(0.0, 0.0, 0.0)); + m_arrowPositions.second(Base::Vector3d(0.0, 0.0, 0.0)); } else { double scale = getViewPart()->getScale(); - m_arrowPositions.first = Base::Vector3d(positions[0].x, positions[0].y, 0.0) / scale; - m_arrowPositions.second = Base::Vector3d(positions[1].x, positions[1].y, 0.0) / scale; + m_arrowPositions.first(Base::Vector3d(positions[0].x, positions[0].y, 0.0) / scale); + m_arrowPositions.second(Base::Vector3d(positions[1].x, positions[1].y, 0.0) / scale); } } @@ -1476,22 +1382,24 @@ pointPair DrawViewDimension::getArrowPositions() bool DrawViewDimension::has2DReferences() const { -// Base::Console().Message("DVD::has2DReferences() - %s\n", getNameInDocument()); +// Base::Console().Message("DVD::has2DReferences() - %s\n",getNameInDocument()); const std::vector &objects = References2D.getValues(); - const std::vector &SubNames = References2D.getSubValues(); - if (objects.empty() || SubNames.empty()) { + const std::vector& subNames = References2D.getSubValues(); + if (objects.empty()) { + //we don't even have a DVP return false; } - for (auto& s: SubNames) { // Check individual entries - if (s.empty()) { - return false; - } + if (subNames.front().empty()) { + //this is ok, as we must have a null string entry to balance DVP in first object position + return true; } + //we have a reference to a DVP and at least 1 subName entry, so we have 2d references return true; } +//there is no special structure to 3d references, so anything > 0 is good bool DrawViewDimension::has3DReferences() const { return (References3D.getSize() > 0); @@ -1520,7 +1428,7 @@ bool DrawViewDimension::useDecimals() const return Preferences::useGlobalDecimals(); } -std::string DrawViewDimension::getPrefix() const +std::string DrawViewDimension::getPrefixForDimType() const { if (Type.isValue("Radius")){ return "R"; @@ -1535,63 +1443,18 @@ std::string DrawViewDimension::getPrefix() const std::string DrawViewDimension::getDefaultFormatSpec(bool isToleranceFormat) const { - std::string prefFormat = Preferences::formatSpec(); - QString formatSpec; - QString qPrefix; - if (prefFormat.empty()) { - QString format1 = Base::Tools::fromStdString("%."); - QString format2 = Base::Tools::fromStdString("f"); - int precision; - if (useDecimals()) { - precision = Base::UnitsApi::getDecimals(); - } else { - precision = Preferences::altDecimals(); - } - QString formatPrecision = QString::number(precision); - - std::string prefix = getPrefix(); - - if (!prefix.empty()) { - qPrefix = QString::fromUtf8(prefix.data(), prefix.size()); - } - - formatSpec = qPrefix + format1 + formatPrecision + format2; - } else { - - std::string prefix = getPrefix(); - qPrefix = QString::fromUtf8(prefix.data(), prefix.size()); - formatSpec = qPrefix + QString::fromStdString(prefFormat); - - } - - if (isToleranceFormat) { - formatSpec.replace(QString::fromUtf8("%"), QString::fromUtf8("%+")); - } - - return Base::Tools::toStdString(formatSpec); + return m_formatter->getDefaultFormatSpec(isToleranceFormat); +} + +bool DrawViewDimension::isExtentDim() const +{ + std::string name(getNameInDocument()); + if (name.substr(0, 9) == "DimExtent") { + return true; + } + return false; } -////! is refName a target of this Dim (2D references) -//bool DrawViewDimension::references(std::string refName) const -//{ -// Base::Console().Message("DVD::references(%s) - %s\n", refName.c_str(), getNameInDocument()); -// bool result = false; -// const std::vector &objects = References2D.getValues(); -// if (!objects.empty()) { -// const std::vector &subElements = References2D.getSubValues(); -// if (!subElements.empty()) { -// for (auto& s: subElements) { -// if (!s.empty()) { -// if (s == refName) { -// result = true; -// break; -// } -// } -// } -// } -// } -// return result; -//} PyObject *DrawViewDimension::getPyObject() { diff --git a/src/Mod/TechDraw/App/DrawViewDimension.h b/src/Mod/TechDraw/App/DrawViewDimension.h index 87c150b6ac..79534682b9 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.h +++ b/src/Mod/TechDraw/App/DrawViewDimension.h @@ -25,13 +25,22 @@ #include +#include + #include +#include #include #include +#include "DrawViewPart.h" +#include "DimensionGeometry.h" +#include "DimensionReferences.h" + +#include "Geometry.h" #include "DrawView.h" +#include "DrawUtil.h" - +using DU = TechDraw::DrawUtil; class TopoDS_Shape; namespace Measure { @@ -40,94 +49,26 @@ class Measurement; namespace TechDraw { class DrawViewPart; - -struct DimRef { - DrawViewPart* part; - std::string sub; -}; - -using pointPair = std::pair; - -struct anglePoints -{ - pointPair ends; - Base::Vector3d vertex; - - anglePoints() - { - ends.first = Base::Vector3d(0.0, 0.0, 0.0); - ends.second = Base::Vector3d(0.0, 0.0, 0.0); - vertex = Base::Vector3d(0.0, 0.0, 0.0); - } - - anglePoints(const anglePoints& ap) - : ends(ap.ends) - , vertex(ap.vertex) - { - - } - - anglePoints& operator= (const anglePoints& ap) - { - ends = ap.ends; - vertex = ap.vertex; - return *this; - } -}; - -struct arcPoints -{ - bool isArc; - double radius; - Base::Vector3d center; - pointPair onCurve; - pointPair arcEnds; - Base::Vector3d midArc; - bool arcCW; - - arcPoints() - { - isArc = false; - radius = 0.0; - center = Base::Vector3d(0.0, 0.0, 0.0); - onCurve.first = Base::Vector3d(0.0, 0.0, 0.0); - onCurve.second = Base::Vector3d(0.0, 0.0, 0.0); - arcEnds.first = Base::Vector3d(0.0, 0.0, 0.0); - arcEnds.second = Base::Vector3d(0.0, 0.0, 0.0); - midArc = Base::Vector3d(0.0, 0.0, 0.0); - arcCW = false; - } - - arcPoints(const arcPoints& ap) - : isArc(ap.isArc) - , radius(ap.radius) - , center(ap.center) - , onCurve(ap.onCurve) - , arcEnds(ap.arcEnds) - , midArc(ap.midArc) - , arcCW(ap.arcCW) - { - - } - - arcPoints& operator= (const arcPoints& ap) - { - isArc = ap.isArc; - radius = ap.radius; - center = ap.center; - onCurve = ap.onCurve; - arcEnds = ap.arcEnds; - midArc = ap.midArc; - arcCW = ap.arcCW; - return *this; - } -}; +class DimensionFormatter; class TechDrawExport DrawViewDimension : public TechDraw::DrawView { PROPERTY_HEADER_WITH_OVERRIDE(TechDraw::DrawViewDimension); public: + +// keep this enum synchronized with TypeEnums +enum DimensionType { + Distance, + DistanceX, + DistanceY, + DistanceZ, + Radius, + Diameter, + Angle, + Angle3Pt +}; + /// Constructor DrawViewDimension(); ~DrawViewDimension() override; @@ -158,7 +99,8 @@ public: twoEdge, twoVertex, vertexEdge, - threeVertex + threeVertex, + extent }; @@ -194,35 +136,49 @@ public: QRectF getRect() const override { return {0, 0, 1, 1}; } //pretend dimensions always fit! virtual int getRefType() const; //Vertex-Vertex, Edge, Edge-Edge static int getRefTypeSubElements(const std::vector &); //Vertex-Vertex, Edge, Edge-Edge + + void setReferences2d(ReferenceVector refs); + void setReferences3d(ReferenceVector refs); + void setAll3DMeasurement(); void clear3DMeasurements(); virtual bool checkReferences2D() const; - virtual pointPair getLinearPoints() {return m_linearPoints; } + virtual pointPair getLinearPoints() const {return m_linearPoints; } + virtual void setLinearPoints(Base::Vector3d point0, Base::Vector3d point1) { m_linearPoints.first(point0); + m_linearPoints.second(point1); }; + virtual void setLinearPoints(pointPair newPair) { m_linearPoints = newPair; } arcPoints getArcPoints() {return m_arcPoints; } anglePoints getAnglePoints() {return m_anglePoints; } bool leaderIntersectsArc(Base::Vector3d s, Base::Vector3d pointOnCircle); bool isMultiValueSchema() const; - std::string getBaseLengthUnit(Base::UnitSystem system); - pointPair getArrowPositions(); void saveArrowPositions(const Base::Vector2d positions[]); bool showUnits() const; bool useDecimals() const; + bool isExtentDim() const; + virtual ReferenceVector getEffectiveReferences() const; protected: void handleChangedPropertyType(Base::XMLReader &, const char * , App::Property * ) override; void Restore(Base::XMLReader& reader) override; void onChanged(const App::Property* prop) override; void onDocumentRestored() override; - std::string getPrefix() const; + std::string getPrefixForDimType() const; std::string getDefaultFormatSpec(bool isToleranceFormat = false) const; - virtual pointPair getPointsOneEdge(); - virtual pointPair getPointsTwoEdges(); - virtual pointPair getPointsTwoVerts(); - virtual pointPair getPointsEdgeVert(); + virtual pointPair getPointsOneEdge(ReferenceVector references); + virtual pointPair getPointsTwoEdges(ReferenceVector references); + virtual pointPair getPointsTwoVerts(ReferenceVector references); + virtual pointPair getPointsEdgeVert(ReferenceVector references); + + virtual arcPoints getArcParameters(ReferenceVector references); + virtual arcPoints arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base); + virtual arcPoints arcPointsFromEdge(TopoDS_Edge occEdge); + + virtual anglePoints getAnglePointsTwoEdges(ReferenceVector references); + virtual anglePoints getAnglePointsThreeVerts(ReferenceVector references); protected: Measure::Measurement *measurement; @@ -232,8 +188,6 @@ protected: Base::Vector3d e2) const; pointPair closestPoints(TopoDS_Shape s1, TopoDS_Shape s2) const; - pointPair m_linearPoints; - pointPair m_arrowPositions; void resetLinear(); void resetAngular(); @@ -244,10 +198,14 @@ private: static const char* MeasureTypeEnums[]; void dumpRefs2D(const char* text) const; //Dimension "geometry" -/* pointPair m_linearPoints;*/ + pointPair m_linearPoints; + pointPair m_arrowPositions; arcPoints m_arcPoints; anglePoints m_anglePoints; bool m_hasGeometry; + + friend class DimensionFormatter; + DimensionFormatter* m_formatter; }; } //namespace TechDraw diff --git a/src/Mod/TechDraw/App/DrawViewDimensionPyImp.cpp b/src/Mod/TechDraw/App/DrawViewDimensionPyImp.cpp index d2ac734d1f..1890835cc6 100644 --- a/src/Mod/TechDraw/App/DrawViewDimensionPyImp.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimensionPyImp.cpp @@ -73,8 +73,8 @@ PyObject* DrawViewDimensionPy::getLinearPoints(PyObject* args) DrawViewDimension* dvd = getDrawViewDimensionPtr(); pointPair pts = dvd->getLinearPoints(); Py::List ret; - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.first)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.second)))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.first())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.second())))); return Py::new_reference_to(ret); } @@ -88,10 +88,10 @@ PyObject* DrawViewDimensionPy::getArcPoints(PyObject* args) arcPoints pts = dvd->getArcPoints(); Py::List ret; ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.center)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.onCurve.first)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.onCurve.second)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.arcEnds.first)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.arcEnds.second)))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.onCurve.first())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.onCurve.second())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.arcEnds.first())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.arcEnds.second())))); ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.midArc)))); return Py::new_reference_to(ret); } @@ -105,9 +105,9 @@ PyObject* DrawViewDimensionPy::getAnglePoints(PyObject* args) DrawViewDimension* dvd = getDrawViewDimensionPtr(); anglePoints pts = dvd->getAnglePoints(); Py::List ret; - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.ends.first)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.ends.second)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.vertex)))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.first())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.second())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.vertex())))); return Py::new_reference_to(ret); } @@ -120,8 +120,8 @@ PyObject* DrawViewDimensionPy::getArrowPositions(PyObject* args) DrawViewDimension* dvd = getDrawViewDimensionPtr(); pointPair pts = dvd->getArrowPositions(); Py::List ret; - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.first)))); - ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.second)))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.first())))); + ret.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(pts.second())))); return Py::new_reference_to(ret); } PyObject *DrawViewDimensionPy::getCustomAttributes(const char* /*attr*/) const diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index 093bb4b093..d277eb5ced 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -264,7 +264,6 @@ App::DocumentObjectExecReturn* DrawViewPart::execute(void) XDirection.purgeTouched();//don't trigger updates! } - m_saveShape = shape; partExec(shape); return DrawView::execute(); @@ -319,28 +318,38 @@ void DrawViewPart::partExec(TopoDS_Shape& shape) //prepare the shape for HLR processing by centering, scaling and rotating it GeometryObjectPtr DrawViewPart::makeGeometryForShape(TopoDS_Shape& shape) { - // Base::Console().Message("DVP::makeGeometryForShape() - %s\n", getNameInDocument()); - gp_Pnt inputCenter; - Base::Vector3d stdOrg(0.0, 0.0, 0.0); - gp_Ax2 viewAxis = getProjectionCS(stdOrg); - inputCenter = TechDraw::findCentroid(shape, viewAxis); - Base::Vector3d centroid(inputCenter.X(), inputCenter.Y(), inputCenter.Z()); - - //center shape on origin - TopoDS_Shape centeredShape = TechDraw::moveShape(shape, centroid * -1.0); - m_saveCentroid = centroid; - m_saveShape = centeredShape; - - TopoDS_Shape scaledShape = TechDraw::scaleShape(centeredShape, getScale()); - if (!DrawUtil::fpCompare(Rotation.getValue(), 0.0)) { - scaledShape = TechDraw::rotateShape(scaledShape, viewAxis, - Rotation.getValue());//conventional rotation - } - - GeometryObjectPtr go = buildGeometryObject(scaledShape, viewAxis); +// Base::Console().Message("DVP::makeGeometryForShape() - %s\n", getNameInDocument()); + gp_Pnt inputCenter = TechDraw::findCentroid(shape, + getProjectionCS()); + m_saveCentroid = DU::toVector3d(inputCenter); + m_saveShape = centerScaleRotate(this, shape, m_saveCentroid); + GeometryObjectPtr go = buildGeometryObject(shape, getProjectionCS()); return go; } +//Modify a shape by centering, scaling and rotating and return the centered (but not rotated) shape +TopoDS_Shape DrawViewPart::centerScaleRotate(DrawViewPart* dvp, + TopoDS_Shape& inOutShape, + Base::Vector3d centroid) +{ +// Base::Console().Message("DVP::centerScaleRotate() - %s\n", dvp->getNameInDocument()); + gp_Ax2 viewAxis = dvp->getProjectionCS(); + + //center shape on origin + TopoDS_Shape centeredShape = TechDraw::moveShape(inOutShape, + centroid * -1.0); + + inOutShape = TechDraw::scaleShape(centeredShape, dvp->getScale()); + if (!DrawUtil::fpCompare(dvp->Rotation.getValue(), 0.0)) { + inOutShape = TechDraw::rotateShape(inOutShape, + viewAxis, + dvp->Rotation.getValue()); //conventional rotation + } +// BRepTools::Write(inOutShape, "DVPScaled.brep"); //debug + return centeredShape; +} + + //create a geometry object and trigger the HLR process in another thread TechDraw::GeometryObjectPtr DrawViewPart::buildGeometryObject(TopoDS_Shape& shape, const gp_Ax2& viewAxis) @@ -770,6 +779,53 @@ const std::vector DrawViewPart::getVertexGeometry() const return result; } +TechDraw::VertexPtr DrawViewPart::getVertex(std::string vertexName) const +{ + const std::vector allVertex(DrawViewPart::getVertexGeometry()); + size_t iTarget = DrawUtil::getIndexFromName(vertexName); + if (allVertex.empty()) { + //should not happen + throw Base::IndexError("DVP::getVertex - No vertices found."); + } + if (iTarget > allVertex.size()) { + //should not happen + throw Base::IndexError("DVP::getVertex - Vertex not found."); + } + + return allVertex.at(iTarget); +} + +//! returns existing BaseGeom of 2D Edge +TechDraw::BaseGeomPtr DrawViewPart::getEdge(std::string edgeName) const +{ + const std::vector &geoms = getEdgeGeometry(); + if (geoms.empty()) { + //should not happen + throw Base::IndexError("DVP::getEdge - No edges found."); + } + size_t iEdge = DrawUtil::getIndexFromName(edgeName); + if ((unsigned)iEdge >= geoms.size()) { + throw Base::IndexError("DVP::getEdge - Edge not found."); + } + return geoms.at(iEdge); +} + +//! returns existing 2d Face +TechDraw::FacePtr DrawViewPart::getFace(std::string faceName) const +{ + const std::vector &faces = getFaceGeometry(); + if (faces.empty()) { + //should not happen + throw Base::IndexError("DVP::getFace - No faces found."); + } + size_t iFace = DrawUtil::getIndexFromName(faceName); + if (iFace >= faces.size()) { + throw Base::IndexError("DVP::getFace - Face not found."); + } + return faces.at(iFace); +} + + const std::vector DrawViewPart::getFaceGeometry() const { std::vector result; diff --git a/src/Mod/TechDraw/App/DrawViewPart.h b/src/Mod/TechDraw/App/DrawViewPart.h index a520fc506c..9ee79b3033 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.h +++ b/src/Mod/TechDraw/App/DrawViewPart.h @@ -114,6 +114,9 @@ public: const char* getViewProviderName() const override { return "TechDrawGui::ViewProviderViewPart"; } PyObject* getPyObject() override; + static TopoDS_Shape centerScaleRotate(DrawViewPart* dvp, + TopoDS_Shape& inOutShape, + Base::Vector3d centroid); std::vector getHatches() const; std::vector getGeomHatches() const; std::vector getDimensions() const; @@ -127,10 +130,13 @@ public: bool hasGeometry() const; TechDraw::GeometryObjectPtr getGeometryObject() const { return geometryObject; } - TechDraw::BaseGeomPtr - getGeomByIndex(int idx) const;//get existing geom for edge idx in projection - TechDraw::VertexPtr - getProjVertexByIndex(int idx) const;//get existing geom for vertex idx in projection + TechDraw::VertexPtr getVertex(std::string vertexName) const; + TechDraw::BaseGeomPtr getEdge(std::string edgeName) const; + TechDraw::FacePtr getFace(std::string faceName) const; + + TechDraw::BaseGeomPtr getGeomByIndex(int idx) const; //get existing geom for edge idx in projection + TechDraw::VertexPtr getProjVertexByIndex(int idx) const; //get existing geom for vertex idx in projection + TechDraw::VertexPtr getProjVertexByCosTag(std::string cosTag); std::vector getFaceEdgesByIndex(int idx) const;//get edges for face idx in projection diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp index cf347049b6..14d59bf0b1 100644 --- a/src/Mod/TechDraw/App/Geometry.cpp +++ b/src/Mod/TechDraw/App/Geometry.cpp @@ -1271,198 +1271,15 @@ bool BSpline::isLine() //used by DVDim for approximate dims bool BSpline::isCircle() { - bool result = false; - double radius; - Base::Vector3d center; - bool isArc = false; - getCircleParms(result, radius, center, isArc); - return result; -} - -//used by DVDim for approximate dims -void BSpline::getCircleParms(bool& isCircle, double& radius, Base::Vector3d& center, bool& isArc) -{ - double curveLimit = 0.0001; - BRepAdaptor_Curve c(occEdge); - Handle(Geom_BSplineCurve) spline = c.BSpline(); - double f, l; - f = c.FirstParameter(); - l = c.LastParameter(); - double parmRange = fabs(l - f); - int testCount = 6; - double parmStep = parmRange/testCount; - std::vector curvatures; - std::vector centers; - gp_Pnt curveCenter; - double sumCurvature = 0; - Base::Vector3d sumCenter, valueAt; - try { - GeomLProp_CLProps prop(spline, f, 3, Precision::Confusion()); - curvatures.push_back(prop.Curvature()); - sumCurvature += prop.Curvature(); - prop.CentreOfCurvature(curveCenter); - centers.push_back(curveCenter); - sumCenter += Base::Vector3d(curveCenter.X(), curveCenter.Y(), curveCenter.Z()); - - for (int i = 1; i < (testCount - 1); i++) { - prop.SetParameter(parmStep * i); - curvatures.push_back(prop.Curvature()); - sumCurvature += prop.Curvature(); - prop.CentreOfCurvature(curveCenter); - centers.push_back(curveCenter); - sumCenter += Base::Vector3d(curveCenter.X(), curveCenter.Y(), curveCenter.Z()); - } - prop.SetParameter(l); - curvatures.push_back(prop.Curvature()); - sumCurvature += prop.Curvature(); - prop.CentreOfCurvature(curveCenter); - centers.push_back(curveCenter); - sumCenter += Base::Vector3d(curveCenter.X(), curveCenter.Y(), curveCenter.Z()); - } - catch (Standard_Failure&) { - Base::Console().Log("TechDraw - GEO::BSpline::getCircleParms - CLProps failed\n"); - isCircle = false; - return; - } - Base::Vector3d avgCenter = sumCenter/testCount; - double errorCenter = 0; - for (auto& c: centers) { - errorCenter += (avgCenter - Base::Vector3d(c.X(), c.Y(), c.Z())).Length(); - } - errorCenter = errorCenter/testCount; - - double avgCurve = sumCurvature/testCount; - double errorCurve = 0; - for (auto& cv: curvatures) { - errorCurve += fabs(avgCurve - cv); //fabs??? - } - errorCurve = errorCurve/testCount; - - isArc = !c.IsClosed(); - isCircle = false; - if ( errorCurve < curveLimit ) { - isCircle = true; - radius = 1.0/avgCurve; - center = avgCenter; - } + return GeometryUtils::isCircle(occEdge); } // make a circular edge from BSpline TopoDS_Edge BSpline::asCircle(bool& arc) { - TopoDS_Edge result; - BRepAdaptor_Curve c(occEdge); - - // find the two ends - Handle(Geom_Curve) curve = c.Curve().Curve(); - double f = c.FirstParameter(); - double l = c.LastParameter(); - gp_Pnt s = c.Value(f); - gp_Pnt e = c.Value(l); - - if (s.IsEqual(e, 0.001)) { //more reliable - arc = false; - } else { - arc = true; - } -// arc = !c.IsClosed(); //reliable? - - Handle(Geom_BSplineCurve) spline = c.BSpline(); - - if (spline->NbPoles() < 5) { //need 5 poles (s-p1-pm-p2-e) for algo - return result; //how to do with fewer poles? - } - - try { - // get three points on curve (non extreme poles) - int nb_poles = spline->NbPoles(); - gp_Pnt p1 = spline->Pole(2); //OCC numbering starts at 1!! - gp_Pnt p2 = spline->Pole(nb_poles-1); - gp_Pnt pm; - if (nb_poles == 5) { - pm = spline->Pole(3); //5 poles => 2.5 => 2 - } else { - pm = spline->Pole(nb_poles / 2); - } - - // project three poles onto the curve - GeomAPI_ProjectPointOnCurve proj1; - GeomAPI_ProjectPointOnCurve proj2; - GeomAPI_ProjectPointOnCurve projm; - proj1.Init(p1, curve, f, l); - proj1.Perform(p1); - proj2.Init(p2, curve, f, l); - proj2.Perform(p2); - projm.Init(pm, curve, f, l); - projm.Perform(pm); - if ( (proj1.NbPoints() == 0) || - (proj2.NbPoints() == 0) || - (projm.NbPoints() == 0) ) { - return result; - } - gp_Pnt pc1, pc2, pcm; - - // get projected points - pc1 = proj1.NearestPoint(); - pc2 = proj2.NearestPoint(); - pcm = projm.NearestPoint(); - - // make 2 circles and find their radii - gce_MakeCirc gce_circ1 = gce_MakeCirc(s, pc1, pcm); //3 point circle - if (gce_circ1.Status() != gce_Done) { - return result; - } - gp_Circ circle1 = gce_circ1.Value(); - double radius1 = circle1.Radius(); - gp_Pnt center1 = circle1.Location(); - Base::Vector3d vc1 = DrawUtil::toVector3d(center1); - - gce_MakeCirc gce_circ2 = gce_MakeCirc(pcm, pc2, e); - if (gce_circ2.Status() != gce_Done) { - return result; - } - gp_Circ circle2 = gce_circ2.Value(); - double radius2 = circle2.Radius(); - gp_Pnt center2 = circle2.Location(); - Base::Vector3d vc2 = DrawUtil::toVector3d(center2); - - // compare radii & centers - double allowError = 0.001; //mm^-3 good enough for printing - double radius; - Base::Vector3d center; - if ( (DrawUtil::fpCompare(radius2, radius1, allowError)) && - (vc1.IsEqual(vc2, allowError)) ) { - if (arc) { - GC_MakeArcOfCircle makeArc(s, pcm, e); - Handle(Geom_TrimmedCurve) tCurve = makeArc.Value(); - BRepBuilderAPI_MakeEdge newEdge(tCurve); - result = newEdge; - } else { - radius = (radius1 + radius2) / 2.0; - center = (vc1 + vc2) / 2.0; - gp_Pnt gCenter(center.x, center.y, center.z); - gp_Ax2 stdZ(gCenter, gp_Dir(0, 0, 1)); - gp_Circ newCirc(stdZ, radius); - BRepBuilderAPI_MakeEdge newEdge(newCirc); - result = newEdge; - } - } - } - catch (const Standard_Failure& e) { - Base::Console().Log("Geom::asCircle - OCC error - %s - while approx spline as circle\n", - e.GetMessageString()); - TopoDS_Edge nullReturn; - result = nullReturn; - } - catch (...) { - Base::Console().Log("Geom::asCircle - unknown error occurred while approx spline as circle\n"); - TopoDS_Edge nullReturn; - result = nullReturn; - } - return result; + return GeometryUtils::asCircle(occEdge, arc); } - bool BSpline::intersectsArc(Base::Vector3d p1, Base::Vector3d p2) { gp_Pnt pnt1(p1.x, p1.y,p1.z); @@ -1771,4 +1588,186 @@ TopoDS_Edge GeometryUtils::edgeFromCircleArc(TechDraw::AOCPtr c) return aMakeEdge.Edge(); } +//used by DVDim for approximate dims +bool GeometryUtils::isCircle(TopoDS_Edge occEdge) +{ + double radius; + Base::Vector3d center; + bool isArc = false; + return GeometryUtils::getCircleParms(occEdge, radius, center, isArc); +} +//tries to interpret a BSpline edge as a circle. Used by DVDim for approximate dimensions. +bool GeometryUtils::getCircleParms(TopoDS_Edge occEdge, double& radius, Base::Vector3d& center, bool& isArc) +{ + double curveLimit = EWTOLERANCE; + BRepAdaptor_Curve c(occEdge); + Handle(Geom_BSplineCurve) spline = c.BSpline(); + double f, l; + f = c.FirstParameter(); + l = c.LastParameter(); + double parmRange = fabs(l - f); + int testCount = 6; + double parmStep = parmRange/testCount; + std::vector curvatures; + std::vector centers; + gp_Pnt curveCenter; + double sumCurvature = 0; + Base::Vector3d sumCenter, valueAt; + try { + GeomLProp_CLProps prop(spline, f, 3, Precision::Confusion()); + curvatures.push_back(prop.Curvature()); + sumCurvature += prop.Curvature(); + prop.CentreOfCurvature(curveCenter); + centers.push_back(curveCenter); + sumCenter += DrawUtil::toVector3d(curveCenter); + + for (int i = 1; i < (testCount - 1); i++) { + prop.SetParameter(parmStep * i); + curvatures.push_back(prop.Curvature()); + sumCurvature += prop.Curvature(); + prop.CentreOfCurvature(curveCenter); + centers.push_back(curveCenter); + sumCenter += DrawUtil::toVector3d(curveCenter); + } + prop.SetParameter(l); + curvatures.push_back(prop.Curvature()); + sumCurvature += prop.Curvature(); + prop.CentreOfCurvature(curveCenter); + centers.push_back(curveCenter); + sumCenter += DrawUtil::toVector3d(curveCenter); + } + catch (Standard_Failure&) { + Base::Console().Error("OCC error. Could not interpret BSpline as Circle\n"); + return false; + } + Base::Vector3d avgCenter = sumCenter/testCount; + + double avgCurve = sumCurvature/testCount; + double errorCurve = 0; + for (auto& cv: curvatures) { + errorCurve += fabs(avgCurve - cv); //fabs??? + } + errorCurve = errorCurve/testCount; + + isArc = !c.IsClosed(); + bool isCircle(false); + if ( errorCurve < curveLimit ) { + isCircle = true; + radius = 1.0/avgCurve; + center = avgCenter; + } + return isCircle; +} + +// make a circle or arc of circle Edge from BSpline Edge +TopoDS_Edge GeometryUtils::asCircle(TopoDS_Edge occEdge, bool& arc) +{ + TopoDS_Edge result; + BRepAdaptor_Curve c(occEdge); + + // find the two ends + Handle(Geom_Curve) curve = c.Curve().Curve(); + double f = c.FirstParameter(); + double l = c.LastParameter(); + gp_Pnt s = c.Value(f); + gp_Pnt e = c.Value(l); + + if (s.IsEqual(e, 0.001)) { //more reliable + arc = false; + } else { + arc = true; + } + // arc = !c.IsClosed(); //reliable? + + Handle(Geom_BSplineCurve) spline = c.BSpline(); + + if (spline->NbPoles() < 5) { //need 5 poles (s-p1-pm-p2-e) for algo + return result; //how to do with fewer poles? + } + + try { + // get three points on curve (non extreme poles) + int nb_poles = spline->NbPoles(); + gp_Pnt p1 = spline->Pole(2); //OCC numbering starts at 1!! + gp_Pnt p2 = spline->Pole(nb_poles-1); + gp_Pnt pm; + if (nb_poles == 5) { + pm = spline->Pole(3); //5 poles => 2.5 => 2 + } else { + pm = spline->Pole(nb_poles / 2); + } + + // project three poles onto the curve + GeomAPI_ProjectPointOnCurve proj1; + GeomAPI_ProjectPointOnCurve proj2; + GeomAPI_ProjectPointOnCurve projm; + proj1.Init(p1, curve, f, l); + proj1.Perform(p1); + proj2.Init(p2, curve, f, l); + proj2.Perform(p2); + projm.Init(pm, curve, f, l); + projm.Perform(pm); + if ( (proj1.NbPoints() == 0) || + (proj2.NbPoints() == 0) || + (projm.NbPoints() == 0) ) { + return result; + } + gp_Pnt pc1, pc2, pcm; + + // get projected points + pc1 = proj1.NearestPoint(); + pc2 = proj2.NearestPoint(); + pcm = projm.NearestPoint(); + + // make 2 circles and find their radii + gce_MakeCirc gce_circ1 = gce_MakeCirc(s, pc1, pcm); //3 point circle + if (gce_circ1.Status() != gce_Done) { + return result; + } + gp_Circ circle1 = gce_circ1.Value(); + double radius1 = circle1.Radius(); + gp_Pnt center1 = circle1.Location(); + Base::Vector3d vc1 = DrawUtil::toVector3d(center1); + + gce_MakeCirc gce_circ2 = gce_MakeCirc(pcm, pc2, e); + if (gce_circ2.Status() != gce_Done) { + return result; + } + gp_Circ circle2 = gce_circ2.Value(); + double radius2 = circle2.Radius(); + gp_Pnt center2 = circle2.Location(); + Base::Vector3d vc2 = DrawUtil::toVector3d(center2); + + // compare radii & centers + double allowError = 0.001; //mm^-3 good enough for printing + double radius; + Base::Vector3d center; + if ( (DrawUtil::fpCompare(radius2, radius1, allowError)) && + (vc1.IsEqual(vc2, allowError)) ) { + if (arc) { + GC_MakeArcOfCircle makeArc(s, pcm, e); + Handle(Geom_TrimmedCurve) tCurve = makeArc.Value(); + BRepBuilderAPI_MakeEdge mkEdge(tCurve); + result = mkEdge.Edge(); + } else { + radius = (radius1 + radius2) / 2.0; + center = (vc1 + vc2) / 2.0; + gp_Pnt gCenter(center.x, center.y, center.z); + gp_Ax2 stdZ(gCenter, gp_Dir(0, 0, 1)); + gp_Circ newCirc(stdZ, radius); + BRepBuilderAPI_MakeEdge mkEdge(newCirc); + result = mkEdge.Edge(); + } + } + } + catch (const Standard_Failure& e) { + Base::Console().Error("asCircle - OCC error - %s - while approx spline as circle\n", + e.GetMessageString()); + throw Base::RuntimeError("Failed to make circle from bspline"); + } + catch (...) { + Base::Console().Error("asCircle - unknown error occurred while approx spline as circle\n"); + } + return result; +} diff --git a/src/Mod/TechDraw/App/Geometry.h b/src/Mod/TechDraw/App/Geometry.h index 2d564117e1..344ecccb13 100644 --- a/src/Mod/TechDraw/App/Geometry.h +++ b/src/Mod/TechDraw/App/Geometry.h @@ -274,7 +274,7 @@ class TechDrawExport BSpline: public BaseGeom bool isLine(); bool isCircle(); TopoDS_Edge asCircle(bool& isArc); - void getCircleParms(bool& isCircle, double& radius, Base::Vector3d& center, bool& isArc); +// void getCircleParms(bool& isCircle, double& radius, Base::Vector3d& center, bool& isArc); bool intersectsArc(Base::Vector3d p1, Base::Vector3d p2); std::vector segments; }; @@ -390,6 +390,10 @@ class TechDrawExport GeometryUtils static TopoDS_Edge edgeFromGeneric(TechDraw::GenericPtr g); static TopoDS_Edge edgeFromCircle(TechDraw::CirclePtr c); static TopoDS_Edge edgeFromCircleArc(TechDraw::AOCPtr c); + + static bool isCircle(TopoDS_Edge occEdge); + static bool getCircleParms(TopoDS_Edge occEdge, double& radius, Base::Vector3d& center, bool& isArc); + static TopoDS_Edge asCircle(TopoDS_Edge occEdge, bool& arc); }; } //end namespace TechDraw diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp index e35055dc79..2c8bafdfd2 100644 --- a/src/Mod/TechDraw/App/GeometryObject.cpp +++ b/src/Mod/TechDraw/App/GeometryObject.cpp @@ -143,6 +143,7 @@ void GeometryObject::clear() void GeometryObject::projectShape(const TopoDS_Shape& inShape, const gp_Ax2& viewAxis) { +// Base::Console().Message("GO::projectShape()\n"); clear(); Handle(HLRBRep_Algo) brep_hlr; @@ -251,9 +252,11 @@ void GeometryObject::projectShape(const TopoDS_Shape& inShape, const gp_Ax2& vie //convert the hlr output into TD Geometry void GeometryObject::makeTDGeometry() { - extractGeometry(TechDraw::ecHARD,//always show the hard&outline visible lines - true); - extractGeometry(TechDraw::ecOUTLINE, true); +// Base::Console().Message("GO::makeTDGeometry()\n"); + extractGeometry(TechDraw::ecHARD, //always show the hard&outline visible lines + true); + extractGeometry(TechDraw::ecOUTLINE, + true); const DrawViewPart* dvp = static_cast(m_parent); if (!dvp) { diff --git a/src/Mod/TechDraw/App/LandmarkDimension.cpp b/src/Mod/TechDraw/App/LandmarkDimension.cpp index 0b37991800..0edad37854 100644 --- a/src/Mod/TechDraw/App/LandmarkDimension.cpp +++ b/src/Mod/TechDraw/App/LandmarkDimension.cpp @@ -87,7 +87,7 @@ short LandmarkDimension::mustExecute() const App::DocumentObjectExecReturn *LandmarkDimension::execute() { -// Base::Console().Message("LD::execute() - %s\n", getNameInDocument()); + Base::Console().Message("LD::execute() - %s\n", getNameInDocument()); if (!keepUpdated()) { return App::DocumentObject::StdReturn; } @@ -99,6 +99,7 @@ App::DocumentObjectExecReturn *LandmarkDimension::execute() References2D.setValue(dvp); std::vector features = References3D.getValues(); + Base::Console().Message("LD::execute - features: %d\n", features.size()); //if distance, required size = 2 //if angle, required size = 3; //not implemented yet unsigned int requiredSize = 2; @@ -129,17 +130,16 @@ App::DocumentObjectExecReturn *LandmarkDimension::execute() index++; } } - m_linearPoints.first = points.front(); - m_linearPoints.second = points.back(); - - //m_anglePoints.first = //not implemented yet + Base::Console().Message("LD::execute - front: %s back: %s\n", + DrawUtil::formatVector(points.front()).c_str(), + DrawUtil::formatVector(points.back()).c_str()); + setLinearPoints(points.front(), points.back()); App::DocumentObjectExecReturn* dvdResult = DrawViewDimension::execute(); -// dvp->resetReferenceVerts(); dvp->addReferencesToGeom(); - dvp->requestPaint(); - + dvp->requestPaint(); + overrideKeepUpdated(false); return dvdResult; } @@ -159,51 +159,6 @@ Base::Vector3d LandmarkDimension::projectPoint(const Base::Vector3d& pt, DrawVie return result; } -std::vector LandmarkDimension::get2DPoints() const -{ -// Base::Console().Message("LD::get2DPoints()\n"); - std::vector result; - std::vector refs3 = References3D.getValues(); - TechDraw::DrawViewPart* dvp = getViewPart(); - for (auto& r: refs3) { - Base::Vector3d loc3d = ShapeExtractor::getLocation3dFromFeat(r); - Base::Vector3d loc2d = projectPoint(loc3d, dvp); - result.push_back(loc2d); - } - return result; -} - -//! References2D are only used to store ParentView -bool LandmarkDimension::has2DReferences() const -{ - bool result = false; - const std::vector &objects = References2D.getValues(); - if (!objects.empty()) { - result = true; - } - return result; -} - -//! References2D are only used to store ParentView -bool LandmarkDimension::checkReferences2D() const -{ - return true; -} - -pointPair LandmarkDimension::getPointsTwoVerts() -{ -// Base::Console().Message("LD::getPointsTwoVerts() - %s\n", getNameInDocument()); - pointPair result; - - TechDraw::DrawViewPart* dvp = getViewPart(); - if (dvp) { - std::vector points = get2DPoints(); - result.first = points.at(0) * dvp->getScale(); - result.second = points.at(1) * dvp->getScale(); - } - return result; -} - int LandmarkDimension::getRefType() const { //TODO: need changes here when other reference dim types added @@ -239,18 +194,13 @@ void LandmarkDimension::onDocumentRestored() } ReferenceTags.setValues(tags); - m_linearPoints.first = points.front(); - m_linearPoints.second = points.back(); + setLinearPoints(points.front(), points.back()); DrawViewDimension::onDocumentRestored(); } void LandmarkDimension::unsetupObject() { - -// bool isRemoving = testStatus(App::ObjectStatus::Remove); -// Base::Console().Message("LD::unsetupObject - isRemove: %d status: %X\n", -// isRemoving, getStatus()); TechDraw::DrawViewPart* dvp = getViewPart(); std::vector tags = ReferenceTags.getValues(); @@ -261,14 +211,3 @@ void LandmarkDimension::unsetupObject() dvp->requestPaint(); } - -//??? why does getPyObject work sometimes and no others??? -//PyObject *LandmarkDimension::getPyObject(void) -//{ -// if (PythonObject.is(Py::_None())) { -// // ref counter is set to 1 -// PythonObject = Py::Object(new LandmarkDimensionPy(this), true); -// } -// return Py::new_reference_to(PythonObject); -//} - diff --git a/src/Mod/TechDraw/App/LandmarkDimension.h b/src/Mod/TechDraw/App/LandmarkDimension.h index 4a0d9d119a..2a9efbb18a 100644 --- a/src/Mod/TechDraw/App/LandmarkDimension.h +++ b/src/Mod/TechDraw/App/LandmarkDimension.h @@ -28,7 +28,6 @@ #include "DrawViewDimension.h" - class TopoDS_Shape; class gp_Ax2; @@ -56,12 +55,7 @@ public: const char* getViewProviderName() const override { return "TechDrawGui::ViewProviderDimension"; } -/* virtual PyObject *getPyObject(void) override;*/ - bool checkReferences2D() const override; - bool has2DReferences() const override; - pointPair getPointsTwoVerts() override; - std::vector get2DPoints() const; DrawViewPart* getViewPart() const override; int getRefType() const override; diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index 0b036c46cd..ea8d415d69 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -84,6 +84,8 @@ set(TechDrawGui_UIC_SRCS TaskMoveView.ui TaskProjection.ui TaskComplexSection.ui + TaskDimRepair.ui + ) SET(MRTE_SRCS @@ -108,6 +110,8 @@ SET(TechDrawGui_SRCS CommandExtensionDims.cpp CommandExtensionPack.cpp CommandStack.cpp + DimensionValidators.cpp + DimensionValidators.h Resources/TechDraw.qrc PreCompiled.cpp PreCompiled.h @@ -209,9 +213,9 @@ SET(TechDrawGui_SRCS TaskDetail.h PreferencesGui.cpp PreferencesGui.h - TaskCosmeticLine.ui TaskCosmeticLine.cpp TaskCosmeticLine.h + TaskCosmeticLine.ui TaskMoveView.ui TaskProjection.cpp TaskProjection.h @@ -219,6 +223,9 @@ SET(TechDrawGui_SRCS TaskComplexSection.cpp TaskComplexSection.h TaskComplexSection.ui + TaskDimRepair.cpp + TaskDimRepair.h + TaskDimRepair.ui Widgets/CompassDialWidget.cpp Widgets/CompassDialWidget.h Widgets/CompassWidget.cpp @@ -429,6 +436,7 @@ SET(TechDrawGuiTaskDlgs_SRCS TaskMoveView.ui TaskProjection.ui TaskComplexSection.ui + TaskDimRepair.ui ) SOURCE_GROUP("MRTE" FILES ${MRTE_SRCS}) diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index 2cac992b5f..73296d46d4 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) 2014 Luke Parry * + * Copyright (c) 2022 WandererFan * * * * This file is part of the FreeCAD CAx development system. * * * @@ -22,15 +23,17 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -#endif +#include +#include +#include +#include +#include +#endif //#ifndef _PreComp_ #include #include +#include #include -#include #include #include #include @@ -41,34 +44,24 @@ #include #include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include #include "DrawGuiUtil.h" #include "TaskLinkDim.h" - +#include "TaskDimRepair.h" +#include "DimensionValidators.h" using namespace TechDrawGui; using namespace TechDraw; +using namespace std; -enum EdgeType{ - isInvalid, - isHorizontal, - isVertical, - isDiagonal, - isCircle, - isEllipse, - isBSplineCircle, - isBSpline, - isAngle, - isAngle3Pt - }; //=========================================================================== // utility routines @@ -77,17 +70,23 @@ enum EdgeType{ //internal functions bool _checkSelection(Gui::Command* cmd, unsigned maxObjs = 2); bool _checkDrawViewPart(Gui::Command* cmd); -bool _checkPartFeature(Gui::Command* cmd); -int _isValidSingleEdge(Gui::Command* cmd); -bool _isValidVertexes(Gui::Command* cmd, int count = 2); -int _isValidEdgeToEdge(Gui::Command* cmd); -bool _isValidVertexToEdge(Gui::Command* cmd); -char* _edgeTypeToText(int e); -//bool _checkActive(Gui::Command* cmd, Base::Type classType, bool needSubs); -void execHExtent(Gui::Command* cmd); -void execVExtent(Gui::Command* cmd); +void execDistance(Gui::Command* cmd); +void execDistanceX(Gui::Command* cmd); +void execDistanceY(Gui::Command* cmd); +void execAngle(Gui::Command* cmd); +void execAngle3Pt(Gui::Command* cmd); +void execRadius(Gui::Command* cmd); +void execDiameter(Gui::Command* cmd); +void execExtent(Gui::Command* cmd, int direction); + +DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, + std::string dimType, + ReferenceVector references2d, + ReferenceVector references3d); + +void positionDimText(DrawViewDimension* dim); //NOTE: this is not shown in toolbar and doesn't always work right in the menu. // should be removed. @@ -114,103 +113,6 @@ CmdTechDrawDimension::CmdTechDrawDimension() void CmdTechDrawDimension::activated(int iMsg) { Q_UNUSED(iMsg); -// bool result = _checkSelection(this, 2); -// if (!result) -// return; -// result = _checkDrawViewPart(this); -// if (!result) -// return; - -////do we still need to pick DVPs out of selection? or should we complain about junk? -// std::vector selection = getSelection().getSelectionEx(); -// TechDraw::DrawViewPart* objFeat = 0; -// std::vector SubNames; -// std::vector::iterator itSel = selection.begin(); -// for (; itSel != selection.end(); itSel++) { -// if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { -// objFeat = static_cast ((*itSel).getObject()); -// SubNames = (*itSel).getSubNames(); -// } -// } -// TechDraw::DrawPage* page = objFeat->findParentPage(); -// std::string PageName = page->getNameInDocument(); - -// TechDraw::DrawViewDimension *dim = 0; -// std::string FeatName = getUniqueObjectName("Dimension"); -// std::string dimType; - -// std::vector objs; -// std::vector subs; - -// int edgeType = _isValidSingleEdge(this); - -// if (edgeType) { -// if (edgeType < isCircle) { -// dimType = "Distance"; -// objs.push_back(objFeat); -// subs.push_back(SubNames[0]); -// } else if (edgeType == isCircle) { -// dimType = "Radius"; -// } else { -// dimType = "Radius"; -// } -// } else if (_isValidVertexes(this)) { -// dimType = "Distance"; -// objs.push_back(objFeat); -// objs.push_back(objFeat); -// subs.push_back(SubNames[0]); -// subs.push_back(SubNames[1]); -// } else if (_isValidEdgeToEdge(this)) { -// int edgeCase = _isValidEdgeToEdge(this); -// objs.push_back(objFeat); -// objs.push_back(objFeat); -// subs.push_back(SubNames[0]); -// subs.push_back(SubNames[1]); -// switch (edgeCase) { -// case isHorizontal: -// dimType = "DistanceX"; -// break; -// case isVertical: -// dimType = "DistanceY"; -// break; -// case isDiagonal: -// dimType = "Distance"; -// break; -// case isAngle: -// dimType = "Angle"; -// default: -// break; -// } -// } else if (_isValidVertexToEdge(this)) { -// dimType = "Distance"; -// objs.push_back(objFeat); -// objs.push_back(objFeat); -// subs.push_back(SubNames[0]); -// subs.push_back(SubNames[1]); -// } else { -// QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), -// QObject::tr("Can not make a Dimension from this selection")); -// return; -// } - -// openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); -// doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); -// doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() -// ,dimType.c_str()); - -// dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); -// if (!dim) { -// throw Base::TypeError("CmdTechDrawNewDimension - dim not found\n"); -// } -// dim->References2D.setValues(objs, subs); - -// doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); -// commitCommand(); -// dim->recomputeFeature(); - -// //Horrible hack to force Tree update -// double x = objFeat->X.getValue(); -// objFeat->X.setValue(x); } bool CmdTechDrawDimension::isActive() @@ -220,6 +122,98 @@ bool CmdTechDrawDimension::isActive() return (havePage && haveView); } +//=========================================================================== +// TechDraw_RadialGroup +//=========================================================================== + +DEF_STD_CMD_ACL(CmdTechDrawRadialGroup) + +CmdTechDrawRadialGroup::CmdTechDrawRadialGroup() + : Command("TechDraw_RadialGroup") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert Radius Dimension"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_RadialGroup"; + sStatusTip = sToolTipText; +} + +void CmdTechDrawRadialGroup::activated(int iMsg) +{ + // Base::Console().Message("CMD::LinearGrp - activated(%d)\n", iMsg); + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); + return; + } + + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); + switch(iMsg) { + case 0: + execRadius(this); + break; + case 1: + execDiameter(this); + break; + default: + Base::Console().Message("CMD::RadialGrp - invalid iMsg: %d\n", iMsg); + }; +} + +Gui::Action * CmdTechDrawRadialGroup::createAction() +{ + Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); + pcAction->setDropDownMenu(true); + applyCommandData(this->className(), pcAction); + + QAction* p0 = pcAction->addAction(QString()); + p0->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_RadiusDimension")); + p0->setObjectName(QString::fromLatin1("TechDraw_RadiusDimension")); + p0->setWhatsThis(QString::fromLatin1("TechDraw_RadiusDimension")); + QAction* p1 = pcAction->addAction(QString()); + p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_DiameterDimension")); + p1->setObjectName(QString::fromLatin1("TechDraw_DiameterDimension")); + p1->setWhatsThis(QString::fromLatin1("TechDraw_DiameterDimension")); + + _pcAction = pcAction; + languageChange(); + + pcAction->setIcon(p0->icon()); + int defaultId = 0; + pcAction->setProperty("defaultAction", QVariant(defaultId)); + + return pcAction; +} + +void CmdTechDrawRadialGroup::languageChange() +{ + Command::languageChange(); + + if (!_pcAction) + return; + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + QList a = pcAction->actions(); + + QAction* arc0 = a[0]; + arc0->setText(QApplication::translate("CmdTechDrawRadialGroup", "Radius Dimension")); + arc0->setToolTip(QApplication::translate("TechDraw_RadiusDimension", "Insert Radius Dimension")); + arc0->setStatusTip(arc0->toolTip()); + QAction* arc1 = a[1]; + arc1->setText(QApplication::translate("CmdTechDrawRadialGroup", "Diameter Linear")); + arc1->setToolTip(QApplication::translate("TechDraw_DiameterDimension", "Insert Diameter Dimension")); + arc1->setStatusTip(arc1->toolTip()); +} + +bool CmdTechDrawRadialGroup::isActive() +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this, false); + return (havePage && haveView); +} + //=========================================================================== // TechDraw_RadiusDimension //=========================================================================== @@ -241,92 +235,7 @@ CmdTechDrawRadiusDimension::CmdTechDrawRadiusDimension() void CmdTechDrawRadiusDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 1); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - - std::vector objs; - std::vector subs; - - int edgeType = _isValidSingleEdge(this); - if (edgeType == isCircle) { - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - } else if (edgeType == isEllipse) { - QMessageBox::StandardButton result = - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Ellipse Curve Warning"), - QObject::tr("Selected edge is an Ellipse. Radius will be approximate. Continue?"), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - if (result == QMessageBox::Ok) { - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - } else { - return; - } - } else if (edgeType == isBSplineCircle) { - QMessageBox::StandardButton result = - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("BSpline Curve Warning"), - QObject::tr("Selected edge is a BSpline. Radius will be approximate. Continue?"), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - if (result == QMessageBox::Ok) { - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - } else { - return; - } - } else if (edgeType == isBSpline) { - QMessageBox::critical(Gui::getMainWindow(), QObject::tr("BSpline Curve Error"), - QObject::tr("Selected edge is a BSpline and a radius can not be calculated.")); - return; - } else { - QMessageBox::warning( - Gui::getMainWindow(), QObject::tr("Incorrect Selection"), - QObject::tr("Selection for Radius does not contain a circular edge " - "(edge type: %1)") - .arg(QString::fromStdString(_edgeTypeToText(edgeType)))); - return; - } - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - ,"Radius"); - - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawNewRadiusDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - - dim->recomputeFeature(); - - //Horrible hack to force Tree update - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); + execRadius(this); } bool CmdTechDrawRadiusDimension::isActive() @@ -336,6 +245,96 @@ bool CmdTechDrawRadiusDimension::isActive() return (havePage && haveView); } +void execRadius(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a radius dimension + StringVector acceptableGeometry( { "Edge" } ); + std::vector minimumCounts( { 1 } ); + std::vector acceptableDimensionGeometrys( { isCircle, isEllipse, isBSplineCircle } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d radius dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d(isInvalid); + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d radius dimension from selection")); + return; + } + + } + + //errors and warnings + if (geometryRefs2d == isEllipse || + geometryRefs3d == isEllipse) { + QMessageBox::StandardButton result = + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Ellipse Curve Warning"), + QObject::tr("Selected edge is an Ellipse. Radius will be approximate. Continue?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); + if (result != QMessageBox::Ok) { + return; + } + } + if (geometryRefs2d == isBSplineCircle || + geometryRefs3d == isBSplineCircle) { + QMessageBox::StandardButton result = + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("BSpline Curve Warning"), + QObject::tr("Selected edge is a BSpline. Radius will be approximate. Continue?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); + if (result != QMessageBox::Ok) { + return; + } + } + if (geometryRefs2d == isBSpline || + geometryRefs3d == isBSpline) { + QMessageBox::critical(Gui::getMainWindow(), QObject::tr("BSpline Curve Error"), + QObject::tr("Selected edge is a BSpline and a radius can not be calculated.")); + return; + } + + //build the dimension + // DrawViewDimension* dim = + dimensionMaker(partFeat, + "Radius", + references2d, + references3d); + + //Horrible hack to force Tree update + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + + //=========================================================================== // TechDraw_DiameterDimension //=========================================================================== @@ -357,87 +356,7 @@ CmdTechDrawDiameterDimension::CmdTechDrawDiameterDimension() void CmdTechDrawDiameterDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 1); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - - std::vector objs; - std::vector subs; - - int edgeType = _isValidSingleEdge(this); - if (edgeType == isCircle) { - // nothing to do - } else if (edgeType == isEllipse) { - QMessageBox::StandardButton result = - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Ellipse Curve Warning"), - QObject::tr("Selected edge is an Ellipse. Diameter will be approximate. Continue?"), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - if (result != QMessageBox::Ok) { - return; - } - } else if (edgeType == isBSplineCircle) { - QMessageBox::StandardButton result = - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("BSpline Curve Warning"), - QObject::tr("Selected edge is a BSpline. Diameter will be approximate. Continue?"), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - if (result != QMessageBox::Ok) { - return; - } - } else if (edgeType == isBSpline) { - QMessageBox::critical(Gui::getMainWindow(), QObject::tr("BSpline Curve Error"), - QObject::tr("Selected edge is a BSpline and a diameter can not be calculated.")); - return; - } else { - QMessageBox::warning( - Gui::getMainWindow(), QObject::tr("Incorrect Selection"), - QObject::tr("Selection for Diameter does not contain a circular edge " - "(edge type: %1)") - .arg(QString::fromStdString(_edgeTypeToText(edgeType)))); - return; - } - - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - ,"Diameter"); - - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawNewDiameterDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - dim->recomputeFeature(); - - //Horrible hack to force Tree update - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); + execDiameter(this); } bool CmdTechDrawDiameterDimension::isActive() @@ -447,6 +366,197 @@ bool CmdTechDrawDiameterDimension::isActive() return (havePage && haveView); } +void execDiameter(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a diameter dimension + StringVector acceptableGeometry( { "Edge" } ); + std::vector minimumCounts( { 1 } ); + std::vector acceptableDimensionGeometrys( { isCircle, isEllipse, isBSplineCircle } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d diameter dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d(isInvalid); + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d diameter dimension from selection")); + return; + } + + } + + //errors and warnings + if (geometryRefs2d == isEllipse || + geometryRefs3d == isEllipse) { + QMessageBox::StandardButton result = + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Ellipse Curve Warning"), + QObject::tr("Selected edge is an Ellipse. Diameter will be approximate. Continue?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); + if (result != QMessageBox::Ok) { + return; + } + } + if (geometryRefs2d == isBSplineCircle || + geometryRefs3d == isBSplineCircle) { + QMessageBox::StandardButton result = + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("BSpline Curve Warning"), + QObject::tr("Selected edge is a BSpline. Diameter will be approximate. Continue?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); + if (result != QMessageBox::Ok) { + return; + } + } + if (geometryRefs2d == isBSpline || + geometryRefs3d == isBSpline) { + QMessageBox::critical(Gui::getMainWindow(), QObject::tr("BSpline Curve Error"), + QObject::tr("Selected edge is a BSpline and a diameter can not be calculated.")); + return; + } + + //build the dimension + // DrawViewDimension* dim = + dimensionMaker(partFeat, + "Diameter", + references2d, + references3d); + + //Horrible hack to force Tree update + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + +//=========================================================================== +// TechDraw_LinearGroup +//=========================================================================== + +DEF_STD_CMD_ACL(CmdTechDrawLinearGroup) + +CmdTechDrawLinearGroup::CmdTechDrawLinearGroup() + : Command("TechDraw_LinearGroup") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert Linear Dimension"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_LinearGroup"; + sStatusTip = sToolTipText; +} + +void CmdTechDrawLinearGroup::activated(int iMsg) +{ + // Base::Console().Message("CMD::LinearGrp - activated(%d)\n", iMsg); + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); + return; + } + + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); + switch(iMsg) { + case 0: + execDistance(this); + break; + case 1: + execDistanceX(this); + break; + case 2: + execDistanceY(this); + break; + default: + Base::Console().Message("CMD::LinearGrp - invalid iMsg: %d\n", iMsg); + }; +} + +Gui::Action * CmdTechDrawLinearGroup::createAction() +{ + Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); + pcAction->setDropDownMenu(true); + applyCommandData(this->className(), pcAction); + + QAction* p0 = pcAction->addAction(QString()); + p0->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_LengthDimension")); + p0->setObjectName(QString::fromLatin1("TechDraw_LengthDimension")); + p0->setWhatsThis(QString::fromLatin1("TechDraw_LengthDimension")); + QAction* p1 = pcAction->addAction(QString()); + p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_HorizontalDimension")); + p1->setObjectName(QString::fromLatin1("TechDraw_HorizontalDimension")); + p1->setWhatsThis(QString::fromLatin1("TechDraw_HorizontalDimension")); + QAction* p2 = pcAction->addAction(QString()); + p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_VerticalDimension")); + p2->setObjectName(QString::fromLatin1("TechDraw_VerticalDimension")); + p2->setWhatsThis(QString::fromLatin1("TechDraw_VerticalDimension")); + + _pcAction = pcAction; + languageChange(); + + pcAction->setIcon(p0->icon()); + int defaultId = 0; + pcAction->setProperty("defaultAction", QVariant(defaultId)); + + return pcAction; +} + +void CmdTechDrawLinearGroup::languageChange() +{ + Command::languageChange(); + + if (!_pcAction) + return; + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + QList a = pcAction->actions(); + + QAction* arc0 = a[0]; + arc0->setText(QApplication::translate("CmdTechDrawLinearGroup", "Linear Dimension")); + arc0->setToolTip(QApplication::translate("TechDraw_LengthDimension", "Insert Linear Dimension")); + arc0->setStatusTip(arc0->toolTip()); QAction* arc1 = a[1]; + arc1->setText(QApplication::translate("CmdTechDrawLinearGroup", "Horizontal Linear")); + arc1->setToolTip(QApplication::translate("TechDraw_HorizontalLinear", "Insert Horizontal Linear Dimension")); + arc1->setStatusTip(arc1->toolTip()); + QAction* arc2 = a[2]; + arc2->setText(QApplication::translate("CmdTechDrawLinearGroup", "Vertical Linear")); + arc2->setToolTip(QApplication::translate("TechDraw_VerticalDimension", "Insert Vertical Linear Dimension")); + arc2->setStatusTip(arc2->toolTip()); +} + +bool CmdTechDrawLinearGroup::isActive() +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this, false); + return (havePage && haveView); +} + //=========================================================================== // TechDraw_LengthDimension //=========================================================================== @@ -468,80 +578,7 @@ CmdTechDrawLengthDimension::CmdTechDrawLengthDimension() void CmdTechDrawLengthDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 2); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - std::string dimType; - - std::vector objs; - std::vector subs; - - if ( (_isValidSingleEdge(this) == isVertical) || - (_isValidSingleEdge(this) == isHorizontal) || - (_isValidSingleEdge(this) == isDiagonal) ) { - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - } else if ( _isValidVertexes(this) || - (_isValidEdgeToEdge(this) == isVertical) || - (_isValidEdgeToEdge(this) == isHorizontal) || - (_isValidEdgeToEdge(this) == isDiagonal) || - (_isValidVertexToEdge(this)) ) { - objs.push_back(objFeat); - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - subs.push_back(SubNames[1]); - } else { - QMessageBox::warning(Gui::getMainWindow(), - QObject::tr("Incorrect Selection"), - QObject::tr("Need 1 straight Edge, 2 Vertexes, 2 Edges or " - "1 Vertex and 1 Edge for Distance Dimension")); - return; - } - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - , "Distance"); - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawNewLengthDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - dim->recomputeFeature(); - - TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second)/2.0; - dim->X.setValue(mid.x); - double fontSize = Preferences::dimFontSizeMM(); - dim->Y.setValue(-mid.y + 0.5 * fontSize); - - //Horrible hack to force Tree update (claimChildren) - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); } bool CmdTechDrawLengthDimension::isActive() @@ -551,6 +588,68 @@ bool CmdTechDrawLengthDimension::isActive() return (havePage && haveView); } +void execDistance(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a length dimension + StringVector acceptableGeometry( { "Edge", "Vertex" } ); + std::vector minimumCounts( { 1, 2 } ); + std::vector acceptableDimensionGeometrys( { isVertical, isHorizontal, isDiagonal } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d linear dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d linear dimension from selection")); + return; + } + } + + //build the dimension + DrawViewDimension* dim = dimensionMaker(partFeat, + "Distance", + references2d, + references3d); + + //position the Dimension text on the view + positionDimText(dim); + + //Horrible hack to force Tree update (claimChildren) + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + //=========================================================================== // TechDraw_HorizontalDimension //=========================================================================== @@ -573,80 +672,7 @@ CmdTechDrawHorizontalDimension::CmdTechDrawHorizontalDimension() void CmdTechDrawHorizontalDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 2); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - std::string dimType; - - std::vector objs; - std::vector subs; - - if ( (_isValidSingleEdge(this) == isVertical) || - (_isValidSingleEdge(this) == isHorizontal) || - (_isValidSingleEdge(this) == isDiagonal) ) { - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - } else if ( _isValidVertexes(this) || - (_isValidEdgeToEdge(this) == isVertical) || - (_isValidEdgeToEdge(this) == isHorizontal) || - (_isValidEdgeToEdge(this) == isDiagonal) || - (_isValidVertexToEdge(this)) ) { - objs.push_back(objFeat); - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - subs.push_back(SubNames[1]); - } else { - QMessageBox::warning(Gui::getMainWindow(), - QObject::tr("Incorrect Selection"), - QObject::tr("Need 1 straight Edge, 2 Vertexes, 2 Edges or " - "1 Vertex and 1 Edge for Horizontal Dimension")); - return; - } - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - ,"DistanceX"); - - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawNewDistanceXDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - dim->recomputeFeature(); - TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second)/2.0; - dim->X.setValue(mid.x); - double fontSize = Preferences::dimFontSizeMM(); - dim->Y.setValue(-mid.y + 0.5 * fontSize); - - //Horrible hack to force Tree update - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); + execDistanceX(this); } bool CmdTechDrawHorizontalDimension::isActive() @@ -656,6 +682,69 @@ bool CmdTechDrawHorizontalDimension::isActive() return (havePage && haveView); } +void execDistanceX(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a length dimension + StringVector acceptableGeometry( { "Edge", "Vertex" } ); + std::vector minimumCounts( { 1, 2 } ); + std::vector acceptableDimensionGeometrys( { isHorizontal } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d horizontal dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if (geometryRefs3d == TechDraw::isInvalid) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d horizontal dimension from selection")); + return; + } + } + + //build the dimension + DrawViewDimension* dim = dimensionMaker(partFeat, + "DistanceX", + references2d, + references3d); + + + //position the Dimension text on the view + positionDimText(dim); + + //Horrible hack to force Tree update (claimChildren) + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + //=========================================================================== // TechDraw_VerticalDimension //=========================================================================== @@ -678,79 +767,7 @@ CmdTechDrawVerticalDimension::CmdTechDrawVerticalDimension() void CmdTechDrawVerticalDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 2); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - std::string dimType; - - std::vector objs; - std::vector subs; - - if ( (_isValidSingleEdge(this) == isVertical) || - (_isValidSingleEdge(this) == isHorizontal) || - (_isValidSingleEdge(this) == isDiagonal) ) { - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - } else if ( _isValidVertexes(this) || - (_isValidEdgeToEdge(this) == isVertical) || - (_isValidEdgeToEdge(this) == isHorizontal) || - (_isValidEdgeToEdge(this) == isDiagonal) || - (_isValidVertexToEdge(this)) ) { - objs.push_back(objFeat); - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - subs.push_back(SubNames[1]); - } else { - QMessageBox::warning(Gui::getMainWindow(), - QObject::tr("Incorrect Selection"), - QObject::tr("Need 1 straight Edge, 2 Vertexes, 2 Edges or " - "1 Vertex and 1 Edge for Vertical Dimension")); - return; - } - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - ,"DistanceY"); - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawNewDistanceYDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - dim->recomputeFeature(); - TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second)/2.0; - dim->X.setValue(mid.x); - double fontSize = Preferences::dimFontSizeMM(); - dim->Y.setValue(-mid.y + 0.5 * fontSize); - - //Horrible hack to force Tree update - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); + execDistanceY(this); } bool CmdTechDrawVerticalDimension::isActive() @@ -760,6 +777,158 @@ bool CmdTechDrawVerticalDimension::isActive() return (havePage && haveView); } +void execDistanceY(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a length dimension + StringVector acceptableGeometry( { "Edge", "Vertex" } ); + std::vector minimumCounts( { 1, 2 } ); + std::vector acceptableDimensionGeometrys( { isVertical } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d vertical dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == TechDraw::isVertical) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d vertical dimension from selection")); + return; + } + } + + //build the dimension + DrawViewDimension* dim = dimensionMaker(partFeat, + "DistanceY", + references2d, + references3d); + + //position the Dimension text on the view + positionDimText(dim); + + //Horrible hack to force Tree update (claimChildren) + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + +//=========================================================================== +// TechDraw_AngularGroup +//=========================================================================== + +DEF_STD_CMD_ACL(CmdTechDrawAngularGroup) + +CmdTechDrawAngularGroup::CmdTechDrawAngularGroup() + : Command("TechDraw_AngularGroup") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert Angular Dimension"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_AngularGroup"; + sStatusTip = sToolTipText; +} + +void CmdTechDrawAngularGroup::activated(int iMsg) +{ + // Base::Console().Message("CMD::LinearGrp - activated(%d)\n", iMsg); + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); + return; + } + + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); + switch(iMsg) { + case 0: + execAngle(this); + break; + case 1: + execAngle3Pt(this); + break; + default: + Base::Console().Message("CMD::AngularGrp - invalid iMsg: %d\n", iMsg); + }; +} + +Gui::Action * CmdTechDrawAngularGroup::createAction() +{ + Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); + pcAction->setDropDownMenu(true); + applyCommandData(this->className(), pcAction); + + QAction* p0 = pcAction->addAction(QString()); + p0->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_AngleDimension")); + p0->setObjectName(QString::fromLatin1("TechDraw_AngleDimension")); + p0->setWhatsThis(QString::fromLatin1("TechDraw_AngleDimension")); + QAction* p1 = pcAction->addAction(QString()); + p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_3PtAngleDimension")); + p1->setObjectName(QString::fromLatin1("TechDraw_3PtAngleDimension")); + p1->setWhatsThis(QString::fromLatin1("TechDraw_3PtAngleDimension")); + + _pcAction = pcAction; + languageChange(); + + pcAction->setIcon(p0->icon()); + int defaultId = 0; + pcAction->setProperty("defaultAction", QVariant(defaultId)); + + return pcAction; +} + +void CmdTechDrawAngularGroup::languageChange() +{ + Command::languageChange(); + + if (!_pcAction) + return; + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + QList a = pcAction->actions(); + + QAction* arc0 = a[0]; + arc0->setText(QApplication::translate("CmdTechDrawAngularGroup", "Angular Dimension")); + arc0->setToolTip(QApplication::translate("TechDraw_AngleDimension", "Insert Angle Dimension")); + arc0->setStatusTip(arc0->toolTip()); QAction* arc1 = a[1]; + arc1->setText(QApplication::translate("CmdTechDrawAngularGroup", "3Pt Angular Dimension")); + arc1->setToolTip(QApplication::translate("TechDraw_3PtAngleDimension", "Insert 3-Point Angle Dimension")); + arc1->setStatusTip(arc1->toolTip()); +} + +bool CmdTechDrawAngularGroup::isActive() +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this, false); + return (havePage && haveView); +} //=========================================================================== // TechDraw_AngleDimension //=========================================================================== @@ -781,64 +950,7 @@ CmdTechDrawAngleDimension::CmdTechDrawAngleDimension() void CmdTechDrawAngleDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 2); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - - std::vector objs; - std::vector subs; - - int edgeType = _isValidEdgeToEdge(this); - if (edgeType != isAngle) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), - QObject::tr("Need two straight edges to make an Angle Dimension")); - return; - } - - objs.push_back(objFeat); - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - subs.push_back(SubNames[1]); - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - ,"Angle"); - - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawAngleDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - dim->recomputeFeature(); - - //Horrible hack to force Tree update - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); + execAngle(this); } bool CmdTechDrawAngleDimension::isActive() @@ -848,6 +960,68 @@ bool CmdTechDrawAngleDimension::isActive() return (havePage && haveView); } +void execAngle(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a length dimension + StringVector acceptableGeometry( { "Edge" } ); + std::vector minimumCounts( { 2 } ); + std::vector acceptableDimensionGeometrys( { isAngle } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d angle dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d angle dimension from selection")); + return; + } + } + + //build the dimension + DrawViewDimension* dim = dimensionMaker(partFeat, + "Angle", + references2d, + references3d); + + //position the Dimension text on the view + positionDimText(dim); + + //Horrible hack to force Tree update (claimChildren) + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + //=========================================================================== // TechDraw_3PtAngleDimension //=========================================================================== @@ -869,65 +1043,7 @@ CmdTechDraw3PtAngleDimension::CmdTechDraw3PtAngleDimension() void CmdTechDraw3PtAngleDimension::activated(int iMsg) { Q_UNUSED(iMsg); - bool result = _checkSelection(this, 3); - if (!result) - return; - result = _checkDrawViewPart(this); - if (!result) - return; - - std::vector selection = getSelection().getSelectionEx(); - TechDraw::DrawViewPart * objFeat = nullptr; - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - objFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - TechDraw::DrawPage* page = objFeat->findParentPage(); - std::string PageName = page->getNameInDocument(); - - TechDraw::DrawViewDimension *dim = nullptr; - std::string FeatName = getUniqueObjectName("Dimension"); - - std::vector objs; - std::vector subs; - - if (!_isValidVertexes(this, 3)) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), - QObject::tr("Need three points to make a 3 point Angle Dimension")); - return; - } - - objs.push_back(objFeat); - objs.push_back(objFeat); - objs.push_back(objFeat); - subs.push_back(SubNames[0]); - subs.push_back(SubNames[1]); - subs.push_back(SubNames[2]); - - openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); - doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str() - ,"Angle3Pt"); - - dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); - if (!dim) { - throw Base::TypeError("CmdTechDrawNewAngle3PtDimension - dim not found\n"); - } - dim->References2D.setValues(objs, subs); - - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); - - commitCommand(); - dim->recomputeFeature(); - - //Horrible hack to force Tree update - double x = objFeat->X.getValue(); - objFeat->X.setValue(x); + execAngle3Pt(this); } bool CmdTechDraw3PtAngleDimension::isActive() @@ -937,6 +1053,67 @@ bool CmdTechDraw3PtAngleDimension::isActive() return (havePage && haveView); } +void execAngle3Pt(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a length dimension + StringVector acceptableGeometry( { "Vertex" } ); + std::vector minimumCounts( { 3 } ); + std::vector acceptableDimensionGeometrys( { isAngle3Pt } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d angle dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == TechDraw::isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d angle dimension from selection")); + return; + } + } + + //build the dimension + DrawViewDimension* dim = dimensionMaker(partFeat, + "Angle3Pt", + references2d, + references3d); + //position the Dimension text on the view + positionDimText(dim); + + //Horrible hack to force Tree update (claimChildren) + double x = partFeat->X.getValue(); + partFeat->X.setValue(x); +} + //! link 3D geometry to Dimension(s) on a Page //TODO: should we present all potential Dimensions from all Pages? @@ -1049,10 +1226,10 @@ void CmdTechDrawExtentGroup::activated(int iMsg) pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch(iMsg) { case 0: - execHExtent(this); + execExtent(this, 0); break; case 1: - execVExtent(this); + execExtent(this, 1); break; default: Base::Console().Message("CMD::ExtGrp - invalid iMsg: %d\n", iMsg); @@ -1139,7 +1316,7 @@ void CmdTechDrawHorizontalExtentDimension::activated(int iMsg) return; } - execHExtent(this); + execExtent(this, 0); } bool CmdTechDrawHorizontalExtentDimension::isActive() @@ -1149,48 +1326,80 @@ bool CmdTechDrawHorizontalExtentDimension::isActive() return (havePage && haveView); } -void execHExtent(Gui::Command* cmd) +void execExtent(Gui::Command* cmd, int direction) { - TechDraw::DrawPage* page = DrawGuiUtil::findPage(cmd); - if (!page) { + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning( Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.") ); return; } - std::vector selection = cmd->getSelection().getSelectionEx(); - TechDraw::DrawViewPart* baseFeat = nullptr; - if (selection.empty()) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), - QObject::tr("Please select a View [and Edges].")); + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a extent dimension + StringVector acceptableGeometry( { "Edge" } ); + std::vector minimumCounts( { 1 } ); + std::vector acceptableDimensionGeometrys( { isMultiEdge, + isHorizontal, + isVertical, + isDiagonal, + isCircle, + isEllipse, + isBSplineCircle, + isBSpline, + isZLimited } ); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs2d == TechDraw::isInvalid) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d extent dimension from selection")); return; } - - baseFeat = dynamic_cast(selection[0].getObject()); - if (!baseFeat) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), - QObject::tr("No base View in Selection.")); - return; - } - - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - SubNames = (*itSel).getSubNames(); + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == isInvalid ) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d extent dimension from selection")); + return; } } - std::vector edgeNames; - for (auto& s: SubNames) { - std::string geomType = DrawUtil::getGeomTypeFromName(s); - if (geomType == "Edge") { - edgeNames.push_back(s); + if (references3d.empty()) { + std::vector edgeNames; + for (auto& ref : references2d) { + if (ref.getSubName().empty()) { + continue; + } + std::string geomType = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomType == "Edge") { + edgeNames.push_back(ref.getSubName()); + } } + DrawDimHelper::makeExtentDim(partFeat, + edgeNames, + direction); //0 - horizontal, 1 - vertical + } else { + DrawDimHelper::makeExtentDim3d(partFeat, + references3d, + direction); //0 - horizontal, 1 - vertical } - - DrawDimHelper::makeExtentDim(baseFeat, - edgeNames, - 0); } //=========================================================================== @@ -1222,7 +1431,7 @@ void CmdTechDrawVerticalExtentDimension::activated(int iMsg) return; } - execVExtent(this); + execExtent(this, 1); } bool CmdTechDrawVerticalExtentDimension::isActive() @@ -1232,51 +1441,58 @@ bool CmdTechDrawVerticalExtentDimension::isActive() return (havePage && haveView); } -void execVExtent(Gui::Command* cmd) +//=========================================================================== +// TechDraw_DimensionRepair +//=========================================================================== + +DEF_STD_CMD_A(CmdTechDrawDimensionRepair) + +CmdTechDrawDimensionRepair::CmdTechDrawDimensionRepair() + : Command("TechDraw_DimensionRepair") { - TechDraw::DrawPage* page = DrawGuiUtil::findPage(cmd); - if (!page) { - return; - } - - std::vector selection = cmd->getSelection().getSelectionEx(); - TechDraw::DrawViewPart* baseFeat = nullptr; - if (selection.empty()) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), - QObject::tr("Please select a View [and Edges].")); - return; - } - - - baseFeat = dynamic_cast(selection[0].getObject()); - if (!baseFeat) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Error"), - QObject::tr("No base View in Selection.")); - return; - } - - std::vector SubNames; - - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if ((*itSel).getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { - baseFeat = static_cast ((*itSel).getObject()); - SubNames = (*itSel).getSubNames(); - } - } - std::vector edgeNames; - for (auto& s: SubNames) { - std::string geomType = DrawUtil::getGeomTypeFromName(s); - if (geomType == "Edge") { - edgeNames.push_back(s); - } - } - - DrawDimHelper::makeExtentDim(baseFeat, - edgeNames, - 1); + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Repair Dimension References"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_DimensionRepair"; + sStatusTip = sToolTipText; + sPixmap = "TechDraw_DimensionRepair"; } +void CmdTechDrawDimensionRepair::activated(int iMsg) +{ + Q_UNUSED(iMsg); + std::vector dimObjs = getSelection().getObjectsOfType(TechDraw::DrawViewDimension::getClassTypeId()); + TechDraw::DrawViewDimension* dim = nullptr; + if (dimObjs.empty()) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), + QObject::tr("There is no Dimension in your selection")); + return; + } else { + dim = static_cast(dimObjs.at(0)); + } + + ReferenceVector references2d; + ReferenceVector references3d; + //TechDraw::DrawViewPart* partFeat = + TechDraw::getReferencesFromSelection(references2d, references3d); + + Gui::Control().showDialog(new TaskDlgDimReference(dim, references2d, references3d)); +} + +bool CmdTechDrawDimensionRepair::isActive(void) +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this); + bool taskInProgress = false; + if (havePage) { + taskInProgress = Gui::Control().activeDialog(); + } + return (havePage && haveView && !taskInProgress); +} + +//NOTE: to be deprecated. revisions to the basic dimension allows it to handle +//everything that the Landmark Dimension was created to handle. //=========================================================================== // TechDraw_LandmarkDimension //=========================================================================== @@ -1334,7 +1550,6 @@ void CmdTechDrawLandmarkDimension::activated(int iMsg) openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); doCommand(Doc, "App.activeDocument().addObject('TechDraw::LandmarkDimension', '%s')", FeatName.c_str()); - doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); if (objects.size() == 2) { //what about distanceX and distanceY?? doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), "Distance"); @@ -1357,6 +1572,7 @@ void CmdTechDrawLandmarkDimension::activated(int iMsg) } dim->References2D.setValues(refs2d, subs); dim->References3D.setValues(objects, subs); + doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); commitCommand(); dim->recomputeFeature(); @@ -1373,18 +1589,21 @@ bool CmdTechDrawLandmarkDimension::isActive() return (havePage && haveView); } - //------------------------------------------------------------------------------ + void CreateTechDrawCommandsDims() { Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager(); + rcCmdMgr.addCommand(new CmdTechDrawRadialGroup()); rcCmdMgr.addCommand(new CmdTechDrawDimension()); rcCmdMgr.addCommand(new CmdTechDrawRadiusDimension()); rcCmdMgr.addCommand(new CmdTechDrawDiameterDimension()); + rcCmdMgr.addCommand(new CmdTechDrawLinearGroup()); rcCmdMgr.addCommand(new CmdTechDrawLengthDimension()); rcCmdMgr.addCommand(new CmdTechDrawHorizontalDimension()); rcCmdMgr.addCommand(new CmdTechDrawVerticalDimension()); + rcCmdMgr.addCommand(new CmdTechDrawAngularGroup()); rcCmdMgr.addCommand(new CmdTechDrawAngleDimension()); rcCmdMgr.addCommand(new CmdTechDraw3PtAngleDimension()); rcCmdMgr.addCommand(new CmdTechDrawExtentGroup()); @@ -1392,8 +1611,59 @@ void CreateTechDrawCommandsDims() rcCmdMgr.addCommand(new CmdTechDrawHorizontalExtentDimension()); rcCmdMgr.addCommand(new CmdTechDrawLinkDimension()); rcCmdMgr.addCommand(new CmdTechDrawLandmarkDimension()); + rcCmdMgr.addCommand(new CmdTechDrawDimensionRepair()); } +//------------------------------------------------------------------------------ + +//Common code to build a dimension feature +DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, + std::string dimType, + ReferenceVector references2d, + ReferenceVector references3d) +{ + TechDraw::DrawPage* page = dvp->findParentPage(); + std::string PageName = page->getNameInDocument(); + + TechDraw::DrawViewDimension *dim = nullptr; + std::string dimName = dvp->getDocument()->getUniqueObjectName("Dimension"); + + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", dimName.c_str()); + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.Type = '%s'", dimName.c_str() + ,dimType.c_str()); + std::string measureType("True"); + if (references3d.empty()) { + measureType = "Projected"; + } + Gui::Command::doCommand(Gui::Command::Doc,"App.activeDocument().%s.MeasureType = '%s'", dimName.c_str(), + measureType.c_str()); + + dim = dynamic_cast(dvp->getDocument()->getObject(dimName.c_str())); + if (!dim) { + throw Base::TypeError("CmdTechDrawNewDiameterDimension - dim not found\n"); + } + + //always have References2D, even if only for the parent DVP + dim->setReferences2d(references2d); + dim->setReferences3d(references3d); + + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), dimName.c_str()); + + Gui::Command::commitCommand(); + dim->recomputeFeature(); + return dim; +} + +//position the Dimension text on the view +void positionDimText(DrawViewDimension* dim) +{ + TechDraw::pointPair pp = dim->getLinearPoints(); + Base::Vector3d mid = (pp.first() + pp.second())/2.0; + dim->X.setValue(mid.x); + double fontSize = Preferences::dimFontSizeMM(); + dim->Y.setValue(-mid.y + 0.5 * fontSize); +} //=========================================================================== // Selection Validation Helpers //=========================================================================== @@ -1426,223 +1696,12 @@ bool _checkSelection(Gui::Command* cmd, unsigned maxObjs) { bool _checkDrawViewPart(Gui::Command* cmd) { std::vector selection = cmd->getSelection().getSelectionEx(); - auto objFeat( dynamic_cast(selection[0].getObject()) ); - if( !objFeat ) { - QMessageBox::warning( Gui::getMainWindow(), - QObject::tr("Incorrect selection"), - QObject::tr("No View of a Part in selection.") ); - return false; - } - return true; -} - -bool _checkPartFeature(Gui::Command* cmd) { - bool result = false; - std::vector selection = cmd->getSelection().getSelectionEx(); - std::vector::iterator itSel = selection.begin(); - for (; itSel != selection.end(); itSel++) { - if (itSel->isDerivedFrom(Part::Feature::getClassTypeId())) { - result = true; + for (auto& sel : selection) { + auto dvp = dynamic_cast( sel.getObject() ); + if( dvp ) { + return true; } } - if(!result) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), - QObject::tr("No Feature with Shape in selection.")); - } - return result; + return false; } -//! verify that Selection contains a valid Geometry for a single Edge Dimension -int _isValidSingleEdge(Gui::Command* cmd) { - auto selection( cmd->getSelection().getSelectionEx() ); - - auto objFeat( dynamic_cast(selection[0].getObject()) ); - if (!objFeat) - return isInvalid; - - const std::vector SubNames = selection[0].getSubNames(); - if (SubNames.size() != 1 || - TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]) != "Edge") { - return isInvalid; - } - - - //only 1 subshape selected - //the Name starts with "Edge" - int GeoId( TechDraw::DrawUtil::getIndexFromName(SubNames[0]) ); - TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(GeoId); - if (!geom) { - Base::Console().Error("Logic Error: no geometry for GeoId: %d\n", GeoId); - return isInvalid; - } - - if(geom->geomType == TechDraw::GENERIC) { - TechDraw::GenericPtr gen1 = std::static_pointer_cast(geom); - if(gen1->points.size() > 2) { //the edge is a polyline - return isInvalid; - } - Base::Vector3d line = gen1->points.at(1) - gen1->points.at(0); - if(fabs(line.y) < FLT_EPSILON ) { - return isHorizontal; - } else if(fabs(line.x) < FLT_EPSILON) { - return isVertical; - } else { - return isDiagonal; - } - } else if (geom->geomType == TechDraw::CIRCLE || - geom->geomType == TechDraw::ARCOFCIRCLE ) { - return isCircle; - } else if (geom->geomType == TechDraw::ELLIPSE || - geom->geomType == TechDraw::ARCOFELLIPSE) { - return isEllipse; - } else if (geom->geomType == TechDraw::BSPLINE) { - TechDraw::BSplinePtr spline = std::static_pointer_cast (geom); - if (spline->isCircle()) { - return isBSplineCircle; - } else { - return isBSpline; - } - } - - return isInvalid; -} - -//! verify that Selection contains valid geometries for a Vertex based Dimensions -bool _isValidVertexes(Gui::Command* cmd, int count) { - std::vector selection = cmd->getSelection().getSelectionEx(); - const std::vector SubNames = selection[0].getSubNames(); - - if(SubNames.size() != (unsigned) count) { - return false; - } - - for (auto& s: SubNames) { - if (TechDraw::DrawUtil::getGeomTypeFromName(s) != "Vertex") { - return false; - } - } - - return true; -} - -//! verify that the Selection contains valid geometries for an Edge to Edge Dimension -int _isValidEdgeToEdge(Gui::Command* cmd) { - std::vector selection = cmd->getSelection().getSelectionEx(); - - auto objFeat0( dynamic_cast(selection[0].getObject()) ); - // getObject() can return null pointer, or dynamic_cast can fail - if ( !objFeat0 ) { - Base::Console().Error("Logic error in _isValidEdgeToEdge()\n"); - return isInvalid; - } - - const std::vector SubNames = selection[0].getSubNames(); - - //there has to be 2 - if(SubNames.size() != 2) { - return isInvalid; - } - - //they both must start with "Edge" - if(TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]) != "Edge" || - TechDraw::DrawUtil::getGeomTypeFromName(SubNames[1]) != "Edge") { - return isInvalid; - } - - int GeoId0( TechDraw::DrawUtil::getIndexFromName(SubNames[0]) ); - int GeoId1( TechDraw::DrawUtil::getIndexFromName(SubNames[1]) ); - TechDraw::BaseGeomPtr geom0 = objFeat0->getGeomByIndex(GeoId0); - TechDraw::BaseGeomPtr geom1 = objFeat0->getGeomByIndex(GeoId1); - - if (!geom0 || !geom1) { // missing gometry - Base::Console().Error("Logic Error: no geometry for GeoId: %d or GeoId: %d\n", GeoId0, GeoId1); - return isInvalid; - } - - if(geom0->geomType == TechDraw::GENERIC && - geom1->geomType == TechDraw::GENERIC) { - TechDraw::GenericPtr gen0 = std::static_pointer_cast (geom0); - TechDraw::GenericPtr gen1 = std::static_pointer_cast (geom1); - if(gen0->points.size() > 2 || - gen1->points.size() > 2) { //the edge is a polyline - return isInvalid; //not supported yet - } - Base::Vector3d line0 = gen0->points.at(1) - gen0->points.at(0); - Base::Vector3d line1 = gen1->points.at(1) - gen1->points.at(0); - double xprod = fabs(line0.x * line1.y - line0.y * line1.x); - if (xprod > FLT_EPSILON) { //edges are not parallel - return isAngle; //angle or distance - } else { - return isDiagonal; //distance || line - } - } else { - return isDiagonal; //two edges, not both straight lines - } - - return isInvalid; -} - -//! verify that the Selection contains valid geometries for a Vertex to Edge Dimension -bool _isValidVertexToEdge(Gui::Command* cmd) { - std::vector selection = cmd->getSelection().getSelectionEx(); - TechDraw::DrawViewPart* objFeat0 = static_cast(selection[0].getObject()); - const std::vector SubNames = selection[0].getSubNames(); - - //there has to be 2 - if(SubNames.size() != 2) { - return false; - } - - int eId, vId; - TechDraw::BaseGeomPtr e; - TechDraw::VertexPtr v; - if (TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]) == "Edge" && - TechDraw::DrawUtil::getGeomTypeFromName(SubNames[1]) == "Vertex") { - eId = TechDraw::DrawUtil::getIndexFromName(SubNames[0]); - vId = TechDraw::DrawUtil::getIndexFromName(SubNames[1]); - } else if (TechDraw::DrawUtil::getGeomTypeFromName(SubNames[1]) == "Edge" && - TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]) == "Vertex") { - eId = TechDraw::DrawUtil::getIndexFromName(SubNames[1]); - vId = TechDraw::DrawUtil::getIndexFromName(SubNames[0]); - } else { - return false; - } - e = objFeat0->getGeomByIndex(eId); - v = objFeat0->getProjVertexByIndex(vId); - if (!e || !v) { - Base::Console().Error("Logic Error: no geometry for GeoId: %d or GeoId: %d\n", eId, vId); - return false; - } - - return true; -} - -char* _edgeTypeToText(int e) -{ - switch(e) { - case isInvalid: - return "invalid"; - case isHorizontal: - return "horizontal"; - case isVertical: - return "vertical"; - case isDiagonal: - return "diagonal"; - case isCircle: - return "circle"; - case isEllipse: - return "ellipse"; - case isBSpline: - return "bspline"; - case isBSplineCircle: - return "circular bspline"; - case isAngle: - return "angle"; - case isAngle3Pt: - return "angle3"; - default: - return "unknown"; - } -} - - diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp index b8fb683c01..53c1acb821 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp @@ -569,8 +569,8 @@ void execPosHorizChainDimension(Gui::Command* cmd) { for (auto dim : validDimension) { dim->Y.setValue(yMaster); pointPair pp = dim->getLinearPoints(); - Base::Vector3d p1 = pp.first; - Base::Vector3d p2 = pp.second; + Base::Vector3d p1 = pp.first(); + Base::Vector3d p2 = pp.second(); dim->X.setValue((p1.x + p2.x) / 2.0); } Gui::Command::commitCommand(); @@ -631,8 +631,8 @@ void execPosVertChainDimension(Gui::Command* cmd) { for (auto dim : validDimension) { dim->X.setValue(xMaster); pointPair pp = dim->getLinearPoints(); - Base::Vector3d p1 = pp.first; - Base::Vector3d p2 = pp.second; + Base::Vector3d p1 = pp.first(); + Base::Vector3d p2 = pp.second(); dim->Y.setValue((p1.y + p2.y) / -2.0 + 0.5 * fontSize); } Gui::Command::commitCommand(); @@ -692,7 +692,7 @@ void execPosObliqueChainDimension(Gui::Command* cmd) { float yMaster = validDimension[0]->Y.getValue(); Base::Vector3d pMaster(xMaster, yMaster, 0.0); pointPair pp = validDimension[0]->getLinearPoints(); - Base::Vector3d dirMaster = pp.second - pp.first; + Base::Vector3d dirMaster = pp.second() - pp.first(); dirMaster.y = -dirMaster.y; for (auto dim : validDimension) { float xDim = dim->X.getValue(); @@ -881,8 +881,8 @@ void execCascadeHorizDimension(Gui::Command* cmd) { for (auto dim : validDimension) { dim->Y.setValue(yMaster); pointPair pp = dim->getLinearPoints(); - Base::Vector3d p1 = pp.first; - Base::Vector3d p2 = pp.second; + Base::Vector3d p1 = pp.first(); + Base::Vector3d p2 = pp.second(); dim->X.setValue((p1.x + p2.x) / 2.0); yMaster = yMaster + dimDistance; } @@ -948,8 +948,8 @@ void execCascadeVertDimension(Gui::Command* cmd) { for (auto dim : validDimension) { dim->X.setValue(xMaster); pointPair pp = dim->getLinearPoints(); - Base::Vector3d p1 = pp.first; - Base::Vector3d p2 = pp.second; + Base::Vector3d p1 = pp.first(); + Base::Vector3d p2 = pp.second(); dim->Y.setValue((p1.y + p2.y) / -2.0 + 0.5 * fontSize); xMaster = xMaster + dimDistance; } @@ -1011,7 +1011,7 @@ void execCascadeObliqueDimension(Gui::Command* cmd) { float yMaster = validDimension[0]->Y.getValue(); Base::Vector3d pMaster(xMaster, yMaster, 0.0); pointPair pp = validDimension[0]->getLinearPoints(); - Base::Vector3d dirMaster = pp.second - pp.first; + Base::Vector3d dirMaster = pp.second() - pp.first(); dirMaster.y = -dirMaster.y; Base::Vector3d origin(0.0, 0.0, 0.0); Base::Vector3d ipDelta = _getTrianglePoint(pMaster, dirMaster, origin); @@ -1206,7 +1206,7 @@ void execCreateHorizChainDimension(Gui::Command* cmd) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(cmd, objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceX"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); if (n == 0) yMaster = -mid.y; @@ -1271,7 +1271,7 @@ void execCreateVertChainDimension(Gui::Command* cmd) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(cmd, objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceY"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; if (n == 0) xMaster = mid.x; dim->X.setValue(xMaster); @@ -1363,7 +1363,7 @@ void execCreateObliqueChainDimension(Gui::Command* cmd) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(cmd, objFeat, carrierVertexes[n].name, carrierVertexes[n + 1].name, "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0 + delta; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0 + delta; dim->X.setValue(mid.x); dim->Y.setValue(-mid.y + 0.5 * fontSize); } @@ -1548,7 +1548,7 @@ void execCreateHorizCoordDimension(Gui::Command* cmd) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceX"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); dim->Y.setValue(-yMaster - dimDistance * n); } @@ -1620,7 +1620,7 @@ void execCreateVertCoordDimension(Gui::Command* cmd) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceY"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(xMaster + dimDistance * n); dim->Y.setValue(-mid.y + 0.5 * fontSize); } @@ -1717,7 +1717,7 @@ void execCreateObliqueCoordDimension(Gui::Command* cmd) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(cmd, objFeat, carrierVertexes[0].name, carrierVertexes[n + 1].name, "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0 + delta * (n + 1); + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0 + delta * (n + 1); dim->X.setValue(mid.x); dim->Y.setValue(-mid.y + 0.5 * fontSize); } @@ -1905,7 +1905,7 @@ void execCreateHorizChamferDimension(Gui::Command* cmd) { if (std::signbit(allVertexes[0].point.y)) yMax = -yMax; TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); dim->Y.setValue(-yMax); float dx = allVertexes[0].point.x - allVertexes[1].point.x; @@ -1972,7 +1972,7 @@ void execCreateVertChamferDimension(Gui::Command* cmd) { if (std::signbit(allVertexes[0].point.x)) xMax = -xMax; TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(xMax); dim->Y.setValue(-mid.y); float dx = allVertexes[0].point.x - allVertexes[1].point.x; @@ -2169,7 +2169,7 @@ void CmdTechDrawExtensionCreateLengthArc::activated(int iMsg) { TechDraw::DrawViewDimension* dim; dim = _createLinDimension(this, objFeat, startName.str(), endName.str(), "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first + pp.second) / 2.0; + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); dim->Y.setValue(-mid.y); Base::Vector3d radVec1 = startPt - centerPt; diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.cpp b/src/Mod/TechDraw/Gui/DimensionValidators.cpp new file mode 100644 index 0000000000..7422b712eb --- /dev/null +++ b/src/Mod/TechDraw/Gui/DimensionValidators.cpp @@ -0,0 +1,712 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan +#include +#include +#include +#include +#endif //#ifndef _PreComp_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "DimensionValidators.h" + +using namespace TechDraw; +using DU = DrawUtil; + +TechDraw::DrawViewPart* TechDraw::getReferencesFromSelection( ReferenceVector& references2d, ReferenceVector& references3d ) +{ + TechDraw::DrawViewPart* dvp(nullptr); + TechDraw::DrawViewDimension* dim(nullptr); + std::vector selectionAll = Gui::Selection().getSelectionEx(); + for (auto& selItem : selectionAll) { + if (selItem.getObject()->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())) { + //we are probably repairing a dimension, but we will check later + dim = static_cast (selItem.getObject()); + } else if (selItem.getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) { + //this could be a 2d geometry selection or just a DrawViewPart for context in + //a 3d selection + dvp = static_cast (selItem.getObject()); + if (selItem.getSubNames().empty()) { + //there are no subNames, so we think this is a 3d case, + //and we only need to select the view. We set the reference + //subName to a null string to avoid later misunderstandings. + ReferenceEntry ref(dvp, std::string()); + references2d.push_back(ref); + } + for (auto& sub : selItem.getSubNames()) { + ReferenceEntry ref(dvp, sub); + references2d.push_back(ref); + } + } else if ( !selItem.getObject()->isDerivedFrom(TechDraw::DrawView::getClassTypeId()) ) { + //this is not a TechDraw object, so we check to see if it has 3d geometry + std::vector links; + links.push_back(selItem.getObject()); + if (!ShapeExtractor::getShapes(links).IsNull()) { + //this item has 3d geometry so we are interested + App::DocumentObject* obj3d = selItem.getObject(); + if (selItem.getSubNames().empty()) { + if (ShapeExtractor::isPointType(obj3d)) { + //a point object may not have a subName when selected, + //so we need to perform some special handling. + ReferenceEntry ref(obj3d, "Vertex1"); + references3d.push_back(ref); + continue; + } else { + //this is a whole object reference, probably for an extent dimension + ReferenceEntry ref(obj3d, std::string()); + references3d.push_back(ref); + continue; + } + } + //this is a regular reference in form obj+subelement + for (auto& sub3d : selItem.getSubNames()) { + ReferenceEntry ref(obj3d, sub3d); + references3d.push_back(ref); + } + } else { + Base::Console().Message("DV::getRefsFromSel - %s has no shape!\n", selItem.getObject()->getNameInDocument()); + } + } + } + if (dim) { + if (!dvp) { + ReferenceEntry ref(dim->getViewPart(), std::string()); + references2d.push_back(ref); + return dim->getViewPart(); + } + } + return dvp; +} + +//! verify that the proposed references contains valid geometries from a 2d DrawViewPart. +DimensionGeometryType TechDraw::validateDimSelection(ReferenceVector references, //[(dvp*, std::string),...,(dvp*, std::string)] + StringVector acceptableGeometry, //"Edge", "Vertex", etc + std::vector minimumCounts, //how many of each geometry are needed for a good dimension + std::vector acceptableDimensionGeometrys) //isVertical, isHorizontal, ... +{ + StringVector subNames; + TechDraw::DrawViewPart* dvpSave(nullptr); + for (auto& ref : references) { + TechDraw::DrawViewPart* dvp = dynamic_cast(ref.getObject()); + if ( dvp ) { + dvpSave = dvp; + //TODO: check for non-empty subname? + subNames.push_back(ref.getSubName()); + } + } + if (!dvpSave) { + //must have 1 DVP in selection + return isInvalid; + } + + if (subNames.front().empty()) { + //no geometry referenced. can not make a dim from this mess. We are being called to validate + //a selection for a 3d reference + return isViewReference; + } + + //check for invalid geometry descriptors in the subNames + std::unordered_set acceptableGeometrySet(acceptableGeometry.begin(), + acceptableGeometry.end()); + if (!TechDraw::validateSubnameList(subNames, acceptableGeometrySet)) { + //can not make a dimension from this + return isInvalid; + } + + //check for wrong number of geometry + GeomCountVector foundCounts; + GeomCountMap minimumCountMap = loadRequiredCounts(acceptableGeometry, minimumCounts); + if (!checkGeometryOccurences(subNames, minimumCountMap)) { + //too many or too few geometry descriptors. + return isInvalid; + } + + //we have a (potentially valid collection of 2d geometry + ReferenceVector valid2dReferences; + for (auto& sub : subNames) { + ReferenceEntry validEntry(dvpSave, sub); + valid2dReferences.push_back(validEntry); + } + + DimensionGeometryType foundGeometry = getGeometryConfiguration(valid2dReferences); + if (acceptableDimensionGeometrys.empty()) { + //if the list is empty, we are accepting anything + return foundGeometry; + } + for (auto& acceptable : acceptableDimensionGeometrys) { + if (foundGeometry == acceptable) { + return foundGeometry; + } + } + + return isInvalid; +} + +//! verify that the proposed references contains valid geometries from non-TechDraw objects. +DimensionGeometryType TechDraw::validateDimSelection3d(TechDraw::DrawViewPart* dvp, + ReferenceVector references, //[(dvp*, std::string),...,(dvp*, std::string)] + StringVector acceptableGeometry, //"Edge", "Vertex", etc + std::vector minimumCounts, //how many of each geometry are needed for a good dimension + std::vector acceptableDimensionGeometrys) //isVertical, isHorizontal, ... +{ +// Base::Console().Message("DV::validateDimSelection3d() - references: %d\n", references.size()); + StringVector subNames; + for (auto& ref : references) { + if (!ref.getSubName().empty()) { + subNames.push_back(ref.getSubName()); + } + } + + + //check for invalid geometry descriptors in the subNames + std::unordered_set acceptableGeometrySet(acceptableGeometry.begin(), + acceptableGeometry.end()); + if (!TechDraw::validateSubnameList(subNames, acceptableGeometrySet)) { + //can not make a dimension from this + return isInvalid; + } + + //check for wrong number of geometry + GeomCountMap minimumCountMap = loadRequiredCounts(acceptableGeometry, minimumCounts); + if (!checkGeometryOccurences(subNames, minimumCountMap)) { + //too many or too few geometry descriptors. + return isInvalid; + } + + //we have a (potentially valid collection of 3d geometry + DimensionGeometryType foundGeometry = getGeometryConfiguration3d(dvp, references); + if (acceptableDimensionGeometrys.empty()) { + //if the list is empty, we are accepting anything + return foundGeometry; + } + for (auto& acceptable : acceptableDimensionGeometrys) { + if (foundGeometry == acceptable) { + return foundGeometry; + } + } + + return isInvalid; +} +bool TechDraw::validateSubnameList(StringVector subNames, + GeometrySet acceptableGeometrySet) +{ +// Base::Console().Message("DV::validateSubNameList()\n"); + for (auto& sub : subNames) { + std::string geometryType = DrawUtil::getGeomTypeFromName(sub); + if (acceptableGeometrySet.count(geometryType) == 0) { + //this geometry type is not allowed + return false; + } + } + return true; +} + +//count how many of each "Edge", "Vertex, etc and compare totals to required minimum +bool TechDraw::checkGeometryOccurences(StringVector subNames, + GeomCountMap keyedMinimumCounts) +{ +// Base::Console().Message("DV::checkGeometryOccurences() - subNames: %d\n", subNames.size()); + //how many of each geometry descriptor are input + GeomCountMap foundCounts; + for (auto& sub : subNames) { + std::string geometryType = DrawUtil::getGeomTypeFromName(sub); + std::map::iterator it0(foundCounts.find(geometryType)); + if (it0 == foundCounts.end()) { + //already have this geometryType + it0->second++; + } else { + //first occurrence of this geometryType + foundCounts[geometryType] = 1; + } + } + + //check found geometry counts against required counts + for (auto& foundItem : foundCounts) { + std::string currentKey = foundItem.first; + int foundCount = foundItem.second; + auto itAccept = keyedMinimumCounts.find(currentKey); + if (itAccept == keyedMinimumCounts.end()) { + //not supposed to happen by this point + throw Base::IndexError("Dimension validation counts and geometry do not match"); + } + if (foundCount < keyedMinimumCounts[currentKey]) { + //not enough of this type of geom to make a good dimension - ex 1 Vertex + return false; + } + } + //we have no complaints about the input + return true; +} + +//return the first valid configuration contained in the already validated references +DimensionGeometryType TechDraw::getGeometryConfiguration(ReferenceVector valid2dReferences) +{ + DimensionGeometryType config = isValidMultiEdge(valid2dReferences); + if ( config > isInvalid) { + return config; + } + config = isValidVertexes(valid2dReferences); + if ( config > isInvalid) { + return config; + } + config = isValidSingleEdge(valid2dReferences.front()); + if ( config > isInvalid) { + return config; + } + config = isValidHybrid(valid2dReferences); + if ( config > isInvalid) { + return config; + } + + // no valid configuration found + return isInvalid; +} + +//return the first valid configuration contained in the already validated references +DimensionGeometryType TechDraw::getGeometryConfiguration3d(DrawViewPart* dvp, ReferenceVector valid3dReferences) +{ +// Base::Console().Message("DV::getGeometryConfig3d() - refs: %d\n", valid3dReferences.size()); + //first we check for whole object references + ReferenceVector wholeObjectRefs; + ReferenceVector subElementRefs; + for (auto& ref : valid3dReferences) { + if (ref.isWholeObject()) { + wholeObjectRefs.push_back(ref); + } else { + subElementRefs.push_back(ref); + } + } + if (subElementRefs.empty()) { + //only whole object references + return isMultiEdge; + } + if (!wholeObjectRefs.empty()) { + //mix of whole object and subelement refs + return isMultiEdge; //??? correct ??? + } + + //only have subelement refs + DimensionGeometryType config = isValidMultiEdge3d(dvp, valid3dReferences); + if ( config > isInvalid) { + return config; + } + config = isValidVertexes3d(dvp, valid3dReferences); + if ( config > isInvalid) { + return config; + } + config = isValidSingleEdge3d(dvp, valid3dReferences.front()); + if ( config > isInvalid) { + return config; + } + config = isValidHybrid3d(dvp, valid3dReferences); + if ( config > isInvalid) { + return config; + } + + // no valid configuration found + return isInvalid; +} + +//fill the GeomCountMap with pairs made from corresponding items in acceptableGeometry +//and minimumCounts +GeomCountMap TechDraw::loadRequiredCounts(StringVector& acceptableGeometry, + std::vector& minimumCounts) +{ + if (acceptableGeometry.size() != minimumCounts.size()) { + throw Base::IndexError("acceptableGeometry and minimum counts have different sizes."); + } + + GeomCountMap result; + int iCount = 0; + for (auto& acceptableItem : acceptableGeometry) { + result[acceptableItem] = minimumCounts.at(iCount); + iCount++; + } + return result; +} + +//! verify that Selection contains a valid Geometry for a single Edge Dimension +DimensionGeometryType TechDraw::isValidSingleEdge(ReferenceEntry ref) +{ + auto objFeat( dynamic_cast(ref.getObject()) ); + if (!objFeat) + return isInvalid; + + //the Name starts with "Edge" + std::string geomName = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomName != "Edge") { + return isInvalid; + } + + //the geometry exists (redundant?) + int GeoId( TechDraw::DrawUtil::getIndexFromName(ref.getSubName()) ); + TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(GeoId); + if (!geom) { + return isInvalid; + } + + if(geom->geomType == TechDraw::GENERIC) { + TechDraw::GenericPtr gen1 = std::static_pointer_cast(geom); + if(gen1->points.size() < 2) { + return isInvalid; + } + Base::Vector3d line = gen1->points.at(1) - gen1->points.at(0); + if(fabs(line.y) < FLT_EPSILON ) { + return TechDraw::isHorizontal; + } else if(fabs(line.x) < FLT_EPSILON) { + return TechDraw::isVertical; + } else { + return TechDraw::isDiagonal; + } + } else if (geom->geomType == TechDraw::CIRCLE || + geom->geomType == TechDraw::ARCOFCIRCLE ) { + return isCircle; + } else if (geom->geomType == TechDraw::ELLIPSE || + geom->geomType == TechDraw::ARCOFELLIPSE) { + return isEllipse; + } else if (geom->geomType == TechDraw::BSPLINE) { + TechDraw::BSplinePtr spline = std::static_pointer_cast (geom); + if (spline->isCircle()) { + return isBSplineCircle; + } else { + return isBSpline; + } + } + return isInvalid; +} + +//! verify that Selection contains a valid Geometry for a single Edge Dimension +DimensionGeometryType TechDraw::isValidSingleEdge3d(DrawViewPart *dvp, ReferenceEntry ref) +{ + (void) dvp; + //the Name starts with "Edge" + std::string geomName = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomName != "Edge") { + return isInvalid; + } + + TopoDS_Shape refShape = ref.getGeometry(); + if (refShape.IsNull() || refShape.ShapeType() != TopAbs_EDGE) { + throw Base::RuntimeError("Geometry for reference is not an edge."); + } + + TopoDS_Edge occEdge = TopoDS::Edge(refShape); + BRepAdaptor_Curve adapt(occEdge); + if (adapt.GetType() == GeomAbs_Line) { + Base::Vector3d point0 = DU::toVector3d(BRep_Tool::Pnt(TopExp::FirstVertex(occEdge))); + Base::Vector3d point1 = DU::toVector3d(BRep_Tool::Pnt(TopExp::LastVertex(occEdge))); + Base::Vector3d line = point1 - point0; + if(fabs(line.y) < FLT_EPSILON ) { + return TechDraw::isHorizontal; + } else if(fabs(line.x) < FLT_EPSILON) { + return TechDraw::isVertical; + } else if(fabs(line.z) < FLT_EPSILON) { + return TechDraw::isZLimited; + } else { + return TechDraw::isDiagonal; + } + } else if (adapt.GetType() == GeomAbs_Circle) { + return isCircle; + } else if (adapt.GetType() == GeomAbs_Ellipse) { + return isEllipse; + } else if (adapt.GetType() == GeomAbs_BSplineCurve) { + if (GeometryUtils::isCircle(occEdge)) { + return isBSplineCircle; + } else { + return isBSpline; + } + } + + return isInvalid; +} + +//! verify that the edge references can make a dimension. Currently only extent +//! dimensions support more than 2 edges +DimensionGeometryType TechDraw::isValidMultiEdge(ReferenceVector refs) +{ + //there has to be at least 2 + if(refs.size() < 2) { + return isInvalid; + } + + //must be an extent? + if (refs.size() > 2) { + return isMultiEdge; + } + + auto objFeat0( dynamic_cast(refs.at(0).getObject())); + if ( !objFeat0 ) { + //probably redundant + throw Base::RuntimeError("Logic error in isValidMultiEdge"); + } + + //they both must start with "Edge" + if(TechDraw::DrawUtil::getGeomTypeFromName(refs.at(0).getSubName()) != "Edge" || + TechDraw::DrawUtil::getGeomTypeFromName(refs.at(1).getSubName()) != "Edge") { + return isInvalid; + } + + int GeoId0( TechDraw::DrawUtil::getIndexFromName(refs.at(0).getSubName()) ); + int GeoId1( TechDraw::DrawUtil::getIndexFromName(refs.at(1).getSubName()) ); + TechDraw::BaseGeomPtr geom0 = objFeat0->getGeomByIndex(GeoId0); + TechDraw::BaseGeomPtr geom1 = objFeat0->getGeomByIndex(GeoId1); + + if(geom0->geomType == TechDraw::GENERIC && + geom1->geomType == TechDraw::GENERIC) { + TechDraw::GenericPtr gen0 = std::static_pointer_cast (geom0); + TechDraw::GenericPtr gen1 = std::static_pointer_cast (geom1); + if(gen0->points.size() > 2 || + gen1->points.size() > 2) { //the edge is a polyline + return isInvalid; //not supported yet + } + Base::Vector3d line0 = gen0->points.at(1) - gen0->points.at(0); + Base::Vector3d line1 = gen1->points.at(1) - gen1->points.at(0); + double xprod = fabs(line0.x * line1.y - line0.y * line1.x); + if (xprod > FLT_EPSILON) { //edges are not parallel + return isAngle; //angle or distance + } else { + return isDiagonal; //distance || line + } + } else { + return isDiagonal; //two edges, not both straight lines + } + + return isInvalid; +} + +//! verify that the edge references can make a dimension. Currently only extent +//! dimensions support more than 2 edges +DimensionGeometryType TechDraw::isValidMultiEdge3d(DrawViewPart *dvp, ReferenceVector refs) +{ + (void) dvp; + //there has to be at least 2 + if(refs.size() < 2) { + return isInvalid; + } + + std::vector edges; + for (auto& ref : refs) { + std::vector shapesAll = ShapeExtractor::getShapesFromObject(ref.getObject()); + if (shapesAll.empty()) { + //reference has no geometry + return isInvalid; + } + + //the Name starts with "Edge" + std::string geomName = DrawUtil::getGeomTypeFromName(ref.getSubName().c_str()); + if (geomName != "Edge") { + return isInvalid; + } + } + std::vector edgesAll; + std::vector typeAll; + for (auto& ref : refs) { + TopoDS_Shape geometry = ref.getGeometry(); + if (geometry.ShapeType() != TopAbs_EDGE) { + return isInvalid; + } + TopoDS_Edge edge = TopoDS::Edge(geometry); + BRepAdaptor_Curve adapt(edge); + if (adapt.GetType() != GeomAbs_Line) { + //not a line, so this must be an extent dim? + return isMultiEdge; + } + edgesAll.push_back(edge); + } + if (edgesAll.size() > 2) { + //must be an extent dimension of lines? + return isMultiEdge; + } else if (edgesAll.size() == 2) { + Base::Vector3d first0 = DU::toVector3d(BRep_Tool::Pnt(TopExp::FirstVertex(edgesAll.at(0)))); + Base::Vector3d last0 = DU::toVector3d(BRep_Tool::Pnt(TopExp::LastVertex(edgesAll.at(1)))); + Base::Vector3d line0 = last0 - first0; + Base::Vector3d first1 = DU::toVector3d(BRep_Tool::Pnt(TopExp::FirstVertex(edgesAll.at(0)))); + Base::Vector3d last1 = DU::toVector3d(BRep_Tool::Pnt(TopExp::LastVertex(edgesAll.at(1)))); + Base::Vector3d line1 = last1 - first1; + if (DU::fpCompare(fabs(line0.Dot(line1)), 1)) { + //lines are parallel, must be distance dim + return isDiagonal; + } else { + //lines are skew, could be angle, could be distance? + return isAngle; + } + } + + return isInvalid; +} + +//! verify that the vertex references can make a dimension +DimensionGeometryType TechDraw::isValidVertexes(ReferenceVector refs) +{ + TechDraw::DrawViewPart* dvp( dynamic_cast(refs.front().getObject()) ); + if ( !dvp ) { + //probably redundant + throw Base::RuntimeError("Logic error in isValidMultiEdge"); + } + + if (refs.size() == 2) { + //2 vertices can only make a distance dimension + TechDraw::VertexPtr v0 = dvp->getVertex(refs.at(0).getSubName()); + TechDraw::VertexPtr v1 = dvp->getVertex(refs.at(1).getSubName()); + Base::Vector3d line = v1->point() - v0->point(); + if(fabs(line.y) < FLT_EPSILON ) { + return isHorizontal; + } else if(fabs(line.x) < FLT_EPSILON) { + return isVertical; + } else { + return isDiagonal; + } + } else if (refs.size() == 3) { + //three vertices make an angle dimension + return isAngle3Pt; + } + + // did not find a valid configuration + return isInvalid; +} + +//! verify that the vertex references can make a dimension +DimensionGeometryType TechDraw::isValidVertexes3d(DrawViewPart *dvp, ReferenceVector refs) +{ +// Base::Console().Message("DV::isValidVertexes3d() - refs: %d\n", refs.size()); + (void) dvp; + if (refs.size() == 2) { + //2 vertices can only make a distance dimension + TopoDS_Shape geometry0 = refs.at(0).getGeometry(); + TopoDS_Shape geometry1 = refs.at(1).getGeometry(); + if (geometry0.IsNull() || geometry1.IsNull() || + geometry0.ShapeType() != TopAbs_VERTEX || + geometry1.ShapeType() != TopAbs_VERTEX) { + return isInvalid; + } + Base::Vector3d point0 = DU::toVector3d(BRep_Tool::Pnt(TopoDS::Vertex(geometry0))); + point0 = dvp->projectPoint(point0); + Base::Vector3d point1 = DU::toVector3d(BRep_Tool::Pnt(TopoDS::Vertex(geometry1))); + point1 = dvp->projectPoint(point1); + Base::Vector3d line = point1 - point0; + if(fabs(line.y) < FLT_EPSILON ) { + return isHorizontal; + } else if(fabs(line.x) < FLT_EPSILON) { + return isVertical; +// } else if(fabs(line.z) < FLT_EPSILON) { +// return isZLimited; + } else { + return isDiagonal; + } + } else if (refs.size() == 3) { + //three vertices make an angle dimension + //we could check here that all the geometries are Vertex + return isAngle3Pt; + } + + // did not find a valid configuration + return isInvalid; +} + +//! verify that the mixed bag (ex Vertex-Edge) of references can make a dimension +DimensionGeometryType TechDraw::isValidHybrid(ReferenceVector refs) +{ + if (refs.empty()) { + return isInvalid; + } + + int vertexCount(0); + int edgeCount(0); + for (auto& ref : refs) { + if (DU::getGeomTypeFromName(ref.getSubName()) == "Vertex") { + vertexCount++; + } + if (DU::getGeomTypeFromName(ref.getSubName()) == "Edge") { + edgeCount++; + } + } + if (vertexCount > 0 && + edgeCount > 0) { + //must be a diagonal dim? could it be isHorizontal or isVertical? + return isDiagonal; + } + + return isInvalid; +} + +//! verify that the mixed bag (ex Vertex-Edge) of references can make a dimension +DimensionGeometryType TechDraw::isValidHybrid3d(DrawViewPart *dvp, ReferenceVector refs) +{ + (void) dvp; + //we don't have a special check for 3d in this case + return isValidHybrid(refs); +} + +//handle situations where revised geometry type is valid but not suitable for existing dimType +long int TechDraw::mapGeometryTypeToDimType(long int dimType, + DimensionGeometryType geometry2d, + DimensionGeometryType geometry3d) +{ + if (geometry2d == isInvalid && geometry3d == isInvalid) { + //probably an error, but we can't do anything with this + return dimType; + } + + if (geometry2d == isViewReference && geometry3d != isInvalid) { + switch (geometry3d) { + case isDiagonal: + return DrawViewDimension::Distance; + case isHorizontal: + return DrawViewDimension::DistanceX; + case isVertical: + return DrawViewDimension::DistanceY; + case isAngle: + return DrawViewDimension::Angle; + case isAngle3Pt: + return DrawViewDimension::Angle3Pt; + default: + return dimType; + } + } else if (geometry2d != isViewReference) { + switch (geometry2d) { + case isDiagonal: + return DrawViewDimension::Distance; + case isHorizontal: + return DrawViewDimension::DistanceX; + case isVertical: + return DrawViewDimension::DistanceY; + case isAngle: + return DrawViewDimension::Angle; + case isAngle3Pt: + return DrawViewDimension::Angle3Pt; + default: + return dimType; + } + } + return dimType; +} diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.h b/src/Mod/TechDraw/Gui/DimensionValidators.h new file mode 100644 index 0000000000..a57b53eefc --- /dev/null +++ b/src/Mod/TechDraw/Gui/DimensionValidators.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan + +#include + +namespace App +{ +class Document; +class DocumentObject; +} + +using StringVector = std::vector; +using GeomCount = std::pair; //geometry descriptor ("Edge") + counter pair +using GeomCountVector = std::vector; +using GeomCountMap = std::map; +using GeometrySet = std::unordered_set; //queryable unique set of geometrty descriptors + +using DimensionGeometryType = int; + +namespace TechDraw +{ + +class DrawViewPart; + +enum DimensionGeometryEnum { + isInvalid, + isHorizontal, + isVertical, + isDiagonal, + isCircle, + isEllipse, + isBSplineCircle, + isBSpline, + isAngle, + isAngle3Pt, + isMultiEdge, + isZLimited, + isViewReference //never needs to be specified in the acceptable list + }; + + TechDraw::DrawViewPart* TechDrawExport getReferencesFromSelection( ReferenceVector& references2d, ReferenceVector& references3d ); + DimensionGeometryType TechDrawExport validateDimSelection(ReferenceVector references, + StringVector acceptableGeometry, //"Edge", "Vertex", etc + std::vector minimumCounts, //how many of each geometry are needed for a good dimension + std::vector acceptableDimensionGeometrys); //isVertical, isHorizontal, ... + DimensionGeometryType TechDrawExport validateDimSelection3d(TechDraw::DrawViewPart* dvp, + ReferenceVector references, + StringVector acceptableGeometry, //"Edge", "Vertex", etc + std::vector minimumCounts, //how many of each geometry are needed for a good dimension + std::vector acceptableDimensionGeometrys); //isVertical, isHorizontal, ... + + bool TechDrawExport validateSubnameList(StringVector subNames, + GeometrySet acceptableGeometrySet); + + DimensionGeometryType TechDrawExport getGeometryConfiguration(TechDraw::ReferenceVector valid2dReferences); + DimensionGeometryType TechDrawExport getGeometryConfiguration3d(TechDraw::DrawViewPart* dvp, + TechDraw::ReferenceVector valid3dReferences); + + GeomCountMap TechDrawExport loadRequiredCounts(StringVector& acceptableGeometry, + std::vector& minimumCouts); + bool TechDrawExport checkGeometryOccurences(StringVector subNames, + GeomCountMap keyedMinimumCounts); + + DimensionGeometryType TechDrawExport isValidVertexes(TechDraw::ReferenceVector refs); + DimensionGeometryType TechDrawExport isValidMultiEdge(TechDraw::ReferenceVector refs); + DimensionGeometryType TechDrawExport isValidSingleEdge(TechDraw::ReferenceEntry ref); + DimensionGeometryType TechDrawExport isValidHybrid(TechDraw::ReferenceVector refs); + + DimensionGeometryType TechDrawExport isValidVertexes3d(TechDraw::DrawViewPart* dvp, TechDraw::ReferenceVector refs); + DimensionGeometryType TechDrawExport isValidMultiEdge3d(TechDraw::DrawViewPart* dvp, TechDraw::ReferenceVector refs); + DimensionGeometryType TechDrawExport isValidSingleEdge3d(TechDraw::DrawViewPart* dvp, TechDraw::ReferenceEntry ref); + DimensionGeometryType TechDrawExport isValidHybrid3d(TechDraw::DrawViewPart* dvp, TechDraw::ReferenceVector refs); + + long int TechDrawExport mapGeometryTypeToDimType(long int dimType, + DimensionGeometryType geometry2d, + DimensionGeometryType geometry3d); +} +#endif //TECHDRAW_DIMENSIONVALIDATORS_H + diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index bf897a11c6..c91b9aaecd 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -2097,7 +2097,7 @@ void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension, lineAngle = M_PI_2; } else { - lineAngle = (fromQtApp(linePoints.second) - fromQtApp(linePoints.first)).Angle(); + lineAngle = (fromQtApp(linePoints.second()) - fromQtApp(linePoints.first())).Angle(); } int standardStyle = viewProvider->StandardAndStyle.getValue(); @@ -2106,14 +2106,14 @@ void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension, if (dimension->AngleOverride.getValue()) { - drawDistanceOverride(fromQtApp(linePoints.first), fromQtApp(linePoints.second), + drawDistanceOverride(fromQtApp(linePoints.first()), fromQtApp(linePoints.second()), dimension->LineAngle.getValue() * M_PI / 180.0, labelRectangle, standardStyle, renderExtent, flipArrows, dimension->ExtensionAngle.getValue() * M_PI / 180.0); } else { - drawDistanceExecutive(fromQtApp(linePoints.first), fromQtApp(linePoints.second), lineAngle, - labelRectangle, standardStyle, renderExtent, flipArrows); + drawDistanceExecutive(fromQtApp(linePoints.first()), fromQtApp(linePoints.second()), + lineAngle, labelRectangle, standardStyle, renderExtent, flipArrows); } } @@ -2127,10 +2127,12 @@ void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension* dimension, double endAngle; double startRotation; if (curvePoints.isArc) { - endAngle = (fromQtApp(curvePoints.arcEnds.second) - fromQtApp(curvePoints.center)).Angle(); + endAngle = + (fromQtApp(curvePoints.arcEnds.second()) - fromQtApp(curvePoints.center)).Angle(); startRotation = - (fromQtApp(curvePoints.arcEnds.first) - fromQtApp(curvePoints.center)).Angle() + (fromQtApp(curvePoints.arcEnds.first()) - fromQtApp(curvePoints.center)).Angle() - endAngle; + if (startRotation != 0.0 && ((startRotation > 0.0) != curvePoints.arcCW)) { startRotation += curvePoints.arcCW ? +M_2PI : -M_2PI; } @@ -2323,9 +2325,9 @@ void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension, anglePoints anglePoints = dimension->getAnglePoints(); - Base::Vector2d angleVertex = fromQtApp(anglePoints.vertex); - Base::Vector2d startPoint = fromQtApp(anglePoints.ends.first); - Base::Vector2d endPoint = fromQtApp(anglePoints.ends.second); + Base::Vector2d angleVertex = fromQtApp(anglePoints.vertex()); + Base::Vector2d startPoint = fromQtApp(anglePoints.first()); + Base::Vector2d endPoint = fromQtApp(anglePoints.second()); double endAngle = (endPoint - angleVertex).Angle(); double startAngle = (startPoint - angleVertex).Angle(); diff --git a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc index b9cb348ca2..9920f6eec1 100644 --- a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc +++ b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc @@ -83,6 +83,7 @@ icons/TechDraw_Balloon.svg icons/TechDraw_DiameterDimension.svg icons/TechDraw_Dimension.svg + icons/TechDraw_DimensionRepair.svg icons/TechDraw_ExtensionAreaAnnotation.svg icons/TechDraw_ExtensionCascadeHorizDimension.svg icons/TechDraw_ExtensionCascadeObliqueDimension.svg diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_DimensionRepair.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_DimensionRepair.svg new file mode 100644 index 0000000000..6770399ff2 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_DimensionRepair.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [WandererFan] + + + TechDraw_Dimension + 2016-04-27 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + arrow + double arrow + diagonal + + + Double arrow at an angle between two diagonal lines + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/TaskDimRepair.cpp b/src/Mod/TechDraw/Gui/TaskDimRepair.cpp new file mode 100644 index 0000000000..e01ea5e785 --- /dev/null +++ b/src/Mod/TechDraw/Gui/TaskDimRepair.cpp @@ -0,0 +1,346 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan * + * * + * 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" + +#include +#include + +#ifndef _PreComp_ +#include +#endif // #ifndef _PreComp_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "PreferencesGui.h" +#include "DimensionValidators.h" +#include "TaskDimRepair.h" +#include + +using namespace Gui; +using namespace TechDraw; +using namespace TechDrawGui; + +TaskDimRepair::TaskDimRepair(TechDraw::DrawViewDimension* inDvd, + ReferenceVector references2d, + ReferenceVector references3d) : + ui(new Ui_TaskDimRepair), + m_dim(inDvd), + m_references2d(references2d), + m_references3d(references3d) +{ + ui->setupUi(this); + + connect(ui->pbSelection, SIGNAL(clicked(bool)), this, SLOT(slotUseSelection())); + + saveDimState(); + setUiPrimary(); +} + +TaskDimRepair::~TaskDimRepair() +{ +} + +void TaskDimRepair::setUiPrimary() +{ + setWindowTitle(QObject::tr("Dimension Repair")); + ui->leName->setReadOnly(true); + ui->leLabel->setReadOnly(true); + + ui->leName->setText(Base::Tools::fromStdString(m_dim->getNameInDocument())); + ui->leLabel->setText(Base::Tools::fromStdString(m_dim->Label.getValue())); + + std::string objName = m_dim->getViewPart()->getNameInDocument(); + std::string objLabel = m_dim->getViewPart()->Label.getValue(); + ui->leObject2d->setText(Base::Tools::fromStdString(objName + " / " + objLabel)); + const std::vector& subElements2d = m_dim->References2D.getSubValues(); + std::vector noLabels(subElements2d.size()); + fillList(ui->lwGeometry2d, subElements2d, noLabels); + + const std::vector& objs3d = m_dim->References3D.getValues(); + QStringList headers; + headers << tr("Object Name") + << tr("Object Label") + << tr("SubElement"); + ui->twReferences3d->setHorizontalHeaderLabels(headers); + + ReferenceVector references3d; + if (!m_references3d.empty()) { + references3d = m_references3d; + } else if (!objs3d.empty()) { + references3d = m_dim->getEffectiveReferences(); + } + loadTableWidget(ui->twReferences3d, references3d); +} + +void TaskDimRepair::saveDimState() +{ + m_saveMeasureType = m_dim->MeasureType.getValue(); + m_saveDimType = m_dim->Type.getValue(); + m_dimType = m_dim->Type.getValue(); + m_saveObjs3d = m_dim->References3D.getValues(); + m_saveSubs3d = m_dim->References3D.getSubValues(); + m_saveDvp = static_cast(m_dim->References2D.getValues().front()); + m_saveSubs2d = m_dim->References2D.getSubValues(); +} + +//restore the start conditions +void TaskDimRepair::restoreDimState() +{ +// Base::Console().Message("TDR::restoreDimState()\n"); + if (m_dim != nullptr) { + std::vector objs2d(m_saveSubs2d.size()); + std::iota(objs2d.begin(), objs2d.end(), m_saveDvp); + m_dim->References2D.setValues(objs2d, m_saveSubs2d); + m_dim->References3D.setValues(m_saveObjs3d, m_saveSubs3d); + } +} + +//similar to code in CommandCreateDims.cpp +//use the current selection to replace the references in dim +void TaskDimRepair::slotUseSelection() +{ + const std::vector dimObjects = Gui::Selection().getObjectsOfType(TechDraw::DrawViewDimension::getClassTypeId()); + if (dimObjects.empty()) { + //selection does not include a dimension, so we need to add our dimension to keep the + //validators happy + //bool accepted = + static_cast (Gui::Selection().addSelection(m_dim->getDocument()->getName(), + m_dim->getNameInDocument())); + } + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* dvp = TechDraw::getReferencesFromSelection(references2d, references3d); + if (dvp != m_saveDvp) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not use references from a different View")); + return; + } + + StringVector acceptableGeometry( { "Edge", "Vertex" } ); + std::vector minimumCounts( { 1, 1 } ); + std::vector acceptableDimensionGeometrys; //accept anything + DimensionGeometryType geometryRefs2d = validateDimSelection(references2d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if (geometryRefs2d == isInvalid) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make a dimension from selection")); + return; + } + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d(isInvalid); + if (geometryRefs2d == TechDraw::isViewReference && + !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(dvp, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if ( geometryRefs3d == isInvalid) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make dimension from selection")); + return; + } + } + + m_dimType = mapGeometryTypeToDimType(m_dim->Type.getValue(), + geometryRefs2d, + geometryRefs3d); + m_references2d = references2d; + if (references3d.empty()) { + m_references3d.clear(); + } else { + m_references3d = references3d; + } + updateUi(); +} + +void TaskDimRepair::updateUi() +{ +// Base::Console().Message("TDR::updateUi()\n"); + std::string objName = m_dim->getViewPart()->getNameInDocument(); + std::string objLabel = m_dim->getViewPart()->Label.getValue(); + ui->leObject2d->setText(Base::Tools::fromStdString(objName + " / " + objLabel)); + + std::vector subElements2d; + for (auto& ref : m_references2d) { + subElements2d.push_back(ref.getSubName()); + } + std::vector noLabels(subElements2d.size()); + fillList(ui->lwGeometry2d, subElements2d, noLabels); + loadTableWidget(ui->twReferences3d, m_references3d); +} + +void TaskDimRepair::loadTableWidget(QTableWidget* tw, ReferenceVector refs) +{ +// Base::Console().Message("TDR::loadTableWidget() - refs: %d\n", refs.size()); + tw->clearContents(); + tw->setRowCount(refs.size() + 1); + size_t iRow = 0; + for (auto& ref : refs) { + QString qName = Base::Tools::fromStdString(ref.getObject()->getNameInDocument()); + QTableWidgetItem* itemName = new QTableWidgetItem(qName); + itemName->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + tw->setItem(iRow,0, itemName); + QString qLabel = Base::Tools::fromStdString(std::string(ref.getObject()->Label.getValue())); + QTableWidgetItem* itemLabel = new QTableWidgetItem(qLabel); + itemLabel->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + tw->setItem(iRow,1, itemLabel); + QString qSubName = Base::Tools::fromStdString(ref.getSubName()); + QTableWidgetItem* itemSubName = new QTableWidgetItem(qSubName); + itemSubName->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + tw->setItem(iRow,2, itemSubName); + iRow++; + } +} + +void TaskDimRepair::fillList(QListWidget* lwItems, std::vector labels, std::vector names) +{ + QListWidgetItem* item; + QString qLabel; + QString qName; + QString qText; + int labelCount = labels.size(); + int i = 0; + lwItems->clear(); + for (; i < labelCount; i++) { + qLabel = Base::Tools::fromStdString(labels[i]); + qName = Base::Tools::fromStdString(names[i]); + qText = QString::fromUtf8("%1 %2").arg(qName, qLabel); + item = new QListWidgetItem(qText, lwItems); + item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + item->setData(Qt::UserRole, qName); + } +} +void TaskDimRepair::replaceReferences() +{ + if (m_dim) { + m_dim->setReferences2d(m_references2d); + m_dim->setReferences3d(m_references3d); + } +} + +void TaskDimRepair::updateTypes() +{ + if (m_references3d.empty()) { + m_dim->MeasureType.setValue("Projected"); + } else { + m_dim->MeasureType.setValue("True"); + } + m_dim->Type.setValue(m_dimType); +} + + +bool TaskDimRepair::accept() +{ +// Base::Console().Message("TDR::accept()\n"); + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + replaceReferences(); + updateTypes(); + m_dim->recomputeFeature(); + return true; +} + +bool TaskDimRepair::reject() +{ +// Base::Console().Message("TDR::reject()\n"); + restoreDimState(); + Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()"); + return false; +} + +void TaskDimRepair::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TaskDlgDimReference::TaskDlgDimReference(TechDraw::DrawViewDimension* inDvd, + ReferenceVector references2d, + ReferenceVector references3d) : + TaskDialog() +{ + widget = new TaskDimRepair(inDvd, references2d, references3d); + taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("TechDraw_DimensionRepair"), + widget->windowTitle(), true, 0); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + + +TaskDlgDimReference::~TaskDlgDimReference() +{ +} + +void TaskDlgDimReference::update() +{ + //widget->updateTask(); +} + +//==== calls from the TaskView =============================================================== +void TaskDlgDimReference::open() +{ +} + +void TaskDlgDimReference::clicked(int i) +{ + Q_UNUSED(i); +} + +bool TaskDlgDimReference::accept() +{ + widget->accept(); + return true; +} + +bool TaskDlgDimReference::reject() +{ + widget->reject(); + return true; +} + +#include diff --git a/src/Mod/TechDraw/Gui/TaskDimRepair.h b/src/Mod/TechDraw/Gui/TaskDimRepair.h new file mode 100644 index 0000000000..6b68a7c68d --- /dev/null +++ b/src/Mod/TechDraw/Gui/TaskDimRepair.h @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (c) 2022 WandererFan * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef TECHDRAW_TASKDIMREPAIR_H +#define TECHDRAW_TASKDIMREPAIR_H + +#include + +#include +#include + +#include +#include +#include + + +class Ui_TaskDimRepair; + +namespace App +{ +class DocumentObject; +} + +namespace TechDrawGui +{ + +class TaskDimRepair : public QWidget +{ + Q_OBJECT + +public: + TaskDimRepair(TechDraw::DrawViewDimension* inDvd, + TechDraw::ReferenceVector references2d, + TechDraw::ReferenceVector references3d); + ~TaskDimRepair(); + +public: + virtual bool accept(); + virtual bool reject(); + +protected Q_SLOTS: + void slotUseSelection(); + +protected: + void changeEvent(QEvent *e); + + void setUiPrimary(); + void replaceReferences(); + void updateTypes(); + void updateUi(); + void fillList(QListWidget* lwItems, std::vector labels, std::vector names); + void loadTableWidget(QTableWidget* tw, TechDraw::ReferenceVector refs); + void saveDimState(); + void restoreDimState(); + +private: + std::unique_ptr ui; + TechDraw::DrawViewDimension* m_dim; + TechDraw::ReferenceVector m_references2d; + TechDraw::ReferenceVector m_references3d; + long int m_dimType; + + long int m_saveMeasureType; + long int m_saveDimType; + TechDraw::DrawViewPart* m_saveDvp; + std::vector m_saveSubs2d; + std::vector m_saveObjs3d; + std::vector m_saveSubs3d; +}; + +class TaskDlgDimReference : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskDlgDimReference(TechDraw::DrawViewDimension* inDvd, + TechDraw::ReferenceVector references2d, + TechDraw::ReferenceVector references3d); + ~TaskDlgDimReference(); + +public: + /// is called the TaskView when the dialog is opened + virtual void open(); + /// is called by the framework if an button is clicked which has no accept or reject role + virtual void clicked(int); + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + /// is called by the framework if the dialog is rejected (Cancel) + virtual bool reject(); + /// is called by the framework if the user presses the help button + virtual void helpRequested() { return;} + virtual bool isAllowedAlterDocument(void) const + { return false; } + + void update(); + +protected: + +private: + TaskDimRepair * widget; + Gui::TaskView::TaskBox* taskbox; +}; + +} //namespace TechDrawGui + +#endif // #ifndef TECHDRAW_TASKDIMREPAIR_H diff --git a/src/Mod/TechDraw/Gui/TaskDimRepair.ui b/src/Mod/TechDraw/Gui/TaskDimRepair.ui new file mode 100644 index 0000000000..1784d1c126 --- /dev/null +++ b/src/Mod/TechDraw/Gui/TaskDimRepair.ui @@ -0,0 +1,211 @@ + + + TaskDimRepair + + + + 0 + 0 + 356 + 512 + + + + Dimension Repair + + + + + + Dimension + + + + + + + + Name + + + + + + + Label + + + + + + + true + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + true + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + Replace References with Current Selection + + + + + + + + + + + + References 2D + + + + + + + + Object + + + + + + + The View that owns this Dimension + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + 16777215 + 90 + + + + Geometry + + + + + + + + 0 + 0 + + + + + 0 + 64 + + + + + 16777215 + 90 + + + + The subelements of the View that define the geometry for this Dimension + + + QAbstractItemView::NoEditTriggers + + + true + + + QListView::Adjust + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + References 3D + + + + + + true + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + 3 + + + true + + + false + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/TaskDimension.cpp b/src/Mod/TechDraw/Gui/TaskDimension.cpp index b3de94e460..a341dcb081 100644 --- a/src/Mod/TechDraw/Gui/TaskDimension.cpp +++ b/src/Mod/TechDraw/Gui/TaskDimension.cpp @@ -327,8 +327,8 @@ void TaskDimension::onDimUseDefaultClicked() { pointPair points = m_parent->getDimFeat()->getLinearPoints(); //duplicate coordinate conversion logic from QGIViewDimension - Base::Vector2d first2(points.first.x, -points.first.y); - Base::Vector2d second2(points.second.x, -points.second.y); + Base::Vector2d first2(points.first().x, -points.first().y); + Base::Vector2d second2(points.second().x, -points.second().y); double lineAngle = (second2 - first2).Angle(); ui->dsbDimAngle->setValue(lineAngle * 180.0 / M_PI); } @@ -345,8 +345,8 @@ void TaskDimension::onExtUseDefaultClicked() { pointPair points = m_parent->getDimFeat()->getLinearPoints(); //duplicate coordinate conversion logic from QGIViewDimension - Base::Vector2d first2(points.first.x, -points.first.y); - Base::Vector2d second2(points.second.x, -points.second.y); + Base::Vector2d first2(points.first().x, -points.first().y); + Base::Vector2d second2(points.second().x, -points.second().y); Base::Vector2d lineDirection = second2 - first2; Base::Vector2d extensionDirection(-lineDirection.y, lineDirection.x); double extensionAngle = extensionDirection.Angle(); diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp index fb82514f8c..79adca445c 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp @@ -99,7 +99,8 @@ void ViewProviderDimension::attach(App::DocumentObject *pcFeat) // call parent attach method ViewProviderDrawingView::attach(pcFeat); - sPixmap = "TechDraw_Dimension"; +// sPixmap = "TechDraw_Dimension"; + setPixmapForType(); if (getViewObject()->isDerivedFrom(TechDraw::LandmarkDimension::getClassTypeId())) { sPixmap = "TechDraw_LandmarkDimension"; } @@ -141,20 +142,7 @@ bool ViewProviderDimension::setEdit(int ModNum) void ViewProviderDimension::updateData(const App::Property* prop) { if (prop == &(getViewObject()->Type)) { - if (getViewObject()->Type.isValue("DistanceX")) { - sPixmap = "TechDraw_HorizontalDimension"; - } else if (getViewObject()->Type.isValue("DistanceY")) { - sPixmap = "TechDraw_VerticalDimension"; - } else if (getViewObject()->Type.isValue("Radius")) { - sPixmap = "TechDraw_RadiusDimension"; - } else if (getViewObject()->Type.isValue("Diameter")) { - sPixmap = "TechDraw_DiameterDimension"; - } else if (getViewObject()->Type.isValue("Angle")) { - sPixmap = "TechDraw_AngleDimension"; - } else if (getViewObject()->Type.isValue("Angle3Pt")) { - sPixmap = "TechDraw_3PtAngleDimension"; - } - return; + setPixmapForType(); } //Dimension handles X, Y updates differently that other QGIView @@ -171,7 +159,8 @@ void ViewProviderDimension::updateData(const App::Property* prop) prop == &(getViewObject()->EqualTolerance) || prop == &(getViewObject()->OverTolerance) || prop == &(getViewObject()->UnderTolerance) || - prop == &(getViewObject()->Inverted) ){ + prop == &(getViewObject()->Inverted) ) { + QGIView* qgiv = getQView(); if (qgiv) { qgiv->updateView(true); @@ -183,6 +172,23 @@ void ViewProviderDimension::updateData(const App::Property* prop) Gui::ViewProviderDocumentObject::updateData(prop); } +void ViewProviderDimension::setPixmapForType() +{ + if (getViewObject()->Type.isValue("DistanceX")) { + sPixmap = "TechDraw_HorizontalDimension"; + } else if (getViewObject()->Type.isValue("DistanceY")) { + sPixmap = "TechDraw_VerticalDimension"; + } else if (getViewObject()->Type.isValue("Radius")) { + sPixmap = "TechDraw_RadiusDimension"; + } else if (getViewObject()->Type.isValue("Diameter")) { + sPixmap = "TechDraw_DiameterDimension"; + } else if (getViewObject()->Type.isValue("Angle")) { + sPixmap = "TechDraw_AngleDimension"; + } else if (getViewObject()->Type.isValue("Angle3Pt")) { + sPixmap = "TechDraw_3PtAngleDimension"; + } +} + void ViewProviderDimension::onChanged(const App::Property* p) { if ((p == &Font) || diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.h b/src/Mod/TechDraw/Gui/ViewProviderDimension.h index f583832cfe..ef3f96882f 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.h +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.h @@ -23,8 +23,8 @@ #ifndef DRAWINGGUI_VIEWPROVIDERDIMENSION_H #define DRAWINGGUI_VIEWPROVIDERDIMENSION_H - -#include + +#include #include @@ -84,6 +84,7 @@ public: double prefWeight() const; int prefStandardAndStyle() const; bool canDelete(App::DocumentObject* obj) const override; + void setPixmapForType(); protected: void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property * prop) override; diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index ed0cb7533d..2f2144aa01 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -86,6 +86,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *dimensions << "TechDraw_VerticalExtentDimension"; *dimensions << "TechDraw_LinkDimension"; *dimensions << "TechDraw_LandmarkDimension"; + *dimensions << "TechDraw_DimensionRepair"; // extension: attributes and modifications Gui::MenuItem* toolattrib = new Gui::MenuItem; @@ -269,13 +270,16 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem *dims = new Gui::ToolBarItem(root); dims->setCommand("TechDraw Dimensions"); - *dims << "TechDraw_LengthDimension"; - *dims << "TechDraw_HorizontalDimension"; - *dims << "TechDraw_VerticalDimension"; - *dims << "TechDraw_RadiusDimension"; - *dims << "TechDraw_DiameterDimension"; - *dims << "TechDraw_AngleDimension"; - *dims << "TechDraw_3PtAngleDimension"; + *dims << "TechDraw_LinearGroup"; +// *dims << "TechDraw_LengthDimension"; +// *dims << "TechDraw_HorizontalDimension"; +// *dims << "TechDraw_VerticalDimension"; + *dims << "TechDraw_RadialGroup"; +// *dims << "TechDraw_RadiusDimension"; +// *dims << "TechDraw_DiameterDimension"; + *dims << "TechDraw_AngularGroup"; +// *dims << "TechDraw_AngleDimension"; +// *dims << "TechDraw_3PtAngleDimension"; *dims << "TechDraw_ExtentGroup"; // *dims << "TechDraw_HorizontalExtentDimension"; // *dims << "TechDraw_VerticalExtentDimension"; @@ -283,6 +287,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *dims << "TechDraw_Balloon"; *dims << "TechDraw_LandmarkDimension"; // *dims << "TechDraw_Dimension" + *dims << "TechDraw_DimensionRepair"; Gui::ToolBarItem *extattribs = new Gui::ToolBarItem(root); extattribs->setCommand("TechDraw Attributes"); @@ -416,13 +421,16 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const Gui::ToolBarItem *dims = new Gui::ToolBarItem(root); dims->setCommand("TechDraw Dimensions"); - *dims << "TechDraw_LengthDimension"; - *dims << "TechDraw_HorizontalDimension"; - *dims << "TechDraw_VerticalDimension"; - *dims << "TechDraw_RadiusDimension"; - *dims << "TechDraw_DiameterDimension"; - *dims << "TechDraw_AngleDimension"; - *dims << "TechDraw_3PtAngleDimension"; + *dims << "TechDraw_LinearGroup"; +// *dims << "TechDraw_LengthDimension"; +// *dims << "TechDraw_HorizontalDimension"; +// *dims << "TechDraw_VerticalDimension"; + *dims << "TechDraw_RadialGroup"; +// *dims << "TechDraw_RadiusDimension"; +// *dims << "TechDraw_DiameterDimension"; + *dims << "TechDraw_AngularGroup"; +// *dims << "TechDraw_AngleDimension"; +// *dims << "TechDraw_3PtAngleDimension"; *dims << "TechDraw_ExtentGroup"; // *dims << "TechDraw_HorizontalExtentDimension"; // *dims << "TechDraw_VerticalExtentDimension"; @@ -430,6 +438,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const *dims << "TechDraw_Balloon"; *dims << "TechDraw_LandmarkDimension"; // *dims << "TechDraw_Dimension"; + *dims << "TechDraw_DimensionRepair"; Gui::ToolBarItem *extattribs = new Gui::ToolBarItem(root); extattribs->setCommand("TechDraw Attributes");