File indexing completed on 2023-10-03 06:50:26
0001 /************************************************************************************* 0002 * Copyright (C) 2012 by Percy Camilo T. Aucahuasi <percy.camilo.ta@gmail.com> * 0003 * Copyright (C) 2007 by Abderrahman Taha: Basic OpenGL calls like scene, lights * 0004 * and mouse behaviour taken from K3DSurf * 0005 * * 0006 * This program is free software; you can redistribute it and/or * 0007 * modify it under the terms of the GNU General Public License * 0008 * as published by the Free Software Foundation; either version 2 * 0009 * of the License, or (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, write to the Free Software * 0018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 0019 *************************************************************************************/ 0020 0021 #include "plotter3d_es.h" 0022 0023 #include "surface.h" 0024 #include "spacecurve.h" 0025 0026 #include "plotsmodel.h" 0027 #include "private/utils/mathutils.h" 0028 #include "private/export3d.h" 0029 0030 #include <cmath> 0031 #include <QDebug> 0032 #include <QFile> 0033 #include <QPixmap> 0034 #include <QUrl> 0035 #include <QPrinter> 0036 #include <QPainter> 0037 0038 #if defined(HAVE_IEEEFP_H) 0039 #include <ieeefp.h> 0040 // bool isinf(double x) { return !finite(x) && x==x; } 0041 #endif 0042 0043 using namespace std; 0044 using namespace Analitza; 0045 0046 const GLubyte Plotter3DES::XAxisArrowColor[] = {250 -1, 1, 1}; 0047 const GLubyte Plotter3DES::YAxisArrowColor[] = {1, 255 - 1, 1}; 0048 const GLubyte Plotter3DES::ZAxisArrowColor[] = {1, 1, 255 - 1}; 0049 0050 static QOpenGLBuffer createSurfaceBuffer(Surface* surf) 0051 { 0052 Q_ASSERT(QOpenGLContext::currentContext()); 0053 0054 QOpenGLBuffer buffer(QOpenGLBuffer::VertexBuffer); 0055 buffer.create(); 0056 buffer.bind(); 0057 0058 const auto offsetNormal = 3*sizeof(float)*surf->vertices().size(); 0059 const auto offsetEnd = offsetNormal + 3*sizeof(float)*surf->normals().size(); 0060 buffer.allocate(surf->vertices().constData(), offsetEnd); 0061 buffer.write(offsetNormal, surf->normals().constData(), 3*sizeof(float)*surf->normals().size()); 0062 0063 Q_ASSERT(buffer.isCreated()); 0064 buffer.release(); 0065 0066 return buffer; 0067 } 0068 0069 Plotter3DES::Plotter3DES(QAbstractItemModel* model) 0070 : m_model(model) 0071 , m_plotStyle(Solid) 0072 , m_plottingFocusPolicy(All) 0073 , m_depth(-5) 0074 , m_scale(60) 0075 , m_currentAxisIndicator(InvalidAxis) 0076 , m_simpleRotation(false) 0077 , m_referencePlaneColor(Qt::darkGray) 0078 { 0079 resetViewPrivate(QVector3D(-45, 0, -135)); 0080 } 0081 0082 Plotter3DES::~Plotter3DES() 0083 { 0084 for (int i = 0; i < m_itemGeometries.size(); ++i) 0085 { 0086 m_itemGeometries.take(itemAt(i)).destroy(); 0087 } 0088 0089 // glDeleteLists(m_sceneObjects.value(RefPlaneXY), 1); 0090 // glDeleteLists(m_sceneObjects.value(XArrowAxisHint), 1); 0091 // glDeleteLists(m_sceneObjects.value(YArrowAxisHint), 1); 0092 // glDeleteLists(m_sceneObjects.value(ZArrowAxisHint), 1); 0093 } 0094 0095 void Plotter3DES::initGL() 0096 { 0097 initializeOpenGLFunctions(); 0098 0099 //TODO: provide GLSL version 0100 program.addShaderFromSourceCode(QOpenGLShader::Vertex, 0101 "attribute highp vec4 vertex;\n" 0102 "attribute highp vec4 normal;\n" 0103 "uniform highp mat4 matrix;\n" 0104 0105 "void main(void)\n" 0106 "{\n" 0107 " gl_Position = matrix * vertex;\n" 0108 "}" 0109 ); 0110 program.addShaderFromSourceCode(QOpenGLShader::Fragment, 0111 "uniform mediump vec4 color;\n" 0112 0113 "void main(void)\n" 0114 "{\n" 0115 " highp float w = 10.*gl_FragCoord.w;\n" 0116 " highp vec4 zvec = vec4(w, w, w, 1.0);" 0117 " gl_FragColor = mix(color, zvec, vec4(.5,.5,.5,1.));\n" 0118 "}" 0119 ); 0120 program.link(); 0121 0122 if (m_model && m_model->rowCount() > 0) { 0123 updatePlots(QModelIndex(), 0, m_model->rowCount()-1); 0124 } 0125 } 0126 0127 void Plotter3DES::resetViewport() 0128 { 0129 m_rot.setToIdentity(); 0130 resetViewPrivate(QVector3D(-45, 0, -135)); 0131 renderGL(); 0132 } 0133 0134 void Plotter3DES::resetViewPrivate(const QVector3D& rot) 0135 { 0136 m_rot.translate(0,0, -20); 0137 m_rot.rotate(rot.x(), 1, 0, 0); 0138 m_rot.rotate(rot.y(), 0, 1, 0); 0139 m_rot.rotate(rot.z(), 0, 0, 1); 0140 m_simpleRotationVector = rot; 0141 } 0142 0143 void Plotter3DES::setViewport(const QRectF& vp) 0144 { 0145 m_viewport = vp; 0146 0147 m_projection.setToIdentity(); 0148 m_projection.perspective(m_scale, m_viewport.width()/m_viewport.height(), 0.1, 3000); 0149 0150 renderGL(); 0151 } 0152 0153 void Plotter3DES::drawPlots() 0154 { 0155 glClearColor(0, 0, 0, 1); 0156 glEnable(GL_DEPTH_TEST); 0157 glDepthMask(true); 0158 0159 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 0160 0161 if(!m_model) 0162 { 0163 return; 0164 } 0165 0166 program.bind(); 0167 program.setUniformValue("matrix", m_projection * m_rot); 0168 0169 const int vertexLocation = program.attributeLocation("vertex"); 0170 const int normalLocation = program.attributeLocation("normal"); 0171 GLenum mode; 0172 switch (m_plotStyle) 0173 { 0174 case Solid: mode = GL_TRIANGLES; break; 0175 case Wired: mode = GL_LINES; break; 0176 case Dots: mode = GL_POINTS; break; 0177 } 0178 0179 drawAxes(); 0180 drawRefPlane(); 0181 program.enableAttributeArray(vertexLocation); 0182 for (int i = 0, c = m_model->rowCount(); i < c; ++i) 0183 { 0184 PlotItem *item = itemAt(i); 0185 0186 if (!item || item->spaceDimension() != Dim3D || !item->isVisible()) 0187 continue; 0188 0189 program.setUniformValue("color", item->color()); 0190 if (SpaceCurve *curv = dynamic_cast<SpaceCurve*>(item)) 0191 { 0192 //TODO: implement jumps 0193 program.setAttributeArray(vertexLocation, GL_FLOAT, curv->points().constData(), 3); 0194 glDrawArrays(GL_LINE_STRIP, 0, curv->points().size()); 0195 } 0196 else if (Surface *surf = dynamic_cast<Surface*>(item)) 0197 { 0198 QOpenGLBuffer it; 0199 if(!m_itemGeometries.contains(surf)) { 0200 m_itemGeometries[surf] = it = createSurfaceBuffer(surf); 0201 } else { 0202 it = m_itemGeometries[surf]; 0203 } 0204 0205 Q_ASSERT(it.isCreated()); 0206 0207 program.enableAttributeArray(normalLocation); 0208 it.bind(); 0209 0210 const auto offsetNormal = 3*sizeof(float)*surf->vertices().size(); 0211 0212 program.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3); 0213 program.setAttributeBuffer(normalLocation, GL_FLOAT, offsetNormal, 3); 0214 glDrawElements(mode, surf->indexes().size(), GL_UNSIGNED_INT, surf->indexes().constData()); 0215 0216 if (mode == GL_TRIANGLES) { 0217 program.setUniformValue("color", QColor(Qt::black)); 0218 glDrawElements(GL_POINTS, surf->indexes().size(), GL_UNSIGNED_INT, surf->indexes().constData()); 0219 } 0220 it.release(); 0221 program.disableAttributeArray(normalLocation); 0222 } 0223 } 0224 program.disableAttributeArray(vertexLocation); 0225 program.release(); 0226 } 0227 0228 void Plotter3DES::exportSurfaces(const QString& path) const 0229 { 0230 Export3D::exportX3D(path, m_model); 0231 } 0232 0233 void Plotter3DES::updatePlots(const QModelIndex & parent, int s, int e) 0234 { 0235 Q_ASSERT(!parent.isValid()); 0236 Q_UNUSED(parent); 0237 0238 for(int i=s; i<=e; i++) { 0239 PlotItem *item = itemAt(i); 0240 0241 if (!item) 0242 return; 0243 0244 m_itemGeometries.take(item).destroy(); 0245 0246 if (item->isVisible()) { 0247 // addFuncs(QModelIndex(), s.row(), s.row()); 0248 //igual no usar addFuncs sino la funcion interna pues no actualiza los items si tienen data 0249 addPlots(item); 0250 } 0251 } 0252 0253 const int count = m_model->rowCount(); 0254 if (count <= e) 0255 { 0256 for (int i = e; i < count; ++i) 0257 { 0258 m_itemGeometries.take(itemAt(i)).destroy(); 0259 } 0260 } 0261 0262 renderGL(); 0263 } 0264 0265 void Plotter3DES::setModel(QAbstractItemModel* f) 0266 { 0267 m_model = f; 0268 0269 if (m_model) 0270 updatePlots(QModelIndex(), 0, m_model->rowCount()-1); 0271 modelChanged(); 0272 } 0273 0274 void Plotter3DES::setPlottingFocusPolicy(PlottingFocusPolicy fp) 0275 { 0276 m_plottingFocusPolicy = fp; 0277 0278 for (int i = 0; i < m_itemGeometries.size(); ++i) 0279 { 0280 m_itemGeometries.take(itemAt(i)).destroy(); 0281 } 0282 0283 updatePlots(QModelIndex(), 0, m_model->rowCount()-1); 0284 } 0285 0286 void Plotter3DES::scale(qreal factor) 0287 { 0288 m_scale = qBound(1., factor*m_scale, 140.); 0289 0290 setViewport(m_viewport); 0291 } 0292 0293 void Plotter3DES::setUseSimpleRotation(bool simplerot) 0294 { 0295 m_simpleRotation = simplerot; 0296 } 0297 0298 void Plotter3DES::rotate(int dx, int dy) 0299 { 0300 const qreal ax = -dy; 0301 const qreal ay = -dx; 0302 const double angle = sqrt(ax*ax + ay*ay)/(m_viewport.width() + 1)*360.0; 0303 0304 if (m_simpleRotation) { 0305 m_rot.setToIdentity(); 0306 resetViewPrivate(m_simpleRotationVector + QVector3D(ax, 0, ay)); 0307 renderGL(); 0308 } else if (!m_rotFixed.isNull()) { 0309 m_rot.rotate(angle, m_rotFixed.normalized()); 0310 renderGL(); 0311 } else { 0312 // TODO: figure out how to do this on an opengl es compatible way 0313 0314 // GLfloat matrix[16] = {0}; // model view matrix from current OpenGL state 0315 // 0316 // glGetFloatv(GL_MODELVIEW_MATRIX, matrix); 0317 // 0318 // QMatrix4x4 matrix4(matrix, 4, 4); 0319 // bool couldInvert; 0320 // matrix4 = matrix4.inverted(&couldInvert); 0321 // 0322 // if (couldInvert) { 0323 // QVector3D rot(matrix4.row(0).x()*ax + matrix4.row(1).x()*ay, 0324 // matrix4.row(0).y()*ax + matrix4.row(1).y()*ay, 0325 // matrix4.row(0).z()*ax + matrix4.row(1).z()*ay); 0326 // 0327 // m_rot.rotate(rot.length(), rot.normalized()); 0328 // 0329 // renderGL(); 0330 // } 0331 } 0332 } 0333 0334 CartesianAxis Plotter3DES::selectAxisArrow(int x, int y) 0335 { 0336 GLint viewport[4]; 0337 GLubyte pixel[3]; 0338 0339 glGetIntegerv(GL_VIEWPORT,viewport); 0340 0341 glReadPixels(x, viewport[3]-y, 1, 1, GL_RGB,GL_UNSIGNED_BYTE,(void *)pixel); 0342 0343 if (memcmp(pixel, XAxisArrowColor, sizeof(pixel)) == 0) return XAxis; 0344 if (memcmp(pixel, YAxisArrowColor, sizeof(pixel)) == 0) return YAxis; 0345 if (memcmp(pixel, ZAxisArrowColor, sizeof(pixel)) == 0) return ZAxis; 0346 0347 return InvalidAxis; 0348 } 0349 0350 void Plotter3DES::fixRotation(const QVector3D& vec) 0351 { 0352 m_rotFixed = vec; 0353 } 0354 0355 void Plotter3DES::showAxisArrowHint(CartesianAxis axis) 0356 { 0357 if (axis == InvalidAxis) 0358 return ; 0359 0360 m_currentAxisIndicator = axis; 0361 0362 renderGL(); 0363 } 0364 0365 void Plotter3DES::hideAxisHint() 0366 { 0367 m_currentAxisIndicator = InvalidAxis; 0368 0369 renderGL(); 0370 } 0371 0372 void Plotter3DES::addPlots(PlotItem* item) 0373 { 0374 Q_ASSERT(item); 0375 0376 if (SpaceCurve *curve = dynamic_cast<SpaceCurve*>(item)) 0377 { 0378 if (curve->points().isEmpty()) 0379 curve->update(QVector3D(), QVector3D()); 0380 } 0381 else if (Surface* surf = dynamic_cast<Surface*>(item)) 0382 { 0383 if (surf->indexes().isEmpty()) 0384 surf->update(QVector3D(), QVector3D()); 0385 0386 Q_ASSERT(!surf->indexes().isEmpty()); 0387 } 0388 } 0389 0390 PlotItem* Plotter3DES::itemAt(int row) const 0391 { 0392 QModelIndex pi = m_model->index(row, 0); 0393 0394 if (!pi.isValid()) 0395 return nullptr; 0396 0397 PlotItem* plot = pi.data(PlotsModel::PlotRole).value<PlotItem*>(); 0398 0399 if (plot->spaceDimension() != Dim3D) 0400 return nullptr; 0401 0402 return plot; 0403 } 0404 0405 void Plotter3DES::drawAxes() 0406 { 0407 glLineWidth(1.5f); 0408 0409 static GLfloat const xVertices[] = { 0410 0.f, 0.f, 0.f, 0411 10.f, 0.f, 0.f, 0412 }; 0413 static GLfloat const yVertices[] = { 0414 0.f, 0.f, 0.f, 0415 0.f, 10.f, 0.f, 0416 }; 0417 static GLfloat const zVertices[] = { 0418 0.f, 0.f, 0.f, 0419 0.f, 0.f, 10.f, 0420 }; 0421 0422 static QVector<GLuint> const idxs = {0,1}; 0423 0424 const int vertexLocation = program.attributeLocation("vertex"); 0425 program.enableAttributeArray(vertexLocation); 0426 0427 program.setUniformValue("color", QColor(Qt::red)); 0428 program.setAttributeArray(vertexLocation, xVertices, 3); 0429 glDrawElements(GL_LINES, idxs.size(), GL_UNSIGNED_INT, idxs.constData()); 0430 0431 static GLfloat const XArrowVertices[] = { 0432 10.f, 0.f, 0.f, 0433 0434 9.8f, 0.1f, 0.f, 0435 9.8f, 0.f, 0.1f, 0436 9.8f, -0.1f, 0.f, 0437 9.8f, 0.f, -0.1f, 0438 9.8f, 0.1f, 0.f 0439 }; 0440 program.setAttributeArray(vertexLocation, XArrowVertices, 3); 0441 glDrawArrays(GL_TRIANGLE_FAN, 0, 6); 0442 0443 program.setUniformValue("color", QColor(Qt::green)); 0444 program.setAttributeArray(vertexLocation, yVertices, 3); 0445 glDrawElements(GL_LINES, idxs.size(), GL_UNSIGNED_INT, idxs.constData()); 0446 0447 static GLfloat const YArrowVertices[] = { 0448 0.f, 10.f, 0.f, 0449 0450 0.1f, 9.8f, 0.f, 0451 0.f, 9.8f, 0.1f, 0452 -0.1f, 9.8f, 0.f, 0453 0.f, 9.8f,-0.1f, 0454 0.1f, 9.8f, 0.f 0455 }; 0456 program.setAttributeArray(vertexLocation, YArrowVertices, 3); 0457 glDrawArrays(GL_TRIANGLE_FAN, 0, 6); 0458 0459 program.setUniformValue("color", QColor(Qt::blue)); 0460 program.setAttributeArray(vertexLocation, zVertices, 3); 0461 glDrawElements(GL_LINES, idxs.size(), GL_UNSIGNED_INT, idxs.constData()); 0462 0463 static GLfloat const ZArrowVertices[] = { 0464 0.f, 0.f, 10.f, 0465 0.1f, 0.f, 9.8f, 0466 0.f, 0.1f, 9.8f, 0467 -0.1f, 0.f, 9.8f, 0468 0.f,-0.1f, 9.8f, 0469 0.1f, 0.f, 9.8f 0470 }; 0471 program.setAttributeArray(vertexLocation, ZArrowVertices, 3); 0472 glDrawArrays(GL_TRIANGLE_FAN, 0, 6); 0473 0474 program.disableAttributeArray(vertexLocation); 0475 } 0476 0477 void Plotter3DES::drawRefPlane() 0478 { 0479 glLineWidth(1.f); 0480 const float lims = 10; 0481 QVector<QVector3D> vxs; 0482 0483 for(float x=-lims; x<=lims; ++x) { 0484 vxs += { x, -lims, m_depth }; 0485 vxs += { x, lims, m_depth }; 0486 } 0487 0488 for(float y=-lims; y<=lims; ++y) { 0489 vxs += { -lims, y, m_depth }; 0490 vxs += { lims, y, m_depth }; 0491 } 0492 0493 const int vertexLocation = program.attributeLocation("vertex"); 0494 program.enableAttributeArray(vertexLocation); 0495 program.setUniformValue("color", m_referencePlaneColor); 0496 program.setAttributeArray(vertexLocation, GL_FLOAT, vxs.constData(), 3); 0497 glDrawArrays(GL_LINES, 0, vxs.size()); 0498 program.disableAttributeArray(vertexLocation); 0499 } 0500 0501 bool Plotter3DES::save(const QUrl& url) 0502 { 0503 if (!url.isLocalFile()) 0504 return false; 0505 0506 const QString path = url.toLocalFile(); 0507 if(path.endsWith(QLatin1String(".x3d")) || path.endsWith(QLatin1String(".stl"))) { 0508 exportSurfaces(path); 0509 } else if(path.endsWith(QLatin1String(".pdf"))) { 0510 auto px = grabImage(); 0511 //this pulls widgets 0512 QPrinter printer; 0513 printer.setOutputFormat(QPrinter::PdfFormat); 0514 printer.setOutputFileName(path); 0515 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0516 printer.setPaperSize(px.size(), QPrinter::DevicePixel); 0517 #else 0518 #pragma "port printer.setPaperSize"; 0519 #endif 0520 printer.setPageMargins(QMarginsF(0,0,0,0)); 0521 QPainter painter; 0522 painter.begin(&printer); 0523 painter.drawImage(QPoint(0,0), px); 0524 painter.end(); 0525 } else { 0526 auto px = grabImage(); 0527 return px.save(path); 0528 } 0529 return true; 0530 } 0531 0532 QStringList Plotter3DES::filters() const 0533 { 0534 return {QObject::tr("PNG Image (*.png)"), QObject::tr("PDF Document (*.pdf)"), QObject::tr("X3D Document (*.x3d)"), QObject::tr("STL Document (*.stl)")}; 0535 }