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.
940 lines
34 KiB
940 lines
34 KiB
/***************************************************************************
|
|
qsfigure.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 "qsfigure.h"
|
|
#include "qsdrv.h"
|
|
#include "qsconsole.h"
|
|
#include <algo.h>
|
|
#include <assert.h>
|
|
|
|
//
|
|
// Draw order ( painer algorithm )
|
|
// 1. For each facet remember its index and z-depth in depth_sorted_facets
|
|
// 2. Sort depth_sorted_facets
|
|
// 3. From depth_sorted_facets.beign() to depth_sorted_facets.end()
|
|
// get index to facet
|
|
// draw facet
|
|
//
|
|
//
|
|
// Calculating normal to vertices
|
|
// Normal to vertex is calculated as an average of normals to all
|
|
// facets which contain this vertex. Unfortunately the same vertex may
|
|
// be put a few times ito a vertex table - so you should compare all vertices to find out
|
|
// which of them are actually the same vertex.
|
|
// 1. For each VERTEX put its global index to sorted_global_vertex_table
|
|
// 2. Sort it using vcomp or icomp object
|
|
// - map all coordinates to <0,1> and round to the m_accuracy digits after the point
|
|
// - sort by x, next by y, next by z,
|
|
// 3. Now all the same vertices on the screen are also neighbours in sorted_global_vertex_table.
|
|
// When drawing:
|
|
// 4. But how you can find vertex for example nr. 5 in sorted_global_vertex_table.
|
|
// Index 5 in pos_in_sorted_global_vertex_table points to the position in sorted_global_vertex_table where the vertex nr 5
|
|
// resides.
|
|
// 5. Now potentially equal vertices are:
|
|
// sorted_global_vertex_table[pos_in_sorted_global_vertex_table[5]+1], sorted_global_vertex_table[pos_in_sorted_global_vertex_table[5]+2], ..
|
|
// sorted_global_vertex_table[pos_in_sorted_global_vertex_table[5]-1], sorted_global_vertex_table[pos_in_sorted_global_vertex_table[5]-2], ..
|
|
// Check them to find out if they are really equal ..
|
|
// 6. For each equal vertex find out to which facet it belongs, calculate normal to this facet.
|
|
// 7. Sum all normals and calculate an average
|
|
// We need something less complicated !
|
|
//-------------------------------------------------------------//
|
|
|
|
struct QSFigure::figure_runtime_data {
|
|
enum stage { OrderVertices = 0,
|
|
InitFind = 1,
|
|
FindNeighbours = 2,
|
|
InitDraw = 3,
|
|
DrawFigure = 4,
|
|
Finished = 5 };
|
|
|
|
int pi;
|
|
int pj;
|
|
|
|
QSAxis *xaxis;
|
|
QSAxis *yaxis;
|
|
QSAxis *zaxis;
|
|
QSAxis *vaxis;
|
|
int first_vertex; // order vertices in signle facet ( clockwise or anti-clockwise )
|
|
int vertex_step;
|
|
|
|
int stage;
|
|
int cmode; // see CMode
|
|
int vertex_count;
|
|
int facet_count;
|
|
|
|
bool is_4d_data; // is v coordinate available
|
|
bool is_index_table; // use indexes to build polygons
|
|
bool is_ordered; // is a special drawing order
|
|
|
|
QSPt3f *vertex_data_buff; // vertices of the current facet - temporary buffer ( length vertex_count )
|
|
// vertex number 'nr' of the given facet doesn't need to have position
|
|
// 'nr' in this buffer - order can be reversed ( normals ! ) see
|
|
// d->first_vertex and d->vertex_step
|
|
QSPt3f *vertex_world_buff; // as above - vertices in the world3D coordinates - temporary buffer ( vertex_count )
|
|
double *vertex_value_buff;
|
|
QSPt3f *vertex_temp_buff; // another temporary buffer for vertices of some facet. ( vertex_count )
|
|
QSPt3f *vertex_norm_buff; // normals to vertices buffer plus normat to the whole facet ( vertex_count+1)
|
|
|
|
|
|
QSMatrix *xm;
|
|
QSMatrix *ym;
|
|
QSMatrix *zm;
|
|
QSMatrix *vm;
|
|
QSMatrix *im;
|
|
|
|
QSGPoint point_mark;
|
|
|
|
// description of a single facet
|
|
struct facet_t {
|
|
int i; double z;
|
|
};
|
|
|
|
vector<facet_t>* depth_sorted_facets;
|
|
|
|
// Object for comparing depth of facets. Used with depth_sorted_facets - Ordering facets.
|
|
struct comp_t : public binary_function<facet_t, facet_t, bool> {
|
|
inline bool operator()( const facet_t& f1, const facet_t& f2 ) { return furthermost_first ? f1.z<f2.z : f1.z>f2.z; }
|
|
bool furthermost_first; // further first
|
|
} comp;
|
|
|
|
// Object for comparing vertices of facets.
|
|
// Find the same vertices for an average normal calculating.
|
|
// 1. compare x coordinate
|
|
// 2. compare y coordinate
|
|
// 3. compare z coordinate
|
|
struct vcomp_t : public binary_function<int, int, bool> {
|
|
// return global_vertex(v1) < global_vertex(v2)
|
|
// v1 and v2 are normalized numbers
|
|
bool operator()( int v1, int v2 ) {
|
|
int vertex1 = d->vertex( v1 );
|
|
int vertex2 = d->vertex( v2 );
|
|
int facet1 = d->facet( v1 );
|
|
int facet2 = d->facet( v2 );
|
|
double val1 = vroundx(d->xm->value(facet1,vertex1));
|
|
double val2 = vroundx(d->xm->value(facet2,vertex2));
|
|
if ( val1 > val2 ) return false;
|
|
else if ( val1 == val2 ) {
|
|
val1 = vroundy(d->ym->value(facet1,vertex1));
|
|
val2 = vroundy(d->ym->value(facet2,vertex2));
|
|
if ( val1 > val2 ) return false;
|
|
else if ( val1 == val2 ) {
|
|
val1 = vroundz(d->zm->value(facet1,vertex1));
|
|
val2 = vroundz(d->zm->value(facet2,vertex2));
|
|
if ( val1 >= val2 ) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// map to <0,1> and round to ac digits after the point
|
|
inline double vroundx( double value ) { return floor((value-minx)*acsx+0.5); }
|
|
inline double vroundy( double value ) { return floor((value-miny)*acsy+0.5); }
|
|
inline double vroundz( double value ) { return floor((value-minz)*acsz+0.5); }
|
|
figure_runtime_data *d;
|
|
double acsx; // accuracy/scalex
|
|
double acsy; // accuracy/scaley
|
|
double acsz; // accuracy/scalez
|
|
double minx;
|
|
double miny;
|
|
double minz;
|
|
} vcomp;
|
|
|
|
// Compare vertices of facets for the case with the table of indices.
|
|
// Find the same vertices for an average normal calculating.
|
|
struct icomp_t : public binary_function<int, int, bool> {
|
|
// return global_vertex(v1) < global_vertex(v2)
|
|
inline bool operator()( int v1, int v2 ) {
|
|
return (int)d->im->value(d->facet(v1),d->vertex(v1)) < (int)d->im->value(d->facet(v2),d->vertex(v2));
|
|
}
|
|
figure_runtime_data *d;
|
|
} icomp;
|
|
|
|
vector<facet_t>::iterator bit;
|
|
vector<facet_t>::iterator eit;
|
|
vector<int>* sorted_global_vertex_table;
|
|
vector<int>::iterator bvit;
|
|
vector<int>::iterator evit;
|
|
vector<int>* pos_in_sorted_global_vertex_table;
|
|
|
|
// returns index which vertex 'vertex' of facet 'facet' has in 'pos_to_sorted_global_vertex_table'
|
|
inline int global_vertex_index( int facet, int vertex ) {
|
|
return facet*vertex_count+vertex;
|
|
}
|
|
|
|
inline int facet( int global_vertex_index ) {
|
|
return global_vertex_index/vertex_count;
|
|
}
|
|
|
|
inline int vertex( int global_vertex_index ) {
|
|
return global_vertex_index%vertex_count;
|
|
}
|
|
|
|
vector<int>* processed_facets; // neighbouring facets ( vertex_count ? )
|
|
|
|
inline bool facet_processed( int facet ) {
|
|
return binary_search(processed_facets->begin(),processed_facets->end(),facet);
|
|
}
|
|
inline void set_facet_processed( int facet ) {
|
|
processed_facets->insert(lower_bound(processed_facets->begin(),processed_facets->end(),facet),facet);
|
|
}
|
|
inline void clear_processed_facets() {
|
|
processed_facets->erase( processed_facets->begin(),processed_facets->end() );
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
enum CMode { One,
|
|
MAuto,
|
|
VAuto,
|
|
MUserG,
|
|
MUserRGB,
|
|
MUserRGBA,
|
|
VUserG,
|
|
VUserRGB,
|
|
VUserRGBA };
|
|
*/
|
|
// QSGFill *vertex_fill_buff; // fills of vertices buffer ( vertex_count )
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
QSFigure::QSFigure(QSAxes* parent, const char * name)
|
|
:QSPlot3D(parent,name)
|
|
{
|
|
assert( parent );
|
|
m_accuracy = 4;
|
|
m_extremes_valid = false;
|
|
m_minmax_v_valid = false;
|
|
m_title_str = tr("Untitled figure");
|
|
m_dmin.set( 0.0, 0.0, 0.0 );
|
|
m_dmax.set( 0.0, 0.0, 0.0 );
|
|
m_vmin = 0.0;
|
|
m_vmax = 0.0;
|
|
#define CHANNELS_NUM 5
|
|
initChannelTable( CHANNELS_NUM );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
QSFigure::~QSFigure()
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
|
|
void QSFigure::setVertexCompareAccuracy( int accuracy )
|
|
{
|
|
SET_PROPERTY( m_accuracy, accuracy );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::dataChanged( int channel )
|
|
// Ouu. We need to calculate new extremes in data
|
|
// Refresh data on screen also.
|
|
{
|
|
if ( channel == -1 ||
|
|
channel == VXCoord ||
|
|
channel == VYCoord ||
|
|
channel == VZCoord ||
|
|
channel == VTableI ) m_extremes_valid = false;
|
|
if ( channel == VVCoord ) m_minmax_v_valid = false;
|
|
QSPlot3D::dataChanged( channel );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::allocRuntimeData()
|
|
{
|
|
QSPlot3D::allocRuntimeData();
|
|
d = new figure_runtime_data();
|
|
|
|
d->depth_sorted_facets = NULL;
|
|
d->vertex_data_buff = NULL;
|
|
d->vertex_norm_buff = NULL;
|
|
d->vertex_world_buff = NULL;
|
|
d->vertex_value_buff = NULL;
|
|
d->vertex_temp_buff = NULL;
|
|
// d->vertex_fill_buff = NULL;
|
|
|
|
d->sorted_global_vertex_table = NULL;
|
|
d->pos_in_sorted_global_vertex_table = NULL;
|
|
d->processed_facets = NULL;
|
|
|
|
d->xaxis = defaultAxis(QSAxis::XAxisType);
|
|
d->yaxis = defaultAxis(QSAxis::YAxisType);
|
|
d->zaxis = defaultAxis(QSAxis::ZAxisType);
|
|
d->vaxis = defaultAxis(QSAxis::VAxisType);
|
|
|
|
d->stage = 0;
|
|
init_data();
|
|
bool vertex_order_reverse = false;
|
|
if (d->xaxis->reversed()) vertex_order_reverse = !vertex_order_reverse;
|
|
if (d->yaxis->reversed()) vertex_order_reverse = !vertex_order_reverse;
|
|
if (d->zaxis->reversed()) vertex_order_reverse = !vertex_order_reverse;
|
|
|
|
if ( vertex_order_reverse ) { d->first_vertex = d->vertex_count-1; d->vertex_step= - 1; }
|
|
else { d->first_vertex = 0; d->vertex_step = 1; }
|
|
|
|
d->point_mark = m_settings.points[PointMark];
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::init_data()
|
|
// Check sizes of tables and which table with vertices we will be using.
|
|
{
|
|
d->facet_count = 0;
|
|
d->vertex_count = 0;
|
|
if ( matrixCols( VXCoord ) == matrixCols( VYCoord ) &&
|
|
matrixRows( VXCoord ) == matrixRows( VYCoord ) &&
|
|
matrixCols( VYCoord ) == matrixCols( VZCoord ) &&
|
|
matrixRows( VYCoord ) == matrixRows( VZCoord ) &&
|
|
matrixRows( VXCoord ) > 0 &&
|
|
matrixCols( VXCoord ) > 0 ) {
|
|
d->facet_count = matrixRows( VXCoord );
|
|
d->vertex_count = matrixCols( VXCoord );
|
|
}
|
|
|
|
d->is_4d_data = false;
|
|
if ( matrixCols( VVCoord ) == matrixCols( VXCoord ) &&
|
|
matrixRows( VVCoord ) == matrixRows( VXCoord ) ) {
|
|
d->is_4d_data = true;
|
|
}
|
|
|
|
d->is_index_table = false;
|
|
if ( matrixCols( VTableI ) > 0 && matrixRows( VTableI ) > 0 ) {
|
|
d->is_index_table = true;
|
|
d->vertex_count = matrixCols( VTableI );
|
|
d->facet_count = matrixRows( VTableI );
|
|
}
|
|
|
|
// clip buffers etc.
|
|
if ( d->vertex_count > 40 ) {
|
|
d->vertex_count = 0;
|
|
d->facet_count = 0;
|
|
}
|
|
|
|
// ordering of facets
|
|
d->is_ordered = ( m_corder != QSDrv::NoOrdering );
|
|
d->comp.furthermost_first = ( m_corder == QSDrv::FurtherFirst );
|
|
|
|
// facets data
|
|
d->xm = matrix( VXCoord );
|
|
d->ym = matrix( VYCoord );
|
|
d->zm = matrix( VZCoord );
|
|
d->vm = matrix( VVCoord );
|
|
d->im = matrix( VTableI );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::freeRuntimeData()
|
|
{
|
|
free_buffers();
|
|
delete d; d=NULL;
|
|
QSPlot3D::freeRuntimeData();
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
bool QSFigure::getAxisRange( QSAxis *axis, double& min, double& max )
|
|
{
|
|
allocRuntimeData();
|
|
if ( d->facet_count == 0 || d->vertex_count == 0 ) {
|
|
freeRuntimeData();
|
|
return false;
|
|
}
|
|
|
|
if ( !m_extremes_valid || !m_minmax_v_valid ) {
|
|
QSPt3f *vertex_buff = new QSPt3f[d->vertex_count+1];
|
|
double *value_buff = new double[d->vertex_count+1];
|
|
for( int pi=0; pi<d->facet_count; pi++ ) {
|
|
QSPt3f facet_min;
|
|
QSPt3f facet_max;
|
|
double value_min;
|
|
double value_max;
|
|
get_facet( pi, vertex_buff, value_buff );
|
|
facet_max.x = facet_min.x = vertex_buff[0].x;
|
|
facet_max.y = facet_min.y = vertex_buff[0].y;
|
|
facet_max.z = facet_min.z = vertex_buff[0].z;
|
|
value_min = value_max = value_buff[0];
|
|
for ( int k=1; k<d->vertex_count; k++ ) {
|
|
double x = vertex_buff[k].x ;
|
|
double y = vertex_buff[k].y ;
|
|
double z = vertex_buff[k].z ;
|
|
double v = value_buff[k];
|
|
if ( x < facet_min.x ) facet_min.x = x;
|
|
if ( y < facet_min.y ) facet_min.y = y;
|
|
if ( z < facet_min.z ) facet_min.z = z;
|
|
if ( x > facet_max.x ) facet_max.x = x;
|
|
if ( y > facet_max.y ) facet_max.y = y;
|
|
if ( z > facet_max.z ) facet_max.z = z;
|
|
if ( v < value_min ) value_min = v;
|
|
if ( v > value_max ) value_max = v;
|
|
}
|
|
|
|
if ( facet_min.x < m_dmin.x || pi == 0 ) m_dmin.x = facet_min.x;
|
|
if ( facet_min.y < m_dmin.y || pi == 0 ) m_dmin.y = facet_min.y;
|
|
if ( facet_min.z < m_dmin.z || pi == 0 ) m_dmin.z = facet_min.z;
|
|
|
|
if ( facet_max.x > m_dmax.x || pi == 0 ) m_dmax.x = facet_max.x;
|
|
if ( facet_max.y > m_dmax.y || pi == 0 ) m_dmax.y = facet_max.y;
|
|
if ( facet_max.z > m_dmax.z || pi == 0 ) m_dmax.z = facet_max.z;
|
|
|
|
if ( value_min < m_vmin || pi == 0 ) m_vmin = value_min;
|
|
if ( value_max > m_vmax || pi == 0 ) m_vmax = value_max;
|
|
}
|
|
|
|
delete[] vertex_buff;
|
|
delete[] value_buff;
|
|
m_extremes_valid = true;
|
|
m_minmax_v_valid = true;
|
|
}
|
|
|
|
if ( axis==d->xaxis ) { min = m_dmin.x; max = m_dmax.x; }
|
|
else if ( axis==d->yaxis ) { min = m_dmin.y; max = m_dmax.y; }
|
|
else if ( axis==d->zaxis ) { min = m_dmin.z; max = m_dmax.z; }
|
|
else if ( axis==d->vaxis && !d->is_4d_data ) { min = m_dmin.z; max = m_dmax.z; }
|
|
else if ( axis==d->vaxis && d->is_4d_data ) { min = m_vmin; max = m_vmax; }
|
|
else { freeRuntimeData(); return false; }
|
|
|
|
freeRuntimeData();
|
|
return true;
|
|
}
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::get_facet( int facet, QSPt3f *vert, double *values )
|
|
// Return all vertex of an 'nr' facet in 'vert' table.
|
|
// This table must be large enough to contain all vertices.
|
|
{
|
|
int i = d->first_vertex;
|
|
if ( d->is_index_table ) {
|
|
for( int vertex=0; vertex<d->vertex_count; vertex++, i+=d->vertex_step ) {
|
|
int vertex_index = int( d->im->value( facet, vertex ) );
|
|
if ( vertex_index <0 || vertex_index >= d->xm->rows() ) {
|
|
QSConsole::write( tr("Figure:%1 : Invalid index %2 at row=%3, col=%4 ").arg(title()).arg(vertex_index).arg(facet).arg(vertex) );
|
|
vert[i].set(sqrt(-1.0),sqrt(-1.0),sqrt(-1.0));
|
|
if ( d->is_4d_data && values ) values[i] = sqrt(-1.0);
|
|
} else {
|
|
vert[i].set( d->xm->value( vertex_index, 0 ),
|
|
d->ym->value( vertex_index, 0 ),
|
|
d->zm->value( vertex_index, 0 ) );
|
|
if ( d->is_4d_data && values ) values[i] = d->vm->value( vertex_index, 0 );
|
|
}
|
|
}
|
|
} else {
|
|
for( int vertex=0; vertex<d->vertex_count; vertex++, i+=d->vertex_step ) {
|
|
vert[i].set( d->xm->value( facet, vertex ),
|
|
d->ym->value( facet, vertex ),
|
|
d->zm->value( facet, vertex ) );
|
|
if ( d->is_4d_data && values ) values[i] = d->vm->value( facet, vertex );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
bool QSFigure::start()
|
|
{
|
|
QSPlot3D::start();
|
|
|
|
if ( d->facet_count == 0 || d->vertex_count == 0 ) return false;
|
|
alloc_buffers();
|
|
// init_colors();
|
|
d->stage = 0;
|
|
d->pi = 0;
|
|
d->pj = 0;
|
|
return true;
|
|
}
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::alloc_buffers()
|
|
{
|
|
assert( d );
|
|
free_buffers();
|
|
|
|
d->vertex_data_buff = new QSPt3f[d->vertex_count];
|
|
d->vertex_value_buff = new double[d->vertex_count];
|
|
d->vertex_world_buff = new QSPt3f[d->vertex_count];
|
|
d->vertex_temp_buff = new QSPt3f[d->vertex_count];
|
|
d->vertex_norm_buff = new QSPt3f[d->vertex_count+1];
|
|
// d->vertex_fill_buff = new QSGFill[d->vertex_count];
|
|
|
|
if ( d->is_ordered ) d->depth_sorted_facets = new vector<figure_runtime_data::facet_t>( d->facet_count );
|
|
|
|
if ( m_cnormals == QSDrv::VertexNormals ) {
|
|
d->sorted_global_vertex_table = new vector<int>( d->vertex_count*d->facet_count );
|
|
d->pos_in_sorted_global_vertex_table = new vector<int>( d->vertex_count*d->facet_count );
|
|
d->processed_facets = new vector<int>();
|
|
d->processed_facets->reserve( d->vertex_count );
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::free_buffers()
|
|
{
|
|
assert( d );
|
|
|
|
delete d->depth_sorted_facets; d->depth_sorted_facets = NULL;
|
|
delete d->sorted_global_vertex_table; d->sorted_global_vertex_table = NULL;
|
|
delete d->pos_in_sorted_global_vertex_table; d->pos_in_sorted_global_vertex_table = NULL;
|
|
delete d->processed_facets; d->processed_facets = NULL;
|
|
|
|
delete[] d->vertex_data_buff; d->vertex_data_buff = NULL;
|
|
delete[] d->vertex_value_buff; d->vertex_value_buff = NULL;
|
|
delete[] d->vertex_world_buff; d->vertex_world_buff = NULL;
|
|
delete[] d->vertex_temp_buff; d->vertex_temp_buff = NULL;
|
|
delete[] d->vertex_norm_buff; d->vertex_norm_buff = NULL;
|
|
// delete[] d->vertex_fill_buff; d->vertex_fill_buff = NULL;
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
/*
|
|
void QSFigure::init_colors()
|
|
// Uff !!
|
|
{
|
|
assert( d );
|
|
|
|
if ( !colored() ) { d->cmode = figure_runtime_data::One; return; }
|
|
|
|
if ( m_ccolors == QSDrv::VertexColors ) d->cmode = figure_runtime_data::VAuto;
|
|
else d->cmode = figure_runtime_data::MAuto;
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
//-------------------------------------------------------------//
|
|
/*
|
|
void QSFigure::get_facet_colors( int findex, QSGFill *vertex_fill_buff )
|
|
// vertex_world_buff must be defined
|
|
//
|
|
{
|
|
int k;
|
|
double sum = 0.0;
|
|
switch( d->cmode ) {
|
|
case figure_runtime_data::VAuto:
|
|
for( k=0; k<d->vertex_count; k++ ) m_gradient.fill( d->vertex_world_buff[k].z, vertex_fill_buff[k] );
|
|
break;
|
|
case figure_runtime_data::MAuto:
|
|
for( k=0; k<d->vertex_count; k++ ) sum+=d->vertex_world_buff[k].z;
|
|
m_gradient.fill( sum/d->vertex_count, vertex_fill_buff[0] );
|
|
break;
|
|
default: vertex_fill_buff[0] = fill(TMeshFill);
|
|
if ( m_ccolors == QSDrv::VertexColors ) for( k=0; k<d->vertex_count; k++ ) vertex_fill_buff[k] = vertex_fill_buff[0];
|
|
break;
|
|
}
|
|
|
|
}
|
|
*/
|
|
|
|
//-------------------------------------------------------------//
|
|
//-------------------------------------------------------------//
|
|
|
|
bool QSFigure::step()
|
|
{
|
|
switch( d->stage ) {
|
|
case figure_runtime_data::OrderVertices: order_vertices_step(); break;
|
|
case figure_runtime_data::InitFind: init_find_neighbours(); break;
|
|
case figure_runtime_data::FindNeighbours: find_neighbours_step(); break;
|
|
case figure_runtime_data::InitDraw: init_draw_figure(); break;
|
|
case figure_runtime_data::DrawFigure: draw_figure_step(); break;
|
|
default: return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::end()
|
|
{
|
|
free_buffers();
|
|
QSPlot3D::end();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::order_vertices_step()
|
|
{
|
|
if ( !d->is_ordered ) { d->pi = 0; d->stage ++; return; }
|
|
|
|
double z;
|
|
double maxz;
|
|
double minz;
|
|
|
|
int curr_step = 0;
|
|
while ( d->pi<d->facet_count ) {
|
|
get_facet( d->pi, d->vertex_data_buff );
|
|
minz = maxz = m_axes->proj()->world3DToCanvas3(dataToWorld(d->vertex_data_buff[0])).z;
|
|
for ( int k=1; k<d->vertex_count; k++ ) {
|
|
z = m_axes->proj()->world3DToCanvas3(dataToWorld(d->vertex_data_buff[k])).z;
|
|
if ( z > maxz ) maxz = z;
|
|
if ( z < minz ) minz = z;
|
|
}
|
|
|
|
(*d->depth_sorted_facets)[d->pi].i = d->pi;
|
|
(*d->depth_sorted_facets)[d->pi].z = d->comp.furthermost_first ? minz : maxz;
|
|
d->pi++; if ( ++curr_step > work_steps && m_bkg_handler ) return;
|
|
}
|
|
|
|
d->bit = d->depth_sorted_facets->begin();
|
|
d->eit = d->depth_sorted_facets->end();
|
|
make_heap( d->bit, d->eit, d->comp );
|
|
|
|
d->pi = 0; d->stage ++;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::init_find_neighbours()
|
|
{
|
|
if ( m_cnormals == QSDrv::VertexNormals ) {
|
|
d->icomp.d = d;
|
|
d->vcomp.d = d;
|
|
|
|
double accuracy = pow( 10, m_accuracy );
|
|
d->vcomp.minx = m_dmin.x;
|
|
d->vcomp.miny = m_dmin.y;
|
|
d->vcomp.minz = m_dmin.z;
|
|
|
|
if ( m_dmax.x > m_dmin.x ) d->vcomp.acsx = accuracy/(m_dmax.x-m_dmin.x);
|
|
else d->vcomp.acsx = accuracy;
|
|
if ( m_dmax.y > m_dmin.y ) d->vcomp.acsy = accuracy/(m_dmax.y-m_dmin.y);
|
|
else d->vcomp.acsy = accuracy;
|
|
if ( m_dmax.z > m_dmin.z ) d->vcomp.acsz = accuracy/(m_dmax.z-m_dmin.z);
|
|
else d->vcomp.acsz = accuracy;
|
|
|
|
for( int global_vertex=0; global_vertex<d->vertex_count*d->facet_count; global_vertex++ )
|
|
(*d->sorted_global_vertex_table)[global_vertex] = global_vertex;
|
|
|
|
d->bvit = d->sorted_global_vertex_table->begin();
|
|
d->evit = d->sorted_global_vertex_table->end();
|
|
// sorting make_heap + pop_heap
|
|
if ( d->is_index_table ) make_heap( d->bvit, d->evit, d->icomp );
|
|
else make_heap( d->bvit, d->evit, d->vcomp );
|
|
|
|
|
|
}
|
|
|
|
d->stage ++;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::find_neighbours_step()
|
|
{
|
|
if ( m_cnormals != QSDrv::VertexNormals ) { d->pi=0 ; d->stage ++; return; }
|
|
int curr_step = 0;
|
|
while ( d->evit != d->bvit ) {
|
|
if ( d->is_index_table ) pop_heap( d->bvit, d->evit, d->icomp );
|
|
else pop_heap( d->bvit, d->evit, d->vcomp );
|
|
d->evit --; if ( ++curr_step > work_steps && m_bkg_handler ) return;
|
|
}
|
|
|
|
for( int k=0; k<d->facet_count*d->vertex_count; k++ )
|
|
(*d->pos_in_sorted_global_vertex_table)[(*d->sorted_global_vertex_table)[k]]=k;
|
|
|
|
d->pi = 0; d->stage ++;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::init_draw_figure()
|
|
{
|
|
m_drv->setLine( line(MeshLine) );
|
|
d->pi = 0; d->stage ++;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::draw_figure_step()
|
|
// facet_count - number of facets
|
|
// vertex_count - number of vertices in a facet
|
|
// vertex_world_buff[] - buffer of coordinates of vertices
|
|
// vertex_fill_buff[] - buffer of fills of vertices
|
|
// vertex_norm_buff[0] - normal to the current facet
|
|
// vertex_norm_buff[] - buffer of normals to vertices
|
|
//
|
|
{
|
|
int facet;
|
|
int curr_step = 0;
|
|
while( d->pi< d->facet_count ) {
|
|
|
|
// Get the current facet.
|
|
if ( d->is_ordered ) { pop_heap( d->bit, d->eit, d->comp ); d->eit--; }
|
|
facet = d->is_ordered?(*d->eit).i:d->pi;
|
|
get_facet( facet, d->vertex_data_buff, d->vertex_value_buff );
|
|
|
|
// Prepare all vertices.
|
|
map_facet_to_world( d->vertex_data_buff, d->vertex_world_buff );
|
|
|
|
// Update vertex_fill_buff with colors.
|
|
// get_facet_colors( facet, d->vertex_fill_buff );
|
|
|
|
// The normal to the current facet.
|
|
if ( m_cnormals != QSDrv::NoNormals ) d->vertex_norm_buff[0] = QSProjection::normal( d->vertex_world_buff, d->vertex_count );
|
|
|
|
// Normals to the vertices of the current facet
|
|
if ( m_cnormals == QSDrv::VertexNormals && d->vertex_count > 2 ) get_facet_normals( facet, &d->vertex_norm_buff[1] );
|
|
|
|
// Draw the final polygon.
|
|
double *value_buff = NULL;
|
|
if ( d->is_4d_data ) {
|
|
for( int i=0; i<d->vertex_count; i++ ) d->vertex_value_buff[i] = d->vaxis->dataToWorld( d->vertex_value_buff[i] );
|
|
value_buff = d->vertex_value_buff;
|
|
}
|
|
if ( d->vertex_count > 2 ) drawPolygon( d->vertex_world_buff, d->vertex_count, d->vertex_norm_buff, value_buff );
|
|
else
|
|
// lines
|
|
if ( d->vertex_count == 2 ) m_drv->drawLine3( d->vertex_world_buff[0], d->vertex_world_buff[1], NULL ); // no vertex_norm_buff
|
|
|
|
// trajectory/scatter plot
|
|
if ( d->vertex_count == 1 && facet > 0 && m_settings.lines[MeshLine].style != QSGLine::Invisible ) {
|
|
// connect succesive points with a line
|
|
// further end can overdraw closer end !
|
|
QSPt3f temp_wbuff[2];
|
|
// QSGFill temp_fbuff[2];
|
|
get_facet( facet-1, temp_wbuff );
|
|
map_facet_to_world( temp_wbuff, temp_wbuff );
|
|
// get_facet_colors( facet-1, temp_fbuff );
|
|
temp_wbuff[1] = d->vertex_world_buff[0];
|
|
// temp_fbuff[1] = d->vertex_fill_buff[0];
|
|
m_drv->drawLine3( temp_wbuff[0], temp_wbuff[1], NULL );
|
|
draw_point_marks( 2, temp_wbuff );
|
|
}
|
|
else
|
|
// draw points
|
|
draw_point_marks( d->vertex_count, d->vertex_world_buff );
|
|
|
|
d->pi++;
|
|
if ( ++curr_step > work_steps && m_bkg_handler ) return;
|
|
}
|
|
|
|
d->stage ++;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::draw_point_marks( int number, const QSPt3f* vertex_world_buff )
|
|
// points shoud be sorted by position ( further first or closer first )
|
|
{
|
|
if ( d->point_mark.style != QSGPoint::Invisible )
|
|
for ( int k=0; k<number; k++ ) {
|
|
/*
|
|
if ( d->cmode == figure_runtime_data::MUserG ||
|
|
d->cmode == figure_runtime_data::MUserRGB ||
|
|
d->cmode == figure_runtime_data::MUserRGBA ) d->point_mark.color = vertex_fill_buff[0].color;
|
|
if ( d->cmode == figure_runtime_data::VUserG ||
|
|
d->cmode == figure_runtime_data::VUserRGB ||
|
|
d->cmode == figure_runtime_data::VUserRGBA ) d->point_mark.color = vertex_fill_buff[k+1].color;
|
|
*/
|
|
if ( m_axes->proj()->clipPoint3(vertex_world_buff[k]) ) {
|
|
m_drv->drawPoint3( vertex_world_buff[k], d->point_mark );
|
|
}
|
|
}
|
|
m_drv->setLine( m_settings.lines[MeshLine] );
|
|
m_drv->setFill( m_settings.fills[TMeshFill] );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::map_facet_to_world( const QSPt3f *vertex_data_buff, QSPt3f *vertex_world_buff )
|
|
{
|
|
for( int k=0; k<d->vertex_count; k++ ) vertex_world_buff[k] = dataToWorld( vertex_data_buff[k] );
|
|
}
|
|
|
|
//#include<iostream.h>
|
|
|
|
void QSFigure::get_facet_normals( int facet, QSPt3f *normals )
|
|
// For each vertex accumulate normals of facet itself and all neighouring facets ( and next normalize the result ).
|
|
// Neighbouring facets are ones which share current vertex with this facet, How to find shared vertices
|
|
// ( it is difficult if the same vertex is put a few times into a vertex table ):
|
|
// 1. for each vertex of the facet - find this vertex in sorted_global_vertex_table[]
|
|
// 2. Test if neighbouring ( at previous and next positions ) vertices in this table have the same coordinates as our vertex:
|
|
// 3. If they have find a facet which they belong to, calculate normal to this facet and add this normal to the result
|
|
// . ( Take next vertex of a facet and go to 2 )
|
|
// 4. Normalize the result
|
|
{
|
|
int i=d->first_vertex;
|
|
for( int vertex=0; vertex<d->vertex_count; vertex++, i+=d->vertex_step ) {
|
|
/////////////////////////////////////////////
|
|
// Calculate normal for vertex 'vertex' //
|
|
/////////////////////////////////////////////
|
|
normals[i].set( 0.0, 0.0, 0.0 );
|
|
int bindex1 = (*d->pos_in_sorted_global_vertex_table)[d->global_vertex_index(facet,vertex)];
|
|
// test next position and add normal of current facet
|
|
get_vertex_normal( bindex1, bindex1, 1, &normals[i] );
|
|
// test previous positions
|
|
get_vertex_normal( bindex1, bindex1-1, -1, &normals[i] );
|
|
d->clear_processed_facets();
|
|
normals[i] = QSProjection::normalize( normals[i] );
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::get_vertex_normal( int vertex_index, int search_start_index, int search_index_step, QSPt3f *normal )
|
|
// Calculate normal for vertex sorted_global_vertex_table[bindex1], by adding
|
|
// normals to all facets which the current vertex belongs to.
|
|
// Check only in one direction
|
|
{
|
|
int curr_index = search_start_index;
|
|
while( is_equal(vertex_index,curr_index) ) {
|
|
//Find a facet which vertex 'vert_index2' belongs to. All indices are indices to sorted_vertex_table .
|
|
int findex = d->facet( (*d->sorted_global_vertex_table)[curr_index] );
|
|
|
|
//////////////////////////////////////////////
|
|
// add normal of facet findex to the result//
|
|
//////////////////////////////////////////////
|
|
if ( !d->facet_processed(findex) ) {
|
|
d->set_facet_processed(findex);
|
|
get_facet( findex, d->vertex_temp_buff );
|
|
map_facet_to_world( d->vertex_temp_buff, d->vertex_temp_buff );
|
|
*normal = *normal + QSProjection::normal( d->vertex_temp_buff, d->vertex_count );
|
|
}
|
|
curr_index += search_index_step;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
bool QSFigure::is_equal( int index1, int index2 )
|
|
// Compare two wertices sorted_global_vertex_table[index1] == sorted_global_vertex_table[index2].
|
|
{
|
|
int gnum = d->vertex_count*d->facet_count-1;
|
|
|
|
if ( index1 < 0 || index1 > gnum ||
|
|
index2 < 0 || index2 > gnum ) return false;
|
|
|
|
int v1 = (*d->sorted_global_vertex_table)[index1];
|
|
int v2 = (*d->sorted_global_vertex_table)[index2];
|
|
|
|
int vertex1 = d->vertex( v1 );
|
|
int vertex2 = d->vertex( v2 );
|
|
int facet1 = d->facet( v1 );
|
|
int facet2 = d->facet( v2 );
|
|
|
|
if ( d->is_index_table ) return int(d->im->value(facet1,vertex1)) == int(d->im->value(facet2,vertex2));
|
|
|
|
return d->vcomp.vroundx(d->xm->value(facet1,vertex1)) == d->vcomp.vroundx(d->xm->value(facet2,vertex2)) &&
|
|
d->vcomp.vroundy(d->ym->value(facet1,vertex1)) == d->vcomp.vroundy(d->ym->value(facet2,vertex2)) &&
|
|
d->vcomp.vroundz(d->zm->value(facet1,vertex1)) == d->vcomp.vroundz(d->zm->value(facet2,vertex2)) ;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
QString QSFigure::posInfo( QSPt2f& pos )
|
|
{
|
|
if ( m_busy ) return QString::null;
|
|
allocRuntimeData();
|
|
|
|
QSPt2f p;
|
|
double pz = 0.0;
|
|
bool init = false;
|
|
double pdistance = 10.0*10.0;
|
|
QString result = QString::null;
|
|
QSPt3f *vertex_buff = new QSPt3f[d->vertex_count+1];
|
|
double *value_buff = new double[d->vertex_count+1];
|
|
for ( int f=0; f<d->facet_count; f++ ) {
|
|
get_facet( f, vertex_buff, value_buff );
|
|
for ( int k=0; k<d->vertex_count; k++ ) {
|
|
QSPt3f dpos = vertex_buff[k];;
|
|
double value = value_buff[k];
|
|
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<pz||!init) ) {
|
|
pdistance = distance;
|
|
init = true; pz = cpos.z; p = QSPt2f(cpos.x,cpos.y);
|
|
result = QString(tr("row = "))+QString::number(f)+ "\n";
|
|
result += QString(tr("col = "))+QString::number(k)+ "\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(value)+ "\n";
|
|
}
|
|
}
|
|
}
|
|
delete[] vertex_buff;
|
|
delete[] value_buff;
|
|
freeRuntimeData();
|
|
if ( result != QString::null ) pos = p;
|
|
return result;
|
|
}
|
|
//-------------------------------------------------------------//
|
|
|
|
QSPt2f QSFigure::legendItemSize( QSDrv *drv )
|
|
{
|
|
return standardLegendItemSize( drv, defaultAxis(QSAxis::ZAxisType), title() );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::drawLegendItem( const QSPt2f& pos, QSDrv *drv )
|
|
{
|
|
drawStandardLegendItem( pos, drv, defaultAxis(QSAxis::ZAxisType), title(), &m_gradient );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory )
|
|
{
|
|
QSPlot3D::loadStateFromStream( stream, factory );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
void QSFigure::saveStateToStream( QDataStream& stream, QSObjectFactory *factory )
|
|
{
|
|
QSPlot3D::saveStateToStream( stream, factory );
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
QString QSFigure::channelVariable( int channel ) const
|
|
{
|
|
switch( channel ) {
|
|
case VXCoord: return "x";
|
|
case VYCoord: return "y";
|
|
case VZCoord: return "z";
|
|
case VVCoord: return "v";
|
|
case VTableI: return "i";
|
|
}
|
|
return QString::null;
|
|
}
|
|
|
|
//-------------------------------------------------------------//
|
|
|
|
QSFigure::ColumnType QSFigure::columnType( int channel, int column ) const
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|