FreeCAD/src/Mod/Sketcher/Gui/SoDatumLabel.cpp

963 lines
31 KiB
C++

/***************************************************************************
* Copyright (c) 2011-2012 Luke Parry <l.parry@warwick.ac.uk> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# ifdef FC_OS_WIN32
# include <windows.h>
# undef min
# undef max
# endif
# ifdef FC_OS_MACOSX
# include <OpenGL/gl.h>
# else
# include <GL/gl.h>
# endif
# include <cfloat>
# include <algorithm>
# include <QFontMetrics>
# include <QGLWidget>
# include <QPainter>
# include <QPen>
# include <Inventor/SoPrimitiveVertex.h>
# include <Inventor/actions/SoGLRenderAction.h>
# include <Inventor/misc/SoState.h>
# include <cmath>
#endif
#include <Inventor/actions/SoGetMatrixAction.h>
#include <Inventor/elements/SoFontNameElement.h>
#include <Inventor/elements/SoFontSizeElement.h>
#include <Inventor/elements/SoModelMatrixElement.h>
#include <Inventor/elements/SoProjectionMatrixElement.h>
#include <Inventor/elements/SoViewingMatrixElement.h>
#include <Inventor/elements/SoViewVolumeElement.h>
#include <Inventor/elements/SoViewportRegionElement.h>
#include "SoDatumLabel.h"
#include <Gui/BitmapFactory.h>
using namespace SketcherGui;
// ------------------------------------------------------
SO_NODE_SOURCE(SoDatumLabel);
void SoDatumLabel::initClass()
{
SO_NODE_INIT_CLASS(SoDatumLabel, SoShape, "Shape");
}
SoDatumLabel::SoDatumLabel()
{
SO_NODE_CONSTRUCTOR(SoDatumLabel);
SO_NODE_ADD_FIELD(string, (""));
SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f)));
SO_NODE_ADD_FIELD(pnts, (SbVec3f(.0f,.0f,.0f)));
SO_NODE_ADD_FIELD(norm, (SbVec3f(.0f,.0f,1.f)));
SO_NODE_ADD_FIELD(name, ("Helvetica"));
SO_NODE_ADD_FIELD(size, (10.f));
SO_NODE_ADD_FIELD(lineWidth, (2.f));
SO_NODE_ADD_FIELD(datumtype, (SoDatumLabel::DISTANCE));
SO_NODE_DEFINE_ENUM_VALUE(Type, DISTANCE);
SO_NODE_DEFINE_ENUM_VALUE(Type, DISTANCEX);
SO_NODE_DEFINE_ENUM_VALUE(Type, DISTANCEY);
SO_NODE_DEFINE_ENUM_VALUE(Type, ANGLE);
SO_NODE_DEFINE_ENUM_VALUE(Type, RADIUS);
SO_NODE_SET_SF_ENUM_TYPE(datumtype, Type);
SO_NODE_ADD_FIELD(param1, (0.f));
SO_NODE_ADD_FIELD(param2, (0.f));
useAntialiasing = true;
this->imgWidth = 0;
this->imgHeight = 0;
this->glimagevalid = false;
}
void SoDatumLabel::drawImage()
{
const SbString* s = string.getValues(0);
int num = string.getNum();
if (num == 0) {
this->image = SoSFImage();
return;
}
QFont font(QString::fromAscii(name.getValue()), size.getValue());
QFontMetrics fm(font);
QString str = QString::fromUtf8(s[0].getString());
int w = fm.width(str);
int h = fm.height();
// No Valid text
if (!w) {
this->image = SoSFImage();
return;
}
const SbColor& t = textColor.getValue();
QColor front;
front.setRgbF(t[0],t[1], t[2]);
QImage image(w, h,QImage::Format_ARGB32_Premultiplied);
image.fill(0x00000000);
QPainter painter(&image);
if(useAntialiasing)
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(front);
painter.setFont(font);
painter.drawText(0, 0, w, h, Qt::AlignLeft, str);
painter.end();
Gui::BitmapFactory().convert(image, this->image);
}
void SoDatumLabel::computeBBox(SoAction *action, SbBox3f &box, SbVec3f &center)
{
if (!this->bbox.isEmpty()) {
// Set the bounding box using stored parameters
box.setBounds(this->bbox.getMin(),this->bbox.getMax() );
SbVec3f bbcenter = this->bbox.getCenter();
center.setValue(bbcenter[0], bbcenter[1], bbcenter[2]);
}
}
void SoDatumLabel::generatePrimitives(SoAction * action)
{
// Initialisation check (needs something more sensible) prevents an infinite loop bug
if(this->imgHeight <= FLT_EPSILON || this->imgWidth <= FLT_EPSILON)
return;
// Get the points stored
const SbVec3f *pnts = this->pnts.getValues(0);
SbVec3f p1 = pnts[0];
SbVec3f p2 = pnts[1];
// Change the offset and bounding box parameters depending on Datum Type
if(this->datumtype.getValue() == DISTANCE || this->datumtype.getValue() == DISTANCEX || this->datumtype.getValue() == DISTANCEY ){
float length = this->param1.getValue();
float length2 = this->param2.getValue();
SbVec3f dir, norm;
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();
norm = SbVec3f (-dir[1],dir[0],0);
float normproj12 = (p2-p1).dot(norm);
SbVec3f p1_ = p1 + normproj12 * norm;
SbVec3f midpos = (p1_ + p2)/2;
// Get magnitude of angle between horizontal
float angle = atan2f(dir[1],dir[0]);
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
// Rotate through an angle
float s = sin(angle);
float c = cos(angle);
img1 = SbVec3f((img1[0] * c) - (img1[1] * s), (img1[0] * s) + (img1[1] * c), 0.f);
img2 = SbVec3f((img2[0] * c) - (img2[1] * s), (img2[0] * s) + (img2[1] * c), 0.f);
img3 = SbVec3f((img3[0] * c) - (img3[1] * s), (img3[0] * s) + (img3[1] * c), 0.f);
img4 = SbVec3f((img4[0] * c) - (img4[1] * s), (img4[0] * s) + (img4[1] * c), 0.f);
SbVec3f textOffset = midpos + norm * length + dir * length2;
img1 += textOffset;
img2 += textOffset;
img3 += textOffset;
img4 += textOffset;
// Primitive Shape is only for text as this should only be selectable
SoPrimitiveVertex pv;
this->beginShape(action, QUADS);
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
// Set coordinates
pv.setPoint( img1 );
shapeVertex(&pv);
pv.setPoint( img2 );
shapeVertex(&pv);
pv.setPoint( img3 );
shapeVertex(&pv);
pv.setPoint( img4 );
shapeVertex(&pv);
this->endShape();
} else if (this->datumtype.getValue() == RADIUS) {
SbVec3f dir = (p2-p1);
dir.normalize();
SbVec3f norm (-dir[1],dir[0],0);
float length = this->param1.getValue();
SbVec3f pos = p2 + length*dir;
float angle = atan2f(dir[1],dir[0]);
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
// Rotate through an angle
float s = sin(angle);
float c = cos(angle);
img1 = SbVec3f((img1[0] * c) - (img1[1] * s), (img1[0] * s) + (img1[1] * c), 0.f);
img2 = SbVec3f((img2[0] * c) - (img2[1] * s), (img2[0] * s) + (img2[1] * c), 0.f);
img3 = SbVec3f((img3[0] * c) - (img3[1] * s), (img3[0] * s) + (img3[1] * c), 0.f);
img4 = SbVec3f((img4[0] * c) - (img4[1] * s), (img4[0] * s) + (img4[1] * c), 0.f);
SbVec3f textOffset = pos;
img1 += textOffset;
img2 += textOffset;
img3 += textOffset;
img4 += textOffset;
// Primitive Shape is only for text as this should only be selectable
SoPrimitiveVertex pv;
this->beginShape(action, QUADS);
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
// Set coordinates
pv.setPoint( img1 );
shapeVertex(&pv);
pv.setPoint( img2 );
shapeVertex(&pv);
pv.setPoint( img3 );
shapeVertex(&pv);
pv.setPoint( img4 );
shapeVertex(&pv);
this->endShape();
} else if (this->datumtype.getValue() == ANGLE) {
// Only the angle intersection point is needed
SbVec3f p0 = pnts[0];
// Load the Paramaters
float length = this->param1.getValue();
float startangle = this->param2.getValue();
float range = this->param3.getValue();
float endangle = startangle + range;
float r = 2*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 * r;
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
img1 += textOffset;
img2 += textOffset;
img3 += textOffset;
img4 += textOffset;
// Primitive Shape is only for text as this should only be selectable
SoPrimitiveVertex pv;
this->beginShape(action, QUADS);
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
// Set coordinates
pv.setPoint( img1 );
shapeVertex(&pv);
pv.setPoint( img2 );
shapeVertex(&pv);
pv.setPoint( img3 );
shapeVertex(&pv);
pv.setPoint( img4 );
shapeVertex(&pv);
this->endShape();
} else if (this->datumtype.getValue() == SYMMETRIC) {
// Get the Scale
SoState *state = action->getState();
const SbViewVolume & vv = SoViewVolumeElement::get(state);
float scale = vv.getWorldToScreenScale(SbVec3f(0.f,0.f,0.f), 0.4f);
SbVec3f dir = (p2-p1);
dir.normalize();
SbVec3f norm (-dir[1],dir[0],0);
float margin = 0.01f;
margin *= scale;
// Calculate coordinates for the first arrow
SbVec3f ar0, ar1, ar2;
ar0 = p1 + dir * 5 * margin;
ar1 = ar0 - dir * 0.866f * 2 * margin; // Base Point of Arrow
ar2 = ar1 + norm * margin; // Triangular corners
ar1 -= norm * margin;
// Calculate coordinates for the second arrow
SbVec3f ar3, ar4, ar5;
ar3 = p2 - dir * 5 * margin;
ar4 = ar3 + dir * 0.866f * 2 * margin; // Base Point of 2nd Arrow
ar5 = ar4 + norm * margin; // Triangular corners
ar4 -= norm * margin;
SoPrimitiveVertex pv;
this->beginShape(action, TRIANGLES);
pv.setNormal( SbVec3f(0.f, 0.f, 1.f) );
// Set coordinates
pv.setPoint( ar0 );
shapeVertex(&pv);
pv.setPoint( ar1 );
shapeVertex(&pv);
pv.setPoint( ar2 );
shapeVertex(&pv);
// Set coordinates
pv.setPoint( ar3 );
shapeVertex(&pv);
pv.setPoint( ar4 );
shapeVertex(&pv);
pv.setPoint( ar5 );
shapeVertex(&pv);
this->endShape();
}
}
void SoDatumLabel::notify(SoNotList * l)
{
SoField * f = l->getLastField();
if (f == &this->string) {
this->glimagevalid = false;
}
else if (f == &this->textColor) {
this->glimagevalid = false;
}
else if (f == &this->name) {
this->glimagevalid = false;
}
else if (f == &this->size) {
this->glimagevalid = false;
}
else if (f == &this->image) {
this->glimagevalid = false;
}
inherited::notify(l);
}
void SoDatumLabel::GLRender(SoGLRenderAction * action)
{
SoState *state = action->getState();
if (!shouldGLRender(action))
return;
if (action->handleTransparency(true))
return;
// Get the Scale
const SbViewVolume & vv = SoViewVolumeElement::get(state);
float scale = vv.getWorldToScreenScale(SbVec3f(0.f,0.f,0.f), 0.4f);
const SbString* s = string.getValues(0);
bool hasText = (s->getLength() > 0) ? true : false;
SbVec2s size;
int nc;
int srcw, srch;
if (hasText) {
if (!this->glimagevalid) {
drawImage();
this->glimagevalid = true;
}
const unsigned char * dataptr = this->image.getValue(size, nc);
if (dataptr == NULL) return; // no image
srcw = size[0];
srch = size[1];
float aspectRatio = (float) srcw / (float) srch;
this->imgHeight = scale / (float) srch;
this->imgWidth = aspectRatio * (float) this->imgHeight;
}
// Get the points stored in the pnt field
const SbVec3f *pnts = this->pnts.getValues(0);
state->push();
//Set General OpenGL Properties
glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_COLOR_BUFFER_BIT);
glDisable(GL_LIGHTING);
//Enable Anti-alias
if(action->isSmoothing())
{
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
}
// Position for Datum Text Label
float angle;
SbVec3f textOffset;
// Get the colour
const SbColor& t = textColor.getValue();
// Set GL Properties
glLineWidth(2.f);
glColor3f(t[0], t[1], t[2]);
if(this->datumtype.getValue() == DISTANCE || this->datumtype.getValue() == DISTANCEX || this->datumtype.getValue() == DISTANCEY )
{
float length = this->param1.getValue();
float length2 = this->param2.getValue();
const SbVec3f *pnts = this->pnts.getValues(0);
SbVec3f p1 = pnts[0];
SbVec3f p2 = pnts[1];
SbVec3f dir, norm;
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();
norm = SbVec3f (-dir[1],dir[0],0);
// when the datum line is not parallel to p1-p2 the projection of
// p1-p2 on norm is not zero, p2 is considered as reference and p1
// is replaced by its projection p1_
float normproj12 = (p2-p1).dot(norm);
SbVec3f p1_ = p1 + normproj12 * norm;
SbVec3f midpos = (p1_ + p2)/2;
float offset1 = (length + normproj12 < 0) ? -0.02 : 0.02;
float offset2 = (length < 0) ? -0.02 : 0.02;
// Get magnitude of angle between horizontal
angle = atan2f(dir[1],dir[0]);
bool flip=false;
if (angle > M_PI_2+M_PI/12) {
angle -= (float)M_PI;
flip = true;
} else if (angle <= -M_PI_2+M_PI/12) {
angle += (float)M_PI;
flip = true;
}
textOffset = midpos + norm * 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 = 0.01f;
margin *= scale;
SbVec3f perp1 = p1_ + norm * (length + offset1 * scale);
SbVec3f perp2 = p2 + norm * (length + offset2 * scale);
// Calculate the coordinates for the parallel datum lines
SbVec3f par1 = p1_ + norm * length;
SbVec3f par2 = midpos + norm * length + dir * (length2 - this->imgWidth / 2 - margin);
SbVec3f par3 = midpos + norm * length + dir * (length2 + this->imgWidth / 2 + margin);
SbVec3f par4 = p2 + norm * length;
bool flipTriang = false;
if ((par3-par1).dot(dir) > (par4 - par1).length()) {
// Increase Margin to improve visability
float tmpMargin = 0.08f * scale;
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 = 0.08f * scale;
par2 = par1;
if((par3-par1).dot(dir) < 0.f) {
par2 = par3;
par3 = par4 + dir * tmpMargin;
flipTriang = true;
}
}
// Perp Lines
glBegin(GL_LINES);
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();
SbVec3f ar1 = par1 + ((flipTriang) ? -1 : 1) * dir * 0.866f * 2 * margin;
SbVec3f ar2 = ar1 + norm * margin;
ar1 -= norm * margin;
SbVec3f ar3 = par4 - ((flipTriang) ? -1 : 1) * dir * 0.866f * 2 * margin;
SbVec3f ar4 = ar3 + norm * margin ;
ar3 -= norm * margin;
//Draw a pretty arrowhead (Equilateral) (Eventually could be improved to other shapes?)
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();
// BOUNDING BOX CALCULATION - IMPORTANT
// Finds the mins and maxes
std::vector<SbVec3f> 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 * (this->imgWidth / 2 + margin) + norm * (this->imgHeight + margin));
corners.push_back(textOffset - dir * (this->imgWidth / 2 + margin) + norm * (this->imgHeight + margin));
corners.push_back(textOffset + dir * (this->imgWidth / 2 + margin) - norm * margin);
corners.push_back(textOffset - dir * (this->imgWidth / 2 + margin) - norm * margin);
float minX = p1[0], minY = p1[1], maxX = p1[0] , maxY = p1[1];
for (std::vector<SbVec3f>::const_iterator it=corners.begin(); it != corners.end(); ++it) {
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
this->bbox.setBounds(SbVec3f(minX, minY, 0.f), SbVec3f (maxX, maxY, 0.f));
} else if (this->datumtype.getValue() == RADIUS) {
// Get the Points
SbVec3f p1 = pnts[0];
SbVec3f p2 = pnts[1];
SbVec3f dir = (p2-p1);
dir.normalize();
SbVec3f norm (-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]);
bool flip=false;
if (angle > M_PI_2+M_PI/12) {
angle -= (float)M_PI;
flip = true;
} else if (angle <= -M_PI_2+M_PI/12) {
angle += (float)M_PI;
flip = true;
}
textOffset = pos;
float margin = 0.01f;
margin *= scale;
// Create the arrowhead
SbVec3f ar0 = p2;
SbVec3f ar1 = p2 - dir * 0.866f * 2 * margin;
SbVec3f ar2 = ar1 + norm * margin;
ar1 -= norm * margin;
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();
// BOUNDING BOX CALCULATION - IMPORTANT
// Finds the mins and maxes
std::vector<SbVec3f> corners;
corners.push_back(p1);
corners.push_back(p2);
corners.push_back(pnt1);
corners.push_back(pnt2);
float minX = p1[0], minY = p1[1], maxX = p1[0] , maxY = p1[1];
for (std::vector<SbVec3f>::const_iterator it=corners.begin(); it != corners.end(); ++it) {
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
this->bbox.setBounds(SbVec3f(minX, minY, 0.f), SbVec3f (maxX, maxY, 0.f));
} else if (this->datumtype.getValue() == ANGLE) {
// Only the angle intersection point is needed
SbVec3f p0 = pnts[0];
// Load the Paramaters
float length = this->param1.getValue();
float startangle = this->param2.getValue();
float range = this->param3.getValue();
float endangle = startangle + range;
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;
float margin = 0.01f;
margin *= scale;
// Draw
glBegin(GL_LINE_STRIP);
int i=0;
for (; 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);
i=0;
for (; 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-margin)*v1;
SbVec3f pnt2 = p0+(r+margin)*v1;
SbVec3f pnt3 = p0+(r-margin)*v2;
SbVec3f pnt4 = p0+(r+margin)*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();
// BOUNDING BOX CALCULATION - IMPORTANT
// Finds the mins and maxes
// We may need to include the text position too
SbVec3f img1 = SbVec3f(-this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img2 = SbVec3f(-this->imgWidth / 2, this->imgHeight / 2, 0.f);
SbVec3f img3 = SbVec3f( this->imgWidth / 2, -this->imgHeight / 2, 0.f);
SbVec3f img4 = SbVec3f( this->imgWidth / 2, this->imgHeight / 2, 0.f);
img1 += textOffset;
img2 += textOffset;
img3 += textOffset;
img4 += textOffset;
std::vector<SbVec3f> 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);
float minX = pnt1[0], minY = pnt1[1], maxX = pnt1[0] , maxY = pnt1[1];
for (std::vector<SbVec3f>::const_iterator it=corners.begin(); it != corners.end(); ++it) {
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
this->bbox.setBounds(SbVec3f(minX, minY, 0.f), SbVec3f (maxX, maxY, 0.f));
} else if (this->datumtype.getValue() == SYMMETRIC) {
SbVec3f p1 = pnts[0];
SbVec3f p2 = pnts[1];
SbVec3f dir = (p2-p1);
dir.normalize();
SbVec3f norm (-dir[1],dir[0],0);
float margin = 0.01f;
margin *= scale;
// Calculate coordinates for the first arrow
SbVec3f ar0, ar1, ar2;
ar0 = p1 + dir * 4 * margin; // Tip of Arrow
ar1 = ar0 - dir * 0.866f * 2 * margin;
ar2 = ar1 + norm * margin;
ar1 -= norm * margin;
glBegin(GL_LINES);
glVertex2f(p1[0],p1[1]);
glVertex2f(ar0[0],ar0[1]);
glVertex2f(ar0[0],ar0[1]);
glVertex2f(ar1[0],ar1[1]);
glVertex2f(ar0[0],ar0[1]);
glVertex2f(ar2[0],ar2[1]);
glEnd();
// Calculate coordinates for the second arrow
SbVec3f ar3, ar4, ar5;
ar3 = p2 - dir * 4 * margin; // Tip of 2nd Arrow
ar4 = ar3 + dir * 0.866f * 2 * margin;
ar5 = ar4 + norm * margin;
ar4 -= norm * margin;
glBegin(GL_LINES);
glVertex2f(p2[0],p2[1]);
glVertex2f(ar3[0],ar3[1]);
glVertex2f(ar3[0], ar3[1]);
glVertex2f(ar4[0], ar4[1]);
glVertex2f(ar3[0], ar3[1]);
glVertex2f(ar5[0], ar5[1]);
glEnd();
// BOUNDING BOX CALCULATION - IMPORTANT
// Finds the mins and maxes
std::vector<SbVec3f> corners;
corners.push_back(p1);
corners.push_back(p2);
float minX = p1[0], minY = p1[1], maxX = p1[0] , maxY = p1[1];
for (std::vector<SbVec3f>::iterator it=corners.begin(); it != corners.end(); ++it) {
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
this->bbox.setBounds(SbVec3f(minX, minY, 0.f), SbVec3f (maxX, maxY, 0.f));
}
if (hasText) {
const unsigned char * dataptr = this->image.getValue(size, nc);
//Get the camera z-direction
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 = (const char*)(glGetString(GL_EXTENSIONS));
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 image(w, h,QImage::Format_ARGB32_Premultiplied);
image.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*)image.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);
}
glPopAttrib();
state->pop();
}