1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libwps
3 * Version: MPL 2.0 / LGPLv2.1+
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * Major Contributor(s):
10 * Copyright (C) 2002, 2005 William Lachance (william.lachance@sympatico.ca)
11 * Copyright (C) 2002, 2004 Marc Maurer (uwog@uwog.net)
12 *
13 * For minor contributions see the git repository.
14 *
15 * Alternatively, the contents of this file may be used under the terms
16 * of the GNU Lesser General Public License Version 2.1 or later
17 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
18 * applicable instead of those above.
19 *
20 * For further information visit http://libwps.sourceforge.net
21 */
22
23 #include <string.h>
24
25 #include <cmath>
26 #include <cstring>
27 #include <iomanip>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
31
32 #include <librevenge/librevenge.h>
33
34 #include "libwps_internal.h"
35
36 #include "WPSGraphicShape.h"
37
38 ////////////////////////////////////////////////////////////
39 // WPSGraphicShape::PathData
40 ////////////////////////////////////////////////////////////
operator <<(std::ostream & o,WPSGraphicShape::PathData const & path)41 std::ostream &operator<<(std::ostream &o, WPSGraphicShape::PathData const &path)
42 {
43 o << path.m_type;
44 switch (path.m_type)
45 {
46 case 'H':
47 o << ":" << path.m_x[0];
48 break;
49 case 'V':
50 o << ":" << path.m_x[1];
51 break;
52 case 'M':
53 case 'L':
54 case 'T':
55 o << ":" << path.m_x;
56 break;
57 case 'Q':
58 case 'S':
59 o << ":" << path.m_x << ":" << path.m_x1;
60 break;
61 case 'C':
62 o << ":" << path.m_x << ":" << path.m_x1 << ":" << path.m_x2;
63 break;
64 case 'A':
65 o << ":" << path.m_x << ":r=" << path.m_r;
66 if (path.m_largeAngle) o << ":largeAngle";
67 if (path.m_sweep) o << ":sweep";
68 if (path.m_rotate<0 || path.m_rotate>0) o << ":rot=" << path.m_rotate;
69 break;
70 case 'Z':
71 break;
72 default:
73 o << "###";
74 }
75 return o;
76 }
77
translate(Vec2f const & decal)78 void WPSGraphicShape::PathData::translate(Vec2f const &decal)
79 {
80 if (m_type=='Z')
81 return;
82 m_x += decal;
83 if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
84 return;
85 m_x1 += decal;
86 if (m_type=='Q' || m_type=='S')
87 return;
88 m_x2 += decal;
89 }
90
scale(Vec2f const & scaling)91 void WPSGraphicShape::PathData::scale(Vec2f const &scaling)
92 {
93 if (m_type=='Z')
94 return;
95 m_x = Vec2f(m_x[0]*scaling[0], m_x[1]*scaling[1]);
96 if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
97 return;
98 m_x1 = Vec2f(m_x1[0]*scaling[0], m_x1[1]*scaling[1]);
99 if (m_type=='Q' || m_type=='S')
100 return;
101 m_x2 = Vec2f(m_x2[0]*scaling[0], m_x2[1]*scaling[1]);
102 }
103
rotate(float angle,Vec2f const & decal)104 void WPSGraphicShape::PathData::rotate(float angle, Vec2f const &decal)
105 {
106 if (m_type=='Z')
107 return;
108 float angl=angle*float(M_PI/180.);
109 m_x = Vec2f(std::cos(angl)*m_x[0]-std::sin(angl)*m_x[1],
110 std::sin(angl)*m_x[0]+std::cos(angl)*m_x[1])+decal;
111 if (m_type=='A')
112 {
113 m_rotate += angle;
114 return;
115 }
116 if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
117 return;
118 m_x1 = Vec2f(std::cos(angl)*m_x1[0]-std::sin(angl)*m_x1[1],
119 std::sin(angl)*m_x1[0]+std::cos(angl)*m_x1[1])+decal;
120 if (m_type=='Q' || m_type=='S')
121 return;
122 m_x2 = Vec2f(std::cos(angl)*m_x2[0]-std::sin(angl)*m_x2[1],
123 std::sin(angl)*m_x2[0]+std::cos(angl)*m_x2[1])+decal;
124 }
125
transform(WPSTransformation const & matrix,float rotation)126 void WPSGraphicShape::PathData::transform(WPSTransformation const &matrix, float rotation)
127 {
128 if (m_type=='Z')
129 return;
130 m_x = matrix*m_x;
131 if (m_type=='A')
132 {
133 m_rotate += rotation;
134 return;
135 }
136 if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
137 return;
138 m_x1 = matrix*m_x1;
139 if (m_type=='Q' || m_type=='S')
140 return;
141 m_x2 = matrix*m_x2;
142 }
143
get(librevenge::RVNGPropertyList & list,Vec2f const & orig) const144 bool WPSGraphicShape::PathData::get(librevenge::RVNGPropertyList &list, Vec2f const &orig) const
145 {
146 list.clear();
147 std::string type("");
148 type += m_type;
149 list.insert("librevenge:path-action", type.c_str());
150 if (m_type=='Z')
151 return true;
152 if (m_type=='H')
153 {
154 list.insert("svg:x",double(m_x[0]-orig[0]), librevenge::RVNG_POINT);
155 return true;
156 }
157 if (m_type=='V')
158 {
159 list.insert("svg:y",double(m_x[1]-orig[1]), librevenge::RVNG_POINT);
160 return true;
161 }
162 list.insert("svg:x",double(m_x[0]-orig[0]), librevenge::RVNG_POINT);
163 list.insert("svg:y",double(m_x[1]-orig[1]), librevenge::RVNG_POINT);
164 if (m_type=='M' || m_type=='L' || m_type=='T')
165 return true;
166 if (m_type=='A')
167 {
168 list.insert("svg:rx",double(m_r[0]), librevenge::RVNG_POINT);
169 list.insert("svg:ry",double(m_r[1]), librevenge::RVNG_POINT);
170 list.insert("librevenge:large-arc", m_largeAngle);
171 list.insert("librevenge:sweep", m_sweep);
172 list.insert("librevenge:rotate", double(m_rotate), librevenge::RVNG_GENERIC);
173 return true;
174 }
175 list.insert("svg:x1",double(m_x1[0]-orig[0]), librevenge::RVNG_POINT);
176 list.insert("svg:y1",double(m_x1[1]-orig[1]), librevenge::RVNG_POINT);
177 if (m_type=='Q' || m_type=='S')
178 return true;
179 list.insert("svg:x2",double(m_x2[0]-orig[0]), librevenge::RVNG_POINT);
180 list.insert("svg:y2",double(m_x2[1]-orig[1]), librevenge::RVNG_POINT);
181 if (m_type=='C')
182 return true;
183 WPS_DEBUG_MSG(("WPSGraphicShape::PathData::get: unknown command %c\n", m_type));
184 list.clear();
185 return false;
186 }
187
cmp(WPSGraphicShape::PathData const & a) const188 int WPSGraphicShape::PathData::cmp(WPSGraphicShape::PathData const &a) const
189 {
190 if (m_type < a.m_type) return 1;
191 if (m_type > a.m_type) return 1;
192 int diff = m_x.cmp(a.m_x);
193 if (diff) return diff;
194 diff = m_x1.cmp(a.m_x1);
195 if (diff) return diff;
196 diff = m_x2.cmp(a.m_x2);
197 if (diff) return diff;
198 diff = m_r.cmp(a.m_r);
199 if (diff) return diff;
200 if (m_rotate < a.m_rotate) return 1;
201 if (m_rotate > a.m_rotate) return -1;
202 if (m_largeAngle != a.m_largeAngle)
203 return m_largeAngle ? 1 : -1;
204 if (m_sweep != a.m_sweep)
205 return m_sweep ? 1 : -1;
206 return 0;
207 }
208
209 ////////////////////////////////////////////////////////////
210 // WPSGraphicShape
211 ////////////////////////////////////////////////////////////
line(Vec2f const & orig,Vec2f const & dest)212 WPSGraphicShape WPSGraphicShape::line(Vec2f const &orig, Vec2f const &dest)
213 {
214 WPSGraphicShape res;
215 res.m_type = WPSGraphicShape::Line;
216 res.m_vertices.resize(2);
217 res.m_vertices[0]=orig;
218 res.m_vertices[1]=dest;
219 Vec2f minPt(orig), maxPt(orig);
220 for (int c=0; c<2; ++c)
221 {
222 if (orig[c] < dest[c])
223 maxPt[c]=dest[c];
224 else
225 minPt[c]=dest[c];
226 }
227 res.m_bdBox=WPSBox2f(minPt,maxPt);
228 return res;
229 }
230
operator <<(std::ostream & o,WPSGraphicShape const & sh)231 std::ostream &operator<<(std::ostream &o, WPSGraphicShape const &sh)
232 {
233 o << "box=" << sh.m_bdBox << ",";
234 switch (sh.m_type)
235 {
236 case WPSGraphicShape::Line:
237 o << "line,";
238 if (sh.m_vertices.size()!=2)
239 o << "###pts,";
240 else
241 o << "pts=" << sh.m_vertices[0] << "<->" << sh.m_vertices[1] << ",";
242 break;
243 case WPSGraphicShape::Rectangle:
244 o << "rect,";
245 if (sh.m_formBox!=sh.m_bdBox)
246 o << "box[rect]=" << sh.m_formBox << ",";
247 if (sh.m_cornerWidth!=Vec2f(0,0))
248 o << "corners=" << sh.m_cornerWidth << ",";
249 break;
250 case WPSGraphicShape::Circle:
251 o << "circle,";
252 break;
253 case WPSGraphicShape::Arc:
254 case WPSGraphicShape::Pie:
255 o << (sh.m_type == WPSGraphicShape::Arc ? "arc," : "pie,");
256 o << "box[ellipse]=" << sh.m_formBox << ",";
257 o << "angle=" << sh.m_arcAngles << ",";
258 break;
259 case WPSGraphicShape::Polygon:
260 o << "polygons,pts=[";
261 for (auto const &pt : sh.m_vertices)
262 o << pt << ",";
263 o << "],";
264 break;
265 case WPSGraphicShape::Polyline:
266 o << "polyline,pts=[";
267 for (auto const &pt : sh.m_vertices)
268 o << pt << ",";
269 o << "],";
270 break;
271 case WPSGraphicShape::Path:
272 o << "path,pts=[";
273 for (auto const &pt : sh.m_path)
274 o << pt << ",";
275 o << "],";
276 break;
277 case WPSGraphicShape::ShapeUnknown:
278 default:
279 o << "###unknwown[shape],";
280 break;
281 }
282 o << sh.m_extra;
283 return o;
284 }
285
cmp(WPSGraphicShape const & a) const286 int WPSGraphicShape::cmp(WPSGraphicShape const &a) const
287 {
288 if (m_type < a.m_type) return 1;
289 if (m_type > a.m_type) return -1;
290 int diff = m_bdBox.cmp(a.m_bdBox);
291 if (diff) return diff;
292 diff = m_formBox.cmp(a.m_formBox);
293 if (diff) return diff;
294 diff = m_cornerWidth.cmp(a.m_cornerWidth);
295 if (diff) return diff;
296 diff = m_arcAngles.cmp(a.m_arcAngles);
297 if (diff) return diff;
298 if (m_vertices.size()<a.m_vertices.size()) return -1;
299 if (m_vertices.size()>a.m_vertices.size()) return -1;
300 for (size_t pt=0; pt < m_vertices.size(); ++pt)
301 {
302 diff = m_vertices[pt].cmp(a.m_vertices[pt]);
303 if (diff) return diff;
304 }
305 if (m_path.size()<a.m_path.size()) return -1;
306 if (m_path.size()>a.m_path.size()) return -1;
307 for (size_t pt=0; pt < m_path.size(); ++pt)
308 {
309 diff = m_path[pt].cmp(a.m_path[pt]);
310 if (diff) return diff;
311 }
312 return 0;
313 }
314
translate(Vec2f const & decal)315 void WPSGraphicShape::translate(Vec2f const &decal)
316 {
317 if (decal==Vec2f(0,0))
318 return;
319 m_bdBox=WPSBox2f(m_bdBox.min()+decal, m_bdBox.max()+decal);
320 m_formBox=WPSBox2f(m_formBox.min()+decal, m_formBox.max()+decal);
321 for (auto &pt : m_vertices)
322 pt+=decal;
323 for (auto &pt : m_path)
324 pt.translate(decal);
325 }
326
scale(Vec2f const & scaling)327 void WPSGraphicShape::scale(Vec2f const &scaling)
328 {
329 m_bdBox=WPSBox2f(Vec2f(scaling[0]*m_bdBox.min()[0],scaling[1]*m_bdBox.min()[1]),
330 Vec2f(scaling[0]*m_bdBox.max()[0],scaling[1]*m_bdBox.max()[1]));
331 m_formBox=WPSBox2f(Vec2f(scaling[0]*m_formBox.min()[0],scaling[1]*m_formBox.min()[1]),
332 Vec2f(scaling[0]*m_formBox.max()[0],scaling[1]*m_formBox.max()[1]));
333 for (auto &vertex : m_vertices)
334 vertex=Vec2f(scaling[0]*vertex[0],
335 scaling[1]*vertex[1]);
336 for (auto &pt : m_path)
337 pt.scale(scaling);
338 }
339
rotate(float angle,Vec2f const & center) const340 WPSGraphicShape WPSGraphicShape::rotate(float angle, Vec2f const ¢er) const
341 {
342 while (angle >= 360) angle -= 360;
343 while (angle <= -360) angle += 360;
344 if (angle >= -1e-3f && angle <= 1e-3f) return *this;
345 float angl=angle*float(M_PI/180.);
346 Vec2f decal=center-Vec2f(std::cos(angl)*center[0]-std::sin(angl)*center[1],
347 std::sin(angl)*center[0]+std::cos(angl)*center[1]);
348 WPSBox2f fBox;
349 for (int i=0; i < 4; ++i)
350 {
351 Vec2f pt=Vec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
352 pt = Vec2f(std::cos(angl)*pt[0]-std::sin(angl)*pt[1],
353 std::sin(angl)*pt[0]+std::cos(angl)*pt[1])+decal;
354 if (i==0) fBox=WPSBox2f(pt,pt);
355 else fBox=fBox.getUnion(WPSBox2f(pt,pt));
356 }
357 WPSGraphicShape res = path(fBox);
358 res.m_path=getPath(false);
359 for (auto &pt : res.m_path)
360 pt.rotate(angle, decal);
361 return res;
362 }
363
transform(WPSTransformation const & matrix) const364 WPSGraphicShape WPSGraphicShape::transform(WPSTransformation const &matrix) const
365 {
366 if (matrix.isIdentity()) return *this;
367 if (matrix[0][1]<=0 && matrix[0][1]>=0 && matrix[1][0]<=0 && matrix[1][0]>=0)
368 {
369 WPSGraphicShape res=*this;
370 if (matrix[0][0]<1 || matrix[0][0]>1 || matrix[1][1]<1 || matrix[1][1]>1)
371 res.scale(Vec2f(matrix[0][0], matrix[1][1]));
372 res.translate(Vec2f(matrix[0][2],matrix[1][2]));
373 return res;
374 }
375
376 WPSBox2f fBox;
377 for (int i=0; i < 4; ++i)
378 {
379 Vec2f pt = matrix*Vec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
380 if (i==0) fBox=WPSBox2f(pt,pt);
381 else fBox=fBox.getUnion(WPSBox2f(pt,pt));
382 }
383 WPSGraphicShape res = path(fBox);
384 res.m_path=getPath(true);
385
386 WPSTransformation transf;
387 float rotation=0;
388 Vec2f shearing;
389 if (!matrix.decompose(rotation,shearing,transf,fBox.center()))
390 rotation=0;
391 for (auto &p : res.m_path)
392 p.transform(matrix, rotation);
393 return res;
394 }
395
addTo(Vec2f const & orig,bool asSurface,librevenge::RVNGPropertyList & propList) const396 WPSGraphicShape::Command WPSGraphicShape::addTo(Vec2f const &orig, bool asSurface, librevenge::RVNGPropertyList &propList) const
397 {
398 Vec2f pt;
399 librevenge::RVNGPropertyList list;
400 librevenge::RVNGPropertyListVector vect;
401 Vec2f decal=orig-m_bdBox[0];
402 switch (m_type)
403 {
404 case Line:
405 if (m_vertices.size()!=2) break;
406 pt=m_vertices[0]+decal;
407 list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
408 list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
409 vect.append(list);
410 pt=m_vertices[1]+decal;
411 list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
412 list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
413 vect.append(list);
414 propList.insert("svg:points", vect);
415 return C_Polyline;
416 case Rectangle:
417 if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0)
418 {
419 propList.insert("svg:rx",double(m_cornerWidth[0]), librevenge::RVNG_POINT);
420 propList.insert("svg:ry",double(m_cornerWidth[1]), librevenge::RVNG_POINT);
421 }
422 pt=m_formBox[0]+decal;
423 propList.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
424 propList.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
425 pt=m_formBox.size();
426 propList.insert("svg:width",double(pt.x()), librevenge::RVNG_POINT);
427 propList.insert("svg:height",double(pt.y()), librevenge::RVNG_POINT);
428 return C_Rectangle;
429 case Circle:
430 pt=0.5*(m_formBox[0]+m_formBox[1])+decal;
431 propList.insert("svg:cx",double(pt.x()), librevenge::RVNG_POINT);
432 propList.insert("svg:cy",double(pt.y()), librevenge::RVNG_POINT);
433 pt=0.5*(m_formBox[1]-m_formBox[0]);
434 propList.insert("svg:rx",double(pt.x()), librevenge::RVNG_POINT);
435 propList.insert("svg:ry",double(pt.y()), librevenge::RVNG_POINT);
436 return C_Ellipse;
437 case Arc:
438 case Pie:
439 {
440 Vec2f center=0.5*(m_formBox[0]+m_formBox[1])+decal;
441 Vec2f rad=0.5*(m_formBox[1]-m_formBox[0]);
442 float angl0=m_arcAngles[0];
443 float angl1=m_arcAngles[1];
444 if (rad[1]<0)
445 {
446 static bool first=true;
447 if (first)
448 {
449 WPS_DEBUG_MSG(("WPSGraphicShape::addTo: oops radiusY for arc is negative, inverse it\n"));
450 first=false;
451 }
452 rad[1]=-rad[1];
453 }
454 while (angl1<angl0)
455 angl1+=360.f;
456 while (angl1>angl0+360.f)
457 angl1-=360.f;
458 if (angl1-angl0>=180.f && angl1-angl0<=180.f)
459 angl1+=0.01f;
460 float angl=angl0*float(M_PI/180.);
461 bool addCenter=m_type==Pie && asSurface;
462 if (addCenter)
463 {
464 pt=center;
465 list.insert("librevenge:path-action", "M");
466 list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
467 list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
468 vect.append(list);
469 }
470 list.clear();
471 pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
472 list.insert("librevenge:path-action", addCenter ? "L" : "M");
473 list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
474 list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
475 vect.append(list);
476
477 list.clear();
478 angl=angl1*float(M_PI/180.);
479 pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
480 list.insert("librevenge:path-action", "A");
481 list.insert("librevenge:large-arc", !(angl1-angl0<180.f));
482 list.insert("librevenge:sweep", false);
483 list.insert("svg:rx",double(rad.x()), librevenge::RVNG_POINT);
484 list.insert("svg:ry",double(rad.y()), librevenge::RVNG_POINT);
485 list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
486 list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
487 vect.append(list);
488 if (asSurface)
489 {
490 list.clear();
491 list.insert("librevenge:path-action", "Z");
492 vect.append(list);
493 }
494
495 propList.insert("svg:d", vect);
496 return C_Path;
497 }
498 case Polyline:
499 case Polygon:
500 {
501 size_t n=m_vertices.size();
502 if (n<2) break;
503 for (size_t i = 0; i < n; ++i)
504 {
505 list.clear();
506 pt=m_vertices[i]+decal;
507 list.insert("svg:x", double(pt.x()), librevenge::RVNG_POINT);
508 list.insert("svg:y", double(pt.y()), librevenge::RVNG_POINT);
509 vect.append(list);
510 }
511 propList.insert("svg:points", vect);
512 return (m_type==Polygon && asSurface) ? C_Polygon : C_Polyline;
513 }
514 case Path:
515 {
516 size_t n=m_path.size();
517 if (!n) break;
518 for (size_t c=0; c < n; ++c)
519 {
520 list.clear();
521 if (m_path[c].get(list, -1.0f*decal))
522 vect.append(list);
523 }
524 if (asSurface && m_path[n-1].m_type != 'Z')
525 {
526 // odg need a closed path to draw surface, so ...
527 list.clear();
528 list.insert("librevenge:path-action", "Z");
529 vect.append(list);
530 }
531 propList.insert("svg:d", vect);
532 return C_Path;
533 }
534 case ShapeUnknown:
535 default:
536 break;
537 }
538 WPS_DEBUG_MSG(("WPSGraphicShape::addTo: can not send a shape with type=%d\n", int(m_type)));
539 return C_Bad;
540 }
541
getPath(bool forTransformation) const542 std::vector<WPSGraphicShape::PathData> WPSGraphicShape::getPath(bool forTransformation) const
543 {
544 std::vector<WPSGraphicShape::PathData> res;
545 float const delta=0.55228f;
546 switch (m_type)
547 {
548 case Line:
549 case Polyline:
550 case Polygon:
551 {
552 size_t n=m_vertices.size();
553 if (n<2) break;
554 res.push_back(PathData('M',m_vertices[0]));
555 for (size_t i = 1; i < n; ++i)
556 res.push_back(PathData('L', m_vertices[i]));
557 break;
558 }
559 case Rectangle:
560 if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0)
561 {
562 WPSBox2f box=m_formBox;
563 Vec2f c=m_cornerWidth;
564 if (2*c[0]>box.size()[0]) c[0]=0.5f*box.size()[0];
565 if (2*c[1]>box.size()[1]) c[1]=0.5f*box.size()[1];
566 if (forTransformation)
567 {
568 Vec2f pt0(box[1][0]-c[0],box[0][1]);
569 res.push_back(PathData('M',pt0));
570 Vec2f pt1(box[1][0],box[0][1]+c[1]);
571 res.push_back(PathData('C',pt1,pt0+Vec2f(delta*c[0],0),pt1-Vec2f(0,delta*c[1])));
572 pt0=Vec2f(box[1][0],box[1][1]-c[1]);
573 res.push_back(PathData('L',pt0));
574 pt1=Vec2f(box[1][0]-c[0],box[1][1]);
575 res.push_back(PathData('C',pt1,pt0+Vec2f(0,delta*c[1]),pt1+Vec2f(delta*c[0],0)));
576 pt0=Vec2f(box[0][0]+c[0],box[1][1]);
577 res.push_back(PathData('L',pt0));
578 pt1=Vec2f(box[0][0],box[1][1]-c[1]);
579 res.push_back(PathData('C',pt1,pt0-Vec2f(delta*c[0],0),pt1+Vec2f(0,delta*c[1])));
580 pt0=Vec2f(box[0][0],box[0][1]+c[1]);
581 res.push_back(PathData('L',pt0));
582 pt1=Vec2f(box[0][0]+c[0],box[0][1]);
583 res.push_back(PathData('C',pt1,pt0-Vec2f(0,delta*c[1]),pt1-Vec2f(delta*c[0],0)));
584 }
585 else
586 {
587 res.push_back(PathData('M',Vec2f(box[1][0]-c[0],box[0][1])));
588 PathData data('A',Vec2f(box[1][0],box[0][1]+c[1]));
589 data.m_r=c;
590 data.m_sweep=true;
591 res.push_back(data);
592 res.push_back(PathData('L',Vec2f(box[1][0],box[1][1]-c[1])));
593 data.m_x=Vec2f(box[1][0]-c[0],box[1][1]);
594 res.push_back(data);
595 res.push_back(PathData('L',Vec2f(box[0][0]+c[0],box[1][1])));
596 data.m_x=Vec2f(box[0][0],box[1][1]-c[1]);
597 res.push_back(data);
598 res.push_back(PathData('L',Vec2f(box[0][0],box[0][1]+c[1])));
599 data.m_x=Vec2f(box[0][0]+c[0],box[0][1]);
600 res.push_back(data);
601 }
602 res.push_back(PathData('Z'));
603 break;
604 }
605 res.push_back(PathData('M',m_formBox[0]));
606 res.push_back(PathData('L',Vec2f(m_formBox[0][0],m_formBox[1][1])));
607 res.push_back(PathData('L',m_formBox[1]));
608 res.push_back(PathData('L',Vec2f(m_formBox[1][0],m_formBox[0][1])));
609 res.push_back(PathData('Z'));
610 break;
611 case Circle:
612 {
613 if (forTransformation)
614 {
615 Vec2f center=m_formBox.center();
616 Vec2f dir=0.5f*delta*(m_formBox[1]-m_formBox[0]);
617 Vec2f pt0(m_formBox[0][0],center[1]);
618 res.push_back(PathData('M',pt0));
619 Vec2f pt1(center[0],m_formBox[0][1]);
620 res.push_back(PathData('C',pt1, pt0-Vec2f(0,dir[1]), pt1-Vec2f(dir[0],0)));
621 pt0=Vec2f(m_formBox[1][0],center[1]);
622 res.push_back(PathData('C',pt0, pt1+Vec2f(dir[0],0), pt0-Vec2f(0,dir[1])));
623 pt1=Vec2f(center[0],m_formBox[1][1]);
624 res.push_back(PathData('C',pt1, pt0+Vec2f(0,dir[1]), pt1+Vec2f(dir[0],0)));
625 pt0=Vec2f(m_formBox[0][0],center[1]);
626 res.push_back(PathData('C',pt0, pt1-Vec2f(dir[0],0), pt0+Vec2f(0,dir[1])));
627 res.push_back(PathData('Z'));
628 }
629 else
630 {
631 Vec2f pt0 = Vec2f(m_formBox[0][0],0.5f*(m_formBox[0][1]+m_formBox[1][1]));
632 Vec2f pt1 = Vec2f(m_formBox[1][0],pt0[1]);
633 res.push_back(PathData('M',pt0));
634 PathData data('A',pt1);
635 data.m_r=0.5*(m_formBox[1]-m_formBox[0]);
636 data.m_largeAngle=true;
637 res.push_back(data);
638 data.m_x=pt0;
639 res.push_back(data);
640 break;
641 }
642 }
643 WPS_FALLTHROUGH;
644 case Arc:
645 case Pie:
646 {
647 Vec2f center=0.5*(m_formBox[0]+m_formBox[1]);
648 Vec2f rad=0.5*(m_formBox[1]-m_formBox[0]);
649 float angl0=m_arcAngles[0];
650 float angl1=m_arcAngles[1];
651 if (rad[1]<0)
652 {
653 static bool first=true;
654 if (first)
655 {
656 WPS_DEBUG_MSG(("WPSGraphicShape::getPath: oops radiusY for arc is negative, inverse it\n"));
657 first=false;
658 }
659 rad[1]=-rad[1];
660 }
661 while (angl1<angl0)
662 angl1+=360.f;
663 while (angl1>angl0+360.f)
664 angl1-=360.f;
665 if (angl1-angl0>=180.f && angl1-angl0<=180.f)
666 angl1+=0.01f;
667 float angl=angl0*float(M_PI/180.);
668 bool addCenter=m_type==Pie;
669 if (addCenter)
670 res.push_back(PathData('M', center));
671 Vec2f pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
672 res.push_back(PathData(addCenter ? 'L' : 'M', pt));
673 if (!forTransformation)
674 {
675 angl=angl1*float(M_PI/180.);
676 pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
677 PathData data('A',pt);
678 data.m_largeAngle=(angl1-angl0>=180.f);
679 data.m_r=rad;
680 res.push_back(data);
681 }
682 else
683 {
684 int N=int(angl1-angl0)/90;
685 float dAngle=float(angl1-angl0)/float(N+1);
686 for (int i=0; i<=N; ++i)
687 {
688 float newAngl= i==N ? angl1 : angl0+float(i+1)*dAngle;
689 newAngl*=float(M_PI/180.);
690 Vec2f newPt=center+Vec2f(std::cos(angl1)*rad[0],-std::sin(angl1)*rad[1]);
691 Vec2f dir(-std::sin(angl)*rad[0],-std::cos(angl)*rad[1]);
692 Vec2f newDir(-std::sin(newAngl)*rad[0],-std::cos(newAngl)*rad[1]);
693 float deltaDir=4/3.f*std::tan((newAngl-angl)/4);
694 res.push_back(PathData('C',newPt,pt+deltaDir*dir,newPt-deltaDir*newDir));
695 pt=newPt;
696 angl=newAngl;
697 }
698 if (m_type==Pie) res.push_back(PathData('Z'));
699 }
700 break;
701 }
702 case Path:
703 return m_path;
704 case ShapeUnknown:
705 default:
706 WPS_DEBUG_MSG(("WPSGraphicShape::getPath: unexpected type\n"));
707 break;
708 }
709 return res;
710 }
711 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
712