1
/***************************************************************************
2
* Copyright (c) 2011-2012 Luke Parry <l.parry@warwick.ac.uk> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
31
# include <OpenGL/gl.h>
39
# include <QFontMetrics>
42
# include <Inventor/SoPrimitiveVertex.h>
43
# include <Inventor/actions/SoGLRenderAction.h>
44
# include <Inventor/elements/SoFocalDistanceElement.h>
45
# include <Inventor/elements/SoViewportRegionElement.h>
46
# include <Inventor/elements/SoViewVolumeElement.h>
47
# include <Inventor/misc/SoState.h>
50
#include <Gui/BitmapFactory.h>
53
#include "SoDatumLabel.h"
60
// ------------------------------------------------------
62
SO_NODE_SOURCE(SoDatumLabel)
64
void SoDatumLabel::initClass()
66
SO_NODE_INIT_CLASS(SoDatumLabel, SoShape, "Shape");
70
SoDatumLabel::SoDatumLabel()
72
SO_NODE_CONSTRUCTOR(SoDatumLabel);
73
SO_NODE_ADD_FIELD(string, (""));
74
SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f)));
75
SO_NODE_ADD_FIELD(pnts, (SbVec3f(.0f,.0f,.0f)));
76
SO_NODE_ADD_FIELD(norm, (SbVec3f(.0f,.0f,1.f)));
78
SO_NODE_ADD_FIELD(name, ("Helvetica"));
79
SO_NODE_ADD_FIELD(size, (10.f));
80
SO_NODE_ADD_FIELD(lineWidth, (2.f));
82
SO_NODE_ADD_FIELD(datumtype, (SoDatumLabel::DISTANCE));
84
SO_NODE_DEFINE_ENUM_VALUE(Type, DISTANCE);
85
SO_NODE_DEFINE_ENUM_VALUE(Type, DISTANCEX);
86
SO_NODE_DEFINE_ENUM_VALUE(Type, DISTANCEY);
87
SO_NODE_DEFINE_ENUM_VALUE(Type, ANGLE);
88
SO_NODE_DEFINE_ENUM_VALUE(Type, RADIUS);
89
SO_NODE_DEFINE_ENUM_VALUE(Type, DIAMETER);
90
SO_NODE_SET_SF_ENUM_TYPE(datumtype, Type);
92
SO_NODE_ADD_FIELD(param1, (0.f));
93
SO_NODE_ADD_FIELD(param2, (0.f));
94
SO_NODE_ADD_FIELD(param4, (0.f));
95
SO_NODE_ADD_FIELD(param5, (0.f));
96
SO_NODE_ADD_FIELD(param6, (0.f));
97
SO_NODE_ADD_FIELD(param7, (0.f));
98
SO_NODE_ADD_FIELD(param8, (0.f));
100
useAntialiasing = true;
104
this->glimagevalid = false;
107
void SoDatumLabel::drawImage()
109
const SbString* s = string.getValues(0);
110
int num = string.getNum();
112
this->image = SoSFImage();
116
QFont font(QString::fromLatin1(name.getValue(), -1), size.getValue());
117
QFontMetrics fm(font);
118
QString str = QString::fromUtf8(s[0].getString());
120
int w = Gui::QtTools::horizontalAdvance(fm, str);
125
this->image = SoSFImage();
129
const SbColor& t = textColor.getValue();
131
front.setRgbF(t[0],t[1], t[2]);
133
QImage image(w, h,QImage::Format_ARGB32_Premultiplied);
134
image.fill(0x00000000);
136
QPainter painter(&image);
138
painter.setRenderHint(QPainter::Antialiasing);
140
painter.setPen(front);
141
painter.setFont(font);
142
painter.drawText(0, 0, w, h, Qt::AlignLeft, str);
145
Gui::BitmapFactory().convert(image, this->image);
149
// Helper class to determine the bounding box of a datum label
153
DatumLabelBox(float scale, SoDatumLabel* label)
159
void computeBBox(SbBox3f& box, SbVec3f& center) const
161
std::vector<SbVec3f> corners;
162
if (label->datumtype.getValue() == SoDatumLabel::DISTANCE ||
163
label->datumtype.getValue() == SoDatumLabel::DISTANCEX ||
164
label->datumtype.getValue() == SoDatumLabel::DISTANCEY ) {
165
corners = computeDistanceBBox();
167
else if (label->datumtype.getValue() == SoDatumLabel::RADIUS ||
168
label->datumtype.getValue() == SoDatumLabel::DIAMETER) {
169
corners = computeRadiusDiameterBBox();
171
else if (label->datumtype.getValue() == SoDatumLabel::ANGLE) {
172
corners = computeAngleBBox();
174
else if (label->datumtype.getValue() == SoDatumLabel::SYMMETRIC) {
175
corners = computeSymmetricBBox();
178
getBBox(corners, box, center);
182
void getBBox(const std::vector<SbVec3f>& corners, SbBox3f& box, SbVec3f& center) const
184
if (corners.size() > 1) {
185
float minX = FLT_MAX;
186
float minY = FLT_MAX;
187
float maxX = -FLT_MAX;
188
float maxY = -FLT_MAX;
189
for (SbVec3f it : corners) {
190
minX = (it[0] < minX) ? it[0] : minX;
191
minY = (it[1] < minY) ? it[1] : minY;
192
maxX = (it[0] > maxX) ? it[0] : maxX;
193
maxY = (it[1] > maxY) ? it[1] : maxY;
196
// Store the bounding box
197
box.setBounds(SbVec3f(minX, minY, 0.0F), SbVec3f (maxX, maxY, 0.0F));
198
center = box.getCenter();
201
std::vector<SbVec3f> computeDistanceBBox() const
208
const unsigned char * dataptr = label->image.getValue(imgsize, nc);
214
float aspectRatio = (float) srcw / (float) srch;
215
float imgHeight = scale * (float) (srch);
216
float imgWidth = aspectRatio * imgHeight;
218
// Get the points stored in the pnt field
219
const SbVec3f *points = label->pnts.getValues(0);
220
if (label->pnts.getNum() < 2) {
226
float length = label->param1.getValue();
227
float length2 = label->param2.getValue();
229
SbVec3f p1 = points[0];
230
SbVec3f p2 = points[1];
234
if (label->datumtype.getValue() == SoDatumLabel::DISTANCE) {
237
else if (label->datumtype.getValue() == SoDatumLabel::DISTANCEX) {
238
dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0);
240
else if (label->datumtype.getValue() == SoDatumLabel::DISTANCEY) {
241
dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0);
245
normal = SbVec3f (-dir[1], dir[0], 0);
247
// when the datum line is not parallel to p1-p2 the projection of
248
// p1-p2 on normal is not zero, p2 is considered as reference and p1
249
// is replaced by its projection p1_
250
float normproj12 = (p2 - p1).dot(normal);
251
SbVec3f p1_ = p1 + normproj12 * normal;
253
SbVec3f midpos = (p1_ + p2)/2;
255
float offset1 = ((length + normproj12 < 0.0F) ? -1.0F : 1.0F) * float(srch);
256
float offset2 = ((length < 0.0F) ? -1.0F : 1.0F) * float(srch);
258
textOffset = midpos + normal * length + dir * length2;
259
float margin = imgHeight / 4.0F;
261
SbVec3f perp1 = p1_ + normal * (length + offset1 * scale);
262
SbVec3f perp2 = p2 + normal * (length + offset2 * scale);
264
// Finds the mins and maxes
265
std::vector<SbVec3f> corners;
266
corners.push_back(p1);
267
corners.push_back(p2);
268
corners.push_back(perp1);
269
corners.push_back(perp2);
271
// Make sure that the label is inside the bounding box
272
corners.push_back(textOffset + dir * (imgWidth / 2.0F + margin) + normal * (srch + margin));
273
corners.push_back(textOffset - dir * (imgWidth / 2.0F + margin) + normal * (srch + margin));
274
corners.push_back(textOffset + dir * (imgWidth / 2.0F + margin) - normal * margin);
275
corners.push_back(textOffset - dir * (imgWidth / 2.0F + margin) - normal * margin);
280
std::vector<SbVec3f> computeRadiusDiameterBBox() const
287
const unsigned char * dataptr = label->image.getValue(imgsize, nc);
293
float aspectRatio = (float) srcw / (float) srch;
294
float imgHeight = scale * (float) (srch);
295
float imgWidth = aspectRatio * imgHeight;
297
// Get the points stored in the pnt field
298
const SbVec3f *points = label->pnts.getValues(0);
299
if (label->pnts.getNum() < 2) {
304
SbVec3f p1 = points[0];
305
SbVec3f p2 = points[1];
307
SbVec3f dir = p2 - p1;
309
SbVec3f normal (-dir[1], dir[0], 0);
311
float length = label->param1.getValue();
312
SbVec3f pos = p2 + length*dir;
314
float margin = imgHeight / 4.0F;
316
SbVec3f p3 = pos + dir * (imgWidth / 2.0F + margin);
317
if ((p3-p1).length() > (p2-p1).length()) {
321
// Calculate the points
322
SbVec3f pnt1 = pos - dir * (margin + imgWidth / 2.0F);
323
SbVec3f pnt2 = pos + dir * (margin + imgWidth / 2.0F);
325
// Finds the mins and maxes
326
std::vector<SbVec3f> corners;
327
corners.push_back(p1);
328
corners.push_back(p2);
329
corners.push_back(pnt1);
330
corners.push_back(pnt2);
335
std::vector<SbVec3f> computeAngleBBox() const
342
const unsigned char * dataptr = label->image.getValue(imgsize, nc);
348
float aspectRatio = (float) srcw / (float) srch;
349
float imgHeight = scale * (float) (srch);
350
float imgWidth = aspectRatio * imgHeight;
352
// Get the points stored in the pnt field
353
const SbVec3f *points = label->pnts.getValues(0);
354
if (label->pnts.getNum() < 1) {
358
// Only the angle intersection point is needed
359
SbVec3f p0 = points[0];
361
// Load the Parameters
362
float length = label->param1.getValue();
363
float startangle = label->param2.getValue();
364
float range = label->param3.getValue();
365
float endangle = startangle + range;
368
float len2 = 2.0F * length;
370
// Useful Information
371
// v0 - vector for text position
372
// p0 - vector for angle intersect
373
SbVec3f v0(cos(startangle+range/2), sin(startangle+range/2), 0);
375
SbVec3f textOffset = p0 + v0 * len2;
377
float margin = imgHeight / 4.0F;
379
// Direction vectors for start and end lines
380
SbVec3f v1(cos(startangle), sin(startangle), 0);
381
SbVec3f v2(cos(endangle), sin(endangle), 0);
383
SbVec3f pnt1 = p0+(len2-margin)*v1;
384
SbVec3f pnt2 = p0+(len2+margin)*v1;
385
SbVec3f pnt3 = p0+(len2-margin)*v2;
386
SbVec3f pnt4 = p0+(len2+margin)*v2;
388
// Finds the mins and maxes
389
// We may need to include the text position too
391
SbVec3f img1 = SbVec3f(-imgWidth / 2.0F, -imgHeight / 2, 0.0F);
392
SbVec3f img2 = SbVec3f(-imgWidth / 2.0F, imgHeight / 2, 0.0F);
393
SbVec3f img3 = SbVec3f( imgWidth / 2.0F, -imgHeight / 2, 0.0F);
394
SbVec3f img4 = SbVec3f( imgWidth / 2.0F, imgHeight / 2, 0.0F);
401
std::vector<SbVec3f> corners;
402
corners.push_back(pnt1);
403
corners.push_back(pnt2);
404
corners.push_back(pnt3);
405
corners.push_back(pnt4);
406
corners.push_back(img1);
407
corners.push_back(img2);
408
corners.push_back(img3);
409
corners.push_back(img4);
414
std::vector<SbVec3f> computeSymmetricBBox() const
416
// Get the points stored in the pnt field
417
const SbVec3f *points = label->pnts.getValues(0);
418
if (label->pnts.getNum() < 2) {
422
SbVec3f p1 = points[0];
423
SbVec3f p2 = points[1];
425
// Finds the mins and maxes
426
std::vector<SbVec3f> corners;
427
corners.push_back(p1);
428
corners.push_back(p2);
439
void SoDatumLabel::computeBBox(SoAction * action, SbBox3f &box, SbVec3f ¢er)
441
SoState *state = action->getState();
442
float scale = getScaleFactor(state);
444
DatumLabelBox datumBox(scale, this);
445
datumBox.computeBBox(box, center);
448
SbVec3f SoDatumLabel::getLabelTextCenter()
450
// Get the points stored
451
const SbVec3f* points = this->pnts.getValues(0);
452
SbVec3f p1 = points[0];
453
SbVec3f p2 = points[1];
455
if (datumtype.getValue() == SoDatumLabel::DISTANCE ||
456
datumtype.getValue() == SoDatumLabel::DISTANCEX ||
457
datumtype.getValue() == SoDatumLabel::DISTANCEY) {
458
return getLabelTextCenterDistance(p1, p2);
460
else if (datumtype.getValue() == SoDatumLabel::RADIUS ||
461
datumtype.getValue() == SoDatumLabel::DIAMETER) {
462
return getLabelTextCenterDiameter(p1, p2);
465
else if (datumtype.getValue() == SoDatumLabel::ANGLE) {
466
return getLabelTextCenterAngle(p1);
472
SbVec3f SoDatumLabel::getLabelTextCenterDistance(const SbVec3f& p1, const SbVec3f& p2)
474
float length = param1.getValue();
475
float length2 = param2.getValue();
479
if (datumtype.getValue() == SoDatumLabel::DISTANCE) {
482
else if (datumtype.getValue() == SoDatumLabel::DISTANCEX) {
483
dir = SbVec3f((p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0);
485
else if (datumtype.getValue() == SoDatumLabel::DISTANCEY) {
486
dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0);
490
normal = SbVec3f(-dir[1], dir[0], 0);
492
float normproj12 = (p2 - p1).dot(normal);
493
SbVec3f p1_ = p1 + normproj12 * normal;
495
SbVec3f midpos = (p1_ + p2) / 2;
497
SbVec3f textCenter = midpos + normal * length + dir * length2;
501
SbVec3f SoDatumLabel::getLabelTextCenterDiameter(const SbVec3f& p1, const SbVec3f& p2)
503
SbVec3f dir = (p2 - p1);
506
float length = this->param1.getValue();
507
SbVec3f textCenter = p2 + length * dir;
511
SbVec3f SoDatumLabel::getLabelTextCenterAngle(const SbVec3f& p0)
513
// Load the Parameters
514
float length = param1.getValue();
515
float startangle = param2.getValue();
516
float range = param3.getValue();
517
float len2 = 2.0F * length;
519
// Useful Information
520
// v0 - vector for text position
521
// p0 - vector for angle intersect
522
SbVec3f v0(cos(startangle + range / 2), sin(startangle + range / 2), 0);
524
SbVec3f textCenter = p0 + v0 * len2;
528
void SoDatumLabel::generateDistancePrimitives(SoAction * action, const SbVec3f& p1, const SbVec3f& p2)
531
if (this->datumtype.getValue() == DISTANCE) {
533
} else if (this->datumtype.getValue() == DISTANCEX) {
534
dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0);
535
} else if (this->datumtype.getValue() == DISTANCEY) {
536
dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0);
541
// Get magnitude of angle between horizontal
542
float angle = atan2f(dir[1],dir[0]);
544
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
545
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
546
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
547
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
549
// Rotate through an angle
550
float s = sin(angle);
551
float c = cos(angle);
553
img1 = SbVec3f((img1[0] * c) - (img1[1] * s), (img1[0] * s) + (img1[1] * c), 0.f);
554
img2 = SbVec3f((img2[0] * c) - (img2[1] * s), (img2[0] * s) + (img2[1] * c), 0.f);
555
img3 = SbVec3f((img3[0] * c) - (img3[1] * s), (img3[0] * s) + (img3[1] * c), 0.f);
556
img4 = SbVec3f((img4[0] * c) - (img4[1] * s), (img4[0] * s) + (img4[1] * c), 0.f);
558
SbVec3f textOffset = getLabelTextCenterDistance(p1, p2);
565
// Primitive Shape is only for text as this should only be selectable
566
SoPrimitiveVertex pv;
568
this->beginShape(action, TRIANGLE_STRIP);
570
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
588
void SoDatumLabel::generateDiameterPrimitives(SoAction * action, const SbVec3f& p1, const SbVec3f& p2)
590
SbVec3f dir = (p2-p1);
593
float angle = atan2f(dir[1],dir[0]);
595
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
596
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
597
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
598
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
600
// Rotate through an angle
601
float s = sin(angle);
602
float c = cos(angle);
604
img1 = SbVec3f((img1[0] * c) - (img1[1] * s), (img1[0] * s) + (img1[1] * c), 0.f);
605
img2 = SbVec3f((img2[0] * c) - (img2[1] * s), (img2[0] * s) + (img2[1] * c), 0.f);
606
img3 = SbVec3f((img3[0] * c) - (img3[1] * s), (img3[0] * s) + (img3[1] * c), 0.f);
607
img4 = SbVec3f((img4[0] * c) - (img4[1] * s), (img4[0] * s) + (img4[1] * c), 0.f);
609
SbVec3f textOffset = getLabelTextCenterDiameter(p1, p2);
616
// Primitive Shape is only for text as this should only be selectable
617
SoPrimitiveVertex pv;
619
this->beginShape(action, TRIANGLE_STRIP);
621
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
639
void SoDatumLabel::generateAnglePrimitives(SoAction * action, const SbVec3f& p0)
641
SbVec3f textOffset = getLabelTextCenterAngle(p0);
643
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
644
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
645
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
646
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
653
// Primitive Shape is only for text as this should only be selectable
654
SoPrimitiveVertex pv;
656
this->beginShape(action, TRIANGLE_STRIP);
658
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
676
void SoDatumLabel::generateSymmetricPrimitives(SoAction * action, const SbVec3f& p1, const SbVec3f& p2)
678
SbVec3f dir = (p2-p1);
680
SbVec3f normal (-dir[1],dir[0],0);
682
float margin = this->imgHeight / 4.0;
684
// Calculate coordinates for the first arrow
685
SbVec3f ar0, ar1, ar2;
686
ar0 = p1 + dir * 5 * margin ;
687
ar1 = ar0 - dir * 0.866f * 2 * margin; // Base Point of Arrow
688
ar2 = ar1 + normal * margin; // Triangular corners
689
ar1 -= normal * margin;
691
// Calculate coordinates for the second arrow
692
SbVec3f ar3, ar4, ar5;
693
ar3 = p2 - dir * 5 * margin ;
694
ar4 = ar3 + dir * 0.866f * 2 * margin; // Base Point of 2nd Arrow
696
ar5 = ar4 + normal * margin; // Triangular corners
697
ar4 -= normal * margin;
699
SoPrimitiveVertex pv;
701
this->beginShape(action, TRIANGLES);
703
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
728
void SoDatumLabel::generatePrimitives(SoAction * action)
730
// Initialisation check (needs something more sensible) prevents an infinite loop bug
731
if (this->imgHeight <= FLT_EPSILON || this->imgWidth <= FLT_EPSILON)
734
// Get the points stored
735
const SbVec3f *points = this->pnts.getValues(0);
736
SbVec3f p1 = points[0];
737
SbVec3f p2 = points[1];
739
// Change the offset and bounding box parameters depending on Datum Type
740
if (this->datumtype.getValue() == DISTANCE ||
741
this->datumtype.getValue() == DISTANCEX ||
742
this->datumtype.getValue() == DISTANCEY) {
744
generateDistancePrimitives(action, p1, p2);
746
else if (this->datumtype.getValue() == RADIUS ||
747
this->datumtype.getValue() == DIAMETER) {
749
generateDiameterPrimitives(action, p1, p2);
751
else if (this->datumtype.getValue() == ANGLE) {
753
generateAnglePrimitives(action, p1);
755
else if (this->datumtype.getValue() == SYMMETRIC) {
757
generateSymmetricPrimitives(action, p1, p2);
761
void SoDatumLabel::notify(SoNotList * l)
763
SoField * f = l->getLastField();
764
if (f == &this->string) {
765
this->glimagevalid = false;
767
else if (f == &this->textColor) {
768
this->glimagevalid = false;
770
else if (f == &this->name) {
771
this->glimagevalid = false;
773
else if (f == &this->size) {
774
this->glimagevalid = false;
776
else if (f == &this->image) {
777
this->glimagevalid = false;
779
inherited::notify(l);
782
float SoDatumLabel::getScaleFactor(SoState* state) const
784
/**Remark from Stefan Tröger:
785
* The scale calculation is based on knowledge of SbViewVolume::getWorldToScreenScale
786
* implementation internals. The factor returned from this function is calculated from the view frustums
787
* nearplane width, height is not taken into account, and hence we divide it with the viewport width
788
* to get the exact pixel scale factor.
789
* This is not documented and therefore may change on later coin versions!
791
const SbViewVolume & vv = SoViewVolumeElement::get(state);
792
// As reference use the center point the camera is looking at on the focal plane
793
// because then independent of the camera we get a constant scale factor when panning.
794
// If we used (0,0,0) instead then the scale factor would change heavily in perspective
795
// rendering mode. See #0002921 and #0002922.
796
// It's important to use the distance to the focal plane an not near or far plane because
797
// depending on additionally displayed objects they may change heavily and thus impact the
798
// scale factor. See #7082 and #7860.
799
float focal = SoFocalDistanceElement::get(state);
800
SbVec3f center = vv.getSightPoint(focal);
801
float scale = vv.getWorldToScreenScale(center, 1.f);
802
const SbViewportRegion & vp = SoViewportRegionElement::get(state);
803
SbVec2s vp_size = vp.getViewportSizePixels();
804
scale /= float(vp_size[0]);
809
void SoDatumLabel::GLRender(SoGLRenderAction * action)
811
SoState *state = action->getState();
813
if (!shouldGLRender(action))
815
if (action->handleTransparency(true))
818
float scale = getScaleFactor(state);
820
const SbString* s = string.getValues(0);
821
bool hasText = (s->getLength() > 0) ? true : false;
828
if (!this->glimagevalid) {
830
this->glimagevalid = true;
833
const unsigned char * dataptr = this->image.getValue(imgsize, nc);
834
if (!dataptr) // no image
840
float aspectRatio = (float) srcw / (float) srch;
841
this->imgHeight = scale * (float) (srch);
842
this->imgWidth = aspectRatio * (float) this->imgHeight;
845
if (this->datumtype.getValue() == SYMMETRIC) {
846
this->imgHeight = scale*25.0f;
847
this->imgWidth = scale*25.0f;
850
// Get the points stored in the pnt field
851
const SbVec3f *points = this->pnts.getValues(0);
855
//Set General OpenGL Properties
856
glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_COLOR_BUFFER_BIT);
857
glDisable(GL_LIGHTING);
860
if (action->isSmoothing()) {
861
glEnable(GL_LINE_SMOOTH);
863
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
864
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
867
// Position for Datum Text Label
871
const SbColor& t = textColor.getValue();
874
glLineWidth(this->lineWidth.getValue());
875
glColor3f(t[0], t[1], t[2]);
879
if (this->datumtype.getValue() == DISTANCE ||
880
this->datumtype.getValue() == DISTANCEX ||
881
this->datumtype.getValue() == DISTANCEY ) {
882
float length = this->param1.getValue();
883
float length2 = this->param2.getValue();
885
SbVec3f p1 = points[0];
886
SbVec3f p2 = points[1];
889
if (this->datumtype.getValue() == DISTANCE) {
891
} else if (this->datumtype.getValue() == DISTANCEX) {
892
dir = SbVec3f( (p2[0] - p1[0] >= FLT_EPSILON) ? 1 : -1, 0, 0);
893
} else if (this->datumtype.getValue() == DISTANCEY) {
894
dir = SbVec3f(0, (p2[1] - p1[1] >= FLT_EPSILON) ? 1 : -1, 0);
898
normal = SbVec3f (-dir[1],dir[0],0);
900
// when the datum line is not parallel to p1-p2 the projection of
901
// p1-p2 on normal is not zero, p2 is considered as reference and p1
902
// is replaced by its projection p1_
903
float normproj12 = (p2-p1).dot(normal);
904
SbVec3f p1_ = p1 + normproj12 * normal;
906
SbVec3f midpos = (p1_ + p2)/2;
908
float offset1 = ((length + normproj12 < 0) ? -1. : 1.) * srch;
909
float offset2 = ((length < 0) ? -1 : 1)*srch;
911
// Get magnitude of angle between horizontal
912
angle = atan2f(dir[1],dir[0]);
913
if (angle > M_PI_2+M_PI/12) {
914
angle -= (float)M_PI;
915
} else if (angle <= -M_PI_2+M_PI/12) {
916
angle += (float)M_PI;
919
textOffset = midpos + normal * length + dir * length2;
922
const SbColor& t = textColor.getValue();
925
glLineWidth(this->lineWidth.getValue());
926
glColor3f(t[0], t[1], t[2]);
927
float margin = this->imgHeight / 3.0;
930
SbVec3f perp1 = p1_ + normal * (length + offset1 * scale);
931
SbVec3f perp2 = p2 + normal * (length + offset2 * scale);
933
// Calculate the coordinates for the parallel datum lines
934
SbVec3f par1 = p1_ + normal * length;
935
SbVec3f par2 = midpos + normal * length + dir * (length2 - this->imgWidth / 2 - margin);
936
SbVec3f par3 = midpos + normal * length + dir * (length2 + this->imgWidth / 2 + margin);
937
SbVec3f par4 = p2 + normal * length;
939
bool flipTriang = false;
941
if ((par3-par1).dot(dir) > (par4 - par1).length()) {
942
// Increase Margin to improve visibility
943
float tmpMargin = this->imgHeight /0.75;
945
if ((par2-par1).dot(dir) > (par4 - par1).length()) {
947
par2 = par1 - dir * tmpMargin;
951
else if ((par2-par1).dot(dir) < 0.f) {
952
float tmpMargin = this->imgHeight /0.75;
954
if((par3-par1).dot(dir) < 0.f) {
956
par3 = par4 + dir * tmpMargin;
963
glVertex2f(p1[0], p1[1]);
964
glVertex2f(perp1[0], perp1[1]);
966
glVertex2f(p2[0], p2[1]);
967
glVertex2f(perp2[0], perp2[1]);
970
glVertex2f(par1[0], par1[1]);
971
glVertex2f(par2[0], par2[1]);
973
glVertex2f(par3[0], par3[1]);
974
glVertex2f(par4[0], par4[1]);
977
float arrowWidth = margin * 0.5;
979
SbVec3f ar1 = par1 + ((flipTriang) ? -1 : 1) * dir * 0.866f * 2 * margin;
980
SbVec3f ar2 = ar1 + normal * arrowWidth;
981
ar1 -= normal * arrowWidth;
983
SbVec3f ar3 = par4 - ((flipTriang) ? -1 : 1) * dir * 0.866f * 2 * margin;
984
SbVec3f ar4 = ar3 + normal * arrowWidth;
985
ar3 -= normal * arrowWidth;
987
// Draw the arrowheads
988
glBegin(GL_TRIANGLES);
989
glVertex2f(par1[0], par1[1]);
990
glVertex2f(ar1[0], ar1[1]);
991
glVertex2f(ar2[0], ar2[1]);
993
glVertex2f(par4[0], par4[1]);
994
glVertex2f(ar3[0], ar3[1]);
995
glVertex2f(ar4[0], ar4[1]);
999
if (this->datumtype.getValue() == DISTANCE) {
1000
// Draw arc helpers if needed
1001
float range1 = this->param4.getValue();
1002
if (range1 != 0.0) {
1003
float startangle1 = this->param3.getValue();
1004
float radius1 = this->param5.getValue();
1005
SbVec3f center = points[2];
1006
int countSegments = std::max(6, abs(int(50.0 * range1 / (2 * M_PI))));
1007
double segment = range1 / (countSegments - 1);
1009
glBegin(GL_LINE_STRIP);
1010
for (int i = 0; i < countSegments; i++) {
1011
double theta = startangle1 + segment * i;
1012
SbVec3f v1 = center + SbVec3f(radius1 * cos(theta), radius1 * sin(theta), 0);
1013
glVertex2f(v1[0], v1[1]);
1017
float range2 = this->param7.getValue();
1018
if (range2 != 0.0) {
1019
float startangle2 = this->param6.getValue();
1020
float radius2 = this->param8.getValue();
1021
SbVec3f center = points[3];
1022
int countSegments = std::max(6, abs(int(50.0 * range2 / (2 * M_PI))));
1023
double segment = range2 / (countSegments - 1);
1025
glBegin(GL_LINE_STRIP);
1026
for (int i = 0; i < countSegments; i++) {
1027
double theta = startangle2 + segment * i;
1028
SbVec3f v1 = center + SbVec3f(radius2 * cos(theta), radius2 * sin(theta), 0);
1029
glVertex2f(v1[0], v1[1]);
1035
else if (this->datumtype.getValue() == RADIUS || this->datumtype.getValue() == DIAMETER) {
1037
SbVec3f p1 = points[0];
1038
SbVec3f p2 = points[1];
1040
SbVec3f dir = (p2-p1);
1041
SbVec3f center = p1;
1042
double radius = (p2 - p1).length();
1043
if (this->datumtype.getValue() == DIAMETER) {
1044
center = (p1 + p2) / 2;
1045
radius = radius / 2;
1049
SbVec3f normal (-dir[1],dir[0],0);
1051
float length = this->param1.getValue();
1052
SbVec3f pos = p2 + length*dir;
1054
// Get magnitude of angle between horizontal
1055
angle = atan2f(dir[1],dir[0]);
1056
if (angle > M_PI_2+M_PI/12) {
1057
angle -= (float)M_PI;
1058
} else if (angle <= -M_PI_2+M_PI/12) {
1059
angle += (float)M_PI;
1064
float margin = this->imgHeight / 3.0;
1066
// Create the arrowhead
1067
float arrowWidth = margin * 0.5;
1069
SbVec3f ar1 = p2 - dir * 0.866f * 2 * margin;
1070
SbVec3f ar2 = ar1 + normal * arrowWidth;
1071
ar1 -= normal * arrowWidth;
1073
SbVec3f p3 = pos + dir * (this->imgWidth / 2 + margin);
1074
if ((p3-p1).length() > (p2-p1).length())
1077
// Calculate the points
1078
SbVec3f pnt1 = pos - dir * (margin + this->imgWidth / 2);
1079
SbVec3f pnt2 = pos + dir * (margin + this->imgWidth / 2);
1083
glVertex2f(p1[0], p1[1]);
1084
glVertex2f(pnt1[0], pnt1[1]);
1086
glVertex2f(pnt2[0], pnt2[1]);
1087
glVertex2f(p2[0], p2[1]);
1090
glBegin(GL_TRIANGLES);
1091
glVertex2f(ar0[0], ar0[1]);
1092
glVertex2f(ar1[0], ar1[1]);
1093
glVertex2f(ar2[0], ar2[1]);
1096
if (this->datumtype.getValue() == DIAMETER) {
1097
// create second arrowhead
1099
SbVec3f ar1_1 = p1 + dir * 0.866f * 2 * margin;
1100
SbVec3f ar2_1 = ar1_1 + normal * arrowWidth;
1101
ar1_1 -= normal * arrowWidth;
1103
glBegin(GL_TRIANGLES);
1104
glVertex2f(ar0_1[0], ar0_1[1]);
1105
glVertex2f(ar1_1[0], ar1_1[1]);
1106
glVertex2f(ar2_1[0], ar2_1[1]);
1110
// Draw arc helper if needed
1111
float startangle = this->param3.getValue();
1112
float range = this->param4.getValue();
1114
int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI))));
1115
double segment = range / (countSegments - 1);
1117
glBegin(GL_LINE_STRIP);
1118
for (int i = 0; i < countSegments; i++) {
1119
double theta = startangle + segment * i;
1120
SbVec3f v1 = center + SbVec3f(radius * cos(theta), radius * sin(theta), 0);
1121
glVertex2f(v1[0], v1[1]);
1127
else if (this->datumtype.getValue() == ANGLE) {
1128
// Only the angle intersection point is needed
1129
SbVec3f p0 = points[0];
1131
float margin = this->imgHeight / 3.0;
1133
// Load the Parameters
1134
float length = this->param1.getValue();
1135
float startangle = this->param2.getValue();
1136
float range = this->param3.getValue();
1137
float endangle = startangle + range;
1138
float endLineLength1 = std::max(this->param4.getValue(), margin);
1139
float endLineLength2 = std::max(this->param5.getValue(), margin);
1140
float endLineLength12 = std::max(- this->param4.getValue(), margin);
1141
float endLineLength22 = std::max(- this->param5.getValue(), margin);
1146
// Set the Text label angle to zero
1149
// Useful Information
1150
// v0 - vector for text position
1151
// p0 - vector for angle intersect
1152
SbVec3f v0(cos(startangle+range/2),sin(startangle+range/2),0);
1154
// leave some space for the text
1156
range = std::max(0.2f*range, range - this->imgWidth/(2*r));
1158
range = std::min(0.2f*range, range + this->imgWidth/(2*r));
1160
int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI))));
1161
double segment = range / (2*countSegments-2);
1163
textOffset = p0 + v0 * r;
1167
glBegin(GL_LINE_STRIP);
1169
for (int i=0; i < countSegments; i++) {
1170
double theta = startangle + segment*i;
1171
SbVec3f v1 = p0+SbVec3f(r*cos(theta),r*sin(theta),0);
1172
glVertex2f(v1[0],v1[1]);
1176
glBegin(GL_LINE_STRIP);
1177
for (int i=0; i < countSegments; i++) {
1178
double theta = endangle - segment*i;
1179
SbVec3f v1 = p0+SbVec3f(r*cos(theta),r*sin(theta),0);
1180
glVertex2f(v1[0],v1[1]);
1184
// Direction vectors for start and end lines
1185
SbVec3f v1(cos(startangle),sin(startangle),0);
1186
SbVec3f v2(cos(endangle),sin(endangle),0);
1188
SbVec3f pnt1 = p0 + (r - endLineLength1) * v1;
1189
SbVec3f pnt2 = p0 + (r + endLineLength12) * v1;
1190
SbVec3f pnt3 = p0 + (r - endLineLength2) * v2;
1191
SbVec3f pnt4 = p0 + (r + endLineLength22) * v2;
1194
glVertex2f(pnt1[0],pnt1[1]);
1195
glVertex2f(pnt2[0],pnt2[1]);
1197
glVertex2f(pnt3[0],pnt3[1]);
1198
glVertex2f(pnt4[0],pnt4[1]);
1201
// Create the arrowheads
1202
float arrowLength = margin * 2;
1203
float arrowWidth = margin * 0.5;
1205
// Normals for the arrowheads
1206
SbVec3f dirStart(v1[1], -v1[0], 0);
1207
SbVec3f dirEnd(-v2[1], v2[0], 0);
1209
// Calculate arrowhead points for start angle
1210
SbVec3f startArrowBase = p0 + r * v1;
1211
SbVec3f startArrowLeft = startArrowBase - arrowLength * dirStart + arrowWidth * v1;
1212
SbVec3f startArrowRight = startArrowBase - arrowLength * dirStart - arrowWidth * v1;
1214
// Calculate arrowhead points for end angle
1215
SbVec3f endArrowBase = p0 + r * v2;
1216
SbVec3f endArrowLeft = endArrowBase - arrowLength * dirEnd + arrowWidth * v2;
1217
SbVec3f endArrowRight = endArrowBase - arrowLength * dirEnd - arrowWidth * v2;
1220
glBegin(GL_TRIANGLES);
1221
// Start angle arrowhead
1222
glVertex2f(startArrowBase[0], startArrowBase[1]);
1223
glVertex2f(startArrowLeft[0], startArrowLeft[1]);
1224
glVertex2f(startArrowRight[0], startArrowRight[1]);
1226
// End angle arrowhead
1227
glVertex2f(endArrowBase[0], endArrowBase[1]);
1228
glVertex2f(endArrowLeft[0], endArrowLeft[1]);
1229
glVertex2f(endArrowRight[0], endArrowRight[1]);
1232
else if (this->datumtype.getValue() == SYMMETRIC) {
1234
SbVec3f p1 = points[0];
1235
SbVec3f p2 = points[1];
1237
SbVec3f dir = (p2-p1);
1239
SbVec3f normal (-dir[1],dir[0],0);
1241
float margin = this->imgHeight / 4.0;
1243
// Calculate coordinates for the first arrow
1244
SbVec3f ar0, ar1, ar2;
1245
ar0 = p1 + dir * 4 * margin; // Tip of Arrow
1246
ar1 = ar0 - dir * 0.866f * 2 * margin;
1247
ar2 = ar1 + normal * margin;
1248
ar1 -= normal * margin;
1251
glVertex3f(p1[0], p1[1], ZCONSTR);
1252
glVertex3f(ar0[0], ar0[1], ZCONSTR);
1253
glVertex3f(ar0[0], ar0[1], ZCONSTR);
1254
glVertex3f(ar1[0], ar1[1], ZCONSTR);
1255
glVertex3f(ar0[0], ar0[1], ZCONSTR);
1256
glVertex3f(ar2[0], ar2[1], ZCONSTR);
1259
// Calculate coordinates for the second arrow
1260
SbVec3f ar3, ar4, ar5;
1261
ar3 = p2 - dir * 4 * margin; // Tip of 2nd Arrow
1262
ar4 = ar3 + dir * 0.866f * 2 * margin;
1263
ar5 = ar4 + normal * margin;
1264
ar4 -= normal * margin;
1267
glVertex3f(p2[0], p2[1], ZCONSTR);
1268
glVertex3f(ar3[0], ar3[1], ZCONSTR);
1269
glVertex3f(ar3[0], ar3[1], ZCONSTR);
1270
glVertex3f(ar4[0], ar4[1], ZCONSTR);
1271
glVertex3f(ar3[0], ar3[1], ZCONSTR);
1272
glVertex3f(ar5[0], ar5[1], ZCONSTR);
1277
const unsigned char * dataptr = this->image.getValue(imgsize, nc);
1279
//Get the camera z-direction
1280
const SbViewVolume & vv = SoViewVolumeElement::get(state);
1281
SbVec3f z = vv.zVector();
1283
bool flip = norm.getValue().dot(z) > FLT_EPSILON;
1285
static bool init = false;
1286
static bool npot = false;
1289
std::string ext = (const char*)(glGetString(GL_EXTENSIONS));
1290
npot = (ext.find("GL_ARB_texture_non_power_of_two") != std::string::npos);
1296
// make power of two
1297
if ((w & (w-1)) != 0) {
1306
// make power of two
1307
if ((h & (h-1)) != 0) {
1318
glDisable(GL_DEPTH_TEST);
1319
glEnable(GL_TEXTURE_2D); // Enable Textures
1322
// glGenTextures/glBindTexture was commented out but it must be active, see:
1323
// #0000971: Tracing over a background image in Sketcher: image is overwritten by first dimensional constraint text
1324
// #0001185: Planer image changes to number graphic when a part design constraint is made after the planar image
1326
// Copy the text bitmap into memory and bind
1328
// generate a texture
1329
glGenTextures(1, &myTexture);
1330
glBindTexture(GL_TEXTURE_2D, myTexture);
1332
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1333
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1336
QImage imagedata(w, h,QImage::Format_ARGB32_Premultiplied);
1337
imagedata.fill(0x00000000);
1338
int sx = (w - srcw)/2;
1339
int sy = (h - srch)/2;
1340
glTexImage2D(GL_TEXTURE_2D, 0, nc, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)imagedata.bits());
1341
glTexSubImage2D(GL_TEXTURE_2D, 0, sx, sy, srcw, srch, GL_RGBA, GL_UNSIGNED_BYTE,(const GLvoid*) dataptr);
1344
glTexImage2D(GL_TEXTURE_2D, 0, nc, srcw, srch, 0, GL_RGBA, GL_UNSIGNED_BYTE,(const GLvoid*) dataptr);
1346
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1348
glMatrixMode(GL_MODELVIEW);
1351
// Apply a rotation and translation matrix
1352
glTranslatef(textOffset[0],textOffset[1], textOffset[2]);
1353
glRotatef((GLfloat) angle * 180 / M_PI, 0,0,1);
1356
glColor3f(1.f, 1.f, 1.f);
1358
glTexCoord2f(flip ? 0.f : 1.f, 1.f); glVertex2f( -this->imgWidth / 2, this->imgHeight / 2);
1359
glTexCoord2f(flip ? 0.f : 1.f, 0.f); glVertex2f( -this->imgWidth / 2, -this->imgHeight / 2);
1360
glTexCoord2f(flip ? 1.f : 0.f, 0.f); glVertex2f( this->imgWidth / 2, -this->imgHeight / 2);
1361
glTexCoord2f(flip ? 1.f : 0.f, 1.f); glVertex2f( this->imgWidth / 2, this->imgHeight / 2);
1368
// wmayer: see bug report below which is caused by generating but not
1369
// deleting the texture.
1370
// #0000721: massive memory leak when dragging an unconstrained model
1371
glDeleteTextures(1, &myTexture);
1378
void SoDatumLabel::setPoints(SbVec3f p1, SbVec3f p2)
1381
SbVec3f* verts = pnts.startEditing();
1384
pnts.finishEditing();