/*************************************************************************** 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 #include #include //---------------------------------------------------------------------------------------------// 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; icount(); 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; ivalue( 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_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_nrp1 == 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; jp1 >= 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_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; ix; 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+rxc 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 ); }