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