// Copyright (C) 2003 Dominique Devriese // 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. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. #include "special_constructors.h" #include "calcpaths.h" #include "common.h" #include "conic-common.h" #include "guiaction.h" #include "kigpainter.h" #include "../kig/kig_part.h" #include "../modes/construct_mode.h" #include "../objects/bogus_imp.h" #include "../objects/centerofcurvature_type.h" #include "../objects/circle_imp.h" #include "../objects/conic_imp.h" #include "../objects/conic_types.h" #include "../objects/cubic_imp.h" #include "../objects/intersection_types.h" #include "../objects/inversion_type.h" #include "../objects/line_imp.h" #include "../objects/line_type.h" #include "../objects/locus_imp.h" #include "../objects/object_calcer.h" #include "../objects/object_drawer.h" #include "../objects/object_factory.h" #include "../objects/object_holder.h" #include "../objects/object_imp.h" #include "../objects/object_type.h" #include "../objects/other_imp.h" #include "../objects/other_type.h" #include "../objects/point_imp.h" #include "../objects/point_type.h" #include "../objects/polygon_imp.h" #include "../objects/polygon_type.h" #include "../objects/tangent_type.h" #include "../objects/text_imp.h" #include "../objects/transform_types.h" #include #include #include #include class ConicConicIntersectionConstructor : public StandardConstructorBase { protected: ArgsParser mparser; public: ConicConicIntersectionConstructor(); ~ConicConicIntersectionConstructor(); void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const; std::vector build( const std::vector& os, KigDocument& d, KigWidget& w ) const; void plug( KigPart* doc, KigGUIAction* kact ); bool isTransform() const; }; class ConicLineIntersectionConstructor : public MultiObjectTypeConstructor { public: ConicLineIntersectionConstructor(); ~ConicLineIntersectionConstructor(); }; class ArcLineIntersectionConstructor : public MultiObjectTypeConstructor { public: ArcLineIntersectionConstructor(); ~ArcLineIntersectionConstructor(); }; ConicRadicalConstructor::ConicRadicalConstructor() : StandardConstructorBase( I18N_NOOP( "Radical Lines for Conics" ), I18N_NOOP( "The lines constructed through the intersections " "of two conics. This is also defined for " "non-intersecting conics." ), "conicsradicalline", mparser ), mtype( ConicRadicalType::instance() ), mparser( mtype->argsParser().without( IntImp::stype() ) ) { } ConicRadicalConstructor::~ConicRadicalConstructor() { } void ConicRadicalConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& doc ) const { if ( tqparents.size() == 2 && tqparents[0]->imp()->inherits( ConicImp::stype() ) && tqparents[1]->imp()->inherits( ConicImp::stype() ) ) { Args args; std::transform( tqparents.begin(), tqparents.end(), std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) ); for ( int i = -1; i < 2; i += 2 ) { IntImp root( i ); IntImp zeroindex( 1 ); args.push_back( &root ); args.push_back( &zeroindex ); ObjectImp* data = mtype->calc( args, doc ); drawer.draw( *data, p, true ); delete data; data = 0; args.pop_back(); args.pop_back(); }; }; } std::vector ConicRadicalConstructor::build( const std::vector& os, KigDocument&, KigWidget& ) const { using namespace std; std::vector ret; ObjectCalcer* zeroindexcalcer = new ObjectConstCalcer( new IntImp( 1 ) ); for ( int i = -1; i < 2; i += 2 ) { std::vector args; std::copy( os.begin(), os.end(), back_inserter( args ) ); args.push_back( new ObjectConstCalcer( new IntImp( i ) ) ); // we use only one zeroindex dataobject, so that if you switch one // radical line around, then the other switches along.. args.push_back( zeroindexcalcer ); ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); }; return ret; } static const struct ArgsParser::spec argsspecpp[] = { { PointImp::stype(), I18N_NOOP( "Moving Point" ), I18N_NOOP( "Select the moving point, which will be moved around while drawing the locus..." ), false }, { PointImp::stype(), I18N_NOOP( "Following Point" ), I18N_NOOP( "Select the following point, whose locations the locus will be drawn through..." ), true } }; LocusConstructor::LocusConstructor() : StandardConstructorBase( I18N_NOOP( "Locus" ), I18N_NOOP( "A locus" ), "locus", margsparser ), margsparser( argsspecpp, 2 ) { } LocusConstructor::~LocusConstructor() { } void LocusConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const { // this function is rather ugly, but it is necessary to do it this // way in order to play nice with Kig's design.. if ( tqparents.size() != 2 ) return; const ObjectTypeCalcer* constrained = dynamic_cast( tqparents.front() ); const ObjectCalcer* moving = tqparents.back(); if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) { // moving is in fact the constrained point.. swap them.. moving = tqparents.front(); constrained = dynamic_cast( tqparents.back() ); assert( constrained ); }; assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ); const ObjectImp* oimp = constrained->tqparents().back()->imp(); if( !oimp->inherits( CurveImp::stype() ) ) oimp = constrained->tqparents().front()->imp(); assert( oimp->inherits( CurveImp::stype() ) ); const CurveImp* cimp = static_cast( oimp ); ObjectHierarchy hier( constrained, moving ); LocusImp limp( cimp->copy(), hier ); drawer.draw( limp, p, true ); } const int LocusConstructor::wantArgs( const std::vector& os, const KigDocument&, const KigWidget& ) const { int ret = margsparser.check( os ); if ( ret == ArgsParser::Invalid ) return ret; else if ( os.size() != 2 ) return ret; if ( dynamic_cast( os.front() ) && static_cast( os.front() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) { std::set tqchildren = getAllChildren( os.front() ); return tqchildren.find( os.back() ) != tqchildren.end() ? ret : ArgsParser::Invalid; } if ( dynamic_cast( os.back() ) && static_cast( os.back() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) { std::set tqchildren = getAllChildren( os.back() ); return tqchildren.find( os.front() ) != tqchildren.end() ? ret : ArgsParser::Invalid; } return ArgsParser::Invalid; } std::vector LocusConstructor::build( const std::vector& tqparents, KigDocument&, KigWidget& ) const { std::vector ret; assert( tqparents.size() == 2 ); ObjectTypeCalcer* constrained = dynamic_cast( tqparents.front() ); ObjectCalcer* moving = tqparents.back(); if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) { // moving is in fact the constrained point.. swap them.. moving = tqparents.front(); constrained = dynamic_cast( tqparents.back() ); assert( constrained ); }; assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ); ret.push_back( ObjectFactory::instance()->locus( constrained, moving ) ); return ret; } TQString LocusConstructor::useText( const ObjectCalcer& o, const std::vector& os, const KigDocument&, const KigWidget& ) const { if ( dynamic_cast( &o ) && static_cast( o ).type()->inherits( ObjectType::ID_ConstrainedPointType ) && ( os.empty() || !dynamic_cast( os[0] ) || !static_cast( os[0] )->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) ) return i18n( "Moving Point" ); else return i18n( "Dependent Point" ); } void ConicRadicalConstructor::plug( KigPart*, KigGUIAction* ) { } void LocusConstructor::plug( KigPart*, KigGUIAction* ) { } bool ConicRadicalConstructor::isTransform() const { return mtype->isTransform(); } bool LocusConstructor::isTransform() const { return false; } /* * generic polygon constructor */ PolygonBNPTypeConstructor::PolygonBNPTypeConstructor() : mtype( PolygonBNPType::instance() ) { } PolygonBNPTypeConstructor::~PolygonBNPTypeConstructor() { } const TQString PolygonBNPTypeConstructor::descriptiveName() const { return i18n("Polygon by Its Vertices"); } const TQString PolygonBNPTypeConstructor::description() const { return i18n("Construct a polygon by giving its vertices"); } const TQCString PolygonBNPTypeConstructor::iconFileName( const bool ) const { return "kig_polygon"; } const bool PolygonBNPTypeConstructor::isAlreadySelectedOK( const std::vector& os, const int& pos ) const { if ( pos == 0 && os.size() >= 3 ) return true; return false; } const int PolygonBNPTypeConstructor::wantArgs( const std::vector& os, const KigDocument&, const KigWidget& ) const { int count=os.size() - 1; for ( int i = 0; i <= count; i++ ) { if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid; } if ( count < 3 ) return ArgsParser::Valid; if ( os[0] == os[count] ) return ArgsParser::Complete; return ArgsParser::Valid; } void PolygonBNPTypeConstructor::handleArgs( const std::vector& os, KigPart& d, KigWidget& v ) const { std::vector bos = build( os, d.document(), v ); for ( std::vector::iterator i = bos.begin(); i != bos.end(); ++i ) { (*i)->calc( d.document() ); } d.addObjects( bos ); } void PolygonBNPTypeConstructor::handlePrelim( KigPainter& p, const std::vector& os, const KigDocument& d, const KigWidget& ) const { uint count = os.size(); if ( count < 2 ) return; for ( uint i = 0; i < count; i++ ) { assert ( os[i]->imp()->inherits( PointImp::stype() ) ); } std::vector args = os; p.setBrushStyle( Qt::NoBrush ); p.setBrushColor( TQt::red ); p.setPen( TQPen ( TQt::red, 1) ); p.setWidth( -1 ); // -1 means the default width for the object being // drawn.. ObjectDrawer drawer( TQt::red ); drawprelim( drawer, p, args, d ); } TQString PolygonBNPTypeConstructor::useText( const ObjectCalcer&, const std::vector& os, const KigDocument&, const KigWidget& ) const { if ( os.size() > 3 ) return i18n("... with this vertex (click on the first vertex to terminate construction)"); else return i18n("Construct a polygon with this vertex"); } TQString PolygonBNPTypeConstructor::selectStatement( const std::vector&, const KigDocument&, const KigWidget& ) const { return i18n("Select a point to be a vertex of the new polygon..."); } void PolygonBNPTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const { if ( tqparents.size() < 2 ) return; std::vector points; for ( uint i = 0; i < tqparents.size(); ++i ) { const Coordinate vertex = static_cast( tqparents[i]->imp() )->coordinate(); points.push_back( vertex ); } if ( tqparents.size() == 2 ) { SegmentImp segment = SegmentImp( points[0], points[1] ); drawer.draw( segment, p, true ); } else { PolygonImp polygon = PolygonImp( points ); drawer.draw( polygon, p, true ); } } std::vector PolygonBNPTypeConstructor::build( const std::vector& tqparents, KigDocument&, KigWidget& ) const { uint count = tqparents.size() - 1; assert ( count >= 3 ); std::vector args; for ( uint i = 0; i < count; ++i ) args.push_back( tqparents[i] ); ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args ); ObjectHolder* h = new ObjectHolder( calcer ); std::vector ret; ret.push_back( h ); return ret; } void PolygonBNPTypeConstructor::plug( KigPart*, KigGUIAction* ) { } bool PolygonBNPTypeConstructor::isTransform() const { return false; } /* * construction of polygon vertices */ static const struct ArgsParser::spec argsspecpv[] = { { PolygonImp::stype(), I18N_NOOP( "Polygon" ), I18N_NOOP( "Construct the vertices of this polygon..." ), true } }; PolygonVertexTypeConstructor::PolygonVertexTypeConstructor() : StandardConstructorBase( I18N_NOOP( "Vertices of a Polygon" ), I18N_NOOP( "The vertices of a polygon." ), "polygonvertices", margsparser ), mtype( PolygonVertexType::instance() ), margsparser( argsspecpv, 1 ) { } PolygonVertexTypeConstructor::~PolygonVertexTypeConstructor() { } void PolygonVertexTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const { if ( tqparents.size() != 1 ) return; const PolygonImp* polygon = dynamic_cast( tqparents.front()->imp() ); const std::vector points = polygon->points(); int sides = points.size(); for ( int i = 0; i < sides; ++i ) { PointImp point = PointImp( points[i] ); drawer.draw( point, p, true ); } } std::vector PolygonVertexTypeConstructor::build( const std::vector& tqparents, KigDocument&, KigWidget& ) const { std::vector ret; assert( tqparents.size() == 1 ); const PolygonImp* polygon = dynamic_cast( tqparents.front()->imp() ); const std::vector points = polygon->points(); int sides = points.size(); for ( int i = 0; i < sides; ++i ) { ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) ); std::vector args( tqparents ); args.push_back( d ); ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); } return ret; } void PolygonVertexTypeConstructor::plug( KigPart*, KigGUIAction* ) { } bool PolygonVertexTypeConstructor::isTransform() const { return false; } /* * construction of polygon sides */ static const struct ArgsParser::spec argsspecps[] = { { PolygonImp::stype(), I18N_NOOP( "Polygon" ), I18N_NOOP( "Construct the sides of this polygon..." ), false } }; PolygonSideTypeConstructor::PolygonSideTypeConstructor() : StandardConstructorBase( I18N_NOOP( "Sides of a Polygon" ), I18N_NOOP( "The sides of a polygon." ), "polygonsides", margsparser ), mtype( PolygonSideType::instance() ), margsparser( argsspecps, 1 ) { } PolygonSideTypeConstructor::~PolygonSideTypeConstructor() { } void PolygonSideTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const { if ( tqparents.size() != 1 ) return; const PolygonImp* polygon = dynamic_cast( tqparents.front()->imp() ); const std::vector points = polygon->points(); uint sides = points.size(); for ( uint i = 0; i < sides; ++i ) { uint nexti = ( i + 1 < sides )?(i + 1):0; SegmentImp segment = SegmentImp( points[i], points[nexti] ); drawer.draw( segment, p, true ); } } std::vector PolygonSideTypeConstructor::build( const std::vector& tqparents, KigDocument&, KigWidget& ) const { std::vector ret; assert( tqparents.size() == 1 ); const PolygonImp* polygon = dynamic_cast( tqparents.front()->imp() ); const std::vector points = polygon->points(); uint sides = points.size(); for ( uint i = 0; i < sides; ++i ) { ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) ); std::vector args( tqparents ); args.push_back( d ); ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) ); } return ret; } void PolygonSideTypeConstructor::plug( KigPart*, KigGUIAction* ) { } bool PolygonSideTypeConstructor::isTransform() const { return false; } /* * polygon by center and vertex */ PolygonBCVConstructor::PolygonBCVConstructor() : mtype( PolygonBCVType::instance() ) { } PolygonBCVConstructor::~PolygonBCVConstructor() { } const TQString PolygonBCVConstructor::descriptiveName() const { return i18n("Regular Polygon with Given Center"); } const TQString PolygonBCVConstructor::description() const { return i18n("Construct a regular polygon with a given center and vertex"); } const TQCString PolygonBCVConstructor::iconFileName( const bool ) const { return "hexagonbcv"; } const bool PolygonBCVConstructor::isAlreadySelectedOK( const std::vector&, const int& ) const { return false; } const int PolygonBCVConstructor::wantArgs( const std::vector& os, const KigDocument&, const KigWidget& ) const { if ( os.size() > 3 ) return ArgsParser::Invalid; uint imax = ( os.size() <= 2) ? os.size() : 2; for ( uint i = 0; i < imax; ++i ) if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid; if ( os.size() < 3 ) return ArgsParser::Valid; if ( ! ( os[2]->imp()->inherits( BogusPointImp::stype() ) ) ) return ArgsParser::Invalid; return ArgsParser::Complete; } void PolygonBCVConstructor::handleArgs( const std::vector& os, KigPart& d, KigWidget& v ) const { std::vector bos = build( os, d.document(), v ); for ( std::vector::iterator i = bos.begin(); i != bos.end(); ++i ) { (*i)->calc( d.document() ); } d.addObjects( bos ); } void PolygonBCVConstructor::handlePrelim( KigPainter& p, const std::vector& os, const KigDocument& d, const KigWidget& ) const { if ( os.size() < 2 ) return; for ( uint i = 0; i < 2; i++ ) { assert ( os[i]->imp()->inherits( PointImp::stype() ) ); } Coordinate c = static_cast( os[0]->imp() )->coordinate(); Coordinate v = static_cast( os[1]->imp() )->coordinate(); int nsides = 6; bool moreinfo = false; int winding = 0; // 0 means allow winding > 1 if ( os.size() == 3 ) { assert ( os[2]->imp()->inherits( BogusPointImp::stype() ) ); Coordinate cntrl = static_cast( os[2]->imp() )->coordinate(); nsides = computeNsides( c, v, cntrl, winding ); moreinfo = true; } std::vector args; args.push_back( os[0] ); args.push_back( os[1] ); ObjectConstCalcer* ns = new ObjectConstCalcer( new IntImp( nsides ) ); args.push_back( ns ); if ( winding > 1 ) { ns = new ObjectConstCalcer( new IntImp( winding ) ); args.push_back( ns ); } p.setBrushStyle( Qt::NoBrush ); p.setBrushColor( TQt::red ); p.setPen( TQPen ( TQt::red, 1) ); p.setWidth( -1 ); // -1 means the default width for the object being // drawn.. ObjectDrawer drawer( TQt::red ); drawprelim( drawer, p, args, d ); if ( moreinfo ) { p.setPointStyle( 1 ); p.setWidth( 6 ); double ro = 1.0/(2.5); Coordinate where = getRotatedCoord( c, (1-ro)*c+ro*v, 4*M_PI/5.0 ); PointImp ptn = PointImp( where ); TextImp text = TextImp( "(5,2)", where, false ); ptn.draw( p ); text.draw( p ); for ( int i = 3; i < 9; ++i ) { where = getRotatedCoord( c, v, 2.0*M_PI/i ); ptn = PointImp( where ); ptn.draw( p ); if ( i > 5 ) continue; text = TextImp( TQString( "(%1)" ).tqarg(i), where, false ); text.draw( p ); } p.setStyle( Qt::DotLine ); p.setWidth( 1 ); double radius = ( v - c ).length(); CircleImp circle = CircleImp( c, radius ); circle.draw( p ); for ( int i = 2; i < 5; i++ ) { ro = 1.0/(i+0.5); CircleImp circle = CircleImp( c, ro*radius ); circle.draw( p ); } } delete_all( args.begin() + 2, args.end() ); } std::vector PolygonBCVConstructor::build( const std::vector& tqparents, KigDocument&, KigWidget& ) const { assert ( tqparents.size() == 3 ); std::vector args; Coordinate c = static_cast( tqparents[0]->imp() )->coordinate(); Coordinate v = static_cast( tqparents[1]->imp() )->coordinate(); Coordinate cntrl = static_cast( tqparents[2]->imp() )->coordinate(); args.push_back( tqparents[0] ); args.push_back( tqparents[1] ); int winding = 0; int nsides = computeNsides( c, v, cntrl, winding ); ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( nsides ) ); args.push_back( d ); if ( winding > 1 ) { d = new ObjectConstCalcer( new IntImp( winding ) ); args.push_back( d ); } ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args ); ObjectHolder* h = new ObjectHolder( calcer ); std::vector ret; ret.push_back( h ); return ret; } TQString PolygonBCVConstructor::useText( const ObjectCalcer&, const std::vector& os, const KigDocument&, const KigWidget& ) const { switch ( os.size() ) { case 1: return i18n( "Construct a regular polygon with this center" ); break; case 2: return i18n( "Construct a regular polygon with this vertex" ); break; case 3: Coordinate c = static_cast( os[0]->imp() )->coordinate(); Coordinate v = static_cast( os[1]->imp() )->coordinate(); Coordinate cntrl = static_cast( os[2]->imp() )->coordinate(); int winding = 0; int nsides = computeNsides( c, v, cntrl, winding ); if ( winding > 1 ) { TQString result = TQString( i18n( "Adjust the number of sides (%1/%2)" ) ).tqarg( nsides ).tqarg( winding ); return result; } else { TQString result = TQString( i18n( "Adjust the number of sides (%1)" ) ).tqarg( nsides ); return result; } break; } return ""; } TQString PolygonBCVConstructor::selectStatement( const std::vector& os, const KigDocument&, const KigWidget& ) const { switch ( os.size() ) { case 1: return i18n( "Select the center of the new polygon..." ); break; case 2: return i18n( "Select a vertex for the new polygon..." ); break; case 3: return i18n( "Move the cursor to get the desired number of sides..." ); break; } return ""; } void PolygonBCVConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& doc ) const { if ( tqparents.size() < 3 || tqparents.size() > 4 ) return; assert ( tqparents[0]->imp()->inherits( PointImp::stype() ) && tqparents[1]->imp()->inherits( PointImp::stype() ) && tqparents[2]->imp()->inherits( IntImp::stype() ) ); if ( tqparents.size() == 4 ) assert ( tqparents[3]->imp()->inherits( IntImp::stype() ) ); Args args; std::transform( tqparents.begin(), tqparents.end(), std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) ); ObjectImp* data = mtype->calc( args, doc ); drawer.draw( *data, p, true ); delete data; data = 0; } void PolygonBCVConstructor::plug( KigPart*, KigGUIAction* ) { } bool PolygonBCVConstructor::isTransform() const { return false; } Coordinate PolygonBCVConstructor::getRotatedCoord( const Coordinate& c, const Coordinate& v, double alpha ) const { double cosalpha = cos(alpha); double sinalpha = sin(alpha); double dx = v.x - c.x; double dy = v.y - c.y; return c + Coordinate( cosalpha*dx - sinalpha*dy, sinalpha*dx + cosalpha*dy ); } int PolygonBCVConstructor::computeNsides ( const Coordinate& c, const Coordinate& v, const Coordinate& cntrl, int& winding ) const { Coordinate lvect = v - c; Coordinate rvect = cntrl - c; double angle = atan2( rvect.y, rvect.x ) - atan2( lvect.y, lvect.x ); angle = fabs( angle/(2*M_PI) ); while ( angle > 1 ) angle -= 1; if ( angle > 0.5 ) angle = 1 - angle; double realsides = 1.0/angle; // this is bigger that 2 if ( angle == 0. ) realsides = 3; if ( winding <= 0 ) // free to compute winding { winding = 1; double ratio = lvect.length()/rvect.length(); winding = int ( ratio ); if ( winding < 1 ) winding = 1; if ( winding > 50 ) winding = 50; } int nsides = int( winding*realsides + 0.5 ); // nsides/winding should be reduced! if ( nsides > 100 ) nsides = 100; // well, 100 seems large enough! if ( nsides < 3 ) nsides = 3; while ( !relativePrimes ( nsides, winding ) ) ++nsides; return nsides; } /* * ConicConic intersection... */ static const ArgsParser::spec argsspectc[] = { { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true }, { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true } }; ConicConicIntersectionConstructor::ConicConicIntersectionConstructor() : StandardConstructorBase( "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "curvelineintersection", mparser ), mparser( argsspectc, 2 ) { } ConicConicIntersectionConstructor::~ConicConicIntersectionConstructor() { } void ConicConicIntersectionConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const { if ( tqparents.size() != 2 ) return; assert ( tqparents[0]->imp()->inherits( ConicImp::stype() ) && tqparents[1]->imp()->inherits( ConicImp::stype() ) ); const ConicCartesianData conica = static_cast( tqparents[0]->imp() )->cartesianData(); const ConicCartesianData conicb = static_cast( tqparents[1]->imp() )->cartesianData(); bool ok = true; for ( int wr = -1; wr < 2; wr += 2 ) { LineData radical = calcConicRadical( conica, conicb, wr, 1, ok ); if ( ok ) { for ( int wi = -1; wi < 2; wi += 2 ) { Coordinate c = calcConicLineIntersect( conica, radical, 0.0, wi ); if ( c.valid() ) { PointImp pi( c ); drawer.draw( pi, p, true ); } }; }; }; } std::vector ConicConicIntersectionConstructor::build( const std::vector& os, KigDocument& doc, KigWidget& ) const { assert( os.size() == 2 ); std::vector ret; ObjectCalcer* conica = os[0]; ObjectConstCalcer* zeroindexdo = new ObjectConstCalcer( new IntImp( 1 ) ); for ( int wr = -1; wr < 2; wr += 2 ) { std::vector args = os; args.push_back( new ObjectConstCalcer( new IntImp( wr ) ) ); args.push_back( zeroindexdo ); ObjectTypeCalcer* radical = new ObjectTypeCalcer( ConicRadicalType::instance(), args ); radical->calc( doc ); for ( int wi = -1; wi < 2; wi += 2 ) { args.clear(); args.push_back( conica ); args.push_back( radical ); args.push_back( new ObjectConstCalcer( new IntImp( wi ) ) ); ret.push_back( new ObjectHolder( new ObjectTypeCalcer( ConicLineIntersectionType::instance(), args ) ) ); }; }; return ret; } void ConicConicIntersectionConstructor::plug( KigPart*, KigGUIAction* ) { } bool ConicConicIntersectionConstructor::isTransform() const { return false; } ConicLineIntersectionConstructor::ConicLineIntersectionConstructor() : MultiObjectTypeConstructor( ConicLineIntersectionType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "curvelineintersection", -1, 1 ) { } ConicLineIntersectionConstructor::~ConicLineIntersectionConstructor() { } ArcLineIntersectionConstructor::ArcLineIntersectionConstructor() : MultiObjectTypeConstructor( ArcLineIntersectionType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "curvelineintersection", -1, 1 ) { } ArcLineIntersectionConstructor::~ArcLineIntersectionConstructor() { } TQString ConicRadicalConstructor::useText( const ObjectCalcer& o, const std::vector&, const KigDocument&, const KigWidget& ) const { if ( o.imp()->inherits( CircleImp::stype() ) ) return i18n( "Construct the Radical Lines of This Circle" ); else return i18n( "Construct the Radical Lines of This Conic" ); } /* * generic affinity and generic projectivity. A unique affinity can be * obtained by specifying the image of three points (four for projectivity) * in the end we need, besides the object to be transformed, a total of * six point or (alternatively) two triangles; our affinity will map the * first triangle onto the second with corresponding ordering of their * vertices. Since we allow for two different ways of specifying the six * points we shall use a Generic constructor, like that for intersections. */ GenericAffinityConstructor::GenericAffinityConstructor() : MergeObjectConstructor( I18N_NOOP( "Generic Affinity" ), I18N_NOOP( "The unique affinity that maps three points (or a triangle) onto three other points (or a triangle)" ), "genericaffinity" ) { SimpleObjectTypeConstructor* b2tr = new SimpleObjectTypeConstructor( AffinityB2TrType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "genericaffinity" ); SimpleObjectTypeConstructor* gi3p = new SimpleObjectTypeConstructor( AffinityGI3PType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "genericaffinity" ); merge( b2tr ); merge( gi3p ); } GenericAffinityConstructor::~GenericAffinityConstructor() {} GenericProjectivityConstructor::GenericProjectivityConstructor() : MergeObjectConstructor( I18N_NOOP( "Generic Projective Transformation" ), I18N_NOOP( "The unique projective transformation that maps four points (or a quadrilateral) onto four other points (or a quadrilateral)" ), "genericprojectivity" ) { SimpleObjectTypeConstructor* b2qu = new SimpleObjectTypeConstructor( ProjectivityB2QuType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "genericprojectivity" ); SimpleObjectTypeConstructor* gi4p = new SimpleObjectTypeConstructor( ProjectivityGI4PType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "genericprojectivity" ); merge( b2qu ); merge( gi4p ); } GenericProjectivityConstructor::~GenericProjectivityConstructor() {} /* * inversion of points, lines with respect to a circle */ InversionConstructor::InversionConstructor() : MergeObjectConstructor( I18N_NOOP( "Inversion of Point, Line or Circle" ), I18N_NOOP( "The inversion of a point, line or circle with respect to a circle" ), "inversion" ) { SimpleObjectTypeConstructor* pointobj = new SimpleObjectTypeConstructor( InvertPointType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "inversion" ); SimpleObjectTypeConstructor* lineobj = new SimpleObjectTypeConstructor( InvertLineType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "inversion" ); SimpleObjectTypeConstructor* segmentobj = new SimpleObjectTypeConstructor( InvertSegmentType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "inversion" ); SimpleObjectTypeConstructor* circleobj = new SimpleObjectTypeConstructor( InvertCircleType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "inversion" ); SimpleObjectTypeConstructor* arcobj = new SimpleObjectTypeConstructor( InvertArcType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "inversion" ); merge( arcobj ); merge( circleobj ); merge( pointobj ); merge( segmentobj ); merge( lineobj ); } InversionConstructor::~InversionConstructor() {} /* * Transport of Measure */ MeasureTransportConstructor::MeasureTransportConstructor() : mtype( MeasureTransportType::instance() ) { } MeasureTransportConstructor::~MeasureTransportConstructor() { } const TQString MeasureTransportConstructor::descriptiveName() const { return i18n("Measure Transport"); } const TQString MeasureTransportConstructor::description() const { return i18n("Transport the measure of a segment or arc over a line or circle."); } const TQCString MeasureTransportConstructor::iconFileName( const bool ) const { return "measuretransport"; } const bool MeasureTransportConstructor::isAlreadySelectedOK( const std::vector&, const int& ) const { return false; } /* * we want the arguments in the exact order, this makes * the code simpler, but I guess it is also less confusing * to the user */ const int MeasureTransportConstructor::wantArgs( const std::vector& os, const KigDocument&, const KigWidget& ) const { if ( os.size() == 0 ) return ArgsParser::Valid; if ( ! os[0]->imp()->inherits( SegmentImp::stype() ) && ! os[0]->imp()->inherits( ArcImp::stype() ) ) return ArgsParser::Invalid; if ( os.size() == 1 ) return ArgsParser::Valid; if ( ! os[1]->imp()->inherits( LineImp::stype() ) && ! os[1]->imp()->inherits( CircleImp::stype() ) ) return ArgsParser::Invalid; if ( os.size() == 2 ) return ArgsParser::Valid; if ( ! os[2]->imp()->inherits( PointImp::stype() ) ) return ArgsParser::Invalid; // we here use the "isPointOnCurve", which relies on // "by construction" incidence, instead of a numerical // check if ( ! isPointOnCurve( os[2], os[1] ) ) return ArgsParser::Invalid; if ( os.size() == 3 ) return ArgsParser::Complete; return ArgsParser::Invalid; } void MeasureTransportConstructor::handleArgs( const std::vector& os, KigPart& d, KigWidget& v ) const { std::vector bos = build( os, d.document(), v ); for ( std::vector::iterator i = bos.begin(); i != bos.end(); ++i ) { (*i)->calc( d.document() ); } d.addObjects( bos ); } void MeasureTransportConstructor::handlePrelim( KigPainter& p, const std::vector& os, const KigDocument& d, const KigWidget& ) const { p.setBrushStyle( Qt::NoBrush ); p.setBrushColor( TQt::red ); p.setPen( TQPen ( TQt::red, 1) ); p.setWidth( -1 ); // -1 means the default width for the object being // drawn.. ObjectDrawer drawer( TQt::red ); drawprelim( drawer, p, os, d ); } void MeasureTransportConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& doc ) const { Args args; using namespace std; transform( tqparents.begin(), tqparents.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) ); ObjectImp* data = mtype->calc( args, doc ); drawer.draw( *data, p, true ); delete data; } TQString MeasureTransportConstructor::useText( const ObjectCalcer& o, const std::vector& os, const KigDocument&, const KigWidget& ) const { if ( o.imp()->inherits( SegmentImp::stype() ) ) return i18n("Segment to transport"); if ( o.imp()->inherits( ArcImp::stype() ) ) return i18n("Arc to transport"); if ( o.imp()->inherits( LineImp::stype() ) ) return i18n("Transport a measure on this line"); if ( o.imp()->inherits( CircleImp::stype() ) ) return i18n("Transport a measure on this circle"); if ( o.imp()->inherits( PointImp::stype() ) ) { if ( os[1]->imp()->inherits( CircleImp::stype() ) ) return i18n("Start transport from this point of the circle"); if ( os[1]->imp()->inherits( LineImp::stype() ) ) return i18n("Start transport from this point of the line"); else return i18n("Start transport from this point of the curve"); // well, this isn't impemented yet, should never get here } return ""; } TQString MeasureTransportConstructor::selectStatement( const std::vector&, const KigDocument&, const KigWidget& ) const { //TODO return i18n("Select a point to be a vertex of the new polygon..."); } std::vector MeasureTransportConstructor::build( const std::vector& tqparents, KigDocument&, KigWidget& ) const { assert ( tqparents.size() == 3 ); // std::vector args; // for ( uint i = 0; i < count; ++i ) args.push_back( tqparents[i] ); ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, tqparents ); ObjectHolder* h = new ObjectHolder( calcer ); std::vector ret; ret.push_back( h ); return ret; } void MeasureTransportConstructor::plug( KigPart*, KigGUIAction* ) { } bool MeasureTransportConstructor::isTransform() const { return false; } /* * Generic intersection */ GenericIntersectionConstructor::GenericIntersectionConstructor() : MergeObjectConstructor( I18N_NOOP( "Intersect" ), I18N_NOOP( "The intersection of two objects" ), "curvelineintersection" ) { // intersection type.. // There is one "toplevel" object_constructor, that is composed // of multiple subconstructors.. First we build the // subconstructors: SimpleObjectTypeConstructor* lineline = new SimpleObjectTypeConstructor( LineLineIntersectionType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "curvelineintersection" ); ObjectConstructor* lineconic = new ConicLineIntersectionConstructor(); ObjectConstructor* arcline = new ArcLineIntersectionConstructor(); MultiObjectTypeConstructor* linecubic = new MultiObjectTypeConstructor( LineCubicIntersectionType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "curvelineintersection", 1, 2, 3 ); ObjectConstructor* conicconic = new ConicConicIntersectionConstructor(); MultiObjectTypeConstructor* circlecircle = new MultiObjectTypeConstructor( CircleCircleIntersectionType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "circlecircleintersection", -1, 1 ); SimpleObjectTypeConstructor* polygonline = new SimpleObjectTypeConstructor( PolygonLineIntersectionType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "curvelineintersection" ); merge( lineline ); merge( circlecircle ); merge( lineconic ); merge( linecubic ); merge( conicconic ); merge( arcline ); merge( polygonline ); } GenericIntersectionConstructor::~GenericIntersectionConstructor() { } bool GenericIntersectionConstructor::isIntersection() const { return true; } TQString GenericIntersectionConstructor::useText( const ObjectCalcer& o, const std::vector& os, const KigDocument&, const KigWidget& ) const { TQString preamble; switch (os.size()) { case 1: if ( o.imp()->inherits( CircleImp::stype() ) ) return i18n( "Intersect this Circle" ); else if ( o.imp()->inherits( ConicImp::stype() ) ) return i18n( "Intersect this Conic" ); else if ( o.imp()->inherits( AbstractLineImp::stype() ) ) return i18n( "Intersect this Line" ); else if ( o.imp()->inherits( CubicImp::stype() ) ) return i18n( "Intersect this Cubic Curve" ); else if ( o.imp()->inherits( ArcImp::stype() ) ) return i18n( "Intersect this Arc" ); else if ( o.imp()->inherits( PolygonImp::stype() ) ) return i18n( "Intersect this Polygon" ); else assert( false ); break; case 2: if ( o.imp()->inherits( CircleImp::stype() ) ) return i18n( "with this Circle" ); else if ( o.imp()->inherits( ConicImp::stype() ) ) return i18n( "with this Conic" ); else if ( o.imp()->inherits( AbstractLineImp::stype() ) ) return i18n( "with this Line" ); else if ( o.imp()->inherits( CubicImp::stype() ) ) return i18n( "with this Cubic Curve" ); else if ( o.imp()->inherits( ArcImp::stype() ) ) return i18n( "with this Arc" ); else if ( o.imp()->inherits( PolygonImp::stype() ) ) return i18n( "with this Polygon" ); else assert( false ); break; } return TQString(); } static const ArgsParser::spec argsspecMidPointOfTwoPoints[] = { { PointImp::stype(), I18N_NOOP( "Construct Midpoint of This Point and Another One" ), I18N_NOOP( "Select the first of the points of which you want to construct the midpoint..." ), false }, { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another one" ), I18N_NOOP( "Select the other of the points of which to construct the midpoint..." ), false } }; MidPointOfTwoPointsConstructor::MidPointOfTwoPointsConstructor() : StandardConstructorBase( "Mid Point", "Construct the midpoint of two points", "bisection", mparser ), mparser( argsspecMidPointOfTwoPoints, 2 ) { } MidPointOfTwoPointsConstructor::~MidPointOfTwoPointsConstructor() { } void MidPointOfTwoPointsConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector& tqparents, const KigDocument& ) const { if ( tqparents.size() != 2 ) return; assert( tqparents[0]->imp()->inherits( PointImp::stype() ) ); assert( tqparents[1]->imp()->inherits( PointImp::stype() ) ); const Coordinate m = ( static_cast( tqparents[0]->imp() )->coordinate() + static_cast( tqparents[1]->imp() )->coordinate() ) / 2; drawer.draw( PointImp( m ), p, true ); } std::vector MidPointOfTwoPointsConstructor::build( const std::vector& os, KigDocument& d, KigWidget& ) const { ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), os ); seg->calc( d ); int index = seg->imp()->propertiesInternalNames().tqfindIndex( "mid-point" ); assert( index != -1 ); ObjectPropertyCalcer* prop = new ObjectPropertyCalcer( seg, index ); prop->calc( d ); std::vector ret; ret.push_back( new ObjectHolder( prop ) ); return ret; } void MidPointOfTwoPointsConstructor::plug( KigPart*, KigGUIAction* ) { } bool MidPointOfTwoPointsConstructor::isTransform() const { return false; } TestConstructor::TestConstructor( const ArgsParserObjectType* type, const char* descname, const char* desc, const char* iconfile ) : StandardConstructorBase( descname, desc, iconfile, type->argsParser() ), mtype( type ) { } TestConstructor::~TestConstructor() { } void TestConstructor::drawprelim( const ObjectDrawer&, KigPainter&, const std::vector&, const KigDocument& ) const { // not used, only here because of the wrong // ObjectConstructor-GUIAction design. See the TODO } std::vector TestConstructor::build( const std::vector&, KigDocument&, KigWidget& ) const { // not used, only here because of the wrong // ObjectConstructor-GUIAction design. See the TODO std::vector ret; return ret; } void TestConstructor::plug( KigPart*, KigGUIAction* ) { } bool TestConstructor::isTransform() const { return false; } bool TestConstructor::isTest() const { return true; } BaseConstructMode* TestConstructor::constructMode( KigPart& doc ) { return new TestConstructMode( doc, mtype ); } const int TestConstructor::wantArgs( const std::vector& os, const KigDocument& d, const KigWidget& v ) const { int ret = StandardConstructorBase::wantArgs( os, d, v ); if ( ret == ArgsParser::Complete ) ret = ArgsParser::Valid; return ret; } TQString GenericIntersectionConstructor::selectStatement( const std::vector& sel, const KigDocument&, const KigWidget& ) const { if ( sel.size() == 0 ) return i18n( "Select the first object to intersect..." ); else return i18n( "Select the second object to intersect..." ); } TangentConstructor::TangentConstructor() : MergeObjectConstructor( I18N_NOOP( "Tangent" ), I18N_NOOP( "The line tangent to a curve" ), "tangent" ) { SimpleObjectTypeConstructor* conic = new SimpleObjectTypeConstructor( TangentConicType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "tangentconic" ); SimpleObjectTypeConstructor* arc = new SimpleObjectTypeConstructor( TangentArcType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "tangentarc" ); SimpleObjectTypeConstructor* cubic = new SimpleObjectTypeConstructor( TangentCubicType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "tangentcubic" ); SimpleObjectTypeConstructor* curve = new SimpleObjectTypeConstructor( TangentCurveType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "tangentcurve" ); merge( conic ); merge( arc ); merge( cubic ); merge( curve ); } TangentConstructor::~TangentConstructor() { } TQString TangentConstructor::useText( const ObjectCalcer& o, const std::vector&, const KigDocument&, const KigWidget& ) const { if ( o.imp()->inherits( CircleImp::stype() ) ) return i18n( "Tangent to This Circle" ); else if ( o.imp()->inherits( ConicImp::stype() ) ) return i18n( "Tangent to This Conic" ); else if ( o.imp()->inherits( ArcImp::stype() ) ) return i18n( "Tangent to This Arc" ); else if ( o.imp()->inherits( CubicImp::stype() ) ) return i18n( "Tangent to This Cubic Curve" ); else if ( o.imp()->inherits( CurveImp::stype() ) ) return i18n( "Tangent to This Curve" ); else if ( o.imp()->inherits( PointImp::stype() ) ) return i18n( "Tangent at This Point" ); // else assert( false ); return TQString(); } //TQString TangentConstructor::selectStatement( // const std::vector& sel, const KigDocument&, // const KigWidget& ) const //{ // if ( sel.size() == 0 ) // return i18n( "Select the object..." ); // else // return i18n( "Select the point for the tangent to go through..." ); //} /* * center of curvature of a curve */ CocConstructor::CocConstructor() : MergeObjectConstructor( I18N_NOOP( "Center Of Curvature" ), I18N_NOOP( "The center of the osculating circle to a curve" ), "centerofcurvature" ) { SimpleObjectTypeConstructor* conic = new SimpleObjectTypeConstructor( CocConicType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "cocconic" ); SimpleObjectTypeConstructor* cubic = new SimpleObjectTypeConstructor( CocCubicType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "coccubic" ); SimpleObjectTypeConstructor* curve = new SimpleObjectTypeConstructor( CocCurveType::instance(), "SHOULDNOTBESEEN", "SHOULDNOTBESEEN", "coccurve" ); merge( conic ); merge( cubic ); merge( curve ); } CocConstructor::~CocConstructor() { } TQString CocConstructor::useText( const ObjectCalcer& o, const std::vector&, const KigDocument&, const KigWidget& ) const { if ( o.imp()->inherits( ConicImp::stype() ) ) return i18n( "Center of Curvature of This Conic" ); else if ( o.imp()->inherits( CubicImp::stype() ) ) return i18n( "Center of Curvature of This Cubic Curve" ); else if ( o.imp()->inherits( CurveImp::stype() ) ) return i18n( "Center of Curvature of This Curve" ); else if ( o.imp()->inherits( PointImp::stype() ) ) return i18n( "Center of Curvature at This Point" ); return TQString(); } bool relativePrimes( int n, int p ) { if ( p > n ) return relativePrimes( p, n ); assert ( p >= 0 ); if ( p == 0 ) return false; if ( p == 1 ) return true; int d = int( n/p ); return relativePrimes( p, n-d*p ); } //TQString CocConstructor::selectStatement( // const std::vector& sel, const KigDocument&, // const KigWidget& ) const //{ // if ( sel.size() == 0 ) // return i18n( "Select the object..." ); // else // return i18n( "Select the point where to compute the center of curvature..." ); //}