From ad10a01ad90c82776d893608a9ac80e27d4d638b Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 13 Sep 2023 15:54:57 +0200 Subject: [PATCH] Gui: determine the bounding box of an SoDatumLabel outside its GLRender() method --- src/Gui/SoDatumLabel.cpp | 300 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 1 deletion(-) diff --git a/src/Gui/SoDatumLabel.cpp b/src/Gui/SoDatumLabel.cpp index 86388aaf87..336b377ec7 100644 --- a/src/Gui/SoDatumLabel.cpp +++ b/src/Gui/SoDatumLabel.cpp @@ -140,7 +140,298 @@ void SoDatumLabel::drawImage() Gui::BitmapFactory().convert(image, this->image); } -void SoDatumLabel::computeBBox(SoAction * /*action*/, SbBox3f &box, SbVec3f ¢er) +namespace { +// Helper class to determine the bounding box of a datum label +class DatumLabelBox +{ +public: + DatumLabelBox(float scale, SoDatumLabel* label) + : scale{scale} + , label{label} + { + + } + void computeBBox(SbBox3f& box, SbVec3f& center) const + { + std::vector corners; + if (label->datumtype.getValue() == SoDatumLabel::DISTANCE || + label->datumtype.getValue() == SoDatumLabel::DISTANCEX || + label->datumtype.getValue() == SoDatumLabel::DISTANCEY ) { + corners = computeDistanceBBox(); + } + else if (label->datumtype.getValue() == SoDatumLabel::RADIUS || + label->datumtype.getValue() == SoDatumLabel::DIAMETER) { + corners = computeRadiusDiameterBBox(); + } + else if (label->datumtype.getValue() == SoDatumLabel::ANGLE) { + corners = computeAngleBBox(); + } + else if (label->datumtype.getValue() == SoDatumLabel::SYMMETRIC) { + corners = computeSymmetricBBox(); + } + + getBBox(corners, box, center); + } + +private: + void getBBox(const std::vector& corners, SbBox3f& box, SbVec3f& center) const + { + if (corners.size() > 1) { + float minX = FLT_MAX; + float minY = FLT_MAX; + float maxX = -FLT_MAX; + float maxY = -FLT_MAX; + for (SbVec3f it : corners) { + minX = (it[0] < minX) ? it[0] : minX; + minY = (it[1] < minY) ? it[1] : minY; + maxX = (it[0] > maxX) ? it[0] : maxX; + maxY = (it[1] > maxY) ? it[1] : maxY; + } + + // Store the bounding box + box.setBounds(SbVec3f(minX, minY, 0.0F), SbVec3f (maxX, maxY, 0.0F)); + center = box.getCenter(); + } + } + std::vector computeDistanceBBox() const + { + SbVec2s imgsize; + int nc; + int srcw = 1; + int srch = 1; + + const unsigned char * dataptr = label->image.getValue(imgsize, nc); + if (dataptr) { + srcw = imgsize[0]; + srch = imgsize[1]; + } + + float aspectRatio = (float) srcw / (float) srch; + float imgHeight = scale * (float) (srch); + float imgWidth = aspectRatio * imgHeight; + + // Get the points stored in the pnt field + const SbVec3f *points = label->pnts.getValues(0); + if (label->pnts.getNum() < 2) { + return {}; + } + + SbVec3f textOffset; + + float length = label->param1.getValue(); + float length2 = label->param2.getValue(); + + SbVec3f p1 = points[0]; + SbVec3f p2 = points[1]; + + SbVec3f dir; + SbVec3f normal; + if (label->datumtype.getValue() == SoDatumLabel::DISTANCE) { + dir = (p2-p1); + } + else if (label->datumtype.getValue() == SoDatumLabel::DISTANCEX) { + dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); + } + else if (label->datumtype.getValue() == SoDatumLabel::DISTANCEY) { + dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); + } + + dir.normalize(); + normal = SbVec3f (-dir[1], dir[0], 0); + + // when the datum line is not parallel to p1-p2 the projection of + // p1-p2 on normal is not zero, p2 is considered as reference and p1 + // is replaced by its projection p1_ + float normproj12 = (p2 - p1).dot(normal); + SbVec3f p1_ = p1 + normproj12 * normal; + + SbVec3f midpos = (p1_ + p2)/2; + + float offset1 = ((length + normproj12 < 0.0F) ? -1.0F : 1.0F) * float(srch); + float offset2 = ((length < 0.0F) ? -1.0F : 1.0F) * float(srch); + + textOffset = midpos + normal * length + dir * length2; + float margin = imgHeight / 4.0F; + + SbVec3f perp1 = p1_ + normal * (length + offset1 * scale); + SbVec3f perp2 = p2 + normal * (length + offset2 * scale); + + // Finds the mins and maxes + std::vector corners; + corners.push_back(p1); + corners.push_back(p2); + corners.push_back(perp1); + corners.push_back(perp2); + + // Make sure that the label is inside the bounding box + corners.push_back(textOffset + dir * (imgWidth / 2.0F + margin) + normal * (srch + margin)); + corners.push_back(textOffset - dir * (imgWidth / 2.0F + margin) + normal * (srch + margin)); + corners.push_back(textOffset + dir * (imgWidth / 2.0F + margin) - normal * margin); + corners.push_back(textOffset - dir * (imgWidth / 2.0F + margin) - normal * margin); + + return corners; + } + + std::vector computeRadiusDiameterBBox() const + { + SbVec2s imgsize; + int nc; + int srcw = 1; + int srch = 1; + + const unsigned char * dataptr = label->image.getValue(imgsize, nc); + if (dataptr) { + srcw = imgsize[0]; + srch = imgsize[1]; + } + + float aspectRatio = (float) srcw / (float) srch; + float imgHeight = scale * (float) (srch); + float imgWidth = aspectRatio * imgHeight; + + // Get the points stored in the pnt field + const SbVec3f *points = label->pnts.getValues(0); + if (label->pnts.getNum() < 2) { + return {}; + } + + // Get the Points + SbVec3f p1 = points[0]; + SbVec3f p2 = points[1]; + + SbVec3f dir = p2 - p1; + dir.normalize(); + SbVec3f normal (-dir[1], dir[0], 0); + + float length = label->param1.getValue(); + SbVec3f pos = p2 + length*dir; + + float margin = imgHeight / 4.0F; + + SbVec3f p3 = pos + dir * (imgWidth / 2.0F + margin); + if ((p3-p1).length() > (p2-p1).length()) { + p2 = p3; + } + + // Calculate the points + SbVec3f pnt1 = pos - dir * (margin + imgWidth / 2.0F); + SbVec3f pnt2 = pos + dir * (margin + imgWidth / 2.0F); + + // Finds the mins and maxes + std::vector corners; + corners.push_back(p1); + corners.push_back(p2); + corners.push_back(pnt1); + corners.push_back(pnt2); + + return corners; + } + + std::vector computeAngleBBox() const + { + SbVec2s imgsize; + int nc; + int srcw = 1; + int srch = 1; + + const unsigned char * dataptr = label->image.getValue(imgsize, nc); + if (dataptr) { + srcw = imgsize[0]; + srch = imgsize[1]; + } + + float aspectRatio = (float) srcw / (float) srch; + float imgHeight = scale * (float) (srch); + float imgWidth = aspectRatio * imgHeight; + + // Get the points stored in the pnt field + const SbVec3f *points = label->pnts.getValues(0); + if (label->pnts.getNum() < 1) { + return {}; + } + + // Only the angle intersection point is needed + SbVec3f p0 = points[0]; + + // Load the Parameters + float length = label->param1.getValue(); + float startangle = label->param2.getValue(); + float range = label->param3.getValue(); + float endangle = startangle + range; + + + float len2 = 2.0F * length; + + // Useful Information + // v0 - vector for text position + // p0 - vector for angle intersect + SbVec3f v0(cos(startangle+range/2), sin(startangle+range/2), 0); + + SbVec3f textOffset = p0 + v0 * len2; + + float margin = imgHeight / 4.0F; + + // Direction vectors for start and end lines + SbVec3f v1(cos(startangle), sin(startangle), 0); + SbVec3f v2(cos(endangle), sin(endangle), 0); + + SbVec3f pnt1 = p0+(len2-margin)*v1; + SbVec3f pnt2 = p0+(len2+margin)*v1; + SbVec3f pnt3 = p0+(len2-margin)*v2; + SbVec3f pnt4 = p0+(len2+margin)*v2; + + // Finds the mins and maxes + // We may need to include the text position too + + SbVec3f img1 = SbVec3f(-imgWidth / 2.0F, -imgHeight / 2, 0.0F); + SbVec3f img2 = SbVec3f(-imgWidth / 2.0F, imgHeight / 2, 0.0F); + SbVec3f img3 = SbVec3f( imgWidth / 2.0F, -imgHeight / 2, 0.0F); + SbVec3f img4 = SbVec3f( imgWidth / 2.0F, imgHeight / 2, 0.0F); + + img1 += textOffset; + img2 += textOffset; + img3 += textOffset; + img4 += textOffset; + + std::vector corners; + corners.push_back(pnt1); + corners.push_back(pnt2); + corners.push_back(pnt3); + corners.push_back(pnt4); + corners.push_back(img1); + corners.push_back(img2); + corners.push_back(img3); + corners.push_back(img4); + + return corners; + } + + std::vector computeSymmetricBBox() const + { + // Get the points stored in the pnt field + const SbVec3f *points = label->pnts.getValues(0); + if (label->pnts.getNum() < 2) { + return {}; + } + + SbVec3f p1 = points[0]; + SbVec3f p2 = points[1]; + + // Finds the mins and maxes + std::vector corners; + corners.push_back(p1); + corners.push_back(p2); + + return corners; + } + +private: + float scale; + SoDatumLabel* label; +}; +} + +void SoDatumLabel::computeBBox(SoAction * action, SbBox3f &box, SbVec3f ¢er) { if (!this->bbox.isEmpty()) { // Set the bounding box using stored parameters @@ -148,6 +439,13 @@ void SoDatumLabel::computeBBox(SoAction * /*action*/, SbBox3f &box, SbVec3f &cen SbVec3f bbcenter = this->bbox.getCenter(); center.setValue(bbcenter[0], bbcenter[1], bbcenter[2]); } + else { + SoState *state = action->getState(); + float scale = getScaleFactor(state); + + DatumLabelBox datumBox(scale, this); + datumBox.computeBBox(box, center); + } } void SoDatumLabel::generateDistancePrimitives(SoAction * action, const SbVec3f& p1, const SbVec3f& p2)