From b85a292ce06475d560bfa1195b63a8bfe211f22d Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Wed, 11 Jul 2012 14:15:27 -0500 Subject: Add 0.2.7 release of qwtplot3d for future TQt3 conversion and use --- lib/tqwtplot3d/src/qwt3d_autoscaler.cpp | 253 ++++++++++ lib/tqwtplot3d/src/qwt3d_axis.cpp | 386 +++++++++++++++ lib/tqwtplot3d/src/qwt3d_color.cpp | 63 +++ lib/tqwtplot3d/src/qwt3d_colorlegend.cpp | 223 +++++++++ lib/tqwtplot3d/src/qwt3d_coordsys.cpp | 633 +++++++++++++++++++++++++ lib/tqwtplot3d/src/qwt3d_dataviews.cpp | 10 + lib/tqwtplot3d/src/qwt3d_drawable.cpp | 140 ++++++ lib/tqwtplot3d/src/qwt3d_enrichment_std.cpp | 347 ++++++++++++++ lib/tqwtplot3d/src/qwt3d_function.cpp | 101 ++++ lib/tqwtplot3d/src/qwt3d_gridmapping.cpp | 32 ++ lib/tqwtplot3d/src/qwt3d_gridplot.cpp | 596 +++++++++++++++++++++++ lib/tqwtplot3d/src/qwt3d_io.cpp | 355 ++++++++++++++ lib/tqwtplot3d/src/qwt3d_io_gl2ps.cpp | 387 +++++++++++++++ lib/tqwtplot3d/src/qwt3d_io_reader.cpp | 225 +++++++++ lib/tqwtplot3d/src/qwt3d_label.cpp | 262 ++++++++++ lib/tqwtplot3d/src/qwt3d_lighting.cpp | 192 ++++++++ lib/tqwtplot3d/src/qwt3d_meshplot.cpp | 320 +++++++++++++ lib/tqwtplot3d/src/qwt3d_mousekeyboard.cpp | 387 +++++++++++++++ lib/tqwtplot3d/src/qwt3d_movements.cpp | 106 +++++ lib/tqwtplot3d/src/qwt3d_parametricsurface.cpp | 104 ++++ lib/tqwtplot3d/src/qwt3d_plot.cpp | 498 +++++++++++++++++++ lib/tqwtplot3d/src/qwt3d_scale.cpp | 304 ++++++++++++ lib/tqwtplot3d/src/qwt3d_surfaceplot.cpp | 183 +++++++ lib/tqwtplot3d/src/qwt3d_types.cpp | 222 +++++++++ 24 files changed, 6329 insertions(+) create mode 100644 lib/tqwtplot3d/src/qwt3d_autoscaler.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_axis.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_color.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_colorlegend.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_coordsys.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_dataviews.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_drawable.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_enrichment_std.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_function.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_gridmapping.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_gridplot.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_io.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_io_gl2ps.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_io_reader.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_label.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_lighting.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_meshplot.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_mousekeyboard.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_movements.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_parametricsurface.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_plot.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_scale.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_surfaceplot.cpp create mode 100644 lib/tqwtplot3d/src/qwt3d_types.cpp (limited to 'lib/tqwtplot3d/src') diff --git a/lib/tqwtplot3d/src/qwt3d_autoscaler.cpp b/lib/tqwtplot3d/src/qwt3d_autoscaler.cpp new file mode 100644 index 0000000..6e785cf --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_autoscaler.cpp @@ -0,0 +1,253 @@ +#include "qwt3d_helper.h" +#include "qwt3d_autoscaler.h" + +using namespace Qwt3D; + +namespace +{ + +double floorExt( int& exponent, double x, std::vector& sortedmantissi) +{ + if (x == 0.0) + { + exponent = 0; + return 0.0; + } + + double sign = (x > 0) ? 1.0 : -1.0; + double lx = log10(fabs(x)); + exponent = (int)floor(lx); + + double fr = pow(10.0, lx - exponent); + if (fr >= 10.0) + { + fr = 1.0; + ++exponent; + } + else + { + for (int i=(int)sortedmantissi.size()-1; i>=0;--i) + { + if (fr>=sortedmantissi[i]) + { + fr = sortedmantissi[i]; + break; + } + } + } + return sign * fr; +} + +/* + \brief Find the largest value out of {1,2,5}*10^n with an integer number n + which is smaller than or equal to x + \param exponent n + \param x Input value + \return Mantissa +*/ +double floor125( int& exponent, double x) +{ + std::vector m(2); + m[0] = 1; + m[1] = 2; + m[2] = 5; + return floorExt(exponent, x, m); +} + +} // anon ns + + +//! Initializes with an {1,2,5} sequence of mantissas +LinearAutoScaler::LinearAutoScaler() +{ + init(0,1,1); + mantissi_ = std::vector(3); + mantissi_[0] = 1; + mantissi_[1] = 2; + mantissi_[2] = 5; +} +//! Initialize with interval [0,1] and one requested interval +/*! +val mantisse A increasing ordered vector of values representing +mantisse values between 1 and 9. +*/ +LinearAutoScaler::LinearAutoScaler(std::vector& mantisse) +{ + init(0,1,1); + if (mantisse.empty()) + { + mantissi_ = std::vector(3); + mantissi_[0] = 1; + mantissi_[1] = 2; + mantissi_[2] = 5; + return; + } + mantissi_ = mantisse; +} + + +//! Initialize with interval [start,stop] and number of requested intervals +/** + Switchs start and stop, if stop < start and sets intervals = 1 if ivals < 1 +*/ +void LinearAutoScaler::init(double start, double stop, int ivals) +{ + start_ = start; + stop_ = stop; + intervals_ = ivals; + + if (start_ > stop_) + { + double tmp = start_; + start_ = stop_; + stop_ = tmp; + } + if (intervals_ < 1) + intervals_ = 1; +} + +/*! +\return Anchor value + +\verbatim +|_______|____________ _ _ _ _ _____|_____________|________________ + +0 m*10^n start anchor := c*m*10^n + +c 'minimal' (anchor-start < m*10^n) +\endverbatim +*/ +double LinearAutoScaler::anchorvalue(double start, double m, int n) +{ + double stepval = m * pow(10.0, n); + return stepval * ceil(start / stepval); +} + +/*! +\return New number of intervals (:= l_intervals + r_intervals) +\param l_intervals Number of intervals left from anchor +\param r_intervals Number of intervals right from anchor + +\verbatim + -l_intervals * i -2 * i -i +r_intervals * i + | +|______|_______ _ _ _ ____|____|___ _ _ _ _ _ _ _|_______|_______|_ _ _ _ _ _ _____|__|_____ + | | | | +0 i := m*10^n start anchor stop + +c 'minimal' (anchor-start < m*10^n) +\endverbatim +*/ +int LinearAutoScaler::segments(int& l_intervals, int& r_intervals, double start, double stop, double anchor, double m, int n) +{ + double val = m * pow(10.0, n); + double delta = (stop - anchor) / val; + + r_intervals = (int)floor(delta); // right side intervals + + delta = (anchor - start) / val; + + l_intervals = (int)floor(delta); // left side intervals + + return r_intervals + l_intervals; +} + + +/*! + \brief Does the actual scaling + \return Number of intervals after rescaling. This will in the most cases differ + from the requested interval number! Always >0. + \param a Start value after scaling (always >= start) + \param b Stop value after scaling (always <= stop) + \param start Start value + \param stop Stop value + \param ivals Requested intervals + \return Number of intervals after autoscaling + + If the given interval has zero length the function returns the current + interval number and a and b remain unchanged. +*/ +int LinearAutoScaler::execute(double& a, double& b, double start, double stop, int ivals) +{ + init(start,stop,ivals); + + double delta = stop_ - start_; + + if (isPracticallyZero(delta)) + return intervals_; + + double c; + int n; + + c = floorExt(n, delta, mantissi_); + + int l_ival, r_ival; + + double anchor = anchorvalue(start_, c, n); + int ival = segments(l_ival, r_ival, start_, stop_, anchor, c, n); + + if (ival >= intervals_) + { + a = anchor - l_ival * c * pow(10.0,n); + b = anchor + r_ival * c * pow(10.0,n); + intervals_ = ival; + return intervals_; + } + + int prev_ival, prev_l_ival, prev_r_ival; + double prev_anchor; + double prev_c; + int prev_n; + + while(1) + { + prev_c = c; + prev_n = n; + prev_anchor = anchor; + prev_ival = ival; + prev_l_ival = l_ival; + prev_r_ival = r_ival; + + + if (int(c) == 1) + { + c = mantissi_.back(); + --n; + } + else + { + for (unsigned int i=mantissi_.size()-1; i>0; --i) + { + if (int(c) == mantissi_[i]) + { + c = mantissi_[i-1]; + break; + } + } + } + + anchor = anchorvalue(start_, c, n); + ival = segments(l_ival, r_ival, start_, stop_, anchor, c, n); + + int prev_diff = intervals_ - prev_ival; + int actual_diff = ival - intervals_; + + if (prev_diff >= 0 && actual_diff >= 0) + { + if (prev_diff < actual_diff) + { + c = prev_c; + n = prev_n; + anchor = prev_anchor; + ival = prev_ival; + l_ival = prev_l_ival; + r_ival = prev_r_ival; + } + a = anchor - l_ival * c * pow(10.0,n); + b = anchor + r_ival * c * pow(10.0,n); + intervals_ = ival; + break; + } + } + return intervals_; +} diff --git a/lib/tqwtplot3d/src/qwt3d_axis.cpp b/lib/tqwtplot3d/src/qwt3d_axis.cpp new file mode 100644 index 0000000..fe45f37 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_axis.cpp @@ -0,0 +1,386 @@ +#include "qwt3d_axis.h" + +using namespace Qwt3D; + +Axis::Axis() +{ + init(); +}; + +Axis::~Axis() +{ +} + +Axis::Axis(Triple beg, Triple end) +{ + init(); + setPosition(beg,end); +} + +void Axis::init() +{ + detachAll(); + + scale_ = qwt3d_ptr(new LinearScale); + + beg_ = Triple(0.0, 0.0, 0.0); + end_ = beg_; + + majorintervals_ = 0; + minorintervals_ = 0; + setMajors(1); + setMinors(1); + setLimits(0,0); + + setTicOrientation(0.0, 0.0, 0.0); + setTicLength(0.0, 0.0); + setColor(0.0, 0.0, 0.0); + setLineWidth(1.0); + symtics_ = false; + drawNumbers_ = false; + drawLabel_ = false; + + drawTics_ = false; + autoscale_ = true; + markerLabel_.clear(); + numberfont_ = QFont("Courier",12); + setLabelFont(QFont("Courier",14)); + + numbercolor_ = RGBA(0,0,0,0); + + setNumberAnchor(Center); + + numbergap_ = 0; + labelgap_ = 0; +} + +void Axis::setPosition(const Triple& beg, const Triple& end) +{ + beg_ = beg; + end_ = end; +} + +void Axis::setMajors(int val) +{ + if (val == majorintervals_) + return; + + majorintervals_ = (val<=0) ? 1 : val; // always >= 1 +} + +/*! +\see LogScale::setMinors(). +*/ +void Axis::setMinors(int val) +{ + if (val == minorintervals_) + return; + + minorintervals_ = (val<=0) ? 1 : val; // always >= 1 +} + +void Axis::setTicLength(double majorl, double minorl) +{ + lmaj_ = majorl; + lmin_ = minorl; +} + +void Axis::setTicOrientation(double tx, double ty, double tz) +{ + setTicOrientation(Triple(tx,ty,tz)); +} + +void Axis::setTicOrientation(const Triple& val) +{ + orientation_ = val; + orientation_.normalize(); +} + +/** +\param val thickness for axis base line +\param majfac relative thickness for axis major tics (majfac*val) +\param minfac relative thickness for axis minor tics (minfac*val) +*/ +void Axis::setLineWidth(double val, double majfac, double minfac) +{ + lineWidth_ = val; + majLineWidth_ = majfac * lineWidth_; + minLineWidth_ = minfac * lineWidth_; +} + +void Axis::draw() +{ + Drawable::draw(); + + saveGLState(); + +// GLStateBewarer sb(GL_LINE_SMOOTH, true); +// glBlendFunc(GL_ONE, GL_ZERO); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4d(color.r,color.g,color.b,color.a); + + drawBase(); + drawTics(); + drawLabel(); + + restoreGLState(); +} + +/** +Always use AFTER drawNumbers() ! (Needs length of number string) +*/ +void Axis::drawLabel() +{ + if (!drawLabel_) + return; + + Triple diff = end() - begin(); + Triple center = begin() + diff/2; + + Triple bnumber = biggestNumberString(); +// double fac = 6*(second()-first()).length() / 100; + + switch (scaleNumberAnchor_) + { + case BottomLeft: + case TopLeft: + case CenterLeft: + bnumber.y = 0; + break; + case BottomRight: + case TopRight: + case CenterRight: + bnumber.x = -bnumber.x; + bnumber.y = 0; + break; + case TopCenter: + bnumber.x = 0; + bnumber.y = -bnumber.y; + break; + case BottomCenter: + bnumber.x = 0; + break; + default: + break; + } + + Triple pos = ViewPort2World(World2ViewPort(center + ticOrientation() * lmaj_) + bnumber); + setLabelPosition(pos, scaleNumberAnchor_); + + label_.adjust(labelgap_); + label_.draw(); +} + +void Axis::drawBase() +{ + setDeviceLineWidth( lineWidth_ ); + glBegin( GL_LINES ); + glVertex3d( beg_.x, beg_.y, beg_.z); + glVertex3d( end_.x, end_.y, end_.z); + glEnd(); +} + +bool Axis::prepTicCalculation(Triple& startpoint) +{ + if (isPracticallyZero(start_, stop_)) + return false; + + autostart_ = start_; + autostop_ = stop_; + + if (autoScale()) + { + setMajors(scale_->autoscale(autostart_, autostop_, start_, stop_, majors())); + if (isPracticallyZero(autostart_, autostop_)) + return false; + } + + scale_->setLimits(start_,stop_); + scale_->setMajors(majors()); + scale_->setMinors(minors()); + scale_->setMajorLimits(autostart_,autostop_); + scale_->calculate(); + + Triple normal = (end_ - beg_); + //normal.normalize(); + Triple beg = beg_ + ((autostart_ - start_) / (stop_ - start_)) * normal; + Triple end = end_ - ((stop_ - autostop_) / (stop_ - start_))* normal; + + startpoint = end_ - beg_; + + majorpos_.clear(); + minorpos_.clear(); + + return true; +} + +void Axis::recalculateTics() +{ + Triple runningpoint; + if (false==prepTicCalculation(runningpoint)) + return; + + unsigned int i; + + for (i = 0; i != scale_->majors_p.size(); ++i) + { + double t = (scale_->majors_p[i] - start_) / (stop_-start_); + majorpos_.push_back(beg_ + t * runningpoint); + } + for (i = 0; i != scale_->minors_p.size(); ++i) + { + double t = (scale_->minors_p[i] - start_) / (stop_-start_); + minorpos_.push_back(beg_ + t * runningpoint); + } +} + +void Axis::drawTics() +{ + Triple runningpoint; + if (!drawTics_ || false==prepTicCalculation(runningpoint)) + return; + + unsigned int i; + Triple nadir; + + markerLabel_.resize(scale_->majors_p.size()); + setDeviceLineWidth(majLineWidth_); + for (i = 0; i != scale_->majors_p.size(); ++i) + { + double t = (scale_->majors_p[i] - start_) / (stop_-start_); + nadir = beg_ + t * runningpoint; + majorpos_.push_back(drawTic(nadir, lmaj_)); + drawTicLabel(nadir + 1.2 * lmaj_ * orientation_, i); + } + setDeviceLineWidth(minLineWidth_); + for (i = 0; i != scale_->minors_p.size(); ++i) + { + double t = (scale_->minors_p[i] - start_) / (stop_-start_); + nadir = beg_ + t * runningpoint; + minorpos_.push_back(drawTic(nadir, lmin_)); + } +} + +void Axis::drawTicLabel(Triple pos, int mtic) +{ + if (!drawNumbers_ || (mtic < 0)) + return; + + markerLabel_[mtic].setFont(numberfont_.family(), numberfont_.pointSize(), numberfont_.weight(), numberfont_.italic()); + markerLabel_[mtic].setColor(numbercolor_); + markerLabel_[mtic].setString(scale_->ticLabel(mtic)); + markerLabel_[mtic].setPosition(pos, scaleNumberAnchor_); + markerLabel_[mtic].adjust(numbergap_); + markerLabel_[mtic].draw(); +} + +Triple Axis::drawTic(Triple nadir, double length) +{ + double ilength = (symtics_) ? -length : 0.0; + + glBegin( GL_LINES ); + glVertex3d( nadir.x + ilength * orientation_.x, + nadir.y + ilength * orientation_.y, + nadir.z + ilength * orientation_.z) ; + glVertex3d( nadir.x + length * orientation_.x, + nadir.y + length * orientation_.y, + nadir.z + length * orientation_.z); + glEnd(); + return nadir; +} + +void Axis::setNumberFont(QString const& family, int pointSize, int weight, bool italic) +{ + numberfont_ = QFont(family, pointSize, weight, italic ); +} + +void Axis::setNumberFont(QFont const& font) +{ + numberfont_ = font; +} + +void Axis::setNumberColor(RGBA col) +{ + numbercolor_ = col; +} + +void Axis::setLabelFont(QString const& family, int pointSize, int weight, bool italic) +{ + labelfont_ = QFont(family, pointSize, weight, italic ); + label_.setFont(family, pointSize, weight, italic); +} + +void Axis::setLabelFont(QFont const& font) +{ + setLabelFont(font.family(), font.pointSize(), font.weight(), font.italic()); +} + +void Axis::setLabelString(QString const& name) +{ + label_.setString(name); +} + +/*! + Sets label position in conjunction with an anchoring strategy +*/ +void Axis::setLabelPosition(const Triple& pos,Qwt3D::ANCHOR an) +{ + label_.setPosition(pos, an); +} + +//! Sets color for label +void Axis::setLabelColor(RGBA col) +{ + label_.setColor(col); +} + +Triple Axis::biggestNumberString() +{ + Triple ret; + unsigned size = markerLabel_.size(); + + double width, height; + + for (unsigned i=0; i!=size; ++i) + { + width = fabs( (World2ViewPort(markerLabel_[i].second())-World2ViewPort(markerLabel_[i].first())).x ); + height = fabs( (World2ViewPort(markerLabel_[i].second())-World2ViewPort(markerLabel_[i].first())).y ); + + if (width > ret.x) + ret.x = width + markerLabel_[i].gap(); + if (height > ret.y) + ret.y = height + markerLabel_[i].gap();; + } + return ret; +} + +/*! + This variant sets a user-defined scale object. + Use with a heap based initialized pointer only. + The axis adopts ownership. +*/ +void Axis::setScale(Scale* val) +{ + scale_ = qwt3d_ptr(val); +} + +/*! + Sets one of the predefined scaling types. + \warning Too small intervals in logarithmic scales lead to + empty scales (or perhaps a scale only containing an isolated + major tic). Better switch to linear scales in such cases. +*/ +void Axis::setScale(Qwt3D::SCALETYPE val) +{ + switch(val) { + case Qwt3D::LINEARSCALE: + setScale(new LinearScale); + break; + case Qwt3D::LOG10SCALE: + setScale(new LogScale); + setMinors(9); + break; + default: + break; + } +} diff --git a/lib/tqwtplot3d/src/qwt3d_color.cpp b/lib/tqwtplot3d/src/qwt3d_color.cpp new file mode 100644 index 0000000..5543a8a --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_color.cpp @@ -0,0 +1,63 @@ +#include "qwt3d_color.h" +#include "qwt3d_plot.h" + +using namespace Qwt3D; + +StandardColor::StandardColor(Plot3D* data, unsigned size) + : data_(data) +{ + Q_ASSERT(data_); + + reset(size); +} + +void StandardColor::reset(unsigned size) +{ + colors_ = ColorVector(size); + RGBA elem; + + double dsize = size; + + for (unsigned int i=0; i!=size; ++i) + { + elem.r = i / dsize; + elem.g = i / dsize / 4; + elem.b = 1 - i/dsize; + elem.a = 1.0; + colors_[i] = elem; + } +} + +/** + Assigns a new ColorVector (Also overwrites the constructors size argument) +*/ +void StandardColor::setColorVector(ColorVector const& cv) +{ + colors_ = cv; +} + +void StandardColor::setAlpha(double a) +{ + if (a<0 || a>1) + return; + + RGBA elem; + + for (unsigned int i=0; i!=colors_.size(); ++i) + { + elem = colors_[i]; + elem.a = a; + colors_[i] = elem; + } +} + +RGBA StandardColor::operator()(double, double, double z) const +{ + Q_ASSERT(data_); + int index = (int)((colors_.size()-1) * (z - data_->hull().minVertex.z) / (data_->hull().maxVertex.z-data_->hull().minVertex.z)); + if (index < 0) + index = 0; + if ((unsigned int)index > colors_.size() - 1) + index = (int)(colors_.size() - 1); + return colors_[index]; +} diff --git a/lib/tqwtplot3d/src/qwt3d_colorlegend.cpp b/lib/tqwtplot3d/src/qwt3d_colorlegend.cpp new file mode 100644 index 0000000..2b114aa --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_colorlegend.cpp @@ -0,0 +1,223 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#endif + +#include "qwt3d_colorlegend.h" + +using namespace Qwt3D; + +/** +Contructs a legend object with an axis at the left +side. The legend resides in the top-right area +and has no caption. Scale numbering is shown. +*/ +ColorLegend::ColorLegend() +{ + axis_.setNumbers(true); + axis_.setScaling(true); + axis_.setNumberColor(RGBA(0,0,0,1)); + axis_.setNumberAnchor(CenterRight); + axis_.setNumberFont(QFont("Courier",8)); + + caption_.setFont("Courier", 10, QFont::Bold); + caption_.setColor(RGBA(0,0,0,1)); + axisposition_ = ColorLegend::Left; + orientation_ = ColorLegend::BottomTop; + showaxis_ = true; + setRelPosition(Tuple(0.94, 1-0.36),Tuple(0.97, 1-0.04)); +} + +void ColorLegend::setTitleString(QString const& s) +{ + caption_.setString(s); +} + +void ColorLegend::setTitleFont(QString const& family, int pointSize, int weight, bool italic) +{ + caption_.setFont(family, pointSize, weight, italic); +} + +void ColorLegend::setLimits(double start, double stop) +{ + axis_.setLimits(start, stop); +} + +void ColorLegend::setMajors(int majors) +{ + axis_.setMajors(majors); +} + +void ColorLegend::setMinors(int minors) +{ + axis_.setMinors(minors); +} + +void ColorLegend::setAutoScale(bool val) +{ + axis_.setAutoScale(val); +} + +void ColorLegend::setScale(SCALETYPE val) +{ + axis_.setScale(val); +} + +void ColorLegend::setScale(Scale* val) +{ + axis_.setScale(val); +} + + +void ColorLegend::setOrientation(ORIENTATION orientation, SCALEPOSITION pos) +{ + orientation_ = orientation; + axisposition_ = pos; + + if (orientation_==BottomTop) + { + if (axisposition_ == Bottom || axisposition_ == Top) + axisposition_ = Left; + } + else + { + if (axisposition_ == Left || axisposition_ == Right) + axisposition_ = Bottom; + } +} + +void ColorLegend::setRelPosition(Tuple relMin, Tuple relMax) +{ + relMin_ = relMin; + relMax_ = relMax; +} + +void ColorLegend::setGeometryInternal() +{ + double ot = .99; + + getMatrices(modelMatrix, projMatrix, viewport); + pe_.minVertex = relativePosition(Triple(relMin_.x, relMin_.y, ot)); + pe_.maxVertex = relativePosition(Triple(relMax_.x, relMax_.y, ot)); + + double diff = 0; + Triple b; + Triple e; + + switch (axisposition_) + { + case ColorLegend::Left: + b = pe_.minVertex; + e = pe_.maxVertex; e.x = b.x; + axis_.setTicOrientation(-1,0,0); + axis_.setNumberAnchor(CenterRight); + diff = pe_.maxVertex.x - pe_.minVertex.x; + break; + case ColorLegend::Right: + e = pe_.maxVertex; + b = pe_.minVertex; b.x = e.x; + axis_.setTicOrientation(+1,0,0); + axis_.setNumberAnchor(CenterLeft); + diff = pe_.maxVertex.x - pe_.minVertex.x; + break; + case ColorLegend::Top: + e = pe_.maxVertex; + b = pe_.minVertex; b.z = e.z; + axis_.setTicOrientation(0,0,+1); + axis_.setNumberAnchor(BottomCenter); + diff = pe_.maxVertex.z - pe_.minVertex.z; + break; + case ColorLegend::Bottom: + b = pe_.minVertex; + e = pe_.maxVertex; e.z = b.z; + axis_.setTicOrientation(0,0,-1); + axis_.setNumberAnchor(TopCenter); + diff = pe_.maxVertex.z - pe_.minVertex.z; + break; + default: + break; + } + + axis_.setPosition(b,e); + diff /= 10; + + axis_.setTicLength(diff, 0.6*diff); + + Triple c; + c.x = pe_.minVertex.x + ((pe_.maxVertex-pe_.minVertex) / 2).x; + c.z = pe_.maxVertex.z; + c.z += (pe_.maxVertex.z-pe_.minVertex.z)/20; + c.y = pe_.maxVertex.y; + + caption_.setPosition(c, BottomCenter); +} + +void ColorLegend::draw() +{ + if (colors.empty()) + return; + + setGeometryInternal(); + + saveGLState(); + + Triple one = pe_.minVertex; + Triple two = pe_.maxVertex; + + double h = (orientation_ == ColorLegend::BottomTop) + ? (two-one).z / colors.size() + : (two-one).x / colors.size(); + + //glEnable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + GLStateBewarer(GL_POLYGON_OFFSET_FILL,true); +// glPolygonOffset(0.0, 0.0); + + glColor4d(0, 0, 0, 1); + glBegin(GL_LINE_LOOP); + glVertex3d(one.x, one.y, one.z); + glVertex3d(one.x, one.y, two.z); + glVertex3d(two.x, one.y, two.z); + glVertex3d(two.x, one.y, one.z); + glEnd(); + + + unsigned size = colors.size(); + RGBA rgb; + + if (orientation_ == ColorLegend::BottomTop) + { + for (unsigned i=1; i<=size; ++i) + { + rgb = colors[i-1]; + glColor4d(rgb.r,rgb.g,rgb.b,rgb.a); + glBegin( GL_POLYGON ); + glVertex3d( one.x, one.y, one.z+(i-1)*h ); + glVertex3d( one.x, one.y, one.z+i*h ); + glVertex3d( two.x, one.y, one.z+i*h ); + glVertex3d( two.x, one.y, one.z+(i-1)*h ); + glEnd(); + } + } + else + { + for (unsigned i=1; i<=size; ++i) + { + rgb = colors[i-1]; + glColor4d(rgb.r,rgb.g,rgb.b,rgb.a); + glBegin( GL_POLYGON ); + glVertex3d( one.x+(i-1)*h, one.y, one.z ); + glVertex3d( one.x+i*h, one.y, one.z ); + glVertex3d( one.x+i*h, one.y, two.z ); + glVertex3d( one.x+(i-1)*h, one.y, two.z ); + glEnd(); + } + } + + restoreGLState(); + + if (showaxis_) + axis_.draw(); + + caption_.draw(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_coordsys.cpp b/lib/tqwtplot3d/src/qwt3d_coordsys.cpp new file mode 100644 index 0000000..2aae28d --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_coordsys.cpp @@ -0,0 +1,633 @@ +#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); + } +} diff --git a/lib/tqwtplot3d/src/qwt3d_dataviews.cpp b/lib/tqwtplot3d/src/qwt3d_dataviews.cpp new file mode 100644 index 0000000..7a022dd --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_dataviews.cpp @@ -0,0 +1,10 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include "qwt3d_plot.h" + +using namespace std; +using namespace Qwt3D; + diff --git a/lib/tqwtplot3d/src/qwt3d_drawable.cpp b/lib/tqwtplot3d/src/qwt3d_drawable.cpp new file mode 100644 index 0000000..4025817 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_drawable.cpp @@ -0,0 +1,140 @@ +#include "qwt3d_drawable.h" + +using namespace Qwt3D; + +Drawable::~Drawable() +{ + detachAll(); +} + +void Drawable::saveGLState() +{ + glGetBooleanv(GL_LINE_SMOOTH, &ls); + glGetBooleanv(GL_POLYGON_SMOOTH, &pols); + glGetFloatv(GL_LINE_WIDTH, &lw); + glGetIntegerv(GL_BLEND_SRC, &blsrc); + glGetIntegerv(GL_BLEND_DST, &bldst); + glGetDoublev(GL_CURRENT_COLOR, col); + glGetIntegerv(GL_LINE_STIPPLE_PATTERN, &pattern); + glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &factor); + glGetBooleanv(GL_LINE_STIPPLE, &sallowed); + glGetBooleanv(GL_TEXTURE_2D, &tex2d); + glGetIntegerv(GL_POLYGON_MODE, polmode); + glGetIntegerv(GL_MATRIX_MODE, &matrixmode); + glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &poloffs[0]); + glGetFloatv(GL_POLYGON_OFFSET_UNITS, &poloffs[1]); + glGetBooleanv(GL_POLYGON_OFFSET_FILL, &poloffsfill); +} + +void Drawable::restoreGLState() +{ + Enable(GL_LINE_SMOOTH, ls); + Enable(GL_POLYGON_SMOOTH, pols); + + setDeviceLineWidth(lw); + glBlendFunc(blsrc, bldst); + glColor4dv(col); + + glLineStipple(factor,pattern); + Enable(GL_LINE_STIPPLE,sallowed); + Enable(GL_TEXTURE_2D,tex2d); + glPolygonMode(polmode[0], polmode[1]); + glMatrixMode(matrixmode); + glPolygonOffset(poloffs[0], poloffs[1]); + setDevicePolygonOffset(poloffs[0], poloffs[1]); + + Enable(GL_POLYGON_OFFSET_FILL, poloffsfill); +} + +void Drawable::Enable(GLenum what, GLboolean val) +{ + if (val) + glEnable(what); + else + glDisable(what); +} + +void Drawable::attach(Drawable* dr) +{ + if ( dlist.end() == std::find( dlist.begin(), dlist.end(), dr ) ) + if (dr) + { + dlist.push_back(dr); + } +} + +void Drawable::detach(Drawable* dr) +{ + std::list::iterator it = std::find(dlist.begin(), dlist.end(), dr); + + if ( it != dlist.end() ) + { + dlist.erase(it); + } +} +void Drawable::detachAll() +{ + dlist.clear(); +} + + +//! simplified glut routine (glUnProject): windows coordinates_p --> object coordinates_p +/** + Don't rely on (use) this in display lists ! +*/ +Triple Drawable::ViewPort2World(Triple win, bool* err) +{ + Triple obj; + + getMatrices(modelMatrix, projMatrix, viewport); + int res = gluUnProject(win.x, win.y, win.z, modelMatrix, projMatrix, viewport, &obj.x, &obj.y, &obj.z); + + if (err) + *err = (res) ? false : true; + return obj; +} + +//! simplified glut routine (glProject): object coordinates_p --> windows coordinates_p +/** + Don't rely on (use) this in display lists ! +*/ +Triple Drawable::World2ViewPort(Triple obj, bool* err) +{ + Triple win; + + getMatrices(modelMatrix, projMatrix, viewport); + int res = gluProject(obj.x, obj.y, obj.z, modelMatrix, projMatrix, viewport, &win.x, &win.y, &win.z); + + if (err) + *err = (res) ? false : true; + return win; +} + +/** + Don't rely on (use) this in display lists ! +*/ +Triple Drawable::relativePosition(Triple rel) +{ + return ViewPort2World(Triple((rel.x-viewport[0])*viewport[2],(rel.y-viewport[1])*viewport[3],rel.z)); +} + +void Drawable::draw() +{ + saveGLState(); + + for (std::list::iterator it = dlist.begin(); it!=dlist.end(); ++it) + { + (*it)->draw(); + } + restoreGLState(); +} + +void Drawable::setColor(double r, double g, double b, double a) +{ + color = RGBA(r,g,b,a); +} + +void Drawable::setColor(RGBA rgba) +{ + color = rgba; +} diff --git a/lib/tqwtplot3d/src/qwt3d_enrichment_std.cpp b/lib/tqwtplot3d/src/qwt3d_enrichment_std.cpp new file mode 100644 index 0000000..d20ffc2 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_enrichment_std.cpp @@ -0,0 +1,347 @@ +#include +#include "qwt3d_color.h" +#include "qwt3d_plot.h" +#include "qwt3d_enrichment_std.h" + +using namespace Qwt3D; + + +///////////////////////////////////////////////////////////////// +// +// CrossHair +// +///////////////////////////////////////////////////////////////// + +CrossHair::CrossHair() +{ + configure(0, 1, false, false); +} + +CrossHair::CrossHair(double rad, double linewidth, bool smooth, bool boxed) +{ + configure(rad, linewidth, smooth, boxed); +} + +void CrossHair::configure(double rad, double linewidth, bool smooth, bool boxed) +{ + plot = 0; + radius_ = rad; + linewidth_ = linewidth; + smooth_ = smooth; + boxed_ = boxed; +} + +void CrossHair::drawBegin() +{ + setDeviceLineWidth( linewidth_ ); + oldstate_ = glIsEnabled(GL_LINE_SMOOTH); + if (smooth_) + glEnable(GL_LINE_SMOOTH); + else + glDisable(GL_LINE_SMOOTH); + glBegin( GL_LINES ); +} + +void CrossHair::drawEnd() +{ + glEnd(); + + if (oldstate_) + glEnable(GL_LINE_SMOOTH); + else + glDisable(GL_LINE_SMOOTH); +} + +void CrossHair::draw(Qwt3D::Triple const& pos) +{ + RGBA rgba = (*plot->dataColor())(pos); + glColor4d(rgba.r,rgba.g,rgba.b,rgba.a); + + double diag = (plot->hull().maxVertex-plot->hull().minVertex).length() * radius_; + + glVertex3d( pos.x - diag, pos.y, pos.z); + glVertex3d( pos.x + diag, pos.y, pos.z); + + glVertex3d( pos.x, pos.y - diag, pos.z); + glVertex3d( pos.x, pos.y + diag, pos.z); + + glVertex3d( pos.x, pos.y, pos.z - diag); + glVertex3d( pos.x, pos.y, pos.z + diag); + + // hull + + if (!boxed_) + return; + + glVertex3d( pos.x - diag, pos.y - diag, pos.z + diag); + glVertex3d( pos.x + diag, pos.y - diag, pos.z + diag); + glVertex3d( pos.x - diag, pos.y - diag, pos.z - diag); + glVertex3d( pos.x + diag, pos.y - diag, pos.z - diag); + + glVertex3d( pos.x - diag, pos.y + diag, pos.z + diag); + glVertex3d( pos.x + diag, pos.y + diag, pos.z + diag); + glVertex3d( pos.x - diag, pos.y + diag, pos.z - diag); + glVertex3d( pos.x + diag, pos.y + diag, pos.z - diag); + + glVertex3d( pos.x - diag, pos.y - diag, pos.z + diag); + glVertex3d( pos.x - diag, pos.y + diag, pos.z + diag); + glVertex3d( pos.x - diag, pos.y - diag, pos.z - diag); + glVertex3d( pos.x - diag, pos.y + diag, pos.z - diag); + + glVertex3d( pos.x + diag, pos.y - diag, pos.z + diag); + glVertex3d( pos.x + diag, pos.y + diag, pos.z + diag); + glVertex3d( pos.x + diag, pos.y - diag, pos.z - diag); + glVertex3d( pos.x + diag, pos.y + diag, pos.z - diag); + + glVertex3d( pos.x - diag, pos.y - diag, pos.z - diag); + glVertex3d( pos.x - diag, pos.y - diag, pos.z + diag); + glVertex3d( pos.x + diag, pos.y - diag, pos.z - diag); + glVertex3d( pos.x + diag, pos.y - diag, pos.z + diag); + + glVertex3d( pos.x - diag, pos.y + diag, pos.z - diag); + glVertex3d( pos.x - diag, pos.y + diag, pos.z + diag); + glVertex3d( pos.x + diag, pos.y + diag, pos.z - diag); + glVertex3d( pos.x + diag, pos.y + diag, pos.z + diag); +} + +///////////////////////////////////////////////////////////////// +// +// Dot +// +///////////////////////////////////////////////////////////////// + +Dot::Dot() +{ + configure(1, false); +} + +Dot::Dot(double pointsize, bool smooth) +{ + configure(pointsize, smooth); +} + +void Dot::configure(double pointsize, bool smooth) +{ + plot = 0; + pointsize_ = pointsize; + smooth_ = smooth; +} + +void Dot::drawBegin() +{ + setDevicePointSize( pointsize_ ); + oldstate_ = glIsEnabled(GL_POINT_SMOOTH); + if (smooth_) + glEnable(GL_POINT_SMOOTH); + else + glDisable(GL_POINT_SMOOTH); + + //glPointSize(10); + glBegin( GL_POINTS ); +} + +void Dot::drawEnd() +{ + glEnd(); + + if (oldstate_) + glEnable(GL_POINT_SMOOTH); + else + glDisable(GL_POINT_SMOOTH); +} + +void Dot::draw(Qwt3D::Triple const& pos) +{ + RGBA rgba = (*plot->dataColor())(pos); + glColor4d(rgba.r,rgba.g,rgba.b,rgba.a); + glVertex3d( pos.x, pos.y, pos.z); +} + + +///////////////////////////////////////////////////////////////// +// +// Cone +// +///////////////////////////////////////////////////////////////// + +Cone::Cone() +{ + hat = gluNewQuadric(); + disk = gluNewQuadric(); + + configure(0, 3); +} + +Cone::Cone(double rad, unsigned quality) +{ + hat = gluNewQuadric(); + disk = gluNewQuadric(); + + configure(rad, quality); +} + +Cone::~Cone() +{ + gluDeleteQuadric(hat); + gluDeleteQuadric(disk); +} + +void Cone::configure(double rad, unsigned quality) +{ + plot = 0; + radius_ = rad; + quality_ = quality; + oldstate_ = GL_FALSE; + + gluQuadricDrawStyle(hat,GLU_FILL); + gluQuadricNormals(hat,GLU_SMOOTH); + gluQuadricOrientation(hat,GLU_OUTSIDE); + gluQuadricDrawStyle(disk,GLU_FILL); + gluQuadricNormals(disk,GLU_SMOOTH); + gluQuadricOrientation(disk,GLU_OUTSIDE); +} + +void Cone::draw(Qwt3D::Triple const& pos) +{ + RGBA rgba = (*plot->dataColor())(pos); + glColor4d(rgba.r,rgba.g,rgba.b,rgba.a); + + GLint mode; + glGetIntegerv(GL_MATRIX_MODE, &mode); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + glTranslatef(pos.x, pos.y, pos.z); + + gluCylinder(hat, 0.0, radius_, radius_*2, quality_, 1); + glTranslatef(0, 0, radius_*2); + gluDisk(disk, 0.0, radius_, quality_, 1); + + glPopMatrix(); + glMatrixMode(mode); +} + + +///////////////////////////////////////////////////////////////// +// +// Arrow +// +///////////////////////////////////////////////////////////////// + +Arrow::Arrow() +{ + hat = gluNewQuadric(); + disk = gluNewQuadric(); + base = gluNewQuadric(); + bottom = gluNewQuadric(); + + gluQuadricDrawStyle(hat,GLU_FILL); + gluQuadricNormals(hat,GLU_SMOOTH); + gluQuadricOrientation(hat,GLU_OUTSIDE); + gluQuadricDrawStyle(disk,GLU_FILL); + gluQuadricNormals(disk,GLU_SMOOTH); + gluQuadricOrientation(disk,GLU_OUTSIDE); + gluQuadricDrawStyle(base,GLU_FILL); + gluQuadricNormals(base,GLU_SMOOTH); + gluQuadricOrientation(base,GLU_OUTSIDE); + gluQuadricDrawStyle(bottom,GLU_FILL); + gluQuadricNormals(bottom,GLU_SMOOTH); + gluQuadricOrientation(bottom,GLU_OUTSIDE); + + configure(3, 0.4, 0.06, 0.02); +} + +Arrow::~Arrow() +{ + gluDeleteQuadric(hat); + gluDeleteQuadric(disk); + gluDeleteQuadric(base); + gluDeleteQuadric(bottom); +} + +/** +\param segs number of faces for the fields arrows (see the gallery for examples) +\param relconelength see picture +\param relconerad see picture +\param relstemrad see picture +\image html arrowanatomy.png +*/ +void Arrow::configure(int segs, double relconelength, double relconerad, double relstemrad) +{ + plot = 0; + segments_ = segs; + oldstate_ = GL_FALSE; + rel_cone_length = relconelength; + rel_cone_radius = relconerad; + rel_stem_radius = relstemrad; +} + +void Arrow::draw(Qwt3D::Triple const& pos) +{ + Triple end = top_; + Triple beg = pos; + Triple vdiff = end-beg; + double length = vdiff.length(); + glColor4d(rgba_.r,rgba_.g,rgba_.b,rgba_.a); + + double radius[2]; + radius[0] = rel_cone_radius * length; + radius[1] = rel_stem_radius * length; + + GLint mode; + glGetIntegerv(GL_MATRIX_MODE, &mode); + + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + + Triple axis; + double phi = calcRotation(axis, FreeVector(beg,end)); + + glTranslatef(beg.x, beg.y, beg.z); + glRotatef(phi, axis.x, axis.y, axis.z); + + double baseheight = (1-rel_cone_length) * length; + + glTranslatef(0, 0, baseheight); + + gluCylinder(hat, radius[0], 0.0, rel_cone_length * length, segments_,1); + gluDisk(disk,radius[1],radius[0], segments_,1); + + glTranslatef(0, 0, -baseheight); + + gluCylinder(base, radius[1],radius[1], baseheight,segments_,1); + gluDisk(disk,0,radius[1],segments_,1); + + glPopMatrix(); + glMatrixMode(mode); +} + + +//! transform a vector on the z axis with length |beg-end|, to get them in coincidence with the vector(beg,end) +/** + \return Angle in degree to rotate + \param axis The axis to rotate around + \param beg result vector base point + \param end result vector top point +*/ +double Arrow::calcRotation(Triple& axis, FreeVector const& vec) +{ + + Triple end = vec.top; + Triple beg = vec.base; + + Triple firstbeg(0.0,0.0,0.0); + Triple firstend(0.0,0.0,(end-beg).length()); + + Triple first = firstend - firstbeg; + first.normalize(); + + Triple second = end-beg; + second.normalize(); + + axis = normalizedcross(first,second); + double cosphi = dotProduct(first,second); + + return 180 * acos(cosphi) / Qwt3D::PI; +} diff --git a/lib/tqwtplot3d/src/qwt3d_function.cpp b/lib/tqwtplot3d/src/qwt3d_function.cpp new file mode 100644 index 0000000..28d874e --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_function.cpp @@ -0,0 +1,101 @@ +#include "qwt3d_surfaceplot.h" +#include "qwt3d_function.h" + +using namespace Qwt3D; + +Function::Function() +:GridMapping() +{ +} + +Function::Function(SurfacePlot& pw) +:GridMapping() +{ + plotwidget_p = &pw; +} + +Function::Function(SurfacePlot* pw) +:GridMapping() +{ + plotwidget_p = pw; +} + +void Function::assign(SurfacePlot& plotWidget) +{ + if (&plotWidget != plotwidget_p) + plotwidget_p = &plotWidget; +} + +void Function::assign(SurfacePlot* plotWidget) +{ + if (plotWidget != plotwidget_p) + plotwidget_p = plotWidget; +} + +void Function:: setMinZ(double val) +{ + range_p.minVertex.z = val; +} + +void Function:: setMaxZ(double val) +{ + range_p.maxVertex.z = val; +} + +bool Function::create() +{ + if ((umesh_p<=2) || (vmesh_p<=2) || !plotwidget_p) + return false; + + /* allocate some space for the mesh */ + double** data = new double* [umesh_p] ; + + unsigned i,j; + for ( i = 0; i < umesh_p; i++) + { + data[i] = new double [vmesh_p]; + } + + /* get the data */ + + double dx = (maxu_p - minu_p) / (umesh_p - 1); + double dy = (maxv_p - minv_p) / (vmesh_p - 1); + + for (i = 0; i < umesh_p; ++i) + { + for (j = 0; j < vmesh_p; ++j) + { + data[i][j] = operator()(minu_p + i*dx, minv_p + j*dy); + + if (data[i][j] > range_p.maxVertex.z) + data[i][j] = range_p.maxVertex.z; + else if (data[i][j] < range_p.minVertex.z) + data[i][j] = range_p.minVertex.z; + } + } + + Q_ASSERT(plotwidget_p); + if (!plotwidget_p) + { + fprintf(stderr,"Function: no valid Plot3D Widget assigned"); + } + else + { + ((SurfacePlot*)plotwidget_p)->loadFromData(data, umesh_p, vmesh_p, minu_p, maxu_p, minv_p, maxv_p); + } + + for ( i = 0; i < umesh_p; i++) + { + delete [] data[i]; + } + + delete [] data; + + return true; +} + +bool Function::create(SurfacePlot& pl) +{ + assign(pl); + return create(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_gridmapping.cpp b/lib/tqwtplot3d/src/qwt3d_gridmapping.cpp new file mode 100644 index 0000000..4de521b --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_gridmapping.cpp @@ -0,0 +1,32 @@ +#include "qwt3d_gridmapping.h" +#include "qwt3d_surfaceplot.h" + +using namespace Qwt3D; + +GridMapping::GridMapping() +{ + plotwidget_p = 0; + setMesh(0,0); + setDomain(0,0,0,0); + restrictRange(ParallelEpiped(Triple(-DBL_MAX,-DBL_MAX,-DBL_MAX),Triple(DBL_MAX,DBL_MAX,DBL_MAX))); +} + +void GridMapping::setMesh(unsigned int columns,unsigned int rows) +{ + umesh_p = columns; + vmesh_p = rows; +} + +void GridMapping::setDomain(double minu, double maxu, double minv, double maxv) +{ + minu_p = minu; + maxu_p = maxu; + minv_p = minv; + maxv_p = maxv; +} + +void GridMapping::restrictRange(Qwt3D::ParallelEpiped const& p) +{ + range_p = p; +} + diff --git a/lib/tqwtplot3d/src/qwt3d_gridplot.cpp b/lib/tqwtplot3d/src/qwt3d_gridplot.cpp new file mode 100644 index 0000000..6543785 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_gridplot.cpp @@ -0,0 +1,596 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include "qwt3d_surfaceplot.h" +#include "qwt3d_enrichment_std.h" + +using namespace std; +using namespace Qwt3D; + + + +void SurfacePlot::createDataG() +{ + createFloorData(); + + if (plotStyle() == NOPLOT) + return; + + int i, j; + RGBA col; + int step = resolution(); + + if (plotStyle() == Qwt3D::POINTS) + { + createPoints(); + return; + } + else if (plotStyle() == Qwt3D::USER) + { + if (userplotstyle_p) + createEnrichment(*userplotstyle_p); + return; + } + + setDeviceLineWidth(meshLineWidth()); + + GLStateBewarer sb(GL_POLYGON_OFFSET_FILL,true); + setDevicePolygonOffset(polygonOffset(),1.0); + + GLStateBewarer sb2(GL_LINE_SMOOTH, smoothDataMesh()); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + + int lastcol = actualDataG_->columns(); + int lastrow = actualDataG_->rows(); + + if (plotStyle() != WIREFRAME) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_QUADS); + + bool hl = (plotStyle() == HIDDENLINE); + if (hl) + { + col = backgroundRGBAColor(); + glColor4d(col.r, col.g, col.b, col.a); + } + + for (i = 0; i < lastcol - step; i += step) + { + glBegin(GL_TRIANGLE_STRIP); + setColorFromVertexG(i, 0, hl); + glNormal3dv(actualDataG_->normals[i][0]); + glVertex3dv(actualDataG_->vertices[i][0]); + + setColorFromVertexG(i+step, 0, hl); + glNormal3dv(actualDataG_->normals[i+step][0]); + glVertex3dv(actualDataG_->vertices[i+step][0]); + + for (j = 0; j < lastrow - step; j += step) + { + setColorFromVertexG(i,j+step, hl); + glNormal3dv(actualDataG_->normals[i][j+step]); + glVertex3dv(actualDataG_->vertices[i][j+step]); + + setColorFromVertexG(i+step, j+step, hl); + glNormal3dv(actualDataG_->normals[i+step][j+step]); + glVertex3dv(actualDataG_->vertices[i+step][j+step]); + } + glEnd(); + } + } + + if (plotStyle() == FILLEDMESH || plotStyle() == WIREFRAME || plotStyle() == HIDDENLINE) + { + glColor4d(meshColor().r, meshColor().g, meshColor().b, meshColor().a); + + if (step < actualDataG_->columns() && step < actualDataG_->rows()) + { + glBegin(GL_LINE_LOOP); + for (i = 0; i < actualDataG_->columns() - step; i += step) + glVertex3dv(actualDataG_->vertices[i][0]); + for (j = 0; j < actualDataG_->rows() - step; j += step) + glVertex3dv(actualDataG_->vertices[i][j]); + for (; i >= 0; i -= step) + glVertex3dv(actualDataG_->vertices[i][j]); + for (; j >= 0; j -= step) + glVertex3dv(actualDataG_->vertices[0][j]); + glEnd(); + } + + // weaving + for (i = step; i < actualDataG_->columns() - step; i += step) + { + glBegin(GL_LINE_STRIP); + for (j = 0; j < actualDataG_->rows(); j += step) + glVertex3dv(actualDataG_->vertices[i][j]); + glEnd(); + } + for (j = step; j < actualDataG_->rows() - step; j += step) + { + glBegin(GL_LINE_STRIP); + for (i = 0; i < actualDataG_->columns(); i += step) + glVertex3dv(actualDataG_->vertices[i][j]); + glEnd(); + } + } +} + +void SurfacePlot::setColorFromVertexG(int ix, int iy, bool skip) +{ + if (skip) + return; + + RGBA col = (*datacolor_p)( + actualDataG_->vertices[ix][iy][0], + actualDataG_->vertices[ix][iy][1], + actualDataG_->vertices[ix][iy][2]); + + glColor4d(col.r, col.g, col.b, col.a); +} + + +void SurfacePlot::createNormalsG() +{ + if (!normals() || actualDataG_->empty()) + return; + + Arrow arrow; + arrow.setQuality(normalQuality()); + + Triple basev, topv, norm; + + int step = resolution(); + + double diag = (actualDataG_->hull().maxVertex-actualDataG_->hull().minVertex).length() * normalLength(); + + arrow.assign(*this); + arrow.drawBegin(); + for (int i = 0; i <= actualDataG_->columns() - step; i += step) + { + for (int j = 0; j <= actualDataG_->rows() - step; j += step) + { + basev = Triple(actualDataG_->vertices[i][j][0],actualDataG_->vertices[i][j][1],actualDataG_->vertices[i][j][2]); + topv = Triple(actualDataG_->vertices[i][j][0]+actualDataG_->normals[i][j][0], + actualDataG_->vertices[i][j][1]+actualDataG_->normals[i][j][1], + actualDataG_->vertices[i][j][2]+actualDataG_->normals[i][j][2]); + + norm = topv-basev; + norm.normalize(); + norm *= diag; + + arrow.setTop(basev+norm); + arrow.setColor((*datacolor_p)(basev.x,basev.y,basev.z)); + arrow.draw(basev); + } + } + arrow.drawEnd(); +} + +void SurfacePlot::readIn(GridData& gdata, Triple** data, unsigned int columns, unsigned int rows) +{ + gdata.setSize(columns,rows); + + ParallelEpiped range(Triple(DBL_MAX,DBL_MAX,DBL_MAX),Triple(-DBL_MAX,-DBL_MAX,-DBL_MAX)); + + /* fill out the vertex array for the mesh. */ + for (unsigned i = 0; i != columns; ++i) + { + for (unsigned j = 0; j != rows; ++j) + { + gdata.vertices[i][j][0] = data[i][j].x; + gdata.vertices[i][j][1] = data[i][j].y; + gdata.vertices[i][j][2] = data[i][j].z; + + if (data[i][j].x > range.maxVertex.x) + range.maxVertex.x = data[i][j].x; + if (data[i][j].y > range.maxVertex.y) + range.maxVertex.y = data[i][j].y; + if (data[i][j].z > range.maxVertex.z) + range.maxVertex.z = data[i][j].z; + if (data[i][j].x < range.minVertex.x) + range.minVertex.x = data[i][j].x; + if (data[i][j].y < range.minVertex.y) + range.minVertex.y = data[i][j].y; + if (data[i][j].z < range.minVertex.z) + range.minVertex.z = data[i][j].z; + } + } + gdata.setHull(range); +} + + +void SurfacePlot::readIn(GridData& gdata, double** data, unsigned int columns, unsigned int rows + , double minx, double maxx, double miny, double maxy) +{ + gdata.setPeriodic(false,false); + gdata.setSize(columns,rows); + + double dx = (maxx - minx) / (gdata.columns() - 1); + double dy = (maxy - miny) / (gdata.rows() - 1); + + double tmin = DBL_MAX; + double tmax = -DBL_MAX; + + /* fill out the vertex array for the mesh. */ + for (unsigned i = 0; i != columns; ++i) + { + for (unsigned j = 0; j != rows; ++j) + { + gdata.vertices[i][j][0] = minx + i*dx; + gdata.vertices[i][j][1] = miny + j*dy; + gdata.vertices[i][j][2] = data[i][j]; + + if (data[i][j] > tmax) + tmax = data[i][j]; + if (data[i][j] < tmin) + tmin = data[i][j]; + } + } + ParallelEpiped hull = + ParallelEpiped( + Triple( + gdata.vertices[0][0][0], + gdata.vertices[0][0][1], + tmin + ), + Triple( + gdata.vertices[gdata.columns()-1][gdata.rows()-1][0], + gdata.vertices[gdata.columns()-1][gdata.rows()-1][1], + tmax + ) + ); + + gdata.setHull(hull); +} + + +void SurfacePlot::calcNormals(GridData& gdata) +{ + + unsigned int rows = gdata.rows(); + unsigned int columns = gdata.columns(); + + // normals + + Triple u, v, n; // for cross product + + for (unsigned i = 0; i != columns; ++i) + { + for (unsigned j = 0; j != rows; ++j) + { + n = Triple(0,0,0); + + + if (i0 && j0 && j>0) + { + u = Triple( + gdata.vertices[i-1][j][0] - gdata.vertices[i][j][0], + gdata.vertices[i-1][j][1] - gdata.vertices[i][j][1], + gdata.vertices[i-1][j][2] - gdata.vertices[i][j][2] + ); + + v = Triple( + gdata.vertices[i][j-1][0] - gdata.vertices[i][j][0], + gdata.vertices[i][j-1][1] - gdata.vertices[i][j][1], + gdata.vertices[i][j-1][2] - gdata.vertices[i][j][2] + ); + n += normalizedcross(u,v); + } + + if (i0) + { + u = Triple( + gdata.vertices[i][j-1][0] - gdata.vertices[i][j][0], + gdata.vertices[i][j-1][1] - gdata.vertices[i][j][1], + gdata.vertices[i][j-1][2] - gdata.vertices[i][j][2] + ); + + v = Triple( + gdata.vertices[i+1][j][0] - gdata.vertices[i][j][0], + gdata.vertices[i+1][j][1] - gdata.vertices[i][j][1], + gdata.vertices[i+1][j][2] - gdata.vertices[i][j][2] + ); + n += normalizedcross(u,v); + } + n.normalize(); + + gdata.normals[i][j][0] = n.x; + gdata.normals[i][j][1] = n.y; + gdata.normals[i][j][2] = n.z; + } + } +} + + +void SurfacePlot::sewPeriodic(GridData& gdata) +{ + // sewing + + Triple n; + + unsigned int columns = gdata.columns(); + unsigned int rows = gdata.rows(); + + if (gdata.uperiodic()) + { + for (unsigned i = 0; i != columns; ++i) + { + n = Triple( + gdata.normals[i][0][0] + gdata.normals[i][rows-1][0], + gdata.normals[i][0][1] + gdata.normals[i][rows-1][1], + gdata.normals[i][0][2] + gdata.normals[i][rows-1][2] + ); + + n.normalize(); + gdata.normals[i][0][0] = gdata.normals[i][rows-1][0] = n.x; + gdata.normals[i][0][1] = gdata.normals[i][rows-1][1] = n.y; + gdata.normals[i][0][2] = gdata.normals[i][rows-1][2] = n.z; + } + } + if (gdata.vperiodic()) + { + for (unsigned j = 0; j != rows; ++j) + { + n = Triple( + gdata.normals[0][j][0] + gdata.normals[columns-1][j][0], + gdata.normals[0][j][1] + gdata.normals[columns-1][j][1], + gdata.normals[0][j][2] + gdata.normals[columns-1][j][2] + ); + + n.normalize(); + gdata.normals[0][j][0] = gdata.normals[columns-1][j][0] = n.x; + gdata.normals[0][j][1] = gdata.normals[columns-1][j][1] = n.y; + gdata.normals[0][j][2] = gdata.normals[columns-1][j][2] = n.z; + } + } +} + +/*! + Convert user grid data to internal vertex structure. + See also NativeReader::read() and Function::create() +*/ +bool SurfacePlot::loadFromData(Triple** data, unsigned int columns, unsigned int rows, bool uperiodic, bool vperiodic) +{ + actualDataC_->clear(); + actualData_p = actualDataG_; + + readIn(*actualDataG_, data, columns, rows); + calcNormals(*actualDataG_); + actualDataG_->setPeriodic(uperiodic,vperiodic); + sewPeriodic(*actualDataG_); + + updateData(); + updateNormals(); + createCoordinateSystem(); + + return true; +} + +/*! + Convert user grid data to internal vertex structure. + See also NativeReader::read() and Function::create() +*/ +bool SurfacePlot::loadFromData(double** data, unsigned int columns, unsigned int rows + , double minx, double maxx, double miny, double maxy) +{ + actualDataC_->clear(); + actualData_p = actualDataG_; + + actualDataG_->setPeriodic(false,false); + actualDataG_->setSize(columns,rows); + readIn(*actualDataG_,data,columns,rows,minx,maxx,miny,maxy); + calcNormals(*actualDataG_); + + updateData(); + updateNormals(); + createCoordinateSystem(); + + return true; +} + + +void SurfacePlot::createFloorDataG() +{ + switch (floorStyle()) + { + case FLOORDATA: + Data2FloorG(); + break; + case FLOORISO: + Isolines2FloorG(); + break; + default: + break; + } +} + +void SurfacePlot::Data2FloorG() +{ + if (actualData_p->empty()) + return; + + int step = resolution(); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPolygonMode(GL_FRONT_AND_BACK, GL_QUADS); + + double zshift = actualData_p->hull().minVertex.z; + for (int i = 0; i < actualDataG_->columns() - step; i += step) + { + glBegin(GL_TRIANGLE_STRIP); + setColorFromVertexG(i, 0); + glVertex3d(actualDataG_->vertices[i][0][0], actualDataG_->vertices[i][0][1], zshift); + + setColorFromVertexG(i+step, 0); + glVertex3d(actualDataG_->vertices[i+step][0][0],actualDataG_->vertices[i+step][0][1], zshift); + for (int j = 0; j < actualDataG_->rows() - step; j += step) + { + setColorFromVertexG(i, j+step); + glVertex3d(actualDataG_->vertices[i][j+step][0],actualDataG_->vertices[i][j+step][1], zshift); + + setColorFromVertexG(i+step, j+step); + glVertex3d(actualDataG_->vertices[i+step][j+step][0],actualDataG_->vertices[i+step][j+step][1], zshift); + } + glEnd(); + } +} + +void SurfacePlot::Isolines2FloorG() +{ + if (isolines() <= 0 || actualData_p->empty()) + return; + + double count = (actualData_p->hull().maxVertex.z - actualData_p->hull().minVertex.z) / isolines(); + + RGBA col; + + int step = resolution(); + + double zshift = actualData_p->hull().minVertex.z; + + int cols = actualDataG_->columns(); + int rows = actualDataG_->rows(); + + Triple t[4]; + vector intersection; + + double lambda = 0; + + GLStateBewarer sb2(GL_LINE_SMOOTH, false); + + for (int k = 0; k != isolines(); ++k) + { + double val = zshift + k * count; + + for (int i = 0; i < cols-step; i += step) + { + for (int j = 0; j < rows-step; j += step) + { + t[0] = Triple( actualDataG_->vertices[i][j][0], + actualDataG_->vertices[i][j][1], + actualDataG_->vertices[i][j][2]); + + col = (*datacolor_p)(t[0].x,t[0].y,t[0].z); + glColor4d(col.r, col.g, col.b, col.a); +// glColor4d(0,0,0,1); + + t[1] = Triple( actualDataG_->vertices[i+step][j][0], + actualDataG_->vertices[i+step][j][1], + actualDataG_->vertices[i+step][j][2]); + t[2] = Triple( actualDataG_->vertices[i+step][j+step][0], + actualDataG_->vertices[i+step][j+step][1], + actualDataG_->vertices[i+step][j+step][2]); + t[3] = Triple( actualDataG_->vertices[i][j+step][0], + actualDataG_->vertices[i][j+step][1], + actualDataG_->vertices[i][j+step][2]); + + double diff = 0; + for (int m = 0; m!=4; ++m) + { + int mm = (m+1)%4; + if ((val>=t[m].z && val<=t[mm].z) || (val>=t[mm].z && val<=t[m].z)) + { + diff = t[mm].z - t[m].z; + + if (isPracticallyZero(diff)) // degenerated + { + intersection.push_back(t[m]); + intersection.push_back(t[mm]); + continue; + } + + lambda = (val - t[m].z) / diff; + intersection.push_back(Triple(t[m].x + lambda * (t[mm].x-t[m].x), t[m].y + lambda * (t[mm].y-t[m].y), val)); + } + } + + if (!intersection.empty()) + { + if (intersection.size()>2) + { + glBegin(GL_LINE_STRIP); + for (unsigned dd = 0; dd!=intersection.size(); ++dd) + { + glVertex3d(intersection[dd].x, intersection[dd].y, zshift); + } + glEnd(); + glBegin(GL_POINTS); + glVertex3d(intersection[0].x,intersection[0].y,zshift); + glEnd(); + } + else if (intersection.size() == 2) + { + glBegin(GL_LINES); + glVertex3d(intersection[0].x,intersection[0].y,zshift); + glVertex3d(intersection[1].x,intersection[1].y,zshift); + + // small pixel gap problem (see OpenGL spec.) + glVertex3d(intersection[1].x,intersection[1].y,zshift); + glVertex3d(intersection[0].x,intersection[0].y,zshift); + glEnd(); + } + + intersection.clear(); + } + } + } + } +} + + + +/* +void SurfacePlot::calcLowResolution() +{ + if (!actualDataG_) + return; + + int res = resolution(); + if (res == 1) + { + lowresData_p = *actualDataG_; + return; + } + + GridData const& src = *actualDataG_; + result.clear(); + + +}*/ + diff --git a/lib/tqwtplot3d/src/qwt3d_io.cpp b/lib/tqwtplot3d/src/qwt3d_io.cpp new file mode 100644 index 0000000..c9ce46b --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_io.cpp @@ -0,0 +1,355 @@ +#include + +#include "qwt3d_plot.h" +#include "qwt3d_io_gl2ps.h" +#include "qwt3d_io_reader.h" +#if QT_VERSION < 0x040000 +#else + #include +#endif + +using namespace Qwt3D; + +IO::Entry::Entry() : iofunc(0) +{ +} + +IO::Entry::~Entry() +{ + delete iofunc; +} + +IO::Entry::Entry(IO::Entry const& e) +{ + if (this==&e) + return; + + fmt = e.fmt; + iofunc = e.iofunc->clone(); +} + +void IO::Entry::operator=(IO::Entry const& e) +{ + if (this==&e) + return; + + delete iofunc; + fmt = e.fmt; + iofunc = e.iofunc->clone(); +} + +IO::Entry::Entry(QString const& s, Functor const& f) + : fmt(s) +{ + iofunc = f.clone(); +} + +IO::Entry::Entry(QString const& s, Function f) + : fmt(s) +{ + Wrapper w(f); + iofunc = w.clone(); +} + + +IO::FormatCompare::FormatCompare(IO::Entry const& e) +{ + e_ = e; +} + +bool IO::FormatCompare::operator() (IO::Entry const& e) +{ + return ( e.fmt == e_.fmt); +} + +IO::FormatCompare2::FormatCompare2(QString s) +{ + s_ = s; +} + +bool IO::FormatCompare2::operator() (IO::Entry const& e) +{ + return( e.fmt == s_); +} + + + + +bool IO::add_unique(Container& l, Entry const& e) +{ + FormatCompare comp(e); + l.erase(std::remove_if(l.begin(), l.end(), comp), l.end()); + l.push_back(e); + + return true; +} + +IO::IT IO::find(Container& l, QString const& fmt) +{ + FormatCompare2 comp(fmt); + return std::find_if(l.begin(), l.end(), comp); +} + +IO::Container& IO::rlist() +{ + static Container rl = Container(); + static bool rfirst = true; + bool f = false; + f = rfirst; + if (rfirst) + { + rfirst = false; + setupHandler(); + } + return rl; +} + +IO::Container& IO::wlist() +{ + static Container wl = Container(); + static bool wfirst = true; + bool f = false; + f = wfirst; + if (wfirst) + { + wfirst = false; + setupHandler(); + } + return wl; +} + +/*! + Registers a new IO::Function for data input.\n + Every call overwrites a formerly registered handler for the same format string + (case sensitive). +*/ +bool IO::defineInputHandler(QString const& format, IO::Function func) +{ + return add_unique(rlist(), Entry(format, func)); +} + +/*! + Registers a new Functor for data input.\n + Every call overwrites a formerly registered handler for the same format string + (case sensitive). +*/ +bool IO::defineInputHandler(QString const& format, IO::Functor const& func) +{ + return add_unique(rlist(), Entry(format, func)); +} + +/*! + Registers a new IO::Function for data output. + Every call overwrites a formerly registered handler for the same format string + (case sensitive). + */ +bool IO::defineOutputHandler(QString const& format, IO::Function func) +{ + return add_unique(wlist(), Entry(format, func)); +} + +/*! + Registers a new Functor for data output.\n + Every call overwrites a formerly registered handler for the same format string + (case sensitive). +*/ +bool IO::defineOutputHandler(QString const& format, IO::Functor const& func) +{ + return add_unique(wlist(), Entry(format, func)); +} + +/*! + Applies a reading IO::Function or IO::Functor. + \param plot Plot with the content that should be loaded + \param fname File name + \param format Input format + \return The return value from the called Function/Functor. + The function returns false, if no registered handler could be found. +*/ +bool IO::load(Plot3D* plot, QString const& fname, QString const& format) +{ + IT it = IO::find(rlist(), format); + + if (it == rlist().end()) + return false; + + return (*it->iofunc)(plot, fname); +} + +/*! + Applies a writing IO::Function or IO::Functor. + \param plot Plot with the content that should be saved + \param fname File name + \param format Output format + \return The return value from the called Function/Functor. + The function returns false, if no registered handler could be found. +*/ +bool IO::save(Plot3D* plot, QString const& fname, QString const& format) +{ + IT it = IO::find(wlist(), format); + + if (it == wlist().end()) + return false; + + return (*it->iofunc)(plot, fname); +} + +/*! + Returns a list of currently registered input formats. +*/ +QStringList IO::inputFormatList() +{ + QStringList list; + for ( IT it = rlist().begin(); it!=rlist().end(); ++it ) + list.append(it->fmt); + + return list; +} + +/*! + Returns a list of currently registered output formats. +*/ +QStringList IO::outputFormatList() +{ + QStringList list; + for ( IT it = wlist().begin(); it!=wlist().end(); ++it ) + list.append(it->fmt); + + return list; +} + +/*! + Returns the input functor in charge for format and 0 if non-existent. +*/ +IO::Functor* IO::inputHandler(QString const& format) +{ + IO::IT it = IO::find(rlist(), format); + + if (it==rlist().end()) + return 0; + + return it->iofunc; +} + +/*! + Returns the output functor in charge for format and 0 if non-existent. +*/ +IO::Functor* IO::outputHandler(QString const& format) +{ + IO::IT it = IO::find(wlist(), format); + + if (it==wlist().end()) + return 0; + + return it->iofunc; +} + +bool PixmapWriter::operator()(Plot3D* plot, QString const& fname) +{ + QImage im = plot->grabFrameBuffer(true); + +#if QT_VERSION < 0x040000 + QImageIO iio; + iio.setImage(im); +#else + QImageWriter iio; +#endif + iio.setFormat(QWT3DLOCAL8BIT(fmt_)); + iio.setQuality(quality_); + iio.setFileName(fname); +#if QT_VERSION < 0x040000 + return iio.write(); +#else + return iio.write(im); +#endif +} + +//! Calls Qt's QImageIO::setQuality() function. +void PixmapWriter::setQuality(int val) +{ + quality_ = val; +} + +void IO::setupHandler() +{ +#if QT_VERSION < 0x040000 + QStringList list = QImage::outputFormatList(); + QStringList::Iterator it = list.begin(); +#else + QList list = QImageWriter::supportedImageFormats(); + QList::Iterator it = list.begin(); +#endif + PixmapWriter qtw; + while( it != list.end() ) + { + qtw.fmt_ = *it; + defineOutputHandler(*it, qtw); + ++it; + } + VectorWriter vecfunc; + vecfunc.setCompressed(false); + vecfunc.setFormat("EPS"); + defineOutputHandler("EPS", vecfunc); + vecfunc.setFormat("PS"); + defineOutputHandler("PS", vecfunc); + +#ifdef GL2PS_HAVE_ZLIB + vecfunc.setCompressed(true); + vecfunc.setFormat("EPS_GZ"); + defineOutputHandler("EPS_GZ", vecfunc); + vecfunc.setFormat("PS_GZ"); + defineOutputHandler("PS_GZ", vecfunc); +#endif + vecfunc.setFormat("PDF"); + defineOutputHandler("PDF", vecfunc); + + defineInputHandler("mes", NativeReader()); + defineInputHandler("MES", NativeReader()); +} + +/*! + \deprecated Use Plot3D::save or IO::save instead. + + Writes vector data supported by gl2ps. The corresponding format types are "EPS","PS"or "PDF". + If zlib has been configured this will be extended by "EPS_GZ" and "PS_GZ". + \b Beware: BSPSORT turns out to behave very slowly and memory consuming, especially in cases where + many polygons appear. It is still more exact than SIMPLESORT. +*/ +bool Plot3D::saveVector(QString const& fileName, QString const& format, VectorWriter::TEXTMODE text, VectorWriter::SORTMODE sortmode) +{ + if (format == "EPS" || format == "EPS_GZ" || format == "PS" + || format == "PS_GZ" || format == "PDF") + { + VectorWriter* gl2ps = (VectorWriter*)IO::outputHandler(format); + if (gl2ps) + { + gl2ps->setSortMode(sortmode); + gl2ps->setTextMode(text); + } + return IO::save(this, fileName, format); + } + return false; +} +/*! + \deprecated Use Plot3D::save or IO::save instead. + + Saves the framebuffer to the file fileName using one of the image file formats supported by Qt. +*/ +bool Plot3D::savePixmap(QString const& fileName, QString const& format) +{ + if (format == "EPS" || format == "EPS_GZ" || format == "PS" + || format == "PS_GZ" || format == "PDF") + return false; + + return IO::save(this, fileName, format); +} + +/*! + Saves content in one of the registered output formats. To modify the + behaviour for more complex output handling use IO::outputHandler. +*/ +bool Plot3D::save(QString const& fileName, QString const& format) +{ + return IO::save(this, fileName, format); +} + diff --git a/lib/tqwtplot3d/src/qwt3d_io_gl2ps.cpp b/lib/tqwtplot3d/src/qwt3d_io_gl2ps.cpp new file mode 100644 index 0000000..be94e51 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_io_gl2ps.cpp @@ -0,0 +1,387 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4786 ) +#endif + +#include +#include "qwt3d_openglhelper.h" +#include "../3rdparty/gl2ps/gl2ps.h" +#include "qwt3d_io_gl2ps.h" +#include "qwt3d_plot.h" + +using namespace Qwt3D; + +//! Provides a new VectorWriter object. +IO::Functor* VectorWriter::clone() const +{ + return new VectorWriter(*this); +} + +VectorWriter::VectorWriter() + : gl2ps_format_(GL2PS_EPS), + formaterror_(false), +#ifdef GL2PS_HAVE_ZLIB + compressed_(true), +#else + compressed_(false), +#endif + sortmode_(SIMPLESORT), + landscape_(VectorWriter::AUTO), + textmode_(VectorWriter::PIXEL), + texfname_("") + {} + + +/*! + Sets the mode for text output:\n + \param val The underlying format for the generated output:\n + PIXEL - poor quality but exact positioning\n + NATIVE - high quality but inexact positioning\n + TEX - high quality and exact positioning, arbitrary TeX strings as content for + the saved labels are possible. The disadvantage is the need for an additionally TeX run + to get the final output.\n + \param fname Optional, used only in conjunction with TeX output; file name + for the generated TeX file. If not set, a file called "OUTPUT.FOR.tex" + will be generated, where "OUTPUT.FOR" describes the file name argument for IO::save().\n\n + (04/05/27: On Linux platforms, pdflatex seems a file named 'dump_0.pdf.tex' mistakenly to + identify as PDF file.) +*/ +void VectorWriter::setTextMode(TEXTMODE val, QString fname) +{ + textmode_ = val; + texfname_ = (fname.isEmpty()) ? QString("") : fname; +} + + +#ifdef GL2PS_HAVE_ZLIB +//! Turns compressed output on or off (no effect if zlib support has not been set) +void VectorWriter::setCompressed(bool val) +{ + compressed_ = val; +} +#else +//! Turns compressed output on or off (no effect if zlib support has not been set) +void VectorWriter::setCompressed(bool) +{ + compressed_ = false; +} +#endif + + +/*! +Set output format, must be one of "EPS_GZ", "PS_GZ", "EPS", +"PS", "PDF" (case sensitive) +*/ +bool VectorWriter::setFormat(QString const& format) +{ + if (format == QString("EPS")) + { + gl2ps_format_ = GL2PS_EPS; + } + else if (format == QString("PS")) + { + gl2ps_format_ = GL2PS_PS; + } + else if (format == QString("PDF")) + { + gl2ps_format_ = GL2PS_PDF; + } +#ifdef GL2PS_HAVE_ZLIB + else if (format == QString("EPS_GZ")) + { + gl2ps_format_ = GL2PS_EPS; + } + else if (format == QString("PS_GZ")) + { + gl2ps_format_ = GL2PS_PS; + } +#endif + else + { + formaterror_ = true; + return false; + } + formaterror_ = false; + return true; +} + +//! Performs actual output +bool VectorWriter::operator()(Plot3D* plot, QString const& fname) +{ + if (formaterror_) + return false; + + plot->makeCurrent(); + + + GLint bufsize = 0, state = GL2PS_OVERFLOW; + GLint viewport[4]; + + glGetIntegerv(GL_VIEWPORT, viewport); + + GLint options = GL2PS_SIMPLE_LINE_OFFSET | GL2PS_SILENT | GL2PS_DRAW_BACKGROUND | + GL2PS_OCCLUSION_CULL | GL2PS_BEST_ROOT; + + + if (compressed_) + options |= GL2PS_COMPRESS; + + switch (landscape_) + { + case VectorWriter::AUTO: + if (viewport[2] - viewport[0] > viewport[3] - viewport[0]) + options |= GL2PS_LANDSCAPE; + break; + case VectorWriter::ON: + options |= GL2PS_LANDSCAPE; + break; + default: + break; + } + + int sortmode = GL2PS_SIMPLE_SORT; + switch (sortmode_) + { + case VectorWriter::NOSORT: + sortmode = GL2PS_NO_SORT; + break; + case VectorWriter::SIMPLESORT: + sortmode = GL2PS_SIMPLE_SORT; + break; + case VectorWriter::BSPSORT: + sortmode = GL2PS_BSP_SORT; + break; + default: + break; + } + + switch (textmode_) + { + case NATIVE: + Label::useDeviceFonts(true); + break; + case PIXEL: + Label::useDeviceFonts(false); + break; + case TEX: + options |= GL2PS_NO_PIXMAP | GL2PS_NO_TEXT; + break; + default: + break; + } + + QString version = QString::number(QWT3D_MAJOR_VERSION) + "." + + QString::number(QWT3D_MINOR_VERSION) + "." + + QString::number(QWT3D_PATCH_VERSION); + + QString producer = QString("QwtPlot3D ") + version + + " (beta) , (C) 2002"; + + // calculate actual year + time_t now; + struct tm *newtime; + time(&now); + newtime = gmtime(&now); + if (newtime && newtime->tm_year + 1900 > 2002) + producer += "-" + QString::number(newtime->tm_year+1900); + + producer += " Micha Bieber "; + + FILE *fp = fopen(QWT3DLOCAL8BIT(fname), "wb"); + if (!fp) + { + Label::useDeviceFonts(false); + return false; + } + while( state == GL2PS_OVERFLOW ) + { + bufsize += 2*1024*1024; + gl2psBeginPage ( "---", QWT3DLOCAL8BIT(producer), viewport, + gl2ps_format_, sortmode, + options, GL_RGBA, 0, NULL, 0, 0, 0, bufsize, + fp, QWT3DLOCAL8BIT(fname) ); + + plot->updateData(); + plot->updateGL(); + state = gl2psEndPage(); + } + fclose(fp); + + // extra TeX file + if (textmode_ == TEX) + { + QString fn = (texfname_.isEmpty()) + ? fname + ".tex" + : texfname_; + + fp = fopen(QWT3DLOCAL8BIT(fn), "wb"); + if (!fp) + { + Label::useDeviceFonts(false); + return false; + } + Label::useDeviceFonts(true); + options &= ~GL2PS_NO_PIXMAP & ~GL2PS_NO_TEXT; + state = GL2PS_OVERFLOW; + while( state == GL2PS_OVERFLOW ) + { + bufsize += 2*1024*1024; + gl2psBeginPage ( "---", QWT3DLOCAL8BIT(producer), viewport, + GL2PS_TEX, sortmode, + options, GL_RGBA, 0, NULL, 0, 0, 0, bufsize, + fp, QWT3DLOCAL8BIT(fn) ); + + plot->updateData(); + plot->updateGL(); + state = gl2psEndPage(); + } + fclose(fp); + } + + + Label::useDeviceFonts(false); + + return true; +} + + +// moved + +GLint Qwt3D::setDeviceLineWidth(GLfloat val) +{ + if (val<0) + val=0; + + GLint ret = gl2psLineWidth(val); + + GLfloat lw[2]; + glGetFloatv(GL_LINE_WIDTH_RANGE, lw); + + if (val < lw[0]) + val = lw[0]; + else if (val > lw[1]) + val = lw[1]; + + glLineWidth(val); + return ret; +} + +GLint Qwt3D::setDevicePointSize(GLfloat val) +{ + if (val<0) + val=0; + + GLint ret = gl2psPointSize(val); + + GLfloat lw[2]; + glGetFloatv(GL_POINT_SIZE_RANGE, lw); + + if (val < lw[0]) + val = lw[0]; + else if (val > lw[1]) + val = lw[1]; + + glPointSize(val); + return ret; +} + +GLint Qwt3D::drawDevicePixels(GLsizei width, GLsizei height, + GLenum format, GLenum type, + const void *pixels) +{ + glDrawPixels(width, height, format, type, pixels); + + if(format != GL_RGBA || type != GL_UNSIGNED_BYTE) + return GL2PS_ERROR; + + GLfloat* convertedpixel = (GLfloat*)malloc(3 * width * height * sizeof(GLfloat)); + if (!convertedpixel) + return GL2PS_ERROR; + + GLubyte* px = (GLubyte*)pixels; + for (int i=0; i!=3*width*height; i+=3) + { + int pxi = (4*i)/3; + convertedpixel[i] = px[pxi] / float(255); + convertedpixel[i+1] = px[pxi+1] / float(255); + convertedpixel[i+2] = px[pxi+2] / float(255); + } + GLint ret = gl2psDrawPixels(width, height, 0, 0, GL_RGB, GL_FLOAT, convertedpixel); + free(convertedpixel); + return ret; +} + +GLint Qwt3D::drawDeviceText(const char* str, const char* fontname, int fontsize, Triple pos, RGBA /*rgba*/, ANCHOR align, double gap) +{ + double vp[3]; + + World2ViewPort(vp[0], vp[1], vp[2], pos.x, pos.y, pos.z); + Triple start(vp[0],vp[1],vp[2]); + + GLdouble fcol[4]; + glGetDoublev(GL_CURRENT_COLOR, fcol); + GLdouble bcol[4]; + glGetDoublev(GL_COLOR_CLEAR_VALUE, bcol); + +// glColor4d(color.r, color.g, color.b, color.a); +// glClearColor(color.r, color.g, color.b, color.a); + + GLint ret = GL2PS_SUCCESS; + + GLint a = GL2PS_TEXT_BL; + switch(align) + { + case Center: + a = GL2PS_TEXT_C; + break; + case CenterLeft: + a = GL2PS_TEXT_CL; + start += Triple(gap,0,0); + break; + case CenterRight: + a = GL2PS_TEXT_CR; + start += Triple(-gap,0,0); + break; + case BottomCenter: + a = GL2PS_TEXT_B; + start += Triple(0,gap,0); + break; + case BottomLeft: + a = GL2PS_TEXT_BL; + start += Triple(gap,gap,0); + break; + case BottomRight: + a = GL2PS_TEXT_BR; + start += Triple(-gap,gap,0); + break; + case TopCenter: + a = GL2PS_TEXT_T; + start += Triple(0,-gap,0); + break; + case TopLeft: + a = GL2PS_TEXT_TL; + start += Triple(gap,-gap,0); + break; + case TopRight: + a = GL2PS_TEXT_TR; + start += Triple(-gap,-gap,0); + break; + default: + break; + } + + ViewPort2World(vp[0], vp[1], vp[2], start.x, start.y, start.z); + Triple adjpos(vp[0],vp[1],vp[2]); + + glRasterPos3d(adjpos.x, adjpos.y, adjpos.z); + ret = gl2psTextOpt(str, fontname, (int)fontsize, a, 0); + glColor4dv(fcol); + glClearColor(bcol[0], bcol[1], bcol[2], bcol[3]); + return ret; +} + +void Qwt3D::setDevicePolygonOffset(GLfloat factor, GLfloat units) +{ + glPolygonOffset(factor, units); + gl2psEnable(GL2PS_POLYGON_OFFSET_FILL); +} + diff --git a/lib/tqwtplot3d/src/qwt3d_io_reader.cpp b/lib/tqwtplot3d/src/qwt3d_io_reader.cpp new file mode 100644 index 0000000..2cc57a7 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_io_reader.cpp @@ -0,0 +1,225 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4786 ) +#endif + +#include +#include +#include + +#include "qwt3d_surfaceplot.h" +#include "qwt3d_io_reader.h" + +using namespace std; +using namespace Qwt3D; + +const char* NativeReader::magicstring = "jk:11051895-17021986"; + +namespace +{ + FILE* open(QString fname) + { + FILE* file = fopen(QWT3DLOCAL8BIT(fname), "r"); + if (!file) + { + fprintf(stderr, "NativeReader::read: cannot open data file \"%s\"\n", QWT3DLOCAL8BIT(fname)); + } + return file; + } + + int read_char (FILE * fp, bool skipcomments = true) + { + int c; + + if ((c = fgetc (fp)) == EOF) + return (c); + if (skipcomments) + { + if (c == '#') + { + do + { + if ((c = fgetc (fp)) == EOF) + return (c); + } + while (c != '\n' && c != '\r'); + } + } + return (c); + } + + char* read_field (FILE * fp, bool skipcomments = true) + { + static char buf[71]; + int c, i; + + do + { + if ((c = read_char (fp,skipcomments)) == EOF) + return (NULL); + } + while (isspace (c)); + for (i = 0; i < 70 && !isspace (c); ++i) + { + buf[i] = c; + if ((c = read_char (fp,skipcomments)) == EOF) + break; + } + buf[i] = '\0'; + return (buf); + } + + + //! set to data begin + bool extract_info(FILE* fp, unsigned int& xmesh, unsigned int& ymesh, double& xmin, double& xmax, double& ymin, double& ymax) + { + char* p; + + // find out the size + if ((p = read_field (fp)) == 0) + return false; + xmesh = (unsigned int)atoi(p); + + if ((p = read_field (fp)) == 0) + return false; + ymesh = (unsigned int)atoi (p); + + if (xmesh < 1 || ymesh < 1) + return false; + + // ... and the limits + if ((p = read_field (fp)) == 0) + return false; + xmin = atof (p); + + if ((p = read_field (fp)) == 0) + return false; + xmax = atof (p); + + if ((p = read_field (fp)) == 0) + return false; + ymin = atof (p); + + if ((p = read_field (fp)) == 0) + return false; + ymax = atof (p); + + if (xmin > xmax || ymin > ymax) + return false; + + return true; + } + + //! find out what the magic string is and compare + bool check_magic(FILE* fp, const char* val) + { + char* p; + if ((p = read_field (fp,false)) == 0) + return false; + + if (strcmp (p, val ) != 0) + return false; + return true; + } + + //! find out what the type is + bool check_type(FILE* fp, const char* val) + { + char* p; + if ((p = read_field (fp)) == 0) + return false; + + if (strcmp (p, val ) != 0) + return false; + return true; + } + + double** allocateData(int columns, int rows) + { + double** data = new double* [columns] ; + + for ( int i = 0; i < columns; ++i) + { + data[i] = new double [rows]; + } + return data; + } + + void deleteData(double**data, int columns) + { + for ( int i = 0; i < columns; i++) + { + delete [] data[i]; + } + delete [] data; + } +} + +NativeReader::NativeReader() +: minz_(-DBL_MAX), maxz_(DBL_MAX) +{ +} + +bool NativeReader::collectInfo(FILE*& file, QString const& fname, unsigned& xmesh, unsigned& ymesh, + double& minx, double& maxx, double& miny, double& maxy) +{ + if (fname.isEmpty()) + return false; + + file = open(fname); + + if (!file) + return false; + + + if ( + (!check_magic(file, magicstring)) + ||(!check_type(file, "MESH")) + ||(!extract_info(file, xmesh, ymesh, minx, maxx, miny, maxy)) + ) + { + fclose(file); + return false; + } + + return true; +} + + +bool NativeReader::operator()(Plot3D* plot, QString const& fname) +{ + + FILE* file; + unsigned int xmesh, ymesh; + double minx, maxx, miny, maxy; + + if ( !collectInfo(file, fname, xmesh, ymesh, minx, maxx, miny, maxy) ) + return false; + + /* allocate some space for the mesh */ + double** data = allocateData(xmesh, ymesh); + + for (unsigned int j = 0; j < ymesh; j++) + { + for (unsigned int i = 0; i < xmesh; i++) + { + if (fscanf(file, "%lf", &data[i][j]) != 1) + { + fprintf(stderr, "NativeReader::read: error in data file \"%s\"\n", QWT3DLOCAL8BIT(fname)); + return false; + } + + if (data[i][j] > maxz_) + data[i][j] = maxz_; + else if (data[i][j] < minz_) + data[i][j] = minz_; + } + } + + /* close the file */ + fclose(file); + + ((SurfacePlot*)plot)->loadFromData(data, xmesh, ymesh, minx, maxx, miny, maxy); + deleteData(data,xmesh); + + return true; +} diff --git a/lib/tqwtplot3d/src/qwt3d_label.cpp b/lib/tqwtplot3d/src/qwt3d_label.cpp new file mode 100644 index 0000000..eb8b8c0 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_label.cpp @@ -0,0 +1,262 @@ +#include +#include "qwt3d_label.h" + +using namespace Qwt3D; + +bool Label::devicefonts_ = false; + +Label::Label() +{ + init(); +} + +Label::Label(const QString & family, int pointSize, int weight, bool italic) +{ + init(family, pointSize, weight, italic); +} + + +void Label::init(const QString & family, int pointSize, int weight, bool italic) +{ + init(); + font_ = QFont(family, pointSize, weight, italic ); +} + +void Label::init() +{ + beg_ = Triple(0.0, 0.0, 0.0); + end_ = beg_; + pos_ = beg_; + setColor(0,0,0); + pm_ = QPixmap(0, 0); + font_ = QFont(); + anchor_ = BottomLeft; + gap_ = 0; + flagforupdate_ = true; +} + +void Label::useDeviceFonts(bool val) +{ + devicefonts_ = val; +} + +void Label::setFont(const QString & family, int pointSize, int weight, bool italic) +{ + font_ = QFont(family, pointSize, weight, italic ); + flagforupdate_ = true; +} + +void Label::setString(QString const& s) +{ + text_ = s; + flagforupdate_ = true; +} + +void Label::setColor(double r, double g, double b, double a) +{ + Drawable::setColor(r,g,b,a); + flagforupdate_ = true; +} + +void Label::setColor(Qwt3D::RGBA rgba) +{ + Drawable::setColor(rgba); + flagforupdate_ = true; +} + +/** +example: + +\verbatim + + Anchor TopCenter (*) resp. BottomRight(X) + + +----*----+ + | Pixmap | + +---------X + +\endverbatim +*/ +void Label::setPosition(Triple pos, ANCHOR a) +{ + anchor_ = a; + pos_ = pos; +} + +void Label::setRelPosition(Tuple rpos, ANCHOR a) +{ + double ot = 0.99; + + getMatrices(modelMatrix, projMatrix, viewport); + beg_ = relativePosition(Triple(rpos.x, rpos.y, ot)); + setPosition(beg_, a); +} + +void Label::update() +{ + QPainter p; + QFontMetrics fm(font_); + + QFontInfo info(font_); + + QRect r = QRect(QPoint(0,0),fm.size(Qwt3D::SingleLine, text_));//fm.boundingRect(text_) misbehaviour under linux; + +#if QT_VERSION < 0x040000 + r.moveBy(0, -r.top()); +#else + r.translate(0, -r.top()); +#endif + + pm_ = QPixmap(r.width(), r.bottom()); + + if (pm_.isNull()) // else crash under linux + { + r = QRect(QPoint(0,0),fm.size(Qwt3D::SingleLine, QString(" "))); // draw empty space else //todo +#if QT_VERSION < 0x040000 + r.moveBy(0, -r.top()); +#else + r.translate(0, -r.top()); +#endif + pm_ = QPixmap(r.width(), r.bottom()); + } + + QBitmap bm(pm_.width(),pm_.height()); + bm.fill(Qt::color0); + p.begin( &bm ); + p.setPen(Qt::color1); + p.setFont(font_); + p.drawText(0,r.height() - fm.descent() -1 , text_); + p.end(); + + pm_.setMask(bm); + + // avoids uninitialized areas in some cases +#if QT_VERSION < 0x040000 + pm_.fill(); +#endif + p.begin( &pm_ ); + p.setFont( font_ ); + p.setPen( Qt::SolidLine ); + p.setPen( GL2Qt(color.r, color.g, color.b) ); + + p.drawText(0,r.height() - fm.descent() -1 , text_); + p.end(); +#if QT_VERSION < 0x040000 + buf_ = pm_.convertToImage(); +#else + buf_ = pm_.toImage(); +#endif + tex_ = QGLWidget::convertToGLFormat( buf_ ); // flipped 32bit RGBA ? +} + +/** +Adds an additional shift to the anchor point. This happens in a more or less intelligent manner +depending on the nature of the anchor: +\verbatim +anchor type shift + +left aligned --> +right aligned <-- +top aligned top-down +bottom aligned bottom-up +\endverbatim +The unit is user space dependend (one pixel on screen - play around to get satisfying results) +*/ +void Label::adjust(int gap) +{ + gap_ = gap; +} + +void Label::convert2screen() +{ + Triple start = World2ViewPort(pos_); + + switch (anchor_) + { + case BottomLeft : + beg_ = pos_; + break; + case BottomRight: + beg_ = ViewPort2World(start - Triple(width() + gap_, 0, 0)); + break; + case BottomCenter: + beg_ = ViewPort2World(start - Triple(width() / 2, -gap_, 0)); + break; + case TopRight: + beg_ = ViewPort2World(start - Triple(width() + gap_, height(), 0)); + break; + case TopLeft: + beg_ = ViewPort2World(start - Triple(-gap_, height(), 0)); + break; + case TopCenter: + beg_ = ViewPort2World(start - Triple(width() / 2, height() + gap_, 0)); + break; + case CenterLeft: + beg_ = ViewPort2World(start - Triple(-gap_, height() / 2, 0)); + break; + case CenterRight: + beg_ = ViewPort2World(start - Triple(width() + gap_, height() / 2, 0)); + break; + case Center: + beg_ = ViewPort2World(start - Triple(width() / 2, height() / 2, 0)); + break; + default: + break; + } + start = World2ViewPort(beg_); + end_ = ViewPort2World(start + Triple(width(), height(), 0)); +} + +void Label::draw() +{ + if (flagforupdate_) + { + update(); + flagforupdate_ = false; + } + + if (buf_.isNull()) + return; + + GLboolean b; + GLint func; + GLdouble v; + glGetBooleanv(GL_ALPHA_TEST, &b); + glGetIntegerv(GL_ALPHA_TEST_FUNC, &func); + glGetDoublev(GL_ALPHA_TEST_REF, &v); + + glEnable (GL_ALPHA_TEST); + glAlphaFunc (GL_NOTEQUAL, 0.0); + + convert2screen(); + glRasterPos3d(beg_.x, beg_.y, beg_.z); + + + int w = tex_.width(); + int h = tex_.height(); + + if (devicefonts_) + { + drawDeviceText(QWT3DLOCAL8BIT(text_), "Courier", font_.pointSize(), pos_, color, anchor_, gap_); + } + else + { + drawDevicePixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, tex_.bits()); +// glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, tex_.bits()); + } + + + glAlphaFunc(func,v); + Enable(GL_ALPHA_TEST, b); +} + + +double Label::width() const +{ + return pm_.width(); +} + +double Label::height() const +{ + return pm_.height(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_lighting.cpp b/lib/tqwtplot3d/src/qwt3d_lighting.cpp new file mode 100644 index 0000000..2f368a0 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_lighting.cpp @@ -0,0 +1,192 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include +#include "qwt3d_plot.h" + +using namespace Qwt3D; + +namespace { +inline GLenum lightEnum(unsigned idx) +{ + switch(idx) { + case 0: + return GL_LIGHT0; + case 1: + return GL_LIGHT1; + case 2: + return GL_LIGHT2; + case 3: + return GL_LIGHT3; + case 4: + return GL_LIGHT4; + case 5: + return GL_LIGHT5; + case 6: + return GL_LIGHT6; + case 7: + return GL_LIGHT7; + default: + return GL_LIGHT0; + } +} + +} + +void Plot3D::enableLighting(bool val) +{ + if (lighting_enabled_ == val) + return; + + lighting_enabled_ = val; + makeCurrent(); + if (val) + glEnable(GL_LIGHTING); + else + glDisable(GL_LIGHTING); + + if (!initializedGL()) + return; + updateGL(); +} + +void Plot3D::disableLighting(bool val) +{ + enableLighting(!val); +} + +bool Plot3D::lightingEnabled() const +{ + return lighting_enabled_; +} + +/** + \param light light number [0..7] + \see setLight +*/ +void Plot3D::illuminate(unsigned light) +{ + if (light>7) + return; + lights_[light].unlit = false; +} +/** + \param light light number [0..7] + \see setLight +*/ +void Plot3D::blowout(unsigned light) +{ + if (light>7) + return; + lights_[light].unlit = false; +} + +/** + Sets GL material properties +*/ +void Plot3D::setMaterialComponent(GLenum property, double r, double g, double b, double a) +{ + GLfloat rgba[4] = {(GLfloat)r, (GLfloat)g, (GLfloat)b, (GLfloat)a}; + makeCurrent(); + glMaterialfv(GL_FRONT_AND_BACK, property, rgba); +} + +/** + This function is for convenience. It sets GL material properties with the equal r,g,b values + and a blending alpha with value 1.0 +*/ +void Plot3D::setMaterialComponent(GLenum property, double intensity) +{ + setMaterialComponent(property,intensity,intensity,intensity,1.0); +} + +/** + Sets GL shininess +*/ +void Plot3D::setShininess(double exponent) +{ + makeCurrent(); + glMaterialf(GL_FRONT, GL_SHININESS, exponent); +} + +/** + Sets GL light properties for light 'light' +*/ +void Plot3D::setLightComponent(GLenum property, double r, double g, double b, double a, unsigned light) +{ + GLfloat rgba[4] = {(GLfloat)r, (GLfloat)g, (GLfloat)b, (GLfloat)a}; + makeCurrent(); + glLightfv(lightEnum(light), property, rgba); +} + +/** + This function is for convenience. It sets GL light properties with the equal r,g,b values + and a blending alpha with value 1.0 +*/ +void Plot3D::setLightComponent(GLenum property, double intensity, unsigned light) +{ + setLightComponent(property,intensity,intensity,intensity,1.0, lightEnum(light)); +} + +/** + Set the rotation angle of the light source. If you look along the respective axis towards ascending values, + the rotation is performed in mathematical \e negative sense + \param xVal angle in \e degree to rotate around the X axis + \param yVal angle in \e degree to rotate around the Y axis + \param zVal angle in \e degree to rotate around the Z axis + \param light light number +*/ +void Plot3D::setLightRotation( double xVal, double yVal, double zVal, unsigned light ) +{ + if (light>7) + return; + lights_[light].rot.x = xVal; + lights_[light].rot.y = yVal; + lights_[light].rot.z = zVal; +} + +/** + Set the shift in light source (world) coordinates. + \param xVal shift along (world) X axis + \param yVal shift along (world) Y axis + \param zVal shift along (world) Z axis + \param light light number + \see setViewportShift() +*/ +void Plot3D::setLightShift( double xVal, double yVal, double zVal, unsigned light ) +{ + if (light>7) + return; + lights_[light].shift.x = xVal; + lights_[light].shift.y = yVal; + lights_[light].shift.z = zVal; +} + +void Plot3D::applyLight(unsigned light) +{ + if (lights_[light].unlit) + return; + + glEnable(lightEnum(light)); + glLoadIdentity(); + + glRotatef( lights_[light].rot.x-90, 1.0, 0.0, 0.0 ); + glRotatef( lights_[light].rot.y , 0.0, 1.0, 0.0 ); + glRotatef( lights_[light].rot.z , 0.0, 0.0, 1.0 ); + GLfloat lightPos[4] = { lights_[light].shift.x, lights_[light].shift.y, lights_[light].shift.z, 1.0}; + GLenum le = lightEnum(light); + glLightfv(le, GL_POSITION, lightPos); +} + +void Plot3D::applyLights() +{ + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + for (unsigned i=0; i<8; ++i) + { + applyLight(i); + } + glPopMatrix(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_meshplot.cpp b/lib/tqwtplot3d/src/qwt3d_meshplot.cpp new file mode 100644 index 0000000..0c975fe --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_meshplot.cpp @@ -0,0 +1,320 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include "qwt3d_surfaceplot.h" +#include "qwt3d_enrichment_std.h" + +using namespace std; +using namespace Qwt3D; + + +///////////////////////////////////////////////////////////////////////////////// +// +// cell specific +// + + +void SurfacePlot::createDataC() +{ + createFloorDataC(); + + if (plotStyle() == NOPLOT) + return; + + if (plotStyle() == Qwt3D::POINTS) + { + createPoints(); + return; + } + else if (plotStyle() == Qwt3D::USER) + { + if (userplotstyle_p) + createEnrichment(*userplotstyle_p); + return; + } + + setDeviceLineWidth(meshLineWidth()); + GLStateBewarer sb(GL_POLYGON_OFFSET_FILL,true); + setDevicePolygonOffset(polygonOffset(),1.0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int idx = 0; + if (plotStyle() != WIREFRAME) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_QUADS); + + bool hl = (plotStyle() == HIDDENLINE); + if (hl) + { + RGBA col = backgroundRGBAColor(); + glColor4d(col.r, col.g, col.b, col.a); + } + + for (unsigned i=0; i!=actualDataC_->cells.size(); ++i) + { + glBegin(GL_POLYGON); + for (unsigned j=0; j!=actualDataC_->cells[i].size(); ++j) + { + idx = actualDataC_->cells[i][j]; + setColorFromVertexC(idx, hl); + glVertex3d( actualDataC_->nodes[idx].x, actualDataC_->nodes[idx].y, actualDataC_->nodes[idx].z ); + glNormal3d( actualDataC_->normals[idx].x, actualDataC_->normals[idx].y, actualDataC_->normals[idx].z ); + } + glEnd(); + } + } + + if (plotStyle() == FILLEDMESH || plotStyle() == WIREFRAME || plotStyle() == HIDDENLINE) + { + glColor4d(meshColor().r, meshColor().g, meshColor().b, meshColor().a); + { + for (unsigned i=0; i!=actualDataC_->cells.size(); ++i) + { + glBegin(GL_LINE_LOOP); + for (unsigned j=0; j!=actualDataC_->cells[i].size(); ++j) + { + idx = actualDataC_->cells[i][j]; + glVertex3d( actualDataC_->nodes[idx].x, actualDataC_->nodes[idx].y, actualDataC_->nodes[idx].z ); + } + glEnd(); + } + } + } +} + +// ci = cell index +// cv = vertex index in cell ci +void SurfacePlot::setColorFromVertexC(int node, bool skip) +{ + if (skip) + return; + + RGBA col = (*datacolor_p)( + actualDataC_->nodes[node].x, actualDataC_->nodes[node].y, actualDataC_->nodes[node].z); + + glColor4d(col.r, col.g, col.b, col.a); +} + +void SurfacePlot::createFloorDataC() +{ + switch (floorStyle()) + { + case FLOORDATA: + Data2FloorC(); + break; + case FLOORISO: + Isolines2FloorC(); + break; + default: + break; + } +} + +void SurfacePlot::Data2FloorC() +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + double zshift = actualDataC_->hull().minVertex.z; + int idx; + + for (unsigned i = 0; i!=actualDataC_->cells.size(); ++i) + { + glBegin(GL_POLYGON); + for (unsigned j=0; j!=actualDataC_->cells[i].size(); ++j) + { + idx = actualDataC_->cells[i][j]; + setColorFromVertexC(idx); + glVertex3d( actualDataC_->nodes[idx].x, actualDataC_->nodes[idx].y, zshift ); + } + glEnd(); + } +} + +void SurfacePlot::Isolines2FloorC() +{ + if (isolines() <= 0 || actualData_p->empty()) + return; + + double step = (actualData_p->hull().maxVertex.z - actualData_p->hull().minVertex.z) / isolines(); + + RGBA col; + + double zshift = actualData_p->hull().minVertex.z; + + TripleField nodes; + TripleField intersection; + + double lambda = 0; + + GLStateBewarer sb2(GL_LINE_SMOOTH, false); + + for (int k = 0; k != isolines(); ++k) + { + double val = zshift + k * step; + + for (unsigned i=0; i!=actualDataC_->cells.size(); ++i) + { + nodes.clear(); + unsigned cellnodes = actualDataC_->cells[i].size(); + for (unsigned j=0; j!=cellnodes; ++j) + { + nodes.push_back(actualDataC_->nodes[actualDataC_->cells[i][j]]); + } + + double diff = 0; + for (unsigned m = 0; m!=cellnodes; ++m) + { + unsigned mm = (m+1)%cellnodes; + if ((val>=nodes[m].z && val<=nodes[mm].z) || (val>=nodes[mm].z && val<=nodes[m].z)) + { + diff = nodes[mm].z - nodes[m].z; + + if (isPracticallyZero(diff)) // degenerated + { + intersection.push_back(nodes[m]); + intersection.push_back(nodes[mm]); + continue; + } + + lambda = (val - nodes[m].z) / diff; + intersection.push_back(Triple(nodes[m].x + lambda * (nodes[mm].x-nodes[m].x), nodes[m].y + lambda * (nodes[mm].y-nodes[m].y), val)); + } + } + + if (!intersection.empty()) + { + col = (*datacolor_p)(nodes[0].x,nodes[0].y,nodes[0].z); + glColor4d(col.r, col.g, col.b, col.a); + if (intersection.size()>2) + { + glBegin(GL_LINE_STRIP); + for (unsigned dd = 0; dd!=intersection.size(); ++dd) + { + glVertex3d(intersection[dd].x, intersection[dd].y, zshift); + } + glEnd(); + glBegin(GL_POINTS); + glVertex3d(intersection[0].x,intersection[0].y,zshift); + glEnd(); + } + else if (intersection.size() == 2) + { + glBegin(GL_LINES); + glVertex3d(intersection[0].x,intersection[0].y,zshift); + glVertex3d(intersection[1].x,intersection[1].y,zshift); + + // small pixel gap problem (see OpenGL spec.) + glVertex3d(intersection[1].x,intersection[1].y,zshift); + glVertex3d(intersection[0].x,intersection[0].y,zshift); + glEnd(); + } + + intersection.clear(); + } + } + } +} + +void SurfacePlot::createNormalsC() +{ + if (!normals() || actualData_p->empty()) + return; + + if (actualDataC_->nodes.size() != actualDataC_->normals.size()) + return; + Arrow arrow; + arrow.setQuality(normalQuality()); + + Triple basev, topv, norm; + + double diag = (actualData_p->hull().maxVertex-actualData_p->hull().minVertex).length() * normalLength(); + + RGBA col; + arrow.assign(*this); + arrow.drawBegin(); + for (unsigned i = 0; i != actualDataC_->normals.size(); ++i) + { + basev = actualDataC_->nodes[i]; + topv = basev + actualDataC_->normals[i]; + + norm = topv-basev; + norm.normalize(); + norm *= diag; + + arrow.setTop(basev+norm); + arrow.setColor((*datacolor_p)(basev.x,basev.y,basev.z)); + arrow.draw(basev); + } + arrow.drawEnd(); +} + +/*! + Convert user (non-rectangular) mesh based data to internal structure. + See also Qwt3D::TripleField and Qwt3D::CellField +*/ +bool SurfacePlot::loadFromData(TripleField const& data, CellField const& poly) +{ + actualDataG_->clear(); + actualData_p = actualDataC_; + + actualDataC_->nodes = data; + actualDataC_->cells = poly; + actualDataC_->normals = TripleField(actualDataC_->nodes.size()); + + unsigned i; + +// normals for the moment + Triple n, u, v; + for ( i = 0; i < poly.size(); ++i) + { + if (poly[i].size() < 3) + n = Triple(0,0,0); + else + { + for (unsigned j = 0; j < poly[i].size(); ++j) + { + unsigned jj = (j+1) % poly[i].size(); + unsigned pjj = (j) ? j-1 : poly[i].size()-1; + u = actualDataC_->nodes[poly[i][jj]]-actualDataC_->nodes[poly[i][j]]; + v = actualDataC_->nodes[poly[i][pjj]]-actualDataC_->nodes[poly[i][j]]; + n = normalizedcross(u,v); + actualDataC_->normals[poly[i][j]] += n; + } + } + } + for ( i = 0; i != actualDataC_->normals.size(); ++i) + { + actualDataC_->normals[i].normalize(); + } + + ParallelEpiped hull(Triple(DBL_MAX,DBL_MAX,DBL_MAX),Triple(-DBL_MAX,-DBL_MAX,-DBL_MAX)); + + for (i = 0; i!=data.size(); ++i) + { + if (data[i].x < hull.minVertex.x) + hull.minVertex.x = data[i].x; + if (data[i].y < hull.minVertex.y) + hull.minVertex.y = data[i].y; + if (data[i].z < hull.minVertex.z) + hull.minVertex.z = data[i].z; + + if (data[i].x > hull.maxVertex.x) + hull.maxVertex.x = data[i].x; + if (data[i].y > hull.maxVertex.y) + hull.maxVertex.y = data[i].y; + if (data[i].z > hull.maxVertex.z) + hull.maxVertex.z = data[i].z; + } + + actualDataC_->setHull(hull); + + updateData(); + updateNormals(); + createCoordinateSystem(); + + return true; +} + + diff --git a/lib/tqwtplot3d/src/qwt3d_mousekeyboard.cpp b/lib/tqwtplot3d/src/qwt3d_mousekeyboard.cpp new file mode 100644 index 0000000..800bc06 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_mousekeyboard.cpp @@ -0,0 +1,387 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include "qwt3d_plot.h" + +using namespace std; +using namespace Qwt3D; + + +/** + Standard mouse button Function. Prepares the call to mouseMoveEvent + \see mouseMoveEvent() +*/ +void Plot3D::mousePressEvent( QMouseEvent *e ) +{ + lastMouseMovePosition_ = e->pos(); + mpressed_ = true; +} + +/** + Standard mouse button Function. Completes the call to mouseMoveEvent + \see mouseMoveEvent() +*/ +void Plot3D::mouseReleaseEvent( QMouseEvent* ) +{ + mpressed_ = false; +} + +/** + Standard mouse button Function + \see assignMouse() +*/ +void Plot3D::mouseMoveEvent( QMouseEvent *e ) +{ + if (!mpressed_ || !mouseEnabled()) + { + e->ignore(); + return; + } + +#if QT_VERSION < 0x040000 + MouseState bstate = e->state(); +#else + MouseState bstate(e->buttons(),e->modifiers()); +#endif + + QPoint diff = e->pos() - lastMouseMovePosition_; + + setRotationMouse(bstate, 3, diff); + setScaleMouse(bstate, 5, diff); + setShiftMouse(bstate, 2, diff); + + lastMouseMovePosition_ = e->pos(); +} + +void Plot3D::setRotationMouse(MouseState bstate, double accel, QPoint diff) +{ + // Rotation + double w = max(1,width()); + double h = max(1,height()); + + double relx = accel*360 * diff.x() / w; + double relyz = accel*360 * diff.y() / h; + + double new_xrot = xRotation(); + double new_yrot = yRotation(); + double new_zrot = zRotation(); + + if ( bstate == xrot_mstate_ ) + new_xrot = round(xRotation() + relyz) % 360; + if ( bstate == yrot_mstate_ ) + new_yrot = round(yRotation() + relx) % 360; + if ( bstate == zrot_mstate_ ) + new_zrot = round(zRotation() + relx) % 360; + + setRotation(new_xrot, new_yrot, new_zrot); +} + +void Plot3D::setScaleMouse(MouseState bstate, double accel, QPoint diff) +{ + // Scale + double w = max(1,width()); + double h = max(1,height()); + + double relx = diff.x() * accel / w; relx = exp(relx) - 1; + double relyz = diff.y() * accel / h; relyz = exp(relyz) - 1; + + double new_xscale = xScale(); + double new_yscale = yScale(); + double new_zscale = zScale(); + + if ( bstate == xscale_mstate_) + new_xscale = max(0.0,xScale() + relx); + if ( bstate == yscale_mstate_) + new_yscale = max(0.0,yScale() - relyz); + if ( bstate == zscale_mstate_) + new_zscale = max(0.0,zScale() - relyz); + + setScale(new_xscale, new_yscale, new_zscale); + + if ( bstate == zoom_mstate_) + setZoom(max(0.0,zoom() - relyz)); +} + +void Plot3D::setShiftMouse(MouseState bstate, double accel, QPoint diff) +{ + // Shift + double w = max(1,width()); + double h = max(1,height()); + + double relx = diff.x() * accel / w; + double relyz = diff.y() * accel / h; + + double new_xshift = xViewportShift(); + double new_yshift = yViewportShift(); + + if ( bstate == xshift_mstate_) + new_xshift = xViewportShift() + relx; + if ( bstate == yshift_mstate_) + new_yshift = yViewportShift() - relyz; + + setViewportShift(new_xshift, new_yshift); +} + +/** + Standard wheel Function - zoom (wheel only) or z-scale (shift+wheel) +*/ +void Plot3D::wheelEvent( QWheelEvent *e ) +{ + if (!mouseEnabled()) + return; + + double accel = 0.05; + + double step = accel * e->delta() / WHEEL_DELTA ; + step = exp(step)-1; + +#if QT_VERSION < 0x040000 + if ( e->state() & Qt::ShiftButton ) +#else + if ( e->modifiers() & Qt::ShiftModifier ) +#endif + setScale(xScale(),yScale(), max(0.0,zScale() + step)); + else + setZoom(max(0.0,zoom() + step )); +} + +/** + Sets the key/mousebutton combination for data/coordinatesystem moves inside the widget\n\n + default behaviour:\n + + \verbatim + rotate around x axis: Qt::LeftButton + rotate around y axis: Qt::LeftButton | Qt::ShiftButton + rotate around z axis: Qt::LeftButton + scale x: Qt::LeftButton | Qt::AltButton + scale y: Qt::LeftButton | Qt::AltButton + scale z: Qt::LeftButton | Qt::AltButton | Qt::ShiftButton + zoom: Qt::LeftButton | Qt::AltButton | Qt::ControlButton + shifting along x: Qt::LeftButton | Qt::ControlButton + shifting along y: Qt::LeftButton | Qt::ControlButton + \endverbatim + + mouseMoveEvent() evaluates this function - if overridden, their usefulness becomes somehow limited +*/ +void Plot3D::assignMouse(MouseState xrot, MouseState yrot, MouseState zrot, + MouseState xscale, MouseState yscale, MouseState zscale, + MouseState zoom, MouseState xshift, MouseState yshift) +{ + xrot_mstate_ = xrot; + yrot_mstate_ = yrot; + zrot_mstate_ = zrot; + xscale_mstate_ = xscale; + yscale_mstate_ = yscale; + zscale_mstate_ = zscale; + zoom_mstate_ = zoom; + xshift_mstate_ = xshift; + yshift_mstate_ = yshift; +} + +/** +The function has no effect if you derive from Plot3D and overrides the mouse Function too careless. +In this case check first against mouseEnabled() in your version of mouseMoveEvent() and wheelEvent(). +A more fine grained input control can be achieved by combining assignMouse() with enableMouse(). +*/ +void Plot3D::enableMouse(bool val) {mouse_input_enabled_ = val;} + +/** +\see enableMouse() +*/ +void Plot3D::disableMouse(bool val) {mouse_input_enabled_ = !val;} +bool Plot3D::mouseEnabled() const {return mouse_input_enabled_;} + + + + +void Plot3D::keyPressEvent( QKeyEvent *e ) +{ + if (!keyboardEnabled()) + { + e->ignore(); + return; + } + +#if QT_VERSION < 0x040000 + int bstate = e->state() & Qt::KeyButtonMask; // filter kbd modifier only + KeyboardState keyseq = bstate + e->key(); +#else + KeyboardState keyseq(e->key(), e->modifiers()); +#endif + + setRotationKeyboard(keyseq, kbd_rot_speed_); + setScaleKeyboard(keyseq, kbd_scale_speed_); + setShiftKeyboard(keyseq, kbd_shift_speed_); +} + +void Plot3D::setRotationKeyboard(KeyboardState kseq, double speed) +{ + // Rotation + double w = max(1,width()); + double h = max(1,height()); + + double relx = speed*360 / w; + double relyz = speed*360 / h; + + double new_xrot = xRotation(); + double new_yrot = yRotation(); + double new_zrot = zRotation(); + + if ( kseq == xrot_kstate_[0] ) + new_xrot = round(xRotation() + relyz) % 360; + if ( kseq == xrot_kstate_[1] ) + new_xrot = round(xRotation() - relyz) % 360; + if ( kseq == yrot_kstate_[0] ) + new_yrot = round(yRotation() + relx) % 360; + if ( kseq == yrot_kstate_[1] ) + new_yrot = round(yRotation() - relx) % 360; + if ( kseq == zrot_kstate_[0] ) + new_zrot = round(zRotation() + relx) % 360; + if ( kseq == zrot_kstate_[1] ) + new_zrot = round(zRotation() - relx) % 360; + + setRotation(new_xrot, new_yrot, new_zrot); +} + +void Plot3D::setScaleKeyboard(KeyboardState kseq, double speed) +{ + // Scale + double w = max(1,width()); + double h = max(1,height()); + + double relx = speed / w; relx = exp(relx) - 1; + double relyz = speed / h; relyz = exp(relyz) - 1; + + double new_xscale = xScale(); + double new_yscale = yScale(); + double new_zscale = zScale(); + + if ( kseq == xscale_kstate_[0]) + new_xscale = max(0.0,xScale() + relx); + if ( kseq == xscale_kstate_[1]) + new_xscale = max(0.0,xScale() - relx); + if ( kseq == yscale_kstate_[0]) + new_yscale = max(0.0,yScale() - relyz); + if ( kseq == yscale_kstate_[1]) + new_yscale = max(0.0,yScale() + relyz); + if ( kseq == zscale_kstate_[0]) + new_zscale = max(0.0,zScale() - relyz); + if ( kseq == zscale_kstate_[1]) + new_zscale = max(0.0,zScale() + relyz); + + setScale(new_xscale, new_yscale, new_zscale); + + if ( kseq == zoom_kstate_[0]) + setZoom(max(0.0,zoom() - relyz)); + if ( kseq == zoom_kstate_[1]) + setZoom(max(0.0,zoom() + relyz)); +} + +void Plot3D::setShiftKeyboard(KeyboardState kseq, double speed) +{ + // Shift + double w = max(1,width()); + double h = max(1,height()); + + double relx = speed / w; + double relyz = speed / h; + + double new_xshift = xViewportShift(); + double new_yshift = yViewportShift(); + + if ( kseq == xshift_kstate_[0]) + new_xshift = xViewportShift() + relx; + if ( kseq == xshift_kstate_[1]) + new_xshift = xViewportShift() - relx; + if ( kseq == yshift_kstate_[0]) + new_yshift = yViewportShift() - relyz; + if ( kseq == yshift_kstate_[1]) + new_yshift = yViewportShift() + relyz; + + setViewportShift(new_xshift, new_yshift); +} + +/** + Sets the keybutton combination for data/coordinatesystem moves inside the widget\n\n + default behaviour:\n + + \verbatim + rotate around x axis: [Key_Down, Key_Up] + rotate around y axis: SHIFT+[Key_Right, Key_Left] + rotate around z axis: [Key_Right, Key_Left] + scale x: ALT+[Key_Right, Key_Left] + scale y: ALT+[Key_Up, Key_Down] + scale z: ALT+SHIFT[Key_Down, Key_Up] + zoom: ALT+CTRL+[Key_Down, Key_Up] + shifting along x: CTRL+[Key_Right, Key_Left] + shifting along z: CTRL+[Key_Down, Key_Up] + \endverbatim +*/ +void Plot3D::assignKeyboard( + KeyboardState xrot_n, KeyboardState xrot_p + ,KeyboardState yrot_n, KeyboardState yrot_p + ,KeyboardState zrot_n, KeyboardState zrot_p + ,KeyboardState xscale_n, KeyboardState xscale_p + ,KeyboardState yscale_n, KeyboardState yscale_p + ,KeyboardState zscale_n, KeyboardState zscale_p + ,KeyboardState zoom_n, KeyboardState zoom_p + ,KeyboardState xshift_n, KeyboardState xshift_p + ,KeyboardState yshift_n, KeyboardState yshift_p + ) +{ + xrot_kstate_[0] = xrot_n; + yrot_kstate_[0] = yrot_n; + zrot_kstate_[0] = zrot_n; + xrot_kstate_[1] = xrot_p; + yrot_kstate_[1] = yrot_p; + zrot_kstate_[1] = zrot_p; + + xscale_kstate_[0] = xscale_n; + yscale_kstate_[0] = yscale_n; + zscale_kstate_[0] = zscale_n; + xscale_kstate_[1] = xscale_p; + yscale_kstate_[1] = yscale_p; + zscale_kstate_[1] = zscale_p; + + zoom_kstate_[0] = zoom_n; + xshift_kstate_[0] = xshift_n; + yshift_kstate_[0] = yshift_n; + zoom_kstate_[1] = zoom_p; + xshift_kstate_[1] = xshift_p; + yshift_kstate_[1] = yshift_p; +} + +/** +The function has no effect if you derive from Plot3D and overrides the keyboard Functions too careless. +In this case check first against keyboardEnabled() in your version of keyPressEvent() +A more fine grained input control can be achieved by combining assignKeyboard() with enableKeyboard(). +*/ +void Plot3D::enableKeyboard(bool val) {kbd_input_enabled_ = val;} + +/** +\see enableKeyboard() +*/ +void Plot3D::disableKeyboard(bool val) {kbd_input_enabled_ = !val;} +bool Plot3D::keyboardEnabled() const {return kbd_input_enabled_;} + +/** +Values < 0 are ignored. Default is (3,5,5) +*/ +void Plot3D::setKeySpeed(double rot, double scale, double shift) +{ + if (rot > 0) + kbd_rot_speed_ = rot; + if (scale > 0) + kbd_scale_speed_ = scale; + if (shift > 0) + kbd_shift_speed_ = shift; +} + +void Plot3D::keySpeed(double& rot, double& scale, double& shift) const +{ + rot = kbd_rot_speed_; + scale = kbd_scale_speed_; + shift = kbd_shift_speed_; +} diff --git a/lib/tqwtplot3d/src/qwt3d_movements.cpp b/lib/tqwtplot3d/src/qwt3d_movements.cpp new file mode 100644 index 0000000..73ff9b1 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_movements.cpp @@ -0,0 +1,106 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include +#include "qwt3d_plot.h" + +using namespace Qwt3D; + + +/** + Set the rotation angle of the object. If you look along the respective axis towards ascending values, + the rotation is performed in mathematical \e negative sense + \param xVal angle in \e degree to rotate around the X axis + \param yVal angle in \e degree to rotate around the Y axis + \param zVal angle in \e degree to rotate around the Z axis +*/ +void Plot3D::setRotation( double xVal, double yVal, double zVal ) +{ + if (xRot_ == xVal && yRot_ == yVal && zRot_ == zVal) + return; + + xRot_ = xVal; + yRot_ = yVal; + zRot_ = zVal; + + updateGL(); + emit rotationChanged(xVal, yVal, zVal); +} + +/** + Set the shift in object (world) coordinates. + \param xVal shift along (world) X axis + \param yVal shift along (world) Y axis + \param zVal shift along (world) Z axis + \see setViewportShift() +*/ +void Plot3D::setShift( double xVal, double yVal, double zVal ) +{ + if (xShift_ == xVal && yShift_ == yVal && zShift_ == zVal) + return; + + xShift_ = xVal; + yShift_ = yVal; + zShift_ = zVal; + updateGL(); + emit shiftChanged(xVal, yVal, zVal); +} + +/** + Performs shifting along screen axes. + The shift moves points inside a sphere, + which encloses the unscaled and unzoomed data + by multiples of the spheres diameter + + \param xVal shift along (view) X axis + \param yVal shift along (view) Y axis + \see setShift() +*/ +void Plot3D::setViewportShift( double xVal, double yVal ) +{ + if (xVPShift_ == xVal && yVPShift_ == yVal) + return; + + xVPShift_ = xVal; + yVPShift_ = yVal; + + updateGL(); + emit vieportShiftChanged(xVPShift_, yVPShift_); +} + +/** + Set the scale in object (world) coordinates. + \param xVal scaling for X values + \param yVal scaling for Y values + \param zVal scaling for Z values + + A respective value of 1 represents no scaling; +*/ +void Plot3D::setScale( double xVal, double yVal, double zVal ) +{ + if (xScale_ == xVal && yScale_ == yVal && zScale_ == zVal) + return; + + xScale_ = (xVal < DBL_EPSILON ) ? DBL_EPSILON : xVal; + yScale_ = (yVal < DBL_EPSILON ) ? DBL_EPSILON : yVal; + zScale_ = (zVal < DBL_EPSILON ) ? DBL_EPSILON : zVal; + + updateGL(); + emit scaleChanged(xVal, yVal, zVal); +} + +/** + Set the (zoom in addition to scale). + \param val zoom value (value == 1 indicates no zooming) +*/ +void Plot3D::setZoom( double val ) +{ + if (zoom_ == val) + return; + + zoom_ = (val < DBL_EPSILON ) ? DBL_EPSILON : val; + updateGL(); + emit zoomChanged(val); +} diff --git a/lib/tqwtplot3d/src/qwt3d_parametricsurface.cpp b/lib/tqwtplot3d/src/qwt3d_parametricsurface.cpp new file mode 100644 index 0000000..c3103c9 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_parametricsurface.cpp @@ -0,0 +1,104 @@ +#include "qwt3d_parametricsurface.h" +#include "qwt3d_surfaceplot.h" + +using namespace Qwt3D; + +ParametricSurface::ParametricSurface() +:GridMapping() +{ +} + +ParametricSurface::ParametricSurface(SurfacePlot& pw) +:GridMapping() +{ + plotwidget_p = &pw; + uperiodic_ = false; + vperiodic_ = false; +} + +ParametricSurface::ParametricSurface(SurfacePlot* pw) +:GridMapping() +{ + plotwidget_p = pw; + uperiodic_ = false; + vperiodic_ = false; +} + +void ParametricSurface::setPeriodic(bool u, bool v) +{ + uperiodic_ = u; + vperiodic_ = v; +} + +void ParametricSurface::assign(SurfacePlot& plotWidget) +{ + if (&plotWidget != plotwidget_p) + plotwidget_p = &plotWidget; +} + +void ParametricSurface::assign(SurfacePlot* plotWidget) +{ + if (plotWidget != plotwidget_p) + plotwidget_p = plotWidget; +} + +/** +For plotWidget != 0 the function permanently assigns her argument (In fact, assign(plotWidget) is called) +*/ +bool ParametricSurface::create() +{ + if ((umesh_p<=2) || (vmesh_p<=2) || !plotwidget_p) + return false; + + /* allocate some space for the mesh */ + Triple** data = new Triple* [umesh_p] ; + + unsigned i,j; + for ( i = 0; i < umesh_p; i++) + { + data[i] = new Triple [vmesh_p]; + } + + /* get the data */ + + double du = (maxu_p - minu_p) / (umesh_p - 1); + double dv = (maxv_p - minv_p) / (vmesh_p - 1); + + for (i = 0; i < umesh_p; ++i) + { + for (j = 0; j < vmesh_p; ++j) + { + data[i][j] = operator()(minu_p + i*du, minv_p + j*dv); + + if (data[i][j].x > range_p.maxVertex.x) + data[i][j].x = range_p.maxVertex.x; + else if (data[i][j].y > range_p.maxVertex.y) + data[i][j].y = range_p.maxVertex.y; + else if (data[i][j].z > range_p.maxVertex.z) + data[i][j].z = range_p.maxVertex.z; + else if (data[i][j].x < range_p.minVertex.x) + data[i][j].x = range_p.minVertex.x; + else if (data[i][j].y < range_p.minVertex.y) + data[i][j].y = range_p.minVertex.y; + else if (data[i][j].z < range_p.minVertex.z) + data[i][j].z = range_p.minVertex.z; + } + } + + ((SurfacePlot*)plotwidget_p)->loadFromData(data, umesh_p, vmesh_p, uperiodic_, vperiodic_); + + for ( i = 0; i < umesh_p; i++) + { + delete [] data[i]; + } + + delete [] data; + + return true; +} + +bool ParametricSurface::create(SurfacePlot& pl) +{ + assign(pl); + return create(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_plot.cpp b/lib/tqwtplot3d/src/qwt3d_plot.cpp new file mode 100644 index 0000000..e61b125 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_plot.cpp @@ -0,0 +1,498 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4305 ) +#pragma warning ( disable : 4786 ) +#endif + +#include "qwt3d_plot.h" +#include "qwt3d_enrichment.h" + + +using namespace Qwt3D; + +/*! + This should be the first call in your derived classes constructors. +*/ +#if QT_VERSION < 0x040000 +Plot3D::Plot3D( QWidget* parent, const char* name ) + : QGLWidget( parent, name ) +#else +Plot3D::Plot3D( QWidget * parent, const QGLWidget * shareWidget) + : QGLWidget( parent, shareWidget) +#endif +{ + initializedGL_ = false; + renderpixmaprequest_ = false; + xRot_ = yRot_ = zRot_ = 0.0; // default object rotation + + xShift_ = yShift_ = zShift_ = xVPShift_ = yVPShift_ = 0.0; + xScale_ = yScale_ = zScale_ = 1.0; + zoom_ = 1; + ortho_ = true; + plotstyle_ = FILLEDMESH; + userplotstyle_p = 0; + shading_ = GOURAUD; + floorstyle_ = NOFLOOR; + isolines_ = 10; + displaylegend_ = false; + smoothdatamesh_p = false; + actualData_p = 0; + + lastMouseMovePosition_ = QPoint(0,0); + mpressed_ = false; + mouse_input_enabled_ = true; + + setPolygonOffset(0.5); + setMeshColor(RGBA(0.0,0.0,0.0)); + setMeshLineWidth(1); + setBackgroundColor(RGBA(1.0,1.0,1.0,1.0)); + + displaylists_p = std::vector(DisplayListSize); + for (unsigned k=0; k!=displaylists_p.size(); ++k) + { + displaylists_p[k] = 0; + } + + datacolor_p = new StandardColor(this, 100); + title_.setFont("Courier", 16, QFont::Bold); + title_.setString(""); + + setTitlePosition(0.95); + + kbd_input_enabled_ = true; + +#if QT_VERSION < 0x040000 + setFocusPolicy(QWidget::StrongFocus); + assignMouse(Qt::LeftButton, + Qt::LeftButton | Qt::ShiftButton, + Qt::LeftButton, + Qt::LeftButton | Qt::AltButton, + Qt::LeftButton | Qt::AltButton, + Qt::LeftButton | Qt::AltButton | Qt::ShiftButton, + Qt::LeftButton | Qt::AltButton | Qt::ControlButton, + Qt::LeftButton | Qt::ControlButton, + Qt::LeftButton | Qt::ControlButton); + + + assignKeyboard(Qt::Key_Down, Qt::Key_Up, + Qt::ShiftButton + Qt::Key_Right, Qt::ShiftButton + Qt::Key_Left, + Qt::Key_Right, Qt::Key_Left, + Qt::AltButton + Qt::Key_Right, Qt::AltButton + Qt::Key_Left, + Qt::AltButton + Qt::Key_Down, Qt::AltButton + Qt::Key_Up, + Qt::AltButton + Qt::ShiftButton + Qt::Key_Down, Qt::AltButton + Qt::ShiftButton + Qt::Key_Up, + Qt::AltButton + Qt::ControlButton + Qt::Key_Down, Qt::AltButton + Qt::ControlButton + Qt::Key_Up, + Qt::ControlButton + Qt::Key_Right, Qt::ControlButton + Qt::Key_Left, + Qt::ControlButton + Qt::Key_Down, Qt::ControlButton + Qt::Key_Up + ); +#else + setFocusPolicy(Qt::StrongFocus); + assignMouse(Qt::LeftButton, + MouseState(Qt::LeftButton, Qt::ShiftModifier), + Qt::LeftButton, + MouseState(Qt::LeftButton, Qt::AltModifier), + MouseState(Qt::LeftButton, Qt::AltModifier), + MouseState(Qt::LeftButton, Qt::AltModifier | Qt::ShiftModifier), + MouseState(Qt::LeftButton, Qt::AltModifier | Qt::ControlModifier), + MouseState(Qt::LeftButton, Qt::ControlModifier), + MouseState(Qt::LeftButton, Qt::ControlModifier) + ); + + + assignKeyboard(Qt::Key_Down, Qt::Key_Up, + KeyboardState(Qt::Key_Right, Qt::ShiftModifier), KeyboardState(Qt::Key_Left, Qt::ShiftModifier), + Qt::Key_Right, Qt::Key_Left, + KeyboardState(Qt::Key_Right, Qt::AltModifier), KeyboardState(Qt::Key_Left, Qt::AltModifier), + KeyboardState(Qt::Key_Down, Qt::AltModifier), KeyboardState(Qt::Key_Up, Qt::AltModifier), + KeyboardState(Qt::Key_Down, Qt::AltModifier|Qt::ShiftModifier), KeyboardState(Qt::Key_Up, Qt::AltModifier|Qt::ShiftModifier), + KeyboardState(Qt::Key_Down, Qt::AltModifier|Qt::ControlModifier), KeyboardState(Qt::Key_Up, Qt::AltModifier|Qt::ControlModifier), + KeyboardState(Qt::Key_Right, Qt::ControlModifier), KeyboardState(Qt::Key_Left, Qt::ControlModifier), + KeyboardState(Qt::Key_Down, Qt::ControlModifier), KeyboardState(Qt::Key_Up, Qt::ControlModifier) + ); +#endif + setKeySpeed(3,5,5); + + legend_.setLimits(0, 100); + legend_.setMajors(10); + legend_.setMinors(2); + legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left); + + lighting_enabled_ = false; + disableLighting(); + lights_ = std::vector(8); +} + +/*! + Release allocated resources +*/ + +Plot3D::~Plot3D() +{ + makeCurrent(); + SaveGlDeleteLists( displaylists_p[0], displaylists_p.size() ); + datacolor_p->destroy(); + delete userplotstyle_p; + for (ELIT it = elist_p.begin(); it!=elist_p.end(); ++it) + delete (*it); + + elist_p.clear(); +} + + +/*! + Set up the OpenGL rendering state +*/ +void Plot3D::initializeGL() +{ + glEnable( GL_BLEND ); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + + // Set up the lights + + disableLighting(); + + GLfloat whiteAmb[4] = {1.0, 1.0, 1.0, 1.0}; + + setLightShift(0, 0, 3000); + glEnable(GL_COLOR_MATERIAL); + + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, whiteAmb); + + setMaterialComponent(GL_DIFFUSE, 1.0); + setMaterialComponent(GL_SPECULAR, 0.3); + setMaterialComponent(GL_SHININESS, 5.0); + setLightComponent(GL_DIFFUSE, 1.0); + setLightComponent(GL_SPECULAR, 1.0); + + initializedGL_ = true; + if (renderpixmaprequest_) + { + updateData(); + renderpixmaprequest_ = false; + } +} + +/*! + Paint the widgets content. +*/ +void Plot3D::paintGL() +{ + glClearColor(bgcolor_.r, bgcolor_.g, bgcolor_.b, bgcolor_.a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + applyLights(); + + glRotatef( -90, 1.0, 0.0, 0.0 ); + glRotatef( 0.0, 0.0, 1.0, 0.0 ); + glRotatef( 0.0, 0.0, 0.0, 1.0 ); + + if (displaylegend_) + { + legend_.draw(); + } + title_.setRelPosition(titlerel_, titleanchor_); + title_.draw(); + + Triple beg = coordinates_p.first(); + Triple end = coordinates_p.second(); + + Triple center = beg + (end-beg) / 2; + double radius = (center-beg).length(); + + glLoadIdentity(); + + glRotatef( xRot_-90, 1.0, 0.0, 0.0 ); + glRotatef( yRot_, 0.0, 1.0, 0.0 ); + glRotatef( zRot_, 0.0, 0.0, 1.0 ); + + glScalef( zoom_ * xScale_, zoom_ * yScale_, zoom_ * zScale_ ); + + glTranslatef(xShift_-center.x, yShift_-center.y, zShift_-center.z); + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + + if (beg != end) + { + if (ortho_) + { + glOrtho( -radius, +radius, -radius, +radius, 0, 40 * radius); + } + else + { + glFrustum( -radius, +radius, -radius, +radius, 5 * radius, 400 * radius ); + } + } + else + { + if (ortho_) + glOrtho( -1.0, 1.0, -1.0, 1.0, 10.0, 100.0 ); + else + glFrustum( -1.0, 1.0, -1.0, 1.0, 10.0, 100.0 ); + } + + glTranslatef( xVPShift_ * 2 * radius , yVPShift_ * 2 * radius , -7 * radius ); + + if (lighting_enabled_) + glEnable(GL_NORMALIZE); + + for (unsigned i=0; i!= displaylists_p.size(); ++i) + { + if (i!=LegendObject) + glCallList( displaylists_p[i] ); + } + coordinates_p.draw(); + + if (lighting_enabled_) + glDisable(GL_NORMALIZE); + + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); +} + + +/*! + Set up the OpenGL view port +*/ +void Plot3D::resizeGL( int w, int h ) +{ + glViewport( 0, 0, w, h ); + paintGL(); +} + +/*! + Reimplemented from QGLWidget +*/ +QPixmap Plot3D::renderPixmap(int w/* =0 */, int h/* =0 */, bool useContext/* =false */) +{ + renderpixmaprequest_ = true; + return QGLWidget::renderPixmap(w,h,useContext); +} + +/*! + Create a coordinate system with generating corners beg and end +*/ +void Plot3D::createCoordinateSystem( Triple beg, Triple end ) +{ + if (beg != coordinates_p.first() || end != coordinates_p.second()) + coordinates_p.init(beg, end); +} + +/*! + Create a coordinate system from data +*/ +void Plot3D::createCoordinateSystem() +{ + calculateHull(); + Triple beg = hull().minVertex; // Irix 6.5 compiler bug + Triple end = hull().maxVertex; + createCoordinateSystem(beg, end); +} + +/*! + Show a color legend +*/ +void Plot3D::showColorLegend( bool show ) +{ + displaylegend_ = show; + if (show) + datacolor_p->createVector(legend_.colors); + updateGL(); +} + +void Plot3D::setMeshColor(RGBA rgba) +{ + meshcolor_ = rgba; +} + +void Plot3D::setBackgroundColor(RGBA rgba) +{ + bgcolor_ = rgba; +} + + +/*! + assign a new coloring object for the data. +*/ +void Plot3D::setDataColor( Color* col ) +{ + Q_ASSERT(datacolor_p); + + datacolor_p->destroy(); + datacolor_p = col; +} + +/*! + Set up ortogonal or perspective mode and updates widget +*/ +void Plot3D::setOrtho( bool val ) +{ + if (val == ortho_) + return; + ortho_ = val; + updateGL(); + + emit projectionChanged(val); +} + +/*! + Set style of coordinate system +*/ +void Plot3D::setCoordinateStyle(COORDSTYLE st) +{ + coordinates_p.setStyle(st); + updateGL(); +} + +/*! + Set plotstyle for the standard plotting types. An argument of value Qwt3D::USER + is ignored. +*/ +void Plot3D::setPlotStyle( PLOTSTYLE val ) +{ + if (val == Qwt3D::USER) + return; + delete userplotstyle_p; + userplotstyle_p = 0; + plotstyle_ = val; +} + +/*! + Set plotstyle to Qwt3D::USER and an associated enrichment object. +*/ +Qwt3D::Enrichment* Plot3D::setPlotStyle( Qwt3D::Enrichment const& obj ) +{ + if (&obj == userplotstyle_p) + return userplotstyle_p; + + delete userplotstyle_p; + userplotstyle_p = obj.clone(); + plotstyle_ = Qwt3D::USER; + return userplotstyle_p; +} + +/*! + Set shading style +*/ +void Plot3D::setShading( SHADINGSTYLE val ) +{ + if (val == shading_) + return; + + shading_ = val; + + switch (shading_) + { + case FLAT: + glShadeModel(GL_FLAT); + break; + case GOURAUD: + glShadeModel(GL_SMOOTH); + break; + default: + break; + } + updateGL(); +} + +/*! + Set number of isolines. The lines are equidistant between minimal and maximal Z value +*/ +void Plot3D::setIsolines(int steps) +{ + if (steps < 0) + return; + + isolines_ = steps; +} + +/*! + Set Polygon offset. The function affects the OpenGL rendering process. + Try different values for surfaces with polygons only and with mesh and polygons +*/ +void Plot3D::setPolygonOffset( double val ) +{ + polygonOffset_ = val; +} + +void Plot3D::setMeshLineWidth( double val ) +{ + Q_ASSERT(val>=0); + + if (val < 0) + return; + + meshLineWidth_ = val; +} + + +/*! +Set relative caption position (0.5,0.5) means, the anchor point lies in the center of the screen +*/ +void Plot3D::setTitlePosition(double rely, double relx, Qwt3D::ANCHOR anchor) +{ + titlerel_.y = (rely<0 || rely>1) ? 0.5 : rely; + titlerel_.x = (relx<0 || relx>1) ? 0.5 : relx; + + titleanchor_ = anchor; +} + +/*! +Set caption font +*/ +void Plot3D::setTitleFont(const QString& family, int pointSize, int weight, bool italic) +{ + title_.setFont(family, pointSize, weight, italic); +} + +Enrichment* Plot3D::addEnrichment(Enrichment const& e) +{ + if ( elist_p.end() == std::find( elist_p.begin(), elist_p.end(), &e ) ) + elist_p.push_back(e.clone()); + return elist_p.back(); +} + +bool Plot3D::degrade(Enrichment* e) +{ + ELIT it = std::find(elist_p.begin(), elist_p.end(), e); + + if ( it != elist_p.end() ) + { + delete (*it); + elist_p.erase(it); + return true; + } + return false; +} + +void Plot3D::createEnrichments() +{ + for (ELIT it = elist_p.begin(); it!=elist_p.end(); ++it) + { + this->createEnrichment(**it); + } +} + +/*! + Update OpenGL data representation +*/ +void Plot3D::updateData() +{ + makeCurrent(); + GLStateBewarer dt(GL_DEPTH_TEST, true); + GLStateBewarer ls(GL_LINE_SMOOTH, true); + + calculateHull(); + + SaveGlDeleteLists(displaylists_p[DataObject], 1); // nur Daten + + displaylists_p[DataObject] = glGenLists(1); + glNewList(displaylists_p[DataObject], GL_COMPILE); + + this->createEnrichments(); + this->createData(); + + glEndList(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_scale.cpp b/lib/tqwtplot3d/src/qwt3d_scale.cpp new file mode 100644 index 0000000..ecac454 --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_scale.cpp @@ -0,0 +1,304 @@ +#include "qwt3d_scale.h" + +using namespace Qwt3D; + +Scale::Scale() +: start_p(0.), stop_p(0.), + majorintervals_p(0), minorintervals_p(0), + mstart_p(0.), mstop_p(0.) +{ +} + +/*! The function maps the double value at tic-position idx to a final +representation. The default return value is simply the tic values QString +representation. Overwrite this function, if you plan to transform the value +in some way. See e.g. LogScale::ticLabel. +\param idx the current major tic index +\return The QString representation for the value corresponding to a valid index, +an empty QString else. +*/ +QString Scale::ticLabel(unsigned int idx) const +{ + if (idxstop_p) +// return; + + majors_p.push_back(mstart_p); + + // remaining tics + for (i = 1; i <= majorintervals_p; ++i) + { + double t = double(i) / majorintervals_p; + runningval = mstart_p + t * interval; + if (runningval>stop_p) + break; + if (isPracticallyZero(mstart_p, -t*interval)) // prevent rounding errors near 0 + runningval = 0.0; + majors_p.push_back(runningval); + } + majorintervals_p = majors_p.size(); + if (majorintervals_p) + --majorintervals_p; + + + // minors + + if (!majorintervals_p || !minorintervals_p) // no valid interval + { + minorintervals_p = 0; + return; + } + + // start_p mstart_p + // |_____________|_____ _ _ _ + + double step = (majors_p[1]-majors_p[0]) / minorintervals_p; + if (isPracticallyZero(step)) + return; + + runningval = mstart_p-step; + while (runningval>start_p) + { + minors_p.push_back(runningval); + runningval -= step; + } + + // mstart_p mstop_p + // ________|_____ _ _ _ _ _ ___|__________ + + for (i=0; i!=majorintervals_p; ++i) + { + runningval = majors_p[i] + step; + for (int j=0; j!=minorintervals_p; ++j) + { + minors_p.push_back(runningval); + runningval += step; + } + } + + // mstop_p stop_p + // _ _ _|_____________| + + runningval = mstop_p + step; + while (runningval DBL_MAX_10_EXP) + stop_p = DBL_MAX_10_EXP; + + double interval = stop_p-start_p; + if (interval<=0) + return; + + double runningval = floor(start_p); + while(runningval<=stop_p) + { + if (runningval>=start_p) + majors_p.push_back(runningval); + ++runningval; + } + majorintervals_p = majors_p.size(); + if (majorintervals_p) + --majorintervals_p; + + if (majors_p.size()<1) // not even a single major tic + { + return; + } + + + // minors + + // start_p mstart_p + // |_____________|_____ _ _ _ + + double k; + int step; + setupCounter(k,step); + runningval = log10(k)+(majors_p[0]-1); + while (runningval>start_p && k>1) + { + minors_p.push_back(runningval); + k -=step; + runningval = log10(k)+(majors_p[0]-1); + } + + // mstart_p mstop_p + // ________|_____ _ _ _ _ _ ___|__________ + + for (int i=0; i!=majorintervals_p; ++i) + { + setupCounter(k,step); + runningval = log10(k)+(majors_p[i]); + while (k>1) + { + minors_p.push_back(runningval); + k-=step; + runningval = log10(k)+(majors_p[i]); + } + } + + // mstop_p stop_p + // _ _ _|_____________| + + setupCounter(k,step); + runningval = log10(k)+(majors_p.back()); + do + { + k-=step; + runningval = log10(k)+(majors_p.back()); + } + while(runningval>=stop_p); + while (k>1) + { + minors_p.push_back(runningval); + k-=step; + runningval = log10(k)+(majors_p.back()); + } +} + +/*! +Sets the minor intervals for the logarithmic scale. Only values of 9,5,3 or 2 +are accepted as arguments. They will produce mantissa sets of {2,3,4,5,6,7,8,9}, +{2,4,6,8}, {2,5} or {5} respectively. +*/ +void LogScale::setMinors(int val) +{ + if ((val == 2) || (val == 3) || (val == 5) || (val == 9)) + minorintervals_p = val; +} + +LogScale::LogScale() +{ + minorintervals_p = 9; +} + +//! Returns a power of 10 associated to the major value at index idx. +QString LogScale::ticLabel(unsigned int idx) const +{ + if (idx 1 are ignored +*/ +void SurfacePlot::setNormalLength(double val) +{ + if (val<0 || val>1) + return; + normalLength_p = val; +} + +/** +Values < 3 are ignored +*/ +void SurfacePlot::setNormalQuality(int val) +{ + if (val<3) + return; + normalQuality_p = val; +} + +/** + Calculates the smallest x-y-z parallelepiped enclosing the data. + It can be accessed by hull(); +*/ +void SurfacePlot::calculateHull() +{ + if (actualData_p->empty()) + return; + setHull(actualData_p->hull()); +} + +/*! + Sets data resolution (res == 1 original resolution) and updates widget + If res < 1, the function does nothing +*/ +void SurfacePlot::setResolution( int res ) +{ + if (!actualData_p || actualData_p->datatype == Qwt3D::POLYGON) + return; + + if ((resolution_p == res) || res < 1) + return; + + resolution_p = res; + updateNormals(); + updateData(); + if (initializedGL()) + updateGL(); + + emit resolutionChanged(res); +} + +void SurfacePlot::updateNormals() +{ + SaveGlDeleteLists(displaylists_p[NormalObject], 1); + + if (plotStyle() == NOPLOT && !normals() || !actualData_p) + return; + + displaylists_p[NormalObject] = glGenLists(1); + glNewList(displaylists_p[NormalObject], GL_COMPILE); + + if (actualData_p->datatype == Qwt3D::POLYGON) + createNormalsC(); + else if (actualData_p->datatype == Qwt3D::GRID) + createNormalsG(); + + glEndList(); +} + +void SurfacePlot::createData() +{ + if (!actualData_p) + return; + if (actualData_p->datatype == Qwt3D::POLYGON) + createDataC(); + else if (actualData_p->datatype == Qwt3D::GRID) + createDataG(); +} + + +void SurfacePlot::createFloorData() +{ + if (!actualData_p) + return; + if (actualData_p->datatype == Qwt3D::POLYGON) + createFloorDataC(); + else if (actualData_p->datatype == Qwt3D::GRID) + createFloorDataG(); +} + +/** + The returned value is not affected by resolution(). The pair gives (columns,rows) for grid data +, (number of cells,1) for free formed data (datatype() == POLYGON) and (0,0) else +*/ +pair SurfacePlot::facets() const +{ + if (!hasData()) + return pair(0,0); + + if (actualData_p->datatype == Qwt3D::POLYGON) + return pair(int(actualDataC_->cells.size()), 1); + else if (actualData_p->datatype == Qwt3D::GRID) + return pair(actualDataG_->columns(), actualDataG_->rows()); + else + return pair(0,0); +} + +void SurfacePlot::createPoints() +{ + Dot pt; + createEnrichment(pt); +} + +void SurfacePlot::createEnrichment(Enrichment& p) +{ + if (!actualData_p) + return; + + //todo future work + if (p.type() != Enrichment::VERTEXENRICHMENT) + return; + + p.assign(*this); + p.drawBegin(); + + VertexEnrichment* ve = (VertexEnrichment*)&p; + if (actualData_p->datatype == Qwt3D::POLYGON) + { + for (unsigned i = 0; i != actualDataC_->normals.size(); ++i) + ve->draw(actualDataC_->nodes[i]); + } + else if (actualData_p->datatype == Qwt3D::GRID) + { + int step = resolution(); + for (int i = 0; i <= actualDataG_->columns() - step; i += step) + for (int j = 0; j <= actualDataG_->rows() - step; j += step) + ve->draw(Triple(actualDataG_->vertices[i][j][0], + actualDataG_->vertices[i][j][1], + actualDataG_->vertices[i][j][2])); + } + p.drawEnd(); +} diff --git a/lib/tqwtplot3d/src/qwt3d_types.cpp b/lib/tqwtplot3d/src/qwt3d_types.cpp new file mode 100644 index 0000000..b6f3a4d --- /dev/null +++ b/lib/tqwtplot3d/src/qwt3d_types.cpp @@ -0,0 +1,222 @@ +#if defined(_MSC_VER) /* MSVC Compiler */ +#pragma warning ( disable : 4786 ) +#endif + +#include // qsort +#include +#include +#include "qwt3d_types.h" + +using namespace Qwt3D; + +#ifndef QWT3D_NOT_FOR_DOXYGEN + +namespace { + // convex hull + + typedef double coordinate_type; + + int ccw(coordinate_type **P, int i, int j, int k) { + coordinate_type a = P[i][0] - P[j][0], + b = P[i][1] - P[j][1], + c = P[k][0] - P[j][0], + d = P[k][1] - P[j][1]; + return a*d - b*c <= 0; /* true if points i, j, k counterclockwise */ + } + + +#define CMPM(c,A,B) \ + v = (*(coordinate_type**)A)[c] - (*(coordinate_type**)B)[c];\ + if (v>0) return 1;\ + if (v<0) return -1; + + int cmpl(const void *a, const void *b) { + double v; + CMPM(0,a,b); + CMPM(1,b,a); + return 0; + } + + int cmph(const void *a, const void *b) {return cmpl(b,a);} + + + int make_chain(coordinate_type** V, int n, int (*cmp)(const void*, const void*)) { + int i, j, s = 1; + coordinate_type* t; + + qsort(V, n, sizeof(coordinate_type*), cmp); + for (i=2; i=1 && ccw(V, i, j, j-1); j--){} + s = j+1; + t = V[s]; V[s] = V[i]; V[i] = t; + } + return s; + } + + int _ch2d(coordinate_type **P, int n) { + int u = make_chain(P, n, cmpl); /* make lower hull */ + if (!n) return 0; + P[n] = P[0]; + return u+make_chain(P+u, n-u+1, cmph); /* make upper hull */ + } + + +} // ns anon + + +GridData::GridData() +{ + datatype = Qwt3D::GRID; + setSize(0,0); + setPeriodic(false,false); +} + +GridData::GridData(unsigned int columns, unsigned int rows) +{ + datatype = Qwt3D::GRID; + setSize(columns,rows); + setPeriodic(false,false); +} + +int GridData::columns() const +{ + return (int)vertices.size(); +} + +int GridData::rows() const +{ + return (empty()) ? 0 : (int)vertices[0].size(); +} + +void GridData::clear() +{ + setHull(ParallelEpiped()); + { + for (unsigned i=0; i!=vertices.size(); ++i) + { + for (unsigned j=0; j!=vertices[i].size(); ++j) + { + delete [] vertices[i][j]; + } + vertices[i].clear(); + } + } + + vertices.clear(); + + { + for (unsigned i=0; i!=normals.size(); ++i) + { + for (unsigned j=0; j!=normals[i].size(); ++j) + { + delete [] normals[i][j]; + } + normals[i].clear(); + } + } + + normals.clear(); +} + + +void GridData::setSize(unsigned int columns, unsigned int rows) +{ + this->clear(); + vertices = std::vector(columns); + { + for (unsigned int i=0; i!=vertices.size(); ++i) + { + vertices[i] = DataRow(rows); + for (unsigned int j=0; j!=vertices[i].size(); ++j) + { + vertices[i][j] = new GLdouble[3]; + } + } + } + normals = std::vector(columns); + { + for (unsigned int i=0; i!=normals.size(); ++i) + { + normals[i] = DataRow(rows); + for (unsigned int j=0; j!=normals[i].size(); ++j) + { + normals[i][j] = new GLdouble[3]; + } + } + } +} + +Triple const& CellData::operator()(unsigned cellnumber, unsigned vertexnumber) +{ + return nodes[cells[cellnumber][vertexnumber]]; +} + +void CellData::clear() +{ + setHull(ParallelEpiped()); + cells.clear(); + nodes.clear(); + normals.clear(); +} + +QColor Qwt3D::GL2Qt(GLdouble r, GLdouble g, GLdouble b) +{ + return QColor(round(r * 255), round(g * 255), round(b * 255)); +} + +RGBA Qwt3D::Qt2GL(QColor col) +{ + QRgb qrgb = col.rgb(); + RGBA rgba; + rgba.r = qRed(qrgb) / 255.0; + rgba.g = qGreen(qrgb) / 255.0; + rgba.b = qBlue(qrgb) / 255.0; + rgba.a = qAlpha(qrgb) / 255.0; + return rgba; +} + + +void Qwt3D::convexhull2d( std::vector& idx, const std::vector& src ) +{ + idx.clear(); + if (src.empty()) + return; + if (src.size()==1) + { + idx.push_back(0); + return; + } + coordinate_type** points = new coordinate_type*[src.size()+1] ; + coordinate_type* P = new coordinate_type[src.size()*2]; + + int i; + for (i=0; i<(int)src.size(); ++i) + { + points[i] = &P[2*i]; + points[i][0] = src[i].x; + points[i][1] = src[i].y; + } + + coordinate_type* start = points[0]; + int m = _ch2d( points, src.size() ); + idx.resize(m); + + for (i=0; i