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