Files
create/src/Mod/TechDraw/Gui/QGIViewPart.cpp
Sebastian Bachmann c41363c117 TechDraw: new center/section draw style
using custom QT pen styles, the center and section lines look much
better.
With some math we are also able to control the middle position, thus
the centerlines will always look good, regardless of the size of an
object.

Also getting the section label size from the settings, so the font
size is controllable and not hardcoded.

Bonus: adding blank templates for the common paper sizes (as the
        Drawing WB has them)
2018-02-24 16:58:17 +01:00

1015 lines
37 KiB
C++

/***************************************************************************
* Copyright (c) 2013 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_
#include <cmath>
#include <qmath.h>
#include <QAction>
#include <QApplication>
#include <QContextMenuEvent>
#include <QGraphicsScene>
#include <QMenu>
#include <QMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include <QPainterPathStroker>
#include <QPainter>
#include <QTextOption>
#include <QBitmap>
#include <QImage>
#include <QString>
#include <QSvgRenderer>
#endif // #ifndef _PreComp_
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/Material.h>
#include <Base/Console.h>
#include <Base/Vector3D.h>
#include <Gui/ViewProvider.h>
#include <Mod/TechDraw/App/DrawUtil.h>
#include <Mod/TechDraw/App/DrawViewPart.h>
#include <Mod/TechDraw/App/DrawViewSection.h>
#include <Mod/TechDraw/App/DrawHatch.h>
#include <Mod/TechDraw/App/DrawGeomHatch.h>
#include <Mod/TechDraw/App/DrawViewDetail.h>
#include "Rez.h"
#include "ZVALUE.h"
#include "QGIFace.h"
#include "QGIEdge.h"
#include "QGIVertex.h"
#include "QGICMark.h"
#include "QGISectionLine.h"
#include "QGICenterLine.h"
#include "QGIHighlight.h"
#include "QGCustomBorder.h"
#include "QGCustomLabel.h"
#include "QGCustomRect.h"
#include "QGIMatting.h"
#include "QGIViewPart.h"
#include "ViewProviderGeomHatch.h"
#include "ViewProviderHatch.h"
#include "ViewProviderViewPart.h"
using namespace TechDrawGui;
using namespace TechDrawGeometry;
const float lineScaleFactor = Rez::guiX(1.); // temp fiddle for devel
QGIViewPart::QGIViewPart()
{
setCacheMode(QGraphicsItem::NoCache);
setHandlesChildEvents(false);
setAcceptHoverEvents(true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges,true);
showSection = false;
}
QGIViewPart::~QGIViewPart()
{
tidy();
}
QVariant QGIViewPart::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemSelectedHasChanged && scene()) {
QList<QGraphicsItem*> items = childItems();
for(QList<QGraphicsItem*>::iterator it = items.begin(); it != items.end(); ++it) {
//Highlight the children if this is highlighted!? seems to mess up Face selection?
QGIEdge *edge = dynamic_cast<QGIEdge *>(*it);
QGIVertex *vert = dynamic_cast<QGIVertex *>(*it);
QGIFace *face = dynamic_cast<QGIFace *>(*it);
if(edge) {
//edge->setHighlighted(isSelected());
} else if(vert){
//vert->setHighlighted(isSelected());
} else if(face){
//face->setHighlighted(isSelected());
}
}
} else if(change == ItemSceneChange && scene()) {
tidy();
}
return QGIView::itemChange(change, value);
}
//obs?
void QGIViewPart::tidy()
{
//Delete any leftover items
for(QList<QGraphicsItem*>::iterator it = deleteItems.begin(); it != deleteItems.end(); ++it) {
delete *it;
}
deleteItems.clear();
}
void QGIViewPart::setViewPartFeature(TechDraw::DrawViewPart *obj)
{
if (!obj)
return;
// called from QGVPage
setViewFeature(static_cast<TechDraw::DrawView *>(obj));
draw();
}
QPainterPath QGIViewPart::drawPainterPath(TechDrawGeometry::BaseGeom *baseGeom) const
{
double rot = getViewObject()->Rotation.getValue();
return geomToPainterPath(baseGeom,rot);
}
QPainterPath QGIViewPart::geomToPainterPath(TechDrawGeometry::BaseGeom *baseGeom, double rot)
{
Q_UNUSED(rot);
QPainterPath path;
switch(baseGeom->geomType) {
case TechDrawGeometry::CIRCLE: {
TechDrawGeometry::Circle *geom = static_cast<TechDrawGeometry::Circle *>(baseGeom);
double x = geom->center.x - geom->radius;
double y = geom->center.y - geom->radius;
path.addEllipse(Rez::guiX(x),
Rez::guiX(y),
Rez::guiX(geom->radius * 2),
Rez::guiX(geom->radius * 2)); //topleft@(x,y) radx,rady
} break;
case TechDrawGeometry::ARCOFCIRCLE: {
TechDrawGeometry::AOC *geom = static_cast<TechDrawGeometry::AOC *>(baseGeom);
pathArc(path,
Rez::guiX(geom->radius),
Rez::guiX(geom->radius),
0.,
geom->largeArc,
geom->cw,
Rez::guiX(geom->endPnt.x),
Rez::guiX(geom->endPnt.y),
Rez::guiX(geom->startPnt.x),
Rez::guiX(geom->startPnt.y));
} break;
case TechDrawGeometry::ELLIPSE: {
TechDrawGeometry::Ellipse *geom = static_cast<TechDrawGeometry::Ellipse *>(baseGeom);
// Calculate start and end points as ellipse with theta = 0 and pi
double startX = geom->center.x + geom->major * cos(geom->angle),
startY = geom->center.y + geom->major * sin(geom->angle),
endX = geom->center.x - geom->major * cos(geom->angle),
endY = geom->center.y - geom->major * sin(geom->angle);
pathArc(path,
Rez::guiX(geom->major),
Rez::guiX(geom->minor),
geom->angle,
false,
false,
Rez::guiX(endX),
Rez::guiX(endY),
Rez::guiX(startX),
Rez::guiX(startY));
pathArc(path,
Rez::guiX(geom->major),
Rez::guiX(geom->minor),
geom->angle,
false,
false,
Rez::guiX(startX),
Rez::guiX(startY),
Rez::guiX(endX),
Rez::guiX(endY));
} break;
case TechDrawGeometry::ARCOFELLIPSE: {
TechDrawGeometry::AOE *geom = static_cast<TechDrawGeometry::AOE *>(baseGeom);
pathArc(path,
Rez::guiX(geom->major),
Rez::guiX(geom->minor),
geom->angle,
geom->largeArc,
geom->cw,
Rez::guiX(geom->endPnt.x),
Rez::guiX(geom->endPnt.y),
Rez::guiX(geom->startPnt.x),
Rez::guiX(geom->startPnt.y));
} break;
case TechDrawGeometry::BEZIER: {
TechDrawGeometry::BezierSegment *geom = static_cast<TechDrawGeometry::BezierSegment *>(baseGeom);
// Move painter to the beginning
path.moveTo(Rez::guiX(geom->pnts[0].x), Rez::guiX(geom->pnts[0].y));
if ( geom->poles == 2 ) {
// Degree 1 bezier = straight line...
path.lineTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y));
} else if ( geom->poles == 3 ) {
path.quadTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y),
Rez::guiX(geom->pnts[2].x), Rez::guiX(geom->pnts[2].y));
} else if ( geom->poles == 4 ) {
path.cubicTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y),
Rez::guiX(geom->pnts[2].x), Rez::guiX(geom->pnts[2].y),
Rez::guiX(geom->pnts[3].x), Rez::guiX(geom->pnts[3].y));
} else { //can only handle lines,quads,cubes
Base::Console().Error("Bad pole count (%d) for BezierSegment\n",geom->poles);
auto itBez = geom->pnts.begin() + 1;
for (; itBez != geom->pnts.end();itBez++) {
path.lineTo(Rez::guiX((*itBez).x), Rez::guiX((*itBez).y)); //show something for debugging
}
}
} break;
case TechDrawGeometry::BSPLINE: {
TechDrawGeometry::BSpline *geom = static_cast<TechDrawGeometry::BSpline *>(baseGeom);
std::vector<TechDrawGeometry::BezierSegment>::const_iterator it = geom->segments.begin();
// Move painter to the beginning of our first segment
path.moveTo(Rez::guiX(it->pnts[0].x), Rez::guiX(it->pnts[0].y));
for ( ; it != geom->segments.end(); ++it) {
// At this point, the painter is either at the beginning
// of the first segment, or end of the last
if ( it->poles == 2 ) {
// Degree 1 bezier = straight line...
path.lineTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y));
} else if ( it->poles == 3 ) {
path.quadTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y),
Rez::guiX(it->pnts[2].x), Rez::guiX(it->pnts[2].y));
} else if ( it->poles == 4 ) {
path.cubicTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y),
Rez::guiX(it->pnts[2].x), Rez::guiX(it->pnts[2].y),
Rez::guiX(it->pnts[3].x), Rez::guiX(it->pnts[3].y));
} else { //can only handle lines,quads,cubes
Base::Console().Error("Bad pole count (%d) for BezierSegment of B-spline geometry\n",it->poles);
path.lineTo(it->pnts[1].x, it->pnts[1].y); //show something for debugging
}
}
} break;
case TechDrawGeometry::GENERIC: {
TechDrawGeometry::Generic *geom = static_cast<TechDrawGeometry::Generic *>(baseGeom);
path.moveTo(Rez::guiX(geom->points[0].x), Rez::guiX(geom->points[0].y));
std::vector<Base::Vector2d>::const_iterator it = geom->points.begin();
for(++it; it != geom->points.end(); ++it) {
path.lineTo(Rez::guiX((*it).x), Rez::guiX((*it).y));
}
} break;
default:
Base::Console().Error("Error - geomToPainterPath - UNKNOWN geomType: %d\n",baseGeom->geomType);
break;
}
//old rotate path logic. now done on App side.
// if (rot != 0.0) {
// QTransform t;
// t.rotate(-rot);
// path = t.map(path);
// }
return path;
}
void QGIViewPart::updateView(bool update)
{
auto viewPart( dynamic_cast<TechDraw::DrawViewPart *>(getViewObject()) );
if( viewPart == nullptr ) {
return;
}
auto vp = static_cast<ViewProviderViewPart*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
QGIView::updateView(update);
if (update ||
viewPart->isTouched() ||
viewPart->Source.isTouched() ||
viewPart->Direction.isTouched() ||
viewPart->Rotation.isTouched() ||
viewPart->Scale.isTouched() ||
viewPart->HardHidden.isTouched() ||
viewPart->SmoothVisible.isTouched() ||
viewPart->SeamVisible.isTouched() ||
viewPart->IsoVisible.isTouched() ||
viewPart->SmoothHidden.isTouched() ||
viewPart->SeamHidden.isTouched() ||
viewPart->IsoHidden.isTouched() ||
viewPart->IsoCount.isTouched() ) {
draw();
} else if (update ||
vp->LineWidth.isTouched() ||
vp->HiddenWidth.isTouched()) {
QList<QGraphicsItem*> items = childItems();
for(QList<QGraphicsItem*>::iterator it = items.begin(); it != items.end(); ++it) {
QGIEdge *edge = dynamic_cast<QGIEdge *>(*it);
if(edge && edge->getHiddenEdge()) {
edge->setWidth(vp->HiddenWidth.getValue() * lineScaleFactor);
} else if (edge){
edge->setWidth(vp->LineWidth.getValue() * lineScaleFactor);
}
}
draw();
} else {
QGIView::draw();
}
}
void QGIViewPart::draw() {
drawViewPart();
drawMatting();
QGIView::draw();
}
void QGIViewPart::drawViewPart()
{
auto viewPart( dynamic_cast<TechDraw::DrawViewPart *>(getViewObject()) );
if ( viewPart == nullptr ) {
return;
}
if (!viewPart->hasGeometry()) {
return;
}
auto vp = static_cast<ViewProviderViewPart*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
float lineWidth = vp->LineWidth.getValue() * lineScaleFactor;
float lineWidthHid = vp->HiddenWidth.getValue() * lineScaleFactor;
float lineWidthIso = vp->IsoWidth.getValue() * lineScaleFactor;
// float lineWidthExtra = viewPart->ExtraWidth.getValue() * lineScaleFactor;
prepareGeometryChange();
removePrimitives(); //clean the slate
removeDecorations();
#if MOD_TECHDRAW_HANDLE_FACES
if (viewPart->handleFaces()) {
// Draw Faces
std::vector<TechDraw::DrawHatch*> hatchObjs = viewPart->getHatches();
std::vector<TechDraw::DrawGeomHatch*> geomObjs = viewPart->getGeomHatches();
const std::vector<TechDrawGeometry::Face *> &faceGeoms = viewPart->getFaceGeometry();
std::vector<TechDrawGeometry::Face *>::const_iterator fit = faceGeoms.begin();
for(int i = 0 ; fit != faceGeoms.end(); fit++, i++) {
QGIFace* newFace = drawFace(*fit,i);
newFace->isHatched(false);
newFace->setFillMode(QGIFace::PlainFill);
TechDraw::DrawHatch* fHatch = faceIsHatched(i,hatchObjs);
TechDraw::DrawGeomHatch* fGeom = faceIsGeomHatched(i,geomObjs);
if (fGeom) {
const std::vector<std::string> &sourceNames = fGeom->Source.getSubValues();
if (!sourceNames.empty()) {
int fdx = TechDraw::DrawUtil::getIndexFromName(sourceNames.at(0));
std::vector<LineSet> lineSets = fGeom->getTrimmedLines(fdx);
if (!lineSets.empty()) {
newFace->clearLineSets();
for (auto& ls: lineSets) {
newFace->addLineSet(ls);
}
newFace->isHatched(true);
newFace->setFillMode(QGIFace::GeomHatchFill);
newFace->setHatchScale(fGeom->ScalePattern.getValue());
newFace->setHatchFile(fGeom->FilePattern.getValue());
Gui::ViewProvider* gvp = QGIView::getViewProvider(fGeom);
ViewProviderGeomHatch* geomVp = dynamic_cast<ViewProviderGeomHatch*>(gvp);
if (geomVp != nullptr) {
newFace->setHatchColor(geomVp->ColorPattern.getValue());
newFace->setLineWeight(geomVp->WeightPattern.getValue());
}
}
}
} else if (fHatch) {
if (!fHatch->HatchPattern.isEmpty()) {
newFace->isHatched(true);
newFace->setFillMode(QGIFace::FromFile);
newFace->setHatchFile(fHatch->HatchPattern.getValue());
Gui::ViewProvider* gvp = QGIView::getViewProvider(fHatch);
ViewProviderHatch* hatchVp = dynamic_cast<ViewProviderHatch*>(gvp);
if (hatchVp != nullptr) {
newFace->setHatchScale(hatchVp->HatchScale.getValue());
newFace->setHatchColor(hatchVp->HatchColor.getValue());
}
}
}
bool drawEdges = getFaceEdgesPref();
newFace->setDrawEdges(drawEdges); //pref. for debugging only
newFace->setZValue(ZVALUE::FACE);
newFace->draw();
newFace->setPrettyNormal();
}
}
#endif //#if MOD_TECHDRAW_HANDLE_FACES
// Draw Edges
const std::vector<TechDrawGeometry::BaseGeom *> &geoms = viewPart->getEdgeGeometry();
std::vector<TechDrawGeometry::BaseGeom *>::const_iterator itEdge = geoms.begin();
QGIEdge* item;
for(int i = 0 ; itEdge != geoms.end(); itEdge++, i++) {
bool showEdge = false;
if ((*itEdge)->visible) {
if (((*itEdge)->classOfEdge == ecHARD) ||
((*itEdge)->classOfEdge == ecOUTLINE) ||
(((*itEdge)->classOfEdge == ecSMOOTH) && viewPart->SmoothVisible.getValue()) ||
(((*itEdge)->classOfEdge == ecSEAM) && viewPart->SeamVisible.getValue()) ||
(((*itEdge)->classOfEdge == ecUVISO) && viewPart->IsoVisible.getValue())) {
showEdge = true;
}
} else {
if ( (((*itEdge)->classOfEdge == ecHARD) && (viewPart->HardHidden.getValue())) ||
(((*itEdge)->classOfEdge == ecOUTLINE) && (viewPart->HardHidden.getValue())) ||
(((*itEdge)->classOfEdge == ecSMOOTH) && (viewPart->SmoothHidden.getValue())) ||
(((*itEdge)->classOfEdge == ecSEAM) && (viewPart->SeamHidden.getValue())) ||
(((*itEdge)->classOfEdge == ecUVISO) && (viewPart->IsoHidden.getValue())) ) {
showEdge = true;
}
}
if (showEdge) {
item = new QGIEdge(i);
addToGroup(item); //item is at scene(0,0), not group(0,0)
item->setPos(0.0,0.0); //now at group(0,0)
item->setPath(drawPainterPath(*itEdge));
item->setWidth(lineWidth);
item->setZValue(ZVALUE::EDGE);
if(!(*itEdge)->visible) {
item->setWidth(lineWidthHid);
item->setHiddenEdge(true);
item->setZValue(ZVALUE::HIDEDGE);
}
if ((*itEdge)->classOfEdge == ecUVISO) {
item->setWidth(lineWidthIso);
}
item->setPrettyNormal();
//debug a path
// QPainterPath edgePath=drawPainterPath(*itEdge);
// std::stringstream edgeId;
// edgeId << "QGIVP.edgePath" << i;
// dumpPath(edgeId.str().c_str(),edgePath);
}
}
// Draw Vertexs:
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->
GetGroup("Preferences")->GetGroup("Mod/TechDraw/General");
double vertexScaleFactor = hGrp->GetFloat("VertexScale", 3.0);
bool usePolygonHLR = viewPart->CoarseView.getValue();
const std::vector<TechDrawGeometry::Vertex *> &verts = viewPart->getVertexGeometry();
std::vector<TechDrawGeometry::Vertex *>::const_iterator vert = verts.begin();
bool showCenters = vp->ArcCenterMarks.getValue();
double cAdjust = vp->CenterScale.getValue();
for(int i = 0 ; vert != verts.end(); ++vert, i++) {
if ((*vert)->isCenter) {
if (showCenters) {
QGICMark* cmItem = new QGICMark(i);
addToGroup(cmItem);
cmItem->setPos(Rez::guiX((*vert)->pnt.x), Rez::guiX((*vert)->pnt.y));
cmItem->setThick(0.5 * lineWidth); //need minimum?
cmItem->setSize( cAdjust * lineWidth * vertexScaleFactor);
cmItem->setZValue(ZVALUE::VERTEX);
}
} else if(!usePolygonHLR){ //Disable dots WHEN usePolygonHLR
QGIVertex *item = new QGIVertex(i);
addToGroup(item);
item->setPos(Rez::guiX((*vert)->pnt.x), Rez::guiX((*vert)->pnt.y));
item->setRadius(lineWidth * vertexScaleFactor);
item->setZValue(ZVALUE::VERTEX);
}
}
//draw section line
if (vp->ShowSectionLine.getValue()) {
auto refs = viewPart->getSectionRefs();
for (auto& r:refs) {
drawSectionLine(r, true);
}
}
//draw detail highlights
auto drefs = viewPart->getDetailRefs();
for (auto& r:drefs) {
drawHighlight(r, true);
}
//draw center lines
drawCenterLines(true);
}
QGIFace* QGIViewPart::drawFace(TechDrawGeometry::Face* f, int idx)
{
std::vector<TechDrawGeometry::Wire *> fWires = f->wires;
QPainterPath facePath;
for(std::vector<TechDrawGeometry::Wire *>::iterator wire = fWires.begin(); wire != fWires.end(); ++wire) {
QPainterPath wirePath;
for(std::vector<TechDrawGeometry::BaseGeom *>::iterator edge = (*wire)->geoms.begin(); edge != (*wire)->geoms.end(); ++edge) {
//Save the start Position
QPainterPath edgePath = drawPainterPath(*edge);
// If the current end point matches the shape end point the new edge path needs reversing
QPointF shapePos = (wirePath.currentPosition()- edgePath.currentPosition());
if(sqrt(shapePos.x() * shapePos.x() + shapePos.y()*shapePos.y()) < 0.05) { //magic tolerance
edgePath = edgePath.toReversed();
}
wirePath.connectPath(edgePath);
}
//dumpPath("wirePath:",wirePath);
facePath.addPath(wirePath);
}
facePath.setFillRule(Qt::OddEvenFill);
QGIFace* gFace = new QGIFace(idx);
addToGroup(gFace);
gFace->setPos(0.0,0.0);
gFace->setOutline(facePath);
//debug a path
//std::stringstream faceId;
//faceId << "facePath " << idx;
//dumpPath(faceId.str().c_str(),facePath);
return gFace;
}
//! Remove all existing QGIPrimPath items(Vertex,Edge,Face)
void QGIViewPart::removePrimitives()
{
QList<QGraphicsItem*> children = childItems();
for (auto& c:children) {
QGIPrimPath* prim = dynamic_cast<QGIPrimPath*>(c);
if (prim) {
removeFromGroup(prim);
scene()->removeItem(prim);
delete prim;
}
}
}
//! Remove all existing QGIDecoration items(SectionLine,SectionMark,...)
void QGIViewPart::removeDecorations()
{
QList<QGraphicsItem*> children = childItems();
for (auto& c:children) {
QGIDecoration* decor = dynamic_cast<QGIDecoration*>(c);
QGIMatting* mat = dynamic_cast<QGIMatting*>(c);
if (decor) {
removeFromGroup(decor);
scene()->removeItem(decor);
delete decor;
} else if (mat) {
removeFromGroup(mat);
scene()->removeItem(mat);
delete mat;
}
}
}
void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b)
{
TechDraw::DrawViewPart *viewPart = static_cast<TechDraw::DrawViewPart *>(getViewObject());
if (!viewPart ||
!viewSection) {
return;
}
if (!viewSection->hasGeometry()) {
return;
}
auto vp = static_cast<ViewProviderViewPart*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
if (b) {
QGISectionLine* sectionLine = new QGISectionLine();
addToGroup(sectionLine);
sectionLine->setSymbol(const_cast<char*>(viewSection->SectionSymbol.getValue()));
//TODO: handle oblique section lines?
//find smallest internal angle(normalDir,get?Dir()) and use -1*get?Dir() +/- angle
//Base::Vector3d normalDir = viewSection->SectionNormal.getValue();
Base::Vector3d arrowDir(0,1,0); //for drawing only, not geom
Base::Vector3d lineDir(1,0,0);
bool horiz = false;
if (viewSection->SectionDirection.isValue("Right")) {
arrowDir = Base::Vector3d(1,0,0);
lineDir = Base::Vector3d(0,1,0);
} else if (viewSection->SectionDirection.isValue("Left")) {
arrowDir = Base::Vector3d(-1,0,0);
lineDir = Base::Vector3d(0,-1,0);
} else if (viewSection->SectionDirection.isValue("Up")) {
arrowDir = Base::Vector3d(0,1,0);
lineDir = Base::Vector3d(1,0,0);
horiz = true;
} else if (viewSection->SectionDirection.isValue("Down")) {
arrowDir = Base::Vector3d(0,-1,0);
lineDir = Base::Vector3d(-1,0,0);
horiz = true;
}
sectionLine->setDirection(arrowDir.x,arrowDir.y);
Base::Vector3d org = viewSection->SectionOrigin.getValue();
double scale = viewPart->getScale();
Base::Vector3d pOrg = scale * viewPart->projectPoint(org);
//now project pOrg onto arrowDir
Base::Vector3d displace;
displace.ProjectToLine(pOrg, arrowDir);
Base::Vector3d offset = pOrg + displace;
sectionLine->setPos(Rez::guiX(offset.x),Rez::guiX(offset.y));
double sectionSpan;
double sectionFudge = Rez::guiX(10.0);
double xVal, yVal;
double fontSize = getPrefFontSize();
if (horiz) {
sectionSpan = m_border->rect().width() + sectionFudge;
xVal = sectionSpan / 2.0;
yVal = 0.0;
} else {
sectionSpan = (m_border->rect().height() - m_label->boundingRect().height()) + sectionFudge;
xVal = 0.0;
yVal = sectionSpan / 2.0;
}
sectionLine->setBounds(-xVal,-yVal,xVal,yVal);
sectionLine->setWidth(Rez::guiX(vp->LineWidth.getValue()));
sectionLine->setFont(m_font, fontSize);
sectionLine->setZValue(ZVALUE::SECTIONLINE);
sectionLine->setRotation(viewPart->Rotation.getValue());
sectionLine->draw();
}
}
void QGIViewPart::drawCenterLines(bool b)
{
TechDraw::DrawViewPart *viewPart = dynamic_cast<TechDraw::DrawViewPart *>(getViewObject());
if (!viewPart) {
return;
}
auto vp = static_cast<ViewProviderViewPart*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
if (b) {
bool horiz = vp->HorizCenterLine.getValue();
bool vert = vp->VertCenterLine.getValue();
QGICenterLine* centerLine;
double sectionSpan;
double sectionFudge = Rez::guiX(10.0);
double xVal, yVal;
if (horiz) {
centerLine = new QGICenterLine();
addToGroup(centerLine);
centerLine->setPos(0.0,0.0);
sectionSpan = m_border->rect().width() + sectionFudge;
xVal = sectionSpan / 2.0;
yVal = 0.0;
centerLine->setIntersection(horiz && vert);
centerLine->setBounds(-xVal,-yVal,xVal,yVal);
centerLine->setWidth(Rez::guiX(vp->HiddenWidth.getValue()));
centerLine->setZValue(ZVALUE::SECTIONLINE);
centerLine->setRotation(viewPart->Rotation.getValue());
centerLine->draw();
}
if (vert) {
centerLine = new QGICenterLine();
addToGroup(centerLine);
centerLine->setPos(0.0,0.0);
sectionSpan = (m_border->rect().height() - m_label->boundingRect().height()) + sectionFudge;
xVal = 0.0;
yVal = sectionSpan / 2.0;
centerLine->setIntersection(horiz && vert);
centerLine->setBounds(-xVal,-yVal,xVal,yVal);
centerLine->setWidth(Rez::guiX(vp->HiddenWidth.getValue()));
centerLine->setZValue(ZVALUE::SECTIONLINE);
centerLine->setRotation(viewPart->Rotation.getValue());
centerLine->draw();
}
}
}
void QGIViewPart::drawHighlight(TechDraw::DrawViewDetail* viewDetail, bool b)
{
TechDraw::DrawViewPart *viewPart = static_cast<TechDraw::DrawViewPart *>(getViewObject());
if (!viewPart ||
!viewDetail) {
return;
}
if (!viewDetail->hasGeometry()) {
return;
}
auto vp = static_cast<ViewProviderViewPart*>(getViewProvider(getViewObject()));
if ( vp == nullptr ) {
return;
}
if (b) {
QGIHighlight* highlight = new QGIHighlight();
addToGroup(highlight);
highlight->setPos(0.0,0.0); //sb setPos(center.x,center.y)?
highlight->setReference(const_cast<char*>(viewDetail->Reference.getValue()));
Base::Vector3d center = viewDetail->AnchorPoint.getValue() * viewPart->getScale();
double radius = viewDetail->Radius.getValue() * viewPart->getScale();
highlight->setBounds(center.x - radius, center.y + radius,center.x + radius, center.y - radius);
highlight->setWidth(Rez::guiX(vp->IsoWidth.getValue()));
highlight->setFont(m_font,Rez::guiX(6.0));
highlight->setZValue(ZVALUE::HIGHLIGHT);
highlight->draw();
}
}
void QGIViewPart::drawMatting()
{
auto viewPart( dynamic_cast<TechDraw::DrawViewPart *>(getViewObject()) );
TechDraw::DrawViewDetail* dvd = nullptr;
if (viewPart && viewPart->isDerivedFrom(TechDraw::DrawViewDetail::getClassTypeId())) {
dvd = static_cast<TechDraw::DrawViewDetail*>(viewPart);
} else {
return;
}
double scale = dvd->getScale();
double radius = dvd->Radius.getValue() * scale;
QGIMatting* mat = new QGIMatting();
addToGroup(mat);
mat->setRadius(Rez::guiX(radius));
mat->setPos(0.0,0.0);
mat->draw();
mat->show();
}
// As called by arc of ellipse case:
// pathArc(path, geom->major, geom->minor, geom->angle, geom->largeArc, geom->cw,
// geom->endPnt.x, geom->endPnt.y,
// geom->startPnt.x, geom->startPnt.y);
void QGIViewPart::pathArc(QPainterPath &path, double rx, double ry, double x_axis_rotation,
bool large_arc_flag, bool sweep_flag,
double x, double y,
double curx, double cury)
{
double sin_th, cos_th;
double a00, a01, a10, a11;
double x0, y0, x1, y1, xc, yc;
double d, sfactor, sfactor_sq;
double th0, th1, th_arc;
int i, n_segs;
double dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
rx = qAbs(rx);
ry = qAbs(ry);
sin_th = qSin(x_axis_rotation);
cos_th = qCos(x_axis_rotation);
dx = (curx - x) / 2.0;
dy = (cury - y) / 2.0;
dx1 = cos_th * dx + sin_th * dy;
dy1 = -sin_th * dx + cos_th * dy;
Pr1 = rx * rx;
Pr2 = ry * ry;
Px = dx1 * dx1;
Py = dy1 * dy1;
/* Spec : check if radii are large enough */
check = Px / Pr1 + Py / Pr2;
if (check > 1) {
rx = rx * qSqrt(check);
ry = ry * qSqrt(check);
}
a00 = cos_th / rx;
a01 = sin_th / rx;
a10 = -sin_th / ry;
a11 = cos_th / ry;
x0 = a00 * curx + a01 * cury;
y0 = a10 * curx + a11 * cury;
x1 = a00 * x + a01 * y;
y1 = a10 * x + a11 * y;
/* (x0, y0) is current point in transformed coordinate space.
(x1, y1) is new point in transformed coordinate space.
The arc fits a unit-radius circle in this space.
*/
d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
sfactor_sq = 1.0 / d - 0.25;
if (sfactor_sq < 0)
sfactor_sq = 0;
sfactor = qSqrt(sfactor_sq);
if (sweep_flag == large_arc_flag)
sfactor = -sfactor;
xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
/* (xc, yc) is center of the circle. */
th0 = qAtan2(y0 - yc, x0 - xc);
th1 = qAtan2(y1 - yc, x1 - xc);
th_arc = th1 - th0;
if (th_arc < 0 && sweep_flag)
th_arc += 2 * M_PI;
else if (th_arc > 0 && !sweep_flag)
th_arc -= 2 * M_PI;
n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001)));
path.moveTo(curx, cury);
for (i = 0; i < n_segs; i++) {
pathArcSegment(path, xc, yc,
th0 + i * th_arc / n_segs,
th0 + (i + 1) * th_arc / n_segs,
rx, ry, x_axis_rotation);
}
}
void QGIViewPart::pathArcSegment(QPainterPath &path,
double xc, double yc,
double th0, double th1,
double rx, double ry, double xAxisRotation)
{
double sinTh, cosTh;
double a00, a01, a10, a11;
double x1, y1, x2, y2, x3, y3;
double t;
double thHalf;
sinTh = qSin(xAxisRotation);
cosTh = qCos(xAxisRotation);
a00 = cosTh * rx;
a01 = -sinTh * ry;
a10 = sinTh * rx;
a11 = cosTh * ry;
thHalf = 0.5 * (th1 - th0);
t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
x1 = xc + qCos(th0) - t * qSin(th0);
y1 = yc + qSin(th0) + t * qCos(th0);
x3 = xc + qCos(th1);
y3 = yc + qSin(th1);
x2 = x3 + t * qSin(th1);
y2 = y3 - t * qCos(th1);
path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
}
void QGIViewPart::toggleCache(bool state)
{
QList<QGraphicsItem*> items = childItems();
for(QList<QGraphicsItem*>::iterator it = items.begin(); it != items.end(); it++) {
//(*it)->setCacheMode((state)? DeviceCoordinateCache : NoCache); //TODO: fiddle cache settings if req'd for performance
Q_UNUSED(state);
(*it)->setCacheMode(NoCache);
(*it)->update();
}
}
void QGIViewPart::toggleCosmeticLines(bool state)
{
QList<QGraphicsItem*> items = childItems();
for(QList<QGraphicsItem*>::iterator it = items.begin(); it != items.end(); it++) {
QGIEdge *edge = dynamic_cast<QGIEdge *>(*it);
if(edge) {
edge->setCosmetic(state);
}
}
}
void QGIViewPart::toggleVertices(bool state)
{
QList<QGraphicsItem*> items = childItems();
for(QList<QGraphicsItem*>::iterator it = items.begin(); it != items.end(); it++) {
QGIVertex *vert = dynamic_cast<QGIVertex *>(*it);
QGICMark *mark = dynamic_cast<QGICMark *>(*it);
if(vert) {
if (!mark) { //leave center marks showing
if(state)
vert->show();
else
vert->hide();
}
}
}
}
TechDraw::DrawHatch* QGIViewPart::faceIsHatched(int i,std::vector<TechDraw::DrawHatch*> hatchObjs) const
{
TechDraw::DrawHatch* result = nullptr;
for (auto& h:hatchObjs) {
const std::vector<std::string> &sourceNames = h->Source.getSubValues();
int fdx = TechDraw::DrawUtil::getIndexFromName(sourceNames.at(0));
if (fdx == i) {
result = h;
break;
}
}
return result;
}
TechDraw::DrawGeomHatch* QGIViewPart::faceIsGeomHatched(int i,std::vector<TechDraw::DrawGeomHatch*> geomObjs) const
{
TechDraw::DrawGeomHatch* result = nullptr;
for (auto& h:geomObjs) {
const std::vector<std::string> &sourceNames = h->Source.getSubValues();
int fdx = TechDraw::DrawUtil::getIndexFromName(sourceNames.at(0));
if (fdx == i) {
result = h;
break;
}
}
return result;
}
void QGIViewPart::dumpPath(const char* text,QPainterPath path)
{
QPainterPath::Element elem;
Base::Console().Message(">>>%s has %d elements\n",text,path.elementCount());
char* typeName;
for(int iElem = 0; iElem < path.elementCount(); iElem++) {
elem = path.elementAt(iElem);
if(elem.isMoveTo()) {
typeName = "MoveTo";
} else if (elem.isLineTo()) {
typeName = "LineTo";
} else if (elem.isCurveTo()) {
typeName = "CurveTo";
} else {
typeName = "CurveData";
}
Base::Console().Message(">>>>> element %d: type:%d/%s pos(%.3f,%.3f) M:%d L:%d C:%d\n",iElem,
elem.type,typeName,elem.x,elem.y,elem.isMoveTo(),elem.isLineTo(),elem.isCurveTo());
}
}
QRectF QGIViewPart::boundingRect() const
{
return childrenBoundingRect();
}
//QGIViewPart derived classes do not need a rotate view method as rotation is handled on App side.
void QGIViewPart::rotateView(void)
{
}
bool QGIViewPart::getFaceEdgesPref(void)
{
bool result = false;
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/General");
result = hGrp->GetBool("DrawFaceEdges", 0l);
return result;
}
double QGIViewPart::getPrefFontSize()
{
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Labels");
double fontSize = hGrp->GetFloat("LabelSize", 5.0);
return Rez::guiX(fontSize);
}