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