1
/***************************************************************************
2
* Copyright (c) 2013 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"
28
# include <QGraphicsView>
30
# include <QPainterPath>
36
#include <App/Application.h>
37
#include <Base/Console.h>
38
#include <Base/Parameter.h>
39
#include <Mod/TechDraw/App/DrawUtil.h>
41
#include "PreferencesGui.h"
43
#include <QByteArrayMatcher>
44
#include "QGCustomRect.h"
45
#include "QGCustomSvg.h"
47
#include "QGIPrimPath.h"
51
using namespace TechDrawGui;
52
using namespace TechDraw;
54
QGIFace::QGIFace(int index) :
56
m_hideSvgTiles(false),
60
// setFillMode(NoFill);
62
setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
64
setStyle(Qt::NoPen); //don't draw face lines, just fill for debugging
65
//setStyle(Qt::DashLine);
66
m_geomColor = PreferencesGui::getAccessibleQColor(QColor(Qt::black));
67
m_styleCurrent = Qt::NoPen;
68
m_pen.setStyle(m_styleCurrent);
69
setLineWeight(0.0); //0 = cosmetic
72
m_texture = QPixmap(); //empty texture
74
m_svgHatchArea = new QGCustomRect();
75
m_svgHatchArea->setParentItem(this);
77
m_svgCol = SVGCOLDEFAULT;
82
// set up style & colour defaults
83
m_colDefFill = App::Color(static_cast<uint32_t>(Preferences::getPreferenceGroup("Colors")->GetUnsigned("FaceColor", 0xFFFFFF)))
85
m_colDefFill.setAlpha(Preferences::getPreferenceGroup("Colors")->GetBool("ClearFace", false) ? 0 : 255);
87
m_fillDef = Qt::SolidPattern;
88
m_fillSelect = Qt::SolidPattern;
90
setFillMode(m_colDefFill.alpha() ? PlainFill : NoFill);
91
setFill(m_colDefFill, m_fillDef);
93
m_sharedRender = new QSvgRenderer();
94
m_patMaker = new PATPathMaker(this, 1.0, 1.0);
99
delete m_sharedRender;
106
// Base::Console().Message("QGIF::draw - pen style: %d\n", m_pen.style());
107
setPath(m_outline); //Face boundary
110
if (m_mode == GeomHatchFill) {
111
//GeomHatch does not appear in pdf if clipping is set to true
112
setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
113
if (!m_lineSets.empty()) {
114
m_brush.setTexture(QPixmap());
115
m_fillStyleCurrent = m_fillDef;
116
m_fillNormal = m_fillStyleCurrent;
117
for (auto& ls: m_lineSets) {
118
lineSetToFillItems(ls);
121
m_svgHatchArea->hide();
122
} else if (m_mode == SvgFill) {
123
m_brush.setTexture(QPixmap());
124
m_fillNormal = m_fillDef;
125
m_fillStyleCurrent = m_fillNormal;
126
loadSvgHatch(m_fileSpec);
127
//SVG tiles need to be clipped
128
setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
130
m_svgHatchArea->show();
131
} else if (m_mode == BitmapFill) {
132
m_fillStyleCurrent = Qt::TexturePattern;
133
m_texture = textureFromBitmap(m_fileSpec);
134
m_brush.setTexture(m_texture);
135
m_svgHatchArea->hide();
136
} else if (m_mode == PlainFill) {
137
setFill(m_colNormalFill, m_fillNormal);
138
m_svgHatchArea->hide();
141
// face is not hatched
142
m_svgHatchArea->hide();
147
/// show the face style & colour in normal configuration
148
void QGIFace::setPrettyNormal() {
149
// Base::Console().Message("QGIF::setPrettyNormal() - hatched: %d\n", isHatched());
151
(m_mode == BitmapFill) ) { //hatch with bitmap fill
152
m_fillStyleCurrent = Qt::TexturePattern;
153
m_brush.setTexture(m_texture);
155
m_brush.setTexture(QPixmap());
157
QGIPrimPath::setPrettyNormal();
160
/// show the face style & colour in pre-select configuration
161
void QGIFace::setPrettyPre() {
162
// Base::Console().Message("QGIF::setPrettyPre()\n");
163
m_fillStyleCurrent = Qt::SolidPattern;
164
m_brush.setTexture(QPixmap());
165
QGIPrimPath::setPrettyPre();
168
/// show the face style & colour in selected configuration
169
void QGIFace::setPrettySel() {
170
// Base::Console().Message("QGIF::setPrettySel()\n");
171
m_fillStyleCurrent = Qt::SolidPattern;
172
m_brush.setTexture(QPixmap());
173
QGIPrimPath::setPrettySel();
176
/// show or hide the edges of this face. Usually just for debugging
177
void QGIFace::setDrawEdges(bool b) {
178
// Base::Console().Message("QGIF::setDrawEdges(%d)\n", b);
180
setStyle(Qt::DashLine);
182
setStyle(Qt::NoPen); //don't draw face lines, just fill
186
void QGIFace::setHatchFile(std::string fileSpec)
188
m_fileSpec = fileSpec;
191
/// get the .svg file to use for hatching this face
192
void QGIFace::loadSvgHatch(std::string fileSpec)
194
QString qfs(QString::fromUtf8(fileSpec.data(), fileSpec.size()));
196
if (!f.open(QFile::ReadOnly | QFile::Text)) {
197
Base::Console().Error("QGIFace could not read %s\n", fileSpec.c_str());
200
m_svgXML = f.readAll();
202
// search in the file for the "stroke" specification in order to find out what declaration style is used
203
// this is necessary to apply a color set by the user to the SVG
204
QByteArray pattern("stroke:");
205
QByteArrayMatcher matcher(pattern);
207
if (matcher.indexIn(m_svgXML, pos) != -1) {
208
SVGCOLPREFIX = "stroke:"; // declaration part of a style="" statement
210
SVGCOLPREFIX = "stroke=\""; // declaration of its own
214
void QGIFace::setFillMode(QGIFace::fillMode m)
217
if ((m_mode == NoFill) ||
218
(m_mode == PlainFill)) {
225
/// update the outline of this face
226
void QGIFace::setOutline(const QPainterPath & path)
231
/// remove the PAT hatch lines
232
void QGIFace::clearLineSets()
238
/// add PAT hatch line set
239
void QGIFace::addLineSet(LineSet& ls)
241
m_lineSets.push_back(ls);
244
/// convert the PAT line set to QGraphicsPathItems
245
void QGIFace::lineSetToFillItems(LineSet& ls)
247
m_patMaker->setLineWidth(Rez::guiX(m_geomWeight));
248
m_patMaker->setScale(m_fillScale);
249
m_patMaker->setPen(setGeomPen());
250
m_patMaker->lineSetToFillItems(ls);
253
QPen QGIFace::setGeomPen()
256
result.setWidthF(Rez::guiX(m_geomWeight));
257
result.setColor(m_geomColor);
258
result.setStyle(Qt::SolidLine);
263
//! get zoom level (scale) from QGraphicsView
265
double QGIFace::getXForm()
267
//try to keep the pattern the same when View scales
270
auto vs = s->views(); //ptrs to views
273
auto i = v->transform().inverted();
280
/// remove the children that make up a PAT fill
281
void QGIFace::clearFillItems()
283
for (auto& f: m_fillItems) {
284
f->setParentItem(nullptr);
285
this->scene()->removeItem(f);
290
/// debugging tool draws a mark at a position on this face
291
void QGIFace::makeMark(double x, double y)
293
QGICMark* cmItem = new QGICMark(-1);
294
cmItem->setParentItem(this);
295
cmItem->setPos(x, y);
296
cmItem->setThick(1.0);
297
cmItem->setSize(40.0);
298
cmItem->setZValue(ZVALUE::VERTEX);
301
/// make an array of svg tiles to cover this face
302
void QGIFace::buildSvgHatch()
304
// Base::Console().Message("QGIF::buildSvgHatch() - offset: %s\n", DrawUtil::formatVector(getHatchOffset()).c_str());
305
double wTile = SVGSIZEW * m_fillScale;
306
double hTile = SVGSIZEH * m_fillScale;
307
double faceWidth = m_outline.boundingRect().width();
308
double faceHeight = m_outline.boundingRect().height();
309
double faceOverlaySize = Preferences::svgHatchFactor() * std::max(faceWidth, faceHeight);
310
QPointF faceCenter = m_outline.boundingRect().center();
311
double tilesWide = ceil(faceOverlaySize / wTile);
312
double tilesHigh = ceil(faceOverlaySize / hTile);
314
double overlayWidth = tilesWide * wTile;
315
double overlayHeight = tilesHigh * hTile;
316
m_svgHatchArea->setRect(0., 0., overlayWidth,-overlayHeight);
317
m_svgHatchArea->centerAt(faceCenter);
318
QByteArray before, after;
319
before = QString::fromStdString(SVGCOLPREFIX + SVGCOLDEFAULT).toUtf8();
320
after = QString::fromStdString(SVGCOLPREFIX + m_svgCol).toUtf8();
321
QByteArray colorXML = m_svgXML.replace(before, after);
322
if (!m_sharedRender->load(colorXML)) {
323
Base::Console().Message("QGIF::buildSvgHatch - failed to load svg string\n");
326
long int tileCount = 0;
327
for (int iw = 0; iw < int(tilesWide); iw++) {
328
for (int ih = 0; ih < int(tilesHigh); ih++) {
329
QGCustomSvg* tile = new QGCustomSvg();
330
tile->setScale(m_fillScale);
331
tile->setSharedRenderer(m_sharedRender);
332
tile->setParentItem(m_svgHatchArea);
333
tile->setPos(iw*wTile + getHatchOffset().x,
334
-overlayWidth + ih*hTile + getHatchOffset().y);
336
if (tileCount > m_maxTile) {
337
Base::Console().Warning("SVG tile count exceeded: %ld. Change hatch scale or raise limit.\n", tileCount);
341
if (tileCount > m_maxTile) {
345
QPointF faceCenterToMRect = mapToItem(m_svgHatchArea, faceCenter);
346
m_svgHatchArea->setTransformOriginPoint(faceCenterToMRect);
347
m_svgHatchArea->setRotation(m_hatchRotation);
350
void QGIFace::clearSvg()
355
//this isn't used currently
356
QPixmap QGIFace::textureFromSvg(std::string fileSpec)
358
QString qs(QString::fromStdString(fileSpec));
360
if (!ffi.isReadable()) {
363
QSvgRenderer renderer(qs);
364
QPixmap pixMap(renderer.defaultSize());
365
pixMap.fill(Qt::white); //try Qt::transparent?
366
QPainter painter(&pixMap);
367
renderer.render(&painter); //svg texture -> bitmap
368
return pixMap.scaled(m_fillScale, m_fillScale);
371
void QGIFace::setHatchColor(App::Color c)
373
m_svgCol = c.asHexString();
374
m_geomColor = c.asValue<QColor>();
377
void QGIFace::setHatchScale(double s)
382
/// turn svg tiles on or off. QtSvg does not handle clipping,
383
/// so we must be able to turn the hatching on/off when exporting a face with an
384
/// svg hatch. Otherwise the full tile pattern is shown in the export.
385
/// NOTE: there appears to have been a change in Qt that it now clips svg items
386
void QGIFace::hideSvg(bool b)
392
/// create a QPixmap from a bitmap file. The QPixmap will be used as a QBrush
394
QPixmap QGIFace::textureFromBitmap(std::string fileSpec)
398
QString qfs(QString::fromUtf8(fileSpec.data(), fileSpec.size()));
400
if (!f.open(QFile::ReadOnly)) {
401
Base::Console().Error("QGIFace could not read %s\n", fileSpec.c_str());
404
QByteArray bytes = f.readAll();
405
pix.loadFromData(bytes);
406
if (m_hatchRotation != 0.0) {
408
rotator.rotate(m_hatchRotation);
409
pix = pix.transformed(rotator);
414
void QGIFace::setLineWeight(double w) {
418
void QGIFace::getParameters()
420
m_maxSeg = Preferences::getPreferenceGroup("PAT")->GetInt("MaxSeg", 10000l);
421
m_maxTile = Preferences::getPreferenceGroup("Decorations")->GetInt("MaxSVGTile", 10000l);
424
QRectF QGIFace::boundingRect() const
426
return shape().controlPointRect();
429
QPainterPath QGIFace::shape() const