1 /* -*- mode: c++ -*- */
2 /**
3  * @file   Shapes.cpp
4  * @author Sebastien Fourey (GREYC)
5  * @date   Aug 2007
6  *
7  * @brief
8  * \@copyright
9  * This source code is part of the Board project, a C++ library whose
10  * purpose is to allow simple drawings in EPS, FIG or SVG files.
11  * Copyright (C) 2007 Sebastien Fourey <http://foureys.users.greyc.fr/>
12  *
13  * This program is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU Lesser General Public License as published
15  * by the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU Lesser General Public License for more details
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  */
26 #include "BoardConfig.h"
27 #include "board/Shapes.h"
28 #include "board/Rect.h"
29 #include "board/Tools.h"
30 #include "board/PathBoundaries.h"
31 #include "board/PSFonts.h"
32 #include "board/Transforms.h"
33 #include "board/ShapeVisitor.h"
34 #include <cmath>
35 #include <cstring>
36 #include <vector>
37 #include <sstream>
38 #include <algorithm>
39 #include <limits>
40 
41 #ifndef M_PI
42 #define M_PI		3.14159265358979323846	/* pi */
43 #endif
44 
45 namespace {
46 const char * xFigDashStylesPS[] = {
47   " [] 0 sd ", // SolidStyle
48   " [1 1] 0 sd ", //DashStyle,
49   " [1.5 4.5] 45 sd ", // DotStyle
50   " [4.5 2.3 1.5 2.3] 0 sd ", // DashDotStyle:
51   " [4.5 2.0 1.5 1.5 1.5 2.0] 0 sd ", // DashDotDotStyle:
52   " [4.5 1.8 1.5 1.4 1.5 1.4 1.5 1.8 ] 0 sd " // DashDotDotDotStyle
53 };
54 
55 const char * xFigDashStylesSVG[] = {
56   "", // SolidStyle
57   "stroke-dasharray:1,1;stroke-dashoffset:0", //DashStyle,
58   "stroke-dasharray:1.5,4.5;stroke-dashoffset:45", // DotStyle
59   "stroke-dasharray:4.5,2.3,1.5,2.3;stroke-dashoffset:0", // DashDotStyle:
60   "stroke-dasharray:4.5,2.0,1.5,1.5,1.5,2.0;stroke-dashoffset;0", // DashDotDotStyle:
61   "stroke-dasharray:4.5,1.8,1.5,1.4,1.5,1.4,1.5,1.8;stroke-dashoffset:0" // DashDotDotDotStyle
62 };
63 
64 const char * xFigDashStylesTikZ[] = {
65   "",                                           // SolidStyle
66   "dash pattern=on 1pt off 1pt,",               // DashStyle
67   "dotted,",                                    // DotStyle
68   "dashdotted,",                                // DashDotStyle
69   "dashdotdotted,",                             // DashDotDotStyle
70   "dash pattern=on 2pt off 3pt on 4pt off 4pt," // DashDotDotDotStyle
71 };
72 }
73 
74 namespace LibBoard {
75 
76 extern const char * XFigPostscriptFontnames[];
77 
78 bool Shape::_lineWidthScaling = true;
79 double Shape::_defaultLineWidth = 1.0;
80 Color Shape::_defaultPenColor = Color::Black;
81 Color Shape::_defaultFillColor = Color::Null;
82 Shape::LineStyle Shape::_defaultLineStyle = Shape::SolidStyle;
83 Shape::LineCap Shape::_defaultLineCap = Shape::ButtCap;
84 Shape::LineJoin Shape::_defaultLineJoin = Shape::MiterJoin;
85 
86 bool
shapeGreaterDepth(const Shape * s1,const Shape * s2)87 shapeGreaterDepth( const Shape *s1, const Shape *s2 )
88 {
89   return s1->depth() > s2->depth();
90 }
91 
92 const std::string Shape::_name("AbstractShape");
93 
94 const std::string &
name() const95 Shape::name() const
96 {
97   return _name;
98 }
99 
100 Point
center(LineWidthFlag lineWidthFlag) const101 Shape::center(LineWidthFlag lineWidthFlag) const
102 {
103   return boundingBox(lineWidthFlag).center();
104 }
105 
106 
107 Shape &
moveCenter(double x,double y,LineWidthFlag lineWidthFlag)108 Shape::moveCenter(double x, double y, LineWidthFlag lineWidthFlag)
109 {
110   Point c = center(lineWidthFlag);
111   translate(x-c.x,y-c.y);
112   return *this;
113 }
114 
115 Shape &
moveCenter(Point p,LineWidthFlag lineWidthFlag)116 Shape::moveCenter(Point p, LineWidthFlag lineWidthFlag)
117 {
118   Point c = center(lineWidthFlag);
119   translate(p.x-c.x,p.y-c.y);
120   return *this;
121 }
122 
123 Shape &
scaleToWidth(double w,Shape::LineWidthFlag lineWidthFlag)124 Shape::scaleToWidth(double w, Shape::LineWidthFlag lineWidthFlag)
125 {
126   double factor = w / boundingBox(lineWidthFlag).width;
127   scale(factor);
128   return *this;
129 }
130 
131 Shape &
scaleToHeight(double h,Shape::LineWidthFlag lineWidthFlag)132 Shape::scaleToHeight(double h, Shape::LineWidthFlag lineWidthFlag)
133 {
134   double factor = h / boundingBox(lineWidthFlag).height;
135   scale(factor);
136   return *this;
137 }
138 
139 void
enableLineWidthScaling()140 Shape::enableLineWidthScaling()
141 {
142   _lineWidthScaling = true;
143 }
144 
145 void
disableLineWidthScaling()146 Shape::disableLineWidthScaling()
147 {
148   _lineWidthScaling = false;
149 }
150 
setLineWidthScaling(bool on)151 void Shape::setLineWidthScaling(bool on)
152 {
153   _lineWidthScaling = on;
154 }
155 
156 std::string
svgProperties(const TransformSVG & transform) const157 Shape::svgProperties( const TransformSVG & transform ) const
158 {
159   static const char * capStrings[3] = { "butt", "round", "square" };
160   static const char * joinStrings[3] = { "miter", "round", "bevel" };
161   std::stringstream str;
162   if ( _penColor != Color::Null ) {
163     str << " fill=\"" << _fillColor.svg() << '"'
164         << " stroke=\"" << _penColor.svg() << '"'
165         << " stroke-width=\"" << transform.mapWidth( _lineWidth ) << "mm\""
166         << " style=\"stroke-linecap:" << capStrings[ _lineCap ]
167            << ";stroke-linejoin:" << joinStrings[ _lineJoin ];
168     if ( _lineStyle != SolidStyle )
169       str << ";" << xFigDashStylesSVG[ _lineStyle ];
170     str << '"'
171         << _fillColor.svgAlpha( " fill" )
172         << _penColor.svgAlpha( " stroke" );
173   } else  {
174     str << " fill=\"" << _fillColor.svg() << '"'
175            // 	<< " stroke=\"" << _fillColor.svg() << '"'
176            // 	<< " stroke-width=\"0.5px\""
177         << " stroke=\"none\""
178         << " stroke-width=\"0\""
179         << " style=\"stroke-linecap:round;stroke-linejoin:round;"
180         << '"'
181         << _fillColor.svgAlpha( " fill" )
182         << _fillColor.svgAlpha( " stroke" );
183   }
184   return str.str();
185 }
186 
187 std::string
postscriptProperties(const TransformEPS & transform) const188 Shape::postscriptProperties( const TransformEPS & transform ) const
189 {
190   std::stringstream str;
191   str << transform.mapWidth(_lineWidth) << " slw ";
192   str << _lineCap << " slc ";
193   str << _lineJoin << " slj";
194   str << xFigDashStylesPS[ _lineStyle ];
195 
196   return str.str();
197 }
198 
199 std::string
tikzProperties(const TransformTikZ & transform) const200 Shape::tikzProperties( const TransformTikZ & transform ) const
201 {
202   static const char * capStrings[3] = { "" /* initial value "butt" */, "line cap=round,", "line cap=rect," };
203   static const char * joinStrings[3] = { "" /* initial value "miter" */, "line join=round", "line join=bevel" };
204 
205   std::stringstream str;
206   str << "fill=" << _fillColor.tikz() << ',';
207   str << "draw=" << _penColor.tikz() << ',';
208   str << "line width=" << transform.mapWidth( _lineWidth ) << "mm,";
209   str << xFigDashStylesTikZ[ _lineStyle ];
210   str << capStrings[ _lineCap ];
211   str << joinStrings[ _lineJoin ];
212 
213   return str.str();
214 }
215 
216 void
depth(int d)217 Shape::depth( int d )
218 {
219   _depth = d;
220 }
221 
222 void
shiftDepth(int shift)223 Shape::shiftDepth( int shift )
224 {
225   _depth += shift;
226 }
227 
228 void
setDefaultLineWidth(double w)229 Shape::setDefaultLineWidth(double w)
230 { _defaultLineWidth = w; }
231 
232 void
setDefaultPenColor(Color c)233 Shape::setDefaultPenColor(Color c)
234 {  _defaultPenColor = c; }
235 
236 void
setDefaultFillColor(Color c)237 Shape::setDefaultFillColor(Color c)
238 { _defaultFillColor = c; }
239 
240 void
setDefaultLineStyle(Shape::LineStyle lineStyle)241 Shape::setDefaultLineStyle( Shape::LineStyle lineStyle )
242 { _defaultLineStyle = lineStyle; }
243 
244 void
setDefaultLineCap(Shape::LineCap lineCap)245 Shape::setDefaultLineCap( Shape::LineCap lineCap )
246 { _defaultLineCap = lineCap; }
247 
248 void
setDefaultLineJoin(Shape::LineJoin lineJoin)249 Shape::setDefaultLineJoin( Shape::LineJoin lineJoin )
250 { _defaultLineJoin = lineJoin; }
251 
252 double
defaultLineWidth()253 Shape::defaultLineWidth()
254 { return _defaultLineWidth; }
255 
256 Color
defaultPenColor()257 Shape::defaultPenColor()
258 { return _defaultPenColor; }
259 
260 Color
defaultFillColor()261 Shape::defaultFillColor()
262 { return _defaultFillColor; }
263 
264 Shape::LineStyle
defaultLineStyle()265 Shape::defaultLineStyle()
266 { return _defaultLineStyle; }
267 
268 Shape::LineCap
defaultLineCap()269 Shape::defaultLineCap()
270 { return _defaultLineCap; }
271 
272 Shape::LineJoin
defaultLineJoin()273 Shape::defaultLineJoin()
274 { return _defaultLineJoin; }
275 
276 void
accept(ShapeVisitor & visitor)277 Shape::accept(ShapeVisitor & visitor)
278 {
279   visitor.visit(*this);
280 }
281 
282 void
accept(const ShapeVisitor & visitor)283 Shape::accept(const ShapeVisitor & visitor)
284 {
285   visitor.visit(*this);
286 }
287 
288 /*
289  * Dot
290  */
291 
292 const std::string Dot::_name("Dot");
293 
294 const std::string &
name() const295 Dot::name() const
296 {
297   return _name;
298 }
299 
300 Point
center(LineWidthFlag) const301 Dot::center(LineWidthFlag) const {
302   return Point( _x, _y );
303 }
304 
305 Dot &
rotate(double angle,const Point & center)306 Dot::rotate( double angle, const Point & center )
307 {
308   Point( _x, _y ).rotate( angle, center ).get( _x, _y );
309   return *this;
310 }
311 
312 Dot
rotated(double angle,const Point & center) const313 Dot::rotated( double angle, const Point & center ) const
314 {
315   return Dot(*this).rotate( angle, center );
316 }
317 
318 Dot &
rotate(double)319 Dot::rotate( double )
320 {
321   return *this;
322 }
323 
324 Dot
rotated(double) const325 Dot::rotated( double ) const
326 {
327   return *this;
328 }
329 
330 Dot &
translate(double dx,double dy)331 Dot::translate( double dx, double dy )
332 {
333   _x += dx;
334   _y += dy;
335   return *this;
336 }
337 
338 Dot
translated(double dx,double dy) const339 Dot::translated( double dx, double dy ) const
340 {
341   return Dot(*this).translate( dx, dy );
342 }
343 
344 Dot &
scale(double,double)345 Dot::scale( double , double )
346 {
347   return *this;
348 }
349 
350 Dot &
scale(double)351 Dot::scale( double )
352 {
353   return *this;
354 }
355 
356 Dot
scaled(double,double) const357 Dot::scaled( double , double ) const
358 {
359   return *this;
360 }
361 
362 Dot
scaled(double) const363 Dot::scaled( double ) const
364 {
365   return *this;
366 }
367 
368 void
scaleAll(double s)369 Dot::scaleAll( double s )
370 {
371   _x *= s;
372   _y *= s;
373 }
374 
375 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const376 Dot::flushPostscript( std::ostream & stream,
377                       const TransformEPS & transform ) const
378 {
379   stream << "\n% Dot\n";
380   stream << postscriptProperties(transform) << " "
381          << "n "
382          << transform.mapX( _x ) << " "
383          << transform.mapY( _y ) << " "
384          << "m "
385          << transform.mapX( _x ) << " "
386          << transform.mapY( _y ) << " "
387          << "l " << _penColor.postscript() << " srgb stroke" << std::endl;
388 }
389 
390 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const391 Dot::flushFIG( std::ostream & stream,
392                const TransformFIG & transform,
393                std::map<Color,int> & colormap ) const
394 {
395   stream << "2 1 0 ";
396   // Thickness
397   stream << ( _penColor.valid()?transform.mapWidth( _lineWidth ):0 ) << " ";
398   // Pen color
399   stream << colormap[ _penColor ] << " ";
400   // Fill color
401   stream << "0 ";
402   // Depth
403   stream << transform.mapDepth( _depth ) << " ";
404   // Pen style
405   stream <<  "-1 ";
406   // Area fill, style val, join style, cap style, radius, f_arrow, b_arrow
407   stream << "-1 0.000 " << _lineJoin << " " << _lineCap << " -1 0 0 ";
408   // Number of points
409   stream << "2\n";
410   stream << "         ";
411   stream << static_cast<int>( transform.mapX( _x ) ) << " "
412          << static_cast<int>( transform.mapY( _y ) ) << " "
413          << static_cast<int>( transform.mapX( _x ) ) << " "
414          << static_cast<int>( transform.mapY( _y ) ) << std::endl;
415 }
416 
417 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const418 Dot::flushSVG( std::ostream & stream,
419                const TransformSVG & transform ) const
420 {
421   stream << "<line x1=\"" << transform.mapX( _x ) << "\""
422          << " y1=\"" << transform.mapY( _y ) << "\""
423          << " x2=\"" << transform.mapX( _x ) << "\""
424          << " y2=\"" << transform.mapY( _y ) << "\""
425          << svgProperties( transform )
426          << " />" << std::endl;
427 }
428 
429 void
flushTikZ(std::ostream & stream,const TransformTikZ &) const430 Dot::flushTikZ( std::ostream & stream,
431                 const TransformTikZ & /*transform*/ ) const
432 
433 {
434   // FIXME: unimplemented
435   stream << "% FIXME: Dot::flushTikZ unimplemented" << std::endl;
436 }
437 
438 Rect
boundingBox(LineWidthFlag lineWidthFlag) const439 Dot::boundingBox(LineWidthFlag lineWidthFlag) const
440 {
441   switch (lineWidthFlag) {
442   case IgnoreLineWidth:
443     return Rect( _x, _y, 0.0, 0.0 );
444     break;
445   case UseLineWidth:
446     return Rect( _x-0.5*_lineWidth, _y+0.5*_lineWidth, _lineWidth, _lineWidth );
447     break;
448   default:
449     Tools::error << "LineWidthFlag incorrect value (" << lineWidthFlag << ")\n";
450     return Rect();
451     break;
452   }
453 }
454 
455 Dot *
clone() const456 Dot::clone() const {
457   return new Dot(*this);
458 }
459 
460 /*
461    * Line
462    */
463 
464 const std::string Line::_name("Line");
465 
466 const std::string &
name() const467 Line::name() const
468 {
469   return _name;
470 }
471 
472 Line &
rotate(double angle,const Point & center)473 Line::rotate( double angle, const Point & center )
474 {
475   Point( _x1, _y1 ).rotate( angle, center ).get( _x1, _y1 );
476   Point( _x2, _y2 ).rotate( angle, center ).get( _x2, _y2 );
477   return *this;
478 }
479 
480 Line &
rotate(double angle)481 Line::rotate( double angle )
482 {
483   return Line::rotate( angle, center() );
484 }
485 
486 Line
rotated(double angle,const Point & center) const487 Line::rotated( double angle, const Point & center ) const
488 {
489   Line res(*this);
490   Point( _x1, _y1 ).rotate( angle, center ).get( res._x1, res._y1 );
491   Point( _x2, _y2 ).rotate( angle, center ).get( res._x2, res._y2 );
492   return res;
493 }
494 
495 Line
rotated(double angle) const496 Line::rotated( double angle ) const
497 {
498   Line res(*this);
499   Point c = center();
500   Point( _x1, _y1 ).rotate( angle, c ).get( res._x1, res._y1 );
501   Point( _x2, _y2 ).rotate( angle, c ).get( res._x2, res._y2 );
502   return res;
503 }
504 
505 Line &
translate(double dx,double dy)506 Line::translate( double dx, double dy )
507 {
508   _x1 += dx; _x2 += dx;
509   _y1 += dy; _y2 += dy;
510   return *this;
511 }
512 
513 Line
translated(double dx,double dy) const514 Line::translated( double dx, double dy ) const
515 {
516   Line res(*this);
517   res._x1 += dx; res._x2 += dx;
518   res._y1 += dy; res._y2 += dy;
519   return *this;
520 }
521 
522 Line &
scale(double sx,double sy)523 Line::scale( double sx, double sy )
524 {
525   Point c = center();
526   _x1 *= sx;
527   _x2 *= sx;
528   _y1 *= sy;
529   _y2 *= sy;
530   Point delta = c - center();
531   translate( delta.x, delta.y );
532   updateLineWidth(std::max(sx,sy));
533   return *this;
534 }
535 
536 Line &
scale(double s)537 Line::scale( double s )
538 {
539   scale( s, s );
540   return (*this);
541 }
542 
543 Line
scaled(double sx,double sy) const544 Line::scaled( double sx, double sy ) const
545 {
546   return Line(*this).scale(sx,sy);
547 }
548 
549 Line
scaled(double s) const550 Line::scaled( double s ) const
551 {
552   return Line::scaled( s , s );
553 }
554 
555 void
scaleAll(double s)556 Line::scaleAll( double s )
557 {
558   _x1 *= s;
559   _y1 *= s;
560   _x2 *= s;
561   _y2 *= s;
562 }
563 
564 Line *
clone() const565 Line::clone() const {
566   return new Line(*this);
567 }
568 
569 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const570 Line::flushPostscript( std::ostream & stream,
571                        const TransformEPS & transform ) const
572 {
573   stream << "\n% Line\n";
574   stream << postscriptProperties(transform) << " "
575          << "n "
576          << transform.mapX( _x1 ) << " "
577          << transform.mapY( _y1 ) << " "
578          << "m "
579          << transform.mapX( _x2 ) << " "
580          << transform.mapY( _y2 ) << " "
581          << "l " << _penColor.postscript() << " srgb stroke" << std::endl;
582 }
583 
584 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const585 Line::flushFIG( std::ostream & stream,
586                 const TransformFIG & transform,
587                 std::map<Color,int> & colormap ) const
588 {
589   stream << "2 1 ";
590   // Line style
591   stream << _lineStyle << " ";
592   // Thickness
593   stream << ( _penColor.valid()?transform.mapWidth( _lineWidth ):0 ) << " ";
594   // Pen color
595   stream << colormap[ _penColor ] << " ";
596   // Fill color
597   stream << "0 ";
598   // Depth
599   stream << transform.mapDepth( _depth ) << " ";
600   // Pen style
601   stream <<  "-1 ";
602   // Area fill, style val, join style, cap style, radius, f_arrow, b_arrow
603   stream << "-1 " << (_lineStyle?"4.000 ":"0.000 ") << _lineJoin << " " << _lineCap << " -1 0 0 ";
604   // Number of points
605   stream << "2\n";
606   stream << "         ";
607   stream << static_cast<int>( transform.mapX( _x1 ) ) << " "
608          << static_cast<int>( transform.mapY( _y1 ) ) << " "
609          << static_cast<int>( transform.mapX( _x2 ) ) << " "
610          << static_cast<int>( transform.mapY( _y2 ) ) << std::endl;
611 }
612 
613 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const614 Line::flushSVG( std::ostream & stream,
615                 const TransformSVG & transform ) const
616 {
617   stream << "<line x1=\"" << transform.mapX( _x1 ) << "\""
618          << " y1=\"" << transform.mapY( _y1 ) << "\""
619          << " x2=\"" << transform.mapX( _x2 ) << "\""
620          << " y2=\"" << transform.mapY( _y2 ) << "\""
621          << svgProperties( transform )
622          << " />" << std::endl;
623 }
624 
625 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const626 Line::flushTikZ( std::ostream & stream,
627                  const TransformTikZ & transform ) const
628 {
629   stream << "\\path[" << tikzProperties(transform) << "] ("
630          << transform.mapX( _x1 ) << ',' << transform.mapY( _y1 )
631          << ") -- ("
632          << transform.mapX( _x2 ) << ',' << transform.mapY( _y2 )
633          << ");" << std::endl;
634 }
635 
636 Rect
boundingBox(LineWidthFlag lineWidthFlag) const637 Line::boundingBox(LineWidthFlag lineWidthFlag) const
638 {
639   Path p;
640   p << Point(_x1,_y1) << Point(_x2,_y2);
641   switch (lineWidthFlag) {
642   case UseLineWidth:
643     return Tools::pathBoundingBox(p,_lineWidth,_lineCap,_lineJoin);
644     break;
645   case IgnoreLineWidth:
646     return p.boundingBox();
647     break;
648   default:
649     Tools::error << "LineWidthFlag incorrect value (" << lineWidthFlag << ")\n";
650     return Rect();
651     break;
652   }
653 }
654 
655 /*
656  * Arrow
657  */
658 
659 const std::string Arrow::_name("Arrow");
660 
661 const std::string &
name() const662 Arrow::name() const
663 {
664   return _name;
665 }
666 
667 Arrow
rotated(double angle,const Point & center) const668 Arrow::rotated( double angle, const Point & center ) const
669 {
670   Arrow res(*this);
671   Point( _x1, _y1 ).rotate( angle, center ).get( res._x1, res._y1 );
672   Point( _x2, _y2 ).rotate( angle, center ).get( res._x2, res._y2 );
673   return res;
674 }
675 
676 Arrow
rotated(double angle) const677 Arrow::rotated( double angle ) const
678 {
679   Arrow res(*this);
680   Point c = center();
681   Point( _x1, _y1 ).rotate( angle, c ).get( res._x1, res._y1 );
682   Point( _x2, _y2 ).rotate( angle, c ).get( res._x2, res._y2 );
683   return res;
684 }
685 
686 Arrow
translated(double dx,double dy) const687 Arrow::translated( double dx, double dy ) const
688 {
689   return static_cast<Arrow&>(Arrow(*this).translate(dx,dy));
690 }
691 
692 Arrow
scaled(double sx,double sy) const693 Arrow::scaled( double sx, double sy ) const
694 {
695   return static_cast<Arrow&>(Arrow(*this).scale(sx,sy));
696 }
697 
698 Arrow
scaled(double s) const699 Arrow::scaled( double s ) const
700 {
701   return Arrow::scaled( s, s );
702 }
703 
boundingBox(Shape::LineWidthFlag) const704 Rect Arrow::boundingBox(Shape::LineWidthFlag) const
705 {
706   double dx = _x1 - _x2;
707   double dy = _y1 - _y2;
708   double norm = sqrt( dx*dx + dy*dy );
709   dx /= norm;
710   dy /= norm;
711   dx *= 10*_lineWidth;
712   dy *= 10*_lineWidth;
713 
714   double ndx1 = dx*cos(0.3)-dy*sin(0.3);
715   double ndy1 = dx*sin(0.3)+dy*cos(0.3);
716   double ndx2 = dx*cos(-0.3)-dy*sin(-0.3);
717   double ndy2 = dx*sin(-0.3)+dy*cos(-0.3);
718 
719   Path pLine;
720   pLine << Point(_x1,_y1);
721   pLine << Point(_x2+(dx*cos(0.3)), _y2+(dy*cos(0.3)));
722 
723   Path pArrow;
724   pArrow << Point(_x2+ndx1,_y2+ndy1)
725          << Point(_x2,_y2 )
726          << Point(_x2+ndx2,_y2+ndy2);
727 
728   return Tools::pathBoundingBox(pLine,_lineWidth,_lineCap,_lineJoin) || pArrow.boundingBox();
729 }
730 
731 Arrow *
clone() const732 Arrow::clone() const {
733   return new Arrow(*this);
734 }
735 
736 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const737 Arrow::flushPostscript( std::ostream & stream,
738                         const TransformEPS & transform ) const
739 {
740   double dx = _x1 - _x2;
741   double dy = _y1 - _y2;
742   double norm = sqrt( dx*dx + dy*dy );
743   dx /= norm;
744   dy /= norm;
745   dx *= 10*_lineWidth;
746   dy *= 10*_lineWidth;
747   double ndx1 = dx*cos(0.3)-dy*sin(0.3);
748   double ndy1 = dx*sin(0.3)+dy*cos(0.3);
749   double ndx2 = dx*cos(-0.3)-dy*sin(-0.3);
750   double ndy2 = dx*sin(-0.3)+dy*cos(-0.3);
751 
752   stream << "\n% Arrow\n";
753   stream << _penColor.postscript() << " srgb "
754          << postscriptProperties(transform) << " "
755          << "n "
756          << transform.mapX( _x1 ) << " "
757          << transform.mapY( _y1 ) << " "
758          << "m "
759          << transform.mapX( _x2 + ( dx * cos(0.3) ) ) << " "
760          << transform.mapY( _y2 + ( dy * cos(0.3) ) ) << " "
761          << "l stroke" << std::endl;
762 
763   if ( filled() ) {
764     stream << "n "
765            << transform.mapX( _x2 ) + transform.scale( ndx1 ) << " "
766            << transform.mapY( _y2 ) + transform.scale( ndy1 ) << " "
767            << "m "
768            << transform.mapX( _x2 ) << " "
769            << transform.mapY( _y2 ) << " l "
770            << transform.mapX( _x2 ) + transform.scale( ndx2 ) << " "
771            << transform.mapY( _y2 ) + transform.scale( ndy2 ) << " ";
772     stream  << "l cp " << _fillColor.postscript() << " srgb  fill" << std::endl;
773   }
774 }
775 
776 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const777 Arrow::flushFIG( std::ostream & stream,
778                  const TransformFIG & transform,
779                  std::map<Color,int> & colormap ) const
780 {
781   stream << "2 1 ";
782   // Line style
783   stream << _lineStyle << " ";
784   // Thickness
785   stream << ( _penColor.valid()?transform.mapWidth( _lineWidth ):0 ) << " ";
786   // Pen color
787   stream << colormap[ _penColor ] << " ";
788   // Fill color
789   stream << colormap[ _fillColor ] << " ";
790   // Depth
791   stream << transform.mapDepth( _depth ) << " ";
792   // Pen style
793   stream <<  "-1 ";
794   // Area fill, style val, join style, cap style, radius, f_arrow, b_arrow
795   stream << "-1 " << (_lineStyle?"4.000 ":"0.000 ") << _lineJoin << " " << _lineCap << " -1 1 0 ";
796   // Number of points
797   stream << "2\n";
798   if ( filled() )
799     stream << "       1 1 1.00 60.00 120.00\n";
800   else
801     stream << "       1 0 1.00 60.00 120.00\n";
802   stream << "         ";
803   stream << static_cast<int>( transform.mapX( _x1 ) ) << " "
804          << static_cast<int>( transform.mapY( _y1 ) ) << " "
805          << static_cast<int>( transform.mapX( _x2 ) ) << " "
806          << static_cast<int>( transform.mapY( _y2 ) ) << std::endl;
807 }
808 
809 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const810 Arrow::flushSVG( std::ostream & stream,
811                  const TransformSVG & transform ) const
812 {
813   double dx = _x1 - _x2;
814   double dy = _y1 - _y2;
815   double norm = sqrt( dx*dx + dy*dy );
816   dx /= norm;
817   dy /= norm;
818   dx *= 10 * _lineWidth;
819   dy *= 10 * _lineWidth;
820   double ndx1 = dx*cos(0.3)-dy*sin(0.3);
821   double ndy1 = dx*sin(0.3)+dy*cos(0.3);
822   double ndx2 = dx*cos(-0.3)-dy*sin(-0.3);
823   double ndy2 = dx*sin(-0.3)+dy*cos(-0.3);
824 
825   stream << "<g>" << std::endl;
826   // The line
827   stream << " <path "
828          << "d=\"M " << transform.mapX( _x1 ) << " " << transform.mapY( _y1 )
829          << " L " << transform.mapX( _x2 + ( dx * cos(0.3) ) )
830          << " " << transform.mapY( _y2 + ( dy * cos(0.3) ) ) << " z\""
831          << " fill=\"none\" stroke=\"" << _penColor.svg() << "\""
832          << _penColor.svgAlpha( " stroke" );
833 
834   if ( _lineStyle != SolidStyle ) {
835     stream << " style=\"" <<   xFigDashStylesSVG[ _lineStyle ] << '"';
836   }
837   stream << " stroke-width=\"" << transform.mapWidth( _lineWidth ) << "mm\" />";
838 
839   // The arrow
840   stream << " <polygon";
841   stream << " fill=\"" << _fillColor.svg() << "\"";
842   stream << " stroke=\"none\""
843          << " stroke-width=\"0mm\""
844          << " style=\"stroke-linecap:butt;stroke-linejoin:miter\""
845          << _fillColor.svgAlpha( " fill" )
846          << _penColor.svgAlpha( " stroke" )
847          << " points=\""
848          << transform.mapX( _x2 ) + transform.scale( ndx1 ) << ","
849          << transform.mapY( _y2 ) - transform.scale( ndy1 ) << " "
850          << transform.mapX( _x2 ) << ","
851          << transform.mapY( _y2 ) << " "
852          << transform.mapX( _x2 ) + transform.scale( ndx2 ) << ","
853          << transform.mapY( _y2 ) - transform.scale( ndy2 ) << " "
854          << transform.mapX( _x2 ) + transform.scale( ndx1 ) << ","
855          << transform.mapY( _y2 ) - transform.scale( ndy1 ) << "\" />" << std::endl;
856   stream << "</g>" << std::endl;
857 }
858 
859 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const860 Arrow::flushTikZ( std::ostream & stream,
861                   const TransformTikZ & transform ) const
862 {
863   stream << "\\path[-latex," << tikzProperties(transform) << "] ("
864          << transform.mapX( _x1 ) << ',' << transform.mapY( _y1 )
865          << ") -- ("
866          << transform.mapX( _x2 ) << ',' << transform.mapY( _y2 )
867          << ");" << std::endl;
868 }
869 
870 
871 /*
872    * Ellipse
873    */
874 
875 const std::string Ellipse::_name("Ellipse");
876 
877 const std::string &
name() const878 Ellipse::name() const
879 {
880   return _name;
881 }
882 
883 Point
center(LineWidthFlag) const884 Ellipse::center(LineWidthFlag ) const {
885   return _center;
886 }
887 
888 Ellipse &
rotate(double angle,const Point & center)889 Ellipse::rotate( double angle, const Point & center )
890 {
891   Point c( _center );
892   Point e = (c + Point( _xRadius, 0 )).rotate( _angle, c );
893   Point rc = c.rotated( angle, center );
894   Point re = e.rotated( angle, center );
895   Point axis = re - rc;
896   _angle = atan( axis.y / axis.x );
897   _center = rc;
898   return *this;
899 }
900 
901 Ellipse
rotated(double angle,const Point & center) const902 Ellipse::rotated( double angle, const Point & center ) const
903 {
904   return Ellipse(*this).rotate( angle, center );
905 }
906 
907 Ellipse &
rotate(double angle)908 Ellipse::rotate( double angle )
909 {
910   return Ellipse::rotate( angle, center() );
911 }
912 
913 Ellipse
rotated(double angle) const914 Ellipse::rotated( double angle ) const
915 {
916   return Ellipse(*this).rotate( angle, center() );
917 }
918 
919 Ellipse &
translate(double dx,double dy)920 Ellipse::translate( double dx, double dy )
921 {
922   _center += Point( dx, dy );
923   return *this;
924 }
925 
926 Ellipse
translated(double dx,double dy) const927 Ellipse::translated( double dx, double dy ) const
928 {
929   return Ellipse(*this).translate( dx, dy );
930 }
931 
932 Ellipse &
scale(double sx,double sy)933 Ellipse::scale( double sx, double sy )
934 {
935   // Thanks to Freddie Exall for pointing an error with the first version
936   // of this function, and for pointing to a fix as well!
937   if ( _angle != 0 ) {
938     double co = cos( _angle );
939     double si = sin( _angle );
940 
941     // current transformation matrix
942     double m00 = ( 1 / _xRadius ) * co;
943     double m01 = ( 1 / _xRadius ) * si;
944     double m10 = - ( 1 / _yRadius ) * si;
945     double m11 = ( 1 / _yRadius ) * co;
946 
947     // Implicit function of ellipse at current
948     // ax^2 + bxy + cy^2 = 1
949     double a = ( m00 * m00 ) + ( m10 * m10 );
950     double b = 2 * ( ( m00 * m01 ) + ( m10 * m11 ) );
951     double c = ( m01 * m01 ) + ( m11 * m11 );
952 
953     // Scale coefficients ( x_new = sx * x, y_new = sy * y )
954     a = a / ( sx * sx );
955     b = b / ( sx * sy );
956     c = c / ( sy * sy );
957 
958     if ( b == 0 ) {
959       _angle = 0;
960     } else if ( a == c ) {
961       _angle = M_PI / 4;
962       a += ( b / 2 );
963       c -= ( b / 2 );
964     } else {
965       _angle = 0.5 * atan( b / ( a - c ) );
966       double k = 1 + ( ( b * b ) / ( ( a - c ) * ( a - c ) ) );
967       k = sqrt( k );
968       k *= ( a - c );
969       c += a;
970       a = 0.5 * ( c + k );
971       c = 0.5 * ( c - k );
972     }
973     _xRadius = 1 / sqrt( a );
974     _yRadius = 1 / sqrt( c );
975   } else {
976     _xRadius = _xRadius * sx;
977     _yRadius = _yRadius * sy;
978   }
979   updateLineWidth(std::max(sx,sy));
980   return *this;
981 }
982 
983 Ellipse &
scale(double s)984 Ellipse::scale( double s )
985 {
986   return Ellipse::scale( s, s );
987 }
988 
989 Ellipse
scaled(double sx,double sy) const990 Ellipse::scaled( double sx, double sy ) const
991 {
992   return Ellipse(*this).scale( sx, sy );
993 }
994 
995 Ellipse
scaled(double s) const996 Ellipse::scaled( double s ) const
997 {
998   return Ellipse(*this).scale( s, s );
999 }
1000 
1001 void
scaleAll(double s)1002 Ellipse::scaleAll( double s )
1003 {
1004   _xRadius *= s;
1005   _yRadius *= s;
1006   _center *= s;
1007 }
1008 
1009 Ellipse *
clone() const1010 Ellipse::clone() const {
1011   return new Ellipse(*this);
1012 }
1013 
1014 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const1015 Ellipse::flushPostscript( std::ostream & stream,
1016                           const TransformEPS & transform ) const
1017 {
1018   double yScale = _yRadius / _xRadius;
1019   stream << "\n% Ellipse\n";
1020   if ( filled() ) {
1021     stream << "gs "
1022            << transform.mapX( _center.x ) << " " << transform.mapY( _center.y ) << " tr";
1023     if ( _angle != 0.0 ) stream << " " << (_angle*180/M_PI) << " rot ";
1024     if ( ! _circle ) stream << " " << 1.0 << " " << yScale << " sc";
1025     stream << " n " << transform.scale( _xRadius ) << " 0 m "
1026            << " 0 0 " << transform.scale( _xRadius ) << " 0.0 360.0 arc ";
1027     stream << " " << _fillColor.postscript() << " srgb";
1028     stream << " fill gr" << std::endl;
1029   }
1030 
1031   if ( _penColor != Color::Null ) {
1032     stream << postscriptProperties(transform) << "\n";
1033     stream << "gs " << transform.mapX( _center.x ) << " " << transform.mapY( _center.y ) << " tr";
1034     if ( _angle != 0.0 ) stream << " " << (_angle*180/M_PI) << " rot ";
1035     if ( ! _circle ) stream << " " << 1.0 << " " << yScale << " sc";
1036     stream << " n " << transform.scale( _xRadius ) << " 0 m "
1037            << " 0 0 " << transform.scale( _xRadius ) << " 0.0 360.0 arc ";
1038     stream << " " << _penColor.postscript() << " srgb";
1039     stream << " stroke gr" << std::endl;
1040   }
1041 }
1042 
1043 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const1044 Ellipse::flushFIG( std::ostream & stream,
1045                    const TransformFIG & transform,
1046                    std::map<Color,int> & colormap ) const
1047 {
1048   // Ellipse, Sub type, Line style, Thickness
1049   if ( _circle )
1050     stream << "1 3 " << _lineStyle << " ";
1051   else
1052     stream << "1 1 " << _lineStyle << " ";
1053   stream << ( _penColor.valid()?transform.mapWidth( _lineWidth ):0 ) << " ";
1054   // Pen color, Fill color
1055   stream << colormap[ _penColor ] << " " << colormap[ _fillColor ] << " ";
1056   // Depth, Pen style, Area fill, Style val, Direction, angle
1057   if ( filled() )
1058     stream << transform.mapDepth( _depth ) << " -1 20 " << (_lineStyle?"4.000 ":"0.000 ") << "  1 " << _angle << " ";
1059   else
1060     stream << transform.mapDepth( _depth ) << " -1 -1 " << (_lineStyle?"4.000 ":"0.000 ") << " 1 " << _angle << " ";
1061   stream << static_cast<int>( transform.mapX( _center.x ) ) << " "
1062          << static_cast<int>( transform.mapY( _center.y ) ) << " "
1063          << static_cast<int>( transform.scale( _xRadius ) ) << " "
1064          << static_cast<int>( transform.scale( _yRadius ) ) << " "
1065          << static_cast<int>( transform.mapX( _center.x ) ) << " "
1066          << static_cast<int>( transform.mapY( _center.y ) ) << " "
1067          << static_cast<int>( transform.mapX( _center.x ) + transform.scale( _xRadius ) ) << " "
1068          << static_cast<int>( transform.mapY( _center.y ) ) << "\n";
1069 }
1070 
1071 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const1072 Ellipse::flushSVG( std::ostream & stream,
1073                    const TransformSVG & transform ) const
1074 {
1075   stream << "<ellipse cx=\"" << transform.mapX( _center.x ) << '"'
1076          << " cy=\"" << transform.mapY( _center.y ) << '"'
1077          << " rx=\"" << transform.scale( _xRadius ) << '"'
1078          << " ry=\"" << transform.scale( _yRadius ) << '"'
1079          << svgProperties( transform ) ;
1080   if ( _angle != 0.0 ) {
1081     stream << " transform=\"rotate( "
1082            << -(_angle*180/M_PI) << ", "
1083            << transform.mapX( _center.x ) << ", "
1084            << transform.mapY( _center.y ) << " )\" ";
1085   }
1086   stream << " />" << std::endl;
1087 }
1088 
1089 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const1090 Ellipse::flushTikZ( std::ostream & stream,
1091                     const TransformTikZ & transform ) const
1092 {
1093   // FIXME: unimplemented
1094   stream << "% FIXME: Ellipse::flushTikZ unimplemented" << std::endl;
1095   stream << "\\path[" << tikzProperties(transform) << "] ("
1096          << transform.mapX( _center.x ) << ','
1097          << transform.mapY( _center.y ) << ')'
1098          << " circle [x radius=" << transform.scale( _xRadius ) << ','
1099          <<          "y radius=" << transform.scale( _yRadius ) << ','
1100                   <<          "rotate=" << -(_angle*180/M_PI)
1101                            << "];"
1102                            << std::endl;
1103 }
1104 
1105 Rect
boundingBox(LineWidthFlag lineWidthFlag) const1106 Ellipse::boundingBox( LineWidthFlag lineWidthFlag ) const
1107 {
1108   Rect box;
1109   if ( _angle == 0.0 ) {
1110     box = Rect( _center.x - _xRadius, _center.y + _yRadius, 2 * _xRadius, 2 * _yRadius );
1111   } else {
1112     double angleXmax = -atan( (_yRadius/_xRadius)*(tan(_angle) ) );
1113     double angleXmin = -atan( (_yRadius/_xRadius)*(tan(_angle) ) ) + M_PI;
1114     double angleYmax =  atan( (_yRadius/_xRadius)*(1/tan(_angle) ) );
1115     double angleYmin =  M_PI + atan( (_yRadius/_xRadius)*(1/tan(_angle) ) );
1116     if ( _angle < 0.0 ) {
1117       angleYmax += M_PI;
1118       angleYmin -= M_PI;
1119     }
1120     box = Rect( _center.x + _xRadius*cos(angleXmin)*cos(_angle) - _yRadius*sin(angleXmin)*sin(_angle),
1121                 _center.y + _xRadius*cos(angleYmax)*sin(_angle) + _yRadius*sin(angleYmax)*cos(_angle),
1122                 ( _xRadius*cos(angleXmax)*cos(_angle) - _yRadius*sin(angleXmax)*sin(_angle) ) -
1123                 ( _xRadius*cos(angleXmin)*cos(_angle) - _yRadius*sin(angleXmin)*sin(_angle) ),
1124                 ( _xRadius*cos(angleYmax)*sin(_angle) + _yRadius*sin(angleYmax)*cos(_angle) ) -
1125                 ( _xRadius*cos(angleYmin)*sin(_angle) + _yRadius*sin(angleYmin)*cos(_angle) ) );
1126   }
1127   if ( lineWidthFlag == UseLineWidth ) {
1128     box.grow(0.5*_lineWidth);
1129   }
1130   return box;
1131 }
1132 
1133 /*
1134    * Circle
1135    */
1136 
1137 const std::string Circle::_name("Circle");
1138 
1139 const std::string &
name() const1140 Circle::name() const
1141 {
1142   return _name;
1143 }
1144 
1145 Point
center(LineWidthFlag) const1146 Circle::center(LineWidthFlag ) const {
1147   return _center;
1148 }
1149 
1150 Circle &
rotate(double angle,const Point & center)1151 Circle::rotate( double angle, const Point & center )
1152 {
1153   if ( _circle ) {
1154     if ( center == _center ) return *this;
1155     _center.rotate( angle, center );
1156     return *this;
1157   }
1158   Ellipse::rotate( angle, center );
1159   return *this;
1160 }
1161 
1162 Circle
rotated(double angle,const Point & center) const1163 Circle::rotated( double angle, const Point & center ) const
1164 {
1165   return Circle(*this).rotate( angle, center );
1166 }
1167 
1168 Circle &
rotate(double angle)1169 Circle::rotate( double angle )
1170 {
1171   if ( !_circle )
1172     Ellipse::rotate( angle );
1173   return *this;
1174 }
1175 
1176 Circle
rotated(double angle) const1177 Circle::rotated( double angle ) const
1178 {
1179   return Circle(*this).rotate( angle );
1180 }
1181 
1182 Circle &
translate(double dx,double dy)1183 Circle::translate( double dx, double dy )
1184 {
1185   _center += Point( dx, dy );
1186   return *this;
1187 }
1188 
1189 Circle
translated(double dx,double dy) const1190 Circle::translated( double dx, double dy ) const
1191 {
1192   return Circle(*this).translate( dx, dy );
1193 }
1194 
1195 Circle &
scale(double sx,double sy)1196 Circle::scale( double sx, double sy )
1197 {
1198   _circle = false;
1199   Ellipse::scale( sx, sy );
1200   return *this;
1201 }
1202 
1203 Circle &
scale(double s)1204 Circle::scale( double s )
1205 {
1206   Ellipse::scale( s );
1207   return *this;
1208 }
1209 
1210 Circle
scaled(double sx,double sy) const1211 Circle::scaled( double sx, double sy ) const
1212 {
1213   return Circle(*this).scale( sx, sy );
1214 }
1215 
1216 Circle
scaled(double s) const1217 Circle::scaled( double s ) const
1218 {
1219   return Circle(*this).scale( s, s );
1220 }
1221 
1222 void
scaleAll(double s)1223 Circle::scaleAll( double s )
1224 {
1225   _center *= s;
1226   _xRadius *= s;
1227   _yRadius *= s;
1228 }
1229 
1230 Circle *
clone() const1231 Circle::clone() const {
1232   return new Circle(*this);
1233 }
1234 
1235 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const1236 Circle::flushSVG( std::ostream & stream,
1237                   const TransformSVG & transform ) const
1238 {
1239   if ( ! _circle ) {
1240     Ellipse::flushSVG( stream, transform );
1241   } else {
1242     stream << "<circle cx=\"" << transform.mapX( _center.x ) << '"'
1243            << " cy=\"" << transform.mapY( _center.y ) << '"'
1244            << " r=\"" << transform.scale( _xRadius ) << '"'
1245            << svgProperties( transform )
1246            << " />" << std::endl;
1247   }
1248 }
1249 
1250 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const1251 Circle::flushTikZ( std::ostream & stream,
1252                    const TransformTikZ & transform ) const
1253 {
1254   if ( ! _circle ) {
1255     Ellipse::flushTikZ( stream, transform );
1256   } else {
1257     stream << "\\path[" << tikzProperties(transform) << "] ("
1258            << transform.mapX( _center.x ) << ','
1259            << transform.mapY( _center.y ) << ')'
1260            << " circle (" << transform.scale( _xRadius ) << ");"
1261            << std::endl;
1262   }
1263 }
1264 
1265 /*
1266  * Polyline
1267  */
1268 
1269 const std::string Polyline::_name("Polyline");
1270 
1271 const std::string &
name() const1272 Polyline::name() const
1273 {
1274   return _name;
1275 }
1276 
1277 Polyline &
operator <<(const Point & p)1278 Polyline::operator<<( const Point & p )
1279 {
1280   _path << p;
1281   return *this;
1282 }
1283 
1284 Polyline &
rotate(double angle,const Point & center)1285 Polyline::rotate( double angle, const Point & center )
1286 {
1287   _path.rotate( angle, center );
1288   return *this;
1289 }
1290 
1291 Polyline
rotated(double angle,const Point & center) const1292 Polyline::rotated( double angle, const Point & center ) const
1293 {
1294   return Polyline(*this).rotate( angle, center );
1295 }
1296 
1297 Polyline &
rotate(double angle)1298 Polyline::rotate( double angle )
1299 {
1300   _path.rotate( angle, center() );
1301   return *this;
1302 }
1303 
1304 Polyline
rotated(double angle) const1305 Polyline::rotated( double angle ) const
1306 {
1307   return Polyline(*this).rotate( angle, center() );
1308 }
1309 
1310 Polyline &
translate(double dx,double dy)1311 Polyline::translate( double dx, double dy )
1312 {
1313   _path.translate( dx, dy );
1314   return *this;
1315 }
1316 
1317 Polyline
translated(double dx,double dy) const1318 Polyline::translated( double dx, double dy ) const
1319 {
1320   return Polyline(*this).translate( dx, dy );
1321 }
1322 
1323 Polyline &
scale(double sx,double sy)1324 Polyline::scale( double sx, double sy )
1325 {
1326   _path.scale( sx, sy );
1327   updateLineWidth(std::max(sx,sy));
1328   return *this;
1329 }
1330 
1331 Polyline &
scale(double s)1332 Polyline::scale( double s )
1333 {
1334   Polyline::scale( s, s );
1335   return *this;
1336 }
1337 
1338 Polyline
scaled(double sx,double sy) const1339 Polyline::scaled( double sx, double sy ) const
1340 {
1341   return Polyline(*this).scale( sx, sy );
1342 }
1343 
1344 Polyline
scaled(double s) const1345 Polyline::scaled( double s) const
1346 {
1347   return Polyline(*this).scale( s, s );
1348 }
1349 
1350 void
scaleAll(double s)1351 Polyline::scaleAll( double s )
1352 {
1353   _path.scaleAll( s );
1354 }
1355 
1356 Polyline *
clone() const1357 Polyline::clone() const {
1358   return new Polyline(*this);
1359 }
1360 
1361 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const1362 Polyline::flushPostscript( std::ostream & stream,
1363                            const TransformEPS & transform ) const
1364 {
1365   if ( _path.empty() ) return;
1366   stream << "\n% Polyline\n";
1367   if ( filled() ) {
1368     stream << "n ";
1369     _path.flushPostscript( stream, transform );
1370     stream << " ";
1371     _fillColor.flushPostscript( stream );
1372     stream << " " << postscriptProperties(transform);
1373     stream << " fill" << std::endl;
1374   }
1375   if ( _penColor != Color::Null ) {
1376     stream << " " << postscriptProperties(transform) << "\n";
1377     stream << "n ";
1378     _path.flushPostscript( stream, transform );
1379     stream << " ";
1380     _penColor.flushPostscript( stream );
1381     stream << " stroke" << std::endl;
1382   }
1383 }
1384 
1385 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const1386 Polyline::flushFIG( std::ostream & stream,
1387                     const TransformFIG & transform,
1388                     std::map<Color,int> & colormap ) const
1389 {
1390   if ( _path.empty() )
1391     return;
1392   if ( _path.closed() )
1393     stream << "2 3 " << _lineStyle << " ";
1394   else
1395     stream << "2 1 " << _lineStyle << " ";
1396   // Thickness
1397   stream << ( _penColor.valid()?transform.mapWidth( _lineWidth ):0 ) << " ";
1398   // Pen color
1399   stream << colormap[ _penColor ] << " ";
1400   // Fill color
1401   stream << colormap[ _fillColor ] << " ";
1402   // Depth
1403   stream << transform.mapDepth( _depth ) << " ";
1404   // Pen style
1405   stream <<  "-1 ";
1406   // Area fill, style val, join style, cap style, radius, f_arrow, b_arrow
1407   if ( filled() )
1408     stream << "20 " << (_lineStyle?"4.000 ":"0.000 ") << _lineJoin << " " << _lineCap << " -1 0 0 ";
1409   else
1410     stream << "-1 " << (_lineStyle?"4.000 ":"0.000 ")  << _lineJoin << " " << _lineCap << " -1 0 0 ";
1411   // Number of points
1412   stream << _path.size() + _path.closed() << std::endl;
1413   _path.flushFIG( stream << "         ", transform );
1414   stream << std::endl;
1415 }
1416 
1417 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const1418 Polyline::flushSVG( std::ostream & stream,
1419                     const TransformSVG & transform ) const
1420 {
1421   if ( _path.empty() )
1422     return;
1423   if ( _path.closed() )
1424     stream << "<polygon";
1425   else
1426     stream << "<polyline";
1427   stream << svgProperties( transform ) << std::endl;
1428   stream << "          points=\"";
1429   _path.flushSVGPoints( stream, transform );
1430   stream << "\" />" << std::endl;
1431 }
1432 
1433 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const1434 Polyline::flushTikZ( std::ostream & stream,
1435                      const TransformTikZ & transform ) const
1436 {
1437   if ( _path.empty() )
1438     return;
1439 
1440   stream << "\\path[" << tikzProperties(transform) << "] ";
1441   _path.flushTikZPoints( stream, transform );
1442   if ( _path.closed() )
1443     stream << " -- cycle";
1444   stream << ";" << std::endl;
1445 }
1446 
1447 Rect
boundingBox(LineWidthFlag lineWidthFlag) const1448 Polyline::boundingBox(LineWidthFlag lineWidthFlag) const
1449 {
1450   switch (lineWidthFlag) {
1451   case UseLineWidth:
1452     return Tools::pathBoundingBox(_path,_lineWidth,_lineCap,_lineJoin);
1453     break;
1454   case IgnoreLineWidth:
1455     return _path.boundingBox();
1456     break;
1457   default:
1458     Tools::error << "LineWidthFlag incorrect value (" << lineWidthFlag << ")\n";
1459     return Rect();
1460     break;
1461   }
1462 }
1463 
1464 /*
1465  * Rectangle
1466  */
1467 
1468 const std::string Rectangle::_name("Rectangle");
1469 
1470 const std::string &
name() const1471 Rectangle::name() const
1472 {
1473   return _name;
1474 }
1475 
1476 Rectangle
rotated(double angle,const Point & center) const1477 Rectangle::rotated( double angle, const Point & center ) const
1478 {
1479   return static_cast<Rectangle &>( Rectangle(*this).rotate( angle, center ) );
1480 }
1481 
1482 Rectangle
rotated(double angle) const1483 Rectangle::rotated( double angle ) const
1484 {
1485   return static_cast<Rectangle &>( Rectangle(*this).rotate( angle, center() ) );
1486 }
1487 
1488 Rectangle
translated(double dx,double dy) const1489 Rectangle::translated( double dx, double dy ) const
1490 {
1491   return static_cast<Rectangle &>( Rectangle(*this).translate( dx, dy ) );
1492 }
1493 
1494 Rectangle
scaled(double sx,double sy) const1495 Rectangle::scaled( double sx, double sy ) const
1496 {
1497   return static_cast<Rectangle &>( Rectangle(*this).scale( sx, sy ) );
1498 }
1499 
1500 Rectangle
scaled(double s) const1501 Rectangle::scaled( double s ) const
1502 {
1503   return static_cast<Rectangle &>( Rectangle(*this).scale( s, s ) );
1504 }
1505 
1506 void
scaleAll(double s)1507 Rectangle::scaleAll( double s )
1508 {
1509   _path.scaleAll( s );
1510 }
1511 
1512 Rectangle *
clone() const1513 Rectangle::clone() const {
1514   return new Rectangle(*this);
1515 }
1516 
1517 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const1518 Rectangle::flushFIG( std::ostream & stream,
1519                      const TransformFIG & transform,
1520                      std::map<Color,int> & colormap ) const
1521 {
1522   if ( _path[0].y != _path[1].y ) {
1523     Polyline::flushFIG( stream, transform, colormap );
1524     return;
1525   }
1526   if ( _path[0].x != _path[3].x ) {
1527     Polyline::flushFIG( stream, transform, colormap );
1528     return;
1529   }
1530   {
1531     double x1 = _path[1].x - _path[0].x;
1532     double y1 = _path[1].y - _path[0].y;
1533     double x2 = _path[3].x - _path[0].x;
1534     double y2 = _path[3].y - _path[0].y;
1535     if ( fabs(x1*x2 + y1*y2) > 0.01 ) {
1536       Polyline::flushFIG( stream, transform, colormap );
1537       return;
1538     }
1539   }
1540   stream << "2 2 " << _lineStyle << " ";
1541   // Thickness
1542   stream << ( _penColor.valid()?transform.mapWidth( _lineWidth ):0 ) << " ";
1543   // Pen color
1544   stream << colormap[ _penColor ] << " ";
1545   // Fill color
1546   stream << colormap[ _fillColor ] << " ";
1547   // Depth
1548   stream << transform.mapDepth( _depth ) << " ";
1549   // Pen style
1550   stream <<  "-1 ";
1551   // Area fill, style val, join style, cap style, radius, f_arrow, b_arrow, number of points
1552   if ( filled() )
1553     stream << "20 " << (_lineStyle?"4.000 ":"0.000 ") << _lineJoin << " " << _lineCap << " -1 0 0 5\n";
1554   else
1555     stream << "-1 " << (_lineStyle?"4.000 ":"0.000 ") << _lineJoin << " " << _lineCap << " -1 0 0 5\n";
1556   stream << "         ";
1557   _path.flushFIG( stream, transform );
1558   stream << std::endl;
1559 }
1560 
1561 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const1562 Rectangle::flushSVG( std::ostream & stream,
1563                      const TransformSVG & transform ) const
1564 {
1565   {
1566     double x1 = _path[1].x - _path[0].x;
1567     double y1 = _path[1].y - _path[0].y;
1568     double x2 = _path[3].x - _path[0].x;
1569     double y2 = _path[3].y - _path[0].y;
1570     if ( fabs(x1*x2 + y1*y2) > 0.001  ) {
1571       Polyline::flushSVG( stream, transform );
1572       return;
1573     }
1574   }
1575 
1576   if ( _path[0].y == _path[1].y ) {
1577     stream << "<rect x=\""
1578            << transform.mapX( _path[0].x )
1579         << '"'
1580         << " y=\"" << transform.mapY( _path[0].y )
1581         << '"'
1582         << " width=\"" << transform.scale( _path[1].x - _path[0].x )
1583         << '"'
1584         << " height=\"" << transform.scale( _path[0].y - _path[3].y )
1585         << '"'
1586         << svgProperties( transform )
1587         << " />" << std::endl;
1588   } else {
1589     Point v = _path[1] - _path[0];
1590     v /= v.norm();
1591     double angle = ( _path[1].y > _path[0].y ) ? acos( v * Point(1,0) ) : -acos( v * Point( 1, 0 ) );
1592     angle = ( angle * 180 ) / M_PI;
1593     stream << "<rect x=\""
1594            << transform.mapX( _path[0].x )
1595         << '"'
1596         << " y=\""
1597         << transform.mapY( _path[0].y )
1598         << '"'
1599         << " width=\""
1600         << transform.scale( (_path[1] - _path[0]).norm() )
1601         << '"'
1602         << " height=\""
1603         << transform.scale( (_path[0] - _path[3]).norm() )
1604         << '"'
1605         << svgProperties( transform )
1606         << ' '
1607         << " transform=\"rotate(" << -angle << ", "
1608         << transform.mapX( _path[0].x )
1609         << ", "
1610         << transform.mapY( _path[0].y )
1611         << ") \" "
1612         << " />"
1613         << std::endl;
1614   }
1615 }
1616 
1617 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const1618 Rectangle::flushTikZ( std::ostream & stream,
1619                       const TransformTikZ & transform ) const
1620 {
1621   Polyline::flushTikZ( stream, transform );
1622   return;
1623 }
1624 
1625 /*
1626  * GouraudTriangle
1627  */
1628 
1629 const std::string GouraudTriangle::_name("GouraudTriangle");
1630 
1631 const std::string &
name() const1632 GouraudTriangle::name() const
1633 {
1634   return _name;
1635 }
1636 
GouraudTriangle(const Point & p0,const Color & color0,const Point & p1,const Color & color1,const Point & p2,const Color & color2,int subdivisions,int depth)1637 GouraudTriangle::GouraudTriangle( const Point & p0, const Color & color0,
1638                                   const Point & p1, const Color & color1,
1639                                   const Point & p2, const Color & color2,
1640                                   int subdivisions,
1641                                   int depth )
1642   : Polyline( std::vector<Point>(), true, Color::Null, Color::Null,
1643               0.0f, SolidStyle, ButtCap, MiterJoin, depth ),
1644     _color0( color0 ), _color1( color1 ), _color2( color2 ), _subdivisions( subdivisions ) {
1645   _path << p0;
1646   _path << p1;
1647   _path << p2;
1648 
1649   Shape::_fillColor.red( ( color0.red() + color1.red() + color2.red() ) / 3 );
1650   Shape::_fillColor.green( ( color0.green() + color1.green() + color2.green() ) / 3 );
1651   Shape::_fillColor.blue( ( color0.blue() + color1.blue() + color2.blue() ) / 3 );
1652 }
1653 
GouraudTriangle(const Point & p0,float brightness0,const Point & p1,float brightness1,const Point & p2,float brightness2,const Color & _fillColor,int subdivisions,int depth)1654 GouraudTriangle::GouraudTriangle( const Point & p0, float brightness0,
1655                                   const Point & p1, float brightness1,
1656                                   const Point & p2, float brightness2,
1657                                   const Color & _fillColor,
1658                                   int subdivisions,
1659                                   int depth )
1660   : Polyline( std::vector<Point>(), true, Color::Null, Color::Null,
1661               0.0f, SolidStyle, ButtCap, MiterJoin, depth ),
1662     _color0( _fillColor ), _color1( _fillColor ), _color2( _fillColor ), _subdivisions( subdivisions )
1663 {
1664   _path << p0;
1665   _path << p1;
1666   _path << p2;
1667   _color0.red( static_cast<unsigned char>( std::min( 255.0f, _color0.red() * brightness0 ) ) );
1668   _color0.green( static_cast<unsigned char>( std::min( 255.0f, _color0.green() * brightness0 ) ) );
1669   _color0.blue( static_cast<unsigned char>( std::min( 255.0f, _color0.blue() * brightness0 ) ) );
1670   _color1.red( static_cast<unsigned char>( std::min( 255.0f, _color1.red() * brightness1 ) ) );
1671   _color1.green( static_cast<unsigned char>( std::min( 255.0f, _color1.green() * brightness1 ) ) );
1672   _color1.blue( static_cast<unsigned char>( std::min( 255.0f, _color1.blue() * brightness1 ) ) );
1673   _color2.red( static_cast<unsigned char>( std::min( 255.0f, _color2.red() * brightness2 ) ) );
1674   _color2.green( static_cast<unsigned char>( std::min( 255.0f, _color2.green() * brightness2 ) ) );
1675   _color2.blue( static_cast<unsigned char>( std::min( 255.0f, _color2.blue() * brightness2 ) ) );
1676 
1677   Shape::_fillColor.red( ( _color0.red() + _color1.red() + _color2.red() ) / 3 );
1678   Shape::_fillColor.green( ( _color0.green() + _color1.green() + _color2.green() ) / 3 );
1679   Shape::_fillColor.blue( ( _color0.blue() + _color1.blue() + _color2.blue() ) / 3 );
1680 }
1681 
1682 GouraudTriangle &
rotate(double angle,const Point & center)1683 GouraudTriangle::rotate( double angle, const Point & center )
1684 {
1685   _path.rotate( angle, center );
1686   return *this;
1687 }
1688 
1689 GouraudTriangle
rotated(double angle,const Point & center) const1690 GouraudTriangle::rotated( double angle, const Point & center ) const
1691 {
1692   return GouraudTriangle(*this).rotate( angle, center );
1693 }
1694 
1695 GouraudTriangle &
rotate(double angle)1696 GouraudTriangle::rotate( double angle )
1697 {
1698   return GouraudTriangle::rotate( angle, center() );
1699 }
1700 
1701 GouraudTriangle
rotated(double angle) const1702 GouraudTriangle::rotated( double angle ) const
1703 {
1704   return GouraudTriangle(*this).rotate( angle, center() );
1705 }
1706 
1707 GouraudTriangle
translated(double dx,double dy) const1708 GouraudTriangle::translated( double dx, double dy ) const
1709 {
1710   return static_cast<GouraudTriangle &>( GouraudTriangle(*this).translate( dx, dy ) );
1711 }
1712 
1713 GouraudTriangle
scaled(double sx,double sy) const1714 GouraudTriangle::scaled( double sx, double sy ) const
1715 {
1716   return static_cast<GouraudTriangle &>( GouraudTriangle(*this).scale( sx, sy ) );
1717 }
1718 
1719 GouraudTriangle
scaled(double s) const1720 GouraudTriangle::scaled( double s ) const
1721 {
1722   return static_cast<GouraudTriangle &>( GouraudTriangle(*this).scale( s, s ) );
1723 }
1724 
1725 void
scaleAll(double s)1726 GouraudTriangle::scaleAll( double s )
1727 {
1728   _path.scaleAll( s );
1729 }
1730 
1731 GouraudTriangle *
clone() const1732 GouraudTriangle::clone() const {
1733   return new GouraudTriangle(*this);
1734 }
1735 
1736 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const1737 GouraudTriangle::flushPostscript( std::ostream & stream,
1738                                   const TransformEPS & transform ) const
1739 {
1740   if ( ! _subdivisions ) {
1741     Polyline::flushPostscript( stream, transform );
1742     return;
1743   }
1744   const Point & p0 = _path[0];
1745   const Point & p1 = _path[1];
1746   const Point & p2 = _path[2];
1747   Point p01( 0.5*(p0.x+p1.x), 0.5*(p0.y+p1.y) );
1748   Color c01( (_color0.red() + _color1.red())/2,
1749              (_color0.green() + _color1.green())/2,
1750              (_color0.blue() + _color1.blue())/2 );
1751   Point p12( 0.5*(p1.x+p2.x), 0.5*(p1.y+p2.y) );
1752   Color c12( (_color1.red() + _color2.red())/2,
1753              (_color1.green() + _color2.green())/2,
1754              (_color1.blue() + _color2.blue())/2 );
1755   Point p20( 0.5*(p2.x+p0.x), 0.5*(p2.y+p0.y) );
1756   Color c20( (_color2.red() + _color0.red())/2,
1757              (_color2.green() + _color0.green())/2,
1758              (_color2.blue() + _color0.blue())/2 );
1759   GouraudTriangle( p0, _color0, p20, c20, p01, c01, _subdivisions - 1, _depth ).flushPostscript( stream, transform );
1760   GouraudTriangle( p1, _color1, p01, c01, p12, c12, _subdivisions - 1, _depth ).flushPostscript( stream, transform );
1761   GouraudTriangle( p2, _color2, p20, c20, p12, c12, _subdivisions - 1, _depth ).flushPostscript( stream, transform );
1762   GouraudTriangle( p01, c01, p12, c12, p20, c20,  _subdivisions - 1, _depth ).flushPostscript( stream, transform );
1763 }
1764 
1765 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const1766 GouraudTriangle::flushFIG( std::ostream & stream,
1767                            const TransformFIG & transform,
1768                            std::map<Color,int> & colormap ) const
1769 {
1770 
1771   Color c( static_cast<unsigned char>((_color0.red() + _color1.red() + _color2.red() )/3.0),
1772            static_cast<unsigned char>((_color0.green() + _color1.green() + _color2.green())/3.0),
1773            static_cast<unsigned char>((_color0.blue() + _color1.blue() + _color2.blue())/3.0 ));
1774   Polyline( _path, Color::Null, c, 0.0f ).flushFIG( stream, transform, colormap );
1775 
1776   // if ( ! _subdivisions ) {
1777   // Polyline::flushFIG( stream, transform, colormap );
1778   // return;
1779   // }
1780   // TODO : Handle extended colormap through clustering...
1781   //   const Point & p0 = _points[0];
1782   //   const Point & p1 = _points[1];
1783   //   const Point & p2 = _points[2];
1784   //   Point p01( 0.5*(p0.x+p1.x), 0.5*(p0.y+p1.y) );
1785   //   Color c01( (_color0.red() + _color1.red())/2,
1786   // 	     (_color0.green() + _color1.green())/2,
1787   // 	     (_color0.blue() + _color1.blue())/2 );
1788   //   Point p12( 0.5*(p1.x+p2.x), 0.5*(p1.y+p2.y) );
1789   //   Color c12( (_color1.red() + _color2.red())/2,
1790   // 	     (_color1.green() + _color2.green())/2,
1791   // 	     (_color1.blue() + _color2.blue())/2 );
1792   //   Point p20( 0.5*(p2.x+p0.x), 0.5*(p2.y+p0.y) );
1793   //   Color c20( (_color2.red() + _color0.red())/2,
1794   // 	     (_color2.green() + _color0.green())/2,
1795   // 	     (_color2.blue() + _color0.blue())/2 );
1796   //   GouraudTriangle( p0, _color0, p20, c20, p01, c01, _subdivisions - 1, _depth ).flushFIG( stream, transform, colormap );
1797   //   GouraudTriangle( p1, _color1, p01, c01, p12, c12, _subdivisions - 1, _depth ).flushFIG( stream, transform, colormap );
1798   //   GouraudTriangle( p2, _color2, p20, c20, p12, c12, _subdivisions - 1, _depth ).flushFIG( stream, transform, colormap );
1799   //   GouraudTriangle( p01, c01, p12, c12, p20, c20,  _subdivisions - 1, _depth ).flushFIG( stream, transform, colormap );
1800 }
1801 
1802 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const1803 GouraudTriangle::flushSVG( std::ostream & stream,
1804                            const TransformSVG & transform ) const
1805 {
1806   if ( ! _subdivisions ) {
1807     Polyline::flushSVG( stream, transform );
1808     return;
1809   }
1810   const Point & p0 = _path[0];
1811   const Point & p1 = _path[1];
1812   const Point & p2 = _path[2];
1813   Point p01( 0.5*(p0.x+p1.x), 0.5*(p0.y+p1.y) );
1814   Color c01( (_color0.red() + _color1.red())/2,
1815              (_color0.green() + _color1.green())/2,
1816              (_color0.blue() + _color1.blue())/2 );
1817   Point p12( 0.5*(p1.x+p2.x), 0.5*(p1.y+p2.y) );
1818   Color c12( (_color1.red() + _color2.red())/2,
1819              (_color1.green() + _color2.green())/2,
1820              (_color1.blue() + _color2.blue())/2 );
1821   Point p20( 0.5*(p2.x+p0.x), 0.5*(p2.y+p0.y) );
1822   Color c20( (_color2.red() + _color0.red())/2,
1823              (_color2.green() + _color0.green())/2,
1824              (_color2.blue() + _color0.blue())/2 );
1825   GouraudTriangle( p0, _color0, p20, c20, p01, c01,
1826                    _subdivisions - 1, _depth ).flushSVG( stream, transform );
1827   GouraudTriangle( p1, _color1, p01, c01, p12, c12,
1828                    _subdivisions - 1, _depth ).flushSVG( stream, transform );
1829   GouraudTriangle( p2, _color2, p20, c20, p12, c12,
1830                    _subdivisions - 1, _depth ).flushSVG( stream, transform );
1831   GouraudTriangle( p01, c01, p12, c12, p20, c20,
1832                    _subdivisions - 1, _depth ).flushSVG( stream, transform );
1833 }
1834 
1835 void
flushTikZ(std::ostream & stream,const TransformTikZ &) const1836 GouraudTriangle::flushTikZ( std::ostream & stream,
1837                             const TransformTikZ & /*transform*/ ) const
1838 {
1839   // FIXME: unimplemented
1840   stream << "% FIXME: GouraudTriangle::flushTikZ unimplemented" << std::endl;
1841 }
1842 
1843 /*
1844  * Triangle
1845  */
1846 
1847 const std::string Triangle::_name("Triangle");
1848 
1849 const std::string &
name() const1850 Triangle::name() const
1851 {
1852   return _name;
1853 }
1854 
1855 Triangle
rotated(double angle) const1856 Triangle::rotated( double angle ) const
1857 {
1858   return static_cast<Triangle &>( Triangle( *this ).rotate( angle ) );
1859 }
1860 
1861 Triangle
translated(double dx,double dy) const1862 Triangle::translated( double dx, double dy ) const
1863 {
1864   return static_cast<Triangle &>( Triangle( *this ).translate( dx, dy ) );
1865 }
1866 
1867 Triangle
scaled(double sx,double sy) const1868 Triangle::scaled( double sx, double sy ) const
1869 {
1870   return static_cast<Triangle &>( Triangle( *this ).scale( sx, sy ) );
1871 }
1872 
1873 Triangle
scaled(double s) const1874 Triangle::scaled( double s ) const
1875 {
1876   return static_cast<Triangle &>( Triangle( *this ).scale( s ) );
1877 }
1878 
1879 Triangle *
clone() const1880 Triangle::clone() const {
1881   return new Triangle(*this);
1882 }
1883 
1884 /*
1885  * Text
1886  */
1887 
1888 const std::string Text::_name("Text");
1889 
1890 
Text(double x,double y,const std::string & text,const Fonts::Font font,double size,Color color,int depth)1891 Text::Text( double x, double y,
1892             const std::string & text,
1893             const Fonts::Font font,
1894             double size,
1895             Color color,
1896             int depth )
1897   : Shape( color, Color::Null, 1.0, SolidStyle, ButtCap, MiterJoin, depth ),
1898     _text( text ), _font( font ),
1899     _size( size ),
1900     _xScale( 1.0 ), _yScale( 1.0 )
1901 {
1902   const double width = text.length() * size * 0.71;   // Why 0.71? Well, give it a try!
1903   _box << Point(x,y);
1904   _box << _box[0] + Point(width,0);
1905   _box << _box[0] + Point(width,size);
1906   _box << _box[0] + Point(0,size);
1907   _box.setClosed(true);
1908 }
1909 
Text(Point p,const std::string & text,const Fonts::Font font,double size,Color color,int depth)1910 Text::Text(Point p,
1911            const std::string & text,
1912            const Fonts::Font font,
1913            double size,
1914            Color color,
1915            int depth)
1916   : Shape( color, Color::Null, 1.0, SolidStyle, ButtCap, MiterJoin, depth ),
1917     _text( text ), _font( font ),
1918     _size( size ),
1919     _xScale( 1.0 ), _yScale( 1.0 )
1920 {
1921   const double width = text.length() * size * 0.71;   // Why 0.71? Well, give it a try!
1922   _box << p;
1923   _box << _box[0] + Point(width,0);
1924   _box << _box[0] + Point(width,size);
1925   _box << _box[0] + Point(0,size);
1926   _box.setClosed(true);
1927 }
1928 
Text(double x,double y,const std::string & text,const Fonts::Font font,const std::string & svgFont,double size,Color color,int depth)1929 Text::Text( double x, double y,
1930             const std::string & text,
1931             const Fonts::Font font,
1932             const std::string & svgFont,
1933             double size,
1934             Color color,
1935             int depth )
1936   : Shape( color, Color::Null, 1.0, SolidStyle, ButtCap, MiterJoin, depth ),
1937     _text( text ), _font( font ), _svgFont( svgFont ),
1938     _size( size ),
1939     _xScale( 1.0 ), _yScale( 1.0 )
1940 {
1941   const double width = text.length() * size * 0.71;   // Why 0.71? Well, give it a try!
1942   _box << Point(x,y);
1943   _box << _box[0] + Point(width,0);
1944   _box << _box[0] + Point(width,size);
1945   _box << _box[0] + Point(0,size);
1946   _box.setClosed(true);
1947 }
1948 
Text(Point p,const std::string & text,const Fonts::Font font,const std::string & svgFont,double size,Color color,int depth)1949 Text::Text(Point p,
1950            const std::string & text,
1951            const Fonts::Font font,
1952            const std::string & svgFont,
1953            double size,
1954            Color color,
1955            int depth)
1956   : Shape( color, Color::Null, 1.0, SolidStyle, ButtCap, MiterJoin, depth ),
1957     _text( text ), _font( font ), _svgFont( svgFont ),
1958     _size( size ),
1959     _xScale( 1.0 ), _yScale( 1.0 )
1960 {
1961   const double width = text.length() * size * 0.71;   // Why 0.71? Well, give it a try!
1962   _box << p;
1963   _box << _box[0] + Point(width,0);
1964   _box << _box[0] + Point(width,size);
1965   _box << _box[0] + Point(0,size);
1966   _box.setClosed(true);
1967 }
1968 
1969 const std::string &
name() const1970 Text::name() const
1971 {
1972   return _name;
1973 }
1974 
1975 Point
center(LineWidthFlag) const1976 Text::center(LineWidthFlag ) const {
1977   return _box.center();
1978 }
1979 
1980 Text &
rotate(double angle,const Point & center)1981 Text::rotate( double angle, const Point & center )
1982 {
1983   _box.rotate(angle,center);
1984   //  Point endPos = _position + Point( 10000 * cos( _angle ), 10000 * sin( _angle ) );
1985   //  _position.rotate( angle, center );
1986   //  endPos.rotate( angle, center );
1987   //  Point v = endPos - _position;
1988   //  v /= v.norm();
1989   //  if ( v.x >= 0 ) _angle = asin( v.y );
1990   //  else if ( v.y > 0 ) _angle = (M_PI/2.0) + acos( v.y );
1991   //  else _angle = (-M_PI/2.0) - acos( -v.y );
1992   return *this;
1993 }
1994 
1995 Text
rotated(double angle,const Point & center) const1996 Text::rotated( double angle, const Point & center ) const
1997 {
1998   return Text(*this).rotate( angle, center );
1999 }
2000 
2001 Text &
rotate(double angle)2002 Text::rotate( double angle )
2003 {
2004   _box.rotate(angle);
2005   //  _angle += angle;
2006   //  while ( _angle < -M_PI ) {
2007   //    _angle += 2 * M_PI;
2008   //  }
2009   //  while ( _angle > M_PI ) {
2010   //    _angle -= 2 * M_PI;
2011   //  }
2012   return *this;
2013 }
2014 
2015 Text
rotated(double angle) const2016 Text::rotated( double angle ) const
2017 {
2018   return Text(*this).rotate( angle );
2019 }
2020 
2021 Text &
translate(double dx,double dy)2022 Text::translate( double dx, double dy )
2023 {
2024   _box.translate(dx,dy);
2025   return *this;
2026 }
2027 
2028 Text
translated(double dx,double dy) const2029 Text::translated( double dx, double dy ) const
2030 {
2031   return static_cast<Text&>( Text(*this).translate( dx, dy ) );
2032 }
2033 
2034 Text &
scale(double sx,double sy)2035 Text::scale( double sx, double sy )
2036 {
2037   // TODO Actually handle scaling.
2038   _xScale *= sx;
2039   _yScale *= sy;
2040   _box.scale(sx,sy);
2041   return *this;
2042 }
2043 
2044 Text &
scale(double s)2045 Text::scale( double s )
2046 {
2047   _xScale *= s;
2048   _yScale *= s;
2049   _box.scale(s);
2050   return *this;
2051 }
2052 
2053 Text
scaled(double sx,double sy) const2054 Text::scaled( double sx, double sy ) const
2055 {
2056   return Text(*this).scale( sx, sy );
2057 }
2058 
2059 Text
scaled(double s) const2060 Text::scaled( double s ) const
2061 {
2062   return Text(*this).scale( s, s );
2063 }
2064 
2065 void
scaleAll(double s)2066 Text::scaleAll( double s )
2067 {
2068   _box.scaleAll(s);
2069 }
2070 
2071 Text *
clone() const2072 Text::clone() const {
2073   return new Text(*this);
2074 }
2075 
boxHeight(const Transform & transform) const2076 double Text::boxHeight(const Transform & transform) const
2077 {
2078   Point baseline = (_box[1] - _box[0]);
2079   Point leftSide = (_box[3] - _box[0]);
2080   Point transformedLeftSide( transform.map(_box[3]) - transform.map(_box[0]));
2081   double angle = std::acos(baseline.normalised() * leftSide.normalised());
2082   if ( std::fabs(2*angle - M_PI) < std::numeric_limits<float>::epsilon() ) {
2083     return transformedLeftSide.norm();
2084   }
2085   return std::sin(angle) * transformedLeftSide.norm();
2086 }
2087 
boxLength(const Transform & transform) const2088 double Text::boxLength(const Transform & transform) const
2089 {
2090   Point transformedBaseline( transform.map(_box[1]) - transform.map(_box[0]));
2091   return transformedBaseline.norm();
2092 }
2093 
2094 double
angle() const2095 Text::angle() const
2096 {
2097   Point v = (_box[1] - _box[0]).normalise();
2098   return std::atan2(v.y,v.x);
2099 }
2100 
2101 Point
position() const2102 Text::position() const
2103 {
2104   return _box[0];
2105 }
2106 
2107 void
flushPostscript(std::ostream & stream,const TransformEPS & transform) const2108 Text::flushPostscript( std::ostream & stream,
2109                        const TransformEPS & transform ) const
2110 {
2111   stream << "\n% Text\n";
2112   stream << "gs /" << PSFontNames[ _font ] << " ff " << boxHeight(transform) << " scf sf";
2113   stream << " " << transform.mapX( position().x ) << " " << transform.mapY( position().y ) << " m";
2114   if ( angle() != 0.0 ) {
2115     stream << " " << (angle()*180.0/M_PI) << " rot ";
2116   }
2117   stream << " (" << _text << ")"
2118          << " " << _penColor.postscript() << " srgb"
2119          << " sh gr" << std::endl;
2120 }
2121 
2122 void
flushFIG(std::ostream & stream,const TransformFIG & transform,std::map<Color,int> & colormap) const2123 Text::flushFIG( std::ostream & stream,
2124                 const TransformFIG & transform,
2125                 std::map<Color,int> & colormap ) const
2126 {
2127   const float ppmm = 720.0f / 254.0f;
2128   const float fig_ppmm = 45.0f;
2129 
2130 
2131   stream << "4 0 " ;
2132   // Color, depth, unused, Font
2133   stream << colormap[ _penColor ] <<  " " << transform.mapDepth( _depth ) << " -1 " << _font << " ";
2134   // Font size, Angle, Font flags
2135   stream << (ppmm * boxHeight(transform) / fig_ppmm) << " " << angle() << " 4 ";
2136   // Height
2137   stream << static_cast<int>( boxHeight(transform) /* * 135 / 12.0 */ ) << " ";
2138   // Width
2139   stream << static_cast<int>( boxLength(transform) /* * 135 / 12.0 */ ) << " ";
2140   // x y
2141   stream << static_cast<int>( transform.mapX( position().x ) ) << " "
2142          << static_cast<int>( transform.mapY( position().y ) ) << " ";
2143   stream << _text << "\\001\n";
2144 }
2145 
2146 void
flushSVG(std::ostream & stream,const TransformSVG & transform) const2147 Text::flushSVG( std::ostream & stream,
2148                 const TransformSVG & transform ) const
2149 {
2150   if ( angle() != 0.0f ) {
2151     stream << "<g transform=\"translate("
2152            << transform.mapX( position().x ) << ","
2153            << transform.mapY( position().y ) << ")\" >"
2154            << "<g transform=\"rotate(" << (-angle()*180.0/M_PI) << ")\" >"
2155            << "<text x=\"0\" y=\"0\""
2156            << " font-family=\"" << ( _svgFont.length() ? _svgFont : PSFontNames[ _font ] ) << "\""
2157            << " font-size=\"" << boxHeight(transform) << "\""
2158            << " fill=\"" << _penColor.svg() << "\""
2159            << _fillColor.svgAlpha( " fill" )
2160            << _penColor.svgAlpha( " stroke" )
2161            << ">"
2162            << _text
2163            << "</text></g></g>" << std::endl;
2164   } else {
2165     stream << "<text x=\"" << transform.mapX( position().x )
2166            << "\" y=\"" << transform.mapY( position().y ) << "\" "
2167            << " font-family=\"" << ( _svgFont.length() ? _svgFont : PSFontNames[ _font ] ) << "\""
2168            << " font-size=\"" << boxHeight(transform) << "\""
2169            << " fill=\"" << _penColor.svg() << "\""
2170            << _fillColor.svgAlpha( " fill" )
2171            << _penColor.svgAlpha( " stroke" )
2172            << ">"
2173            << _text
2174            << "</text>" << std::endl;
2175   }
2176   // DEBUG
2177   // Polyline(_box,Color::Black,Color::None,0.5).flushSVG(stream,transform);
2178 }
2179 
2180 void
flushTikZ(std::ostream & stream,const TransformTikZ & transform) const2181 Text::flushTikZ( std::ostream & stream,
2182                  const TransformTikZ & transform ) const
2183 {
2184   // FIXME: honor font-size
2185   static const char BOLD_FONT =	0x01;
2186   static const char ITALIC_FONT = 0x02;
2187   static const char MONOSPACE_FONT = 0x04;
2188   static const char SANSSERIF_FONT = 0x08;
2189   char fontTraits[] = {
2190     0,						// TimesRoman,
2191     ITALIC_FONT,					// TimesItalic,
2192     BOLD_FONT,					// TimesBold,
2193     BOLD_FONT | ITALIC_FONT,			// TimesBoldItalic,
2194     SANSSERIF_FONT,					// AvantGardeBook,
2195     SANSSERIF_FONT | ITALIC_FONT,			// AvantGardeBookOblique,
2196     SANSSERIF_FONT,					// AvantGardeDemi,
2197     SANSSERIF_FONT | ITALIC_FONT,			// AvantGardeDemiOblique,
2198     0,						// BookmanLight,
2199     ITALIC_FONT,					// BookmanLightItalic,
2200     0,						// BookmanDemi,
2201     ITALIC_FONT,					// BookmanDemiItalic,
2202     MONOSPACE_FONT,					// Courier,
2203     MONOSPACE_FONT | ITALIC_FONT,			// CourierOblique,
2204     MONOSPACE_FONT | BOLD_FONT,			// CourierBold,
2205     MONOSPACE_FONT | BOLD_FONT | ITALIC_FONT,	// CourierBoldOblique,
2206     SANSSERIF_FONT,					// Helvetica,
2207     SANSSERIF_FONT | ITALIC_FONT,			// HelveticaOblique,
2208     SANSSERIF_FONT | BOLD_FONT,			// HelveticaBold,
2209     SANSSERIF_FONT | BOLD_FONT | ITALIC_FONT,	// HelveticaBoldOblique,
2210     SANSSERIF_FONT,					// HelveticaNarrow,
2211     SANSSERIF_FONT | ITALIC_FONT,			// HelveticaNarrowOblique,
2212     SANSSERIF_FONT | BOLD_FONT,			// HelveticaNarrowBold,
2213     SANSSERIF_FONT | BOLD_FONT | ITALIC_FONT,	// HelveticaNarrowBoldOblique,
2214     0,						// NewCenturySchoolbookRoman,
2215     ITALIC_FONT,					// NewCenturySchoolbookItalic,
2216     BOLD_FONT,					// NewCenturySchoolbookBold,
2217     BOLD_FONT | ITALIC_FONT,			// NewCenturySchoolbookBoldItalic,
2218     0,						// PalatinoRoman,
2219     ITALIC_FONT,					// PalatinoItalic,
2220     BOLD_FONT,					// PalatinoBold,
2221     BOLD_FONT | ITALIC_FONT,			// PalatinoBoldItalic,
2222     0,						// Symbol,
2223     ITALIC_FONT,					// ZapfChanceryMediumItalic,
2224     0						// ZapfDingbats
2225   };
2226 
2227   stream << "\\path[" << tikzProperties(transform) << "] ("
2228          << transform.mapX( position().x ) << ',' << transform.mapY( position().y )
2229          << ") node {"
2230          << (fontTraits[ _font ] & ITALIC_FONT ? "\\itshape " : "")
2231          << (fontTraits[ _font ] & BOLD_FONT ? "\\bfseries " : "")
2232          << (fontTraits[ _font ] & MONOSPACE_FONT ? "\\ttfamily " : "")
2233          << (fontTraits[ _font ] & SANSSERIF_FONT ? "\\sffamily " : "")
2234          << _text
2235          << "};" << std::endl;
2236 }
2237 
2238 Rect
boundingBox(LineWidthFlag) const2239 Text::boundingBox( LineWidthFlag ) const
2240 {
2241   // TODO Get correct values!
2242   return _box.boundingBox();
2243 }
2244 
2245 } // namespace LibBoard
2246