Seamly2D
Code documentation
vobjengine.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2017 Seamly, LLC *
4  * *
5  * https://github.com/fashionfreedom/seamly2d *
6  * *
7  ***************************************************************************
8  **
9  ** Seamly2D is free software: you can redistribute it and/or modify
10  ** it under the terms of the GNU General Public License as published by
11  ** the Free Software Foundation, either version 3 of the License, or
12  ** (at your option) any later version.
13  **
14  ** Seamly2D is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  ** GNU General Public License for more details.
18  **
19  ** You should have received a copy of the GNU General Public License
20  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
21  **
22  **************************************************************************
23 
24  ************************************************************************
25  **
26  ** @file vobjengine.cpp
27  ** @author Roman Telezhynskyi <dismine(at)gmail.com>
28  ** @date 12 12, 2014
29  **
30  ** @brief
31  ** @copyright
32  ** This source code is part of the Valentine project, a pattern making
33  ** program, whose allow create and modeling patterns of clothing.
34  ** Copyright (C) 2013-2015 Seamly2D project
35  ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved.
36  **
37  ** Seamly2D is free software: you can redistribute it and/or modify
38  ** it under the terms of the GNU General Public License as published by
39  ** the Free Software Foundation, either version 3 of the License, or
40  ** (at your option) any later version.
41  **
42  ** Seamly2D is distributed in the hope that it will be useful,
43  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
44  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45  ** GNU General Public License for more details.
46  **
47  ** You should have received a copy of the GNU General Public License
48  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
49  **
50  *************************************************************************/
51 
52 #include "vobjengine.h"
53 
54 #include <QByteArray>
55 #include <QFlag>
56 #include <QFlags>
57 #include <QIODevice>
58 #include <QLatin1Char>
59 #include <QMessageLogger>
60 #include <QPaintEngineState>
61 #include <QPainterPath>
62 #include <QPointF>
63 #include <QString>
64 #include <QTextStream>
65 #include <QVector>
66 #include <QtDebug>
67 
68 #include "../vmisc/diagnostic.h"
69 #include "../vmisc/vmath.h"
70 
71 class QPaintDevice;
72 class QPixmap;
73 class QPoint;
74 class QPointF;
75 class QPolygonF;
76 class QRectF;
77 
78 #ifdef Q_CC_MSVC
79  #include <ciso646>
80 #endif /* Q_CC_MSVC */
81 
82 //---------------------------------------------------------------------------------------------------------------------
83 static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures()
84 {
85 QT_WARNING_PUSH
86 QT_WARNING_DISABLE_CLANG("-Wsign-conversion")
87 QT_WARNING_DISABLE_INTEL(68)
88 QT_WARNING_DISABLE_INTEL(2022)
89 
90  return QPaintEngine::PaintEngineFeatures(
91  QPaintEngine::AllFeatures
92  & ~QPaintEngine::PatternBrush
93  & ~QPaintEngine::PerspectiveTransform
94  & ~QPaintEngine::ConicalGradientFill
95  & ~QPaintEngine::PorterDuff);
96 
98 }
99 
100 //---------------------------------------------------------------------------------------------------------------------
102  : QPaintEngine(svgEngineFeatures())
103  , stream()
104  , globalPointsCount(0)
105  , outputDevice()
106  , planeCount(0)
107  , size()
108  , resolution(96)
109  , transform()
110 {
111  for (int i=0; i < MAX_POINTS; i++)
112  {
113  points[i].x = 0;
114  points[i].y = 0;
115  }
116 }
117 
118 #if defined(Q_CC_INTEL)
119 #pragma warning( pop )
120 #endif
121 
122 //---------------------------------------------------------------------------------------------------------------------
124 {
125 }
126 
127 //---------------------------------------------------------------------------------------------------------------------
128 bool VObjEngine::begin(QPaintDevice *pdev)
129 {
130  Q_UNUSED(pdev)
131  if (outputDevice.isNull())
132  {
133  qWarning("VObjEngine::begin(), no output device");
134  return false;
135  }
136  if (outputDevice->isOpen() == false)
137  {
138  if (outputDevice->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate) == false)
139  {
140  qWarning("VObjEngine::begin(), could not open output device: '%s'",
141  qPrintable(outputDevice->errorString()));
142  return false;
143  }
144  }
145  else if (outputDevice->isWritable() == false)
146  {
147  qWarning("VObjEngine::begin(), could not write to read-only output device: '%s'",
148  qPrintable(outputDevice->errorString()));
149  return false;
150  }
151 
152  if (size.isValid() == false)
153  {
154  qWarning()<<"VObjEngine::begin(), size is not valid";
155  return false;
156  }
157 
158  stream = QSharedPointer<QTextStream>(new QTextStream(outputDevice.data()));
159  *stream << "# Seamly2D OBJ File\n";
160  *stream << "# www.seamly2d-project.org/\n";
161  return true;
162 }
163 
164 //---------------------------------------------------------------------------------------------------------------------
166 {
167  stream.reset();
168  return true;
169 }
170 
171 //---------------------------------------------------------------------------------------------------------------------
172 // cppcheck-suppress unusedFunction
173 void VObjEngine::updateState(const QPaintEngineState &state)
174 {
175  QPaintEngine::DirtyFlags flags = state.state();
176 
177  // always stream full gstate, which is not required, but...
178  flags |= QPaintEngine::AllDirty;
179 
180 
181  if (flags & QPaintEngine::DirtyTransform)
182  {
183  transform = state.transform(); // Save new transform for moving paths
184  }
185 }
186 
187 //---------------------------------------------------------------------------------------------------------------------
188 void VObjEngine::drawPath(const QPainterPath &path)
189 {
190  QPolygonF polygon = path.toFillPolygon(transform);
191  polygon = MakePointsUnique(polygon);// Points must be unique
192  if (polygon.size() < 3)
193  {
194  return;
195  }
196 
197  qint64 sq = Square(polygon);
198 
199  ++planeCount;
200  *stream << "o Plane." << QString("%1").arg(planeCount, 3, 10, QLatin1Char('0')) << '\n';
201 
202  quint32 num_points = 0;
203 
204  for (int i=0; i < polygon.count(); i++)
205  {
206  if ( num_points < MAX_POINTS )
207  {
208  points[num_points].x = polygon.at(i).x();
209  points[num_points].y = polygon.at(i).y();
210  num_points++;
211  }
212  }
213 
214  int offset = 0;
215  delaunay2d_t *res = delaunay2d_from(points, num_points);//Calculate faces
216 
217  QPointF pf[MAX_POINTS];
218  bool skipFace=false;//Need skip first face
219 
220  for (quint32 i = 0; i < res->num_faces; i++ )
221  {
222  if (offset == 0)
223  {
224  skipFace=true;
225  }
226  else
227  {
228  skipFace=false;
229  }
230  int num_verts = static_cast<int>(res->faces[offset]);
231  offset++;
232  for ( int j = 0; j < num_verts; j++ )
233  {
234  int p0 = static_cast<int>(res->faces[offset + j]);
235  pf[j] = QPointF(points[p0].x, points[p0].y);
236  }
237  if (skipFace == false )
238  {
239  QPolygonF face;
240  for ( int i = 0; i < num_verts; i++ )
241  {
242  face << QPointF(pf[i]);
243  }
244  QPolygonF united = polygon.united(face);
245  qint64 sqUnited = Square(united);
246  if (sqUnited <= sq)
247  {// This face incide our base polygon.
248  drawPolygon(pf, num_verts, QPaintEngine::OddEvenMode);
249  }
250  }
251  offset += num_verts;
252  }
253 
254  delaunay2d_release(res);//Don't forget release data
255  *stream << "s off\n";
256 }
257 
258 //---------------------------------------------------------------------------------------------------------------------
259 void VObjEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
260 {
261  Q_UNUSED(mode)
262 
263  drawPoints(points, pointCount);
264  *stream << "f";
265 
266  for (int i = 0; i < pointCount; ++i)
267  {
268  *stream << QString(" %1").arg(static_cast<int>(globalPointsCount) - pointCount + i + 1);
269  }
270  *stream << '\n';
271 }
272 
273 //---------------------------------------------------------------------------------------------------------------------
274 void VObjEngine::drawPolygon(const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode)
275 {
276  QPaintEngine::drawPolygon(points, pointCount, mode);
277 }
278 
279 //---------------------------------------------------------------------------------------------------------------------
280 QPaintEngine::Type VObjEngine::type() const
281 {
282  return QPaintEngine::User;
283 }
284 
285 //---------------------------------------------------------------------------------------------------------------------
286 void VObjEngine::drawPoints(const QPointF *points, int pointCount)
287 {
288  for (int i = 0; i < pointCount; ++i)
289  {
290  qreal x = ((points[i].x() - 0)/qFloor(size.width()/2.0)) - 1.0;
291  qreal y = (((points[i].y() - 0)/qFloor(size.width()/2.0)) - 1.0)*-1;
292 
293  *stream << "v" << " " << QString::number(x, 'f', 6 ) << " " << QString::number(y, 'f', 6 ) << " "
294  << "0.000000\n";
296  }
297 }
298 
299 //---------------------------------------------------------------------------------------------------------------------
300 void VObjEngine::drawPoints(const QPoint *points, int pointCount)
301 {
302  QPaintEngine::drawPoints(points, pointCount);
303 }
304 
305 //---------------------------------------------------------------------------------------------------------------------
306 // cppcheck-suppress unusedFunction
307 void VObjEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
308 {
309  Q_UNUSED(r)
310  Q_UNUSED(pm)
311  Q_UNUSED(sr)
312 }
313 
314 //---------------------------------------------------------------------------------------------------------------------
315 QSize VObjEngine::getSize() const
316 {
317  return size;
318 }
319 
320 //---------------------------------------------------------------------------------------------------------------------
321 void VObjEngine::setSize(const QSize &value)
322 {
323  Q_ASSERT(not isActive());
324  size = value;
325 }
326 
327 //---------------------------------------------------------------------------------------------------------------------
328 QIODevice *VObjEngine::getOutputDevice() const
329 {
330  return outputDevice.data();
331 }
332 
333 //---------------------------------------------------------------------------------------------------------------------
334 void VObjEngine::setOutputDevice(QIODevice *value)
335 {
336  Q_ASSERT(not isActive());
337  outputDevice.reset(value);
338 }
339 
340 //---------------------------------------------------------------------------------------------------------------------
342 {
343  return resolution;
344 }
345 
346 //---------------------------------------------------------------------------------------------------------------------
348 {
349  Q_ASSERT(not isActive());
350  resolution = value;
351 }
352 
353 //---------------------------------------------------------------------------------------------------------------------
354 QPolygonF VObjEngine::MakePointsUnique(const QPolygonF &polygon) const
355 {
356  QVector<QPointF> set;
357  QPolygonF uniquePolygon;
358  for (int i=0; i < polygon.count(); i++)
359  {
360  if (set.contains(polygon.at(i)) == false)
361  {
362  set.append(polygon.at(i));
363  uniquePolygon.append(polygon.at(i));
364  }
365  }
366  return uniquePolygon;
367 }
368 
369 //---------------------------------------------------------------------------------------------------------------------
370 qint64 VObjEngine::Square(const QPolygonF &poly) const
371 {
372  QVector<qreal> x;
373  QVector<qreal> y;
374 
375  int n = poly.count();
376  qreal s, res = 0;
377  qint64 sq = 0;
378 
379  for (int i=0; i < n; i++)
380  {
381  x.append(poly.at(i).x());
382  y.append(poly.at(i).y());
383  }
384 
385  // Calculation a polygon area through the sum of the areas of trapezoids
386  for (int i = 0; i < n; i++)
387  {
388  if (i == 0)
389  {
390  s = x.at(i)*(y.at(n-1) - y.at(i+1)); //if i == 0, then y[i-1] replace on y[n-1]
391  res += s;
392  }
393  else
394  {
395  if (i == n-1)
396  {
397  s = x.at(i)*(y.at(i-1) - y.at(0)); // if i == n-1, then y[i+1] replace on y[0]
398  res += s;
399  }
400  else
401  {
402  s = x.at(i)*(y.at(i-1) - y.at(i+1));
403  res += s;
404  }
405  }
406  }
407  sq = qFloor(qAbs(res/2.0));
408  return sq;
409 }
virtual void drawPath(const QPainterPath &path) Q_DECL_OVERRIDE
Definition: vobjengine.cpp:188
int resolution
Definition: vobjengine.h:104
void setOutputDevice(QIODevice *value)
Definition: vobjengine.cpp:334
virtual void drawPoints(const QPointF *points, int pointCount) Q_DECL_OVERRIDE
Definition: vobjengine.cpp:286
int getResolution() const
Definition: vobjengine.cpp:341
virtual ~VObjEngine() Q_DECL_OVERRIDE
Definition: vobjengine.cpp:123
QTransform transform
Definition: vobjengine.h:105
virtual bool end() Q_DECL_OVERRIDE
Definition: vobjengine.cpp:165
virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) Q_DECL_OVERRIDE
Definition: vobjengine.cpp:307
qint64 Square(const QPolygonF &poly) const
Definition: vobjengine.cpp:370
virtual Type type() const Q_DECL_OVERRIDE
Definition: vobjengine.cpp:280
QSize getSize() const
Definition: vobjengine.cpp:315
quint32 planeCount
Definition: vobjengine.h:102
QPolygonF MakePointsUnique(const QPolygonF &polygon) const
Definition: vobjengine.cpp:354
virtual bool begin(QPaintDevice *pdev) Q_DECL_OVERRIDE
Definition: vobjengine.cpp:128
virtual void updateState(const QPaintEngineState &state) Q_DECL_OVERRIDE
Definition: vobjengine.cpp:173
QSharedPointer< QIODevice > outputDevice
Definition: vobjengine.h:100
void setSize(const QSize &value)
Definition: vobjengine.cpp:321
virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) Q_DECL_OVERRIDE
Definition: vobjengine.cpp:259
quint32 globalPointsCount
Definition: vobjengine.h:99
QIODevice * getOutputDevice() const
Definition: vobjengine.cpp:328
del_point2d_t points[512]
Definition: vobjengine.h:101
QSharedPointer< QTextStream > stream
Definition: vobjengine.h:98
QSize size
Definition: vobjengine.h:103
void setResolution(int value)
Definition: vobjengine.cpp:347
void delaunay2d_release(delaunay2d_t *del)
Definition: delaunay.cpp:1082
delaunay2d_t * delaunay2d_from(del_point2d_t *points, quint32 num_points)
Definition: delaunay.cpp:1008
quint32 * faces
Definition: delaunay.h:65
quint32 num_faces
Definition: delaunay.h:62
static QPaintEngine::PaintEngineFeatures svgEngineFeatures()
Definition: vobjengine.cpp:83
#define MAX_POINTS
Definition: vobjengine.h:68