/**************************************************************************** ** Copyright (c) 2022, Fougue Ltd. ** All rights reserved. ** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt ****************************************************************************/ #include "measure_display.h" #include "../base/occ_handle.h" #include "../base/text_id.h" #include "../base/unit_system.h" #include "measure_tool.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Mayo { namespace { struct MeasureDisplayI18N { MAYO_DECLARE_TEXT_ID_FUNCTIONS(Mayo::MeasureDisplayI18N) }; } // namespace std::unique_ptr BaseMeasureDisplay::createFrom(MeasureType type, const MeasureValue& value) { switch (type) { case MeasureType::VertexPosition: return std::make_unique(std::get(value)); case MeasureType::CircleCenter: return std::make_unique(std::get(value)); case MeasureType::CircleDiameter: return std::make_unique(std::get(value)); case MeasureType::MinDistance: case MeasureType::CenterDistance: return std::make_unique(std::get(value)); case MeasureType::Length: return std::make_unique(std::get(value)); case MeasureType::Angle: return std::make_unique(std::get(value)); case MeasureType::Area: return std::make_unique(std::get(value)); case MeasureType::BoundingBox: return std::make_unique(std::get(value)); default: return {}; } } std::unique_ptr BaseMeasureDisplay::createEmptySumFrom(MeasureType type) { switch (type) { case MeasureType::Length: return std::make_unique(Mayo::MeasureLength{}); case MeasureType::Area: return std::make_unique(Mayo::MeasureArea{}); default: return {}; } } void BaseMeasureDisplay::adaptGraphics(const OccHandle& driver) { const auto openGlDriver = OccHandle::DownCast(driver); const auto openGlContext = openGlDriver ? openGlDriver->GetSharedContext() : nullptr; if (!openGlContext) return; const bool useVbo = openGlContext->ToUseVbo(); for (int i = 0; i < this->graphicsObjectsCount(); ++i) { auto gfxText = OccHandle::DownCast(this->graphicsObjectAt(i)); if (gfxText) { // NOTE // Usage of Aspect_TODT_SUBTITLE is causing a crash when VBO are not available(eg because // of too old OpenGL version) gfxText->SetDisplayType(useVbo ? Aspect_TODT_SUBTITLE : Aspect_TODT_NORMAL); gfxText->SetColor(useVbo ? Quantity_NOC_WHITE : Quantity_NOC_BLACK); gfxText->SetTransparency(useVbo ? 0.2 : 0.); } } } void BaseMeasureDisplay::sumAdd(const IMeasureDisplay& /*other*/) { ++m_sumCount; } std::string_view BaseMeasureDisplay::sumTextOr(std::string_view singleItemText) const { return m_sumCount > 1 ? MeasureDisplayI18N::textIdTr("Sum") : singleItemText; } std::string BaseMeasureDisplay::text(const gp_Pnt& pnt, const MeasureDisplayConfig& config) { auto trPntX = UnitSystem::translateLength(pnt.X() * Quantity_Millimeter, config.lengthUnit); auto trPntY = UnitSystem::translateLength(pnt.Y() * Quantity_Millimeter, config.lengthUnit); auto trPntZ = UnitSystem::translateLength(pnt.Z() * Quantity_Millimeter, config.lengthUnit); const std::string str = fmt::format( MeasureDisplayI18N::textIdTr("(X{0} " "Y{1} " "Z{2}){3}"), text(trPntX, config), text(trPntY, config), text(trPntZ, config), trPntX.strUnit ); return str; } std::string BaseMeasureDisplay::text(double value, const MeasureDisplayConfig& config) { return to_stdString(value, config.doubleToStringOptions); } std::string BaseMeasureDisplay::graphicsText(const gp_Pnt& pnt, const MeasureDisplayConfig& config) { const std::string BaseMeasureDisplay = fmt::format( MeasureDisplayI18N::textIdTr(" X{0} Y{1} Z{2}"), text(UnitSystem::translateLength(pnt.X() * Quantity_Millimeter, config.lengthUnit), config), text(UnitSystem::translateLength(pnt.Y() * Quantity_Millimeter, config.lengthUnit), config), text(UnitSystem::translateLength(pnt.Z() * Quantity_Millimeter, config.lengthUnit), config) ); return BaseMeasureDisplay; } void BaseMeasureDisplay::adaptScale(const OccHandle& gfxText, const MeasureDisplayConfig& config) { static const Prs3d_TextAspect defaultTextAspect; gfxText->SetHeight(defaultTextAspect.Height() * config.devicePixelRatio); } void BaseMeasureDisplay::applyGraphicsDefaults(IMeasureDisplay* measureDisplay) { for (int i = 0; i < measureDisplay->graphicsObjectsCount(); ++i) { auto gfxObject = measureDisplay->graphicsObjectAt(i); gfxObject->SetZLayer(Graphic3d_ZLayerId_Topmost); auto gfxText = OccHandle::DownCast(gfxObject); if (gfxText) { gfxText->SetDisplayType(Aspect_TODT_SUBTITLE); gfxText->SetColorSubTitle(Quantity_NOC_BLACK); gfxText->SetColor(Quantity_NOC_WHITE); gfxText->SetTransparency(0.2); } else { gfxObject->SetColor(Quantity_NOC_BLACK); } } } // -- // -- Vertex // -- MeasureDisplayVertex::MeasureDisplayVertex(const gp_Pnt& pnt) : m_pnt(pnt), m_gfxText(new AIS_TextLabel) { m_gfxText->SetPosition(pnt); BaseMeasureDisplay::applyGraphicsDefaults(this); } void MeasureDisplayVertex::update(const MeasureDisplayConfig& config) { this->setText(BaseMeasureDisplay::text(m_pnt, config)); m_gfxText->SetText(to_OccExtString(BaseMeasureDisplay::graphicsText(m_pnt, config))); BaseMeasureDisplay::adaptScale(m_gfxText, config); } GraphicsObjectPtr MeasureDisplayVertex::graphicsObjectAt(int i) const { return i == 0 ? m_gfxText : GraphicsObjectPtr{}; } // -- // -- CircleCenter // -- MeasureDisplayCircleCenter::MeasureDisplayCircleCenter(const MeasureCircle& circle) : m_circle(circle.value), m_gfxPoint(new AIS_Point(new Geom_CartesianPoint(m_circle.Location()))), m_gfxText(new AIS_TextLabel) { m_gfxText->SetPosition(m_circle.Location()); if (circle.isArc) m_gfxCircle = new AIS_Circle(new Geom_Circle(m_circle)); BaseMeasureDisplay::applyGraphicsDefaults(this); // BEWARE LineAspect() is created indirectly by SetColor() call in applyGraphicsDefaults() function if (m_gfxCircle) m_gfxCircle->Attributes()->LineAspect()->SetTypeOfLine(Aspect_TOL_DOT); } void MeasureDisplayCircleCenter::update(const MeasureDisplayConfig& config) { this->setText(BaseMeasureDisplay::text(m_circle.Location(), config)); m_gfxText->SetText(to_OccExtString(BaseMeasureDisplay::graphicsText(m_circle.Location(), config))); BaseMeasureDisplay::adaptScale(m_gfxText, config); } int MeasureDisplayCircleCenter::graphicsObjectsCount() const { return m_gfxCircle ? 3 : 2; } GraphicsObjectPtr MeasureDisplayCircleCenter::graphicsObjectAt(int i) const { switch (i) { case 0: return m_gfxPoint; case 1: return m_gfxText; case 2: return m_gfxCircle; } return GraphicsObjectPtr{}; } // -- // -- CircleDiameter // -- MeasureDisplayCircleDiameter::MeasureDisplayCircleDiameter(const MeasureCircle& circle) : m_circle(circle.value), m_gfxCircle(new AIS_Circle(new Geom_Circle(m_circle))), m_gfxDiameterText(new AIS_TextLabel) { const gp_Pnt otherPntAnchor = diameterOpposedPnt(circle.pntAnchor, m_circle); m_gfxDiameter = new AIS_Line(new Geom_CartesianPoint(circle.pntAnchor), new Geom_CartesianPoint(otherPntAnchor)); m_gfxDiameter->SetWidth(2.5); m_gfxDiameter->Attributes()->LineAspect()->SetTypeOfLine(Aspect_TOL_DOT); m_gfxDiameterText->SetPosition(m_circle.Location()); BaseMeasureDisplay::applyGraphicsDefaults(this); // BEWARE LineAspect() is created indirectly by SetColor() call in applyGraphicsDefaults() function m_gfxCircle->Attributes()->LineAspect()->SetTypeOfLine(Aspect_TOL_DOT); } void MeasureDisplayCircleDiameter::update(const MeasureDisplayConfig& config) { const QuantityLength diameter = 2 * m_circle.Radius() * Quantity_Millimeter; const auto trDiameter = UnitSystem::translateLength(diameter, config.lengthUnit); const auto strDiameter = BaseMeasureDisplay::text(trDiameter, config); this->setText(fmt::format( MeasureDisplayI18N::textIdTr("Diameter: {0}{1}"), strDiameter, trDiameter.strUnit )); m_gfxDiameterText->SetText(to_OccExtString(fmt::format(MeasureDisplayI18N::textIdTr(" Ø{0}"), strDiameter))); BaseMeasureDisplay::adaptScale(m_gfxDiameterText, config); } GraphicsObjectPtr MeasureDisplayCircleDiameter::graphicsObjectAt(int i) const { switch (i) { case 0: return m_gfxCircle; case 1: return m_gfxDiameter; case 2: return m_gfxDiameterText; } return {}; } gp_Pnt MeasureDisplayCircleDiameter::diameterOpposedPnt(const gp_Pnt& pntOnCircle, const gp_Circ& circ) { return pntOnCircle.Translated(2 * gp_Vec{ pntOnCircle, circ.Location() }); } // -- // -- MinDistance // -- MeasureDisplayDistance::MeasureDisplayDistance(const MeasureDistance& dist) : m_dist(dist), m_gfxLength(new AIS_Line(new Geom_CartesianPoint(dist.pnt1), new Geom_CartesianPoint(dist.pnt2))), m_gfxDistText(new AIS_TextLabel), m_gfxPnt1(new AIS_Point(new Geom_CartesianPoint(dist.pnt1))), m_gfxPnt2(new AIS_Point(new Geom_CartesianPoint(dist.pnt2))) { m_gfxLength->SetWidth(2.5); m_gfxLength->Attributes()->LineAspect()->SetTypeOfLine(Aspect_TOL_DOT); m_gfxDistText->SetPosition(dist.pnt1.Translated(gp_Vec(dist.pnt1, dist.pnt2) / 2.)); BaseMeasureDisplay::applyGraphicsDefaults(this); } void MeasureDisplayDistance::update(const MeasureDisplayConfig& config) { const auto trLength = UnitSystem::translateLength(m_dist.value, config.lengthUnit); const auto strLength = BaseMeasureDisplay::text(trLength, config); std::string distStr; switch(m_dist.type) { case DistanceType::Mininmum: distStr = "Min Distance"; break; case DistanceType::CenterToCenter: distStr = "Distance"; break; default: distStr = "Distance"; break; } distStr += ": {0}{1}
Point1: {2}
Point2: {3}"; this->setText(fmt::format( MeasureDisplayI18N::textIdTr(distStr.c_str()), strLength, trLength.strUnit, BaseMeasureDisplay::text(m_dist.pnt1, config), BaseMeasureDisplay::text(m_dist.pnt2, config) )); m_gfxDistText->SetText(to_OccExtString(" " + strLength)); BaseMeasureDisplay::adaptScale(m_gfxDistText, config); } GraphicsObjectPtr MeasureDisplayDistance::graphicsObjectAt(int i) const { switch (i) { case 0: return m_gfxLength; case 1: return m_gfxDistText; case 2: return m_gfxPnt1; case 3: return m_gfxPnt2; } return {}; } // -- // -- Angle // -- MeasureDisplayAngle::MeasureDisplayAngle(MeasureAngle angle) : m_angle(angle), m_gfxEntity1(new AIS_Line(new Geom_CartesianPoint(angle.pntCenter), new Geom_CartesianPoint(angle.pnt1))), m_gfxEntity2(new AIS_Line(new Geom_CartesianPoint(angle.pntCenter), new Geom_CartesianPoint(angle.pnt2))), m_gfxAngleText(new AIS_TextLabel) { const gp_Vec vec1(angle.pntCenter, angle.pnt1); const gp_Vec vec2(angle.pntCenter, angle.pnt2); const gp_Ax2 axCircle(angle.pntCenter, vec1.Crossed(vec2), vec1); auto geomCircle = makeOccHandle(axCircle, 0.8 * vec1.Magnitude()); const double param1 = ElCLib::Parameter(geomCircle->Circ(), angle.pnt1); const double param2 = ElCLib::Parameter(geomCircle->Circ(), angle.pnt2); m_gfxAngle = new AIS_Circle(geomCircle, param1, param2); BaseMeasureDisplay::applyGraphicsDefaults(this); m_gfxAngle->SetWidth(2); m_gfxEntity1->Attributes()->LineAspect()->SetTypeOfLine(Aspect_TOL_DOT); m_gfxEntity2->Attributes()->LineAspect()->SetTypeOfLine(Aspect_TOL_DOT); m_gfxAngleText->SetPosition(ElCLib::Value(param1 + std::abs((param2 - param1) / 2.), geomCircle->Circ())); } void MeasureDisplayAngle::update(const MeasureDisplayConfig& config) { const auto trAngle = UnitSystem::translateAngle(m_angle.value, config.angleUnit); const auto strAngle = BaseMeasureDisplay::text(trAngle, config); this->setText(fmt::format(MeasureDisplayI18N::textIdTr("Angle: {0}{1}"), strAngle, trAngle.strUnit)); m_gfxAngleText->SetText(to_OccExtString(" " + strAngle)); BaseMeasureDisplay::adaptScale(m_gfxAngleText, config); } int MeasureDisplayAngle::graphicsObjectsCount() const { return 4; } GraphicsObjectPtr MeasureDisplayAngle::graphicsObjectAt(int i) const { switch (i) { case 0: return m_gfxAngle; case 1: return m_gfxEntity1; case 2: return m_gfxEntity2; case 3: return m_gfxAngleText; } return {}; } // -- // -- Length // -- MeasureDisplayLength::MeasureDisplayLength(const MeasureLength& length) : m_length(length), m_gfxLenText(new AIS_TextLabel) { m_gfxLenText->SetPosition(length.middlePnt); BaseMeasureDisplay::applyGraphicsDefaults(this); } void MeasureDisplayLength::update(const MeasureDisplayConfig& config) { const auto trLength = UnitSystem::translateLength(m_length.value, config.lengthUnit); const auto strLength = BaseMeasureDisplay::text(trLength, config); this->setText(fmt::format( MeasureDisplayI18N::textIdTr("{0}: {1}{2}"), BaseMeasureDisplay::sumTextOr(MeasureDisplayI18N::textIdTr("Length")), strLength, trLength.strUnit )); m_gfxLenText->SetText(to_OccExtString(" " + strLength)); BaseMeasureDisplay::adaptScale(m_gfxLenText, config); } GraphicsObjectPtr MeasureDisplayLength::graphicsObjectAt(int i) const { return i == 0 ? m_gfxLenText : GraphicsObjectPtr{}; } void MeasureDisplayLength::sumAdd(const IMeasureDisplay& other) { const auto& otherLen = dynamic_cast(other); m_length.value += otherLen.m_length.value; BaseMeasureDisplay::sumAdd(other); } // -- // -- Area // -- MeasureDisplayArea::MeasureDisplayArea(const MeasureArea& area) : m_area(area), m_gfxAreaText(new AIS_TextLabel) { m_gfxAreaText->SetPosition(area.middlePnt); BaseMeasureDisplay::applyGraphicsDefaults(this); } void MeasureDisplayArea::update(const MeasureDisplayConfig& config) { const auto trArea = UnitSystem::translateArea(m_area.value, config.areaUnit); const auto strArea = BaseMeasureDisplay::text(trArea, config); this->setText(fmt::format( MeasureDisplayI18N::textIdTr("{0}: {1}{2}"), BaseMeasureDisplay::sumTextOr(MeasureDisplayI18N::textIdTr("Area")), strArea, trArea.strUnit )); m_gfxAreaText->SetText(to_OccExtString(" " + strArea)); BaseMeasureDisplay::adaptScale(m_gfxAreaText, config); } GraphicsObjectPtr MeasureDisplayArea::graphicsObjectAt(int i) const { return i == 0 ? m_gfxAreaText : GraphicsObjectPtr{}; } void MeasureDisplayArea::sumAdd(const IMeasureDisplay& other) { const auto& otherArea = dynamic_cast(other); m_area.value += otherArea.m_area.value; BaseMeasureDisplay::sumAdd(other); } // -- // -- Bounding Box // -- MeasureDisplayBoundingBox::MeasureDisplayBoundingBox(const MeasureBoundingBox& bnd) : m_bnd(bnd), m_gfxMinPoint(new AIS_Point(new Geom_CartesianPoint(bnd.cornerMin))), m_gfxMaxPoint(new AIS_Point(new Geom_CartesianPoint(bnd.cornerMax))), m_gfxXLengthText(new AIS_TextLabel), m_gfxYLengthText(new AIS_TextLabel), m_gfxZLengthText(new AIS_TextLabel) { const TopoDS_Shape shapeBox = BRepPrimAPI_MakeBox(bnd.cornerMin, bnd.cornerMax); auto aisShapeBox = new AIS_Shape(shapeBox); m_gfxBox = aisShapeBox; m_gfxBox->SetDisplayMode(AIS_Shaded); m_gfxBox->SetTransparency(0.85); m_gfxBox->Attributes()->SetFaceBoundaryDraw(true); m_gfxBox->Attributes()->SetFaceBoundaryAspect(new Prs3d_LineAspect({}, Aspect_TOL_DOT, 1.)); const double diffX = bnd.cornerMax.X() - bnd.cornerMin.X(); const double diffY = bnd.cornerMax.Y() - bnd.cornerMin.Y(); const double diffZ = bnd.cornerMax.Z() - bnd.cornerMin.Z(); m_gfxXLengthText->SetPosition(bnd.cornerMin.Translated((diffX / 2.) * gp_Vec{1, 0, 0})); m_gfxYLengthText->SetPosition(bnd.cornerMin.Translated((diffY / 2.) * gp_Vec{0, 1, 0})); m_gfxZLengthText->SetPosition(bnd.cornerMin.Translated((diffZ / 2.) * gp_Vec{0, 0, 1})); BaseMeasureDisplay::applyGraphicsDefaults(this); m_gfxBox->SetColor(Quantity_NOC_BEIGE); m_gfxBox->SetZLayer(Graphic3d_ZLayerId_Default); } void MeasureDisplayBoundingBox::update(const MeasureDisplayConfig& config) { const auto trXLength = UnitSystem::translateLength(m_bnd.xLength, config.lengthUnit); const auto trYLength = UnitSystem::translateLength(m_bnd.yLength, config.lengthUnit); const auto trZLength = UnitSystem::translateLength(m_bnd.zLength, config.lengthUnit); const auto trVolume = UnitSystem::translateVolume(m_bnd.volume, config.volumeUnit); this->setText(fmt::format( MeasureDisplayI18N::textIdTr( "Min point: {0}
Max point: {1}
" "Size: {2} x {3} x {4}{5}
" "Volume: {6}{7}" ), BaseMeasureDisplay::text(m_bnd.cornerMin, config), BaseMeasureDisplay::text(m_bnd.cornerMax, config), BaseMeasureDisplay::text(trXLength, config), BaseMeasureDisplay::text(trYLength, config), BaseMeasureDisplay::text(trZLength, config), trXLength.strUnit, BaseMeasureDisplay::text(trVolume, config), trVolume.strUnit )); m_gfxXLengthText->SetText(to_OccExtString(BaseMeasureDisplay::text(trXLength, config))); m_gfxYLengthText->SetText(to_OccExtString(BaseMeasureDisplay::text(trYLength, config))); m_gfxZLengthText->SetText(to_OccExtString(BaseMeasureDisplay::text(trZLength, config))); BaseMeasureDisplay::adaptScale(m_gfxXLengthText, config); BaseMeasureDisplay::adaptScale(m_gfxYLengthText, config); BaseMeasureDisplay::adaptScale(m_gfxZLengthText, config); } GraphicsObjectPtr MeasureDisplayBoundingBox::graphicsObjectAt(int i) const { switch (i) { case 0: return m_gfxMinPoint; case 1: return m_gfxMaxPoint; case 2: return m_gfxBox; case 3: return m_gfxXLengthText; case 4: return m_gfxYLengthText; case 5: return m_gfxZLengthText; } return {}; } } // namespace Mayo