Example #1
0
QString cc2DLabel::getTitle(int precision) const
{
	QString title;
	size_t count = m_points.size();
	if (count == 1)
	{
		title = m_name;
		title.replace(POINT_INDEX_0,QString::number(m_points[0].index));

		//if available, we display the point SF value
		LabelInfo1 info;
		getLabelInfo1(info);
		if (info.hasSF)
		{
			title = QString("%1 = %2 (%3)").arg(info.sfName).arg(info.sfValue,0,'f',precision).arg(title);
		}
	}
	else if (count == 2)
	{
		LabelInfo2 info;
		getLabelInfo2(info);
		//display distance by default
		double dist = info.diff.normd();
		title = QString("Distance: %1").arg(dist,0,'f',precision);
	}
	else if (count == 3)
	{
		LabelInfo3 info;
		getLabelInfo3(info);
		//display area by default
		title = QString("Area: %1").arg(info.area,0,'f',precision);
	}

	return title;
}
Example #2
0
void cc2DLabel::drawMeOnly2D(CC_DRAW_CONTEXT& context)
{
	if (!m_dispIn2D)
		return;

	assert(!m_points.empty());

	//get the set of OpenGL functions (version 2.1)
	QOpenGLFunctions_2_1 *glFunc = context.glFunctions<QOpenGLFunctions_2_1>();
	assert( glFunc != nullptr );
	
	if ( glFunc == nullptr )
		return;

	//standard case: list names pushing
	bool pushName = MACRO_DrawEntityNames(context);
	if (pushName)
	{
		glFunc->glPushName(getUniqueID());
	}

	//we should already be in orthoprojective & centered omde
	//glFunc->glOrtho(-halfW,halfW,-halfH,halfH,-maxS,maxS);

	//label title
	const int precision = context.dispNumberPrecision;
	QString title = getTitle(precision);

#define DRAW_CONTENT_AS_TAB
#ifdef DRAW_CONTENT_AS_TAB
	//draw contents as an array
	Tab tab(4);
	int rowHeight = 0;
#else
//simply display the content as text
	QStringList body;
#endif

	//render zoom
	int margin        = static_cast<int>(c_margin        * context.renderZoom);
	int tabMarginX    = static_cast<int>(c_tabMarginX    * context.renderZoom);
	int tabMarginY    = static_cast<int>(c_tabMarginY    * context.renderZoom);
	int arrowBaseSize = static_cast<int>(c_arrowBaseSize * context.renderZoom);
	
	int titleHeight = 0;
	GLdouble arrowDestX = -1.0, arrowDestY = -1.0;
	QFont bodyFont,titleFont;
	if (!pushName)
	{
		/*** line from 2D point to label ***/

		//compute arrow head position
		CCVector3 arrowDest;
		m_points[0].cloud->getPoint(m_points[0].index,arrowDest);
		for (unsigned i = 1; i < m_points.size(); ++i)
		{
			arrowDest += *m_points[i].cloud->getPointPersistentPtr(m_points[i].index);
		}
		arrowDest /= static_cast<PointCoordinateType>(m_points.size());

		//project it in 2D screen coordinates
		{
			ccGLCameraParameters camera;
			context.display->getGLCameraParameters(camera);

			CCVector3d Q2D;
			camera.project(arrowDest, Q2D);
			arrowDestX = Q2D.x;
			arrowDestY = Q2D.y;
		}

		/*** label border ***/
		bodyFont = context.display->getLabelDisplayFont(); //takes rendering zoom into account!
		titleFont = bodyFont; //takes rendering zoom into account!
		//titleFont.setBold(true);

		QFontMetrics titleFontMetrics(titleFont);
		titleHeight = titleFontMetrics.height();

		QFontMetrics bodyFontMetrics(bodyFont);
		rowHeight = bodyFontMetrics.height();

		//get label box dimension
		int dx = 100;
		int dy = 0;
		//int buttonSize    = static_cast<int>(c_buttonSize * context.renderZoom);
		{
			//base box dimension
			dx = std::max(dx,titleFontMetrics.width(title));
			dy += margin;		//top vertical margin
			dy += titleHeight;	//title

			if (m_showFullBody)
			{
#ifdef DRAW_CONTENT_AS_TAB
				try
				{
					size_t labelCount = m_points.size();
					if (labelCount == 1)
					{
						LabelInfo1 info;
						getLabelInfo1(info);

						bool isShifted = info.cloud->isShifted();
						//1st block: X, Y, Z (local)
						{
							int c = tab.add2x3Block();
							QChar suffix;
							if (isShifted)
								suffix = 'l'; //'l' for local
							const CCVector3* P = info.cloud->getPoint(info.pointIndex);
							tab.colContent[c] << QString("X") + suffix; tab.colContent[c+1] << QString::number(P->x,'f',precision);
							tab.colContent[c] << QString("Y") + suffix; tab.colContent[c+1] << QString::number(P->y,'f',precision);
							tab.colContent[c] << QString("Z") + suffix; tab.colContent[c+1] << QString::number(P->z,'f',precision);
						}
						//next block:  X, Y, Z (global)
						if (isShifted)
						{
							int c = tab.add2x3Block();
							CCVector3d P = info.cloud->toGlobal3d(*info.cloud->getPoint(info.pointIndex));
							tab.colContent[c] << "Xg"; tab.colContent[c+1] << QString::number(P.x,'f',precision);
							tab.colContent[c] << "Yg"; tab.colContent[c+1] << QString::number(P.y,'f',precision);
							tab.colContent[c] << "Zg"; tab.colContent[c+1] << QString::number(P.z,'f',precision);
						}
						//next block: normal
						if (info.hasNormal)
						{
							int c = tab.add2x3Block();
							tab.colContent[c] << "Nx"; tab.colContent[c+1] << QString::number(info.normal.x,'f',precision);
							tab.colContent[c] << "Ny"; tab.colContent[c+1] << QString::number(info.normal.y,'f',precision);
							tab.colContent[c] << "Nz"; tab.colContent[c+1] << QString::number(info.normal.z,'f',precision);
						}

						//next block: RGB color
						if (info.hasRGB)
						{
							int c = tab.add2x3Block();
							tab.colContent[c] <<"R"; tab.colContent[c+1] << QString::number(info.rgb.x);
							tab.colContent[c] <<"G"; tab.colContent[c+1] << QString::number(info.rgb.y);
							tab.colContent[c] <<"B"; tab.colContent[c+1] << QString::number(info.rgb.z);
						}
					}
					else if (labelCount == 2)
					{
						LabelInfo2 info;
						getLabelInfo2(info);

						//1st block: dX, dY, dZ
						{
							int c = tab.add2x3Block();
							tab.colContent[c] << MathSymbolDelta + QString("X"); tab.colContent[c+1] << QString::number(info.diff.x,'f',precision);
							tab.colContent[c] << MathSymbolDelta + QString("Y"); tab.colContent[c+1] << QString::number(info.diff.y,'f',precision);
							tab.colContent[c] << MathSymbolDelta + QString("Z"); tab.colContent[c+1] << QString::number(info.diff.z,'f',precision);
						}
						//2nd block: dXY, dXZ, dZY
						{
							int c = tab.add2x3Block();
							PointCoordinateType dXY = sqrt(info.diff.x*info.diff.x + info.diff.y*info.diff.y);
							PointCoordinateType dXZ = sqrt(info.diff.x*info.diff.x + info.diff.z*info.diff.z);
							PointCoordinateType dZY = sqrt(info.diff.z*info.diff.z + info.diff.y*info.diff.y);
							tab.colContent[c] << MathSymbolDelta + QString("XY"); tab.colContent[c+1] << QString::number(dXY,'f',precision);
							tab.colContent[c] << MathSymbolDelta + QString("XZ"); tab.colContent[c+1] << QString::number(dXZ,'f',precision);
							tab.colContent[c] << MathSymbolDelta + QString("ZY"); tab.colContent[c+1] << QString::number(dZY,'f',precision);
						}
					}
					else if (labelCount == 3)
					{
						LabelInfo3 info;
						getLabelInfo3(info);
						tab.setMaxBlockPerRow(2); //square tab (2x2 blocks)

						//next block: indexes
						{
							int c = tab.add2x3Block();
							tab.colContent[c] << "index.A"; tab.colContent[c+1] << QString::number(info.point1Index);
							tab.colContent[c] << "index.B"; tab.colContent[c+1] << QString::number(info.point2Index);
							tab.colContent[c] << "index.C"; tab.colContent[c+1] << QString::number(info.point3Index);
						}
						//next block: edges length
						{
							int c = tab.add2x3Block();
							tab.colContent[c] << "AB"; tab.colContent[c+1] << QString::number(info.edges.u[0],'f',precision);
							tab.colContent[c] << "BC"; tab.colContent[c+1] << QString::number(info.edges.u[1],'f',precision);
							tab.colContent[c] << "CA"; tab.colContent[c+1] << QString::number(info.edges.u[2],'f',precision);
						}
						//next block: angles
						{
							int c = tab.add2x3Block();
							tab.colContent[c] << "angle.A"; tab.colContent[c+1] << QString::number(info.angles.u[0],'f',precision);
							tab.colContent[c] << "angle.B"; tab.colContent[c+1] << QString::number(info.angles.u[1],'f',precision);
							tab.colContent[c] << "angle.C"; tab.colContent[c+1] << QString::number(info.angles.u[2],'f',precision);
						}
						//next block: normal
						{
							int c = tab.add2x3Block();
							tab.colContent[c] << "Nx"; tab.colContent[c+1] << QString::number(info.normal.x,'f',precision);
							tab.colContent[c] << "Ny"; tab.colContent[c+1] << QString::number(info.normal.y,'f',precision);
							tab.colContent[c] << "Nz"; tab.colContent[c+1] << QString::number(info.normal.z,'f',precision);
						}
					}
				}
				catch (const std::bad_alloc&)
				{
					//not enough memory
					return;
				}

				//compute min width of each column
				int totalWidth = tab.updateColumnsWidthTable(bodyFontMetrics);

				int tabWidth = totalWidth + tab.colCount * (2*tabMarginX); //add inner margins
				dx = std::max(dx,tabWidth);
				dy += tab.rowCount * (rowHeight + 2*tabMarginY); //add inner margins
				//we also add a margin every 3 rows
				dy += std::max(0,(tab.rowCount/3)-1) * margin;
				dy += margin;		//bottom vertical margin
#else
				body = getLabelContent(precision);
				if (!body.empty())
				{
					dy += margin;	//vertical margin above separator
					for (int j=0; j<body.size(); ++j)
					{
						dx = std::max(dx,bodyFontMetrics.width(body[j]));
						dy += rowHeight; //body line height
					}
					dy += margin;	//vertical margin below text
				}
#endif //DRAW_CONTENT_AS_TAB
			}

			dx += margin*2;	// horizontal margins
		}

		//main rectangle
		m_labelROI = QRect(0,0,dx,dy);

		//close button
		//m_closeButtonROI.right()   = dx-margin;
		//m_closeButtonROI.left()    = m_closeButtonROI.right()-buttonSize;
		//m_closeButtonROI.bottom()  = margin;
		//m_closeButtonROI.top()     = m_closeButtonROI.bottom()+buttonSize;

		//automatically elide the title
		//title = titleFontMetrics.elidedText(title,Qt::ElideRight,m_closeButtonROI[0]-2*margin);
	}

	int halfW = (context.glW >> 1);
	int halfH = (context.glH >> 1);

	//draw label rectangle
	int xStart = static_cast<int>(context.glW * m_screenPos[0]);
	int yStart = static_cast<int>(context.glH * (1.0f - m_screenPos[1]));

	m_lastScreenPos[0] = xStart;
	m_lastScreenPos[1] = yStart - m_labelROI.height();

	//colors
	bool highlighted = (!pushName && isSelected());
	//default background color
	unsigned char alpha = static_cast<unsigned char>((context.labelOpacity/100.0) * 255);
	ccColor::Rgbaub defaultBkgColor(context.labelDefaultBkgCol,alpha);
	//default border color (mustn't be totally transparent!)
	ccColor::Rgbaub defaultBorderColor(ccColor::red);
	if (!highlighted)
	{
		//apply only half of the transparency
		unsigned char halfAlpha = static_cast<unsigned char>((50.0 + context.labelOpacity/200.0) * 255);
		defaultBorderColor = ccColor::Rgbaub(context.labelDefaultBkgCol,halfAlpha);
	}

	glFunc->glPushAttrib(GL_COLOR_BUFFER_BIT);
	glFunc->glEnable(GL_BLEND);

	glFunc->glMatrixMode(GL_MODELVIEW);
	glFunc->glPushMatrix();
	glFunc->glTranslatef(static_cast<GLfloat>(-halfW + xStart), static_cast<GLfloat>(-halfH + yStart), 0);

	if (!pushName)
	{
		//compute arrow base position relatively to the label rectangle (for 0 to 8)
		int arrowBaseConfig = 0;
		int iArrowDestX = static_cast<int>(arrowDestX)-xStart;
		int iArrowDestY = static_cast<int>(arrowDestY)-yStart;
		{
			if (iArrowDestX < m_labelROI.left()) //left
				arrowBaseConfig += 0;
			else if (iArrowDestX > m_labelROI.right()) //Right
				arrowBaseConfig += 2;
			else  //Middle
				arrowBaseConfig += 1;

			if (iArrowDestY > -m_labelROI.top()) //Top
				arrowBaseConfig += 0;
			else if (iArrowDestY < -m_labelROI.bottom()) //Bottom
				arrowBaseConfig += 6;
			else  //Middle
				arrowBaseConfig += 3;
		}

		//we make the arrow base start from the nearest corner
		if (arrowBaseConfig != 4) //4 = label above point!
		{
			glFunc->glColor4ubv(defaultBorderColor.rgba);
			glFunc->glBegin(GL_TRIANGLE_FAN);
			glFunc->glVertex2d(arrowDestX - xStart, arrowDestY - yStart);
			switch(arrowBaseConfig)
			{
			case 0: //top-left corner
				glFunc->glVertex2i(m_labelROI.left(), -m_labelROI.top()-2*arrowBaseSize);
				glFunc->glVertex2i(m_labelROI.left(), -m_labelROI.top());
				glFunc->glVertex2i(m_labelROI.left()+2*arrowBaseSize, -m_labelROI.top());
				break;
			case 1: //top-middle edge
				glFunc->glVertex2i(std::max(m_labelROI.left(),iArrowDestX-arrowBaseSize), -m_labelROI.top());
				glFunc->glVertex2i(std::min(m_labelROI.right(),iArrowDestX+arrowBaseSize), -m_labelROI.top());
				break;
			case 2: //top-right corner
				glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.top()-2*arrowBaseSize);
				glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.top());
				glFunc->glVertex2i(m_labelROI.right()-2*arrowBaseSize, -m_labelROI.top());
				break;
			case 3: //middle-left edge
				glFunc->glVertex2i(m_labelROI.left(), std::min(-m_labelROI.top(),iArrowDestY+arrowBaseSize));
				glFunc->glVertex2i(m_labelROI.left(), std::max(-m_labelROI.bottom(),iArrowDestY-arrowBaseSize));
				break;
			case 4: //middle of rectangle!
				break;
			case 5: //middle-right edge
				glFunc->glVertex2i(m_labelROI.right(), std::min(-m_labelROI.top(),iArrowDestY+arrowBaseSize));
				glFunc->glVertex2i(m_labelROI.right(), std::max(-m_labelROI.bottom(),iArrowDestY-arrowBaseSize));
				break;
			case 6: //bottom-left corner
				glFunc->glVertex2i(m_labelROI.left(), -m_labelROI.bottom()+2*arrowBaseSize);
				glFunc->glVertex2i(m_labelROI.left(), -m_labelROI.bottom());
				glFunc->glVertex2i(m_labelROI.left()+2*arrowBaseSize, -m_labelROI.bottom());
				break;
			case 7: //bottom-middle edge
				glFunc->glVertex2i(std::max(m_labelROI.left(),iArrowDestX-arrowBaseSize), -m_labelROI.bottom());
				glFunc->glVertex2i(std::min(m_labelROI.right(),iArrowDestX+arrowBaseSize), -m_labelROI.bottom());
				break;
			case 8: //bottom-right corner
				glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.bottom()+2*arrowBaseSize);
				glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.bottom());
				glFunc->glVertex2i(m_labelROI.right()-2*arrowBaseSize, -m_labelROI.bottom());
				break;
			}
			glFunc->glEnd();
		}
	}

	//main rectangle
	glFunc->glColor4ubv(defaultBkgColor.rgba);
	glFunc->glBegin(GL_QUADS);
	glFunc->glVertex2i(m_labelROI.left(),  -m_labelROI.top());
	glFunc->glVertex2i(m_labelROI.left(),  -m_labelROI.bottom());
	glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.bottom());
	glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.top());
	glFunc->glEnd();

	//if (highlighted)
	{
		glFunc->glPushAttrib(GL_LINE_BIT);
		glFunc->glLineWidth(3.0f * context.renderZoom);
		glFunc->glColor4ubv(defaultBorderColor.rgba);
		glFunc->glBegin(GL_LINE_LOOP);
		glFunc->glVertex2i(m_labelROI.left(),  -m_labelROI.top());
		glFunc->glVertex2i(m_labelROI.left(),  -m_labelROI.bottom());
		glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.bottom());
		glFunc->glVertex2i(m_labelROI.right(), -m_labelROI.top());
		glFunc->glEnd();
		glFunc->glPopAttrib();
	}

	//draw close button
	//glFunc->glColor3ubv(ccColor::black);
	//glFunc->glBegin(GL_LINE_LOOP);
	//glFunc->glVertex2i(m_closeButtonROI.left(),-m_closeButtonROI.top());
	//glFunc->glVertex2i(m_closeButtonROI.left(),-m_closeButtonROI.bottom());
	//glFunc->glVertex2i(m_closeButtonROI.right(),-m_closeButtonROI.bottom());
	//glFunc->glVertex2i(m_closeButtonROI.right(),-m_closeButtonROI.top());
	//glFunc->glEnd();
	//glFunc->glBegin(GL_LINES);
	//glFunc->glVertex2i(m_closeButtonROI.left()+2,-m_closeButtonROI.top()+2);
	//glFunc->glVertex2i(m_closeButtonROI.right()-2,-m_closeButtonROI.bottom()-2);
	//glFunc->glVertex2i(m_closeButtonROI.right()-2,-m_closeButtonROI.top()+2);
	//glFunc->glVertex2i(m_closeButtonROI.left()+2,-m_closeButtonROI.bottom()-2);
	//glFunc->glEnd();

	//display text
	if (!pushName)
	{
		int xStartRel = margin;
		int yStartRel = 0;
		yStartRel -= titleHeight;

		ccColor::Rgbub defaultTextColor;
		if (context.labelOpacity < 40)
		{
			//under a given opacity level, we use the default text color instead!
			defaultTextColor = context.textDefaultCol;
		}
		else
		{
			defaultTextColor = ccColor::Rgbub(	255 - context.labelDefaultBkgCol.r,
												255 - context.labelDefaultBkgCol.g,
												255 - context.labelDefaultBkgCol.b);
		}

		//label title
		context.display->displayText(	title,
										xStart+xStartRel,
										yStart+yStartRel,
										ccGenericGLDisplay::ALIGN_DEFAULT,
										0,
										defaultTextColor.rgb,
										&titleFont);
		yStartRel -= margin;
		
		if (m_showFullBody)
		{
#ifdef DRAW_CONTENT_AS_TAB
			int xCol = xStartRel;
			for (int c=0; c<tab.colCount; ++c)
			{
				int width = tab.colWidth[c] + 2*tabMarginX;
				int height = rowHeight + 2*tabMarginY;

				int yRow = yStartRel;
				int actualRowCount = std::min(tab.rowCount,tab.colContent[c].size());

				bool labelCol = ((c & 1) == 0);
				const unsigned char* textColor = labelCol ? ccColor::white.rgba : defaultTextColor.rgb;
				
				for (int r=0; r<actualRowCount; ++r)
				{
					if (r && (r % 3) == 0)
						yRow -= margin;

					if (labelCol)
					{
						//draw background
						int rgbIndex = (r % 3);
						if (rgbIndex == 0)
							glFunc->glColor3ubv(ccColor::red.rgba);
						else if (rgbIndex == 1)
							glFunc->glColor3ubv(c_darkGreen.rgba);
						else if (rgbIndex == 2)
							glFunc->glColor3ubv(ccColor::blue.rgba);

						glFunc->glBegin(GL_QUADS);
						glFunc->glVertex2i(m_labelROI.left() + xCol, -m_labelROI.top() + yRow);
						glFunc->glVertex2i(m_labelROI.left() + xCol, -m_labelROI.top() + yRow - height);
						glFunc->glVertex2i(m_labelROI.left() + xCol + width, -m_labelROI.top() + yRow - height);
						glFunc->glVertex2i(m_labelROI.left() + xCol + width, -m_labelROI.top() + yRow);
						glFunc->glEnd();
					}

					const QString& str = tab.colContent[c][r];

					int xShift = 0;
					if (labelCol)
					{
						//align characters in the middle
						xShift = (tab.colWidth[c] - QFontMetrics(bodyFont).width(str)) / 2;
					}
					else
					{
						//align digits on the right
						xShift = tab.colWidth[c] - QFontMetrics(bodyFont).width(str);
					}

					context.display->displayText(	str,
													xStart + xCol + tabMarginX + xShift,
													yStart + yRow - rowHeight, ccGenericGLDisplay::ALIGN_DEFAULT, 0, textColor, &bodyFont);

					yRow -= height;
				}

				xCol += width;
			}
#else
			if (!body.empty())
			{
				//display body
				yStartRel -= margin;
				for (int i=0; i<body.size(); ++i)
				{
					yStartRel -= rowHeight;
					context.display->displayText(body[i],xStart+xStartRel,yStart+yStartRel,ccGenericGLDisplay::ALIGN_DEFAULT,0,defaultTextColor.rgb,&bodyFont);
				}
			}
#endif //DRAW_CONTENT_AS_TAB
		}
	}

	glFunc->glPopAttrib();

	glFunc->glPopMatrix();

	if (pushName)
	{
		glFunc->glPopName();
	}
}
Example #3
0
QStringList cc2DLabel::getLabelContent(int precision)
{
	QStringList body;

	switch(m_points.size())
	{
	case 0:
		//can happen if the associated cloud(s) has(ve) been deleted!
		body << "Deprecated";
		break;

	case 1: //point
		{
			LabelInfo1 info;
			getLabelInfo1(info);
			if (!info.cloud)
				break;

			//coordinates
			AddPointCoordinates(body,info.pointIndex,info.cloud,precision);

			//normal
			if (info.hasNormal)
			{
				QString normStr = QString("Normal: (%1;%2;%3)").arg(info.normal.x,0,'f',precision).arg(info.normal.y,0,'f',precision).arg(info.normal.z,0,'f',precision);
				body << normStr;
			}
			//color
			if (info.hasRGB)
			{
				QString colorStr = QString("Color: (%1;%2;%3)").arg(info.rgb[0]).arg(info.rgb[1]).arg(info.rgb[2]);
				body << colorStr;
			}
			//scalar field
			if (info.hasSF)
			{
				QString sfVal = GetSFValueAsString(info, precision);
				QString sfStr = QString("%1 = %2").arg(info.sfName).arg(sfVal);
				body << sfStr;
			}
		}
		break;

	case 2: //vector
		{
			LabelInfo2 info;
			getLabelInfo2(info);
			if (!info.cloud1 || !info.cloud2)
				break;

			//distance is now the default label title
			//PointCoordinateType dist = info.diff.norm();
			//QString distStr = QString("Distance = %1").arg(dist,0,'f',precision);
			//body << distStr;

			QString vecStr =	MathSymbolDelta + QString("X: %1\t").arg(info.diff.x,0,'f',precision)
							+	MathSymbolDelta + QString("Y: %1\t").arg(info.diff.y,0,'f',precision)
							+	MathSymbolDelta + QString("Z: %1"  ).arg(info.diff.z,0,'f',precision);

			body << vecStr;

			PointCoordinateType dXY = sqrt(info.diff.x*info.diff.x + info.diff.y*info.diff.y);
			PointCoordinateType dXZ = sqrt(info.diff.x*info.diff.x + info.diff.z*info.diff.z);
			PointCoordinateType dZY = sqrt(info.diff.z*info.diff.z + info.diff.y*info.diff.y);

			vecStr =	MathSymbolDelta + QString("XY: %1\t").arg(dXY,0,'f',precision)
					+	MathSymbolDelta + QString("XZ: %1\t").arg(dXZ,0,'f',precision)
					+	MathSymbolDelta + QString("ZY: %1"  ).arg(dZY,0,'f',precision);
			body << vecStr;

			AddPointCoordinates(body,info.point1Index,info.cloud1,precision);
			AddPointCoordinates(body,info.point2Index,info.cloud2,precision);
		}
		break;

	case 3: //triangle/plane
		{
			LabelInfo3 info;
			getLabelInfo3(info);

			//area
			QString areaStr = QString("Area = %1").arg(info.area,0,'f',precision);
			body << areaStr;

			//coordinates
			AddPointCoordinates(body,info.point1Index,info.cloud1,precision,"A");
			AddPointCoordinates(body,info.point2Index,info.cloud2,precision,"B");
			AddPointCoordinates(body,info.point3Index,info.cloud3,precision,"C");

			//normal
			QString normStr = QString("Normal: (%1;%2;%3)").arg(info.normal.x,0,'f',precision).arg(info.normal.y,0,'f',precision).arg(info.normal.z,0,'f',precision);
			body << normStr;

			//angles
			QString angleStr = QString("Angles: A=%1 - B=%2 - C=%3 deg.")
									.arg(info.angles.u[0],0,'f',precision)
									.arg(info.angles.u[1],0,'f',precision)
									.arg(info.angles.u[2],0,'f',precision);
			body << angleStr;

			//edges
			QString edgesStr = QString("Edges: AB=%1 - BC=%2 - CA=%3")
									.arg(info.edges.u[0],0,'f',precision)
									.arg(info.edges.u[1],0,'f',precision)
									.arg(info.edges.u[2],0,'f',precision);
			body << edgesStr;
		}
		break;

	default:
		assert(false);
		break;
	}

	return body;
}