1 // -*- C++ -*-
2 /*
3 * @brief Graph implements graphic devices for plotting through R
4 *
5 * Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6 *
7 * This library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21 #include <cstdlib>
22 #include <fstream>
23
24 #include "openturns/GraphImplementation.hxx"
25 #include "openturns/Rfunctions.hxx"
26 #include "openturns/Path.hxx"
27 #include "openturns/PersistentObjectFactory.hxx"
28 #include "openturns/ResourceMap.hxx"
29 #include "openturns/Log.hxx"
30 #include "openturns/OTconfig.hxx"
31 #include "openturns/Os.hxx"
32 #include "openturns/Pie.hxx"
33 #include "openturns/Graph.hxx"
34
35 BEGIN_NAMESPACE_OPENTURNS
36
37 TEMPLATE_CLASSNAMEINIT(PersistentCollection<Drawable>)
38
39 static const Factory<PersistentCollection<Drawable> > Factory_PersistentCollection_Drawable;
40
41 CLASSNAMEINIT(GraphImplementation)
42
43 static const Factory<GraphImplementation> Factory_GraphImplementation;
44
45 Bool GraphImplementation::IsFirstInitialization = true;
46
47
48 Description GraphImplementation::ValidLegendPositions;
49
50 /* Initialize valid legend positions */
InitializeValidLegendPositions()51 void GraphImplementation::InitializeValidLegendPositions()
52 {
53 ValidLegendPositions.setName("ValidLegendPositions");
54 ValidLegendPositions.add("");
55 ValidLegendPositions.add("bottomright");
56 ValidLegendPositions.add("bottom");
57 ValidLegendPositions.add("bottomleft");
58 ValidLegendPositions.add("left");
59 ValidLegendPositions.add("topleft");
60 ValidLegendPositions.add("top");
61 ValidLegendPositions.add("topright");
62 ValidLegendPositions.add("right");
63 ValidLegendPositions.add("center");
64 }
65
66 /* Default constructor */
GraphImplementation(const String & title)67 GraphImplementation::GraphImplementation(const String & title)
68 : PersistentObject()
69 , title_(title)
70 , legendPosition_()
71 , legendFontSize_(ResourceMap::GetAsScalar("Graph-DefaultLegendFontSize"))
72 , xTitle_()
73 , yTitle_()
74 , showAxes_(false)
75 , logScale_(NONE)
76 , showGrid_(false)
77 , gridColor_("gray")
78 , xMargin_(ResourceMap::GetAsScalar("Graph-DefaultHorizontalMargin"))
79 , yMargin_(ResourceMap::GetAsScalar("Graph-DefaultVerticalMargin"))
80 , automaticBoundingBox_(true)
81 , boundingBox_(4)
82 , drawablesCollection_(0)
83 {
84 if(IsFirstInitialization)
85 {
86 InitializeValidLegendPositions();
87 IsFirstInitialization = false;
88 }
89 }
90
91 /* Constructor with parameters */
GraphImplementation(const String & title,const String & xTitle,const String & yTitle,const Bool showAxes,const String & legendPosition,const Scalar legendFontSize,const LogScale logScale)92 GraphImplementation::GraphImplementation(const String & title,
93 const String & xTitle,
94 const String & yTitle,
95 const Bool showAxes,
96 const String & legendPosition,
97 const Scalar legendFontSize,
98 const LogScale logScale)
99 : PersistentObject()
100 , title_(title)
101 , legendFontSize_(legendFontSize)
102 , xTitle_(xTitle)
103 , yTitle_(yTitle)
104 , showAxes_(showAxes)
105 , logScale_(NONE)
106 , showGrid_(true)
107 , gridColor_("gray")
108 , xMargin_(ResourceMap::GetAsScalar("Graph-DefaultHorizontalMargin"))
109 , yMargin_(ResourceMap::GetAsScalar("Graph-DefaultVerticalMargin"))
110 , automaticBoundingBox_(true)
111 , boundingBox_(4)
112 , drawablesCollection_(0)
113 {
114 setName(title);
115 if(IsFirstInitialization)
116 {
117 InitializeValidLegendPositions();
118 IsFirstInitialization = false;
119 }
120 // Check if the given legend position is valid
121
122 if (!IsValidLegendPosition(legendPosition)) throw InvalidArgumentException(HERE) << "The given legend position = " << legendPosition << " is invalid";
123 if (!(legendFontSize > 0.0)) throw InvalidArgumentException(HERE) << "The given legend font size = " << legendFontSize << " is invalid, it must be > 0";
124 legendPosition_ = legendPosition;
125 setLogScale(logScale);
126 }
127
128 /* Virtual constructor */
clone() const129 GraphImplementation * GraphImplementation::clone() const
130 {
131 return new GraphImplementation(*this);
132 }
133
134 /* String converter */
__repr__() const135 String GraphImplementation::__repr__() const
136 {
137 OSS oss;
138 oss << "class=" << GraphImplementation::GetClassName()
139 << " name=" << getName()
140 << " title=" << title_
141 << " xTitle=" << xTitle_
142 << " yTitle=" << yTitle_
143 << " axes=" << (showAxes_ ? "ON" : "OFF")
144 << " grid=" << (showGrid_ ? "ON" : "OFF")
145 << " legendposition=" << legendPosition_
146 << " legendFontSize=" << legendFontSize_
147 << " drawables=" << drawablesCollection_;
148 return oss;
149 }
150
151 /* Adds a drawable instance to the collection of drawables contained in GraphImplementation */
add(const Drawable & aDrawable)152 void GraphImplementation::add(const Drawable & aDrawable)
153 {
154 drawablesCollection_.add(aDrawable);
155 }
156
157 /* Erase a drawable instance from the collection of drawables contained in GraphImplementation */
erase(const UnsignedInteger i)158 void GraphImplementation::erase(const UnsignedInteger i)
159 {
160 drawablesCollection_.erase(drawablesCollection_.begin() + i);
161 }
162
163 /* Adds a collection of drawable instances to the collection of drawables contained in GraphImplementation */
add(const DrawableCollection & drawableCollection)164 void GraphImplementation::add(const DrawableCollection & drawableCollection)
165 {
166 for (UnsignedInteger i = 0; i < drawableCollection.getSize(); ++i) drawablesCollection_.add(drawableCollection[i]);
167 }
168
169 /* Adds a collection of drawable instances to the collection of drawables contained in GraphImplementation */
add(const GraphImplementation & graphImplementation)170 void GraphImplementation::add(const GraphImplementation & graphImplementation)
171 {
172 // Add the drawables
173 add(graphImplementation.getDrawables());
174 }
175
176 /* Drawables accessor */
getDrawables() const177 GraphImplementation::DrawableCollection GraphImplementation::getDrawables() const
178 {
179 return drawablesCollection_;
180 }
181
setDrawables(const DrawableCollection & drawableCollection)182 void GraphImplementation::setDrawables(const DrawableCollection & drawableCollection)
183 {
184 drawablesCollection_ = drawableCollection;
185 }
186
187 /* Individual drawable accessor */
getDrawable(const UnsignedInteger index) const188 Drawable GraphImplementation::getDrawable(const UnsignedInteger index) const
189 {
190 if (!(index < drawablesCollection_.getSize())) throw InvalidRangeException(HERE) << "Error: trying to get a drawable at position " << index << " from a collection of size " << drawablesCollection_.getSize();
191 return drawablesCollection_[index];
192 }
193
setDrawable(const Drawable & drawable,const UnsignedInteger index)194 void GraphImplementation::setDrawable(const Drawable & drawable,
195 const UnsignedInteger index)
196 {
197 if (!(index < drawablesCollection_.getSize())) throw InvalidRangeException(HERE) << "Error: trying to set a drawable at position " << index << " into a collection of size " << drawablesCollection_.getSize();
198 drawablesCollection_[index] = drawable;
199 }
200
201 /** Global color accessor */
getColors() const202 Description GraphImplementation::getColors() const
203 {
204 const UnsignedInteger size = drawablesCollection_.getSize();
205 Description colors(size);
206 for (UnsignedInteger i = 0; i < size; ++i) colors[i] = drawablesCollection_[i].getColor();
207 return colors;
208 }
209
setColors(const Description & colors)210 void GraphImplementation::setColors(const Description & colors)
211 {
212 const UnsignedInteger size = drawablesCollection_.getSize();
213 const UnsignedInteger inputSize = colors.getSize();
214 if (inputSize > 0)
215 for (UnsignedInteger i = 0; i < size; ++i) drawablesCollection_[i].setColor(colors[i % inputSize]);
216 }
217
setDefaultColors()218 void GraphImplementation::setDefaultColors()
219 {
220 const UnsignedInteger size = drawablesCollection_.getSize();
221 setColors(Drawable::BuildDefaultPalette(size));
222 }
223
224 /** Global legend accessor */
getLegends() const225 Description GraphImplementation::getLegends() const
226 {
227 const UnsignedInteger size = drawablesCollection_.getSize();
228 Description legends(size);
229 for (UnsignedInteger i = 0; i < size; ++i) legends[i] = drawablesCollection_[i].getLegend();
230 return legends;
231 }
232
setLegends(const Description & legends)233 void GraphImplementation::setLegends(const Description & legends)
234 {
235 const UnsignedInteger size = drawablesCollection_.getSize();
236 const UnsignedInteger inputSize = legends.getSize();
237 if (inputSize > 0)
238 for (UnsignedInteger i = 0; i < size; ++i) drawablesCollection_[i].setLegend(legends[i % inputSize]);
239 }
240
241
242 /* Hide or show x and y axes */
setAxes(const Bool showAxes)243 void GraphImplementation::setAxes(const Bool showAxes)
244 {
245 showAxes_ = showAxes;
246 }
247
248 /* Accessor for showAxes_ */
getAxes() const249 Bool GraphImplementation::getAxes() const
250 {
251 return showAxes_;
252 }
253
254 /* Ticks location flag accessor */
setTickLocation(const TickLocation tickLocation)255 void GraphImplementation::setTickLocation(const TickLocation tickLocation)
256 {
257 tickLocation_ = tickLocation;
258 }
259
getTickLocation() const260 GraphImplementation::TickLocation GraphImplementation::getTickLocation() const
261 {
262 return tickLocation_;
263 }
264
265 /* Set log scale for x, y both or none axes */
setLogScale(const LogScale logScale)266 void GraphImplementation::setLogScale(const LogScale logScale)
267 {
268 if (logScale > LOGXY) logScale_ = NONE;
269 else logScale_ = logScale;
270 }
271
272 /* Accessor for logScale_ */
getLogScale() const273 GraphImplementation::LogScale GraphImplementation::getLogScale() const
274 {
275 return logScale_;
276 }
277
278 /* Hide or show x grid */
setGrid(const Bool showGrid)279 void GraphImplementation::setGrid(const Bool showGrid)
280 {
281 showGrid_ = showGrid;
282 }
283
284 /* Accessor for showGrid_ */
getGrid() const285 Bool GraphImplementation::getGrid() const
286 {
287 return showGrid_;
288 }
289
290 /* Grid color accessors */
setGridColor(const String & color)291 void GraphImplementation::setGridColor(const String & color)
292 {
293 if(!DrawableImplementation::IsValidColor(color)) throw InvalidArgumentException(HERE) << "Given color = " << color << " is incorrect";
294
295 gridColor_ = color;
296 }
297
298 /* Accessor for showGrid_ */
getGridColor() const299 String GraphImplementation::getGridColor() const
300 {
301 return gridColor_;
302 }
303
304 /* Accesor for xTitle */
getXTitle() const305 String GraphImplementation::getXTitle() const
306 {
307 return xTitle_;
308 }
309
310 /* Accessor for xTitle */
setXTitle(const String & title)311 void GraphImplementation::setXTitle(const String & title)
312 {
313 xTitle_ = title;
314 }
315
316 /* Accessor for yTitle */
getYTitle() const317 String GraphImplementation::getYTitle() const
318 {
319 return yTitle_;
320 }
321
322 /* Accessor for yTitle */
setYTitle(const String & title)323 void GraphImplementation::setYTitle(const String & title)
324 {
325 yTitle_ = title;
326 }
327
328 /* Accesor for title */
getTitle() const329 String GraphImplementation::getTitle() const
330 {
331 return title_;
332 }
333
334 /* Accesor for title */
setTitle(const String & title)335 void GraphImplementation::setTitle(const String & title)
336 {
337 title_ = title;
338 }
339
340 /* Build the R command corresponding to the legend */
makeRLegendCommand() const341 String GraphImplementation::makeRLegendCommand() const
342 {
343 OSS labels, colors, lines, points, fill;
344 labels << "c(";
345 colors << "c(";
346 lines << "c(";
347 points << "c(";
348 fill << "c(";
349
350 Bool drawLegend = false;
351
352 for(DrawableCollection::const_iterator it = drawablesCollection_.begin(); it != drawablesCollection_.end(); ++it)
353 {
354 if (it->getLegend().size() != 0)
355 {
356 drawLegend = true;
357 labels << "\"" << it->getLegend() << "\",";
358
359 if(it->getColor().size() == 0)
360 colors << "NA,";
361 else colors << "\"" << it->getColor() << "\",";
362
363 if(it->getFillStyle().size() == 0)
364 fill << "NA,";
365 else fill << "\"" << it->getFillStyle() << "\",";
366
367 if(it->getPointStyle().size() == 0 || it->getFillStyle().size() != 0) //cannot merge fill and point symbol
368 points << "NA,";
369 else points << it->getPointCode(it->getPointStyle()) << ",";
370
371 if(it->getLineStyle().size() == 0 || it->getFillStyle().size() != 0 ) //cannot merge line and fill symbol
372 lines << "NA,";
373 else lines << "\"" << it->getLineStyle() << "\",";
374 }
375 }
376 if (drawLegend)
377 {
378 String labels_str(labels);
379 labels_str.replace(labels_str.length() - 1, 1, ")");
380
381 String colors_str(colors);
382 colors_str.replace(colors_str.length() - 1, 1, ")");
383
384 String lines_str(lines);
385 lines_str.replace(lines_str.length() - 1, 1, ")");
386
387 String points_str(points);
388 points_str.replace(points_str.length() - 1, 1, ")");
389
390 String fill_str(fill);
391 fill_str.replace(fill_str.length() - 1, 1, ")");
392
393 OSS rCommand;
394 rCommand << "legend(\"" << legendPosition_ << "\","
395 << "legend=" << labels_str << ","
396 << "col=" << colors_str << ","
397 << "lty=" << lines_str << ","
398 << "pch=" << points_str << ","
399 << "fill=" << fill_str << ","
400 << "cex=" << legendFontSize_ << ","
401 << "bg=\"grey90\",merge=TRUE,density=40)";
402
403 return rCommand;
404 }
405
406 else return String();
407 }
408
409 /* Get the R command corresponding to the graph */
getRCommand() const410 String GraphImplementation::getRCommand() const
411 {
412 return OSS() << makeRHeaderCommand() << makeRCoreCommand();
413 }
414
415 /* Make R header commande */
makeRHeaderCommand() const416 String GraphImplementation::makeRHeaderCommand() const
417 {
418 OSS oss;
419 // Check if we want to print a legend and if there is a legend to print before to include the corresponding R code
420 // First, do we want a legend?
421 if (legendPosition_ != "")
422 {
423 // Secodn, is there a legend to print?
424 for (UnsignedInteger i = 0; i < drawablesCollection_.getSize(); ++i)
425 {
426 if (drawablesCollection_[i].getLegend().size() > 0)
427 {
428 oss << R_LEGEND << "\n";
429 break;
430 }
431 }
432 }
433 // Check if we want to draw a Pie before to include the corresponding R code
434 for (UnsignedInteger i = 0; i < drawablesCollection_.getSize(); ++i)
435 {
436 if (drawablesCollection_[i].getImplementation()->getClassName() == Pie::GetClassName())
437 {
438 oss << "\n" << R_PIE << "\n";
439 break;
440 }
441 }
442 oss << "options(digits=17)" << "\n" << "options(warn=-1)" << "\n";
443 return oss;
444 }
445
446 /* Make R core command */
makeRCoreCommand() const447 String GraphImplementation::makeRCoreCommand() const
448 {
449 // get the general bounding box
450 const Interval boundingBox(getBoundingBox());
451 const Point lowerBound(boundingBox.getLowerBound());
452 const Point upperBound(boundingBox.getUpperBound());
453
454 //load the R code attached to the general plot
455 OSS graphCommand;
456 graphCommand << "plot(c(" << lowerBound[0] << "," << upperBound[0] << "),"
457 << "c(" << lowerBound[1] << "," << upperBound[1] << "),"
458 << "type=\"n\",main=\"" << title_ << "\",";
459 if (showAxes_)
460 {
461 graphCommand << "xlab=\"" << xTitle_ << "\",ylab=\"" << yTitle_ << "\","
462 << "axes=TRUE";
463 }
464 else
465 {
466 graphCommand << "xlab=\"\",ylab=\"\",axes=FALSE";
467 }
468 if (showGrid_)
469 {
470 graphCommand << ", panel.first=grid(col=\"" + gridColor_ + "\")";
471 }
472 switch (logScale_)
473 {
474 case NONE:
475 break;
476 case LOGX:
477 graphCommand << ", log=\"x\"";
478 break;
479 case LOGY:
480 graphCommand << ", log=\"y\"";
481 break;
482 case LOGXY:
483 graphCommand << ", log=\"xy\"";
484 break;
485 }
486 graphCommand << ", cex.main=2, cex.axis=1.5, cex.lab=1.5)\n";
487
488 // add the R code attached to each drawable
489 UnsignedInteger drawablesSize = drawablesCollection_.getSize();
490 for(UnsignedInteger i = 0; i < drawablesSize; ++i)
491 {
492 if (drawablesCollection_[i].getData().getSize() != 0)
493 graphCommand << drawablesCollection_[i].draw() << "\n";
494 }
495 // make the legend command
496 graphCommand << (legendPosition_.size() == 0 ? String() : makeRLegendCommand());
497 return graphCommand;
498 }
499
500
GetExtensionMap()501 std::map<SignedInteger, String> GraphImplementation::GetExtensionMap()
502 {
503 std::map<SignedInteger, String> m;
504 m[PNG] = ".png";
505 m[EPS] = ".eps";
506 m[FIG] = ".fig";
507 m[PDF] = ".pdf";
508 return m;
509 }
510
511 /* The method that generates the graphic files */
draw(const String & file,const Scalar width,const Scalar height,SignedInteger drawingFormat)512 void GraphImplementation::draw(const String & file,
513 const Scalar width,
514 const Scalar height,
515 SignedInteger drawingFormat)
516 {
517 // Override format base on extension
518 size_t pos = file.find_last_of(".");
519 Bool matchedExtension = false;
520 static std::map<SignedInteger, String> extensionMap = GetExtensionMap();
521 if (pos != String::npos)
522 {
523 String extension = file.substr(pos, file.length());
524 std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
525 for(std::map<SignedInteger, String>::iterator iter = extensionMap.begin(); iter != extensionMap.end(); ++iter)
526 {
527 if (!extension.compare(iter->second))
528 {
529 drawingFormat = iter->first;
530 matchedExtension = true;
531 break;
532 }
533 }
534 }
535
536 // Check the needed drawingFormat. If it is invalid, set it to ALL
537 if (!(drawingFormat & (PNG | EPS | FIG | PDF)))
538 {
539 LOGINFO(OSS() << "Warning: invalid drawing format: " << drawingFormat << ", selecting all formats.");
540 drawingFormat = ALL;
541 }
542
543 OSS rCommand;
544 rCommand << makeRHeaderCommand();
545 String rCoreCommand(makeRCoreCommand());
546 if (drawingFormat & EPS)
547 {
548 rCommand << "postscript(\"" << file + (matchedExtension ? "" : extensionMap[EPS]) << "\", horizontal = FALSE, onefile = FALSE, paper = \"special\", height=" << height / 72. << ", width=" << width / 72. << ")" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
549 }
550 if (drawingFormat & PDF)
551 {
552 rCommand << "pdf(\"" << file + (matchedExtension ? "" : extensionMap[PDF]) << "\", onefile = FALSE, paper = \"special\", height=" << height / 72. << ", width=" << width / 72. << ")" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
553 }
554 if (drawingFormat & PNG)
555 {
556 rCommand << "png(\"" << file + (matchedExtension ? "" : extensionMap[PNG]) << "\",height=" << height << ", width=" << width << ",res=72)" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
557 }
558 if (drawingFormat & FIG)
559 {
560 rCommand << "xfig(\"" << file + (matchedExtension ? "" : extensionMap[FIG]) << "\", horizontal = FALSE, onefile = FALSE, paper = \"A4\", height=" << height / 72. << ", width=" << width / 72. << ")" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
561 }
562 LOGDEBUG(OSS() << "R command=" << String(rCommand));
563
564 // system commands to write R code in a temporary file
565 /* using mkstemp non standard C for temporary file generation */
566 String temporaryFileName(Path::BuildTemporaryFileName("tmp_graph.R.XXXXXX"));
567 std::ofstream cmdFile(temporaryFileName.c_str(), std::ios::out);
568 cmdFile << String(rCommand);
569 cmdFile.close();
570
571 //execute R and load R script in temporary file
572 const String RExecutable(ResourceMap::GetAsString("Graph-RExecutableCommand"));
573 OSS systemCommand;
574 if (RExecutable != "") systemCommand << "" << RExecutable << "" << " --no-save --silent < \"" << temporaryFileName << "\"" << Os::GetDeleteCommandOutput();
575 else throw NotYetImplementedException(HERE) << "In GraphImplementation::draw(): needs R. Please install it and set the absolute path of the R executable in ResourceMap.";
576 if (Os::ExecuteCommand(systemCommand) != 0) throw InternalException(HERE) << "GraphImplementation: error trying to execute R command=" << String(systemCommand);
577 Os::Remove(temporaryFileName);
578 clean();
579 }
580
581 /* Clean temporary files */
clean()582 void GraphImplementation::clean()
583 {
584 UnsignedInteger drawableNumber = drawablesCollection_.getSize();
585 // Clean all the temporary data created by the drawables during their drawing
586 for (UnsignedInteger i = 0; i < drawableNumber; ++i)
587 {
588 if (drawablesCollection_[i].getData().getSize() != 0)
589 drawablesCollection_[i].clean();
590 }
591 }
592
593 /* Margin accessor */
setXMargin(const Scalar xMargin)594 void GraphImplementation::setXMargin(const Scalar xMargin)
595 {
596 if (!(xMargin >= 0.0) || !(xMargin <= 1.0))
597 throw InvalidArgumentException(HERE) << "Horizontal margin must be in [0, 1].";
598 xMargin_ = xMargin;
599 }
600
setYMargin(const Scalar yMargin)601 void GraphImplementation::setYMargin(const Scalar yMargin)
602 {
603 if (!(yMargin >= 0.0) || !(yMargin <= 1.0))
604 throw InvalidArgumentException(HERE) << "Vertical margin must be in [0, 1].";
605 yMargin_ = yMargin;
606 }
607
608 /* Get the bounding box of the whole plot */
getBoundingBox() const609 Interval GraphImplementation::getBoundingBox() const
610 {
611 if (automaticBoundingBox_) computeBoundingBox();
612 return boundingBox_;
613 }
614
615 /* Set the bounding box of the whole plot */
setBoundingBox(const Interval & boundingBox)616 void GraphImplementation::setBoundingBox(const Interval & boundingBox)
617 {
618 if (boundingBox.getDimension() != 2) throw InvalidArgumentException(HERE) << "Error: the given bounding box must have a dimension equal to 2, but dimension=" << boundingBox.getDimension();
619 boundingBox_ = boundingBox;
620 automaticBoundingBox_ = false;
621 }
622
623 /* Automatic bounding box accessor */
getAutomaticBoundingBox() const624 Bool GraphImplementation::getAutomaticBoundingBox() const
625 {
626 return automaticBoundingBox_;
627 }
628
setAutomaticBoundingBox(const Bool automaticBoundingBox)629 void GraphImplementation::setAutomaticBoundingBox(const Bool automaticBoundingBox)
630 {
631 automaticBoundingBox_ = automaticBoundingBox;
632 }
633
634 /* Compute the best bounding box to enclose all the drawables */
computeBoundingBox() const635 void GraphImplementation::computeBoundingBox() const
636 {
637 const UnsignedInteger size = drawablesCollection_.getSize();
638
639 // First exceptional case: no drawable, we default to default bounding box
640 if (!(size > 0))
641 {
642 LOGINFO("Warning: cannot compute the bounding box of a graph with no drawable, switch to [0,1]x[0,1] default bounding box");
643 boundingBox_ = Interval(2);
644 return;
645 }
646
647 // compute the enclosing bounding box
648 Sample minBoxes(0, 2);
649 Sample maxBoxes(0, 2);
650 for (UnsignedInteger i = 0; i < size; ++ i)
651 {
652 if (drawablesCollection_[i].getData().getSize() != 0)
653 {
654 const Interval boundingBox(drawablesCollection_[i].getBoundingBox());
655 minBoxes.add(boundingBox.getLowerBound());
656 maxBoxes.add(boundingBox.getUpperBound());
657 }
658 }
659 Point min(minBoxes.getMin());
660 Point max(maxBoxes.getMax());
661
662 // apply margins:
663 if (logScale_ & LOGX)
664 {
665 if (min[0] > 0.0)
666 {
667 const Scalar margin = std::pow(10.0, xMargin_);
668 min[0] /= margin;
669 max[0] *= margin;
670 }
671 else
672 LOGWARN("Negative x values in log-scale axis");
673 }
674 else
675 {
676 const Scalar delta = max[0] - min[0];
677 const Scalar margin = delta > 0.0 ? xMargin_ * delta : 0.5;
678 min[0] -= margin;
679 max[0] += margin;
680 }
681
682 if (logScale_ & LOGY)
683 {
684 if (min[1] > 0.0)
685 {
686 const Scalar margin = std::pow(10.0, yMargin_);
687 min[1] /= margin;
688 max[1] *= margin;
689 }
690 else
691 LOGWARN("Negative y values in log-scale axis");
692 }
693 else
694 {
695 const Scalar delta = max[1] - min[1];
696 const Scalar margin = delta > 0.0 ? yMargin_ * delta : 0.5;
697 min[1] -= margin;
698 max[1] += margin;
699 }
700 boundingBox_ = Interval(min, max);
701 }
702
703 /* Get the legend position */
getLegendPosition() const704 String GraphImplementation::getLegendPosition() const
705 {
706 return legendPosition_;
707 }
708
709 /* Set the legend position */
setLegendPosition(const String & position)710 void GraphImplementation::setLegendPosition(const String & position)
711 {
712 if(!IsValidLegendPosition(position)) throw InvalidArgumentException(HERE) << "The given legend position = " << position << " is invalid";
713
714 legendPosition_ = position;
715 }
716
717 /* Gives all the valid legend positions */
GetValidLegendPositions()718 Description GraphImplementation::GetValidLegendPositions()
719 {
720 if(IsFirstInitialization)
721 {
722 InitializeValidLegendPositions();
723 IsFirstInitialization = false;
724 }
725 return ValidLegendPositions;
726 }
727
728 /* Get the legend font size */
getLegendFontSize() const729 Scalar GraphImplementation::getLegendFontSize() const
730 {
731 return legendFontSize_;
732 }
733
734 /* Set the legend font size */
setLegendFontSize(const Scalar legendFontSize)735 void GraphImplementation::setLegendFontSize(const Scalar legendFontSize)
736 {
737 if(!(legendFontSize > 0.0)) throw InvalidArgumentException(HERE) << "The given legend font size = " << legendFontSize << " is invalid";
738
739 legendFontSize_ = legendFontSize;
740 }
741
742 /* check for legend position validity */
IsValidLegendPosition(const String & position)743 Bool GraphImplementation::IsValidLegendPosition(const String & position)
744 {
745 const Description::const_iterator it = find(ValidLegendPositions.begin(), ValidLegendPositions.end(), position);
746
747 return (it != ValidLegendPositions.end());
748 }
749
750 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const751 void GraphImplementation::save(Advocate & adv) const
752 {
753 PersistentObject::save(adv);
754 adv.saveAttribute( "title_", title_ );
755 adv.saveAttribute( "legendPosition_", legendPosition_ );
756 adv.saveAttribute( "legendFontSize_", legendFontSize_ );
757 adv.saveAttribute( "xTitle_", xTitle_ );
758 adv.saveAttribute( "yTitle_", yTitle_ );
759 adv.saveAttribute( "showAxes_", showAxes_ );
760 adv.saveAttribute( "tickLocation_", static_cast<UnsignedInteger>(tickLocation_) );
761 adv.saveAttribute( "logScale_", static_cast<UnsignedInteger>(logScale_) );
762 adv.saveAttribute( "showGrid_", showGrid_ );
763 adv.saveAttribute( "gridColor_", gridColor_ );
764 adv.saveAttribute( "xMargin_", xMargin_ );
765 adv.saveAttribute( "yMargin_", yMargin_ );
766 adv.saveAttribute( "automaticBoundingBox_", automaticBoundingBox_ );
767 adv.saveAttribute( "boundingBox_", boundingBox_ );
768 adv.saveAttribute( "drawablesCollection_", drawablesCollection_ );
769 }
770
771 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)772 void GraphImplementation::load(Advocate & adv)
773 {
774 PersistentObject::load(adv);
775 adv.loadAttribute( "title_", title_ );
776 adv.loadAttribute( "legendPosition_", legendPosition_ );
777 adv.loadAttribute( "legendFontSize_", legendFontSize_ );
778 adv.loadAttribute( "xTitle_", xTitle_ );
779 adv.loadAttribute( "yTitle_", yTitle_ );
780 adv.loadAttribute( "showAxes_", showAxes_ );
781 UnsignedInteger tickLocation = 0;
782 adv.loadAttribute( "tickLocation_", tickLocation );
783 tickLocation_ = static_cast<TickLocation>(tickLocation);
784 UnsignedInteger logScale = 0;
785 adv.loadAttribute( "logScale_", logScale );
786 logScale_ = static_cast<LogScale>(logScale);
787 adv.loadAttribute( "showGrid_", showGrid_ );
788 adv.loadAttribute( "gridColor_", gridColor_ );
789 adv.loadAttribute( "xMargin_", xMargin_ );
790 adv.loadAttribute( "yMargin_", yMargin_ );
791 adv.loadAttribute( "automaticBoundingBox_", automaticBoundingBox_ );
792 adv.loadAttribute( "boundingBox_", boundingBox_ );
793 adv.loadAttribute( "drawablesCollection_", drawablesCollection_ );
794 }
795
796 END_NAMESPACE_OPENTURNS
797