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.

835 lines
27 KiB

/***************************************************************************
qssurface.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<stdio.h>
#include<math.h>
#include<assert.h>
#include<qapplication.h>
#include"qssurface.h"
#include"qsdrv.h"
/*
We don't use sorting do use painter algorithm to hide invisible surfaces but rather
divide surface into four quarters. Let's look at our surface from the top:
X or i or cols
--> <--
+------+------+ |
| | | |
| | 0 1 | \/
\/ | M |
+ + + Y or j or rows
^ | | ^
| | 2 3 | |
| | | |
+------+------+
--> <--
0,1,2,3 - d->quarter.number
--> ... d->quatrer.di, quarter.dj ( drawing direction )
M - quarter.middle
Notice that all four quarters are nessesary only when perspective is on. In other
cases only quarter 0 is drawn ( it covers the whole surface ).
*/
struct QSSurface::surface_runtime_data {
QSPt2 middle;
QSPt2 min;
QSPt2 max;
QSPt2 delta;
struct quarter_t {
bool empty;
int number;
int iMin, iMax;
int jMin, jMax;
int di, dj;
int ci, cj;
struct t_t { int p0, p1, p2; } t[2]; // standard way of dividing mesh into two triangles
} quarter;
int w;
int h;
int pi;
int pj;
QSAxis *xaxis;
QSAxis *yaxis;
QSAxis *zaxis;
QSAxis *vaxis;
bool vnormals;
bool is_x_index;
bool is_y_index;
bool is_4d_data;
QSMatrix *x_vector;
QSMatrix *y_vector;
QSMatrix *z_data;
QSMatrix *v_data;
bool x_reversed;
bool y_reversed;
int x_grid_step;
int y_grid_step;
QSGPoint point_mark;
/* increased col should have increased value in world coordinates.
It is important when you want to preserve clockwise direction
of all polygons */
inline int col_norm( int col ) { return x_reversed ? w-1-col : col; }
inline int row_norm( int row ) { return y_reversed ? h-1-row : row; }
double xworld( int col ) { return xaxis->dataToWorld( xvector(col_norm(col)) ); }
double yworld( int row ) { return yaxis->dataToWorld( yvector(row_norm(row)) ); }
inline double zworld( int row, int col ) { return zaxis->dataToWorld( zdata(col_norm(col),row_norm(row)) ); }
inline double vworld( int row, int col ) { return vaxis->dataToWorld( vdata(col_norm(col),row_norm(row)) ); }
double xvector( int col ) { return is_x_index ? x_vector->value(0,col) : col; }
double yvector( int row ) { return is_y_index ? y_vector->value(row,0) : row; }
inline double zdata( int row, int col ) { return z_data->value(row,col); }
inline double vdata( int row, int col ) { return v_data->value(row,col); }
QSPt3f mesh_pts[4];
QSPt3f mesh_norms[5];
double mesh_values[4];
bool mesh_edges[4];
QSPt3f canvas_pts[4];
QSPt3f triangle_pts[3];
QSPt3f triangle_norms[4];
double triangle_values[3];
bool triangle_edges[3];
// QSPt3f *point_mark;
int curr_row;
int curr_col_first;
int curr_col_last;
double *x_coords;
struct row_z_t { double *ptr; double y; int row; } row_z_buff[4];
row_z_t *row_z[4];
struct row_mn_t { QSPt3f *ptr; int row; } row_mn_buff[3];
row_mn_t *row_mn[3];
struct row_vn_t { QSPt3f *ptr; int row; } row_vn_buff[2];
row_vn_t *row_vn[3];
struct row_v_t { double *ptr; int row; } row_v_buff[2];
row_v_t *row_v[3];
row_z_t *get_row_z( int row );
row_v_t *get_row_v( int row );
row_mn_t *get_row_mn( int row );
row_vn_t *get_row_vn( int row );
void invalidate_row_buffers();
void set_current_row( int row, int first_col, int last_col );
bool get_polygon( int row, int col, QSPt3f pts[4] );
bool get_mesh( int col, QSPt3f pts[4], QSPt3f norms[5], double v[4] );
};
//-------------------------------------------------------------//
QSSurface::QSSurface(QSAxes* parent, const char * name)
:QSPlot3D(parent,name)
{
assert( parent );
m_minmax_z_valid = false;
m_minmax_v_valid = false;
m_min_z = 0.0;
m_max_z = 0.0;
m_min_v = 0.0;
m_max_v = 0.0;
m_x_grid_step = 1;
m_y_grid_step = 1;
m_title_str = tr("Untitled surface");
#define CHANNELS_NUM 4
initChannelTable( CHANNELS_NUM );
}
//-------------------------------------------------------------//
QSSurface::~QSSurface()
{
}
//-------------------------------------------------------------//
void QSSurface::setXGridStep( int step )
{
SET_PROPERTY( m_x_grid_step, QMAX(1,step) );
}
//-------------------------------------------------------------//
void QSSurface::setYGridStep( int step )
{
SET_PROPERTY( m_y_grid_step, QMAX(1,step) );
}
//-------------------------------------------------------------//
void QSSurface::dataChanged( int channel )
// Ouu. We need to calculate new extremes in data
// Refresh data on screen also.
{
if ( channel == ZData || channel == -1 ) m_minmax_z_valid = false;
if ( channel == VData || channel == -1 ) m_minmax_v_valid = false;
QSPlot3D::dataChanged( channel );
}
//-------------------------------------------------------------//
void QSSurface::allocRuntimeData()
{
QSPlot3D::allocRuntimeData();
d = new surface_runtime_data();
d->x_vector = matrix(XVector);
d->y_vector = matrix(YVector);
d->z_data = matrix(ZData);
d->v_data = matrix(VData);
d->xaxis = defaultAxis(QSAxis::XAxisType);
d->yaxis = defaultAxis(QSAxis::YAxisType);
d->zaxis = defaultAxis(QSAxis::ZAxisType);
d->vaxis = defaultAxis(QSAxis::VAxisType);
d->w = matrixCols( ZData );
d->h = matrixRows( ZData );
d->is_x_index = false;
d->is_y_index = false;
d->is_4d_data = false;
d->x_grid_step = xGridStep();
d->y_grid_step = yGridStep();
d->vnormals = ( m_cnormals == QSDrv::VertexNormals );
if ( matrixCols(XVector) == d->w ) d->is_x_index = true;
if ( matrixRows(YVector) == d->h ) d->is_y_index = true;
if ( matrixRows(VData) == d->h &&
matrixCols(VData) == d->w ) d->is_4d_data = true;
d->point_mark = m_settings.points[PointMark];
// zero in world coordinates not nessesary means zero index to data matrix...
// dataToWorld(xvector(0)) is 0 or 1 ?
d->x_reversed = false;
d->y_reversed = false;
if ( d->xaxis->reversed() ) d->x_reversed = !d->x_reversed;
if ( d->yaxis->reversed() ) d->y_reversed = !d->y_reversed;
if ( d->xvector(0) > d->xvector(d->w-1) ) d->x_reversed = !d->x_reversed;
if ( d->yvector(0) > d->yvector(d->h-1) ) d->y_reversed = !d->y_reversed;
// x buffer
d->x_coords = new double[d->w];
for( int i=0;i<d->w;i++) d->x_coords[i] = d->xworld(i);
// row buffers
for ( int i=0; i<4; i++ ) {
d->row_z_buff[i].ptr = new double[d->w];
d->row_z_buff[i].row = -1;
}
for ( int i=0; i<3; i++ ) {
d->row_mn_buff[i].ptr = new QSPt3f[d->w];
d->row_mn_buff[i].row = -1;
}
for ( int i=0; i<2; i++ ) {
d->row_vn_buff[i].ptr = new QSPt3f[d->w];
d->row_vn_buff[i].row = -1;
}
for ( int i=0; i<2; i++ ) {
d->row_v_buff[i].ptr = new double[d->w];
d->row_v_buff[i].row = -1;
}
}
//-------------------------------------------------------------//
void QSSurface::freeRuntimeData()
{
delete[] d->x_coords;
for( int i=0; i<4; i++ ) delete[] d->row_z_buff[i].ptr;
for( int i=0; i<3; i++ ) delete[] d->row_mn_buff[i].ptr;
for( int i=0; i<2; i++ ) delete[] d->row_vn_buff[i].ptr;
for( int i=0; i<2; i++ ) delete[] d->row_v_buff[i].ptr;
delete d; d=NULL;
QSPlot3D::freeRuntimeData();
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
bool QSSurface::getAxisRange( QSAxis *axis, double& min, double& max )
{
allocRuntimeData();
if ( d->h == 0 || d->w == 0 ) { freeRuntimeData(); return false; }
if ( !m_minmax_z_valid ) {
for ( int j=0;j<d->h;j++ ) {
for ( int i=0;i<d->w;i++ ) {
double z = d->zdata(j,i);
if ( i == 0 && j == 0 ) {
m_max_z = z;
m_min_z = z;
} else {
m_min_z = QMIN( m_min_z, z );
m_max_z = QMAX( m_max_z, z );
}
}
}
m_minmax_z_valid = true;
}
if ( !m_minmax_v_valid && d->is_4d_data ) {
for ( int j=0;j<d->h;j++ ) {
for ( int i=0;i<d->w;i++ ) {
double v = d->vdata(j,i);
if ( i == 0 && j == 0 ) {
m_max_v = v;
m_min_v = v;
} else {
m_min_v = QMIN( m_min_v, v );
m_max_v = QMAX( m_max_v, v );
}
}
}
m_minmax_v_valid = true;
}
double xmin = d->xvector(0);
double xmax = d->xvector(d->w-1);
double ymin = d->yvector(0);
double ymax = d->yvector(d->h-1);
if ( xmin > xmax ) { double xtemp = xmin; xmin = xmax; xmax = xtemp; }
if ( ymin > ymax ) { double ytemp = ymin; ymin = ymax; ymax = ytemp; }
if ( axis == d->xaxis ) { min = xmin; max = xmax; }
else if ( axis == d->yaxis ) { min = ymin; max = ymax; }
else if ( axis == d->zaxis ) { min = m_min_z; max = m_max_z; }
else if ( axis == d->vaxis && !d->is_4d_data ) { min = m_min_z; max = m_max_z; }
else if ( axis == d->vaxis && d->is_4d_data ) { min = m_min_v; max = m_max_v; }
else { freeRuntimeData(); return false; }
freeRuntimeData();
return true;
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
bool QSSurface::start()
{
QSPlot3D::start();
if ( d->w == 0 || d->h == 0 ) return false;
init_loops();
d->quarter.number = 0;
prepare_quarter();
m_drv->setLine( line(MeshLine) );
m_drv->setFill( fill(TMeshFill) );
return true;
}
//-------------------------------------------------------------//
void QSSurface::init_loops()
{
// visible range in index to ZData
// no need to draw grid outside (min.x, max.x), (min.y, max.y )
// because it is outside the view area.
d->min.x = d->w; // empty area
d->max.x = -1;
for( int i=0;i<d->w;i++ ) {
if ( d->xworld(i)>=0.0 && d->xworld(i)<=1.0 ) {
d->min.x = QMIN(d->min.x,QMAX( 0,i-1));
d->max.x = QMAX(d->max.x,QMIN(d->w-1,i+1));
}
}
d->min.y = d->h; // empty area
d->max.y = -1;
for( int i=0;i<d->h;i++ ) {
if ( d->yworld(i)>=0.0 && d->yworld(i)<=1.0 ) {
d->min.y = QMIN(d->min.y,QMAX( 0,i-1));
d->max.y = QMAX(d->max.y,QMIN(d->h-1,i+1));
}
}
// delta must be set as in the first quarter
d->delta.x = 1;
d->delta.y = 1;
// middle point - middle x and y must be a valid index
QSPt2f middle = m_axes->proj()->middle(); // in world coordinates
d->middle.x = d->w-1; for( int i=1;i<d->w;i++) if ( d->xworld(i) > middle.x ) { d->middle.x=QMAX(0,i-1); break; }
d->middle.y = d->h-1; for( int i=1;i<d->h;i++) if ( d->yworld(i) > middle.y ) { d->middle.y=QMAX(0,i-1); break; }
}
//-------------------------------------------------------------//
//
// 0 3
//
// 1 2
//
void QSSurface::prepare_quarter()
// divide graph into four parts with different drawing directions
// to be sure that invisible areas are hidden ( further objects are
// drawn first ).
{
// empty quarter
d->quarter.empty = true;
///if ( !m_axes->perspective() && d->quarter.number > 0 ) return;
d->quarter.iMin = d->min.x;
d->quarter.iMax = d->max.x;
d->quarter.jMin = d->min.y;
d->quarter.jMax = d->max.y;
d->quarter.di = d->delta.x;
d->quarter.dj = d->delta.y;
switch( d->quarter.number ) {
case 0: break;
case 1: d->quarter.di = -d->quarter.di; break;
case 2: d->quarter.di = -d->quarter.di;
d->quarter.dj = -d->quarter.dj; break;
case 3: d->quarter.dj = -d->quarter.dj; break;
default: break;
}
// divide intor triangles
switch( d->quarter.number ) {
case 0: d->quarter.t[0].p0 = 3; d->quarter.t[0].p1 = 0; d->quarter.t[0].p2 = 1;
d->quarter.t[1].p0 = 1; d->quarter.t[1].p1 = 2; d->quarter.t[1].p2 = 3; break;
case 1: d->quarter.t[0].p0 = 2; d->quarter.t[0].p1 = 3; d->quarter.t[0].p2 = 0;
d->quarter.t[1].p0 = 0; d->quarter.t[1].p1 = 1; d->quarter.t[1].p2 = 2; break;
case 2: d->quarter.t[0].p0 = 1; d->quarter.t[0].p1 = 2; d->quarter.t[0].p2 = 3;
d->quarter.t[1].p0 = 3; d->quarter.t[1].p1 = 0; d->quarter.t[1].p2 = 1; break;
case 3: d->quarter.t[0].p0 = 0; d->quarter.t[0].p1 = 1; d->quarter.t[0].p2 = 2;
d->quarter.t[1].p0 = 2; d->quarter.t[1].p1 = 3; d->quarter.t[1].p2 = 0; break;
}
if ( d->quarter.di > 0 ) d->quarter.iMax = d->middle.x; else d->quarter.iMin = d->middle.x+1;
if ( d->quarter.dj > 0 ) d->quarter.jMax = d->middle.y; else d->quarter.jMin = d->middle.y+1;
// reverse the scan direction for OpenGL ( more effective )
if ( m_corder == QSDrv::NearerFirst ) {
d->quarter.di = -d->quarter.di;
d->quarter.dj = -d->quarter.dj;
}
// empty quarter
if ( (d->quarter.iMax-d->quarter.iMin) < 1 || (d->quarter.jMax-d->quarter.jMin) < 1 ) return;
// start positions
if ( d->quarter.di > 0 ) d->quarter.ci = d->quarter.iMin; else d->quarter.ci = d->quarter.iMax;
if ( d->quarter.dj > 0 ) d->quarter.cj = d->quarter.jMin; else d->quarter.cj = d->quarter.jMax;
d->invalidate_row_buffers();
d->set_current_row( d->quarter.cj, d->quarter.iMin, d->quarter.iMax );
d->quarter.empty = false;
}
//-------------------------------------------------------------//
void QSSurface::end()
{
QSPlot3D::end();
}
//-------------------------------------------------------------//
//-------------------------------------------------------------//
//-------------------------------------------------------------//
void QSSurface::surface_runtime_data::invalidate_row_buffers()
{
for( int i=0; i<4; i++ ) row_z_buff[i].row = -1;
for( int i=0; i<2; i++ ) row_v_buff[i].row = -1;
for( int i=0; i<3; i++ ) row_mn_buff[i].row = -1;
for( int i=0; i<2; i++ ) row_vn_buff[i].row = -1;
}
//-------------------------------------------------------------//
void QSSurface::surface_runtime_data::set_current_row( int row, int first_col, int last_col )
// fills rows buffers with values
// row_z [0-3] -> [curr_row-2, curr_row+1] - z values
// row_v [0-1] -> [curr_row-1, curr_row] - v values ( 4d data )
// row_mn [0-2] -> [curr_row-1, curr_row+1] - normals to meshes
// row_vn [0-1] -> [curr_row-1, curr_row] - normal to vertices
{
curr_col_first = first_col;
curr_col_last = last_col;
curr_row = row;
int i;
// we have four lines in buff ( from row-2, to row+1 )
i=0; for( int curr_row=row-2; curr_row<=row+1; curr_row++ ) row_z[i++] = get_row_z( curr_row );
// mesh normals
i=0; for( int curr_row=row-1; curr_row<=row+1; curr_row++ ) row_mn[i++] = get_row_mn( curr_row );
// vertex normals ( curr_row-1, curr_row )
i=0;
if ( vnormals )
for( int curr_row=row-1; curr_row<=row; curr_row++ ) row_vn[i++] = get_row_vn( curr_row );
// values ( 4d data ) - ( curr_row-1, curr_row )
i=0;
if ( is_4d_data )
for( int curr_row=row-1; curr_row<=row; curr_row++ ) row_v[i++] = get_row_v( curr_row );
}
//-------------------------------------------------------------//
QSSurface::surface_runtime_data::row_z_t *QSSurface::surface_runtime_data::get_row_z( int row )
// Notice the way a buffer for fill is searched for. -
// you should only request row inside range <curr_row-2,curr_row+1>
{
// row outside a valid range
if ( row<0 || row>h-1 ) return NULL;
// check if row is already in buffer
for( int i=0; i<4; i++ ) if ( row == row_z_buff[i].row ) return &row_z_buff[i];
// fill a new row, notice that row request can be only in range <curr_row-2,curr_row+1>
// so we can free any buffer which doesn't hold a row from this range
for( int i=0; i<4; i++ )
if ( row_z_buff[i].row < curr_row-2 ||
row_z_buff[i].row > curr_row+1 ||
row_z_buff[i].row == -1 ) {
int col_start = QMAX( curr_col_first-1, 0 );
int col_stop = QMIN( curr_col_last +1, w-1 );
for( int col=col_start; col<=col_stop; col++ )
row_z_buff[i].ptr[col] = zworld(row,col);
row_z_buff[i].y = yworld(row);
row_z_buff[i].row = row;
return &row_z_buff[i];
}
assert( false );
return NULL;
}
//-------------------------------------------------------------//
QSSurface::surface_runtime_data::row_mn_t *QSSurface::surface_runtime_data::get_row_mn( int row )
// you can request only rows in range <curr_row-1, curr_row+1>
// it assumes that z buffers are already filled with correct z-values
{
if ( row<1 || row>h-1 ) return NULL;
// check if row is already in buffer
for( int i=0; i<3; i++ ) if ( row == row_mn_buff[i].row ) return &row_mn_buff[i];
// fill a new row
for( int i=0; i<3; i++ )
if ( row_mn_buff[i].row < curr_row-1 ||
row_mn_buff[i].row > curr_row+1 ||
row_mn_buff[i].row == -1 ) {
QSPt3f mesh_buff[4];
for( int col=curr_col_first; col<=QMIN(w-1,curr_col_last+1); col++ )
if ( get_polygon( row, col, mesh_buff ) ) row_mn_buff[i].ptr[col] = QSProjection::normal( mesh_buff );
row_mn_buff[i].row = row;
return &row_mn_buff[i];
}
assert( false );
return NULL;
}
//-------------------------------------------------------------//
QSSurface::surface_runtime_data::row_vn_t *QSSurface::surface_runtime_data::get_row_vn( int row )
// you can request only rows in range <curr_row-1, curr_row>
// it assumes that mesh normal buffers are already filled.
{
if ( row<0 || row>h-1 ) return NULL;
// check if row is already in buffer
for( int i=0; i<2; i++ ) if ( row == row_vn_buff[i].row ) return &row_vn_buff[i];
// fill a new row
for( int i=0; i<3; i++ )
if ( row_vn_buff[i].row < curr_row-1 ||
row_vn_buff[i].row > curr_row ||
row_vn_buff[i].row == -1 ) {
for( int col=curr_col_first; col<=curr_col_last; col++ ) {
QSPt3f n;
row_mn_t *curr_row_buff = row_mn[curr_row-row+1];
row_mn_t *next_row_buff = row_mn[curr_row-row+2];
if ( curr_row_buff && col>0 ) n = n + curr_row_buff->ptr[col];
if ( curr_row_buff && col<w-1 ) n = n + curr_row_buff->ptr[col+1];
if ( next_row_buff && col>0 ) n = n + next_row_buff->ptr[col];
if ( next_row_buff && col<w-1 ) n = n + next_row_buff->ptr[col+1];
row_vn_buff[i].ptr[col] = QSProjection::normalize( n );
}
row_vn_buff[i].row = row;
return &row_vn_buff[i];
}
assert( false );
return NULL;
}
//-------------------------------------------------------------//
QSSurface::surface_runtime_data::row_v_t *QSSurface::surface_runtime_data::get_row_v( int row )
// you can request only rows in range <curr_row-1, curr_row>
{
if ( row<0 || row>h-1 ) return NULL;
// check if row is already in buffer
for( int i=0; i<2; i++ ) if ( row == row_v_buff[i].row ) return &row_v_buff[i];
// fill a new row
for( int i=0; i<3; i++ )
if ( row_v_buff[i].row < curr_row-1 ||
row_v_buff[i].row > curr_row ||
row_v_buff[i].row == -1 ) {
for( int col=curr_col_first; col<=curr_col_last; col++ )
row_v_buff[i].ptr[col] = vworld(row,col);
row_v_buff[i].row = row;
return &row_v_buff[i];
}
assert( false );
return NULL;
}
//-------------------------------------------------------------//
// e0
// 0 ----- 1
// | | ---> cols
// |e3 | e1
// 3 ----- 2 \/ rows
// e2
// vertex nr. 2 is at current (row,col)
// Buffers:
bool QSSurface::surface_runtime_data::get_mesh( int col, QSPt3f pts[4], QSPt3f norms[5], double v[4] )
{
if ( col == 0 || curr_row == 0 ) return false;
get_polygon( curr_row, col, pts );
norms[0] = row_mn[1]->ptr[col];
int row = curr_row;
if ( vnormals ) {
norms[0] = row_vn[0]->ptr[col-1];
norms[3] = row_vn[0]->ptr[col-0];
norms[2] = row_vn[1]->ptr[col-0];
norms[1] = row_vn[1]->ptr[col-1];
}
if ( is_4d_data ) {
v[0] = row_v[0]->ptr[col-1];
v[3] = row_v[0]->ptr[col-0];
v[2] = row_v[1]->ptr[col-0];
v[1] = row_v[1]->ptr[col-1];
}
return true;
}
//-------------------------------------------------------------//
bool QSSurface::surface_runtime_data::get_polygon( int row, int col, QSPt3f pts[4] )
// returns mesh coordinates for the row 'row' and col 'col'
// row can be in the range <curr_row-1,curr_row+1>. See: set_current_row()
{
row_z_t *prev_row_buff = row_z[row-curr_row+1];
row_z_t *curr_row_buff = row_z[row-curr_row+2];
if ( prev_row_buff && curr_row_buff && col > 0 ) {
pts[0].set( x_coords[col-1], prev_row_buff->y, prev_row_buff->ptr[col-1] );
pts[3].set( x_coords[col-0], prev_row_buff->y, prev_row_buff->ptr[col-0] );
pts[2].set( x_coords[col-0], curr_row_buff->y, curr_row_buff->ptr[col-0] );
pts[1].set( x_coords[col-1], curr_row_buff->y, curr_row_buff->ptr[col-1] );
return true;
}
return false;
}
//-------------------------------------------------------------//
bool QSSurface::step()
{
int curr_step = 0;
if ( !d->quarter.empty )
while ( d->quarter.cj >= d->quarter.jMin && d->quarter.cj <= d->quarter.jMax ) {
while ( d->quarter.ci >= d->quarter.iMin && d->quarter.ci <= d->quarter.iMax ) {
if ( d->get_mesh( d->quarter.ci, d->mesh_pts, d->mesh_norms, d->mesh_values ) ) {
if ( m_x_grid_step > 1 || m_y_grid_step > 1 ) {
d->mesh_edges[1] = ((d->quarter.ci-1)%m_x_grid_step) == 0;
d->mesh_edges[0] = ((d->quarter.cj-1)%m_y_grid_step) == 0;
d->mesh_edges[3] = ((d->quarter.ci)%m_x_grid_step) == 0;
d->mesh_edges[2] = ((d->quarter.cj)%m_y_grid_step) == 0;
draw_polygon( d->mesh_pts, d->mesh_norms, (d->is_4d_data ? d->mesh_values : NULL), d->mesh_edges );
} else {
draw_polygon( d->mesh_pts, d->mesh_norms, (d->is_4d_data ? d->mesh_values : NULL) );
}
}
d->quarter.ci += d->quarter.di;
// end with this little part ...
if ( curr_step++ > work_steps && m_bkg_handler ) return true;
}
d->quarter.cj += d->quarter.dj;
d->quarter.ci = d->quarter.di>0 ? d->quarter.iMin : d->quarter.iMax;
d->set_current_row( d->quarter.cj, d->quarter.iMin, d->quarter.iMax );
}
if ( d->quarter.number < 3 ) {
d->quarter.number += 1;
prepare_quarter();
return true;
}
return false;
}
//-------------------------------------------------------------//
void QSSurface::draw_polygon( const QSPt3f pts[4], QSPt3f *norms, const double *values, const bool *edges )
{
for( int i=0; i<4; i++ ) d->canvas_pts[i] = m_proj->world3DToCanvas3( pts[i] );
if ( !QSProjection::isCorrect(pts) ||
!QSProjection::isFlat(d->canvas_pts,4) ||
( values && !QSProjection::isCorrect(values) ) ) {
for( int i=0; i<2; i++ ) {
d->triangle_pts[0] = pts[d->quarter.t[i].p0];
d->triangle_pts[1] = pts[d->quarter.t[i].p1];
d->triangle_pts[2] = pts[d->quarter.t[i].p2];
d->triangle_norms[0] = QSProjection::normal( d->triangle_pts, 3 ); //norms[0];
if ( d->vnormals ) {
d->triangle_norms[1] = norms[d->quarter.t[i].p0+1];
d->triangle_norms[2] = norms[d->quarter.t[i].p1+1];
d->triangle_norms[3] = norms[d->quarter.t[i].p2+1];
}
if ( values ) {
d->triangle_values[0] = values[d->quarter.t[i].p0];
d->triangle_values[1] = values[d->quarter.t[i].p1];
d->triangle_values[2] = values[d->quarter.t[i].p2];
}
d->triangle_edges[0] = false;
d->triangle_edges[1] = edges ? edges[d->quarter.t[i].p1] : true;
d->triangle_edges[2] = edges ? edges[d->quarter.t[i].p2] : true;
drawPolygon( d->triangle_pts, 3, d->triangle_norms, (values?d->triangle_values:NULL), d->triangle_edges );
}
} else {
drawPolygon( pts, 4, norms, values, edges );
}
}
//-------------------------------------------------------------//
QString QSSurface::posInfo( QSPt2f& pos )
{
if ( m_busy ) return QString::null;
allocRuntimeData();
if ( d->w == 0 || d->h == 0 ) return QString::null;
init_loops();
QSPt2f p;
double dz = 0.0;
bool init = false;
double pdistance = 10.0*10.0;
QString result = QString::null;
for ( int row=d->min.y; row<=d->max.y; row++ ) {
for ( int col=d->min.x; col<=d->max.x; col++ ) {
QSPt3f dpos(d->xvector(col),d->yvector(row),d->zdata(row,col));
QSPt3f wpos = dataToWorld(dpos);
QSPt3f cpos = m_proj->world3DToCanvas3(wpos);
double distance = (pos.x-cpos.x)*(pos.x-cpos.x)+(pos.y-cpos.y)*(pos.y-cpos.y);
if ( distance < 5.0*5.0 && distance < pdistance && (cpos.z<dz||!init) ) {
pdistance = distance;
init = true; dz = cpos.z; p.set(cpos.x,cpos.y);
result = QString(tr("row = "))+QString::number(row)+"\n";
result += QString(tr("col = "))+QString::number(col)+"\n";
result += QString(tr("X = "))+QString::number(dpos.x)+"\n";
result += QString(tr("Y = "))+QString::number(dpos.y)+"\n";
result += QString(tr("Z = "))+QString::number(dpos.z)+"\n";
if ( d->is_4d_data )
result += QString(tr("V = "))+QString::number(d->vdata(row,col))+"\n";
}
}
}
if ( result != QString::null ) pos = p;
freeRuntimeData();
return result;
}
//-------------------------------------------------------------//
QSPt2f QSSurface::legendItemSize( QSDrv *drv )
{
return standardLegendItemSize( drv, defaultAxis(QSAxis::ZAxisType), title() );
}
//-------------------------------------------------------------//
void QSSurface::drawLegendItem( const QSPt2f& pos, QSDrv *drv )
{
drawStandardLegendItem( pos, drv, defaultAxis(QSAxis::ZAxisType), title(), &m_gradient );
}
//-------------------------------------------------------------//
void QSSurface::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory )
{
QSPlot3D::loadStateFromStream( stream, factory );
}
//-------------------------------------------------------------//
void QSSurface::saveStateToStream( QDataStream& stream, QSObjectFactory *factory )
{
QSPlot3D::saveStateToStream( stream, factory );
}
//-------------------------------------------------------------//
QString QSSurface::channelVariable( int channel ) const
{
switch( channel ) {
case ZData: return "z";
case XVector: return "x";
case YVector: return "y";
case VData: return "v";
}
return QString::null;
}
//-------------------------------------------------------------//
QSSurface::ColumnType QSSurface::columnType( int channel, int column ) const
{
}
//-------------------------------------------------------------//
/*
if ( d->quarter.number == 0 ) {
d->mesh_edges[1] = ((d->quarter.ci-1)%m_x_grid_step) == 0;
d->mesh_edges[0] = ((d->quarter.cj-1)%m_y_grid_step) == 0;
}
if ( d->quarter.number == 1 ) {
d->mesh_edges[3] = ((d->quarter.ci)%m_x_grid_step) == 0;
d->mesh_edges[0] = ((d->quarter.cj-1)%m_y_grid_step) == 0;
}
if ( d->quarter.number == 3 ) {
d->mesh_edges[3] = ((d->quarter.ci)%m_x_grid_step) == 0;
d->mesh_edges[2] = ((d->quarter.cj)%m_y_grid_step) == 0;
}
if ( d->quarter.number == 2 ) {
d->mesh_edges[1] = ((d->quarter.ci-1)%m_x_grid_step) == 0;
d->mesh_edges[2] = ((d->quarter.cj)%m_y_grid_step) == 0;
}
*/