/*************************************************************************** qsaxes3d.cpp ------------------- begin : 01-January-2000 copyright : (C) 2000 by Kamil Dobkowski email : kamildobk@poczta.onet.pl ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include"qsaxes3d.h" #include"qsaxis.h" #include"qsdrvopengl.h" #include"qsdrvqt.h" #include #include #include struct QSAxes3D::qsaxes3d_runtime_data { QSPt3f side_norms[6]; QSGFill wall_fills[6]; QSGLine wall_lines[6]; bool v_max[4]; bool v_min[4]; bool v_fir[4]; bool v_sec[4]; bool has_grid[6][6]; bool has_grid_opposite[6][6]; }; //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// QSAxes3D::QSAxes3D(QObject * parent, const char * name) :QSAxes(parent,&t,name) { m_drv = NULL; m_gl.enabled = false; // default values m_gl.transparency = false; m_gl.shadewalls = true; m_gl.autostroke = true; m_gl.autostrokel = -45; m_gl.globaltr = 0; m_view.azimuth = 45; m_view.elevation = 30; m_view.lightAzimuth = 135; m_view.lightElevation = 30; m_view.distance = 0; m_view.directLight = 0; m_view.ambientLight = 10; m_view.focus = 35; m_view.perspective = false; m_view.lighted = false; m_view.autoscale = true; m_is_graphics_active = false; m_view.xEdge = 1.0; m_view.yEdge = 1.0; m_view.zEdge = 0.75; m_view.xyThick = 0.05; m_view.xzThick = 0.0; m_view.yzThick = 0.0; t.matrixI( t.M ); t.matrixI( t.P ); t.matrixI( t.T ); d = NULL; #define CHANNELS_NUM 0 #define FONTS_NUM 0 #define FILLS_NUM 5 #define LINES_NUM 5 #define POINTS_NUM 0 initChannelTable( CHANNELS_NUM ); initAttributeTables( FONTS_NUM, FILLS_NUM, LINES_NUM, POINTS_NUM ); defaultSettings(); } //-------------------------------------------------------------// QSAxes3D::~QSAxes3D() { } //-------------------------------------------------------------// void QSAxes3D::setEdgeLength( double xEdge, double yEdge, double zEdge ) { if ( xEdge != m_view.xEdge || yEdge != m_view.yEdge || zEdge != m_view.zEdge ) { parametersChanging(); if ( xEdge > 0.0 ) m_view.xEdge = xEdge; if ( yEdge > 0.0 ) m_view.yEdge = yEdge; if ( zEdge > 0.0 ) m_view.zEdge = zEdge; parametersChanged(); } } //-------------------------------------------------------------// void QSAxes3D::setXEdgeLength( double length ) { if ( length > 0.0 ) { SET_PROPERTY( m_view.xEdge, length ); } } //-------------------------------------------------------------// void QSAxes3D::setYEdgeLength( double length ) { if ( length > 0.0 ) { SET_PROPERTY( m_view.yEdge, length ); } } //-------------------------------------------------------------// void QSAxes3D::setZEdgeLength( double length ) { if ( length > 0.0 ) { SET_PROPERTY( m_view.zEdge, length ); } } //-------------------------------------------------------------// void QSAxes3D::drawAxis( QSAxis *axis ) { if ( axis->visible() ) if ( axis->type() == QSAxis::XAxisType || axis->type() == QSAxis::YAxisType || axis->type() == QSAxis::ZAxisType ) { QSPt3f f = t.furthest(); QSPt3f l = t.left(); QSPt3f p1( opposite_side(f.x), opposite_side(f.y), f.z ); QSPt3f p2( opposite_side(f.x), opposite_side(f.y), f.z ); if ( axis->type() == QSAxis::ZAxisType ) p1 = p2 = QSPt3f( l.x, l.y, 0.0 ); if ( axis->oppositePosition() ) p1 = p2 = QSPt3f( opposite_side(p1.x), opposite_side(p1.y), opposite_side(p1.z) ); // tic direction and length QSPt3f d = p1 - f; double xAxisScale; double yAxisScale; double zAxisScale; double tic_len = 15.0 / 500.0; get_axis_lengths( &xAxisScale, &yAxisScale, &zAxisScale ); switch( axis->type() ) { case QSAxis::XAxisType: p1.x = 0.0; p2.x = 1.0; d.x = 0.0; break; case QSAxis::YAxisType: p1.y = 0.0; p2.y = 1.0; d.y = 0.0; break; case QSAxis::ZAxisType: p1.z = 0.0; p2.z = 1.0; d.z = 0.0; break; } // take care about thickness for( int side=0; side<6; side++ ) { if ( point_on_side(p1,side) && point_on_side(p2,side) && point_on_side( f,side) ) { QSPt3f thick = thickness( side ); p1 = p1 + thick; p2 = p2 + thick; } } // take care about position double pos = axis->defaultPosition() ? 0.0 : axis->position(); p1 = p1 + QSPt3f( d.x*pos, d.y*pos, d.z*pos ); p2 = p2 + QSPt3f( d.x*pos, d.y*pos, d.z*pos ); // draw axis line m_drv->setLine( axis->line(QSAxis::AxisLine) ); if ( axis->reversed() ) m_drv->drawArrow( t.world3DToCanvas(p2), t.world3DToCanvas(p1), axis->arrow1(), axis->arrow2() ); else m_drv->drawArrow( t.world3DToCanvas(p1), t.world3DToCanvas(p2), axis->arrow1(), axis->arrow2() ); // draw tics and labels int tic_nr = 0; m_drv->setCurrentElement( QSAxes::GridCategory, axisIndex(axis) ); QSGLine tic_line = axis->line(QSAxis::AxisLine); tic_line.style = QSGLine::Solid; list::const_iterator curr = axis->tics()->begin(); list::const_iterator last = axis->tics()->end(); if ( !m_really_fast ) // a little hack for paintSkeleton while ( curr != last ) { if ( curr->m_major ) { switch( axis->type() ) { case QSAxis::XAxisType: p1.x = curr->m_pos; break; case QSAxis::YAxisType: p1.y = curr->m_pos; break; case QSAxis::ZAxisType: p1.z = curr->m_pos; break; } p2 = p1 + QSPt3f( d.x*tic_len/xAxisScale, d.y*tic_len/yAxisScale, d.z*tic_len ); if ( axis->ticsVisible() ) { m_drv->setLine( tic_line ); draw_line( p1, p2 ); } double tic_shift = (tic_nr%2) ? axis->ticLabelPos2() : axis->ticLabelPos1(); p2 = p2 + QSPt3f( tic_shift*d.x, tic_shift*d.y, tic_shift*d.z ); m_drv->setFont( curr->m_font ); m_drv->drawRTextBox( t.world3DToCanvas(p2), curr->m_angle, curr->m_label, get_label_align(p1,p2,axis->type()) ); tic_nr++; } curr++; } // draw title m_drv->setFont( axis->font(QSAxis::TitleFont) ); m_drv->setCurrentElement( QSAxes::AxisCategory, axisIndex(axis) ); switch( axis->type() ) { case QSAxis::XAxisType: p1.x = axis->titlePosition(); break; case QSAxis::YAxisType: p1.y = axis->titlePosition(); break; case QSAxis::ZAxisType: p1.z = axis->titlePosition(); break; } p2 = p1 + QSPt3f( axis->titleDistance()*d.x*4.0, axis->titleDistance()*d.y*4.0, axis->titleDistance()*d.z*4.0 ); m_drv->drawText3( p2, axis->title(), get_label_align(p1,p2,axis->type()) ); } } //-------------------------------------------------------------// bool QSAxes3D::point_on_side( const QSPt3f& p, int side ) { QSPt3f p1( 0.0, 0.0, 0.0 ); QSPt3f p2( 1.0, 1.0, 1.0 ); QSPt3f pts[4]; QSPt3f norm[5]; get_side_wall( p1, p2, side, pts, norm ); for( int i=0; i<4; i++ ) if ( pts[i] == p ) return true; return false; } //-------------------------------------------------------------// void QSAxes3D::drawGrid( QSAxis *axis, bool major ) { QSPt3f p1; QSPt3f p2; bool wall_visible; QSPt3f pts[4]; QSPt3f norm[5]; if ( axis->visible() ) if ( axis->type() == QSAxis::XAxisType || axis->type() == QSAxis::YAxisType || axis->type() == QSAxis::ZAxisType ) for( int wall=0; wall<6; wall++ ) { get_axis_wall( wall, &p1, &p2, &wall_visible ); bool has_grid = false; if ( axis->oppositePosition() ) has_grid = d->has_grid_opposite[axis->type()][wall]; else has_grid = d->has_grid[axis->type()][wall]; // draw thick wall side by side if ( has_grid && wall_visible ) for( int side=0; side<6; side++ ) { get_side_wall( p1, p2, side, pts, norm ); if ( visible(pts[0],norm[0]) ) draw_grid( axis, major, pts, norm ); } } } //-------------------------------------------------------------// void QSAxes3D::draw_grid( QSAxis *axis, bool major, QSPt3f pts[4], QSPt3f norm[5] ) { QSPt3f p1 = pts[0]; QSPt3f p2 = pts[2]; switch( axis->type() ) { case QSAxis::XAxisType: if ( p1.x == p2.x || ( p1.y == p2.y && p1.z == p2.z ) ) return; break; case QSAxis::YAxisType: if ( p1.y == p2.y || ( p1.x == p2.x && p1.z == p2.z ) ) return; break; case QSAxis::ZAxisType: if ( p1.z == p2.z || ( p1.x == p2.z && p1.y == p2.y ) ) return; break; } list::const_iterator curr = axis->tics()->begin(); list::const_iterator last = axis->tics()->end(); while ( curr != last ) { if ( curr->m_major == major ) { QSPt3f p[2]; p[0] = pts[0]; p[1] = pts[2]; switch( axis->type() ) { case QSAxis::XAxisType: p[0].x = p[1].x = curr->m_pos; break; case QSAxis::YAxisType: p[0].y = p[1].y = curr->m_pos; break; case QSAxis::ZAxisType: p[0].z = p[1].z = curr->m_pos; break; } QSPt3f n[2]; t.clipVertexNormals( pts, 4, p, 2, &norm[1], n ); m_drv->setLine( curr->m_line ); draw_line( p[0], p[1], n ); } curr++; } } //-------------------------------------------------------------// void QSAxes3D::get_side_wall( const QSPt3f& p1, const QSPt3f& p2, int side_nr, QSPt3f result_pts[4], QSPt3f result_norm[5] ) // return a side wall parameters of a cube defined by p1, p2 { result_norm[0] = d->side_norms[side_nr]; QSPt3f min( QMIN(p1.x,p2.x), QMIN(p1.y,p2.y), QMIN(p1.z,p2.z) ); QSPt3f max( QMAX(p1.x,p2.x), QMAX(p1.y,p2.y), QMAX(p1.z,p2.z) ); // make sure that all vertices are ordered in CW direction bool *v_fir = d->v_fir; bool *v_sec = d->v_sec; if ( side_nr == 0 || side_nr == 3 || side_nr == 4 ) { v_fir = d->v_sec; v_sec = d->v_fir; } // one should point to v_min or v_max, another one to v_fir, and another to v_sec bool *v_x = result_norm[0].x<0.0 ? d->v_min : ( result_norm[0].x>0.0 ? d->v_max : v_fir ); bool *v_y = result_norm[0].y<0.0 ? d->v_min : ( result_norm[0].y>0.0 ? d->v_max : v_fir ); bool *v_z = result_norm[0].z<0.0 ? d->v_min : ( result_norm[0].z>0.0 ? d->v_max : v_sec ); if ( v_x == v_y ) v_y = v_sec; for( int i=0; i<4; i++ ) { if ( v_x[i] ) result_pts[i].x = max.x; else result_pts[i].x = min.x; if ( v_y[i] ) result_pts[i].y = max.y; else result_pts[i].y = min.y; if ( v_z[i] ) result_pts[i].z = max.z; else result_pts[i].z = min.z; } // normal to vertices for( int i=1; i<5; i++ ) { result_norm[i] = result_norm[0]; // modified for a better look if ( result_norm[i].x == 0.0 ) result_norm[i].x = result_pts[i-1].x == max.x ? 0.3 : -0.3; if ( result_norm[i].y == 0.0 ) result_norm[i].y = result_pts[i-1].y == max.y ? 0.3 : -0.3; if ( result_norm[i].z == 0.0 ) result_norm[i].z = result_pts[i-1].z == max.z ? 0.3 : -0.3; result_norm[i] = t.normalize( result_norm[i] ); } } //-------------------------------------------------------------// void QSAxes3D::get_axis_wall( int wall_nr, QSPt3f *result_p1, QSPt3f *result_p2, bool *visible ) { QSPt3f p1( 0.0, 0.0, 0.0 ); QSPt3f p2( 1.0, 1.0, 1.0 ); QSPt3f pts[4]; QSPt3f norm[5]; get_side_wall( p1, p2, wall_nr, pts, norm ); // draw only rear walls QSPt3f f = t.furthest(); *visible = false; for ( int i=0; i<4; i++ ) if ( pts[i] == f ) *visible = true; *result_p1 = pts[0]; *result_p2 = pts[2] + thickness( wall_nr ); } //-------------------------------------------------------------// QSPt3f QSAxes3D::thickness( int wall_nr ) { QSPt3f p1( 0.0, 0.0, 0.0 ); QSPt3f p2( 1.0, 1.0, 1.0 ); QSPt3f pts[4]; QSPt3f norm[5]; get_side_wall( p1, p2, wall_nr, pts, norm ); return QSPt3f( norm[0].x*m_view.yzThick, norm[0].y*m_view.xzThick, norm[0].z*m_view.xyThick ); } //-------------------------------------------------------------// bool QSAxes3D::visible( const QSPt3f& p, const QSPt3f& norm ) { if ( perspective() ) t.dvector = p - t.eye; return ( t.dotProduct(norm,t.dvector) <= 0.0 ); } //-------------------------------------------------------------// void QSAxes3D::draw_line( QSPt3f p1, QSPt3f p2, QSPt3f *normals ) { QSPt3f enormals[2]; m_drv->drawLine3( p1, p2, normals ? normals : enormals ); } //-------------------------------------------------------------// double QSAxes3D::opposite_side( double value ) { return value < 0.5 ? 1.0 : 0.0; } //-------------------------------------------------------------// void QSAxes3D::allocRuntimeData() { QSAxes::allocRuntimeData(); d = new qsaxes3d_runtime_data(); d->side_norms[0] = QSPt3f( 0.0, 0.0, -1.0 ); // min xy d->side_norms[1] = QSPt3f( 0.0, 0.0, 1.0 ); // max xy d->side_norms[2] = QSPt3f( 0.0, -1.0, 0.0 ); // min xz d->side_norms[3] = QSPt3f( 0.0, 1.0, 0.0 ); // max xz d->side_norms[4] = QSPt3f( -1.0, 0.0, 0.0 ); // min yz d->side_norms[5] = QSPt3f( 1.0, 0.0, 0.0 ); // max yz bool v_max[4] = { true, true, true, true }; bool v_min[4] = { false, false, false, false }; bool v_fir[4] = { false, false, true, true }; bool v_sec[4] = { false, true, true, false }; for( int i=0; i<4; i++ ) { d->v_max[i] = v_max[i]; d->v_min[i] = v_min[i]; d->v_fir[i] = v_fir[i]; d->v_sec[i] = v_sec[i]; } d->wall_fills[0] = fill( XYWallFill ); d->wall_fills[1] = fill( XYWallFill ); d->wall_fills[2] = fill( XZWallFill ); d->wall_fills[3] = fill( XZWallFill ); d->wall_fills[4] = fill( YZWallFill ); d->wall_fills[5] = fill( YZWallFill ); d->wall_lines[0] = line( XYWallLine ); d->wall_lines[1] = line( XYWallLine ); d->wall_lines[2] = line( XZWallLine ); d->wall_lines[3] = line( XZWallLine ); d->wall_lines[4] = line( YZWallLine ); d->wall_lines[5] = line( YZWallLine ); for( int i=0; i<6; i++ ) for( int j=0; j<6; j++ ) { d->has_grid[i][j] = false; d->has_grid_opposite[i][j] = false; } d->has_grid[QSAxis::XAxisType][0] = true; d->has_grid[QSAxis::XAxisType][1] = true; d->has_grid[QSAxis::YAxisType][0] = true; d->has_grid[QSAxis::YAxisType][1] = true; d->has_grid[QSAxis::ZAxisType][2] = true; d->has_grid[QSAxis::ZAxisType][3] = true; d->has_grid[QSAxis::ZAxisType][4] = true; d->has_grid[QSAxis::ZAxisType][5] = true; d->has_grid_opposite[QSAxis::XAxisType][2] = true; d->has_grid_opposite[QSAxis::XAxisType][3] = true; d->has_grid_opposite[QSAxis::YAxisType][4] = true; d->has_grid_opposite[QSAxis::YAxisType][5] = true; d->has_grid_opposite[QSAxis::ZAxisType][2] = true; d->has_grid_opposite[QSAxis::ZAxisType][3] = true; d->has_grid_opposite[QSAxis::ZAxisType][4] = true; d->has_grid_opposite[QSAxis::ZAxisType][5] = true; } //-------------------------------------------------------------// void QSAxes3D::freeRuntimeData() { delete d; d = NULL; QSAxes::freeRuntimeData(); } //-------------------------------------------------------------// void QSAxes3D::stop() // stop drawing now. // Clean-up { if ( m_is_graphics_active ) { m_is_graphics_active = false; m_drv->stopDrawing(); } QSAxes::stop(); } //-------------------------------------------------------------// void QSAxes3D::axisRangesCalculated() { assert( m_drv ); init_3dtr(); assert( !m_is_graphics_active ); m_is_graphics_active = true; m_drv->startDrawing(); //QSGFill f = background(); //if ( !m_transparent && f.style == QSGFill::Transparent ) f = QSGFill(); m_drv->clearCanvas( background(), QSPt2f( m_cpos.x, m_cpos.y ), QSPt2f( m_csize.x, m_csize.y ) ); m_drv->setTopBottom( false ); m_cnormals = m_drv->cNormals(); m_ccolors = m_drv->cColors(); m_corder = m_drv->cOrdering(); if ( m_cnormals == QSDrv::NoNormals && m_view.lighted ) m_cnormals = QSDrv::MeshNormal; //drawAxes( 1 ); draw_box(); if ( m_axes_only ) draw_arrow(); QSAxes::axisRangesCalculated(); } //-------------------------------------------------------------// // cout << " norm " << norm[0].x << ","<< norm[0].y << "," << norm[0].z << endl; void QSAxes3D::draw_box() { QSPt3f p1; QSPt3f p2; bool wall_visible; QSPt3f pts[4]; QSPt3f norm[5]; QSGFill fills[4]; for( int wall=0; wall<6; wall++ ) { get_axis_wall( wall, &p1, &p2, &wall_visible ); if ( wall_visible ) { for( int i=0; i<4; i++ ) fills[i] = d->wall_fills[wall]; m_drv->setLine( d->wall_lines[wall] ); m_drv->setFill( d->wall_fills[wall] ); // draw thick wall side by side for( int side=0; side<6; side++ ) { get_side_wall( p1, p2, side, pts, norm ); if ( visible(pts[0],norm[0]) ) m_drv->drawPoly3( pts, 4, norm, fills ); } } } } //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// void QSAxes3D::paintPlot( QPainter *paint, double dpi, bool blocking, bool transparent ) { if ( paint ) #ifdef HAVE_GL if ( m_gl.enabled ) { if ( m_internal_state == Busy ) stop(); QSDrvOpenGL gl_drv; gl_drv.setAlpha( m_gl.transparency ); gl_drv.setShadeWalls( m_gl.shadewalls ); gl_drv.setGlobalTransparency( m_gl.globaltr ); gl_drv.setMeshAutoStroke( m_gl.autostroke ); gl_drv.setAutoStrokeLightness( m_gl.autostrokel ); gl_drv.setProjection( &t ); gl_drv.setDC( paint, dpi, false ); gl_drv.init( this ); start( &gl_drv, blocking, transparent ); } else #endif { if ( m_internal_state == Busy ) stop(); QSDrvQt qt_drv; qt_drv.setProjection( &t ); qt_drv.setDC(paint,dpi,false); // do not delete QPainter start( &qt_drv, blocking, transparent ); } } //-------------------------------------------------------------// void QSAxes3D::paintSkeleton( QPainter *p, double dpi, bool reallyFast ) { bool prev_gl = m_gl.enabled; m_gl.enabled = false; QSAxes::paintSkeleton( p, dpi, reallyFast ); m_gl.enabled = prev_gl; } //-------------------------------------------------------------// void QSAxes3D::drawPlot( QSDrv *init_drv, bool blocking, bool transparent ) { if ( init_drv ) { if ( m_internal_state == Busy ) stop(); init_drv->setProjection( &t ); start( init_drv, blocking, transparent ); } } //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// #define EPS 1e-5 int QSAxes3D::get_label_align( const QSPt3f& p1, const QSPt3f& p2, int axis ) { int halign = AlignHCenter; int valign = AlignVCenter; QSPt2f c1 = t.world3DToCanvas( p1 ); QSPt2f c2 = t.world3DToCanvas( p2 ); if ( c2.x - c1.x > EPS ) halign = AlignLeft; if ( c2.x - c1.x < -EPS ) halign = AlignRight; if ( c2.y - c1.y > EPS ) valign = AlignTop; if ( c2.y - c1.y < -EPS ) valign = AlignBottom; // Opps ! Corrrect this by hand switch( axis ) { case QSAxis::XAxisType: case QSAxis::YAxisType: if ( valign == AlignVCenter ) valign = AlignTop; break; case QSAxis::ZAxisType: valign = AlignVCenter; break; } return halign | valign; } //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// void QSAxes3D::setOpenGL( bool enabled ) { SET_PROPERTY(m_gl.enabled,enabled); } //-------------------------------------------------------------// void QSAxes3D::setAzimuth( int angle ) { angle = angle % 360; if ( angle < 0 ) angle += 360; SET_PROPERTY(m_view.azimuth,angle); } //-------------------------------------------------------------// void QSAxes3D::setElevation( int angle ) { if ( angle > 90 ) angle = 90; if ( angle < -90 ) angle = -90; SET_PROPERTY(m_view.elevation,angle); } //-------------------------------------------------------------// void QSAxes3D::setFocusDistance( int d ) { d = d % 51; SET_PROPERTY( m_view.focus, d ); } //-------------------------------------------------------------// void QSAxes3D::setDistance( int d ) { d = d % 51; SET_PROPERTY( m_view.distance, d ); } //-------------------------------------------------------------// void QSAxes3D::setLightAzimuth( int angle ) { angle = angle % 360; if ( angle < 0 ) angle += 360; SET_PROPERTY( m_view.lightAzimuth, angle ); } //-------------------------------------------------------------// void QSAxes3D::setLightElevation( int angle ) { if ( angle > 90 ) angle = 90; if ( angle < -90 ) angle = -90; SET_PROPERTY( m_view.lightElevation, angle ); } //-------------------------------------------------------------// void QSAxes3D::setDirectLight( int lightness ) { lightness = lightness % 51; SET_PROPERTY( m_view.directLight, lightness ); } //-------------------------------------------------------------// void QSAxes3D::setAmbientLight( int lightness ) { lightness = lightness % 51; SET_PROPERTY( m_view.ambientLight, lightness ); } //-------------------------------------------------------------// void QSAxes3D::setLight( bool enabled ) { SET_PROPERTY( m_view.lighted, enabled ); } //-------------------------------------------------------------// void QSAxes3D::setPerspective( bool enabled ) { SET_PROPERTY( m_view.perspective, enabled ); } //-------------------------------------------------------------// void QSAxes3D::setAutoscale( bool enabled ) { SET_PROPERTY( m_view.autoscale, enabled ); } //-------------------------------------------------------------// void QSAxes3D::setWallThickness( double xy, double xz, double yz ) { if ( xy != m_view.xyThick || xz != m_view.xzThick || yz != m_view.yzThick ) { parametersChanging(); if ( xy >= 0.0 ) m_view.xyThick = xy; if ( xz >= 0.0 ) m_view.xzThick = xz; if ( yz >= 0.0 ) m_view.yzThick = yz; parametersChanged(); } } //-------------------------------------------------------------// void QSAxes3D::setXYWallThickness( double thickness ) { if ( thickness > 0.0 ) { SET_PROPERTY( m_view.xyThick, thickness ); } } //-------------------------------------------------------------// void QSAxes3D::setXZWallThickness( double thickness ) { if ( thickness > 0.0 ) { SET_PROPERTY( m_view.xzThick, thickness ); } } //-------------------------------------------------------------// void QSAxes3D::setYZWallThickness( double thickness ) { if ( thickness > 0.0 ) { SET_PROPERTY( m_view.yzThick, thickness ); } } //-------------------------------------------------------------// void QSAxes3D::glSetTransparency( bool enabled ) { SET_PROPERTY( m_gl.transparency, enabled ); } //-------------------------------------------------------------// void QSAxes3D::glSetGlobalTransparency( int value ) { SET_PROPERTY( m_gl.globaltr, value ); } //-------------------------------------------------------------// void QSAxes3D::glSetShadeWalls( bool enable ) { SET_PROPERTY( m_gl.shadewalls, enable ); } //-------------------------------------------------------------// void QSAxes3D::glSetMeshAutoStroke( bool enable ) { SET_PROPERTY( m_gl.autostroke, enable ); } //-------------------------------------------------------------// void QSAxes3D::glSetAutoStrokeLightness( int value ) { SET_PROPERTY( m_gl.autostrokel, value ); } //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// void QSAxes3D::defaultSettings() { QSGColor c; QSGLine line; QSGFill fill; line.style = QSGLine::Solid; line.color = c.set( 96, 96, 96 ); m_settings.lines[XYWallLine] = line; m_settings.lines[XZWallLine] = line; m_settings.lines[YZWallLine] = line; fill.color = c.set( 224, 224, 224 ); m_settings.fills[XYWallFill] = fill; fill.color = c.set( 240, 240, 240 ); m_settings.fills[XZWallFill] = fill; m_settings.fills[YZWallFill] = fill; } //-------------------------------------------------------------// void QSAxes3D::draw_arrow() { QSGLine l; m_drv->setLine( l ); double xlen; double ylen; double zlen; get_axis_lengths( &xlen, &ylen, &zlen ); double m[4][4]; t.matrixI( m ); t.applyR( m, 0.0, m_view.lightElevation ); t.applyR( m, 0.0, 0.0, -m_view.lightAzimuth); t.applyS( m, 1.0/xlen, 1.0/ylen, 1.0/zlen ); t.applyS( m, 0.5, 0.5, 0.5 ); t.applyT( m, 0.5, 0.5, 0.5 ); QSPt3f p1; QSPt3f p2; draw_line( t.worldTransformation( m, p1.set( 0.0, 0.0, 0.0 ) ), t.worldTransformation( m, p2.set( 0.0, 0.5, 0.5 ) ) ); draw_line( t.worldTransformation( m, p1.set( 0.0, 0.5, 0.5 ) ), t.worldTransformation( m, p2.set( 0.0, 0.5, 0.17 ) ) ); draw_line( t.worldTransformation( m, p1.set( 0.0, 0.5, 0.17 ) ), t.worldTransformation( m, p2.set( 0.0, 1.0, 0.17 ) ) ); draw_line( t.worldTransformation( m, p1.set( 0.0, 1.0, 0.17 ) ), t.worldTransformation( m, p2.set( 0.0, 1.0, -0.17 ) ) ); draw_line( t.worldTransformation( m, p1.set( 0.0, 1.0, -0.17 ) ), t.worldTransformation( m, p2.set( 0.0, 0.5, -0.17 ) ) ); draw_line( t.worldTransformation( m, p1.set( 0.0, 0.5, -0.17 ) ), t.worldTransformation( m, p2.set( 0.0, 0.5, -0.5 ) ) ); draw_line( t.worldTransformation( m, p1.set( 0.0, 0.5, -0.5 ) ), t.worldTransformation( m, p2.set( 0.0, 0.0, 0.0 ) ) ); } //-------------------------------------------------------------// void QSAxes3D::get_cube_min_max( QSPt3f* min, QSPt3f *max ) { min->set( 0.0, 0.0, 0.0 ); max->set( 1.0, 1.0, 1.0 ); for( int wall=0; wall<6; wall++ ) { QSPt3f p1, p2; bool visible; get_axis_wall( wall, &p1, &p2, &visible ); if ( visible ) { min->set( QMIN(min->x,p1.x), QMIN(min->y,p1.y), QMIN(min->z,p1.z) ); min->set( QMIN(min->x,p2.x), QMIN(min->y,p2.y), QMIN(min->z,p2.z) ); max->set( QMAX(max->x,p1.x), QMAX(max->y,p1.y), QMAX(max->z,p1.z) ); max->set( QMAX(max->x,p2.x), QMAX(max->y,p2.y), QMAX(max->z,p2.z) ); } } } //-------------------------------------------------------------// void QSAxes3D::init_3dtr() { // // Scale, transform and fit (1,1,1) cube // y // ^ / z // | / // --->x // transformation matrix t.matrixI( t.M ); t.matrixI( t.P ); t.matrixI( t.T ); // make the z axis vertical t.applyR( t.M, 0.0, -90.0 ); // shift to the middle t.applyT( t.M, -0.5, -0.5, 0.5 ); // apply lengths set by user double xAxisScale; double yAxisScale; double zAxisScale; get_axis_lengths( &xAxisScale, &yAxisScale, &zAxisScale ); t.applyS( t.M, xAxisScale, zAxisScale, yAxisScale ); QSPt3f cmin; QSPt3f cmax; get_cube_min_max( &cmin, &cmax ); double xlen = (cmax.x-cmin.x)*xAxisScale; double ylen = (cmax.y-cmin.y)*yAxisScale; double zlen = (cmax.z-cmin.z)*zAxisScale; // translate to make sure that all coordinates have // z value lower than 0 ( see focus ). double max_diagonal = sqrt( xlen*xlen + ylen*ylen + zlen*zlen ) / 2.0; // focus distance from 0 ( 1/101 ) to inf ( 101/1 ) // normalize to max_diagonal double focus = (51.0+focusDistance()) / (51.0-focusDistance()) * max_diagonal; double x1 = -1; double x2 = 1; double y1 = -1; double y2 = 1; double z1 = focus; double z2 = focus+3*max_diagonal; if ( perspective() ) { t.frustum( t.P, x1, x2, y1, y2, z1, z2 ); t.setProjection( x1, x2, y1, y2, z1, z2, true ); } else { t.ortho( t.P, x1, x2, y1, y2, z1, z2 ); t.setProjection( x1, x2, y1, y2, z1, z2, false ); } // Scalling matrix. Because I don't know how to deal with frustums, // wanting to fit axes to a view volume I always scale and fit to // the cube. I apply modelview matrix, next // projection matrix ( transforming frustrum into a cube ), fit to // the cube and next apply the inversed projection matrix ( back to frustum ). . // resulting modelview = M*P*S*P-1, resulting projection=P // overall transform M*P*S*P-1*P = M*P*S QSProjection3D::Matrix S; t.matrixI( S ); if ( autoscale() ) { t.applyR( t.M, -azimuth(), 0.0 ); t.applyR( t.M, 0.0, elevation() ); t.applyT( t.M, 0.0, 0.0, -focus-1.01*max_diagonal ); // round-off errors // transformation needs min and max coordinates // of an axis box to be set ( for autoscaling etc. ) get_cube_min_max( &t.bmin, &t.bmax ); // autoscale t.fit( S ); } else { // fit to screen only when graph is in arbitrary choosen positions. QSProjection3D::Matrix oM; t.copy( oM, t.M ); double angle1 = t.radToDeg(atan(yAxisScale/xAxisScale)); double angle2 = t.radToDeg(atan( sqrt(xAxisScale*xAxisScale+ yAxisScale*yAxisScale) / zAxisScale )); // check a scale ratio at the first position t.applyR( t.M, angle1, angle2 ); t.applyT( t.M, 0.0, 0.0, -focus-max_diagonal ); QSProjection3D::Matrix S1; t.fit( S1 ); // the second position t.copy( t.M, oM ); t.applyR( t.M, angle1, 90.0 ); t.applyT( t.M, 0.0, 0.0, -focus-1.01*max_diagonal ); // round-off errors QSProjection3D::Matrix S2; t.fit( S2 ); // apply a smaller scale ratio if ( S1[0][0] < S2[0][0] ) t.copy( S, S1 ); else t.copy( S, S2 ); // viewpoint t.copy( t.M, oM ); t.applyR( t.M, -azimuth(), 0.0 ); t.applyR( t.M, 0.0, elevation() ); t.applyT( t.M, 0.0, 0.0, -focus-max_diagonal ); // transformation needs min and max coordinates // of an axis box to be set get_cube_min_max( &t.bmin, &t.bmax ); } // Scale set by user double scale = (distance()+50.0)/50.0; // tics not working well ( something wrong with drawLine in OpenGL // when line starts at the border of the view volume ) if ( scale == 1.0 ) scale = 0.99; t.applyS( S, scale, scale, 1.0 ); //cout << " S MATRIX " << endl; //QSProjection3D::matrix_to_stdout( S ); //cout << endl; // incorporate scale matrix in M matrix QSProjection3D::Matrix P1; t.inv( P1, t.P ); QSProjection3D::Matrix SCALE; t.matrixI( SCALE ); t.multiply( SCALE, t.P ); t.multiply( SCALE, S ); t.multiply( SCALE, P1 ); //cout << " SCALE MATRIX " << endl; //QSProjection3D::matrix_to_stdout( SCALE ); //cout << endl; t.multiply( t.M, SCALE ); // make a general transformation matrix t.copy( t.T, t.M ); t.multiply( t.T, t.P ); // 0, 0 in the top-left corner t.applyS( t.T, 1.0, -1.0, 1.0 ); // apply ( and remember ) the viewport transformation t.applyViewport( t.T, m_cpos.x, m_cpos.y, m_csize.x, m_csize.y ); t.setViewport( m_cpos.x, m_cpos.y, m_csize.x, m_csize.y ); // // focus vector ( top/bottom detection ) // QSPt3f p; QSProjection3D::Matrix temp; t.inv( temp, t.M ); t.eye = t.worldTransformation(temp, p.set(0.0,0.0,0.0)); if ( !perspective() ) t.dvector = t.normalize( t.worldTransformation( temp, p.set(0.0, 0.0, -1.0) ) - t.eye ); else t.dvector = QSPt3f( 0.0, 0.0, 0.0 ); t.multiply( temp, t.M ); //-----------------------// // // light vector in 3D world coordinates // t.matrixI( temp ); t.applyR( temp, 0.0, lightElevation() ); t.applyR( temp, 0.0, 0.0, -lightAzimuth() ); t.applyS( temp, 1.0/xAxisScale, 1.0/yAxisScale, 1.0/zAxisScale ); t.lvector = t.normalize( t.worldTransformation(temp, p.set(0.0, 1.0, 0.0)) ); t.setLight( light() ); t.setLightParameters( t.lvector, ambientLight(), directLight() ); } //-------------------------------------------------------------// void QSAxes3D::get_axis_lengths( double *x, double *y, double *z ) // see set..AxisLength() { double xlen = m_view.xEdge; double ylen = m_view.yEdge; double zlen = m_view.zEdge; double min_len = min(min(xlen,ylen),zlen); if ( x ) *x = xlen/min_len; if ( y ) *y = ylen/min_len; if ( z ) *z = zlen/min_len; } //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// //-------------------------------------------------------------// void QSAxes3D::initMappings( QSDrv *m_drv ) { QSAxes::initMappings( m_drv ); if ( !state() ) { allocRuntimeData(); init_3dtr(); freeRuntimeData(); m_drv->startDrawing(); m_drv->stopDrawing(); } } //-------------------------------------------------------------// QSPt3f QSAxes3D::mixedToCanvas( const QSPt3f& pos, CoordinateSystem in_coords[3], double dpi, QSAxis *xAxis, QSAxis *yAxis, QSAxis *zAxis ) const // doesn't check if coordinates are right // if there is at least one dataCoord or worldCoord all remaining normCoords and mmCoord are treaten as worldCoord { bool is_3d = false; QSPt3f result = pos; if ( in_coords[0] == dataCoord ) { result.x = xAxis->dataToWorld( pos.x ); is_3d = true; } if ( in_coords[1] == dataCoord ) { result.y = yAxis->dataToWorld( pos.y ); is_3d = true; } if ( in_coords[2] == dataCoord ) { result.z = zAxis->dataToWorld( pos.z ); is_3d = true; } if ( in_coords[0] == worldCoord ) { result.x = pos.x; is_3d = true; } if ( in_coords[1] == worldCoord ) { result.y = pos.y; is_3d = true; } if ( in_coords[2] == worldCoord ) { result.z = pos.z; is_3d = true; } if ( is_3d ) { result = t.world3DToCanvas3( result ); } else { if ( in_coords[0] == normCoord ) result.x = normalizedXToCanvas( pos.x ); if ( in_coords[1] == normCoord ) result.y = normalizedYToCanvas( pos.y ); if ( in_coords[2] == normCoord ) result.z = pos.z; if ( in_coords[0] == mmCoord ) result.x = QSCoord::mmToPixels( pos.x, dpi ); if ( in_coords[1] == mmCoord ) result.y = QSCoord::mmToPixels( pos.y, dpi ); if ( in_coords[2] == mmCoord ) result.z = QSCoord::mmToPixels( pos.z, dpi ); } return result; } //-------------------------------------------------------------// QSPt3f QSAxes3D::canvasToMixed( const QSPt3f& pos, CoordinateSystem out_coords[3], double dpi, QSAxis *xAxis, QSAxis *yAxis, QSAxis *zAxis ) const // if there is at least one dataCoord or worldCoord all remaining normCoords and mmCoord are treaten as worldCoord { QSPt3f result; bool is_3d = false; if ( out_coords[0] == worldCoord ) is_3d = true; if ( out_coords[1] == worldCoord ) is_3d = true; if ( out_coords[2] == worldCoord ) is_3d = true; result = t.canvas3ToWorld3D( pos ); if ( out_coords[0] == dataCoord ) { result.x = xAxis->worldToData( result.x ); is_3d = true; } if ( out_coords[1] == dataCoord ) { result.y = yAxis->worldToData( result.y ); is_3d = true; } if ( out_coords[2] == dataCoord ) { result.z = zAxis->worldToData( result.z ); is_3d = true; } if ( !is_3d ) { if ( out_coords[0] == normCoord ) result.x = canvasToNormalizedX( pos.x ); if ( out_coords[1] == normCoord ) result.y = canvasToNormalizedY( pos.y ); if ( out_coords[2] == normCoord ) result.z = pos.z; if ( out_coords[0] == mmCoord ) result.x = QSCoord::pixelsToMM( pos.x, dpi ); if ( out_coords[1] == mmCoord ) result.y = QSCoord::pixelsToMM( pos.y, dpi ); if ( out_coords[2] == mmCoord ) result.z = QSCoord::pixelsToMM( pos.z, dpi ); } return result; } //-------------------------------------------------------------// void QSAxes3D::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory ) { QSAxes::loadStateFromStream( stream, factory ); } //-------------------------------------------------------------// void QSAxes3D::saveStateToStream( QDataStream& stream, QSObjectFactory *factory ) { QSAxes::saveStateToStream( stream, factory ); }