A library for working with phylogenetic and population genetic data.
v0.32.0
attributes.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2023 Lucas Czech
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
37 
38 #include <algorithm>
39 #include <cmath>
40 #include <ostream>
41 
42 namespace genesis {
43 namespace utils {
44 
45 // =================================================================================================
46 // Svg Stroke
47 // =================================================================================================
48 
49 // -------------------------------------------------------------
50 // Constructors and Rule of Five
51 // -------------------------------------------------------------
52 
54  : type( type )
55  , color()
56  , width( 1.0 )
57  , width_unit()
58  , line_cap( LineCap::kOmit )
59  , line_join( LineJoin::kOmit )
60  , miterlimit( 1.0 )
61  , dash_array()
62  , dash_offset( 0.0 )
63 {}
64 
65 SvgStroke::SvgStroke( Color color_value, double width_value )
66  : SvgStroke( Type::kColor )
67 {
68  color = color_value;
69  width = width_value;
70 }
71 
72 SvgStroke::SvgStroke( std::string gradient_id_value )
73  : SvgStroke( Type::kGradient )
74 {
75  gradient_id = gradient_id_value;
76 }
77 
78 // -------------------------------------------------------------
79 // Drawing Function
80 // -------------------------------------------------------------
81 
82 void SvgStroke::write( std::ostream& out ) const
83 {
84  // Treat special cases.
85  if( type == Type::kOmit ) {
86  return;
87  }
88  if( type == Type::kNone ) {
89  out << svg_attribute( "stroke", "none" );
90  return;
91  }
92  if( type == Type::kGradient ) {
93  out << svg_attribute( "stroke", "url(#" + gradient_id + ")" );
94  return;
95  }
96 
97  out << svg_attribute( "stroke", color_to_hex( color ) );
98  out << svg_attribute( "stroke-opacity", color.a() );
99  out << svg_attribute( "stroke-width", width, width_unit );
100 
101  switch( line_cap ) {
102  case LineCap::kOmit:
103  break;
104  case LineCap::kButt:
105  out << svg_attribute( "stroke-linecap", "butt" );
106  break;
107  case LineCap::kSquare:
108  out << svg_attribute( "stroke-linecap", "square" );
109  break;
110  case LineCap::kRound:
111  out << svg_attribute( "stroke-linecap", "round" );
112  break;
113  }
114 
115  switch( line_join ) {
116  case LineJoin::kOmit:
117  break;
118  case LineJoin::kMiter:
119  out << svg_attribute( "stroke-linejoin", "miter" );
120  out << svg_attribute( "stroke-miterlimit", miterlimit );
121  break;
122  case LineJoin::kRound:
123  out << svg_attribute( "stroke-linejoin", "round" );
124  break;
125  case LineJoin::kBevel:
126  out << svg_attribute( "stroke-linejoin", "bevel" );
127  break;
128  }
129 
130  if( dash_array.size() > 0 ) {
131  out << svg_attribute( "stroke-dasharray", join( dash_array, " " ));
132  out << svg_attribute( "stroke-dashoffset", dash_offset );
133  }
134 }
135 
136 // =================================================================================================
137 // Svg Fill
138 // =================================================================================================
139 
140 // -------------------------------------------------------------
141 // Constructors and Rule of Five
142 // -------------------------------------------------------------
143 
145  : type( type )
146  , color()
147  , rule( Rule::kNone )
148 {}
149 
151  : type( SvgFill::Type::kColor )
152  , color( color )
153  , rule( Rule::kNone )
154 {}
155 
156 SvgFill::SvgFill( std::string gradient_id_value )
157  : SvgFill( Type::kGradient )
158 {
159  gradient_id = gradient_id_value;
160 }
161 
162 // -------------------------------------------------------------
163 // Drawing Function
164 // -------------------------------------------------------------
165 
166 void SvgFill::write( std::ostream& out ) const
167 {
168  // Treat special cases.
169  if( type == Type::kOmit ) {
170  return;
171  }
172  if( type == Type::kNone ) {
173  out << svg_attribute( "fill", "none" );
174  return;
175  }
176  if( type == Type::kGradient ) {
177  out << svg_attribute( "fill", "url(#" + gradient_id + ")" );
178  return;
179  }
180 
181  out << svg_attribute( "fill", color_to_hex( color ) );
182  out << svg_attribute( "fill-opacity", color.a() );
183 
184  switch( rule ) {
185  case Rule::kNone:
186  break;
187  case Rule::kNonZero:
188  out << svg_attribute( "fill-rule", "nonzero" );
189  break;
190  case Rule::kEvenOdd:
191  out << svg_attribute( "fill-rule", "evenodd" );
192  break;
193  }
194 }
195 
196 // =================================================================================================
197 // Svg Font
198 // =================================================================================================
199 
200 // -------------------------------------------------------------
201 // Constructors and Rule of Five
202 // -------------------------------------------------------------
203 
204 SvgFont::SvgFont( double size_value, std::string const& family_value )
205  : size( size_value )
206  , family( family_value )
207 {}
208 
209 // -------------------------------------------------------------
210 // Drawing Function
211 // -------------------------------------------------------------
212 
213 void SvgFont::write( std::ostream& out ) const
214 {
215  out << svg_attribute( "font-size", size );
216  out << svg_attribute( "font-family", family );
217 }
218 
219 // =================================================================================================
220 // Svg Transformation
221 // =================================================================================================
222 
223 // -------------------------------------------------------------------------
224 // Subclass Translate
225 // -------------------------------------------------------------------------
226 
227 void SvgTransform::Translate::write( std::ostream &out ) const
228 {
229  if( tx != 0.0 || ty != 0.0 ) {
230  out << "translate( " << tx << ", " << ty << " )";
231  }
232 }
233 
235 {
236  return SvgPoint( p.x + tx, p.y + ty );
237 }
238 
239 // -------------------------------------------------------------------------
240 // Subclass Rotate
241 // -------------------------------------------------------------------------
242 
243 void SvgTransform::Rotate::write( std::ostream &out ) const
244 {
245  if( a != 0.0 ) {
246  if( cx != 0.0 || cy != 0.0 ) {
247  out << "rotate( " << a << ", " << cx << ", " << cy << " )";
248  } else {
249  out << "rotate( " << a << " )";
250  }
251  }
252 }
253 
255 {
256  // Convert to radians, and precompute sin and cos.
257  auto const r = a * utils::PI / 180.0;
258  auto const sr = std::sin(r);
259  auto const cr = std::cos(r);
260 
261  // We need to subtract the offset, rotate, and add the offset again.
262  // https://stackoverflow.com/a/2259502/4184258
263  // See also https://www.w3.org/TR/SVGTiny12/coords.html
264  auto const nx = p.x - cx;
265  auto const ny = p.y - cy;
266  auto const rx = nx * cr - ny * sr;
267  auto const ry = nx * sr + ny * cr;
268  auto const fx = rx + cx;
269  auto const fy = ry + cy;
270  return SvgPoint( fx, fy );
271 }
272 
273 // -------------------------------------------------------------------------
274 // Subclass Scale
275 // -------------------------------------------------------------------------
276 
277 void SvgTransform::Scale::write( std::ostream &out ) const
278 {
279  if( sx != 1.0 || sy != 1.0 ) {
280  if( sx == sy ) {
281  out << "scale( " << sx << " )";
282  } else {
283  out << "scale( " << sx << ", " << sy << " )";
284  }
285  }
286 }
287 
289 {
290  return SvgPoint( p.x * sx, p.y * sy );
291 }
292 
293 // -------------------------------------------------------------------------
294 // Subclass SkewX
295 // -------------------------------------------------------------------------
296 
297 void SvgTransform::SkewX::write( std::ostream &out ) const
298 {
299  if( ax != 0.0 ) {
300  out << "skewX( " << ax << " )";
301  }
302 }
303 
305 {
306  // Convert to radians, and compute skew.
307  auto const rx = ax * utils::PI / 180.0;
308  return SvgPoint( p.x + p.y * std::tan(rx), p.y );
309 }
310 
311 // -------------------------------------------------------------------------
312 // Subclass SkewY
313 // -------------------------------------------------------------------------
314 
315 void SvgTransform::SkewY::write( std::ostream &out ) const
316 {
317  if( ay != 0.0 ) {
318  out << "skewY( " << ay << " )";
319  }
320 }
321 
323 {
324  // Convert to radians, and compute skew.
325  auto const ry = ay * utils::PI / 180.0;
326  return SvgPoint( p.x, p.x * std::tan(ry) + p.y );
327 }
328 
329 // -------------------------------------------------------------------------
330 // Subclass Matrix
331 // -------------------------------------------------------------------------
332 
333 void SvgTransform::Matrix::write( std::ostream &out ) const
334 {
335  if( a != 1.0 || b != 0.0 || c != 0.0 || d != 1.0 || e != 0.0 || f != 0.0 ) {
336  out << "matrix( " << a << ", " << b << ", " << c << ", ";
337  out << d << ", " << e << ", " << f << " )";
338  }
339 }
340 
342 {
343  return SvgPoint(
344  p.x * a + p.y * c + e,
345  p.x * b + p.y * d + f
346  );
347 }
348 
349 // -------------------------------------------------------------------------
350 // SvgTransform Main Class
351 // -------------------------------------------------------------------------
352 
354 {
355  transformations.push_back( std::move( t ));
356 }
357 
359 {
360  transformations.push_back( t );
361 }
362 
364 {
365  // Svg transforms are applied from last to first,
366  // see https://stackoverflow.com/a/18587460/4184258 for the rationale.
367  auto r = p;
368  for( auto t = transformations.crbegin(); t != transformations.crend(); ++t ) {
369  r = t->apply( r );
370  }
371  return r;
372 }
373 
375 {
376  assert( b.top_left.x <= b.bottom_right.x );
377  assert( b.top_left.y <= b.bottom_right.y );
378 
379  // Compute the transformed corners.
380  auto const tr_tl = apply( SvgPoint( b.top_left.x, b.top_left.y ));
381  auto const tr_tr = apply( SvgPoint( b.bottom_right.x, b.top_left.y ));
382  auto const tr_bl = apply( SvgPoint( b.top_left.x, b.bottom_right.y ));
383  auto const tr_br = apply( SvgPoint( b.bottom_right.x, b.bottom_right.y ));
384 
385  // Get the overall surrounding box that fits all.
386  auto const tlx = std::min( std::initializer_list<double>{ tr_tl.x, tr_tr.x, tr_bl.x, tr_br.x });
387  auto const tly = std::min( std::initializer_list<double>{ tr_tl.y, tr_tr.y, tr_bl.y, tr_br.y });
388  auto const brx = std::max( std::initializer_list<double>{ tr_tl.x, tr_tr.x, tr_bl.x, tr_br.x });
389  auto const bry = std::max( std::initializer_list<double>{ tr_tl.y, tr_tr.y, tr_bl.y, tr_br.y });
390 
391  return SvgBox( SvgPoint( tlx, tly ), SvgPoint( brx, bry ));
392 }
393 
394 void SvgTransform::write( std::ostream& out ) const
395 {
396  if( ! transformations.empty() ) {
397  out << " transform=\"";
398  for( auto const& t : transformations ) {
399  if( &t != &transformations[0] ) {
400  out << " ";
401  }
402  t.write( out );
403  }
404  out << "\"";
405  }
406 }
407 
408 } // namespace utils
409 } // namespace genesis
genesis::utils::Color
Definition: color.hpp:47
genesis::utils::SvgPoint::x
double x
Definition: utils/formats/svg/helper.hpp:69
helper.hpp
genesis::utils::SvgTransform::append
void append(Transformation &&t)
Definition: attributes.cpp:353
genesis::utils::SvgStroke::LineCap::kRound
@ kRound
genesis::utils::SvgStroke::LineJoin
LineJoin
Definition: attributes.hpp:75
genesis::utils::SvgStroke::LineCap::kSquare
@ kSquare
genesis::utils::SvgStroke::LineJoin::kMiter
@ kMiter
genesis::utils::SvgPoint::y
double y
Definition: utils/formats/svg/helper.hpp:70
genesis::utils::SvgStroke::Type
Type
Definition: attributes.hpp:59
genesis::utils::SvgStroke::miterlimit
double miterlimit
Definition: attributes.hpp:120
genesis::utils::SvgTransform::apply
SvgPoint apply(SvgPoint const &p) const
Apply all transformations to a point, and return the new transformed coordinate.
Definition: attributes.cpp:363
genesis::utils::SvgTransform::Matrix::apply
SvgPoint apply(SvgPoint const &p) const
Definition: attributes.cpp:341
common.hpp
genesis::utils::SvgStroke::line_join
LineJoin line_join
Definition: attributes.hpp:119
genesis::utils::SvgTransform::Translate::ty
double ty
Definition: attributes.hpp:291
genesis::utils::SvgFill::Rule::kNone
@ kNone
genesis::utils::SvgStroke::width
double width
Definition: attributes.hpp:115
genesis::utils::SvgStroke::Type::kOmit
@ kOmit
genesis::utils::SvgTransform::Translate::apply
SvgPoint apply(SvgPoint const &p) const
Definition: attributes.cpp:234
genesis::utils::SvgStroke::Type::kGradient
@ kGradient
genesis::utils::SvgBox::top_left
SvgPoint top_left
Definition: utils/formats/svg/helper.hpp:204
genesis::utils::SvgStroke::LineCap
LineCap
Definition: attributes.hpp:67
genesis::utils::SvgBox
Definition: utils/formats/svg/helper.hpp:127
genesis::utils::SvgFill::SvgFill
SvgFill(Type type=Type::kColor)
Definition: attributes.cpp:144
genesis::utils::SvgStroke::LineCap::kButt
@ kButt
genesis::utils::SvgBox::bottom_right
SvgPoint bottom_right
Definition: utils/formats/svg/helper.hpp:205
genesis::utils::SvgTransform::write
void write(std::ostream &out) const
Definition: attributes.cpp:394
genesis::utils::SkipWhitespace::kNone
@ kNone
Skip no whitespace. Thus, immediately treat the current input char.
genesis::utils::SvgStroke::dash_array
std::vector< double > dash_array
Definition: attributes.hpp:122
genesis::utils::SvgTransform::Scale::write
void write(std::ostream &out) const
Definition: attributes.cpp:277
genesis::utils::SvgFont::SvgFont
SvgFont(double size=10, std::string const &family="Verdana")
Definition: attributes.cpp:204
genesis::utils::SvgFill::Rule
Rule
Definition: attributes.hpp:149
genesis::utils::SvgStroke::LineCap::kOmit
@ kOmit
genesis::utils::SvgFont::size
double size
Definition: attributes.hpp:229
genesis::utils::SvgTransform::SkewY::apply
SvgPoint apply(SvgPoint const &p) const
Definition: attributes.cpp:322
genesis::utils::SvgPoint
Definition: utils/formats/svg/helper.hpp:57
genesis::utils::SvgStroke
Definition: attributes.hpp:49
genesis::utils::SvgStroke::dash_offset
double dash_offset
Definition: attributes.hpp:123
genesis::utils::SvgFont::family
std::string family
Definition: attributes.hpp:230
string.hpp
Provides some commonly used string utility functions.
genesis::utils::SvgFill::gradient_id
std::string gradient_id
Definition: attributes.hpp:184
genesis::utils::Color::a
double a() const
Definition: color.hpp:124
genesis::utils::SvgFill::Rule::kEvenOdd
@ kEvenOdd
genesis::utils::SvgFill::type
Type type
Definition: attributes.hpp:182
genesis::utils::SvgTransform::Translate::tx
double tx
Definition: attributes.hpp:290
genesis::utils::SvgTransform::SkewY::write
void write(std::ostream &out) const
Definition: attributes.cpp:315
genesis::utils::PI
constexpr double PI
Make the world go round.
Definition: common.hpp:55
genesis::utils::join
Interval< DataType, NumericalType, IntervalKind > join(Interval< DataType, NumericalType, IntervalKind > const &a, Interval< DataType, NumericalType, IntervalKind > const &b)
Creates a new Interval that contains both intervals and whatever is between.
Definition: utils/containers/interval_tree/functions.hpp:127
genesis::utils::SvgStroke::Type::kNone
@ kNone
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::SvgTransform::Transformation
Internal helper class used as an abstraction to be able to store Transformations without need for inh...
Definition: attributes.hpp:546
functions.hpp
Color operators and functions.
genesis::utils::SvgStroke::SvgStroke
SvgStroke(Type type=Type::kColor)
Definition: attributes.cpp:53
genesis::utils::SvgStroke::LineJoin::kOmit
@ kOmit
genesis::utils::SvgFill
Definition: attributes.hpp:131
genesis::utils::svg_attribute
std::string svg_attribute(std::string const &name, T const &value, std::string const &unit="")
Definition: utils/formats/svg/helper.hpp:229
genesis::utils::SvgTransform::transformations
std::vector< Transformation > transformations
Definition: attributes.hpp:691
genesis::utils::SvgFill::write
void write(std::ostream &out) const
Definition: attributes.cpp:166
genesis::utils::SvgStroke::line_cap
LineCap line_cap
Definition: attributes.hpp:118
genesis::utils::SvgTransform::Translate::write
void write(std::ostream &out) const
Definition: attributes.cpp:227
genesis::utils::SvgTransform::Rotate::apply
SvgPoint apply(SvgPoint const &p) const
Definition: attributes.cpp:254
genesis::utils::SvgTransform::Scale::apply
SvgPoint apply(SvgPoint const &p) const
Definition: attributes.cpp:288
attributes.hpp
genesis::utils::SvgStroke::write
void write(std::ostream &out) const
Definition: attributes.cpp:82
genesis::utils::SvgStroke::LineJoin::kRound
@ kRound
genesis::utils::SvgFill::Type::kGradient
@ kGradient
genesis::utils::SvgTransform::Matrix::write
void write(std::ostream &out) const
Definition: attributes.cpp:333
genesis::utils::SvgFill::rule
Rule rule
Definition: attributes.hpp:187
genesis::utils::color_to_hex
std::string color_to_hex(Color const &c, std::string const &prefix, bool uppercase, bool with_alpha)
Return a hex string representation of a Color in the format "#003366[ff]".
Definition: utils/color/functions.cpp:100
genesis::utils::SvgFill::color
Color color
Definition: attributes.hpp:186
genesis::utils::SvgTransform::Rotate::write
void write(std::ostream &out) const
Definition: attributes.cpp:243
genesis::utils::SvgStroke::width_unit
std::string width_unit
Definition: attributes.hpp:116
genesis::utils::SvgFill::Rule::kNonZero
@ kNonZero
genesis::utils::SvgStroke::type
Type type
Definition: attributes.hpp:109
genesis::utils::SvgStroke::LineJoin::kBevel
@ kBevel
genesis::utils::SvgFill::Type::kNone
@ kNone
genesis::utils::SvgTransform::SkewX::write
void write(std::ostream &out) const
Definition: attributes.cpp:297
genesis::utils::SvgFill::Type::kOmit
@ kOmit
genesis::utils::SvgStroke::gradient_id
std::string gradient_id
Definition: attributes.hpp:111
genesis::utils::SvgTransform::SkewX::apply
SvgPoint apply(SvgPoint const &p) const
Definition: attributes.cpp:304
genesis::utils::SvgFill::Type
Type
Definition: attributes.hpp:141
genesis::utils::SvgStroke::color
Color color
Definition: attributes.hpp:113
genesis::utils::SvgFont::write
void write(std::ostream &out) const
Definition: attributes.cpp:213