You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1280 lines
38 KiB

/***************************************************************************
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<assert.h>
#include<math.h>
#include<algo.h>
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<QSAxisTic>::const_iterator curr = axis->tics()->begin();
list<QSAxisTic>::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<QSAxisTic>::const_iterator curr = axis->tics()->begin();
list<QSAxisTic>::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 );
}