/* ************************************************************************** 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 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 list = p->points( ); TQValueList::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 list = p->points( ); TQValueList::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::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& 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 segments; TQValueList::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::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::Iterator it; TQPtrList 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 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::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::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& 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& 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& 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 it1( cpViewPosition ); TQPtrListIterator 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 newPoints = m_points; TQValueList::Iterator it = newPoints.at( ( unsigned ) ns ); PMVector p[4]; TQValueList::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& 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 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 newPoints = m_points; TQValueList::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; }