1 #include "qwt3d_curve.h"
2
3 using namespace std;
4 using namespace Qwt3D;
5
6 /**
7 Initializes with dataNormals()==false, NOFLOOR, resolution() == 1
8 */
Curve(QWidget * parent)9 Curve::Curve(QWidget* parent)
10 : plot_p(qobject_cast<Plot3D*> (parent))
11 {
12 if (plot_p) {
13 title_p = new Label();
14 plot_p->setCurve(this);
15 plot_p->addTitle(title_p);
16 connects();
17 }
18
19 plotstyle_ = FILLEDMESH;
20 userplotstyle_p = 0;
21 shading_ = GOURAUD;
22 floorstyle_ = NOFLOOR;
23 floormode_ = true;
24 isolines_ = 10;
25 point_size_ = 5;
26
27 datanormals_p = false;
28 normalLength_p = 0.02;
29 normalQuality_p = 3;
30
31 resolution_p = 1;
32 actualDataG_ = new GridData();
33 actualDataC_ = new CellData();
34 actualData_p = actualDataG_;
35
36 displaylists_p.resize(DisplayListSize);
37 meshLineWidth_ = 1;
38 datacolor_p = new StandardColor(this, 100);
39 setPolygonOffset(0.5);
40 setMeshColor(RGBA(0.0,0.0,0.0));
41 setMeshLineWidth(1);
42
43 title_p->setFont("Courier", 16, QFont::Bold);
44 title_p->setString("");
45
46 titlespace_ = 0.02;
47 titlepos_ = plot_p->titleList().size() - 1;
48 setTitlePosition(0.98 - (titlepos_ * titlespace_));
49
50 update_displaylists_ = false;
51 displaylegend_ = false;
52
53 legend_.setPlot(plot_p);
54 legend_.setLimits(0, 100);
55 legend_.setMajors(10);
56 legend_.setMinors(2);
57 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);
58 }
59
~Curve()60 Curve::~Curve()
61 {
62 delete actualDataG_;
63 delete actualDataC_;
64
65 glDeleteLists(displaylists_p[DataObject], 1);
66
67 for (ELIT it = elist_p.begin(); it!=elist_p.end(); ++it)
68 delete (*it);
69
70 elist_p.clear();
71
72 if (plot_p) {
73 plot_p->removeCurve(this);
74 plot_p->removeTitle(title_p);
75 }
76 disconnects();
77
78 delete title_p;
79 }
80
connects()81 void Curve::connects()
82 {
83 // Parent Plot3D Relayed Signals
84 connect(plot_p, SIGNAL(setCurveResolution(int)), SLOT(setResolution(int)));
85 connect(plot_p, SIGNAL(setCurvePolygonOffset(double)), SLOT(setPolygonOffset(double)));
86 connect(plot_p, SIGNAL(setCurveIsolines(unsigned int)), SLOT(setIsolines(unsigned int)));
87
88 connect(plot_p, SIGNAL(setCurvePlotStyle(Qwt3D::PLOTSTYLE)), SLOT(setPlotStyle(Qwt3D::PLOTSTYLE)));
89 connect(plot_p, SIGNAL(setCurvePlotStyle(Qwt3D::Enrichment&)), SLOT(setPlotStyle(Qwt3D::Enrichment&)));
90 connect(plot_p, SIGNAL(setCurveFloorStyle(Qwt3D::FLOORSTYLE)), SLOT(setFloorStyle(Qwt3D::FLOORSTYLE)));
91 connect(plot_p, SIGNAL(setCurveShading(Qwt3D::SHADINGSTYLE)), SLOT(setShading(Qwt3D::SHADINGSTYLE)));
92
93 connect(plot_p, SIGNAL(showCurveColorLegend(bool)), SLOT(showColorLegend(bool)));
94 connect(plot_p, SIGNAL(showCurveNormals(bool)), SLOT(showNormals(bool)));
95 connect(plot_p, SIGNAL(setCurveNormalLength(double)), SLOT(setNormalLength(double)));
96 connect(plot_p, SIGNAL(setCurveNormalQuality(int)), SLOT(setNormalQuality(int)));
97
98 connect(plot_p, SIGNAL(setCurveSmoothMesh(bool)), SLOT(setSmoothMesh(bool)));
99 connect(plot_p, SIGNAL(setCurveMeshColor(Qwt3D::RGBA)), SLOT(setMeshColor(Qwt3D::RGBA)));
100 connect(plot_p, SIGNAL(setCurveMeshLineWidth(double)), SLOT(setMeshLineWidth(double)));
101
102 connect(plot_p, SIGNAL(setCurveDataColor(Color*)), SLOT(setDataColor(Color*)));
103 connect(plot_p, SIGNAL(setCurveDataProjection(bool)), SLOT(setDataProjection(bool)));
104 connect(plot_p, SIGNAL(setCurveProjection(Qwt3D::PROJECTMODE, bool)), SLOT(setProjection(Qwt3D::PROJECTMODE, bool)));
105
106 connect(plot_p, SIGNAL(setCurveTitleColor(Qwt3D::RGBA)), SLOT(setTitleColor(Qwt3D::RGBA)));
107 connect(plot_p, SIGNAL(setCurveTitle(const QString&)), SLOT(setTitle(const QString&)));
108 connect(plot_p, SIGNAL(setCurveTitlePosition(double,double,Qwt3D::ANCHOR)), SLOT(setTitlePosition(double,double,Qwt3D::ANCHOR)));
109 connect(plot_p, SIGNAL(setCurveTitleFont(const QString&,int,int,bool)), SLOT(setTitleFont(const QString&,int,int,bool)));
110
111 connect(plot_p, SIGNAL(createData()), SLOT(createData()));
112 connect(plot_p, SIGNAL(createEnrichments()), SLOT(createEnrichments()));
113
114 // Reverse relayed signals
115 connect(this, SIGNAL(updatePlotData(bool)), plot_p, SLOT(updateData(bool)));
116 connect(this, SIGNAL(updatePlot()), plot_p, SLOT(update()));
117 }
118
disconnects()119 void Curve::disconnects()
120 {
121 disconnect(plot_p, 0, this, 0);
122 disconnect(this, 0, plot_p, 0);
123 }
124
draw()125 void Curve::draw()
126 {
127 // if the display list needs to be updated
128 if ( update_displaylists_ ) {
129 // create display list if necessary
130 if ( 0 == displaylists_p[DataObject] ) {
131 displaylists_p[DataObject] = glGenLists(1);
132 }
133
134 // glGenLists returns 0 on error. So, if displaylists_p[DataObject] is still 0,
135 // then just call drawImplementation()
136 if ( 0 == displaylists_p[DataObject] ) {
137 drawImplementation();
138 return;
139 }
140
141 glNewList(displaylists_p[DataObject], GL_COMPILE);
142 drawImplementation();
143 glEndList();
144
145 updateNormals();
146 }
147
148 // glGenLists returns 0 on error. So, if displaylists_p[DataObject] is still 0,
149 // then just call drawImplementation()
150 if ( 0 == displaylists_p[DataObject] ) {
151 drawImplementation();
152 return;
153 }
154
155 for ( unsigned int i=0; i < DisplayListSize; ++i ) {
156 glCallList(displaylists_p[i]);
157 }
158 }
159
drawImplementation()160 void Curve::drawImplementation()
161 {
162 createEnrichments();
163 createData();
164 }
165
166 /*!
167 Calculates the smallest x-y-z parallelepiped enclosing the data.
168 It can be accessed by hull();
169 */
calculateHull()170 void Curve::calculateHull()
171 {
172 if (actualData_p->empty()) return;
173
174 setHull(actualData_p->hull());
175 }
176
updateData(bool coord)177 void Curve::updateData(bool coord)
178 {
179 update_displaylists_ = true;
180
181 if ( plot_p ) {
182 calculateHull();
183 emit updatePlotData(coord);
184 }
185 }
186
queueUpdate()187 void Curve::queueUpdate()
188 {
189 update_displaylists_ = true;
190 if ( plot_p ) {
191 emit updatePlot();
192 }
193 }
194
updateNormals()195 void Curve::updateNormals()
196 {
197 SaveGlDeleteLists(displaylists_p[NormalObject], 1);
198
199 if (plotStyle() == NOPLOT && !normals() || !actualData_p) return;
200
201 displaylists_p[NormalObject] = glGenLists(1);
202 glNewList(displaylists_p[NormalObject], GL_COMPILE);
203
204 if (actualData_p->datatype == Qwt3D::POLYGON)
205 createNormalsC();
206 else if (actualData_p->datatype == Qwt3D::GRID)
207 createNormalsG();
208
209 glEndList();
210 }
211
createData()212 void Curve::createData()
213 {
214 if (!actualData_p) return;
215
216 switch (shading_) {
217 case FLAT:
218 glShadeModel(GL_FLAT);
219 break;
220 case GOURAUD:
221 glShadeModel(GL_SMOOTH);
222 break;
223 default:
224 break;
225 }
226
227 if (actualData_p->datatype == Qwt3D::POLYGON)
228 createDataC();
229 else if (actualData_p->datatype == Qwt3D::GRID)
230 createDataG();
231 }
232
createFloorData()233 void Curve::createFloorData()
234 {
235 if (!actualData_p) return;
236
237 if (actualData_p->datatype == Qwt3D::POLYGON)
238 createFloorDataC();
239 else if (actualData_p->datatype == Qwt3D::GRID)
240 createFloorDataG();
241 }
242
createSideData()243 void Curve::createSideData()
244 {
245 if (!actualData_p) return;
246
247 if (actualData_p->datatype == Qwt3D::POLYGON)
248 createSideDataC();
249 else if (actualData_p->datatype == Qwt3D::GRID)
250 createSideDataG();
251 }
252
createFaceData()253 void Curve::createFaceData()
254 {
255 if (!actualData_p) return;
256
257 if (actualData_p->datatype == Qwt3D::POLYGON)
258 createFaceDataC();
259 else if (actualData_p->datatype == Qwt3D::GRID)
260 createFaceDataG();
261 }
262
263 /*!
264 The returned value is not affected by resolution(). The pair gives (columns,rows) for grid data,
265 (number of cells,1) for free formed data (datatype() == POLYGON) and (0,0) else
266 */
facets() const267 pair<int,int> Curve::facets() const
268 {
269 if (!hasData()) {
270 qDebug() << "Curve: has no data, no facets found!" << this;
271 if (actualData_p) qDebug() << "Curve: Grid type -" << (actualData_p->datatype == Qwt3D::GRID);
272 if (actualData_p->datatype == Qwt3D::POLYGON)
273 qDebug() << "Curve: Polygon data -" << actualDataC_->cells.size();
274 else if (actualData_p->datatype == Qwt3D::GRID)
275 qDebug() << "Curve: Grid data -" << actualDataG_->columns() << actualDataG_->rows();
276 return pair<int,int>(0,0);
277 }
278
279 if (actualData_p->datatype == Qwt3D::POLYGON)
280 return pair<int,int>(int(actualDataC_->cells.size()), 1);
281 else if (actualData_p->datatype == Qwt3D::GRID)
282 return pair<int,int>(actualDataG_->columns(), actualDataG_->rows());
283 else
284 return pair<int,int>(0,0);
285 }
286
createPoints()287 void Curve::createPoints()
288 {
289 // Dot pt;
290 // Cone d(15,32);
291 // CrossHair d(0.003,0,true,false);
292
293 Dot pt(5,true);
294 createEnrichment(pt);
295 }
296
addEnrichment(Enrichment const & e)297 Enrichment* Curve::addEnrichment(Enrichment const& e)
298 {
299 ELIT it = std::find( elist_p.begin(), elist_p.end(), &e );
300 if ( elist_p.end() == it ) {
301 elist_p.push_back(e.clone());
302 update_displaylists_ = true;
303 } else return *it;
304
305 return elist_p.back();
306 }
307
degrade(Enrichment * e)308 bool Curve::degrade(Enrichment* e)
309 {
310 ELIT it = std::find(elist_p.begin(), elist_p.end(), e);
311
312 if ( it != elist_p.end() ) {
313 delete (*it);
314 elist_p.erase(it);
315 update_displaylists_ = true;
316 return true;
317 }
318 return false;
319 }
320
clearEnrichments()321 void Curve::clearEnrichments()
322 {
323 for (ELIT it = elist_p.begin(); it != elist_p.end(); ++it)
324 this->degrade(*it);
325 }
326
createEnrichments()327 void Curve::createEnrichments()
328 {
329 for (ELIT it = elist_p.begin(); it!=elist_p.end(); ++it)
330 this->createEnrichment(**it);
331 }
332
createEnrichment(Enrichment & p)333 void Curve::createEnrichment(Enrichment& p)
334 {
335 if (!actualData_p) return;
336
337 //todo future work
338 if (p.type() != Enrichment::VERTEXENRICHMENT) return;
339
340 p.assign(*this);
341 p.drawBegin();
342
343 VertexEnrichment* ve = (VertexEnrichment*)&p;
344 if (actualData_p->datatype == Qwt3D::POLYGON) {
345 for (unsigned i = 0; i != actualDataC_->normals.size(); ++i)
346 ve->draw(actualDataC_->nodes[i]);
347 } else if (actualData_p->datatype == Qwt3D::GRID) {
348 int step = resolution();
349 for (int i = 0; i <= actualDataG_->columns() - step; i += step)
350 for (int j = 0; j <= actualDataG_->rows() - step; j += step)
351 ve->draw(Triple(actualDataG_->vertices[i][j][0],
352 actualDataG_->vertices[i][j][1],
353 actualDataG_->vertices[i][j][2]));
354 }
355
356 p.drawEnd();
357 }
358
drawVertex(Triple & vertex,double shift,unsigned int comp)359 void Curve::drawVertex(Triple& vertex, double shift, unsigned int comp)
360 {
361 switch (comp) {
362 case 0:
363 glVertex3d(shift, vertex.y, vertex.z); break;
364 case 1:
365 glVertex3d(vertex.x, shift, vertex.z); break;
366 case 2:
367 glVertex3d(vertex.x, vertex.y, shift); break;
368 default:
369 glVertex3d(vertex.x, vertex.y, vertex.z);
370 }
371 }
372
drawIntersections(vector<Triple> & intersection,double shift,unsigned int comp,bool projected,vector<RGBA> * colour)373 void Curve::drawIntersections(vector<Triple>& intersection, double shift, unsigned int comp,
374 bool projected, vector<RGBA>* colour)
375 {
376 if (intersection.empty())
377 return;
378
379 if (intersection.size() > 2) {
380 glBegin(GL_LINE_STRIP);
381 for (unsigned dd = 0; dd!=intersection.size(); ++dd) {
382 if (colour) glColor4d((*colour)[dd].r, (*colour)[dd].g, (*colour)[dd].b, (*colour)[dd].a);
383 drawVertex(intersection[dd], shift, comp);
384 }
385 glEnd();
386
387 if (projected) {
388 glBegin(GL_POINTS);
389 drawVertex(intersection[0], shift, comp);
390 glEnd();
391 }
392 } else if (intersection.size() == 2) {
393 glBegin(GL_LINES);
394 drawVertex(intersection[0], shift, comp);
395 drawVertex(intersection[1], shift, comp);
396
397 // small pixel gap problem (see OpenGL spec.)
398 drawVertex(intersection[1], shift, comp);
399 drawVertex(intersection[0], shift, comp);
400 glEnd();
401
402 if (projected) {
403 glBegin(GL_POINTS);
404 drawVertex(intersection[0], shift, comp);
405 drawVertex(intersection[1], shift, comp);
406 glEnd();
407 }
408 }
409
410 intersection.clear();
411 }
412
413 /*!
414 Set relative caption position (0.5,0.5) means, the anchor point lies in the center of the screen
415 */
setTitlePosition(double rely,double relx,Qwt3D::ANCHOR anchor)416 void Curve::setTitlePosition(double rely, double relx, Qwt3D::ANCHOR anchor)
417 {
418 titlerel_.y = (rely < 0 || rely > 1) ? 0.5 : rely;
419 titlerel_.x = (relx < 0 || relx > 1) ? 0.5 : relx;
420
421 titleanchor_ = anchor;
422 }
423
424 /*!
425 Set caption font
426 */
setTitleFont(const QString & family,int pointSize,int weight,bool italic)427 void Curve::setTitleFont(const QString& family, int pointSize, int weight, bool italic)
428 {
429 title_p->setFont(family, pointSize, weight, italic);
430
431 titlespace_ = ((pointSize/8.0 + weight/50.0)*0.01); // factor in pointsize & weight to title spacing
432 setTitlePosition(0.98 - (titlepos_ * titlespace_));
433 }
434
435 /*!
436 Sets data resolution (res == 1 original resolution) and updates widget
437 If res < 1, the function does nothing
438 */
setResolution(int res)439 void Curve::setResolution(int res)
440 {
441 if (!actualData_p || actualData_p->datatype == Qwt3D::POLYGON)
442 return;
443
444 if ((resolution_p == res) || res < 1)
445 return;
446
447 resolution_p = res;
448 calculateHull();
449 update_displaylists_ = true;
450
451 emit resolutionChanged(res);
452 }
453
setProjection(Qwt3D::PROJECTMODE val,bool toggle)454 void Curve::setProjection(Qwt3D::PROJECTMODE val, bool toggle)
455 {
456 switch(val) {
457 case Qwt3D::FACE:
458 facemode_ = toggle; break;
459 case Qwt3D::SIDE:
460 sidemode_ = toggle; break;
461 case Qwt3D::BASE:
462 floormode_ = toggle; break;
463 }
464 }
465
setMeshColor(RGBA rgba)466 void Curve::setMeshColor(RGBA rgba)
467 {
468 meshcolor_ = rgba;
469 update_displaylists_ = true;
470 }
471
472 /*!
473 Assign a new coloring object for the data.
474 */
setDataColor(Color * col)475 void Curve::setDataColor(Color* col)
476 {
477 Q_ASSERT(datacolor_p);
478
479 if (col == datacolor_p) return;
480
481 datacolor_p->destroy();
482 datacolor_p = col;
483 update_displaylists_ = true;
484 }
485
showNormals(bool b)486 void Curve::showNormals(bool b)
487 {
488 datanormals_p = b;
489 update_displaylists_ = true;
490 }
491
492 /*!
493 Values < 0 or > 1 are ignored
494 */
setNormalLength(double val)495 void Curve::setNormalLength(double val)
496 {
497 if (val < 0 || val > 1) return;
498
499 normalLength_p = val;
500 update_displaylists_ = true;
501 }
502
503 /*!
504 Values < 3 are ignored
505 */
setNormalQuality(int val)506 void Curve::setNormalQuality(int val)
507 {
508 if (val < 3) return;
509
510 normalQuality_p = val;
511 update_displaylists_ = true;
512 }
513
514 /*!
515 Set plotstyle for the standard plotting types. An argument of value Qwt3D::USER is ignored.
516 */
setPlotStyle(PLOTSTYLE val)517 void Curve::setPlotStyle( PLOTSTYLE val )
518 {
519 if (val == Qwt3D::USER) return;
520
521 if ( val == plotstyle_ ) return;
522
523 delete userplotstyle_p;
524 userplotstyle_p = 0;
525 plotstyle_ = val;
526 update_displaylists_ = true;
527 }
528
529 /*!
530 Set plotstyle to Qwt3D::USER and an associated enrichment object.
531 */
setPlotStyle(Qwt3D::Enrichment & obj)532 void Curve::setPlotStyle( Qwt3D::Enrichment& obj )
533 {
534 if (&obj == userplotstyle_p) return;
535
536 userplotstyle_p = obj.clone();
537 plotstyle_ = Qwt3D::USER;
538 update_displaylists_ = true;
539 }
540
541 /*!
542 Set shading style
543 */
setShading(SHADINGSTYLE val)544 void Curve::setShading( SHADINGSTYLE val )
545 {
546 if (val == shading_) return;
547
548 shading_ = val;
549 update_displaylists_ = true;
550 }
551
552 /*!
553 Set number of isolines. The lines are equidistant between minimal and maximal Z value
554 */
setIsolines(unsigned int steps)555 void Curve::setIsolines(unsigned int steps)
556 {
557 if (steps == isolines_) return;
558
559 isolines_ = steps;
560 update_displaylists_ = true;
561 }
562
563 /*!
564 Set Polygon offset. The function affects the OpenGL rendering process.
565 Try different values for surfaces with polygons only and with mesh and polygons
566 */
setPolygonOffset(double val)567 void Curve::setPolygonOffset( double val )
568 {
569 if ( val == polygonOffset_ ) return;
570
571 polygonOffset_ = val;
572 update_displaylists_ = true;
573 }
574
setMeshLineWidth(double val)575 void Curve::setMeshLineWidth( double val )
576 {
577 Q_ASSERT(val >= 0);
578
579 if (val < 0 || val == meshLineWidth_ ) return;
580
581 meshLineWidth_ = val;
582 update_displaylists_ = true;
583 }
584
showColorLegend(bool show)585 void Curve::showColorLegend(bool show)
586 {
587 displaylegend_ = show;
588
589 if (show) {
590 Color* color = const_cast<Color*>(dataColor());
591 if (color) color->createVector(legend_.colors);
592 }
593 updateData(false);
594 }
595
updateColorLegend(int majors,int minors)596 void Curve::updateColorLegend(int majors, int minors)
597 {
598 if (legend_.axis()->majors() != majors) legend_.setMajors(majors);
599 if (legend_.axis()->minors() != minors) legend_.setMinors(minors);
600
601 ParallelEpiped hull = data()->hull();
602 pair<double, double> limits(hull.minVertex.z, hull.maxVertex.z);
603
604 legend_.setLimits(limits.first, limits.second);
605 }
606
setColorLegend(int index,bool doublemode,QSize size,QPoint pos)607 void Curve::setColorLegend(int index, bool doublemode, QSize size, QPoint pos)
608 {
609 double w = size.width()/100.0, h = size.height()/100.0; // legend color vector as screen size percentage
610 double x = pos.x()/100.0, y = pos.y()/100.0; // legend anchor as screen position percentage
611
612 switch(index) {
613 case 0:
614 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);
615 legend_.setRelPosition(Tuple(1-x-w, 0.5+y), Tuple(1-x, 0.5+y+h));
616 break;
617 case 1:
618 if (doublemode) {
619 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Right);
620 legend_.setRelPosition(Tuple(1-x, 0.5+y), Tuple(1-x+w, 0.5+y+h));
621 } else {
622 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);
623 legend_.setRelPosition(Tuple(1-x-w, 0.5-y-h), Tuple(1-x, 0.5-y));
624 }
625 break;
626 case 2:
627 if (doublemode) {
628 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);
629 legend_.setRelPosition(Tuple(1-x-w, 0.5-y-h), Tuple(1-x, 0.5-y));
630 } else {
631 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Top);
632 legend_.setRelPosition(Tuple(0.5+y, x), Tuple(0.5+y+h, x+w));
633 }
634 break;
635 case 3:
636 if (doublemode) {
637 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Right);
638 legend_.setRelPosition(Tuple(1-x, 0.5-y-h), Tuple(1-x+w, 0.5-y));
639 } else {
640 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Top);
641 legend_.setRelPosition(Tuple(0.5-y-h, x), Tuple(0.5-y, x+w));
642 }
643 break;
644 case 4:
645 if (doublemode) {
646 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Top);
647 legend_.setRelPosition(Tuple(0.5+y, x), Tuple(0.5+y+h, x+w));
648 } else {
649 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Right);
650 legend_.setRelPosition(Tuple(x, 0.5-y-h), Tuple(x+w, 0.5-y));
651 }
652 break;
653 case 5:
654 if (doublemode) {
655 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Bottom);
656 legend_.setRelPosition(Tuple(0.5+y, x-w), Tuple(0.5+y+h, x));
657 } else {
658 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Right);
659 legend_.setRelPosition(Tuple(x, 0.5+y), Tuple(x+w, 0.5+y+h));
660 }
661 break;
662 case 6:
663 if (doublemode) {
664 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Top);
665 legend_.setRelPosition(Tuple(0.5-y-h, x), Tuple(0.5-y, x+w));
666 } else {
667 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Bottom);
668 legend_.setRelPosition(Tuple(0.5-y-h, 1-x-w), Tuple(0.5-y, 1-x));
669 }
670 break;
671 case 7:
672 if (doublemode) {
673 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Bottom);
674 legend_.setRelPosition(Tuple(0.5-y-h, x-w), Tuple(0.5-y, x));
675 } else {
676 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Bottom);
677 legend_.setRelPosition(Tuple(0.5+y, 1-x-w), Tuple(0.5+y+h, 1-x));
678 }
679 break;
680 case 8:
681 if (doublemode) {
682 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Right);
683 legend_.setRelPosition(Tuple(x, 0.5-y-h), Tuple(x+w, 0.5-y));
684 }
685 break;
686 case 9:
687 if (doublemode) {
688 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);
689 legend_.setRelPosition(Tuple(x-w, 0.5-y-h), Tuple(x, 0.5-y));
690 }
691 break;
692 case 10:
693 if (doublemode) {
694 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Right);
695 legend_.setRelPosition(Tuple(x, 0.5+y), Tuple(x+w, 0.5+y+h));
696 }
697 break;
698 case 11:
699 if (doublemode) {
700 legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);
701 legend_.setRelPosition(Tuple(x-w, 0.5+y), Tuple(x, 0.5+y+h));
702 }
703 break;
704 case 12:
705 if (doublemode) {
706 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Bottom);
707 legend_.setRelPosition(Tuple(0.5-y-h, 1-x-w), Tuple(0.5-y, 1-x));
708 }
709 break;
710 case 13:
711 if (doublemode) {
712 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Top);
713 legend_.setRelPosition(Tuple(0.5-y-h, 1-x), Tuple(0.5-y, 1-x+w));
714 }
715 break;
716 case 14:
717 if (doublemode) {
718 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Bottom);
719 legend_.setRelPosition(Tuple(0.5+y, 1-x-w), Tuple(0.5+y+h, 1-x));
720 }
721 break;
722 case 15:
723 if (doublemode) {
724 legend_.setOrientation(ColorLegend::LeftRight, ColorLegend::Top);
725 legend_.setRelPosition(Tuple(0.5+y, 1-x), Tuple(0.5+y+h, 1-x+w));
726 }
727 break;
728 default:
729 break;
730 }
731 }
732