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.
413 lines
12 KiB
413 lines
12 KiB
13 years ago
|
/***************************************************************************
|
||
|
ksdelunay.cpp
|
||
|
-------------------
|
||
|
begin :
|
||
|
copyright : (C) 2001 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"mpdelunay.h"
|
||
|
#include<math.h>
|
||
|
#include<algo.h>
|
||
|
#include<qobject.h>
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
|
||
|
MPDelunay::MPDelunay( MPSymbolList *args, int columnFrom, int columnTo, const char *id )
|
||
|
: MPSymbol( args, columnFrom, columnTo, id )
|
||
|
{
|
||
|
m_triangle_buff = 0;
|
||
|
m_triangle_buff_len = 0;
|
||
|
m_point_data = NULL;
|
||
|
m_triangle_data = NULL;
|
||
|
m_edge_data = NULL;
|
||
|
m_edges = 0;
|
||
|
m_edge_buffer_capacity = 0;
|
||
|
m_point_data = NULL;
|
||
|
m_points = 0;
|
||
|
m_triangles = 0;
|
||
|
m_active_triangles = 0;
|
||
|
m_p1 = NULL;
|
||
|
m_p2 = NULL;
|
||
|
m_p3 = NULL;
|
||
|
m_result = NULL;
|
||
|
m_complete = NULL;
|
||
|
m_error = QString::null;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
MPDelunay::~MPDelunay()
|
||
|
{
|
||
|
delete[] m_triangle_buff;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
|
||
|
void MPDelunay::checkArgs( MPError& error )
|
||
|
{
|
||
|
stop();
|
||
|
delete m_triangle_buff; m_triangle_buff = 0;
|
||
|
m_rows = 0;
|
||
|
m_cols = 0;
|
||
|
m_error = QString::null;
|
||
|
|
||
|
for ( int i=0; i<m_args->count(); i++ ) m_args->at(i)->checkArgs(error);
|
||
|
MPSymbol::checkArgs( error );
|
||
|
if ( m_args->count() != 2 ) { error.setWrongNumberOfArguments( m_args->count(), 2, this ); return; }
|
||
|
|
||
|
triangulation( m_args->at(0), m_args->at(1), true );
|
||
|
|
||
|
if ( !m_error.isEmpty() ) {
|
||
|
error.setError( m_error,this );
|
||
|
m_rows = 0;
|
||
|
m_cols = 0;
|
||
|
m_error = QString::null;
|
||
|
delete m_triangle_buff; m_triangle_buff = 0;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
void MPDelunay::stop()
|
||
|
{
|
||
|
delete[] m_complete;
|
||
|
m_complete = NULL;
|
||
|
|
||
|
delete[] m_point_data;
|
||
|
m_point_data = NULL;
|
||
|
m_points = 0;
|
||
|
|
||
|
delete[] m_triangle_data;
|
||
|
m_triangle_data = NULL;
|
||
|
m_active_triangles = 0;
|
||
|
m_triangles = 0;
|
||
|
|
||
|
delete[] m_edge_data;
|
||
|
m_edge_data = NULL;
|
||
|
m_edge_buffer_capacity = 0;
|
||
|
m_edges = 0;
|
||
|
|
||
|
delete[] m_result;
|
||
|
m_result = NULL;
|
||
|
|
||
|
m_p1 = NULL;
|
||
|
m_p2 = NULL;
|
||
|
m_p3 = NULL;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------//
|
||
|
|
||
|
double MPDelunay::value( int row , int col )
|
||
|
{
|
||
|
return m_triangle_buff[row+col*m_triangle_buff_len];
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
void MPDelunay::set_error( const QString& message )
|
||
|
{
|
||
|
m_error += message + "\n";
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
void MPDelunay::triangulation( MPSymbol *xColumn, MPSymbol *yColumn, bool useLargeBuffers )
|
||
|
{
|
||
|
m_error = QString::null;
|
||
|
if ( xColumn->rows() != yColumn->rows() ) {
|
||
|
set_error( QObject::tr("X and Y must have the same number of rows !") );
|
||
|
stop();
|
||
|
return;
|
||
|
}
|
||
|
if ( xColumn->cols() != 1 ||
|
||
|
yColumn->cols() != 1 ) {
|
||
|
set_error( QObject::tr("X and Y must be column vectors !") );
|
||
|
stop();
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
if ( xColumn->rows() < 3 ) {
|
||
|
set_error( QObject::tr("There must be at least three points !") );
|
||
|
stop();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/////////////////////////
|
||
|
// prepare input data //
|
||
|
/////////////////////////
|
||
|
|
||
|
// copy all points to the internal buffer
|
||
|
// internal buffer will hold also 3 vertices of bounding super triangle
|
||
|
m_points = xColumn->rows();
|
||
|
m_point_data = new point_data_t[m_points+3];
|
||
|
for( int i=0; i<m_points; i++ ) {
|
||
|
m_point_data[i].index = i;
|
||
|
m_point_data[i].x = xColumn->value( i, 0 );
|
||
|
m_point_data[i].y = yColumn->value( i, 0 );;
|
||
|
}
|
||
|
|
||
|
// presort values using x coordinates - simple bubble sort
|
||
|
sort( m_point_data, m_point_data+m_points );
|
||
|
|
||
|
point_data_t *new_end = unique( m_point_data, m_point_data+m_points );
|
||
|
|
||
|
if ( new_end != m_point_data+m_points ) {
|
||
|
m_points = new_end - m_point_data;
|
||
|
}
|
||
|
|
||
|
if ( m_points < 3 ) {
|
||
|
set_error( QObject::tr("There must be at least three different points !") );
|
||
|
stop();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// calculate the bounding box
|
||
|
double x_min = m_point_data[0].x;
|
||
|
double y_min = m_point_data[0].y;
|
||
|
double x_max = m_point_data[0].x;
|
||
|
double y_max = m_point_data[0].y;
|
||
|
for( int i=1; i<m_points; i++ ) {
|
||
|
if ( x_min > m_point_data[i].x ) x_min = m_point_data[i].x;
|
||
|
if ( y_min > m_point_data[i].y ) y_min = m_point_data[i].y;
|
||
|
if ( x_max < m_point_data[i].x ) x_max = m_point_data[i].x;
|
||
|
if ( y_max < m_point_data[i].y ) y_max = m_point_data[i].y;
|
||
|
}
|
||
|
|
||
|
// construct the bounding super triangle
|
||
|
// anti-clockwise(?) order
|
||
|
// this will be the default order of all triangles
|
||
|
m_point_data[m_points+0].x = x_min - 1.0;
|
||
|
m_point_data[m_points+0].y = y_min - 1.0;
|
||
|
m_point_data[m_points+1].x = x_min - 1.0;
|
||
|
m_point_data[m_points+1].y = y_max + ( y_max-y_min ) + 1.0;
|
||
|
m_point_data[m_points+2].x = x_max + ( x_max-x_min ) + 1.0;
|
||
|
m_point_data[m_points+2].y = y_min - 1.0;
|
||
|
m_point_data[m_points+0].index = -1;
|
||
|
m_point_data[m_points+1].index = -1;
|
||
|
m_point_data[m_points+2].index = -1;
|
||
|
|
||
|
// allocate nessesary space for the output
|
||
|
// max. number of triangles is 2*(m_points+3)
|
||
|
m_triangles = 0;
|
||
|
int triangles_max = (m_points+3)*2;
|
||
|
m_result = new long[3*triangles_max]; // buffer for output vertices
|
||
|
m_p1 = m_result;
|
||
|
m_p2 = m_p1 + triangles_max;
|
||
|
m_p3 = m_p2 + triangles_max;
|
||
|
m_complete = new bool[triangles_max];
|
||
|
|
||
|
if ( useLargeBuffers ) {
|
||
|
m_triangle_data = new triangle_data_t[triangles_max];
|
||
|
}
|
||
|
|
||
|
// add the first triangle
|
||
|
add_triangle( m_points, m_points+1, m_points+2 );
|
||
|
|
||
|
/////////////////////////
|
||
|
// start triangulation //
|
||
|
/////////////////////////
|
||
|
|
||
|
// for each point
|
||
|
for( int point_nr=0; point_nr<m_points; point_nr++ ) {
|
||
|
double curr_x = m_point_data[point_nr].x;
|
||
|
double curr_y = m_point_data[point_nr].y;
|
||
|
//////////////////////////////////////////////
|
||
|
// complete edge buffer for the given point //
|
||
|
//////////////////////////////////////////////
|
||
|
// for each triangle
|
||
|
for( int triangle_nr=0; triangle_nr<m_triangles; triangle_nr++ ) {
|
||
|
// all completed triangles are put at the end, so if
|
||
|
// completed triangle appeared - all remaining triangles
|
||
|
// should be not considered
|
||
|
if ( m_complete[triangle_nr] ) break;
|
||
|
// test if the current point is inside triangle
|
||
|
if ( point_in_circum_circle(curr_x,curr_y,triangle_nr) ) {
|
||
|
// add edges of the current triangle to the buffer
|
||
|
int p1 = m_p1[triangle_nr];
|
||
|
int p2 = m_p2[triangle_nr];
|
||
|
int p3 = m_p3[triangle_nr];
|
||
|
add_edge( p1, p2 );
|
||
|
add_edge( p2, p3 );
|
||
|
add_edge( p3, p1 );
|
||
|
// remove the current triangle from the triangle list
|
||
|
del_triangle( triangle_nr );
|
||
|
// after removal there is a different triangle at the current index
|
||
|
// so we have to test this index once again
|
||
|
// do not increment triangle_nr in the next iteration
|
||
|
triangle_nr--;
|
||
|
}
|
||
|
// triangle was marked as complete - put it on the end
|
||
|
if ( m_complete[triangle_nr] ) {
|
||
|
put_at_end( triangle_nr );
|
||
|
// now at the current index is a different triangle
|
||
|
// so we have to test this index once again
|
||
|
triangle_nr--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// make a new triangles using edges in the edge buffer //
|
||
|
/////////////////////////////////////////////////////////
|
||
|
// tag multiple edges
|
||
|
for ( int j=0; j<m_edges-1; j++ )
|
||
|
for ( int k=j+1; k<m_edges; k++ ) {
|
||
|
edge_data_t *edge1 = &m_edge_data[j];
|
||
|
edge_data_t *edge2 = &m_edge_data[k];
|
||
|
if ( (edge1->p1 == edge2->p2 && edge1->p2 == edge2->p1) ||
|
||
|
(edge1->p1 == edge2->p1 && edge1->p2 == edge2->p2) ) {
|
||
|
edge1->p1 = -1;
|
||
|
edge1->p2 = -1;
|
||
|
edge2->p1 = -1;
|
||
|
edge2->p2 = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// form a new triangle
|
||
|
for ( int j=0; j<m_edges; j++) {
|
||
|
edge_data_t *edge = &m_edge_data[j];
|
||
|
if ( edge->p1 >= 0 && edge->p2 >= 0 ) add_triangle( edge->p1, edge->p2, point_nr );
|
||
|
}
|
||
|
|
||
|
// clear the edge buffer
|
||
|
m_edges = 0;
|
||
|
|
||
|
// emit sig progress
|
||
|
//if ( (point_nr%progress_step) == 0 ) {
|
||
|
// //emit sigProgress( 30+point_nr*70/m_points, &cancel );
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
// remove triangles with the supertriangle vertices
|
||
|
// ( supertriangle vertices have index greater then m_points-1 )
|
||
|
m_active_triangles = m_triangles;
|
||
|
for( int triangle_nr=0; triangle_nr<m_triangles; triangle_nr++ ) {
|
||
|
if ( m_p1[triangle_nr] >= m_points ||
|
||
|
m_p2[triangle_nr] >= m_points ||
|
||
|
m_p3[triangle_nr] >= m_points ) {
|
||
|
del_triangle( triangle_nr );
|
||
|
triangle_nr --;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// change index to point buffer with an index to the apriopriate row.
|
||
|
for( int i=0; i<m_triangles; i++ ) {
|
||
|
m_p1[i] = m_point_data[m_p1[i]].index;
|
||
|
m_p2[i] = m_point_data[m_p2[i]].index;
|
||
|
m_p3[i] = m_point_data[m_p3[i]].index;
|
||
|
}
|
||
|
|
||
|
// free all buffers but m_result
|
||
|
delete m_triangle_buff; m_triangle_buff = NULL;
|
||
|
m_rows = m_triangles;
|
||
|
m_cols = 3;
|
||
|
m_triangle_buff_len = triangles_max;
|
||
|
m_triangle_buff = m_result;
|
||
|
m_result = NULL;
|
||
|
stop();
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------//
|
||
|
|
||
|
bool MPDelunay::point_in_circum_circle( double xp, double yp, int triangle )
|
||
|
{
|
||
|
if ( m_complete[triangle] ) return false;
|
||
|
|
||
|
// calculate circum circle
|
||
|
double xc,yc,r2;
|
||
|
if ( !m_triangle_data || m_triangle_data[triangle].r2 < 0.0 ) {
|
||
|
point_data_t *p1 = &m_point_data[m_p1[triangle]];
|
||
|
point_data_t *p2 = &m_point_data[m_p2[triangle]];
|
||
|
point_data_t *p3 = &m_point_data[m_p3[triangle]];
|
||
|
double x1 = p1->x;
|
||
|
double y1 = p1->y;
|
||
|
double x2 = p2->x;
|
||
|
double y2 = p2->y;
|
||
|
double x3 = p3->x;
|
||
|
double y3 = p3->y;
|
||
|
|
||
|
static const double EPSILON = 10e-100;
|
||
|
double m1,m2,mx1,mx2,my1,my2;
|
||
|
if (fabs(y1-y2) < EPSILON && fabs(y2-y3) < EPSILON) {
|
||
|
//set_error("Error when calculating circum circle ( points too close ? ) ");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( fabs(y2-y1) < EPSILON ) {
|
||
|
m2 = - (x3-x2) / (y3-y2);
|
||
|
mx2 = (x2 + x3) / 2.0;
|
||
|
my2 = (y2 + y3) / 2.0;
|
||
|
xc = (x2 + x1) / 2.0;
|
||
|
yc = m2 * (xc - mx2) + my2;
|
||
|
} else if ( fabs(y3-y2) < EPSILON ) {
|
||
|
m1 = - (x2-x1) / (y2-y1);
|
||
|
mx1 = (x1 + x2) / 2.0;
|
||
|
my1 = (y1 + y2) / 2.0;
|
||
|
xc = (x3 + x2) / 2.0;
|
||
|
yc = m1 * (xc - mx1) + my1;
|
||
|
} else {
|
||
|
m1 = - (x2-x1) / (y2-y1);
|
||
|
m2 = - (x3-x2) / (y3-y2);
|
||
|
mx1 = (x1 + x2) / 2.0;
|
||
|
mx2 = (x2 + x3) / 2.0;
|
||
|
my1 = (y1 + y2) / 2.0;
|
||
|
my2 = (y2 + y3) / 2.0;
|
||
|
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
|
||
|
yc = m1 * (xc - mx1) + my1;
|
||
|
}
|
||
|
|
||
|
double dx = x2 - xc;
|
||
|
double dy = y2 - yc;
|
||
|
r2 = dx*dx + dy*dy;
|
||
|
|
||
|
// remember circum circle
|
||
|
if ( m_triangle_data ) {
|
||
|
triangle_data_t *triangle_data = &m_triangle_data[triangle];
|
||
|
triangle_data->r2 = r2;
|
||
|
triangle_data->xc = xc;
|
||
|
triangle_data->yc = yc;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// circum circle was calculated befor - use buffered values
|
||
|
else if ( m_triangle_data ) {
|
||
|
triangle_data_t *triangle_data = &m_triangle_data[triangle];
|
||
|
r2 = triangle_data->r2;
|
||
|
xc = triangle_data->xc;
|
||
|
yc = triangle_data->yc;
|
||
|
}
|
||
|
|
||
|
// check if this triangle should be considered in further processing
|
||
|
// we have presorted points, so we are sure that further points will have
|
||
|
// greater x value than this point, so all further points will be outside
|
||
|
// of the circum circle if the current point is outside.
|
||
|
// if ( xc+r < xp ) then won't consider this triangle in further processing
|
||
|
// xc+r<xp; xc+sqrt(r2)<xp; sqrt(r2)<xp-xc; r2<(xp-xc)^2 && xp>xc
|
||
|
double dx2 = ( xp - xc ) * ( xp - xc );
|
||
|
double dy2 = ( yp - yc ) * ( yp - yc );
|
||
|
if ( xp > xc && r2 < dx2 ) { m_complete[triangle] = true; return false; }
|
||
|
|
||
|
// check if point is inside circum circle
|
||
|
double d2 = dx2 + dy2;
|
||
|
|
||
|
return ( d2 <= r2 );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|