diff --git a/src/Gui/SoDatumLabel.cpp b/src/Gui/SoDatumLabel.cpp index 616ede92f3..1cd1d21007 100644 --- a/src/Gui/SoDatumLabel.cpp +++ b/src/Gui/SoDatumLabel.cpp @@ -775,7 +775,7 @@ void SoDatumLabel::generateSymmetricPrimitives(SoAction * action, const SbVec3f& dir.normalize(); SbVec3f normal (-dir[1],dir[0],0); - float margin = this->imgHeight / 4.0; + float margin = this->imgHeight / 4.0F; // Calculate coordinates for the first arrow SbVec3f ar0 = p1 + dir * 5 * margin ; @@ -974,33 +974,14 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) return; } - float scale = getScaleFactor(state); + const float scale = getScaleFactor(state); + bool hasText = hasDatumText(); - const SbString* s = string.getValues(0); - bool hasText = (s->getLength() > 0); - - SbVec2s imgsize; - int nc {}; - int srcw=1; - int srch=1; + int srcw = 1; + int srch = 1; if (hasText) { - if (!this->glimagevalid) { - drawImage(); - this->glimagevalid = true; - } - - const unsigned char * dataptr = this->image.getValue(imgsize, nc); - if (!dataptr) { // no image - return; - } - - srcw = imgsize[0]; - srch = imgsize[1]; - - float aspectRatio = (float) srcw / (float) srch; - this->imgHeight = scale * (float) (srch); - this->imgWidth = aspectRatio * (float) this->imgHeight; + getDimension(scale, srcw, srch); } if (this->datumtype.getValue() == SYMMETRIC) { @@ -1040,602 +1021,667 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) if (this->datumtype.getValue() == DISTANCE || this->datumtype.getValue() == DISTANCEX || this->datumtype.getValue() == DISTANCEY ) { - float length = this->param1.getValue(); - float length2 = this->param2.getValue(); - - SbVec3f p1 = points[0]; - SbVec3f p2 = points[1]; - - SbVec3f dir; - if (this->datumtype.getValue() == DISTANCE) { - dir = (p2-p1); - } else if (this->datumtype.getValue() == DISTANCEX) { - dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); - } else if (this->datumtype.getValue() == DISTANCEY) { - dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); - } - - dir.normalize(); - SbVec3f 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) ? -1. : 1.) * srch; - float offset2 = ((length < 0) ? -1 : 1)*srch; - - // Get magnitude of angle between horizontal - angle = atan2f(dir[1],dir[0]); - if (angle > M_PI_2+M_PI/12) { - angle -= (float)M_PI; - } else if (angle <= -M_PI_2+M_PI/12) { - angle += (float)M_PI; - } - - textOffset = midpos + normal * length + dir * length2; - - // Get the colour - const SbColor& t = textColor.getValue(); - - // Set GL Properties - glLineWidth(this->lineWidth.getValue()); - glColor3f(t[0], t[1], t[2]); - float margin = this->imgHeight / 3.0; - - - SbVec3f perp1 = p1_ + normal * (length + offset1 * scale); - SbVec3f perp2 = p2 + normal * (length + offset2 * scale); - - // Calculate the coordinates for the parallel datum lines - SbVec3f par1 = p1_ + normal * length; - SbVec3f par2 = midpos + normal * length + dir * (length2 - this->imgWidth / 2 - margin); - SbVec3f par3 = midpos + normal * length + dir * (length2 + this->imgWidth / 2 + margin); - SbVec3f par4 = p2 + normal * length; - - bool flipTriang = false; - - if ((par3-par1).dot(dir) > (par4 - par1).length()) { - // Increase Margin to improve visibility - float tmpMargin = this->imgHeight /0.75; - par3 = par4; - if ((par2-par1).dot(dir) > (par4 - par1).length()) { - par3 = par2; - par2 = par1 - dir * tmpMargin; - flipTriang = true; - } - } - else if ((par2-par1).dot(dir) < 0.F) { - float tmpMargin = this->imgHeight /0.75; - par2 = par1; - if((par3-par1).dot(dir) < 0.F) { - par2 = par3; - par3 = par4 + dir * tmpMargin; - flipTriang = true; - } - } - // Perp Lines - glBegin(GL_LINES); - if (length != 0.) { - glVertex2f(p1[0], p1[1]); - glVertex2f(perp1[0], perp1[1]); - - glVertex2f(p2[0], p2[1]); - glVertex2f(perp2[0], perp2[1]); - } - - glVertex2f(par1[0], par1[1]); - glVertex2f(par2[0], par2[1]); - - glVertex2f(par3[0], par3[1]); - glVertex2f(par4[0], par4[1]); - glEnd(); - - float arrowWidth = margin * 0.5; - - SbVec3f ar1 = par1 + ((flipTriang) ? -1 : 1) * dir * 0.866F * 2 * margin; - SbVec3f ar2 = ar1 + normal * arrowWidth; - ar1 -= normal * arrowWidth; - - SbVec3f ar3 = par4 - ((flipTriang) ? -1 : 1) * dir * 0.866F * 2 * margin; - SbVec3f ar4 = ar3 + normal * arrowWidth; - ar3 -= normal * arrowWidth; - - // Draw the arrowheads - glBegin(GL_TRIANGLES); - glVertex2f(par1[0], par1[1]); - glVertex2f(ar1[0], ar1[1]); - glVertex2f(ar2[0], ar2[1]); - - glVertex2f(par4[0], par4[1]); - glVertex2f(ar3[0], ar3[1]); - glVertex2f(ar4[0], ar4[1]); - glEnd(); - - - if (this->datumtype.getValue() == DISTANCE) { - // Draw arc helpers if needed - float range1 = this->param4.getValue(); - if (range1 != 0.0) { - float startangle1 = this->param3.getValue(); - float radius1 = this->param5.getValue(); - SbVec3f center = points[2]; - int countSegments = std::max(6, abs(int(50.0 * range1 / (2 * M_PI)))); - double segment = range1 / (countSegments - 1); - - glBegin(GL_LINE_STRIP); - for (int i = 0; i < countSegments; i++) { - double theta = startangle1 + segment * i; - SbVec3f v1 = center + SbVec3f(radius1 * cos(theta), radius1 * sin(theta), 0); - glVertex2f(v1[0], v1[1]); - } - glEnd(); - } - float range2 = this->param7.getValue(); - if (range2 != 0.0) { - float startangle2 = this->param6.getValue(); - float radius2 = this->param8.getValue(); - SbVec3f center = points[3]; - int countSegments = std::max(6, abs(int(50.0 * range2 / (2 * M_PI)))); - double segment = range2 / (countSegments - 1); - - glBegin(GL_LINE_STRIP); - for (int i = 0; i < countSegments; i++) { - double theta = startangle2 + segment * i; - SbVec3f v1 = center + SbVec3f(radius2 * cos(theta), radius2 * sin(theta), 0); - glVertex2f(v1[0], v1[1]); - } - glEnd(); - } - } + drawDistance(points, scale, srch, angle, textOffset); } else if (this->datumtype.getValue() == RADIUS || this->datumtype.getValue() == DIAMETER) { - // Get the Points - SbVec3f p1 = points[0]; - SbVec3f p2 = points[1]; - - SbVec3f dir = (p2-p1); - SbVec3f center = p1; - double radius = (p2 - p1).length(); - if (this->datumtype.getValue() == DIAMETER) { - center = (p1 + p2) / 2; - radius = radius / 2; - } - - dir.normalize(); - SbVec3f normal (-dir[1],dir[0],0); - - float length = this->param1.getValue(); - SbVec3f pos = p2 + length*dir; - - // Get magnitude of angle between horizontal - angle = atan2f(dir[1],dir[0]); - if (angle > M_PI_2+M_PI/12) { - angle -= (float)M_PI; - } else if (angle <= -M_PI_2+M_PI/12) { - angle += (float)M_PI; - } - - textOffset = pos; - - float margin = this->imgHeight / 3.0F; - - // Create the arrowhead - float arrowWidth = margin * 0.5F; - SbVec3f ar0 = p2; - SbVec3f ar1 = p2 - dir * 0.866F * 2 * margin; - SbVec3f ar2 = ar1 + normal * arrowWidth; - ar1 -= normal * arrowWidth; - - SbVec3f p3 = pos + dir * (this->imgWidth / 2 + margin); - if ((p3-p1).length() > (p2-p1).length()) { - p2 = p3; - } - - // Calculate the points - SbVec3f pnt1 = pos - dir * (margin + this->imgWidth / 2); - SbVec3f pnt2 = pos + dir * (margin + this->imgWidth / 2); - - // Draw the Lines - glBegin(GL_LINES); - glVertex2f(p1[0], p1[1]); - glVertex2f(pnt1[0], pnt1[1]); - - glVertex2f(pnt2[0], pnt2[1]); - glVertex2f(p2[0], p2[1]); - glEnd(); - - glBegin(GL_TRIANGLES); - glVertex2f(ar0[0], ar0[1]); - glVertex2f(ar1[0], ar1[1]); - glVertex2f(ar2[0], ar2[1]); - glEnd(); - - if (this->datumtype.getValue() == DIAMETER) { - // create second arrowhead - SbVec3f ar0_1 = p1; - SbVec3f ar1_1 = p1 + dir * 0.866F * 2 * margin; - SbVec3f ar2_1 = ar1_1 + normal * arrowWidth; - ar1_1 -= normal * arrowWidth; - - glBegin(GL_TRIANGLES); - glVertex2f(ar0_1[0], ar0_1[1]); - glVertex2f(ar1_1[0], ar1_1[1]); - glVertex2f(ar2_1[0], ar2_1[1]); - glEnd(); - } - - // Draw arc helper if needed - float startangle = this->param3.getValue(); - float range = this->param4.getValue(); - if (range != 0.0) { - int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); - double segment = range / (countSegments - 1); - - glBegin(GL_LINE_STRIP); - for (int i = 0; i < countSegments; i++) { - double theta = startangle + segment * i; - SbVec3f v1 = center + SbVec3f(radius * cos(theta), radius * sin(theta), 0); - glVertex2f(v1[0], v1[1]); - } - glEnd(); - } - + drawRadiusOrDiameter(points, angle, textOffset); } else if (this->datumtype.getValue() == ANGLE) { - // Only the angle intersection point is needed - SbVec3f p0 = points[0]; - - float margin = this->imgHeight / 3.0F; - - // Load the Parameters - float length = this->param1.getValue(); - float startangle = this->param2.getValue(); - float range = this->param3.getValue(); - float endangle = startangle + range; - float endLineLength1 = std::max(this->param4.getValue(), margin); - float endLineLength2 = std::max(this->param5.getValue(), margin); - float endLineLength12 = std::max(- this->param4.getValue(), margin); - float endLineLength22 = std::max(- this->param5.getValue(), margin); - - - float r = 2*length; - - // Set the Text label angle to zero - angle = 0.F; - - // Useful Information - // v0 - vector for text position - // p0 - vector for angle intersect - SbVec3f v0(cos(startangle+range/2),sin(startangle+range/2),0); - - // leave some space for the text - if (range >= 0) { - range = std::max(0.2F*range, range - this->imgWidth/(2*r)); - } - else { - range = std::min(0.2F*range, range + this->imgWidth/(2*r)); - } - - int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); - double segment = range / (2*countSegments-2); - - textOffset = p0 + v0 * r; - - - // Draw - glBegin(GL_LINE_STRIP); - - for (int i=0; i < countSegments; i++) { - double theta = startangle + segment*i; - SbVec3f v1 = p0+SbVec3f(r*cos(theta),r*sin(theta),0); - glVertex2f(v1[0],v1[1]); - } - glEnd(); - - glBegin(GL_LINE_STRIP); - for (int i=0; i < countSegments; i++) { - double theta = endangle - segment*i; - SbVec3f v1 = p0+SbVec3f(r*cos(theta),r*sin(theta),0); - glVertex2f(v1[0],v1[1]); - } - glEnd(); - - // Direction vectors for start and end lines - SbVec3f v1(cos(startangle),sin(startangle),0); - SbVec3f v2(cos(endangle),sin(endangle),0); - - SbVec3f pnt1 = p0 + (r - endLineLength1) * v1; - SbVec3f pnt2 = p0 + (r + endLineLength12) * v1; - SbVec3f pnt3 = p0 + (r - endLineLength2) * v2; - SbVec3f pnt4 = p0 + (r + endLineLength22) * v2; - - glBegin(GL_LINES); - glVertex2f(pnt1[0],pnt1[1]); - glVertex2f(pnt2[0],pnt2[1]); - - glVertex2f(pnt3[0],pnt3[1]); - glVertex2f(pnt4[0],pnt4[1]); - glEnd(); - - // Create the arrowheads - float arrowLength = margin * 2; - float arrowWidth = margin * 0.5F; - - // Normals for the arrowheads - SbVec3f dirStart(v1[1], -v1[0], 0); - SbVec3f dirEnd(-v2[1], v2[0], 0); - - // Calculate arrowhead points for start angle - SbVec3f startArrowBase = p0 + r * v1; - SbVec3f startArrowLeft = startArrowBase - arrowLength * dirStart + arrowWidth * v1; - SbVec3f startArrowRight = startArrowBase - arrowLength * dirStart - arrowWidth * v1; - - // Calculate arrowhead points for end angle - SbVec3f endArrowBase = p0 + r * v2; - SbVec3f endArrowLeft = endArrowBase - arrowLength * dirEnd + arrowWidth * v2; - SbVec3f endArrowRight = endArrowBase - arrowLength * dirEnd - arrowWidth * v2; - - // Draw arrowheads - glBegin(GL_TRIANGLES); - // Start angle arrowhead - glVertex2f(startArrowBase[0], startArrowBase[1]); - glVertex2f(startArrowLeft[0], startArrowLeft[1]); - glVertex2f(startArrowRight[0], startArrowRight[1]); - - // End angle arrowhead - glVertex2f(endArrowBase[0], endArrowBase[1]); - glVertex2f(endArrowLeft[0], endArrowLeft[1]); - glVertex2f(endArrowRight[0], endArrowRight[1]); - glEnd(); + drawAngle(points, angle, textOffset); } else if (this->datumtype.getValue() == SYMMETRIC) { - - SbVec3f p1 = points[0]; - SbVec3f p2 = points[1]; - - SbVec3f dir = (p2-p1); - dir.normalize(); - SbVec3f normal (-dir[1],dir[0],0); - - float margin = this->imgHeight / 4.0F; - - // Calculate coordinates for the first arrow - SbVec3f ar0 = p1 + dir * 4 * margin; // Tip of Arrow - SbVec3f ar1 = ar0 - dir * 0.866F * 2 * margin; - SbVec3f ar2 = ar1 + normal * margin; - ar1 -= normal * margin; - - glBegin(GL_LINES); - glVertex3f(p1[0], p1[1], ZCONSTR); - glVertex3f(ar0[0], ar0[1], ZCONSTR); - glVertex3f(ar0[0], ar0[1], ZCONSTR); - glVertex3f(ar1[0], ar1[1], ZCONSTR); - glVertex3f(ar0[0], ar0[1], ZCONSTR); - glVertex3f(ar2[0], ar2[1], ZCONSTR); - glEnd(); - - // Calculate coordinates for the second arrow - SbVec3f ar3 = p2 - dir * 4 * margin; // Tip of 2nd Arrow - SbVec3f ar4 = ar3 + dir * 0.866F * 2 * margin; - SbVec3f ar5 = ar4 + normal * margin; - ar4 -= normal * margin; - - glBegin(GL_LINES); - glVertex3f(p2[0], p2[1], ZCONSTR); - glVertex3f(ar3[0], ar3[1], ZCONSTR); - glVertex3f(ar3[0], ar3[1], ZCONSTR); - glVertex3f(ar4[0], ar4[1], ZCONSTR); - glVertex3f(ar3[0], ar3[1], ZCONSTR); - glVertex3f(ar5[0], ar5[1], ZCONSTR); - glEnd(); + drawSymmetric(points); } else if (this->datumtype.getValue() == ARCLENGTH) { - SbVec3f ctr = points[0]; - SbVec3f p1 = points[1]; - SbVec3f p2 = points[2]; - float length = this->param1.getValue(); + drawArcLength(points, angle, textOffset); + } - float margin = this->imgHeight / 3.0F; + if (hasText) { + drawText(state, srcw, srch, angle, textOffset); + } - // Angles calculations - SbVec3f vc1 = (p1 - ctr); - SbVec3f vc2 = (p2 - ctr); + glPopAttrib(); + state->pop(); +} - float startangle = atan2f(vc1[1], vc1[0]); - float endangle = atan2f(vc2[1], vc2[0]); - if (endangle < startangle) { - endangle += 2. * M_PI; +bool SoDatumLabel::hasDatumText() const +{ + const SbString* s = string.getValues(0); + return (s->getLength() > 0); +} + +void SoDatumLabel::getDimension(float scale, int& srcw, int& srch) +{ + SbVec2s imgsize; + int nc {}; + + if (!this->glimagevalid) { + drawImage(); + this->glimagevalid = true; + } + + const unsigned char * dataptr = this->image.getValue(imgsize, nc); + if (!dataptr) { // no image + return; + } + + srcw = imgsize[0]; + srch = imgsize[1]; + + float aspectRatio = (float) srcw / (float) srch; + this->imgHeight = scale * (float) (srch); + this->imgWidth = aspectRatio * (float) this->imgHeight; +} + +void SoDatumLabel::drawDistance(const SbVec3f* points, float scale, int srch, float& angle, SbVec3f& textOffset) +{ + float length = this->param1.getValue(); + float length2 = this->param2.getValue(); + + SbVec3f p1 = points[0]; + SbVec3f p2 = points[1]; + + SbVec3f dir; + if (this->datumtype.getValue() == DISTANCE) { + dir = (p2-p1); + } else if (this->datumtype.getValue() == DISTANCEX) { + dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0); + } else if (this->datumtype.getValue() == DISTANCEY) { + dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0); + } + + dir.normalize(); + SbVec3f 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) ? -1.F : 1.F) * srch; + float offset2 = ((length < 0) ? -1 : 1)*srch; + + // Get magnitude of angle between horizontal + angle = atan2f(dir[1],dir[0]); + if (angle > M_PI_2+M_PI/12) { + angle -= (float)M_PI; + } else if (angle <= -M_PI_2+M_PI/12) { + angle += (float)M_PI; + } + + textOffset = midpos + normal * length + dir * length2; + + // Get the colour + const SbColor& t = textColor.getValue(); + + // Set GL Properties + glLineWidth(this->lineWidth.getValue()); + glColor3f(t[0], t[1], t[2]); + float margin = this->imgHeight / 3.0F; + + + SbVec3f perp1 = p1_ + normal * (length + offset1 * scale); + SbVec3f perp2 = p2 + normal * (length + offset2 * scale); + + // Calculate the coordinates for the parallel datum lines + SbVec3f par1 = p1_ + normal * length; + SbVec3f par2 = midpos + normal * length + dir * (length2 - this->imgWidth / 2 - margin); + SbVec3f par3 = midpos + normal * length + dir * (length2 + this->imgWidth / 2 + margin); + SbVec3f par4 = p2 + normal * length; + + bool flipTriang = false; + + if ((par3-par1).dot(dir) > (par4 - par1).length()) { + // Increase Margin to improve visibility + float tmpMargin = this->imgHeight /0.75F; + par3 = par4; + if ((par2-par1).dot(dir) > (par4 - par1).length()) { + par3 = par2; + par2 = par1 - dir * tmpMargin; + flipTriang = true; + } + } + else if ((par2-par1).dot(dir) < 0.F) { + float tmpMargin = this->imgHeight /0.75F; + par2 = par1; + if((par3-par1).dot(dir) < 0.F) { + par2 = par3; + par3 = par4 + dir * tmpMargin; + flipTriang = true; + } + } + // Perp Lines + glBegin(GL_LINES); + if (length != 0.) { + glVertex2f(p1[0], p1[1]); + glVertex2f(perp1[0], perp1[1]); + + glVertex2f(p2[0], p2[1]); + glVertex2f(perp2[0], perp2[1]); } - float radius = vc1.length(); + glVertex2f(par1[0], par1[1]); + glVertex2f(par2[0], par2[1]); - float range = endangle - startangle; + glVertex2f(par3[0], par3[1]); + glVertex2f(par4[0], par4[1]); + glEnd(); - //Text orientation - SbVec3f dir = (p2 - p1); - dir.normalize(); - // Get magnitude of angle between horizontal - angle = atan2f(dir[1],dir[0]); - if (angle > M_PI_2+M_PI/12) { - angle -= (float)M_PI; - } else if (angle <= -M_PI_2+M_PI/12) { - angle += (float)M_PI; + float arrowWidth = margin * 0.5F; + + SbVec3f ar1 = par1 + ((flipTriang) ? -1 : 1) * dir * 0.866F * 2 * margin; + SbVec3f ar2 = ar1 + normal * arrowWidth; + ar1 -= normal * arrowWidth; + + SbVec3f ar3 = par4 - ((flipTriang) ? -1 : 1) * dir * 0.866F * 2 * margin; + SbVec3f ar4 = ar3 + normal * arrowWidth; + ar3 -= normal * arrowWidth; + + // Draw the arrowheads + glBegin(GL_TRIANGLES); + glVertex2f(par1[0], par1[1]); + glVertex2f(ar1[0], ar1[1]); + glVertex2f(ar2[0], ar2[1]); + + glVertex2f(par4[0], par4[1]); + glVertex2f(ar3[0], ar3[1]); + glVertex2f(ar4[0], ar4[1]); + glEnd(); + + + if (this->datumtype.getValue() == DISTANCE) { + drawDistance(points); + } +} + +void SoDatumLabel::drawDistance(const SbVec3f* points) +{ + // Draw arc helpers if needed + float range1 = this->param4.getValue(); + if (range1 != 0.0) { + float startangle1 = this->param3.getValue(); + float radius1 = this->param5.getValue(); + SbVec3f center = points[2]; + int countSegments = std::max(6, abs(int(50.0 * range1 / (2 * M_PI)))); + double segment = range1 / (countSegments - 1); + + glBegin(GL_LINE_STRIP); + for (int i = 0; i < countSegments; i++) { + double theta = startangle1 + segment * i; + SbVec3f v1 = center + SbVec3f(radius1 * cos(theta), radius1 * sin(theta), 0); + glVertex2f(v1[0], v1[1]); } + glEnd(); + } + float range2 = this->param7.getValue(); + if (range2 != 0.0) { + float startangle2 = this->param6.getValue(); + float radius2 = this->param8.getValue(); + SbVec3f center = points[3]; + int countSegments = std::max(6, abs(int(50.0 * range2 / (2 * M_PI)))); + double segment = range2 / (countSegments - 1); - // Text location - SbVec3f vm = (p1+p2)/2 - ctr; - vm.normalize(); - textOffset = ctr + vm * (length + this->imgHeight); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < countSegments; i++) { + double theta = startangle2 + segment * i; + SbVec3f v1 = center + SbVec3f(radius2 * cos(theta), radius2 * sin(theta), 0); + glVertex2f(v1[0], v1[1]); + } + glEnd(); + } +} +void SoDatumLabel::drawRadiusOrDiameter(const SbVec3f* points, float& angle, SbVec3f& textOffset) +{ + // Get the Points + SbVec3f p1 = points[0]; + SbVec3f p2 = points[1]; + + SbVec3f dir = (p2-p1); + SbVec3f center = p1; + double radius = (p2 - p1).length(); + if (this->datumtype.getValue() == DIAMETER) { + center = (p1 + p2) / 2; + radius = radius / 2; + } + + dir.normalize(); + SbVec3f normal (-dir[1],dir[0],0); + + float length = this->param1.getValue(); + SbVec3f pos = p2 + length*dir; + + // Get magnitude of angle between horizontal + angle = atan2f(dir[1],dir[0]); + if (angle > M_PI_2+M_PI/12) { + angle -= (float)M_PI; + } else if (angle <= -M_PI_2+M_PI/12) { + angle += (float)M_PI; + } + + textOffset = pos; + + float margin = this->imgHeight / 3.0F; + + // Create the arrowhead + float arrowWidth = margin * 0.5F; + SbVec3f ar0 = p2; + SbVec3f ar1 = p2 - dir * 0.866F * 2 * margin; + SbVec3f ar2 = ar1 + normal * arrowWidth; + ar1 -= normal * arrowWidth; + + SbVec3f p3 = pos + dir * (this->imgWidth / 2 + margin); + if ((p3-p1).length() > (p2-p1).length()) { + p2 = p3; + } + + // Calculate the points + SbVec3f pnt1 = pos - dir * (margin + this->imgWidth / 2); + SbVec3f pnt2 = pos + dir * (margin + this->imgWidth / 2); + + // Draw the Lines + glBegin(GL_LINES); + glVertex2f(p1[0], p1[1]); + glVertex2f(pnt1[0], pnt1[1]); + + glVertex2f(pnt2[0], pnt2[1]); + glVertex2f(p2[0], p2[1]); + glEnd(); + + glBegin(GL_TRIANGLES); + glVertex2f(ar0[0], ar0[1]); + glVertex2f(ar1[0], ar1[1]); + glVertex2f(ar2[0], ar2[1]); + glEnd(); + + if (this->datumtype.getValue() == DIAMETER) { + // create second arrowhead + SbVec3f ar0_1 = p1; + SbVec3f ar1_1 = p1 + dir * 0.866F * 2 * margin; + SbVec3f ar2_1 = ar1_1 + normal * arrowWidth; + ar1_1 -= normal * arrowWidth; + + glBegin(GL_TRIANGLES); + glVertex2f(ar0_1[0], ar0_1[1]); + glVertex2f(ar1_1[0], ar1_1[1]); + glVertex2f(ar2_1[0], ar2_1[1]); + glEnd(); + } + + // Draw arc helper if needed + float startangle = this->param3.getValue(); + float range = this->param4.getValue(); + if (range != 0.0) { int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); double segment = range / (countSegments - 1); - // Draw arc glBegin(GL_LINE_STRIP); - - for (int i=0; i < countSegments; i++) { - double theta = startangle + segment*i; - SbVec3f v1 = ctr + radius * SbVec3f(cos(theta),sin(theta),0) + (length-radius) * vm; - glVertex2f(v1[0],v1[1]); + for (int i = 0; i < countSegments; i++) { + double theta = startangle + segment * i; + SbVec3f v1 = center + SbVec3f(radius * cos(theta), radius * sin(theta), 0); + glVertex2f(v1[0], v1[1]); } glEnd(); + } +} - //Draw lines - SbVec3f pnt1 = p1; - SbVec3f pnt2 = p1 + (length-radius) * vm; - SbVec3f pnt3 = p2; - SbVec3f pnt4 = p2 + (length-radius) * vm; +void SoDatumLabel::drawAngle(const SbVec3f* points, float& angle, SbVec3f& textOffset) +{ + // Only the angle intersection point is needed + SbVec3f p0 = points[0]; - glBegin(GL_LINES); + float margin = this->imgHeight / 3.0F; + + // Load the Parameters + float length = this->param1.getValue(); + float startangle = this->param2.getValue(); + float range = this->param3.getValue(); + float endangle = startangle + range; + float endLineLength1 = std::max(this->param4.getValue(), margin); + float endLineLength2 = std::max(this->param5.getValue(), margin); + float endLineLength12 = std::max(- this->param4.getValue(), margin); + float endLineLength22 = std::max(- this->param5.getValue(), margin); + + + float r = 2*length; + + // Set the Text label angle to zero + angle = 0.F; + + // Useful Information + // v0 - vector for text position + // p0 - vector for angle intersect + SbVec3f v0(cos(startangle+range/2),sin(startangle+range/2),0); + + // leave some space for the text + if (range >= 0) { + range = std::max(0.2F*range, range - this->imgWidth/(2*r)); + } + else { + range = std::min(0.2F*range, range + this->imgWidth/(2*r)); + } + + int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); + double segment = range / (2*countSegments-2); + + textOffset = p0 + v0 * r; + + + // Draw + glBegin(GL_LINE_STRIP); + + for (int i=0; i < countSegments; i++) { + double theta = startangle + segment*i; + SbVec3f v1 = p0+SbVec3f(r*cos(theta),r*sin(theta),0); + glVertex2f(v1[0],v1[1]); + } + glEnd(); + + glBegin(GL_LINE_STRIP); + for (int i=0; i < countSegments; i++) { + double theta = endangle - segment*i; + SbVec3f v1 = p0+SbVec3f(r*cos(theta),r*sin(theta),0); + glVertex2f(v1[0],v1[1]); + } + glEnd(); + + // Direction vectors for start and end lines + SbVec3f v1(cos(startangle),sin(startangle),0); + SbVec3f v2(cos(endangle),sin(endangle),0); + + SbVec3f pnt1 = p0 + (r - endLineLength1) * v1; + SbVec3f pnt2 = p0 + (r + endLineLength12) * v1; + SbVec3f pnt3 = p0 + (r - endLineLength2) * v2; + SbVec3f pnt4 = p0 + (r + endLineLength22) * v2; + + glBegin(GL_LINES); glVertex2f(pnt1[0],pnt1[1]); glVertex2f(pnt2[0],pnt2[1]); glVertex2f(pnt3[0],pnt3[1]); glVertex2f(pnt4[0],pnt4[1]); - glEnd(); + glEnd(); - // Create the arrowheads - // Direction vectors at arc start and end - SbVec3f v1(cos(startangle),sin(startangle),0); - SbVec3f v2(cos(endangle),sin(endangle),0); - float arrowLength = margin * 2; - float arrowWidth = margin * 0.5F; + // Create the arrowheads + float arrowLength = margin * 2; + float arrowWidth = margin * 0.5F; - // Normals for the arrowheads - SbVec3f dirStart(v1[1], -v1[0], 0); - SbVec3f dirEnd(-v2[1], v2[0], 0); + // Normals for the arrowheads + SbVec3f dirStart(v1[1], -v1[0], 0); + SbVec3f dirEnd(-v2[1], v2[0], 0); - // Calculate arrowhead points for start angle - SbVec3f startArrowBase = pnt2; - SbVec3f startArrowLeft = startArrowBase - arrowLength * dirStart + arrowWidth * v1; - SbVec3f startArrowRight = startArrowBase - arrowLength * dirStart - arrowWidth * v1; + // Calculate arrowhead points for start angle + SbVec3f startArrowBase = p0 + r * v1; + SbVec3f startArrowLeft = startArrowBase - arrowLength * dirStart + arrowWidth * v1; + SbVec3f startArrowRight = startArrowBase - arrowLength * dirStart - arrowWidth * v1; - // Calculate arrowhead points for end angle - SbVec3f endArrowBase = pnt4; - SbVec3f endArrowLeft = endArrowBase - arrowLength * dirEnd + arrowWidth * v2; - SbVec3f endArrowRight = endArrowBase - arrowLength * dirEnd - arrowWidth * v2; + // Calculate arrowhead points for end angle + SbVec3f endArrowBase = p0 + r * v2; + SbVec3f endArrowLeft = endArrowBase - arrowLength * dirEnd + arrowWidth * v2; + SbVec3f endArrowRight = endArrowBase - arrowLength * dirEnd - arrowWidth * v2; - // Draw arrowheads - glBegin(GL_TRIANGLES); - // Start angle arrowhead - glVertex2f(startArrowBase[0], startArrowBase[1]); - glVertex2f(startArrowLeft[0], startArrowLeft[1]); - glVertex2f(startArrowRight[0], startArrowRight[1]); + // Draw arrowheads + glBegin(GL_TRIANGLES); + // Start angle arrowhead + glVertex2f(startArrowBase[0], startArrowBase[1]); + glVertex2f(startArrowLeft[0], startArrowLeft[1]); + glVertex2f(startArrowRight[0], startArrowRight[1]); - // End angle arrowhead - glVertex2f(endArrowBase[0], endArrowBase[1]); - glVertex2f(endArrowLeft[0], endArrowLeft[1]); - glVertex2f(endArrowRight[0], endArrowRight[1]); - glEnd(); + // End angle arrowhead + glVertex2f(endArrowBase[0], endArrowBase[1]); + glVertex2f(endArrowLeft[0], endArrowLeft[1]); + glVertex2f(endArrowRight[0], endArrowRight[1]); + glEnd(); +} + +void SoDatumLabel::drawSymmetric(const SbVec3f* points) +{ + SbVec3f p1 = points[0]; + SbVec3f p2 = points[1]; + + SbVec3f dir = (p2-p1); + dir.normalize(); + SbVec3f normal (-dir[1],dir[0],0); + + float margin = this->imgHeight / 4.0F; + + // Calculate coordinates for the first arrow + SbVec3f ar0 = p1 + dir * 4 * margin; // Tip of Arrow + SbVec3f ar1 = ar0 - dir * 0.866F * 2 * margin; + SbVec3f ar2 = ar1 + normal * margin; + ar1 -= normal * margin; + + glBegin(GL_LINES); + glVertex3f(p1[0], p1[1], ZCONSTR); + glVertex3f(ar0[0], ar0[1], ZCONSTR); + glVertex3f(ar0[0], ar0[1], ZCONSTR); + glVertex3f(ar1[0], ar1[1], ZCONSTR); + glVertex3f(ar0[0], ar0[1], ZCONSTR); + glVertex3f(ar2[0], ar2[1], ZCONSTR); + glEnd(); + + // Calculate coordinates for the second arrow + SbVec3f ar3 = p2 - dir * 4 * margin; // Tip of 2nd Arrow + SbVec3f ar4 = ar3 + dir * 0.866F * 2 * margin; + SbVec3f ar5 = ar4 + normal * margin; + ar4 -= normal * margin; + + glBegin(GL_LINES); + glVertex3f(p2[0], p2[1], ZCONSTR); + glVertex3f(ar3[0], ar3[1], ZCONSTR); + glVertex3f(ar3[0], ar3[1], ZCONSTR); + glVertex3f(ar4[0], ar4[1], ZCONSTR); + glVertex3f(ar3[0], ar3[1], ZCONSTR); + glVertex3f(ar5[0], ar5[1], ZCONSTR); + glEnd(); +} + +void SoDatumLabel::drawArcLength(const SbVec3f* points, float& angle, SbVec3f& textOffset) +{ + SbVec3f ctr = points[0]; + SbVec3f p1 = points[1]; + SbVec3f p2 = points[2]; + float length = this->param1.getValue(); + + float margin = this->imgHeight / 3.0F; + + // Angles calculations + SbVec3f vc1 = (p1 - ctr); + SbVec3f vc2 = (p2 - ctr); + + float startangle = atan2f(vc1[1], vc1[0]); + float endangle = atan2f(vc2[1], vc2[0]); + if (endangle < startangle) { + endangle += 2. * M_PI; } - if (hasText) { - const unsigned char * dataptr = this->image.getValue(imgsize, nc); + float radius = vc1.length(); - //Get the camera z-direction - const SbViewVolume & vv = SoViewVolumeElement::get(state); - SbVec3f z = vv.zVector(); + float range = endangle - startangle; - bool flip = norm.getValue().dot(z) > FLT_EPSILON; - - static bool init = false; - static bool npot = false; - if (!init) { - init = true; - std::string ext = reinterpret_cast(glGetString(GL_EXTENSIONS)); // NOLINT - npot = (ext.find("GL_ARB_texture_non_power_of_two") != std::string::npos); - } - - int w = srcw; - int h = srch; - if (!npot) { - // make power of two - if ((w & (w-1)) != 0) { - int i=1; - while (i < 8) { - if ((w >> i) == 0) { - break; - } - i++; - } - w = (1 << i); - } - // make power of two - if ((h & (h-1)) != 0) { - int i=1; - while (i < 8) { - if ((h >> i) == 0) { - break; - } - i++; - } - h = (1 << i); - } - } - - glDisable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); // Enable Textures - glEnable(GL_BLEND); - - // glGenTextures/glBindTexture was commented out but it must be active, see: - // #0000971: Tracing over a background image in Sketcher: image is overwritten by first dimensional constraint text - // #0001185: Planer image changes to number graphic when a part design constraint is made after the planar image - // - // Copy the text bitmap into memory and bind - GLuint myTexture {}; - // generate a texture - glGenTextures(1, &myTexture); - glBindTexture(GL_TEXTURE_2D, myTexture); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - if (!npot) { - QImage imagedata(w, h,QImage::Format_ARGB32_Premultiplied); - imagedata.fill(0x00000000); - int sx = (w - srcw)/2; - int sy = (h - srch)/2; - glTexImage2D(GL_TEXTURE_2D, 0, nc, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)imagedata.bits()); - glTexSubImage2D(GL_TEXTURE_2D, 0, sx, sy, srcw, srch, GL_RGBA, GL_UNSIGNED_BYTE,(const GLvoid*) dataptr); - } - else { - glTexImage2D(GL_TEXTURE_2D, 0, nc, srcw, srch, 0, GL_RGBA, GL_UNSIGNED_BYTE,(const GLvoid*) dataptr); - } - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - - // Apply a rotation and translation matrix - glTranslatef(textOffset[0],textOffset[1], textOffset[2]); - glRotatef((GLfloat) angle * 180 / M_PI, 0,0,1); - glBegin(GL_QUADS); - - glColor3f(1.F, 1.F, 1.F); - - glTexCoord2f(flip ? 0.F : 1.F, 1.F); glVertex2f( -this->imgWidth / 2, this->imgHeight / 2); - glTexCoord2f(flip ? 0.F : 1.F, 0.F); glVertex2f( -this->imgWidth / 2, -this->imgHeight / 2); - glTexCoord2f(flip ? 1.F : 0.F, 0.F); glVertex2f( this->imgWidth / 2, -this->imgHeight / 2); - glTexCoord2f(flip ? 1.F : 0.F, 1.F); glVertex2f( this->imgWidth / 2, this->imgHeight / 2); - - glEnd(); - - // Reset the Mode - glPopMatrix(); - - // wmayer: see bug report below which is caused by generating but not - // deleting the texture. - // #0000721: massive memory leak when dragging an unconstrained model - glDeleteTextures(1, &myTexture); + //Text orientation + SbVec3f dir = (p2 - p1); + dir.normalize(); + // Get magnitude of angle between horizontal + angle = atan2f(dir[1],dir[0]); + if (angle > M_PI_2+M_PI/12) { + angle -= (float)M_PI; + } else if (angle <= -M_PI_2+M_PI/12) { + angle += (float)M_PI; } - glPopAttrib(); - state->pop(); + // Text location + SbVec3f vm = (p1+p2)/2 - ctr; + vm.normalize(); + textOffset = ctr + vm * (length + this->imgHeight); + + int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); + double segment = range / (countSegments - 1); + + // Draw arc + glBegin(GL_LINE_STRIP); + + for (int i=0; i < countSegments; i++) { + double theta = startangle + segment*i; + SbVec3f v1 = ctr + radius * SbVec3f(cos(theta),sin(theta),0) + (length-radius) * vm; + glVertex2f(v1[0],v1[1]); + } + glEnd(); + + //Draw lines + SbVec3f pnt1 = p1; + SbVec3f pnt2 = p1 + (length-radius) * vm; + SbVec3f pnt3 = p2; + SbVec3f pnt4 = p2 + (length-radius) * vm; + + glBegin(GL_LINES); + glVertex2f(pnt1[0],pnt1[1]); + glVertex2f(pnt2[0],pnt2[1]); + + glVertex2f(pnt3[0],pnt3[1]); + glVertex2f(pnt4[0],pnt4[1]); + glEnd(); + + // Create the arrowheads + // Direction vectors at arc start and end + SbVec3f v1(cos(startangle),sin(startangle),0); + SbVec3f v2(cos(endangle),sin(endangle),0); + float arrowLength = margin * 2; + float arrowWidth = margin * 0.5F; + + // Normals for the arrowheads + SbVec3f dirStart(v1[1], -v1[0], 0); + SbVec3f dirEnd(-v2[1], v2[0], 0); + + // Calculate arrowhead points for start angle + SbVec3f startArrowBase = pnt2; + SbVec3f startArrowLeft = startArrowBase - arrowLength * dirStart + arrowWidth * v1; + SbVec3f startArrowRight = startArrowBase - arrowLength * dirStart - arrowWidth * v1; + + // Calculate arrowhead points for end angle + SbVec3f endArrowBase = pnt4; + SbVec3f endArrowLeft = endArrowBase - arrowLength * dirEnd + arrowWidth * v2; + SbVec3f endArrowRight = endArrowBase - arrowLength * dirEnd - arrowWidth * v2; + + // Draw arrowheads + glBegin(GL_TRIANGLES); + // Start angle arrowhead + glVertex2f(startArrowBase[0], startArrowBase[1]); + glVertex2f(startArrowLeft[0], startArrowLeft[1]); + glVertex2f(startArrowRight[0], startArrowRight[1]); + + // End angle arrowhead + glVertex2f(endArrowBase[0], endArrowBase[1]); + glVertex2f(endArrowLeft[0], endArrowLeft[1]); + glVertex2f(endArrowRight[0], endArrowRight[1]); + glEnd(); +} + +// NOLINTNEXTLINE +void SoDatumLabel::drawText(SoState *state, int srcw, int srch, float angle, const SbVec3f& textOffset) +{ + SbVec2s imgsize; + int nc {}; + const unsigned char * dataptr = this->image.getValue(imgsize, nc); + + //Get the camera z-direction + const SbViewVolume & vv = SoViewVolumeElement::get(state); + SbVec3f z = vv.zVector(); + + bool flip = norm.getValue().dot(z) > FLT_EPSILON; + + static bool init = false; + static bool npot = false; + if (!init) { + init = true; + std::string ext = reinterpret_cast(glGetString(GL_EXTENSIONS)); // NOLINT + npot = (ext.find("GL_ARB_texture_non_power_of_two") != std::string::npos); + } + + int w = srcw; + int h = srch; + if (!npot) { + // make power of two + if ((w & (w-1)) != 0) { + int i=1; + while (i < 8) { + if ((w >> i) == 0) { + break; + } + i++; + } + w = (1 << i); + } + // make power of two + if ((h & (h-1)) != 0) { + int i=1; + while (i < 8) { + if ((h >> i) == 0) { + break; + } + i++; + } + h = (1 << i); + } + } + + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); // Enable Textures + glEnable(GL_BLEND); + + // glGenTextures/glBindTexture was commented out but it must be active, see: + // #0000971: Tracing over a background image in Sketcher: image is overwritten by first dimensional constraint text + // #0001185: Planer image changes to number graphic when a part design constraint is made after the planar image + // + // Copy the text bitmap into memory and bind + GLuint myTexture {}; + // generate a texture + glGenTextures(1, &myTexture); + glBindTexture(GL_TEXTURE_2D, myTexture); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + if (!npot) { + QImage imagedata(w, h,QImage::Format_ARGB32_Premultiplied); + imagedata.fill(0x00000000); + int sx = (w - srcw)/2; + int sy = (h - srch)/2; + glTexImage2D(GL_TEXTURE_2D, 0, nc, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)imagedata.bits()); + glTexSubImage2D(GL_TEXTURE_2D, 0, sx, sy, srcw, srch, GL_RGBA, GL_UNSIGNED_BYTE,(const GLvoid*) dataptr); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, nc, srcw, srch, 0, GL_RGBA, GL_UNSIGNED_BYTE,(const GLvoid*) dataptr); + } + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + // Apply a rotation and translation matrix + glTranslatef(textOffset[0], textOffset[1], textOffset[2]); + glRotatef((GLfloat) angle * 180 / M_PI, 0,0,1); + glBegin(GL_QUADS); + + glColor3f(1.F, 1.F, 1.F); + + glTexCoord2f(flip ? 0.F : 1.F, 1.F); glVertex2f( -this->imgWidth / 2, this->imgHeight / 2); + glTexCoord2f(flip ? 0.F : 1.F, 0.F); glVertex2f( -this->imgWidth / 2, -this->imgHeight / 2); + glTexCoord2f(flip ? 1.F : 0.F, 0.F); glVertex2f( this->imgWidth / 2, -this->imgHeight / 2); + glTexCoord2f(flip ? 1.F : 0.F, 1.F); glVertex2f( this->imgWidth / 2, this->imgHeight / 2); + + glEnd(); + + // Reset the Mode + glPopMatrix(); + + // wmayer: see bug report below which is caused by generating but not + // deleting the texture. + // #0000721: massive memory leak when dragging an unconstrained model + glDeleteTextures(1, &myTexture); } void SoDatumLabel::setPoints(SbVec3f p1, SbVec3f p2) diff --git a/src/Gui/SoDatumLabel.h b/src/Gui/SoDatumLabel.h index d2b75844ed..70aa8c8b96 100644 --- a/src/Gui/SoDatumLabel.h +++ b/src/Gui/SoDatumLabel.h @@ -105,6 +105,15 @@ private: SbVec3f getLabelTextCenterDiameter(const SbVec3f&, const SbVec3f&); SbVec3f getLabelTextCenterAngle(const SbVec3f&); SbVec3f getLabelTextCenterArcLength(const SbVec3f&, const SbVec3f&, const SbVec3f&); + bool hasDatumText() const; + void getDimension(float scale, int& srcw, int& srch); + void drawDistance(const SbVec3f* points, float scale, int srch, float& angle, SbVec3f& textOffset); + void drawDistance(const SbVec3f* points); + void drawRadiusOrDiameter(const SbVec3f* points, float& angle, SbVec3f& textOffset); + void drawAngle(const SbVec3f* points, float& angle, SbVec3f& textOffset); + void drawSymmetric(const SbVec3f* points); + void drawArcLength(const SbVec3f* points, float& angle, SbVec3f& textOffset); + void drawText(SoState *state, int srcw, int srch, float angle, const SbVec3f& textOffset); private: void drawImage();