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; }
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(); } }
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; }