#include "qwt3d_coordsys.h" using namespace std; using namespace Qwt3D; CoordinateSystem::CoordinateSystem(Triple first, Triple second, COORDSTYLE st) { autodecoration_ = true; axes = std::vector(12); setStyle(st); setLineSmooth(true); init(first,second); setAxesColor(RGBA(0,0,0,1)); setGridLinesColor(RGBA(0.2,0.2,0.2,1)); setNumberFont("Courier", 12); setNumberColor(RGBA(0,0,0)); setLabelFont("Courier", 14, QFont::Bold); setGridLines(false, false); } CoordinateSystem::~CoordinateSystem() { destroy(); } void CoordinateSystem::destroy() { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setLabelString(""); detachAll(); } void CoordinateSystem::init(Triple first, Triple second) { destroy(); for (unsigned i=0; i!=axes.size(); ++i) axes[i].setScale(LINEARSCALE); Triple dv = second - first; setPosition(first, second); double majl = dv.length() / 100; // 1 % setTicLength(majl, 0.6 * majl); axes[X1].setPosition(first, first+Triple(dv.x, 0, 0)); // front bottom x axes[Y1].setPosition(first, first+Triple( 0, dv.y, 0)); // bottom left y axes[Z1].setPosition (first+Triple( 0, dv.y, 0), first+Triple( 0, dv.y, dv.z)); // back left z axes[X1].setTicOrientation(0,-1,0); axes[Y1].setTicOrientation(-1,0,0); axes[Z1].setTicOrientation(-1,0,0); axes[X1].setLimits(first.x, second.x); axes[X2].setLimits(first.x, second.x); axes[X3].setLimits(first.x, second.x); axes[X4].setLimits(first.x, second.x); axes[Y1].setLimits(first.y, second.y); axes[Y2].setLimits(first.y, second.y); axes[Y3].setLimits(first.y, second.y); axes[Y4].setLimits(first.y, second.y); axes[Z1].setLimits(first.z, second.z); axes[Z2].setLimits(first.z, second.z); axes[Z3].setLimits(first.z, second.z); axes[Z4].setLimits(first.z, second.z); // remaining x axes axes[X2].setPosition(first+Triple( 0, 0, dv.z), first+Triple( dv.x, 0, dv.z)); // front top x axes[X3].setPosition(first+Triple( 0, dv.y, dv.z), second); // back top x axes[X4].setPosition(first+Triple( 0, dv.y, 0), first+Triple( dv.x, dv.y, 0)); // back bottom x axes[X2].setTicOrientation(0,-1,0); axes[X3].setTicOrientation(0,1,0); axes[X4].setTicOrientation(0,1,0); // remaining y axes axes[Y2].setPosition(first+Triple(dv.x, 0, 0), first+Triple(dv.x, dv.y, 0)); // bottom right y axes[Y3].setPosition(first+Triple(dv.x, 0, dv.z), second); // top right y axes[Y4].setPosition(first+Triple(0, 0, dv.z), first+Triple(0, dv.y, dv.z)); // top left y axes[Y2].setTicOrientation(1,0,0); axes[Y3].setTicOrientation(1,0,0); axes[Y4].setTicOrientation (-1,0,0); // remaining z axes axes[Z2].setPosition(first, first+Triple( 0, 0, dv.z)); // front left z axes[Z4].setPosition(first+Triple(dv.x, dv.y, 0), second ); // back right z axes[Z3].setPosition(first+Triple(dv.x, 0, 0), first+Triple(dv.x, 0, dv.z)); // front right z axes[Z2].setTicOrientation(-1,0,0); axes[Z4].setTicOrientation(1,0,0); axes[Z3].setTicOrientation(1,0,0); setStyle(style_); } void CoordinateSystem::draw() { // saveGLState(); GLStateBewarer sb(GL_LINE_SMOOTH, true); if (!lineSmooth()) sb.turnOff(); if (autoDecoration()) chooseAxes(); Drawable::draw(); if( style_ == NOCOORD) return; if (majorgridlines_ || minorgridlines_) recalculateAxesTics(); if (majorgridlines_) drawMajorGridLines(); if (minorgridlines_) drawMinorGridLines(); // restoreGLState(); } //! build convex hull (6 axes: 2 x, 2 y, 2 z) and choose one of them at a time for scales, labels etc. void CoordinateSystem::chooseAxes() { vector beg(axes.size()); vector end(axes.size()); vector src(2*axes.size()); unsigned i; // collect axes viewport coordinates and initialize for (i=0; i!=axes.size(); ++i) { if (style() != NOCOORD) attach(&axes[i]); beg[i] = World2ViewPort(axes[i].begin()); end[i] = World2ViewPort(axes[i].end()); src[i] = Tuple(beg[i].x, beg[i].y); src[axes.size()+i] = Tuple(end[i].x, end[i].y); axes[i].setScaling(false); axes[i].setNumbers(false); axes[i].setLabel(false); } vector idx; convexhull2d(idx,src); int rem_x = -1; int rem_y = -1; int rem_z = -1; bool left; int choice_x = -1; int choice_y = -1; int choice_z = -1; int other_x = -1; int other_y = -1; int other_z = -1; //traverse convex hull for (unsigned k=0; k!=idx.size(); ++k) { Triple one, two; if (idx[k] >= axes.size()) // is end point one = end[idx[k]-axes.size()]; else // is begin point one = beg[idx[k]]; unsigned int next = idx[(k+1) % idx.size()]; // next point in cv (considered as ring buffer of points) if (next >= axes.size()) two = end[next-axes.size()]; else two = beg[next]; for (i=0; i!=axes.size(); ++i) { if ( (one == beg[i] && two == end[i]) || (two == beg[i] && one == end[i]) ) { if (i==X1 || i==X2 || i==X3 || i==X4) // x Achsen { if (rem_x>=0) // schon zweite Achse der konvexen Huelle ? { // untere der beiden x Achsen double y = min(min(end[rem_x].y,end[i].y),min(beg[rem_x].y,beg[i].y)); choice_x = (y == beg[i].y || y == end[i].y) ? i : rem_x; other_x = (choice_x == (int)i) ? rem_x : (int)i; left = (beg[choice_x].x < beg[other_x].x || end[choice_x].x < end[other_x].x) ? true : false; autoDecorateExposedAxis(axes[choice_x], left); rem_x = -1; } else { rem_x = i; } } else if (i==Y1 || i==Y2 || i==Y3 || i==Y4) { if (rem_y>=0) { // untere der beiden y Achsen double y = min(min(end[rem_y].y,end[i].y),min(beg[rem_y].y,beg[i].y)); choice_y = (y == beg[i].y || y == end[i].y) ? i : rem_y; other_y = (choice_y == (int)i) ? rem_y : (int)i; left = (beg[choice_y].x < beg[other_y].x || end[choice_y].x < end[other_y].x) ? true : false; autoDecorateExposedAxis(axes[choice_y], left); rem_y = -1; } else { rem_y = i; } } else if (i==Z1 || i==Z2 || i==Z3 || i==Z4) { if (rem_z>=0) { // hintere der beiden z Achsen double z = max(max(end[rem_z].z,end[i].z),max(beg[rem_z].z,beg[i].z)); choice_z = (z == beg[i].z || z == end[i].z) ? i : rem_z; other_z = (choice_z == (int)i) ? rem_z : (int)i; rem_z = -1; } else { rem_z = i; } } } } // for axes } // for idx // fit z axis in - the onthewall axis if the decorated axes build a continous line, the opposite else if (choice_x>=0 && choice_y>=0 && choice_z>=0) { left = (beg[choice_z].x < beg[other_z].x || end[choice_z].x < end[other_z].x) ? true : false; if ( axes[choice_z].begin() == axes[choice_x].begin() || axes[choice_z].begin() == axes[choice_x].end() || axes[choice_z].begin() == axes[choice_y].begin() || axes[choice_z].begin() == axes[choice_y].end() || axes[choice_z].end() == axes[choice_x].begin() || axes[choice_z].end() == axes[choice_x].end() || axes[choice_z].end() == axes[choice_y].begin() || axes[choice_z].end() == axes[choice_y].end() ) { autoDecorateExposedAxis(axes[choice_z], left); } else { autoDecorateExposedAxis(axes[other_z], !left); choice_z = other_z; // for FRAME } } if (style() == FRAME) { for (i=0; i!=axes.size(); ++i) { if ((int)i!=choice_x && (int)i!=choice_y && (int)i!=choice_z) detach(&axes[i]); } } } void CoordinateSystem::autoDecorateExposedAxis(Axis& ax, bool left) { Triple diff = World2ViewPort(ax.end()) - World2ViewPort(ax.begin()); diff = Triple(diff.x,diff.y,0); // projection double s = diff.length(); if (!s) return; ax.setScaling(true); ax.setNumbers(true); ax.setLabel(true); const double SQRT_2 = 0.7071067; double sina = fabs(diff.y / s); if (left) // leftmost (compared with antagonist in CV) axis -> draw decorations on the left side { if ( diff.x >= 0 && diff.y >= 0 && sina < SQRT_2) // 0..Pi/4 { ax.setNumberAnchor(BottomCenter); } else if ( diff.x >= 0 && diff.y >= 0 && !left) // octant 2 { ax.setNumberAnchor(CenterRight); } else if ( diff.x <= 0 && diff.y >= 0 && sina >= SQRT_2) // octant 3 { ax.setNumberAnchor(CenterRight); } else if ( diff.x <= 0 && diff.y >= 0 ) // octant 4 { ax.setNumberAnchor(TopCenter); } else if ( diff.x <= 0 && diff.y <= 0 && sina <= SQRT_2) // octant 5 { ax.setNumberAnchor(BottomCenter); } else if ( diff.x <= 0 && diff.y <= 0) // octant 6 { ax.setNumberAnchor(CenterRight); } else if ( diff.x >= 0 && diff.y <= 0 && sina >= SQRT_2) // octant 7 { ax.setNumberAnchor(CenterRight); } else if ( diff.x >= 0 && diff.y <= 0) // octant 8 { ax.setNumberAnchor(TopCenter); } } else // rightmost axis { if ( diff.x >= 0 && diff.y >= 0 && sina <= SQRT_2) { ax.setNumberAnchor(TopCenter); } else if ( diff.x >= 0 && diff.y >= 0 && !left) { ax.setNumberAnchor(CenterLeft); } else if ( diff.x <= 0 && diff.y >= 0 && sina >= SQRT_2) { ax.setNumberAnchor(CenterLeft); } else if ( diff.x <= 0 && diff.y >= 0) { ax.setNumberAnchor(BottomCenter); } else if ( diff.x <= 0 && diff.y <= 0 && sina <= SQRT_2) { ax.setNumberAnchor(TopCenter); } else if ( diff.x <= 0 && diff.y <= 0) { ax.setNumberAnchor(CenterLeft); } else if ( diff.x >= 0 && diff.y <= 0 && sina >= SQRT_2) { ax.setNumberAnchor(CenterLeft); } else if ( diff.x >= 0 && diff.y <= 0) { ax.setNumberAnchor(BottomCenter); } } } void CoordinateSystem::setPosition(Triple first, Triple second) { first_ = first; second_ = second; } void CoordinateSystem::setTicLength(double major, double minor) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setTicLength(major, minor); } void CoordinateSystem::adjustNumbers(int val) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].adjustNumbers(val); } void CoordinateSystem::adjustLabels(int val) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].adjustLabel(val); } void CoordinateSystem::setAutoScale(bool val) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setAutoScale(val); } void CoordinateSystem::setAxesColor(RGBA val) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setColor(val); } void CoordinateSystem::recalculateAxesTics() { for (unsigned i=0; i!=axes.size(); ++i) axes[i].recalculateTics(); } void CoordinateSystem::setNumberFont(QString const& family, int pointSize, int weight, bool italic) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setNumberFont(family,pointSize,weight,italic); } void CoordinateSystem::setNumberFont(QFont const& font) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setNumberFont(font); } void CoordinateSystem::setNumberColor(RGBA val) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setNumberColor( val); } void CoordinateSystem::setStandardScale() { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setScale(LINEARSCALE); } void CoordinateSystem::setLabelFont(QFont const& font) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setLabelFont(font); } void CoordinateSystem::setLabelFont(QString const& family, int pointSize, int weight, bool italic) { setLabelFont(QFont(family,pointSize,weight,italic)); } void CoordinateSystem::setLabelColor(RGBA val) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setLabelColor(val); } void CoordinateSystem::setLineWidth(double val, double majfac, double minfac) { for (unsigned i=0; i!=axes.size(); ++i) axes[i].setLineWidth(val, majfac, minfac); } void CoordinateSystem::setStyle(COORDSTYLE s, AXIS frame_1, AXIS frame_2, AXIS frame_3) { style_ = s; switch (s) { case NOCOORD: { for (unsigned i=0; i!=axes.size(); ++i) detach (&axes[i]); } break; case BOX: { for (unsigned i=0; i!=axes.size(); ++i) attach (&axes[i]); } break; case FRAME: { for (unsigned i=0; i!=axes.size(); ++i) detach (&axes[i]); if (!autoDecoration()) { attach(&axes[frame_1]); attach(&axes[frame_2]); attach(&axes[frame_3]); } } break; default: break; } } /** The axis used for tic calculation is chosen randomly from the respective pair. For most cases an identical tic distribution is therefore recommended. \param majors Draw grid between major tics \param minors Draw grid between minor tics \param sides Side(s), where the grid should be drawn */ void CoordinateSystem::setGridLines(bool majors, bool minors, int sides) { sides_ = sides; majorgridlines_ = majors; minorgridlines_ = minors; } void CoordinateSystem::drawMajorGridLines() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4d(gridlinecolor_.r,gridlinecolor_.g,gridlinecolor_.b,gridlinecolor_.a); setDeviceLineWidth(axes[X1].majLineWidth()); glBegin( GL_LINES ); if (sides_ & Qwt3D::FLOOR) { drawMajorGridLines(axes[X1],axes[X4]); drawMajorGridLines(axes[Y1],axes[Y2]); } if (sides_ & Qwt3D::CEIL) { drawMajorGridLines(axes[X2],axes[X3]); drawMajorGridLines(axes[Y3],axes[Y4]); } if (sides_ & Qwt3D::LEFT) { drawMajorGridLines(axes[Y1],axes[Y4]); drawMajorGridLines(axes[Z1],axes[Z2]); } if (sides_ & Qwt3D::RIGHT) { drawMajorGridLines(axes[Y2],axes[Y3]); drawMajorGridLines(axes[Z3],axes[Z4]); } if (sides_ & Qwt3D::FRONT) { drawMajorGridLines(axes[X1],axes[X2]); drawMajorGridLines(axes[Z2],axes[Z3]); } if (sides_ & Qwt3D::BACK) { drawMajorGridLines(axes[X3],axes[X4]); drawMajorGridLines(axes[Z4],axes[Z1]); } glEnd(); } void CoordinateSystem::drawMinorGridLines() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4d(gridlinecolor_.r,gridlinecolor_.g,gridlinecolor_.b,gridlinecolor_.a); setDeviceLineWidth(axes[X1].minLineWidth()); glBegin( GL_LINES ); if (sides_ & Qwt3D::FLOOR) { drawMinorGridLines(axes[X1],axes[X4]); drawMinorGridLines(axes[Y1],axes[Y2]); } if (sides_ & Qwt3D::CEIL) { drawMinorGridLines(axes[X2],axes[X3]); drawMinorGridLines(axes[Y3],axes[Y4]); } if (sides_ & Qwt3D::LEFT) { drawMinorGridLines(axes[Y1],axes[Y4]); drawMinorGridLines(axes[Z1],axes[Z2]); } if (sides_ & Qwt3D::RIGHT) { drawMinorGridLines(axes[Y2],axes[Y3]); drawMinorGridLines(axes[Z3],axes[Z4]); } if (sides_ & Qwt3D::FRONT) { drawMinorGridLines(axes[X1],axes[X2]); drawMinorGridLines(axes[Z2],axes[Z3]); } if (sides_ & Qwt3D::BACK) { drawMinorGridLines(axes[X3],axes[X4]); drawMinorGridLines(axes[Z4],axes[Z1]); } glEnd(); } void CoordinateSystem::drawMajorGridLines(Axis& a0, Axis& a1) { Triple d = a1.begin()-a0.begin(); for (unsigned int i=0; i!=a0.majorPositions().size(); ++i) { glVertex3d( a0.majorPositions()[i].x, a0.majorPositions()[i].y, a0.majorPositions()[i].z ); glVertex3d( a0.majorPositions()[i].x + d.x, a0.majorPositions()[i].y + d.y, a0.majorPositions()[i].z +d.z); } } void CoordinateSystem::drawMinorGridLines(Axis& a0, Axis& a1) { Triple d = a1.begin()-a0.begin(); for (unsigned int i=0; i!=a0.minorPositions().size(); ++i) { glVertex3d( a0.minorPositions()[i].x, a0.minorPositions()[i].y, a0.minorPositions()[i].z ); glVertex3d( a0.minorPositions()[i].x + d.x, a0.minorPositions()[i].y + d.y, a0.minorPositions()[i].z +d.z); } }