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.
tdegraphics/kpovmodeler/pmsor.cpp

709 lines
18 KiB

/*
**************************************************************************
description
--------------------
copyright : (C) 2002 by Andreas Zehender
email : zehender@kde.org
**************************************************************************
**************************************************************************
* *
* 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 "pmsor.h"
#include "pmxmlhelper.h"
#include "pmsoredit.h"
#include "pmmemento.h"
#include "pmviewstructure.h"
#include "pmsorcontrolpoint.h"
#include "pmsplinememento.h"
#include "pmsorsegment.h"
#include "pmdefaults.h"
#include "pmobjectaction.h"
#include <tdelocale.h>
const int defaultNumberOfPoints = 4;
const PMVector defaultPoint[defaultNumberOfPoints] =
{
PMVector( 0.0, 0.0 ),
PMVector( 0.5, 0.3 ),
PMVector( 0.5, 0.7 ),
PMVector( 0.0, 1.0 )
};
const bool defaultSturm = false;
const bool defaultOpen = false;
int PMSurfaceOfRevolution::s_rSteps = c_defaultSurfaceOfRevolutionRSteps;
int PMSurfaceOfRevolution::s_sSteps = c_defaultSurfaceOfRevolutionSSteps;
int PMSurfaceOfRevolution::s_parameterKey = 0;
PMMetaObject* PMSurfaceOfRevolution::s_pMetaObject = 0;
PMObject* createNewSurfaceOfRevolution( PMPart* part )
{
return new PMSurfaceOfRevolution( part );
}
PMDefinePropertyClass( PMSurfaceOfRevolution, PMSurfaceOfRevolutionProperty );
class PMPointProperty : public PMPropertyBase
{
public:
PMPointProperty( )
: PMPropertyBase( "controlPoints", PMVariant::Vector )
{
m_index = 0;
}
virtual int dimensions( ) const { return 1; }
virtual void setIndex( int /*dimension*/, int index )
{
m_index = index;
}
virtual int size( PMObject* object, int /*dimension*/ ) const
{
return ( ( PMSurfaceOfRevolution* ) object )->numberOfPoints( );
}
protected:
virtual bool setProtected( PMObject* obj, const PMVariant& var )
{
PMSurfaceOfRevolution* p = ( PMSurfaceOfRevolution* ) obj;
TQValueList<PMVector> list = p->points( );
TQValueList<PMVector>::Iterator it = list.begin( );
int i;
PMVector v = var.vectorData( );
v.resize( 2 );
for( i = 0; i < m_index && it != list.end( ); ++i )
++it;
// expand the list if necessary
for( ; i < m_index; ++i )
list.insert( it, v );
if( it == list.end( ) )
it = list.insert( it, v );
else
*it = v;
p->setPoints( list );
return true;
}
virtual PMVariant getProtected( const PMObject* obj )
{
PMSurfaceOfRevolution* p = ( PMSurfaceOfRevolution* ) obj;
TQValueList<PMVector> list = p->points( );
TQValueList<PMVector>::ConstIterator it = list.at( m_index );
if( it == list.end( ) )
{
kdError( PMArea ) << "Range error in PMSurfaceOfRevolution::PointProperty::get" << endl;
return PMVariant( );
}
return PMVariant( *it );
}
private:
int m_index;
};
PMSurfaceOfRevolution::PMSurfaceOfRevolution( PMPart* part )
: Base( part )
{
int i;
for( i = 0; i < defaultNumberOfPoints; ++i )
m_points.append( defaultPoint[i] );
m_sturm = defaultSturm;
m_open = defaultOpen;
}
PMSurfaceOfRevolution::PMSurfaceOfRevolution( const PMSurfaceOfRevolution& s )
: Base( s )
{
m_points = s.m_points;
m_sturm = s.m_sturm;
m_open = s.m_open;
}
PMSurfaceOfRevolution::~PMSurfaceOfRevolution( )
{
}
TQString PMSurfaceOfRevolution::description( ) const
{
return i18n( "surface of revolution" );
}
void PMSurfaceOfRevolution::serialize( TQDomElement& e, TQDomDocument& doc ) const
{
TQDomElement data = doc.createElement( "extra_data" );
TQDomElement p;
e.setAttribute( "sturm", m_sturm );
e.setAttribute( "open", m_open );
TQValueList<PMVector>::ConstIterator it;
for( it = m_points.begin( ); it != m_points.end( ); ++it )
{
p = doc.createElement( "point" );
p.setAttribute( "vector", ( *it ).serializeXML( ) );
data.appendChild( p );
}
e.appendChild( data );
Base::serialize( e, doc );
}
void PMSurfaceOfRevolution::readAttributes( const PMXMLHelper& h )
{
m_sturm = h.boolAttribute( "sturm", defaultSturm );
m_open = h.boolAttribute( "open", defaultOpen );
m_points.clear( );
PMVector v( 2 );
TQDomElement e = h.extraData( );
if( !e.isNull( ) )
{
TQDomNode c = e.firstChild( );
while( !c.isNull( ) )
{
if( c.isElement( ) )
{
TQDomElement ce = c.toElement( );
if( ce.tagName( ) == "point" )
{
TQString str = ce.attribute( "vector" );
if( !str.isNull( ) )
{
v.loadXML( str );
m_points.append( v );
}
}
}
c = c.nextSibling( );
}
}
Base::readAttributes( h );
}
PMMetaObject* PMSurfaceOfRevolution::metaObject( ) const
{
if( !s_pMetaObject )
{
s_pMetaObject = new PMMetaObject( "SurfaceOfRevolution", Base::metaObject( ),
createNewSurfaceOfRevolution );
s_pMetaObject->addProperty(
new PMSurfaceOfRevolutionProperty( "sturm", &PMSurfaceOfRevolution::setSturm,
&PMSurfaceOfRevolution::sturm ) );
s_pMetaObject->addProperty(
new PMSurfaceOfRevolutionProperty( "open", &PMSurfaceOfRevolution::setOpen,
&PMSurfaceOfRevolution::open ) );
s_pMetaObject->addProperty( new PMPointProperty( ) );
}
return s_pMetaObject;
}
void PMSurfaceOfRevolution::cleanUp( ) const
{
if( s_pMetaObject )
{
delete s_pMetaObject;
s_pMetaObject = 0;
}
Base::cleanUp( );
}
void PMSurfaceOfRevolution::setSturm( bool s )
{
if( m_sturm != s )
{
if( m_pMemento )
m_pMemento->addData( s_pMetaObject, PMSturmID, m_sturm );
m_sturm = s;
}
}
void PMSurfaceOfRevolution::setOpen( bool o )
{
if( m_open != o )
{
if( m_pMemento )
m_pMemento->addData( s_pMetaObject, PMOpenID, m_open );
m_open = o;
}
}
void PMSurfaceOfRevolution::setPoints( const TQValueList<PMVector>& points )
{
if( m_points != points )
{
if( m_pMemento )
( ( PMSplineMemento* ) m_pMemento )->setSplinePoints( m_points );
setViewStructureChanged( );
m_points = points;
}
}
PMDialogEditBase* PMSurfaceOfRevolution::editWidget( TQWidget* parent ) const
{
return new PMSurfaceOfRevolutionEdit( parent );
}
void PMSurfaceOfRevolution::createMemento( )
{
if( m_pMemento )
delete m_pMemento;
m_pMemento = new PMSplineMemento( this );
}
void PMSurfaceOfRevolution::restoreMemento( PMMemento* s )
{
PMSplineMemento* m = ( PMSplineMemento* ) s;
PMMementoDataIterator it( s );
PMMementoData* data;
for( ; it.current( ); ++it )
{
data = it.current( );
if( data->objectType( ) == s_pMetaObject )
{
switch( data->valueID( ) )
{
case PMSturmID:
setSturm( data->boolData( ) );
break;
case PMOpenID:
setOpen( data->boolData( ) );
break;
default:
kdError( PMArea ) << "Wrong ID in PMSurfaceOfRevolution::restoreMemento\n";
break;
}
}
}
if( m->splinePointsSaved( ) )
setPoints( m->splinePoints( ) );
Base::restoreMemento( s );
}
void PMSurfaceOfRevolution::createViewStructure( )
{
if( s_sSteps == 0 )
s_sSteps = c_defaultSurfaceOfRevolutionSSteps;
if( s_rSteps == 0 )
s_rSteps = c_defaultSurfaceOfRevolutionRSteps;
int rSteps = (int)( ( (float)s_rSteps / 2 ) * ( displayDetail( ) + 1 ) );
int sSteps = (int)( ( (float)s_sSteps / 2 ) * ( displayDetail( ) + 1 ) );
int np = m_points.count( );
int i, j, r;
// calculate number of segments
int ns = np - 3;
// calculate number of points and lines of the view structure
int vsp = ns * sSteps + 1;
int vsl = ( 2 * vsp - 1 ) * rSteps;
vsp *= rSteps;
if( m_pViewStructure )
{
if( m_pViewStructure->points( ).size( ) != ( unsigned ) vsp )
m_pViewStructure->points( ).resize( vsp );
if( m_pViewStructure->lines( ).size( ) != ( unsigned ) vsl )
m_pViewStructure->lines( ).resize( vsl );
}
else
m_pViewStructure = new PMViewStructure( vsp, vsl );
// calculate the spline segments
TQValueList<PMSorSegment> segments;
TQValueList<PMVector>::Iterator it1, it2, it3, it4;
it1 = m_points.begin( );
it2 = it1; ++it2;
it3 = it2; ++it3;
it4 = it3; ++it4;
for( i = 0; i < ns; ++i, ++it1, ++it2, ++it3, ++it4 )
segments.append( PMSorSegment( *it1, *it2, *it3, *it4 ) );
// create the line array
PMLineArray& lines = m_pViewStructure->lines( );
int vl = ns * sSteps;
int lb = 0;
for( i = 0; i < vl + 1; ++i )
{
for( j = 0; j < rSteps - 1; ++j )
lines[lb+j] = PMLine( lb + j, lb + j + 1 );
lines[lb+rSteps-1] = PMLine( lb, lb + rSteps - 1 );
lb += rSteps;
}
int pi = 0;
for( i = 0; i < vl; ++i )
{
for( j = 0; j < rSteps; ++j )
{
lines[lb] = PMLine( pi, pi + rSteps );
++pi;
++lb;
}
}
// calculate the points
PMVector point2, point3;
TQValueList<PMSorSegment>::Iterator sit = segments.begin( );
double poffset = 1.0 / sSteps;
PMMatrix rot = PMMatrix::rotation( 0.0, M_PI * 2.0 / rSteps, 0.0 );
PMPointArray& points = m_pViewStructure->points( );
pi = 0;
for( i = 0; i < ns; ++i, ++sit )
{
for( j = 0; j < sSteps; ++j )
{
point2 = ( *sit ).point( poffset * j );
point3[0] = point2[0];
point3[1] = point2[1];
point3[2] = 0.0;
for( r = 0; r < rSteps; ++r )
{
points[pi] = PMPoint( point3 );
if( r != rSteps - 1 )
point3.transform( rot );
++pi;
}
}
if( i == ns - 1 )
{
point2 = ( *sit ).point( 1.0 );
point3[0] = point2[0];
point3[1] = point2[1];
point3[2] = 0.0;
for( r = 0; r < rSteps; ++r )
{
points[pi] = PMPoint( point3 );
if( r != rSteps - 1 )
point3.transform( rot );
++pi;
}
}
}
}
void PMSurfaceOfRevolution::controlPoints( PMControlPointList& list )
{
TQValueList<PMVector>::Iterator it;
TQPtrList<PMSorControlPoint> tmp1, tmp2;
int i;
PMSorControlPoint* cp = 0;
PMSorControlPoint* lastPoint = 0;
cp = 0;
for( it = m_points.begin( ), i = 0; it != m_points.end( ); ++it, ++i )
{
lastPoint = cp;
cp = new PMSorControlPoint( lastPoint, *it, PMSorControlPoint::PM2DXY, i,
i18n( "Point %1 (xy)" ).arg( i + 1 ) );
tmp1.append( cp );
}
lastPoint = 0;
cp = 0;
for( it = m_points.begin( ), i = 0; it != m_points.end( ); ++it, ++i )
{
lastPoint = cp;
cp = new PMSorControlPoint( lastPoint, *it, PMSorControlPoint::PM2DZY, i,
i18n( "Point %1 (yz)" ).arg( i + 1 ) );
tmp2.append( cp );
}
TQPtrListIterator<PMSorControlPoint> cit1( tmp1 ), cit2( tmp2 );
for( ; cit1.current( ) && cit2.current( ); ++cit1, ++cit2 )
{
( *cit1 )->setSorLink( *cit2 );
( *cit2 )->setSorLink( *cit1 );
}
for( cit1.toFirst( ); cit1.current( ); ++cit1 )
list.append( *cit1 );
for( cit2.toFirst( ); cit2.current( ); ++cit2 )
list.append( *cit2 );
}
void PMSurfaceOfRevolution::controlPointsChanged( PMControlPointList& list )
{
PMControlPointListIterator it1( list ), it2( list );
TQValueList<PMVector>::Iterator pit = m_points.begin( );
PMSorControlPoint* p1;
PMSorControlPoint* p2;
bool firstChange = true;
PMVector lastPoint( 2 );
int num = list.count( ) / 2;
int pnr = 0;
for( it2 += num; it2.current( ); ++it1, ++it2, ++pit, ++pnr )
{
p1 = ( PMSorControlPoint* ) it1.current( );
p2 = ( PMSorControlPoint* ) it2.current( );
if( p1->changed( ) )
{
if( firstChange )
{
if( m_pMemento )
{
PMSplineMemento* m = ( PMSplineMemento* ) m_pMemento;
if( !m->splinePointsSaved( ) )
m->setSplinePoints( m_points );
}
firstChange = false;
setViewStructureChanged( );
}
p2->setPoint( p1->point( ) );
( *pit ) = p1->point( );
}
else if( p2->changed( ) )
{
if( firstChange )
{
if( m_pMemento )
{
PMSplineMemento* m = ( PMSplineMemento* ) m_pMemento;
if( !m->splinePointsSaved( ) )
m->setSplinePoints( m_points );
}
firstChange = false;
setViewStructureChanged( );
}
p1->setPoint( p2->point( ) );
( *pit ) = p2->point( );
}
if( ( pnr > 1 ) && ( pnr < ( num - 1 ) ) )
{
if( ( ( *pit )[1] - lastPoint[1] ) < c_sorTolerance )
{
( *pit )[1] = lastPoint[1] + c_sorTolerance;
p1->setPoint( *pit );
p2->setPoint( *pit );
}
}
if( ( pnr == ( num - 1 ) ) || ( pnr == 2 ) )
{
TQValueList<PMVector>::Iterator hit = pit;
--hit; --hit;
if( approxZero( ( *hit )[1] - ( *pit )[1], c_sorTolerance ) )
{
( *pit )[1] = ( *hit )[1] + c_sorTolerance;
p1->setPoint( *pit );
p2->setPoint( *pit );
}
}
lastPoint = *pit;
}
}
void PMSurfaceOfRevolution::addObjectActions( const PMControlPointList& /*cp*/,
TQPtrList<PMObjectAction>& actions )
{
PMObjectAction* a;
a = new PMObjectAction( s_pMetaObject, PMSplitSegmentID,
i18n( "Add Point" ) );
actions.append( a );
a = new PMObjectAction( s_pMetaObject, PMJoinSegmentsID,
i18n( "Remove Point" ) );
int np = m_points.count( );
if( np < 5 )
a->setEnabled( false );
actions.append( a );
}
void PMSurfaceOfRevolution::objectActionCalled( const PMObjectAction* action,
const PMControlPointList& cp,
const TQPtrList<PMVector>& cpViewPosition,
const PMVector& clickPosition )
{
if( action->objectType( ) == s_pMetaObject )
{
switch( action->actionID( ) )
{
case PMSplitSegmentID:
splitSegment( cp, cpViewPosition, clickPosition );
break;
case PMJoinSegmentsID:
joinSegments( cp, cpViewPosition, clickPosition );
break;
default:
kdError( PMArea ) << "Wrong ID in PMSurfaceOfRevolution::objectActionCalled\n";
break;
}
}
else
Base::objectActionCalled( action, cp, cpViewPosition, clickPosition );
}
void PMSurfaceOfRevolution::splitSegment( const PMControlPointList& /*cp*/,
const TQPtrList<PMVector>& cpViewPosition,
const PMVector& clickPosition )
{
// find nearest segment
int nump = cpViewPosition.count( ) / 2 - 1;
double abs = 0.0, minabs = 1e10;
int ns = -1;
int i, j;
PMVector mid( 3 ), dist( 2 );
TQPtrListIterator<PMVector> it1( cpViewPosition );
TQPtrListIterator<PMVector> it2( cpViewPosition );
++it2;
for( j = 0; j < 2; ++j )
{
++it1;
++it2;
for( i = 1; i < ( nump - 1 ); ++i )
{
mid = ( **it1 + **it2 ) / 2.0;
dist[0] = mid[0];
dist[1] = mid[1];
dist -= clickPosition;
abs = dist.abs( );
if( ( minabs > abs ) || ( ns < 0 ) )
{
minabs = abs;
ns = i;
}
++it1;
++it2;
}
++it1;
++it2;
++it1;
++it2;
}
// add a new segment
TQValueList<PMVector> newPoints = m_points;
TQValueList<PMVector>::Iterator it = newPoints.at( ( unsigned ) ns );
PMVector p[4];
TQValueList<PMVector>::Iterator hit = it;
// calculate the spline segment
--hit;
for( i = 0; i < 4; ++i, ++hit )
p[i] = *hit;
PMSorSegment segment( p[0], p[1], p[2], p[3] );
mid = segment.point( 0.5 );
if( mid[0] < 0 )
mid[0] = 0;
++it;
it = newPoints.insert( it, mid );
hit = it;
--it;
for( ; hit != newPoints.end( ); ++it, ++hit )
if( ( ( *hit )[1] - ( *it )[1] ) < c_sorTolerance )
( *hit )[1] = ( *it )[1] + c_sorTolerance;
setPoints( newPoints );
}
void PMSurfaceOfRevolution::joinSegments( const PMControlPointList& /*cp*/,
const TQPtrList<PMVector>& cpViewPosition,
const PMVector& clickPosition )
{
// find nearest point
int nump = cpViewPosition.count( ) / 2;
if( nump < 5 )
{
kdError( PMArea ) << "Not enough points in PMSurfaceOfRevolution::joinSegments\n";
return;
}
double abs = 0.0, minabs = 1e10;
int ns = -1;
int i, j;
PMVector* p;
PMVector dist( 2 );
TQPtrListIterator<PMVector> it1( cpViewPosition );
for( j = 0; j < 2; ++j )
{
for( i = 0; i < nump; ++i )
{
p = *it1;
dist[0] = (*p)[0];
dist[1] = (*p)[1];
dist -= clickPosition;
abs = dist.abs( );
if( ( minabs > abs ) || ( ns < 0 ) )
{
minabs = abs;
ns = i;
}
++it1;
}
}
// join two segments
TQValueList<PMVector> newPoints = m_points;
TQValueList<PMVector>::Iterator it;
// never remove the first or last point
if( ns == 0 )
++ns;
if( ns == ( nump - 1 ) )
--ns;
it = newPoints.at( ns );
newPoints.remove( it );
setPoints( newPoints );
}
void PMSurfaceOfRevolution::setRSteps( int r )
{
if( r >= 4 )
s_rSteps = r;
else
kdDebug( PMArea ) << "PMSurfaceOfRevolution::setRSteps: R must be greater than 3\n";
++s_parameterKey;
}
void PMSurfaceOfRevolution::setSSteps( int s )
{
if( s >= 1 )
s_sSteps = s;
else
kdDebug( PMArea ) << "PMSurfaceOfRevolution::setSSteps: S must be greater than 0\n";
++s_parameterKey;
}