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
835 lines
27 KiB
13 years ago
|
/***************************************************************************
|
||
|
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;
|
||
|
}
|
||
|
*/
|