diff options
Diffstat (limited to 'kig/objects')
79 files changed, 17643 insertions, 0 deletions
diff --git a/kig/objects/Makefile.am b/kig/objects/Makefile.am new file mode 100644 index 00000000..21a02c6f --- /dev/null +++ b/kig/objects/Makefile.am @@ -0,0 +1,81 @@ +INCLUDES=$(all_includes) +noinst_LTLIBRARIES=libobjects.la +noinst_HEADERS=\ + angle_type.h \ + arc_type.h \ + base_type.h \ + bogus_imp.h \ + circle_imp.h \ + circle_type.h \ + polygon_type.h \ + common.h \ + conic_imp.h \ + conic_types.h \ + cubic_imp.h \ + cubic_type.h \ + curve_imp.h \ + intersection_types.h \ + inversion_type.h \ + line_imp.h \ + line_type.h \ + locus_imp.h \ + object_calcer.h \ + object_drawer.h \ + object_factory.h \ + object_holder.h \ + object_imp.h \ + object_imp_factory.h \ + object_type.h \ + object_type_factory.h \ + other_imp.h \ + other_type.h \ + point_imp.h \ + polygon_imp.h \ + tangent_type.h \ + centerofcurvature_type.h \ + tests_type.h \ + text_imp.h \ + text_type.h \ + transform_types.h \ + vector_type.h +libobjects_la_SOURCES=\ + angle_type.cc \ + arc_type.cc \ + base_type.cc \ + bogus_imp.cc \ + circle_imp.cc \ + circle_type.cc \ + polygon_type.cc \ + common.cc \ + conic_imp.cc \ + conic_types.cc \ + cubic_imp.cc \ + cubic_type.cc \ + curve_imp.cc \ + intersection_types.cc \ + inversion_type.cc \ + line_imp.cc \ + line_type.cc \ + locus_imp.cc \ + object_calcer.cc \ + object_drawer.cc \ + object_factory.cc \ + object_holder.cc \ + object_imp.cc \ + object_imp_factory.cc \ + object_type.cc \ + object_type_factory.cc \ + other_imp.cc \ + other_type.cc \ + point_imp.cc \ + point_type.cc \ + polygon_imp.cc \ + tangent_type.cc \ + centerofcurvature_type.cc \ + tests_type.cc \ + text_imp.cc \ + text_type.cc \ + transform_types.cc \ + vector_type.cc +libobjects_la_LIBADD=-lm +METASOURCES=AUTO diff --git a/kig/objects/angle_type.cc b/kig/objects/angle_type.cc new file mode 100644 index 00000000..89a17131 --- /dev/null +++ b/kig/objects/angle_type.cc @@ -0,0 +1,208 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// 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 "angle_type.h" + +#include "bogus_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "../misc/calcpaths.h" +#include "../misc/common.h" +#include "../misc/goniometry.h" +#include "../misc/kiginputdialog.h" +#include "../kig/kig_commands.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +#include <functional> +#include <algorithm> +#include <cmath> + +#include <qstringlist.h> + +static const char* constructanglethroughpoint = + I18N_NOOP( "Construct an angle through this point" ); + +static const ArgsParser::spec argsspecAngle[] = +{ + { PointImp::stype(), constructanglethroughpoint, + I18N_NOOP( "Select a point that the first half-line of the angle should go through..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct an angle at this point" ), + I18N_NOOP( "Select the point to construct the angle in..." ), true }, + { PointImp::stype(), constructanglethroughpoint, + I18N_NOOP( "Select a point that the second half-line of the angle should go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AngleType ) + +AngleType::AngleType() + : ArgsParserObjectType( "Angle", argsspecAngle, 3 ) +{ +} + +AngleType::~AngleType() +{ +} + +const AngleType* AngleType::instance() +{ + static const AngleType t; + return &t; +} + +ObjectImp* AngleType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( uint i = 0; i < parents.size(); ++i ) + points.push_back( + static_cast<const PointImp*>( parents[i] )->coordinate() ); + + Coordinate lvect = points[0] - points[1]; + Coordinate rvect; + if ( points.size() == 3 ) + rvect = points[2] - points[1]; + else + { + rvect = lvect.orthogonal(); + } + + double startangle = atan2( lvect.y, lvect.x ); + double endangle = atan2( rvect.y, rvect.x ); + double anglelength = endangle - startangle; + if ( anglelength < 0 ) anglelength += 2* M_PI; + if ( startangle < 0 ) startangle += 2*M_PI; + + return new AngleImp( points[1], startangle, anglelength ); +} + +const ObjectImpType* AngleType::resultId() const +{ + return AngleImp::stype(); +} + +QStringList AngleType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set Si&ze" ); + return ret; +} + +void AngleType::executeAction( + int i, ObjectHolder&, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& ) const +{ + assert( i == 0 ); + // pretend to use this var.. + (void) i; + + std::vector<ObjectCalcer*> parents = t.parents(); + + assert( margsparser.checkArgs( parents ) ); + + Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + Coordinate c = static_cast<const PointImp*>( parents[2]->imp() )->coordinate(); + + Coordinate lvect = a - b; + Coordinate rvect = c - b; + + double startangle = atan2( lvect.y, lvect.x ); + double endangle = atan2( rvect.y, rvect.x ); + double anglelength = endangle - startangle; + if ( anglelength < 0 ) anglelength += 2* M_PI; + if ( startangle < 0 ) startangle += 2*M_PI; + + Goniometry go( anglelength, Goniometry::Rad ); + go.convertTo( Goniometry::Deg ); + + bool ok; + Goniometry newsize = KigInputDialog::getAngle( &w, &ok, go ); + if ( !ok ) + return; + newsize.convertTo( Goniometry::Rad ); + + double newcangle = startangle + newsize.value(); + Coordinate cdir( cos( newcangle ), sin( newcangle ) ); + Coordinate nc = b + cdir.normalize( rvect.length() ); + + MonitorDataObjects mon( getAllParents( parents ) ); + parents[2]->move( nc, d.document() ); + KigCommand* kc = new KigCommand( d, i18n( "Resize Angle" ) ); + mon.finish( kc ); + d.history()->addCommand( kc ); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HalfAngleType ) + +HalfAngleType::HalfAngleType() + : ArgsParserObjectType( "HalfAngle", argsspecAngle, 3 ) +{ +} + +HalfAngleType::~HalfAngleType() +{ +} + +const HalfAngleType* HalfAngleType::instance() +{ + static const HalfAngleType t; + return &t; +} + +ObjectImp* HalfAngleType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( uint i = 0; i < parents.size(); ++i ) + points.push_back( + static_cast<const PointImp*>( parents[i] )->coordinate() ); + + Coordinate lvect = points[0] - points[1]; + Coordinate rvect; + if ( points.size() == 3 ) + rvect = points[2] - points[1]; + else + { + rvect = lvect.orthogonal(); + } + + double startangle = atan2( lvect.y, lvect.x ); + double endangle = atan2( rvect.y, rvect.x ); + double anglelength = endangle - startangle; + if ( anglelength < 0 ) anglelength += 2 * M_PI; + if ( startangle < 0 ) startangle += 2 * M_PI; + + if ( anglelength > M_PI ) + { + startangle += anglelength; + anglelength = 2 * M_PI - anglelength; + if ( startangle > 2 * M_PI ) startangle -= 2 * M_PI; + if ( anglelength < 0 ) anglelength += 2 * M_PI; + } + + return new AngleImp( points[1], startangle, anglelength ); +} + +const ObjectImpType* HalfAngleType::resultId() const +{ + return AngleImp::stype(); +} + diff --git a/kig/objects/angle_type.h b/kig/objects/angle_type.h new file mode 100644 index 00000000..796143b4 --- /dev/null +++ b/kig/objects/angle_type.h @@ -0,0 +1,50 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@kde.org> +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// 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. + +#ifndef KIG_MISC_ANGLE_TYPE_H +#define KIG_MISC_ANGLE_TYPE_H + +#include "base_type.h" + +class AngleType + : public ArgsParserObjectType +{ + AngleType(); + ~AngleType(); +public: + static const AngleType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class HalfAngleType + : public ArgsParserObjectType +{ + HalfAngleType(); + ~HalfAngleType(); +public: + static const HalfAngleType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/arc_type.cc b/kig/objects/arc_type.cc new file mode 100644 index 00000000..f6c660f2 --- /dev/null +++ b/kig/objects/arc_type.cc @@ -0,0 +1,199 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@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. + +// 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 "arc_type.h" + +#include "bogus_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "line_imp.h" +#include "locus_imp.h" + +#include "../misc/common.h" +#include "../misc/calcpaths.h" +#include "../misc/goniometry.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" + +#include <functional> +#include <algorithm> +#include <cmath> + +using std::find; + +#include <qstringlist.h> + +static const char constructarcstartingstat[] = I18N_NOOP( "Construct an arc starting at this point" ); + +static const ArgsParser::spec argsspecArcBTP[] = +{ + { PointImp::stype(), constructarcstartingstat, + I18N_NOOP( "Select the start point of the new arc..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct an arc through this point" ), + I18N_NOOP( "Select a point for the new arc to go through..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct an arc ending at this point" ), + I18N_NOOP( "Select the end point of the new arc..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcBTPType ) + +ArcBTPType::ArcBTPType() + : ArgsParserObjectType( "ArcBTP", argsspecArcBTP, 3 ) +{ +} + +ArcBTPType::~ArcBTPType() +{ +} + +const ArcBTPType* ArcBTPType::instance() +{ + static const ArcBTPType t; + return &t; +} + +ObjectImp* ArcBTPType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args, 2 ) ) + return new InvalidImp; + + const Coordinate a = + static_cast<const PointImp*>( args[0] )->coordinate(); + const Coordinate b = + static_cast<const PointImp*>( args[1] )->coordinate(); + Coordinate center; + double angle = 0.; + double startangle = 0.; + if ( args.size() == 3 ) + { + Coordinate c = static_cast<const PointImp*>( args[2] )->coordinate(); + center = calcCenter( a, b, c ); + if ( ! center.valid() ) return new InvalidImp; + Coordinate ad = a - center; + Coordinate bd = b - center; + Coordinate cd = c - center; + double anglea = atan2( ad.y, ad.x ); + double angleb = atan2( bd.y, bd.x ); + double anglec = atan2( cd.y, cd.x ); + + // anglea should be smaller than anglec + if ( anglea > anglec ) + { + double t = anglea; + anglea = anglec; + anglec = t; + }; + if ( angleb > anglec || angleb < anglea ) + { + startangle = anglec; + angle = 2 * M_PI + anglea - startangle; + } + else + { + startangle = anglea; + angle = anglec - anglea; + }; + } + else + { + // find a center and angles that look natural.. + center = (b+a)/2 + .6*(b-a).orthogonal(); + Coordinate bd = b - center; + Coordinate ad = a - center; + startangle = atan2( ad.y, ad.x ); + double halfangle = atan2( bd.y, bd.x ) - startangle; + if ( halfangle < - M_PI ) halfangle += 2*M_PI; + angle = 2 * halfangle; + }; + + double radius = ( a - center ).length(); + return new ArcImp( center, radius, startangle, angle ); +} + +const ObjectImpType* ArcBTPType::impRequirement( const ObjectImp*, const Args& ) const +{ + return PointImp::stype(); +} + +bool ArcBTPType::inherits( int type ) const +{ + return Parent::inherits( type ); +} + +const ObjectImpType* ArcBTPType::resultId() const +{ + return ArcImp::stype(); +} + +static const ArgsParser::spec argsspecArcBCPA[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct an arc with this center" ), + I18N_NOOP( "Select the center of the new arc..." ), true }, + { PointImp::stype(), constructarcstartingstat, + I18N_NOOP( "Select the start point of the new arc..." ), true }, + { AngleImp::stype(), I18N_NOOP( "Construct an arc with this angle" ), + I18N_NOOP( "Select the angle of the new arc..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcBCPAType ) + +ArcBCPAType::ArcBCPAType() + : ArgsParserObjectType( "ArcBCPA", argsspecArcBCPA, 3 ) +{ +} + +ArcBCPAType::~ArcBCPAType() +{ +} + +const ArcBCPAType* ArcBCPAType::instance() +{ + static const ArcBCPAType t; + return &t; +} + +ObjectImp* ArcBCPAType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) + return new InvalidImp; + + const Coordinate center = static_cast<const PointImp*>( args[0] )->coordinate(); + const Coordinate p = static_cast<const PointImp*>( args[1] )->coordinate(); + const AngleImp* a = static_cast<const AngleImp*>( args[2] ); + const double angle = a->angle(); + const Coordinate dir = p - center; + const double startangle = atan2( dir.y, dir.x ); + const double radius = center.distance( p ); + + return new ArcImp( center, radius, startangle, angle ); +} + +const ObjectImpType* ArcBCPAType::impRequirement( const ObjectImp*, const Args& ) const +{ + return PointImp::stype(); +} + +bool ArcBCPAType::inherits( int type ) const +{ + return Parent::inherits( type ); +} + +const ObjectImpType* ArcBCPAType::resultId() const +{ + return ArcImp::stype(); +} diff --git a/kig/objects/arc_type.h b/kig/objects/arc_type.h new file mode 100644 index 00000000..cdfe0294 --- /dev/null +++ b/kig/objects/arc_type.h @@ -0,0 +1,64 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_ARC_TYPE_H +#define KIG_OBJECTS_ARC_TYPE_H + +#include "base_type.h" +#include "../misc/object_hierarchy.h" + +/** + * an arc by a start point, an intermediate point and an end point + */ +class ArcBTPType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + ArcBTPType(); + ~ArcBTPType(); +public: + static const ArcBTPType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + bool inherits( int type ) const; + const ObjectImpType* resultId() const; +}; + +/** + * an arc by a point (center), a starting point and an angle + */ +class ArcBCPAType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + ArcBCPAType(); + ~ArcBCPAType(); +public: + static const ArcBCPAType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + bool inherits( int type ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/base_type.cc b/kig/objects/base_type.cc new file mode 100644 index 00000000..0f8eecec --- /dev/null +++ b/kig/objects/base_type.cc @@ -0,0 +1,112 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "base_type.h" + +#include "point_imp.h" +#include "line_imp.h" +#include "bogus_imp.h" +#include "object_calcer.h" + +#include "../misc/common.h" + +ObjectABType::ObjectABType( const char* fulltypename, const ArgsParser::spec* spec, int n ) + : ArgsParserObjectType( fulltypename, spec, n ) +{ +} + +ObjectABType::~ObjectABType() +{ +} + +ObjectImp* ObjectABType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) + return new InvalidImp; + + Coordinate a = static_cast<const PointImp*>( parents[0] )->coordinate(); + Coordinate b = static_cast<const PointImp*>( parents[1] )->coordinate(); + + return calc( a, b ); +} + +bool ObjectABType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +/* + * as observed by domi: this object is actually movable also + * if one point is FreelyTranslatable and the other is + * only movable, but then the "move" itself requires some + * trickery. + */ +} + +bool ObjectABType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return parents[0]->isFreelyTranslatable() && parents[1]->isFreelyTranslatable(); +} + +void ObjectABType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + const Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + const Coordinate dist = b - a; + if ( parents[0]->canMove() ) + parents[0]->move( to, d ); + if ( parents[1]->canMove() ) + parents[1]->move( to + dist, d ); +} + +ObjectLPType::ObjectLPType( const char* fullname, const ArgsParser::spec* spec, int n ) + : ArgsParserObjectType( fullname, spec, n ) +{ +} + +ObjectLPType::~ObjectLPType() +{ +} + +ObjectImp* ObjectLPType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + LineData l = static_cast<const AbstractLineImp*>( args[0] )->data(); + Coordinate c = static_cast<const PointImp*>( args[1] )->coordinate(); + return calc( l, c ); +} + +const Coordinate ObjectABType::moveReferencePoint( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> ObjectABType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + std::vector<ObjectCalcer*> tmp = parents[0]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[1]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + ret.insert( parents.begin(), parents.end() ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} diff --git a/kig/objects/base_type.h b/kig/objects/base_type.h new file mode 100644 index 00000000..ff9c1983 --- /dev/null +++ b/kig/objects/base_type.h @@ -0,0 +1,57 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_BASE_TYPE_H +#define KIG_OBJECTS_BASE_TYPE_H + +#include "object_type.h" + +#include "../misc/argsparser.h" + +class LineData; + +class ObjectABType + : public ArgsParserObjectType +{ +protected: + ObjectABType( const char* fulltypename, const ArgsParser::spec* argsspec, int n ); + ~ObjectABType(); +public: + ObjectImp* calc( const Args& args, const KigDocument& ) const; + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; + + virtual ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const = 0; +}; + +class ObjectLPType + : public ArgsParserObjectType +{ +protected: + ObjectLPType( const char* fullname, const ArgsParser::spec* spec, int n ); + ~ObjectLPType(); +public: + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + virtual ObjectImp* calc( const LineData& a, const Coordinate& b ) const = 0; +}; + +#endif diff --git a/kig/objects/bogus_imp.cc b/kig/objects/bogus_imp.cc new file mode 100644 index 00000000..c1ed6526 --- /dev/null +++ b/kig/objects/bogus_imp.cc @@ -0,0 +1,388 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "bogus_imp.h" + +#include <qcstring.h> +#include <qstringlist.h> +#include <klocale.h> + +#include "../misc/rect.h" + +Coordinate BogusImp::attachPoint( ) const +{ + return Coordinate::invalidCoord(); +} + +void BogusImp::draw( KigPainter& ) const +{ +} + +bool BogusImp::contains( const Coordinate&, int, const KigWidget& ) const +{ + return false; +} + +bool BogusImp::inRect( const Rect&, int, const KigWidget& ) const +{ + return false; +} + +DoubleImp::DoubleImp( const double d ) + : mdata( d ) +{ +} + +IntImp::IntImp( const int d ) + : mdata( d ) +{ +} + +StringImp::StringImp( const QString& d ) + : mdata( d ) +{ +} + +DoubleImp* DoubleImp::copy() const +{ + return new DoubleImp( mdata ); +} + +IntImp* IntImp::copy() const +{ + return new IntImp( mdata ); +} + +StringImp* StringImp::copy() const +{ + return new StringImp( mdata ); +} + +ObjectImp* BogusImp::transform( const Transformation& ) const +{ + return copy(); +} + +InvalidImp* InvalidImp::copy() const +{ + return new InvalidImp(); +} + +InvalidImp::InvalidImp() +{ +} + +void InvalidImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( "[invalid]" ); +} + +void DoubleImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( mdata ); +} + +void IntImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( mdata ); +} + +void StringImp::fillInNextEscape( QString& s, const KigDocument& ) const +{ + s = s.arg( mdata ); +} + +HierarchyImp::HierarchyImp( const ObjectHierarchy& h ) + : BogusImp(), mdata( h ) +{ +} + +HierarchyImp* HierarchyImp::copy() const +{ + return new HierarchyImp( mdata ); +} + +void InvalidImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void DoubleImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void IntImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void StringImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void HierarchyImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +TransformationImp::TransformationImp( const Transformation& h ) + : mdata( h ) +{ +} + +TransformationImp* TransformationImp::copy() const +{ + return new TransformationImp( mdata ); +} + +void TransformationImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool InvalidImp::equals( const ObjectImp& rhs ) const +{ + return !rhs.valid(); +} + +bool DoubleImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( DoubleImp::stype() ) && + static_cast<const DoubleImp&>( rhs ).data() == mdata; +} + +bool IntImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( IntImp::stype() ) && + static_cast<const IntImp&>( rhs ).data() == mdata; +} + +bool StringImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( StringImp::stype() ) && + static_cast<const StringImp&>( rhs ).data() == mdata; +} + +bool HierarchyImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( HierarchyImp::stype() ) && + static_cast<const HierarchyImp&>( rhs ).data() == mdata; +} + +bool TransformationImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( TransformationImp::stype() ) && + static_cast<const TransformationImp&>( rhs ).data() == mdata; +} + +bool InvalidImp::canFillInNextEscape() const +{ + return true; +} + +bool DoubleImp::canFillInNextEscape() const +{ + return true; +} + +bool IntImp::canFillInNextEscape() const +{ + return true; +} + +bool StringImp::canFillInNextEscape() const +{ + return true; +} + +const ObjectImpType* InvalidImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "invalid", "", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* StringImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "string", + "string", "", "", "", "", "", "", "", "" ); + return &t; +} +const ObjectImpType* HierarchyImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "hierarchy", "", "", "", "", "", "", "", "", "" ); + return &t; +} +const ObjectImpType* TransformationImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "transformation", "", "", "", "", "", "", "", "", ""); + return &t; +} + +const ObjectImpType* InvalidImp::type() const +{ + return InvalidImp::stype(); +} + +const ObjectImpType* DoubleImp::type() const +{ + return DoubleImp::stype(); +} + +const ObjectImpType* IntImp::type() const +{ + return IntImp::stype(); +} + +const ObjectImpType* StringImp::type() const +{ + return StringImp::stype(); +} + +const ObjectImpType* HierarchyImp::type() const +{ + return HierarchyImp::stype(); +} + +const ObjectImpType* TransformationImp::type() const +{ + return TransformationImp::stype(); +} + +const ObjectImpType* DoubleImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "double", + "double", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* IntImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "int", + "int", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* BogusImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "bogus", + "", "", "", "", "", "", "", "", "" ); + return &t; +} + +const ObjectImpType* TestResultImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "testresult", "", "", "", "", "", "", "", "", "" ); + return &t; + +} + +TestResultImp::TestResultImp( const QString& s ) + : mdata( s ) +{ +} + +TestResultImp* TestResultImp::copy() const +{ + return new TestResultImp( mdata ); +} + +const ObjectImpType* TestResultImp::type() const +{ + return stype(); +} + +void TestResultImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool TestResultImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( TestResultImp::stype() ) && + static_cast<const TestResultImp&>( rhs ).mdata == mdata; + +} + +const uint TestResultImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 1; +} + +const QCStringList TestResultImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Test Result" ); + assert( l.size() == TestResultImp::numberOfProperties() ); + return l; +} + +const QCStringList TestResultImp::propertiesInternalNames() const +{ + QCStringList s = Parent::propertiesInternalNames(); + s << "test-result"; + assert( s.size() == TestResultImp::numberOfProperties() ); + return s; +} + +ObjectImp* TestResultImp::property( uint which, const KigDocument& d ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, d ); + if ( which == Parent::numberOfProperties() ) + return new StringImp( data() ); + else assert( false ); + return new InvalidImp; +} + +const char* TestResultImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() ) + return ""; // test-result + else assert( false ); + return ""; +} + +const ObjectImpType* TestResultImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return TestResultImp::stype(); +} + +bool TestResultImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return false; +} + +Rect BogusImp::surroundingRect() const +{ + return Rect::invalidRect(); +} diff --git a/kig/objects/bogus_imp.h b/kig/objects/bogus_imp.h new file mode 100644 index 00000000..8e9386a8 --- /dev/null +++ b/kig/objects/bogus_imp.h @@ -0,0 +1,281 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef BOGUS_IMP_H +#define BOGUS_IMP_H + +#include "object_imp.h" +#include "../misc/object_hierarchy.h" +#include "../misc/kigtransform.h" + +#include <qstring.h> + +/** + * This is the base class for the so-called BogusImp's. These + * ObjectImp's are not really ObjectImp's, in that they don't + * represent objects. They exist because ObjectImp's also serve + * another purpose, namely containing data. They can all be loaded + * and saved, and the only difference between these objects and normal + * objects are that these serve *only* to be loaded and saved. This + * approach adds a lot of flexibility to the Kig system, and has + * certainly proven itself very valuable. + */ +class BogusImp + : public ObjectImp +{ + typedef ObjectImp Parent; +public: + /** + * Returns the ObjectImpType representing the BogusImp type. + */ + static const ObjectImpType* stype(); + + Coordinate attachPoint( ) const; + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& w ) const; + bool inRect( const Rect& r, int width, const KigWidget& w ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; +}; + +/** + * This ObjectImp represents an invalid object. If a calculation + * fails, then often an InvalidImp is returned, indicating that the + * generated object is invalid. + */ +class InvalidImp + : public BogusImp +{ +public: + /** + * Returns the ObjectImpType representing the InvalidImp type. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new InvalidImp. + */ + InvalidImp(); + InvalidImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * This ObjectImp is a BogusImp containing only a double value. + */ +class DoubleImp + : public BogusImp +{ + double mdata; +public: + /** + * Returns the ObjectImpType representing the DoubleImp type. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new DoubleImp containing the value d. + */ + DoubleImp( const double d ); + + /** + * Get hold of the contained data. + */ + double data() const { return mdata; } + /** + * Set the contained data to d. + */ + void setData( double d ) { mdata = d; } + + DoubleImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * This ObjectImp is a BogusImp containing only an int value. + */ +class IntImp + : public BogusImp +{ + int mdata; +public: + /** + * Returns the ObjectImpType representing the IntImp type.. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new IntImp containing the value d. + */ + IntImp( const int d ); + + /** + * Get hold of the contained data. + */ + int data() const { return mdata; } + /** + * Set the contained data to d. + */ + void setData( int d ) { mdata = d; } + + IntImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * This ObjectImp is a BogusImp containing only a string value. + */ +class StringImp + : public BogusImp +{ + QString mdata; +public: + /** + * Returns the ObjectImpType representing the StringImp type.. + */ + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + /** + * Construct a new StringImp containing the string d. + */ + StringImp( const QString& d ); + + /** + * Get hold of the contained data. + */ + const QString& data() const { return mdata; } + /** + * Set the contained data. + */ + void setData( const QString& s ) { mdata = s; } + + StringImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool canFillInNextEscape() const; + void fillInNextEscape( QString& s, const KigDocument& ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +class HierarchyImp + : public BogusImp +{ + ObjectHierarchy mdata; +public: + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + HierarchyImp( const ObjectHierarchy& h ); + + const ObjectHierarchy& data() const { return mdata; } + void setData( const ObjectHierarchy& h ) { mdata = h; } + + HierarchyImp* copy() const; + const char* baseName() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * \internal Don't mistake this imp for something that draws a + * transformed object. It does something completely different. It's + * a pure data Imp, like DoubleImp and friends that serves only to + * store the data of a transformation ( see the Transformation class + * in ../misc/kigtransform.h + */ +class TransformationImp + : public BogusImp +{ + Transformation mdata; +public: + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + TransformationImp( const Transformation& h ); + + const Transformation& data() const { return mdata; } + void setData( const Transformation& h ) { mdata = h; } + + TransformationImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +class TestResultImp + : public BogusImp +{ + const QString mdata; +public: + static const ObjectImpType* stype(); + typedef BogusImp Parent; + + TestResultImp( const QString& s ); + + TestResultImp* copy() const; + + const QString& data() const { return mdata; } + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +#endif diff --git a/kig/objects/centerofcurvature_type.cc b/kig/objects/centerofcurvature_type.cc new file mode 100644 index 00000000..8111410f --- /dev/null +++ b/kig/objects/centerofcurvature_type.cc @@ -0,0 +1,304 @@ +// Copyright (C) 2004 Maurizio Paolini <paolini@dmf.unicatt.it> + +// 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 "centerofcurvature_type.h" + +#include "bogus_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +//#include "other_imp.h" +#include "point_imp.h" +//#include "line_imp.h" + +#include "../misc/common.h" +#include "../misc/conic-common.h" +//#include "../misc/calcpaths.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +static const char constructcenterofcurvaturepoint[] = "SHOULDNOTBESEEN"; +// I18N_NOOP( "Construct the center of curvature corresponding to this point" ); +static const char selectcoc1[] = I18N_NOOP( "Select the curve..." ); +static const char selectcoc2[] = I18N_NOOP( "Select a point on the curve..." ); + +static const ArgsParser::spec argsspecCocConic[] = +{ + { ConicImp::stype(), "SHOULDNOTBESEEN", selectcoc1, false }, + { PointImp::stype(), constructcenterofcurvaturepoint, selectcoc2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CocConicType ) + +CocConicType::CocConicType() + : ArgsParserObjectType( "CocConic", argsspecCocConic, 2 ) +{ +} + +CocConicType::~CocConicType() +{ +} + +const CocConicType* CocConicType::instance() +{ + static const CocConicType t; + return &t; +} + +ObjectImp* CocConicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const ConicImp* conic = static_cast<const ConicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !conic->containsPoint( p, doc ) ) + return new InvalidImp; + + double x = p.x; + double y = p.y; + ConicCartesianData data = conic->cartesianData(); +// double aconst = data.coeffs[5]; + double ax = data.coeffs[3]; + double ay = data.coeffs[4]; + double axx = data.coeffs[0]; + double axy = data.coeffs[2]; + double ayy = data.coeffs[1]; + +/* + * mp: we need to compute the normal vector and the curvature + * of the curve. The curve (conic) is given in implicit form + * f(x,y) = 0; the gradient of f gives the direction of the + * normal; for the curvature we can use the following formula: + * k = div(grad f/|grad f|) + * + * the hessian matrix has elements [hfxx, hfxy] + * [hfxy, hfyy] + * + * kgf is the curvature multiplied by the norm of grad f + */ + + double gradfx = 2*axx*x + axy*y + ax; + double gradfy = axy*x + 2*ayy*y + ay; + Coordinate gradf = Coordinate ( gradfx, gradfy ); + + double hfxx = 2*axx; + double hfyy = 2*ayy; + double hfxy = axy; + + double kgf = hfxx + hfyy + - (hfxx*gradfx*gradfx + hfyy*gradfy*gradfy + 2*hfxy*gradfx*gradfy) + /(gradfx*gradfx + gradfy*gradfy); + + bool ok = true; + + const Coordinate coc = p - 1/kgf*gradf; + + if ( !ok ) + return new InvalidImp; + + return new PointImp( coc ); +} + +const ObjectImpType* CocConicType::resultId() const +{ + return PointImp::stype(); +} + +/**** Cubic starts here ****/ + +static const ArgsParser::spec argsspecCocCubic[] = +{ + { CubicImp::stype(), "SHOULDNOTBESEEN", selectcoc1, false }, + { PointImp::stype(), constructcenterofcurvaturepoint, selectcoc2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CocCubicType ) + +CocCubicType::CocCubicType() + : ArgsParserObjectType( "CocCubic", argsspecCocCubic, 2 ) +{ +} + +CocCubicType::~CocCubicType() +{ +} + +const CocCubicType* CocCubicType::instance() +{ + static const CocCubicType t; + return &t; +} + +ObjectImp* CocCubicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CubicImp* cubic = static_cast<const CubicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !cubic->containsPoint( p, doc ) ) + return new InvalidImp; + + double x = p.x; + double y = p.y; + CubicCartesianData data = cubic->data(); +// double aconst = data.coeffs[0]; + double ax = data.coeffs[1]; + double ay = data.coeffs[2]; + double axx = data.coeffs[3]; + double axy = data.coeffs[4]; + double ayy = data.coeffs[5]; + double axxx = data.coeffs[6]; + double axxy = data.coeffs[7]; + double axyy = data.coeffs[8]; + double ayyy = data.coeffs[9]; + + /* + * we use here the same mechanism as for the + * conics, see above + */ + + double gradfx = 3*axxx*x*x + 2*axxy*x*y + axyy*y*y + 2*axx*x + axy*y + ax; + double gradfy = axxy*x*x + 2*axyy*x*y + 3*ayyy*y*y + axy*x + 2*ayy*y + ay; + Coordinate gradf = Coordinate ( gradfx, gradfy ); + + double hfxx = 6*axxx*x + 2*axxy*y + 2*axx; + double hfyy = 6*ayyy*y + 2*axyy*x + 2*ayy; + double hfxy = 2*axxy*x + 2*axyy*y + axy; + + double kgf = hfxx + hfyy + - (hfxx*gradfx*gradfx + hfyy*gradfy*gradfy + 2*hfxy*gradfx*gradfy) + /(gradfx*gradfx + gradfy*gradfy); + + bool ok = true; + + const Coordinate coc = p - 1/kgf*gradf; + + if ( !ok ) + return new InvalidImp; + + return new PointImp( coc ); +} + +const ObjectImpType* CocCubicType::resultId() const +{ + return PointImp::stype(); +} + +/**** Curve starts here ****/ + +static const ArgsParser::spec argsspecCocCurve[] = +{ + { CurveImp::stype(), "SHOULDNOTBESEEN", selectcoc1, false }, + { PointImp::stype(), constructcenterofcurvaturepoint, selectcoc2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CocCurveType ) + +CocCurveType::CocCurveType() + : ArgsParserObjectType( "CocCurve", argsspecCocCurve, 2 ) +{ +} + +CocCurveType::~CocCurveType() +{ +} + +const CocCurveType* CocCurveType::instance() +{ + static const CocCurveType t; + return &t; +} + +ObjectImp* CocCurveType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CurveImp* curve = static_cast<const CurveImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !curve->containsPoint( p, doc ) ) + return new InvalidImp; + + + const double t = curve->getParam( p, doc ); + const double tau0 = 5e-4; + const double sigmasq = 1e-12; + const int maxiter = 20; + + double tau = tau0; + Coordinate gminus, g, gplus, tang, acc, curv, err; + double velsq, curvsq; + double tplus = t + tau; + double tminus = t - tau; + double t0 = t; + if ( tplus > 1 ) {tplus = 1; t0 = 1 - tau; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; t0 = tau; tplus = 2*tau;} + gminus = curve->getPoint( tminus, doc ); + g = curve->getPoint( t0, doc ); + gplus = curve->getPoint( tplus, doc ); + tang = (gplus - gminus)/(2*tau); + acc = (gminus + gplus - 2*g)/(tau*tau); + velsq = tang.x*tang.x + tang.y*tang.y; + tang = tang/velsq; + Coordinate curvold = acc/velsq - (acc.x*tang.x + acc.y*tang.y)*tang; + curvsq = curvold.x*curvold.x + curvold.y*curvold.y; + curvold = curvold/curvsq; + + for (int i = 0; i < maxiter; i++) + { + tau = tau/2; + tplus = t + tau; + tminus = t - tau; + t0 = t; + if ( tplus > 1 ) {tplus = 1; t0 = 1 - tau; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; t0 = tau; tplus = 2*tau;} + + gminus = curve->getPoint( tminus, doc ); + g = curve->getPoint( t0, doc ); + gplus = curve->getPoint( tplus, doc ); + tang = (gplus - gminus)/(2*tau); + acc = (gminus + gplus - 2*g)/(tau*tau); + velsq = tang.x*tang.x + tang.y*tang.y; + tang = tang/velsq; + curv = acc/velsq - (acc.x*tang.x + acc.y*tang.y)*tang; + curvsq = curv.x*curv.x + curv.y*curv.y; + curv = curv/curvsq; + + err = (curvold - curv)/3; + /* + * curvsq is the inverse squared of the norm of curvsq + * so this is actually a relative test + * in the end we return an extrapolated value + */ + if (err.squareLength() < sigmasq/curvsq) + { + curv = (4*curv - curvold)/3; + return new PointImp( p + curv ); + } + curvold = curv; + } + return new InvalidImp; +} + +const ObjectImpType* CocCurveType::resultId() const +{ + return PointImp::stype(); +} diff --git a/kig/objects/centerofcurvature_type.h b/kig/objects/centerofcurvature_type.h new file mode 100644 index 00000000..18dfc42a --- /dev/null +++ b/kig/objects/centerofcurvature_type.h @@ -0,0 +1,68 @@ +// Copyright (C) 2004 Maurizio Paolini <paolini@dmf.unicatt.it> + +// 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. + +#ifndef KIG_OBJECTS_CENTEROFCURVATURE_TYPE_H +#define KIG_OBJECTS_CENTEROFCURVATURE_TYPE_H + +#include "base_type.h" + +/** + * the center of curvature of a conic at a point + */ +class CocConicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + CocConicType(); + ~CocConicType(); +public: + static const CocConicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the center of curvature of a cubic at a point + */ +class CocCubicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + CocCubicType(); + ~CocCubicType(); +public: + static const CocCubicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the center of curvature of a curve at a point + */ +class CocCurveType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + CocCurveType(); + ~CocCurveType(); +public: + static const CocCurveType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/circle_imp.cc b/kig/objects/circle_imp.cc new file mode 100644 index 00000000..13ceef93 --- /dev/null +++ b/kig/objects/circle_imp.cc @@ -0,0 +1,356 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "circle_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" + +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate_system.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <math.h> + +CircleImp::CircleImp( const Coordinate& center, double radius ) + : mcenter( center ), mradius( radius ) +{ +} + +CircleImp::~CircleImp() +{ +} + +ObjectImp* CircleImp::transform( const Transformation& t ) const +{ + if ( t.isHomothetic() ) + { + Coordinate nc = t.apply( mcenter ); + double nr = t.apply( mradius ); + if ( nc.valid() ) + return new CircleImp( nc, nr ); + else return new InvalidImp; + } + else + { + // domi: i should return a ConicImp here, but i don't know how to + // calculate it.. + return Parent::transform( t ); + }; +} + +void CircleImp::draw( KigPainter& p ) const +{ + p.drawCircle( mcenter, mradius ); +} + +bool CircleImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return fabs((mcenter - p).length() - mradius) <= w.screenInfo().normalMiss( width ); +} + +bool CircleImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + // first we check if the rect contains at least one of the + // north/south/east/west points of the circle + if ( r.contains( mcenter + Coordinate( 0, -mradius ) ) ) return true; + if ( r.contains( mcenter + Coordinate( mradius, 0 ) ) ) return true; + if ( r.contains( mcenter + Coordinate( 0, mradius ) ) ) return true; + if ( r.contains( mcenter + Coordinate( -mradius, 0 ) ) ) return true; + + // we allow a miss of some pixels .. + double miss = w.screenInfo().normalMiss( width ); + double bigradius = mradius + miss; + bigradius *= bigradius; + double smallradius = mradius - miss; + smallradius *= smallradius; + + const int in = -1; + const int undecided = 0; + const int out = 1; + + int inorout = undecided; + + Coordinate coords[4]; + coords[0] = r.topLeft(); + coords[1] = r.topRight(); + coords[2] = r.bottomRight(); + coords[3] = r.bottomLeft(); + + // we check if the corners of the rect are either + for ( Coordinate* i = coords; i < coords + 4; ++i ) + { + double t = ( *i - mcenter ).squareLength(); + if ( t >= bigradius ) + { + if ( inorout == in ) return true; + inorout = out; + } + else if ( t <= smallradius ) + { + if ( inorout == out ) return true; + inorout = in; + } + } + return inorout == undecided; +} + +bool CircleImp::valid() const +{ + return true; +} + +const uint CircleImp::numberOfProperties() const +{ + // We _intentionally_ do not use the Conic properties.. + return CurveImp::numberOfProperties() + 7; +} + +const QCStringList CircleImp::propertiesInternalNames() const +{ + QCStringList l = CurveImp::propertiesInternalNames(); + l << "surface"; + l << "circumference"; + l << "radius"; + l << "center"; + l << "cartesian-equation"; + l << "simply-cartesian-equation"; + l << "polar-equation"; + assert( l.size() == CircleImp::numberOfProperties() ); + return l; +} + +const QCStringList CircleImp::properties() const +{ + QCStringList l = CurveImp::properties(); + l << I18N_NOOP( "Surface" ); + l << I18N_NOOP( "Circumference" ); + l << I18N_NOOP( "Radius" ); + l << I18N_NOOP( "Center" ); + l << I18N_NOOP( "Expanded Cartesian Equation" ); + l << I18N_NOOP( "Cartesian Equation" ); + l << I18N_NOOP( "Polar Equation" ); + assert( l.size() == CircleImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* CircleImp::impRequirementForProperty( uint which ) const +{ + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::impRequirementForProperty( which ); + else return CircleImp::stype(); +} + +const char* CircleImp::iconForProperty( uint which ) const +{ + assert( which < CircleImp::numberOfProperties() ); + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::iconForProperty( which ); + else if ( which == CurveImp::numberOfProperties() ) + return "areaCircle"; // surface + else if ( which == CurveImp::numberOfProperties() + 1 ) + return "circumference"; // circumference + else if ( which == CurveImp::numberOfProperties() + 2 ) + return ""; //radius + else if ( which == CurveImp::numberOfProperties() + 3 ) + return "baseCircle"; // circle center + else if ( which == CurveImp::numberOfProperties() + 4 ) + return "kig_text"; // cartesian equation + else if ( which == CurveImp::numberOfProperties() + 5 ) + return "kig_text"; // simply cartesian equation + else if ( which == CurveImp::numberOfProperties() + 6 ) + return "kig_text"; // polar equation + else assert( false ); + return ""; +} + +ObjectImp* CircleImp::property( uint which, const KigDocument& w ) const +{ + assert( which < CircleImp::numberOfProperties() ); + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::property( which, w ); + if ( which == CurveImp::numberOfProperties() ) + return new DoubleImp( surface() ); + else if ( which == CurveImp::numberOfProperties() + 1 ) + return new DoubleImp( circumference() ); + else if ( which == CurveImp::numberOfProperties() + 2 ) + return new DoubleImp( radius() ); + else if ( which == CurveImp::numberOfProperties() + 3 ) + return new PointImp( center() ); + else if ( which == CurveImp::numberOfProperties() + 4 ) + return new StringImp( cartesianEquationString( w ) ); + else if ( which == CurveImp::numberOfProperties() + 5 ) + return new StringImp( simplyCartesianEquationString( w ) ); + else if ( which == CurveImp::numberOfProperties() + 6 ) + return new StringImp( polarEquationString( w ) ); + else assert( false ); + return new InvalidImp; +} + +const Coordinate CircleImp::center() const +{ + return mcenter; +} + +double CircleImp::radius() const +{ + return mradius; +} + +double CircleImp::surface() const +{ + return M_PI * squareRadius(); +} + +double CircleImp::squareRadius() const +{ + return mradius * mradius; +} + +double CircleImp::circumference() const +{ + return 2 * M_PI * radius(); +} + +QString CircleImp::polarEquationString( const KigDocument& w ) const +{ + QString ret = i18n( "rho = %1 [centered at %2]" ); + ConicPolarData data = polarData(); + ret = ret.arg( data.pdimen, 0, 'g', 3 ); + ret = ret.arg( w.coordinateSystem().fromScreen( data.focus1, w ) ); + return ret; +} + +QString CircleImp::cartesianEquationString( const KigDocument& ) const +{ + QString ret = i18n( "x² + y² + %1 x + %2 y + %3 = 0" ); + ConicCartesianData data = cartesianData(); + ret = ret.arg( data.coeffs[3], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[4], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[5], 0, 'g', 3 ); + return ret; +} + +QString CircleImp::simplyCartesianEquationString( const KigDocument& ) const +{ + QString ret = i18n( "( x - %1 )² + ( y - %2 )² = %3" ); + ret = ret.arg( mcenter.x, 0, 'g', 3 ); + ret = ret.arg( mcenter.y, 0, 'g', 3 ); + ret = ret.arg( mradius * mradius, 0, 'g', 3 ); + return ret; +} + +Coordinate CircleImp::focus1() const +{ + return center(); +} + +Coordinate CircleImp::focus2() const +{ + return center(); +} + +int CircleImp::conicType() const +{ + return 1; +} + +const ConicCartesianData CircleImp::cartesianData() const +{ + Coordinate c = center(); + double sqr = squareRadius(); + ConicCartesianData data( + 1.0, 1.0, 0.0, -2*c.x, -2*c.y, + c.x*c.x + c.y*c.y - sqr ); + return data; +} + +const ConicPolarData CircleImp::polarData() const +{ + return ConicPolarData( center(), radius(), 0, 0 ); +} + +CircleImp* CircleImp::copy() const +{ + return new CircleImp( mcenter, mradius ); +} + +double CircleImp::getParam( const Coordinate& point, const KigDocument& ) const +{ + Coordinate tmp = point - mcenter; + double ret = atan2(tmp.y, tmp.x) / ( 2 * M_PI ); + if ( ret > 0 ) return ret; + else return ret + 1; +} + +const Coordinate CircleImp::getPoint( double p, const KigDocument& ) const +{ + return mcenter + Coordinate (cos(p * 2 * M_PI), sin(p * 2 * M_PI)) * mradius; +} + +void CircleImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool CircleImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( CircleImp::stype() ) && + static_cast<const CircleImp&>( rhs ).center() == center() && + static_cast<const CircleImp&>( rhs ).radius() == radius(); +} + +const ObjectImpType* CircleImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "circle", + I18N_NOOP( "circle" ), + I18N_NOOP( "Select this circle" ), + I18N_NOOP( "Select circle %1" ), + I18N_NOOP( "Remove a Circle" ), + I18N_NOOP( "Add a Circle" ), + I18N_NOOP( "Move a Circle" ), + I18N_NOOP( "Attach to this circle" ), + I18N_NOOP( "Show a Circle" ), + I18N_NOOP( "Hide a Circle" ) + ); + return &t; +} + +const ObjectImpType* CircleImp::type() const +{ + return CircleImp::stype(); +} + +bool CircleImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + assert( which < CircleImp::numberOfProperties() ); + if ( which < CurveImp::numberOfProperties() ) + return CurveImp::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +Rect CircleImp::surroundingRect() const +{ + Coordinate d( mradius, mradius ); + return Rect( mcenter - d, mcenter + d ); +} diff --git a/kig/objects/circle_imp.h b/kig/objects/circle_imp.h new file mode 100644 index 00000000..d38716a6 --- /dev/null +++ b/kig/objects/circle_imp.h @@ -0,0 +1,125 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CIRCLE_IMP_H +#define KIG_OBJECTS_CIRCLE_IMP_H + +#include "conic_imp.h" + +/** + * An ObjectImp representing a circle. This class is a subclass of + * ConicImp, ensuring that a circle can be used as a conic. + */ +class CircleImp + : public ConicImp +{ + Coordinate mcenter; + double mradius; +public: + typedef ConicImp Parent; + /** + * Returns the ObjectImpType representing the CircleImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a Circle with a given center and radius. + */ + CircleImp( const Coordinate& center, double radius ); + ~CircleImp(); + CircleImp* copy() const; + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + double getParam( const Coordinate& point, const KigDocument& ) const; + const Coordinate getPoint( double param, const KigDocument& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + /** + * Return the center of this circle. + */ + const Coordinate center() const; + /** + * Return the radius of this circle. + */ + double radius() const; + /** + * Return the square radius of this circle. Use this in preference + * to sqr( radius() ). + */ + double squareRadius() const; + /** + * Return the surface of this circle. + */ + double surface() const; + /** + * Return the circumference of this circle. + */ + double circumference() const; + + // trivial versions of the conic information functions.. + /** + * Always returns 1, since a circle always is an ellipse. + */ + int conicType() const; + const ConicCartesianData cartesianData() const; + const ConicPolarData polarData() const; + /** + * The first focus of a circle is simply its center. + */ + Coordinate focus1() const; + /** + * The second focus of a circle is simply its center. + */ + Coordinate focus2() const; + + /** + * Return a string containing the cartesian equation of this circle. + * This will be of the form "x^2 + y^2 + a x + b y + c = 0" + */ + QString cartesianEquationString( const KigDocument& w ) const; + /** + * Return a string containing the cartesian equation of this circle. + * This will be of the form "( x - x0 )^2 + ( y - y0 )^2 = r^2" + */ + QString simplyCartesianEquationString( const KigDocument& w ) const; + /** + * Return a string containing the polar equation of this circle. + * This will be of the form "rho = r [centered at p]" + */ + QString polarEquationString( const KigDocument& w ) const; + + bool equals( const ObjectImp& rhs ) const; +}; + +#endif diff --git a/kig/objects/circle_type.cc b/kig/objects/circle_type.cc new file mode 100644 index 00000000..97d2f615 --- /dev/null +++ b/kig/objects/circle_type.cc @@ -0,0 +1,181 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "circle_type.h" + +#include "circle_imp.h" +#include "bogus_imp.h" +#include "line_imp.h" +#include "point_imp.h" + +#include "../misc/common.h" + +#include <klocale.h> + +static const char constructcirclethroughpointstat[] = I18N_NOOP( "Construct a circle through this point" ); + +static const char constructcirclewithcenterstat[] = I18N_NOOP( "Construct a circle with this center" ); + +static const ArgsParser::spec argsspecCircleBCP[] = +{ + { PointImp::stype(), constructcirclewithcenterstat, + I18N_NOOP( "Select the center of the new circle..." ), false }, + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBCPType ) + +CircleBCPType::CircleBCPType() + : ObjectABType( "CircleBCP", argsspecCircleBCP, 2 ) +{ +} + +CircleBCPType::~CircleBCPType() +{ +} + +const CircleBCPType* CircleBCPType::instance() +{ + static const CircleBCPType s; + return &s; +} + +ObjectImp* CircleBCPType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new CircleImp( a, ( b - a ).length() ); +} + +const CircleBTPType* CircleBTPType::instance() +{ + static const CircleBTPType t; + return &t; +} + +static const ArgsParser::spec argsspecCircleBTP[] = +{ + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true }, + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true }, + { PointImp::stype(), constructcirclethroughpointstat, + I18N_NOOP( "Select a point for the new circle to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBTPType ) + +CircleBTPType::CircleBTPType() + : ArgsParserObjectType( "CircleBTP", argsspecCircleBTP, 3 ) +{ +} + +CircleBTPType::~CircleBTPType() +{ +} + +ObjectImp* CircleBTPType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args, 2 ) ) return new InvalidImp; + + const Coordinate a = static_cast<const PointImp*>( args[0] )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( args[1] )->coordinate(); + Coordinate c; + if ( args.size() == 3 ) + c = static_cast<const PointImp*>( args[2] )->coordinate(); + else + { + // we pick the third point so that the three points form a + // triangle with equal sides... + + // midpoint: + Coordinate m = ( b + a ) / 2; + if ( b.y != a.y ) + { + // direction of the perpend: + double d = -(b.x-a.x)/(b.y-a.y); + + // length: + // sqrt( 3 ) == tan( 60° ) == sqrt( 2^2 - 1^2 ) + double l = 1.73205080756 * (a-b).length() / 2; + + double d2 = d*d; + double l2 = l*l; + double dx = sqrt( l2 / ( d2 + 1 ) ); + double dy = sqrt( l2 * d2 / ( d2 + 1 ) ); + if( d < 0 ) dy = -dy; + + c.x = m.x + dx; + c.y = m.y + dy; + } + else + { + c.x = m.x; + c.y = m.y + ( a.x - b.x ); + }; + }; + + const Coordinate center = calcCenter( a, b, c ); + if ( center.valid() ) + return new CircleImp( center, (center - a ).length() ); + else return new InvalidImp; +} + +const ObjectImpType* CircleBCPType::resultId() const +{ + return CircleImp::stype(); +} + +const ObjectImpType* CircleBTPType::resultId() const +{ + return CircleImp::stype(); +} + +static const ArgsParser::spec argsspecCircleBPR[] = +{ + { PointImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", false }, + { DoubleImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBPRType ) + +CircleBPRType::CircleBPRType() + : ArgsParserObjectType( "CircleBPR", argsspecCircleBPR, 2 ) +{ +} + +CircleBPRType::~CircleBPRType() +{ +} + +const CircleBPRType* CircleBPRType::instance() +{ + static const CircleBPRType t; + return &t; +} + +ObjectImp* CircleBPRType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + const Coordinate c = static_cast<const PointImp*>( args[0] )->coordinate(); + double r = static_cast<const DoubleImp*>( args[1] )->data(); + return new CircleImp( c, r ); +} + +const ObjectImpType* CircleBPRType::resultId() const +{ + return CircleImp::stype(); +} diff --git a/kig/objects/circle_type.h b/kig/objects/circle_type.h new file mode 100644 index 00000000..874d0b69 --- /dev/null +++ b/kig/objects/circle_type.h @@ -0,0 +1,69 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CIRCLE_TYPE_H +#define KIG_OBJECTS_CIRCLE_TYPE_H + +#include "base_type.h" + +/** + * Circle by center and point + */ +class CircleBCPType + : public ObjectABType +{ + CircleBCPType(); + ~CircleBCPType(); +public: + static const CircleBCPType* instance(); + + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +/** + * Circle by point and radius. + */ +class CircleBPRType + : public ArgsParserObjectType +{ + CircleBPRType(); + ~CircleBPRType(); +public: + static const CircleBPRType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * Circle by three points + */ +class CircleBTPType + : public ArgsParserObjectType +{ + CircleBTPType(); + ~CircleBTPType(); + +public: + static const CircleBTPType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/common.cc b/kig/objects/common.cc new file mode 100644 index 00000000..9932734c --- /dev/null +++ b/kig/objects/common.cc @@ -0,0 +1,43 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "common.h" +#include "object_holder.h" + +std::vector<ObjectCalcer*> getAllCalcers( const std::vector<ObjectHolder*>& os ) +{ + std::set<ObjectCalcer*> ret; + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + { + if ( ( *i )->nameCalcer() ) + ret.insert( ( *i )->nameCalcer() ); + ret.insert( ( *i )->calcer() ); + } + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +std::vector<ObjectCalcer*> getCalcers( const std::vector<ObjectHolder*>& os ) +{ + std::vector<ObjectCalcer*> ret; + ret.reserve( os.size() ); + for ( std::vector<ObjectHolder*>::const_iterator i = os.begin(); + i != os.end(); ++i ) + ret.push_back( ( *i )->calcer() ); + return ret; +} + diff --git a/kig/objects/common.h b/kig/objects/common.h new file mode 100644 index 00000000..a26a92cd --- /dev/null +++ b/kig/objects/common.h @@ -0,0 +1,107 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_COMMON_H +#define KIG_OBJECTS_COMMON_H + +#include <set> +#include <vector> +#include <qcstring.h> +#include <qvaluelist.h> +#include <qstringlist.h> +#include <cassert> +#include <klocale.h> + +class Coordinate; +class KigDocument; +class KigPainter; +class KigPart; +class KigWidget; +class NormalMode; +class ObjectCalcer; +class ObjectDrawer; +class ObjectHolder; +class ObjectImp; +class ObjectImpType; +class ObjectPropertyCalcer; +class ObjectType; +class ObjectTypeCalcer; +class QDomDocument; +class QDomElement; +class Rect; +class ScreenInfo; +class Transformation; + +typedef std::vector<const ObjectImp*> Args; +typedef QValueList<QCString> QCStringList; + +template<typename T> +void delete_all( T begin, T end ) +{ + for( ; begin != end; ++begin ) + { + delete *begin; + } +} + +/** + * get the calcers that the holders represent and their namecalcers + */ +std::vector<ObjectCalcer*> getAllCalcers( const std::vector<ObjectHolder*>& os ); + +/** + * get the calcers that the holders represent ( not their namecalcers ) + */ +std::vector<ObjectCalcer*> getCalcers( const std::vector<ObjectHolder*>& os ); + +/** + * The below is a trick. ObjectType's implement the singleton + * pattern. There can be only one of them at each time. This one + * instance of them needs to be constructed at some time, within the + * following restrictions: + * + * 1. They need to be constructed in the right order: if one ObjectType + * uses another in its constructor, the other needs to be constructed + * first. To achieve this, we use a scheme with ::instance() + * functions, that have a static variable in the body of the function + * ( if we would define them static outside of the function body, C++ + * would provide no guarantee on the order they would be called in ). + * + * 2. They need to be constructed before Kig tries to use them. They + * need to be added to the ObjectTypeFactory before anyone tries to + * use that class to fetch a type. The below is a trick to achieve + * that in combination with the previous. Basically, we add a fake + * static instance of an empty class that receives the instance of the + * ObjectType as an argument to its constructor. C++ then guarantees + * that these things will be constructed before main() is entered. + * + * Thus, for your own ObjectType classes: use the scheme with the + * static ::instance methods, and add + * \code + * KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MyObjectType) + * \endcode + * to the .cpp file of your class. + */ +class FakeClass { +public: + FakeClass( const ObjectType* ) { + } +}; + +#define KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( type ) static class FakeClass _fake_class_instance_##type( type::instance() ); + +#endif diff --git a/kig/objects/conic_imp.cc b/kig/objects/conic_imp.cc new file mode 100644 index 00000000..a65d8511 --- /dev/null +++ b/kig/objects/conic_imp.cc @@ -0,0 +1,385 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "conic_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" + +#include "../misc/kigpainter.h" +#include "../misc/common.h" +#include "../misc/coordinate_system.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +ObjectImp* ConicImp::transform( const Transformation& t ) const +{ + bool valid = true; + ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid ); + if ( ! valid ) return new InvalidImp; + else return new ConicImpCart( d ); +} + +void ConicImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool ConicImp::valid() const +{ + return true; +} + +bool ConicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const +{ + return internalContainsPoint( o, w.screenInfo().normalMiss( width ) ); +} + +bool ConicImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO + return false; +} + +const uint ConicImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 5; +} + +const QCStringList ConicImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "type"; + l << "first-focus"; + l << "second-focus"; + l << "cartesian-equation"; + l << "polar-equation"; + assert( l.size() == ConicImp::numberOfProperties() ); + return l; +} + +const QCStringList ConicImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Conic Type" ); + l << I18N_NOOP( "First Focus" ); + l << I18N_NOOP( "Second Focus" ); + l << I18N_NOOP( "Cartesian Equation" ); + l << I18N_NOOP( "Polar Equation" ); + assert( l.size() == ConicImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* ConicImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return ConicImp::stype(); +} + +const char* ConicImp::iconForProperty( uint which ) const +{ + int pnum = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // conic type string + else if ( which == Parent::numberOfProperties() + pnum++ ) + return ""; // focus1 + else if ( which == Parent::numberOfProperties() + pnum++ ) + return ""; // focus2 + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // cartesian equation string + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // polar equation string + else assert( false ); + return ""; +} + +ObjectImp* ConicImp::property( uint which, const KigDocument& w ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( conicTypeString() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( focus1() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( focus2() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( cartesianEquationString( w ) ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( polarEquationString( w ) ); + else assert( false ); + return new InvalidImp; +} + +double ConicImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + const ConicPolarData d = polarData(); + Coordinate tmp = p - d.focus1; + double l = tmp.length(); + double theta = atan2(tmp.y, tmp.x); + double costheta = cos(theta); + double sintheta = sin(theta); + double ecosthetamtheta0 = costheta*d.ecostheta0 + sintheta*d.esintheta0; + double esinthetamtheta0 = sintheta*d.ecostheta0 - costheta*d.esintheta0; + double oneplus = 1.0 + d.ecostheta0*d.ecostheta0 + d.esintheta0*d.esintheta0; + double fact = esinthetamtheta0*(1.0 - ecosthetamtheta0)/(oneplus - 2*ecosthetamtheta0); +// fact is sin(a)*cos(a) where a is the angle between the ray from the first +// focus and the normal to the conic. We need it in order to adjust the +// angle according to the projection onto the conic of our point + double rho1 = d.pdimen / (1 - ecosthetamtheta0); + double rho2 = - d.pdimen / (1 + ecosthetamtheta0); + if (fabs(rho1 - l) < fabs(rho2 - l)) + { + theta += (rho1 - l)*fact/rho1; + return fmod(theta / ( 2 * M_PI ) + 1, 1); + } else { + theta += (rho2 - l)*fact/rho2; + return fmod(theta / ( 2 * M_PI ) + 0.5, 1); + } +} + +const Coordinate ConicImp::getPoint( double p, const KigDocument& ) const +{ + const ConicPolarData d = polarData(); + + double costheta = cos(p * 2 * M_PI); + double sintheta = sin(p * 2 * M_PI); + double rho = d.pdimen / (1 - costheta* d.ecostheta0 - sintheta* d.esintheta0); + return d.focus1 + Coordinate (costheta, sintheta) * rho; +} + +int ConicImp::conicType() const +{ + const ConicPolarData d = polarData(); + double ec = d.ecostheta0; + double es = d.esintheta0; + double esquare = ec*ec + es*es; + const double parabolamiss = 1e-3; // don't know what a good value could be + + if (esquare < 1.0 - parabolamiss) return 1; + if (esquare > 1.0 + parabolamiss) return -1; + + return 0; +} + +QString ConicImp::conicTypeString() const +{ + switch (conicType()) + { + case 1: + return i18n("Ellipse"); + case -1: + return i18n("Hyperbola"); + case 0: + return i18n("Parabola"); + default: + assert( false ); + return ""; + } +} + +QString ConicImp::cartesianEquationString( const KigDocument& ) const +{ + QString ret = i18n( "%1 x² + %2 y² + %3 xy + %4 x + %5 y + %6 = 0" ); + ConicCartesianData data = cartesianData(); + ret = ret.arg( data.coeffs[0], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[1], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[2], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[3], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[4], 0, 'g', 3 ); + ret = ret.arg( data.coeffs[5], 0, 'g', 3 ); + return ret; +} + +QString ConicImp::polarEquationString( const KigDocument& w ) const +{ + QString ret = i18n( "rho = %1/(1 + %2 cos theta + %3 sin theta)\n [centered at %4]" ); + const ConicPolarData data = polarData(); + + ret = ret.arg( data.pdimen, 0, 'g', 3 ); + ret = ret.arg( -data.ecostheta0, 0, 'g', 3 ); + ret = ret.arg( -data.esintheta0, 0, 'g', 3 ); + + ret = ret.arg( w.coordinateSystem().fromScreen( data.focus1, w ) ); + return ret; +} + +const ConicCartesianData ConicImp::cartesianData() const +{ + return ConicCartesianData( polarData() ); +} + +Coordinate ConicImp::focus1() const +{ + return polarData().focus1; +} + +Coordinate ConicImp::focus2() const +{ + const ConicPolarData d = polarData(); + double ec = d.ecostheta0; + double es = d.esintheta0; + + double fact = 2*d.pdimen/(1 - ec*ec - es*es); + + return d.focus1 + fact*Coordinate(ec, es); +} + +const ConicPolarData ConicImpCart::polarData() const +{ + return mpolardata; +} + +const ConicCartesianData ConicImpCart::cartesianData() const +{ + return mcartdata; +} + +ConicImpCart::ConicImpCart( const ConicCartesianData& data ) + : ConicImp(), mcartdata( data ), mpolardata( data ) +{ + assert( data.valid() ); +} + +ConicImpPolar::ConicImpPolar( const ConicPolarData& data ) + : ConicImp(), mdata( data ) +{ +} + +ConicImpPolar::~ConicImpPolar() +{ +} + +const ConicPolarData ConicImpPolar::polarData() const +{ + return mdata; +} + +ConicImpCart* ConicImpCart::copy() const +{ + return new ConicImpCart( mcartdata ); +} + +ConicImpPolar* ConicImpPolar::copy() const +{ + return new ConicImpPolar( mdata ); +} + +ConicImp::ConicImp() +{ +} + +ConicImp::~ConicImp() +{ +} + +ConicImpCart::~ConicImpCart() +{ +} + +void ConicImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool ConicImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( ConicImp::stype() ) && + static_cast<const ConicImp&>( rhs ).polarData() == polarData(); +} + +const ObjectImpType* ConicImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "conic", + I18N_NOOP( "conic" ), + I18N_NOOP( "Select this conic" ), + I18N_NOOP( "Select conic %1" ), + I18N_NOOP( "Remove a Conic" ), + I18N_NOOP( "Add a Conic" ), + I18N_NOOP( "Move a Conic" ), + I18N_NOOP( "Attach to this conic" ), + I18N_NOOP( "Show a Conic" ), + I18N_NOOP( "Hide a Conic" ) + ); + return &t; +} + +const ObjectImpType* ConicImp::type() const +{ + return ConicImp::stype(); +} + +bool ConicImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + const ConicPolarData d = polarData(); + +// the threshold is relative to the size of the conic (mp) + return internalContainsPoint( p, test_threshold*d.pdimen ); +} + +bool ConicImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + const ConicPolarData d = polarData(); + + Coordinate focus1 = d.focus1; + double ecostheta0 = d.ecostheta0; + double esintheta0 = d.esintheta0; + double pdimen = d.pdimen; + + Coordinate pos = p - focus1; + double len = pos.length(); + double costheta = pos.x / len; + double sintheta = pos.y / len; + + double ecosthetamtheta0 = costheta*ecostheta0 + sintheta*esintheta0; + double rho = pdimen / (1.0 - ecosthetamtheta0); + + double oneplus = 1.0 + ecostheta0*ecostheta0 + esintheta0*esintheta0; + +// fact is the cosine of the angle between the ray from the first focus +// and the normal to the conic, so that we compute the real distance + + double fact = (1.0 - ecosthetamtheta0)/sqrt(oneplus - 2*ecosthetamtheta0); + if ( fabs((len - rho)*fact) <= threshold ) return true; + rho = - pdimen / ( 1.0 + ecosthetamtheta0 ); + fact = (1.0 + ecosthetamtheta0)/sqrt(oneplus + 2*ecosthetamtheta0); + return fabs(( len - rho )*fact) <= threshold; +} + +bool ConicImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +Rect ConicImp::surroundingRect() const +{ + // it's prolly possible to calculate this ( in the case that the + // conic is limited in size ), but for now we don't. + + return Rect::invalidRect(); +} diff --git a/kig/objects/conic_imp.h b/kig/objects/conic_imp.h new file mode 100644 index 00000000..55ba65ca --- /dev/null +++ b/kig/objects/conic_imp.h @@ -0,0 +1,157 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CONIC_IMP_H +#define KIG_OBJECTS_CONIC_IMP_H + +#include "curve_imp.h" + +#include "../misc/conic-common.h" + +/** + * An ObjectImp representing a conic. + * + * A conic is a general second degree curve, and some beautiful theory + * has been developed about it.. See a math book for more + * information. This class is in fact an abstract base class hiding + * the fact that a ConicImp can be constructed in two ways. If only + * its Cartesian equation is known, then you should use ConicImpCart, + * otherwise, you should use ConicImpPolar. If the other + * representation is needed, it will be calculated, but a cartesian + * representation is rarely needed, and not calculating saves some CPU + * cycles. + */ +class ConicImp + : public CurveImp +{ +protected: + ConicImp(); + ~ConicImp(); +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the ConicImp type. + */ + static const ObjectImpType* stype(); + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + const char* iconForProperty( uint which ) const; + ObjectImp* property( uint which, const KigDocument& w ) const; + + double getParam( const Coordinate& point, const KigDocument& ) const; + const Coordinate getPoint( double param, const KigDocument& ) const; + + // information about ourselves.. These are all virtual, because a + // trivial subclass like CircleImp can override these with trivial + // versions.. + + /** + * Type of conic. + * Return what type of conic this is: + * -1 for a hyperbola + * 0 for a parabola + * 1 for an ellipse + */ + virtual int conicType() const; + /** + * A string containing "Hyperbola", "Parabola" or "Ellipse". + */ + virtual QString conicTypeString() const; + /** + * A string containing the cartesian equation of the conic. This + * will be of the form "a x^2 + b y^2 + c xy + d x + e y + f = 0". + */ + virtual QString cartesianEquationString( const KigDocument& w ) const; + /** + * A string containing the polar equation of the conic. This will + * be of the form "rho = pdimen/(1 + ect cos( theta ) + est sin( + * theta ) )\n [centered at p]" + */ + virtual QString polarEquationString( const KigDocument& w ) const; + /** + * Return the cartesian representation of this conic. + */ + virtual const ConicCartesianData cartesianData() const; + /** + * Return the polar representation of this conic. + */ + virtual const ConicPolarData polarData() const = 0; + /** + * Return the first focus of this conic. + */ + virtual Coordinate focus1() const; + /** + * Return the second focus of this conic. + */ + virtual Coordinate focus2() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An implementation of ConicImp to be used when only the cartesian + * equation of the conic is known. + */ +class ConicImpCart + : public ConicImp +{ + ConicCartesianData mcartdata; + ConicPolarData mpolardata; +public: + ConicImpCart( const ConicCartesianData& data ); + ~ConicImpCart(); + ConicImpCart* copy() const; + + const ConicCartesianData cartesianData() const; + const ConicPolarData polarData() const; +}; + +/** + * An implementation of ConicImp to be used when only the cartesian + * equation of the conic is known. + */ +class ConicImpPolar + : public ConicImp +{ + ConicPolarData mdata; +public: + ConicImpPolar( const ConicPolarData& data ); + ~ConicImpPolar(); + ConicImpPolar* copy() const; + + const ConicPolarData polarData() const; +}; + +#endif diff --git a/kig/objects/conic_types.cc b/kig/objects/conic_types.cc new file mode 100644 index 00000000..6e32f844 --- /dev/null +++ b/kig/objects/conic_types.cc @@ -0,0 +1,689 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "conic_types.h" + +#include "bogus_imp.h" +#include "conic_imp.h" +#include "point_imp.h" +#include "circle_imp.h" +#include "line_imp.h" +#include "object_calcer.h" +#include "../misc/conic-common.h" +#include "../misc/common.h" +#include "../kig/kig_commands.h" +#include "../kig/kig_part.h" + +#include <klocale.h> + +static const char conic_constructstatement[] = I18N_NOOP( "Construct a conic through this point" ); + +static const struct ArgsParser::spec argsspecConicB5P[] = +{ + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ), true }, + { PointImp::stype(), conic_constructstatement, + I18N_NOOP( "Select a point for the new conic to go through..." ),true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicB5PType ) + +ConicB5PType::ConicB5PType() + : ArgsParserObjectType( "ConicB5P", argsspecConicB5P, 5 ) +{ +} + +ConicB5PType::~ConicB5PType() +{ +} + +ObjectImp* ConicB5PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 1 ) ) return new InvalidImp; + std::vector<Coordinate> points; + + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + ConicCartesianData d = + calcConicThroughPoints( points, zerotilt, parabolaifzt, ysymmetry ); + if ( d.valid() ) + return new ConicImpCart( d ); + else return new InvalidImp; +} + +const ConicB5PType* ConicB5PType::instance() +{ + static const ConicB5PType t; + return &t; +} + +static const ArgsParser::spec argsspecConicBAAP[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a conic with this asymptote" ), + I18N_NOOP( "Select the first asymptote of the new conic..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Construct a conic with this asymptote" ), + I18N_NOOP( "Select the second asymptote of the new conic..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a conic through this point" ), + I18N_NOOP( "Select a point for the new conic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicBAAPType ) + +ConicBAAPType::ConicBAAPType() + : ArgsParserObjectType( "ConicBAAP", argsspecConicBAAP, 3 ) +{ +} + +ConicBAAPType::~ConicBAAPType() +{ +} + +const ConicBAAPType* ConicBAAPType::instance() +{ + static const ConicBAAPType t; + return &t; +} + +ObjectImp* ConicBAAPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) + return new InvalidImp; + const LineData la = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const LineData lb = static_cast<const AbstractLineImp*>( parents[1] )->data(); + const Coordinate c = static_cast<const PointImp*>( parents[2] )->coordinate(); + + return new ConicImpCart( calcConicByAsymptotes( la, lb, c ) ); +} + +ObjectImp* ConicBFFPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + std::vector<Coordinate> cs; + + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + cs.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + return new ConicImpPolar( calcConicBFFP( cs, type() ) ); +} + +ConicBFFPType::ConicBFFPType( const char* fullname, const ArgsParser::spec* spec, int n ) + : ArgsParserObjectType( fullname, spec, n ) +{ +} + +ConicBFFPType::~ConicBFFPType() +{ +} + +static const char constructellipsewithfocusstat[] = + I18N_NOOP( "Construct an ellipse with this focus" ); + +static const ArgsParser::spec argsspecEllipseBFFP[] = +{ + { PointImp::stype(), constructellipsewithfocusstat, + I18N_NOOP( "Select the first focus of the new ellipse..." ), false }, + { PointImp::stype(), constructellipsewithfocusstat, + I18N_NOOP( "Select the second focus of the new ellipse..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct an ellipse through this point" ), + I18N_NOOP( "Select a point for the new ellipse to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( EllipseBFFPType ) + +EllipseBFFPType::EllipseBFFPType() + : ConicBFFPType( "EllipseBFFP", argsspecEllipseBFFP, 3 ) +{ +} + +EllipseBFFPType::~EllipseBFFPType() +{ +} + +int EllipseBFFPType::type() const +{ + return 1; +} + +const EllipseBFFPType* EllipseBFFPType::instance() +{ + static const EllipseBFFPType t; + return &t; +} + +static const char constructhyperbolawithfocusstat[] = + I18N_NOOP( "Construct a hyperbola with this focus" ); + +static const ArgsParser::spec argsspecHyperbolaBFFP[] = +{ + { PointImp::stype(), constructhyperbolawithfocusstat, + I18N_NOOP( "Select the first focus of the new hyperbola..." ), false }, + { PointImp::stype(), constructhyperbolawithfocusstat, + I18N_NOOP( "Select the second focus of the new hyperbola..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a hyperbola through this point" ), + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HyperbolaBFFPType ) + +HyperbolaBFFPType::HyperbolaBFFPType() + : ConicBFFPType( "HyperbolaBFFP", argsspecHyperbolaBFFP, 3 ) +{ +} + +HyperbolaBFFPType::~HyperbolaBFFPType() +{ +} + +const HyperbolaBFFPType* HyperbolaBFFPType::instance() +{ + static const HyperbolaBFFPType t; + return &t; +} + +int HyperbolaBFFPType::type() const +{ + return -1; +} + +const ConicBDFPType* ConicBDFPType::instance() +{ + static const ConicBDFPType t; + return &t; +} + +static const struct ArgsParser::spec argsspecConicBDFP[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a conic with this line as directrix" ), + I18N_NOOP( "Select the directrix of the new conic..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a conic with this point as focus" ), + I18N_NOOP( "Select the focus of the new conic..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a conic through this point" ), + I18N_NOOP( "Select a point for the new conic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicBDFPType ) + +ConicBDFPType::ConicBDFPType() + : ArgsParserObjectType( "ConicBDFP", argsspecConicBDFP, 3 ) +{ +} + +ConicBDFPType::~ConicBDFPType() +{ +} + +ObjectImp* ConicBDFPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + const LineData line = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const Coordinate focus = + static_cast<const PointImp*>( parents[1] )->coordinate(); + + Coordinate point; + if ( parents.size() == 3 ) + point = static_cast<const PointImp*>( parents[2] )->coordinate(); + else + { + /* !!!! costruisci point come punto medio dell'altezza tra fuoco e d. */ + Coordinate ba = line.dir(); + Coordinate fa = focus - line.b; + double balsq = ba.x*ba.x + ba.y*ba.y; + double scal = (fa.x*ba.x + fa.y*ba.y)/balsq; + point = 0.5*(line.a + focus + scal*ba); + }; + return new ConicImpPolar( calcConicBDFP( line, focus, point ) ); +} + +static const char constructparabolathroughpointstat[] = + I18N_NOOP( "Construct a parabola through this point" ); + +static const ArgsParser::spec argsspecParabolaBTP[] = +{ + { PointImp::stype(), constructparabolathroughpointstat, + I18N_NOOP( "Select a point for the new parabola to go through..." ), true }, + { PointImp::stype(), constructparabolathroughpointstat, + I18N_NOOP( "Select a point for the new parabola to go through..." ), true }, + { PointImp::stype(), constructparabolathroughpointstat, + I18N_NOOP( "Select a point for the new parabola to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ParabolaBTPType ) + +ParabolaBTPType::ParabolaBTPType() + : ArgsParserObjectType( "ParabolaBTP", argsspecParabolaBTP, 3 ) +{ +} + +ParabolaBTPType::~ParabolaBTPType() +{ +} + +const ParabolaBTPType* ParabolaBTPType::instance() +{ + static const ParabolaBTPType t; + return &t; +} + +ObjectImp* ParabolaBTPType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + ConicCartesianData d = + calcConicThroughPoints( points, zerotilt, parabolaifzt, ysymmetry ); + if ( d.valid() ) + return new ConicImpCart( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicPolarPoint[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct a polar point wrt. this conic" ), + I18N_NOOP( "Select the conic wrt. which you want to construct a polar point..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Construct the polar point of this line" ), + I18N_NOOP( "Select the line of which you want to construct the polar point..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicPolarPointType ) + +ConicPolarPointType::ConicPolarPointType() + : ArgsParserObjectType( "ConicPolarPoint", argsspecConicPolarPoint, 2 ) +{ +} + +ConicPolarPointType::~ConicPolarPointType() +{ +} + +const ConicPolarPointType* ConicPolarPointType::instance() +{ + static const ConicPolarPointType t; + return &t; +} + +ObjectImp* ConicPolarPointType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const ConicCartesianData c = static_cast<const ConicImp*>( parents[0] )->cartesianData(); + const LineData l = static_cast<const AbstractLineImp*>( parents[1] )->data(); + const Coordinate p = calcConicPolarPoint( c, l ); + if ( p.valid() ) return new PointImp( p ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicPolarLine[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct a polar line wrt. this conic" ), + I18N_NOOP( "Select the conic wrt. which you want to construct a polar point..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the polar line of this point" ), + I18N_NOOP( "Select the line of which you want to construct the polar point..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicPolarLineType ) + +ConicPolarLineType::ConicPolarLineType() + : ArgsParserObjectType( "ConicPolarLine", argsspecConicPolarLine, 2 ) +{ +} + +ConicPolarLineType::~ConicPolarLineType() +{ +} + +const ConicPolarLineType* ConicPolarLineType::instance() +{ + static const ConicPolarLineType t; + return &t; +} + +ObjectImp* ConicPolarLineType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const ConicCartesianData c = static_cast<const ConicImp*>( parents[0] )->cartesianData(); + const Coordinate p = static_cast<const PointImp*>( parents[1] )->coordinate(); + bool valid = true; + const LineData l = calcConicPolarLine( c, p, valid ); + if ( valid ) return new LineImp( l ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicDirectrix[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct the directrix of this conic" ), + I18N_NOOP( "Select the conic of which you want to construct the directrix..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicDirectrixType ) + +ConicDirectrixType::ConicDirectrixType() + : ArgsParserObjectType( "ConicDirectrix", argsspecConicDirectrix, 1 ) +{ +} + +ConicDirectrixType::~ConicDirectrixType() +{ +} + +const ConicDirectrixType* ConicDirectrixType::instance() +{ + static const ConicDirectrixType t; + return &t; +} + +ObjectImp* ConicDirectrixType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const ConicPolarData data = + static_cast<const ConicImp*>( parents[0] )->polarData(); + + double ec = data.ecostheta0; + double es = data.esintheta0; + double eccsq = ec*ec + es*es; + + Coordinate a = data.focus1 - data.pdimen/eccsq*Coordinate(ec,es); + Coordinate b = a + Coordinate(-es,ec); + return new LineImp( a, b ); +} + +static const char hyperbolatpstatement[] = I18N_NOOP( "Construct a hyperbola through this point" ); + +static const ArgsParser::spec argsspecHyperbolaB4P[] = +{ + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true }, + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true }, + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true }, + { PointImp::stype(), hyperbolatpstatement, + I18N_NOOP( "Select a point for the new hyperbola to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( EquilateralHyperbolaB4PType ) + +EquilateralHyperbolaB4PType::EquilateralHyperbolaB4PType() + : ArgsParserObjectType( "EquilateralHyperbolaB4P", argsspecHyperbolaB4P, 4 ) +{ +} + +EquilateralHyperbolaB4PType::~EquilateralHyperbolaB4PType() +{ +} + +const EquilateralHyperbolaB4PType* EquilateralHyperbolaB4PType::instance() +{ + static const EquilateralHyperbolaB4PType t; + return &t; +} + +ObjectImp* EquilateralHyperbolaB4PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 1 ) ) return new InvalidImp; + + std::vector<Coordinate> pts; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + pts.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + ConicCartesianData d = calcConicThroughPoints( pts, equilateral ); + if ( d.valid() ) + return new ConicImpCart( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecParabolaBDP[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a parabola with this directrix" ), + I18N_NOOP( "Select the directrix of the new parabola..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a parabola with this focus" ), + I18N_NOOP( "Select the focus of the new parabola..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ParabolaBDPType ) + +ParabolaBDPType::ParabolaBDPType() + : ObjectLPType( "ParabolaBDP", argsspecParabolaBDP, 2 ) +{ +} + +ParabolaBDPType::~ParabolaBDPType() +{ +} + +const ParabolaBDPType* ParabolaBDPType::instance() +{ + static const ParabolaBDPType t; + return &t; +} + +ObjectImp* ParabolaBDPType::calc( const LineData& l, const Coordinate& c ) const +{ + ConicPolarData ret; + Coordinate ldir = l.dir(); + ldir = ldir.normalize(); + ret.focus1 = c; + ret.ecostheta0 = - ldir.y; + ret.esintheta0 = ldir.x; + Coordinate fa = c - l.a; + ret.pdimen = fa.y*ldir.x - fa.x*ldir.y; + ConicImpPolar* r = new ConicImpPolar( ret ); + kdDebug() << k_funcinfo << r->conicTypeString() << endl; + return r; +} + +static const ArgsParser::spec argsspecConicAsymptote[] = +{ + { ConicImp::stype(), I18N_NOOP( "Construct the asymptotes of this conic" ), + I18N_NOOP( "Select the conic of which you want to construct the asymptotes..." ), false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicAsymptoteType ) + +ConicAsymptoteType::ConicAsymptoteType() + : ArgsParserObjectType( "ConicAsymptote", argsspecConicAsymptote, 2 ) +{ +} + +ConicAsymptoteType::~ConicAsymptoteType() +{ +} + +const ConicAsymptoteType* ConicAsymptoteType::instance() +{ + static const ConicAsymptoteType t; + return &t; +} + +ObjectImp* ConicAsymptoteType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + bool valid = true; + const LineData ret = calcConicAsymptote( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + static_cast<const IntImp*>( parents[1] )->data(), + valid ); + + if ( valid ) + return new LineImp( ret ); + else + return new InvalidImp; +} + +static const char radicallinesstatement[] = I18N_NOOP( "Construct the radical lines of this conic" ); + +static const ArgsParser::spec argsspecConicRadical[] = +{ + { ConicImp::stype(), radicallinesstatement, + I18N_NOOP( "Select the first of the two conics of which you want to construct the radical line..." ), false }, + { ConicImp::stype(), radicallinesstatement, + I18N_NOOP( "Select the other of the two conic of which you want to construct the radical line..." ), false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicRadicalType ) + +ConicRadicalType::ConicRadicalType() + : ArgsParserObjectType( "ConicRadical", argsspecConicRadical, 4 ) +{ +} + +const ConicRadicalType* ConicRadicalType::instance() +{ + static const ConicRadicalType t; + return &t; +} + +ObjectImp* ConicRadicalType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + if ( parents[0]->inherits( CircleImp::stype() ) && + parents[1]->inherits( CircleImp::stype() ) ) + { + if( static_cast<const IntImp*>( parents[2] )->data() != 1 ) + return new InvalidImp; + else + { + const CircleImp* c1 = static_cast<const CircleImp*>( parents[0] ); + const CircleImp* c2 = static_cast<const CircleImp*>( parents[1] ); + const Coordinate a = calcCircleRadicalStartPoint( + c1->center(), c2->center(), c1->squareRadius(), c2->squareRadius() + ); + return new LineImp( a, calcPointOnPerpend( + LineData( c1->center(), c2->center() ), a ) ); + }; + } + else + { + bool valid = true; + const LineData l = calcConicRadical( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + static_cast<const ConicImp*>( parents[1] )->cartesianData(), + static_cast<const IntImp*>( parents[2] )->data(), + static_cast<const IntImp*>( parents[3] )->data(), valid ); + if ( valid ) + return new LineImp( l ); + else + return new InvalidImp; + }; +} + +ConicRadicalType::~ConicRadicalType() +{ +} + +const ObjectImpType* ConicB5PType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicBAAPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicBFFPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicBDFPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ParabolaBTPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* EquilateralHyperbolaB4PType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicPolarPointType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* ConicPolarLineType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* ConicDirectrixType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* ParabolaBDPType::resultId() const +{ + return ConicImp::stype(); +} + +const ObjectImpType* ConicAsymptoteType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* ConicRadicalType::resultId() const +{ + return LineImp::stype(); +} + +QStringList ConicRadicalType::specialActions() const +{ + QStringList ret; + ret << i18n( "Switch Radical Lines" ); + return ret; +} + +void ConicRadicalType::executeAction( int i, ObjectHolder&, ObjectTypeCalcer& t, + KigPart& d, KigWidget&, NormalMode& ) const +{ + assert( i == 0 ); + std::vector<ObjectCalcer*> parents = t.parents(); + assert( dynamic_cast<ObjectConstCalcer*>( parents[3] ) ); + ObjectConstCalcer* zeroindexo = static_cast<ObjectConstCalcer*>( parents[3] ); + MonitorDataObjects mon( zeroindexo ); + assert( zeroindexo->imp()->inherits( IntImp::stype() ) ); + int oldzeroindex = static_cast<const IntImp*>( zeroindexo->imp() )->data(); + int newzeroindex = oldzeroindex % 3 + 1; + zeroindexo->setImp( new IntImp( newzeroindex ) ); + KigCommand* kc = new KigCommand( d, "Switch Conic Radical Lines" ); + mon.finish( kc ); + d.history()->addCommand( kc ); +} + diff --git a/kig/objects/conic_types.h b/kig/objects/conic_types.h new file mode 100644 index 00000000..e2a1881d --- /dev/null +++ b/kig/objects/conic_types.h @@ -0,0 +1,184 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CONIC_TYPES_H +#define KIG_OBJECTS_CONIC_TYPES_H + +#include "base_type.h" + +class ConicB5PType + : public ArgsParserObjectType +{ + ConicB5PType(); + ~ConicB5PType(); +public: + static const ConicB5PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicBAAPType + : public ArgsParserObjectType +{ + ConicBAAPType(); + ~ConicBAAPType(); +public: + static const ConicBAAPType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicBFFPType + : public ArgsParserObjectType +{ +protected: + ConicBFFPType( const char* fullname, const ArgsParser::spec*, int n ); + ~ConicBFFPType(); +public: + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + + /** + * -1 for hyperbola, 1 for ellipse.. + */ + virtual int type() const = 0; + const ObjectImpType* resultId() const; +}; + +class EllipseBFFPType + : public ConicBFFPType +{ + EllipseBFFPType(); + ~EllipseBFFPType(); +public: + static const EllipseBFFPType* instance(); + int type() const; +}; + +class HyperbolaBFFPType + : public ConicBFFPType +{ + HyperbolaBFFPType(); + ~HyperbolaBFFPType(); +public: + static const HyperbolaBFFPType* instance(); + int type() const; +}; + +class ConicBDFPType + : public ArgsParserObjectType +{ + ConicBDFPType(); + ~ConicBDFPType(); +public: + static const ConicBDFPType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ParabolaBTPType + : public ArgsParserObjectType +{ + ParabolaBTPType(); + ~ParabolaBTPType(); +public: + static const ParabolaBTPType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class EquilateralHyperbolaB4PType + : public ArgsParserObjectType +{ + EquilateralHyperbolaB4PType(); + ~EquilateralHyperbolaB4PType(); +public: + static const EquilateralHyperbolaB4PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicPolarPointType + : public ArgsParserObjectType +{ + ConicPolarPointType(); + ~ConicPolarPointType(); +public: + static const ConicPolarPointType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicPolarLineType + : public ArgsParserObjectType +{ + ConicPolarLineType(); + ~ConicPolarLineType(); +public: + static const ConicPolarLineType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicDirectrixType + : public ArgsParserObjectType +{ + ConicDirectrixType(); + ~ConicDirectrixType(); +public: + static const ConicDirectrixType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ParabolaBDPType + : public ObjectLPType +{ + ParabolaBDPType(); + ~ParabolaBDPType(); +public: + static const ParabolaBDPType* instance(); + ObjectImp* calc( const LineData& l, const Coordinate& c ) const; + const ObjectImpType* resultId() const; +}; + +class ConicAsymptoteType + : public ArgsParserObjectType +{ + ConicAsymptoteType(); + ~ConicAsymptoteType(); +public: + static const ConicAsymptoteType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConicRadicalType + : public ArgsParserObjectType +{ + ConicRadicalType(); + ~ConicRadicalType(); +public: + static const ConicRadicalType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +#endif + diff --git a/kig/objects/cubic_imp.cc b/kig/objects/cubic_imp.cc new file mode 100644 index 00000000..51bb5d9f --- /dev/null +++ b/kig/objects/cubic_imp.cc @@ -0,0 +1,437 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "cubic_imp.h" + +#include "bogus_imp.h" + +#include "../misc/kigpainter.h" +#include "../misc/screeninfo.h" +#include "../misc/kignumerics.h" +#include "../misc/common.h" +#include "../kig/kig_view.h" + +#include <math.h> +#include <klocale.h> + +CubicImp::CubicImp( const CubicCartesianData& data ) + : CurveImp(), mdata( data ) +{ +} + +CubicImp::~CubicImp() +{ +} + +ObjectImp* CubicImp::transform( const Transformation& t ) const +{ + bool valid = true; + CubicCartesianData d = calcCubicTransformation( data(), t, valid ); + if ( valid ) return new CubicImp( d ); + else return new InvalidImp; +} + +void CubicImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool CubicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const +{ + return internalContainsPoint( o, w.screenInfo().normalMiss( width ) ); +} + +bool CubicImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO ? + return false; +} + +CubicImp* CubicImp::copy() const +{ + return new CubicImp( mdata ); +} + +double CubicImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + double x = p.x; + double y = p.y; + double t; + + double a000 = mdata.coeffs[0]; + double a001 = mdata.coeffs[1]; + double a002 = mdata.coeffs[2]; + double a011 = mdata.coeffs[3]; + double a012 = mdata.coeffs[4]; + double a022 = mdata.coeffs[5]; + double a111 = mdata.coeffs[6]; + double a112 = mdata.coeffs[7]; + double a122 = mdata.coeffs[8]; + double a222 = mdata.coeffs[9]; + + /* + * first project p onto the cubic. This is done by computing the + * line through p in the direction of the gradient + */ + + double f = a000 + a001*x + a002*y + a011*x*x + a012*x*y + a022*y*y + + a111*x*x*x + a112*x*x*y + a122*x*y*y + a222*y*y*y; + if ( f != 0 ) + { + double fx = a001 + 2*a011*x + a012*y + 3*a111*x*x + 2*a112*x*y + a122*y*y; + double fy = a002 + 2*a022*y + a012*x + 3*a222*y*y + 2*a122*x*y + a112*x*x; + Coordinate v = Coordinate (fx, fy); + if ( f < 0 ) v = -v; // the line points away from the intersection + double a, b, c, d; + calcCubicLineRestriction ( mdata, p, v, a, b, c, d ); + if ( a < 0 ) + { + a *= -1; + b *= -1; + c *= -1; + d *= -1; + } + + // computing the coefficients of the Sturm sequence + double p1a = 2*b*b - 6*a*c; + double p1b = b*c - 9*a*d; + double p0a = c*p1a*p1a + p1b*(3*a*p1b - 2*b*p1a); + // compute the number of roots for negative lambda + int variations = calcCubicVariations ( 0, a, b, c, d, p1a, p1b, p0a ); + bool valid; + int numroots; + double lambda = calcCubicRoot ( -1e10, 1e10, a, b, c, d, variations, valid, + numroots ); + if ( valid ) + { + Coordinate pnew = p + lambda*v; + x = pnew.x; + y = pnew.y; + } + } + + if (x > 0) t = x/(1+x); + else t = x/(1-x); + t = 0.5*(t + 1); + t /= 3; + + Coordinate p1 = getPoint ( t ); + Coordinate p2 = getPoint ( t + 1.0/3.0 ); + Coordinate p3 = getPoint ( t + 2.0/3.0 ); + + double mint = t; + double mindist = p1.valid() ? fabs ( y - p1.y ) : double_inf; + if ( p2.valid() && fabs ( y - p2.y ) < mindist ) + { + mint = t + 1.0/3.0; + mindist = fabs ( y - p2.y ); + } + if ( p3.valid() && fabs ( y - p3.y ) < mindist ) + { + mint = t + 2.0/3.0; + } + + return mint; +} + +const Coordinate CubicImp::getPoint( double p, const KigDocument& ) const +{ + return getPoint( p ); +} + +const Coordinate CubicImp::getPoint( double p ) const +{ + /* + * this isn't really elegant... + * the magnitude of p tells which one of the maximum 3 intersections + * of the vertical line with the cubic to take. + */ + + p *= 3; + int root = (int) p; + assert ( root >= 0 ); + assert ( root <= 3 ); + if ( root == 3 ) root = 2; + + p -= root; + + assert ( 0 <= p && p <= 1 ); + if ( p <= 0. ) p = 1e-6; + if ( p >= 1. ) p = 1 - 1e-6; + root++; + p = 2*p - 1; + double x; + if (p > 0) x = p/(1 - p); + else x = p/(1 + p); + + // calc the third degree polynomial: + // compute the third degree polinomial: +// double a000 = mdata.coeffs[0]; +// double a001 = mdata.coeffs[1]; +// double a002 = mdata.coeffs[2]; +// double a011 = mdata.coeffs[3]; +// double a012 = mdata.coeffs[4]; +// double a022 = mdata.coeffs[5]; +// double a111 = mdata.coeffs[6]; +// double a112 = mdata.coeffs[7]; +// double a122 = mdata.coeffs[8]; +// double a222 = mdata.coeffs[9]; +// +// // first the y^3 coefficient, it coming only from a222: +// double a = a222; +// // next the y^2 coefficient (from a122 and a022): +// double b = a122*x + a022; +// // next the y coefficient (from a112, a012 and a002): +// double c = a112*x*x + a012*x + a002; +// // finally the constant coefficient (from a111, a011, a001 and a000): +// double d = a111*x*x*x + a011*x*x + a001*x + a000; + +// commented out, since the bound is already computed when passing a huge +// interval; the normalization is not needed also + + // renormalize: positive a +// if ( a < 0 ) +// { +// a *= -1; +// b *= -1; +// c *= -1; +// d *= -1; +// } +// +// const double small = 1e-7; +// int degree = 3; +// if ( fabs(a) < small*fabs(b) || +// fabs(a) < small*fabs(c) || +// fabs(a) < small*fabs(d) ) +// { +// degree = 2; +// if ( fabs(b) < small*fabs(c) || +// fabs(b) < small*fabs(d) ) +// { +// degree = 1; +// } +// } + +// and a bound for all the real roots: + +// double bound; +// switch (degree) +// { +// case 3: +// bound = fabs(d/a); +// if ( fabs(c/a) + 1 > bound ) bound = fabs(c/a) + 1; +// if ( fabs(b/a) + 1 > bound ) bound = fabs(b/a) + 1; +// break; +// +// case 2: +// bound = fabs(d/b); +// if ( fabs(c/b) + 1 > bound ) bound = fabs(c/b) + 1; +// break; +// +// case 1: +// default: +// bound = fabs(d/c) + 1; +// break; +// } + + int numroots; + bool valid = true; + double y = calcCubicYvalue ( x, -double_inf, double_inf, root, mdata, valid, + numroots ); + if ( ! valid ) return Coordinate::invalidCoord(); + else return Coordinate(x,y); +// if ( valid ) return Coordinate(x,y); +// root--; if ( root <= 0) root += 3; +// y = calcCubicYvalue ( x, -bound, bound, root, mdata, valid, +// numroots ); +// if ( valid ) return Coordinate(x,y); +// root--; if ( root <= 0) root += 3; +// y = calcCubicYvalue ( x, -bound, bound, root, mdata, valid, +// numroots ); +// assert ( valid ); +// return Coordinate(x,y); +} + +const uint CubicImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 1; +} + +const QCStringList CubicImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "cartesian-equation"; + assert( l.size() == CubicImp::numberOfProperties() ); + return l; + +} + +/* + * cartesian equation property contributed by Carlo Sardi + */ + +const QCStringList CubicImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Cartesian Equation" ); + assert( l.size() == CubicImp::numberOfProperties() ); + return l; + +} + +const ObjectImpType* CubicImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return CubicImp::stype(); +} + +const char* CubicImp::iconForProperty( uint which ) const +{ + int pnum = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return "kig_text"; // cartesian equation string + else + assert( false ); + return ""; +} + +ObjectImp* CubicImp::property( uint which, const KigDocument& w ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() + pnum++ ) + return new StringImp( cartesianEquationString( w ) ); + else + assert( false ); + return new InvalidImp; +} + +const CubicCartesianData CubicImp::data() const +{ + return mdata; +} + +void CubicImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool CubicImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( CubicImp::stype() ) && + static_cast<const CubicImp&>( rhs ).data() == data(); +} + +const ObjectImpType* CubicImp::type() const +{ + return CubicImp::stype(); +} + +const ObjectImpType* CubicImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "cubic", + I18N_NOOP( "cubic curve" ), + I18N_NOOP( "Select this cubic curve" ), + I18N_NOOP( "Select cubic curve %1" ), + I18N_NOOP( "Remove a Cubic Curve" ), + I18N_NOOP( "Add a Cubic Curve" ), + I18N_NOOP( "Move a Cubic Curve" ), + I18N_NOOP( "Attach to this cubic curve" ), + I18N_NOOP( "Show a Cubic Curve" ), + I18N_NOOP( "Hide a Cubic Curve" ) + ); + return &t; +} + +bool CubicImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool CubicImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + double a000 = mdata.coeffs[0]; + double a001 = mdata.coeffs[1]; + double a002 = mdata.coeffs[2]; + double a011 = mdata.coeffs[3]; + double a012 = mdata.coeffs[4]; + double a022 = mdata.coeffs[5]; + double a111 = mdata.coeffs[6]; + double a112 = mdata.coeffs[7]; + double a122 = mdata.coeffs[8]; + double a222 = mdata.coeffs[9]; + + double x = p.x; + double y = p.y; + + double f = a000 + a001*x + a002*y + a011*x*x + a012*x*y + a022*y*y + + a111*x*x*x + a112*x*x*y + a122*x*y*y + a222*y*y*y; + double fx = a001 + 2*a011*x + a012*y + 3*a111*x*x + 2*a112*x*y + a122*y*y; + double fy = a002 + a012*x + 2*a022*y + a112*x*x + 2*a122*x*y + 3*a222*y*y; + + double dist = fabs(f)/(fabs(fx) + fabs(fy)); + + return dist <= threshold; +} + +bool CubicImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect CubicImp::surroundingRect() const +{ + // it's probably possible to calculate this if it exists, but for + // now we don't. + return Rect::invalidRect(); +} + +QString CubicImp::cartesianEquationString( const KigDocument& ) const +{ + /* + * unfortunately QStrings.arg method is limited to %1, %9, so we cannot + * treat all 10 arguments! Let's split the equation in two parts... + * Now this ends up also in the translation machinery, is this really + * necessary? Otherwise we could do a little bit of tidy up on the + * the equation (removal of zeros, avoid " ... + -1234 x ", etc.) + */ + + QString ret = i18n( "%6 x³ + %9 y³ + %7 x²y + %8 xy² + %5 y² + %3 x² + %4 xy + %1 x + %2 y" ); + ret = ret.arg( mdata.coeffs[1], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[2], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[3], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[4], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[5], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[6], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[7], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[8], 0, 'g', 3 ); + ret = ret.arg( mdata.coeffs[9], 0, 'g', 3 ); + + ret.append( i18n( " + %1 = 0" ) ); + ret = ret.arg( mdata.coeffs[0], 0, 'g', 3 ); + + // we should find a common place to do this... + ret.replace( "+ -", "- " ); + ret.replace( "+-", "-" ); + return ret; +} diff --git a/kig/objects/cubic_imp.h b/kig/objects/cubic_imp.h new file mode 100644 index 00000000..bb7d89c7 --- /dev/null +++ b/kig/objects/cubic_imp.h @@ -0,0 +1,81 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CUBIC_IMP_H +#define KIG_OBJECTS_CUBIC_IMP_H + +#include "curve_imp.h" + +#include "../misc/cubic-common.h" + +class LineData; + +/** + * An ObjectImp representing a cubic. + */ +class CubicImp + : public CurveImp +{ + const CubicCartesianData mdata; +public: + typedef CurveImp Parent; + static const ObjectImpType* stype(); + + CubicImp( const CubicCartesianData& data ); + ~CubicImp(); + + ObjectImp* transform( const Transformation& ) const; + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + Rect surroundingRect() const; + QString cartesianEquationString( const KigDocument& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + CubicImp* copy() const; + + double getParam( const Coordinate& point, const KigDocument& ) const; + + // The getPoint function doesn't need the KigDocument argument, the + // first getPoint function is identical to the other one. It is + // only provided for implementing the CurveImp interface. + const Coordinate getPoint( double param, const KigDocument& ) const; + const Coordinate getPoint( double param ) const; + +public: + /** + * Return the cartesian representation of this cubic. + */ + const CubicCartesianData data() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +#endif diff --git a/kig/objects/cubic_type.cc b/kig/objects/cubic_type.cc new file mode 100644 index 00000000..c322b8c3 --- /dev/null +++ b/kig/objects/cubic_type.cc @@ -0,0 +1,185 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "cubic_type.h" + +#include "cubic_imp.h" +#include "point_imp.h" +#include "bogus_imp.h" + +#include <klocale.h> + +static const char cubictpstatement[] = I18N_NOOP( "Construct a cubic curve through this point" ); + +static const struct ArgsParser::spec argsspecCubicB9P[] = +{ + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CubicB9PType ) + +CubicB9PType::CubicB9PType() + : ArgsParserObjectType( "CubicB9P", argsspecCubicB9P, 9 ) +{ +} + +CubicB9PType::~CubicB9PType() +{ +} + +const CubicB9PType* CubicB9PType::instance() +{ + static const CubicB9PType t; + return &t; +} + +ObjectImp* CubicB9PType::calc( const Args& os, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( os, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( uint i = 0; i < os.size(); ++i ) + points.push_back( static_cast<const PointImp*>( os[i] )->coordinate() ); + + CubicCartesianData d = calcCubicThroughPoints( points ); + if ( d.valid() ) + return new CubicImp( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecCubicNodeB6P[] = +{ + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CubicNodeB6PType ) + +CubicNodeB6PType::CubicNodeB6PType() + : ArgsParserObjectType( "CubicNodeB6P", argsspecCubicNodeB6P, 6 ) +{ +} + +CubicNodeB6PType::~CubicNodeB6PType() +{ +} + +const CubicNodeB6PType* CubicNodeB6PType::instance() +{ + static const CubicNodeB6PType t; + return &t; +} + +ObjectImp* CubicNodeB6PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + CubicCartesianData d = calcCubicNodeThroughPoints( points ); + if ( d.valid() ) + return new CubicImp( d ); + else + return new InvalidImp; +} + +static const ArgsParser::spec argsspecCubicCuspB4P[] = +{ + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true }, + { PointImp::stype(), cubictpstatement, + I18N_NOOP( "Select a point for the new cubic to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CubicCuspB4PType ) + +CubicCuspB4PType::CubicCuspB4PType() + : ArgsParserObjectType( "CubicCuspB4P", argsspecCubicCuspB4P, 4 ) +{ +} + +CubicCuspB4PType::~CubicCuspB4PType() +{ +} + +const CubicCuspB4PType* CubicCuspB4PType::instance() +{ + static const CubicCuspB4PType t; + return &t; +} + +ObjectImp* CubicCuspB4PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 2 ) ) return new InvalidImp; + + std::vector<Coordinate> points; + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + points.push_back( static_cast<const PointImp*>( *i )->coordinate() ); + + CubicCartesianData d = calcCubicCuspThroughPoints( points ); + if ( d.valid() ) return new CubicImp( d ); + else return new InvalidImp; +} + +const ObjectImpType* CubicB9PType::resultId() const +{ + return CubicImp::stype(); +} + +const ObjectImpType* CubicNodeB6PType::resultId() const +{ + return CubicImp::stype(); +} + +const ObjectImpType* CubicCuspB4PType::resultId() const +{ + return CubicImp::stype(); +} diff --git a/kig/objects/cubic_type.h b/kig/objects/cubic_type.h new file mode 100644 index 00000000..39be7387 --- /dev/null +++ b/kig/objects/cubic_type.h @@ -0,0 +1,56 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CUBIC_TYPE_H +#define KIG_OBJECTS_CUBIC_TYPE_H + +#include "object_type.h" + +class CubicB9PType + : public ArgsParserObjectType +{ + CubicB9PType(); + ~CubicB9PType(); +public: + static const CubicB9PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class CubicNodeB6PType + : public ArgsParserObjectType +{ + CubicNodeB6PType(); + ~CubicNodeB6PType(); +public: + static const CubicNodeB6PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class CubicCuspB4PType + : public ArgsParserObjectType +{ + CubicCuspB4PType(); + ~CubicCuspB4PType(); +public: + static const CubicCuspB4PType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/curve_imp.cc b/kig/objects/curve_imp.cc new file mode 100644 index 00000000..315cd8cb --- /dev/null +++ b/kig/objects/curve_imp.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "curve_imp.h" +#include "../misc/coordinate.h" + +const ObjectImpType* CurveImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "curve", + I18N_NOOP( "curve" ), + I18N_NOOP( "Select this curve" ), + I18N_NOOP( "Select curve %1" ), + I18N_NOOP( "Remove a Curve" ), + I18N_NOOP( "Add a Curve" ), + I18N_NOOP( "Move a Curve" ), + I18N_NOOP( "Attach to this curve" ), + I18N_NOOP( "Show a Curve" ), + I18N_NOOP( "Hide a Curve" ) + ); + return &t; +} + +Coordinate CurveImp::attachPoint() const +{ + return Coordinate::invalidCoord(); +} diff --git a/kig/objects/curve_imp.h b/kig/objects/curve_imp.h new file mode 100644 index 00000000..3a33a722 --- /dev/null +++ b/kig/objects/curve_imp.h @@ -0,0 +1,62 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_CURVE_IMP_H +#define KIG_OBJECTS_CURVE_IMP_H + +#include "object_imp.h" + +/** + * This class represents a curve: something which is composed of + * points, like a line, a circle, a locus... + */ +class CurveImp + : public ObjectImp +{ +public: + typedef ObjectImp Parent; + + /** + * Returns the ObjectImpType representing the CurveImp type. + */ + static const ObjectImpType* stype(); + + Coordinate attachPoint() const; + + // param is between 0 and 1. Note that 0 and 1 should be the + // end-points. E.g. for a Line, getPoint(0) returns a more or less + // infinite point. getPoint(0.5) should return the point in the + // middle. + virtual double getParam( const Coordinate& point, const KigDocument& ) const = 0; + // this should be the inverse function of getPoint(). + // Note that it should also do something reasonable when p is not on + // the curve. You can return an invalid Coordinate( + // Coordinate::invalidCoord() ) if you need to in some cases. + virtual const Coordinate getPoint( double param, const KigDocument& ) const = 0; + + virtual CurveImp* copy() const = 0; + + /** + * Return whether this Curve contains the given point. This is + * implemented as a numerical approximation. Implementations + * can/should use the value test_threshold in common.h as a + * threshold value. + */ + virtual bool containsPoint( const Coordinate& p, const KigDocument& ) const = 0; +}; + +#endif diff --git a/kig/objects/intersection_types.cc b/kig/objects/intersection_types.cc new file mode 100644 index 00000000..804d498d --- /dev/null +++ b/kig/objects/intersection_types.cc @@ -0,0 +1,338 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "intersection_types.h" + +#include "bogus_imp.h" +#include "circle_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +#include "line_imp.h" +#include "other_imp.h" +#include "point_imp.h" + +#include <klocale.h> + +static const char intersectlinestat[] = I18N_NOOP( "Intersect with this line" ); + +static const ArgsParser::spec argsspecConicLineIntersection[] = +{ + { ConicImp::stype(), I18N_NOOP( "Intersect with this conic" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicLineIntersectionType ) + +ConicLineIntersectionType::ConicLineIntersectionType() + : ArgsParserObjectType( "ConicLineIntersection", + argsspecConicLineIntersection, 3 ) +{ +} + +ConicLineIntersectionType::~ConicLineIntersectionType() +{ +} + +const ConicLineIntersectionType* ConicLineIntersectionType::instance() +{ + static const ConicLineIntersectionType t; + return &t; +} + +ObjectImp* ConicLineIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int side = static_cast<const IntImp*>( parents[2] )->data(); + assert( side == 1 || side == -1 ); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + Coordinate ret; + if ( parents[0]->inherits( CircleImp::stype() ) ) + { + // easy case.. + const CircleImp* c = static_cast<const CircleImp*>( parents[0] ); + ret = calcCircleLineIntersect( + c->center(), c->squareRadius(), line, side ); + } + else + { + // harder case.. + ret = calcConicLineIntersect( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + line, 0.0, side ); + } + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecConicLineOtherIntersection[] = +{ + { ConicImp::stype(), I18N_NOOP( "Intersect with this conic" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { PointImp::stype(), I18N_NOOP( "Already computed intersection point"), + "SHOULD NOT BE SEEN", true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConicLineOtherIntersectionType ) + +ConicLineOtherIntersectionType::ConicLineOtherIntersectionType() + : ArgsParserObjectType( "ConicLineOtherIntersection", + argsspecConicLineOtherIntersection, 3 ) +{ +} + +ConicLineOtherIntersectionType::~ConicLineOtherIntersectionType() +{ +} + +const ConicLineOtherIntersectionType* ConicLineOtherIntersectionType::instance() +{ + static const ConicLineOtherIntersectionType t; + return &t; +} + +ObjectImp* ConicLineOtherIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + Coordinate p = static_cast<const PointImp*>( parents[2] )->coordinate(); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + Coordinate ret; +// if ( parents[0]->inherits( CircleImp::stype() ) ) +// { +// // easy case.. +// const CircleImp* c = static_cast<const CircleImp*>( parents[0] ); +// ret = calcCircleLineIntersect( +// c->center(), c->squareRadius(), line, side, valid ); +// } +// else +// { + // harder case.. + double pax = p.x - line.a.x; + double pay = p.y - line.a.y; + double bax = line.b.x - line.a.x; + double bay = line.b.y - line.a.y; + double knownparam = (pax*bax + pay*bay)/(bax*bax + bay*bay); + ret = calcConicLineIntersect( + static_cast<const ConicImp*>( parents[0] )->cartesianData(), + line, knownparam, 0 ); +// } + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecLineLineIntersection[] = +{ + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineLineIntersectionType ) + +LineLineIntersectionType::LineLineIntersectionType() + : ArgsParserObjectType( "LineLineIntersection", + argsspecLineLineIntersection, 2 ) +{ +} + +LineLineIntersectionType::~LineLineIntersectionType() +{ +} + +const LineLineIntersectionType* LineLineIntersectionType::instance() +{ + static const LineLineIntersectionType t; + return &t; +} + +ObjectImp* LineLineIntersectionType::calc( const Args& parents, const KigDocument& d ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + Coordinate p = + calcIntersectionPoint( + static_cast<const AbstractLineImp*>( parents[0] )->data(), + static_cast<const AbstractLineImp*>( parents[1] )->data() ); + if ( static_cast<const AbstractLineImp*>( parents[0] )->containsPoint( p, d ) && + static_cast<const AbstractLineImp*>( parents[1] )->containsPoint( p, d ) ) + return new PointImp( p ); + else return new InvalidImp(); +} + +static const ArgsParser::spec argsspecLineCubicIntersection[] = +{ + { CubicImp::stype(), I18N_NOOP( "Intersect with this cubic curve" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineCubicIntersectionType ) + +LineCubicIntersectionType::LineCubicIntersectionType() + : ArgsParserObjectType( "LineCubicIntersection", + argsspecLineCubicIntersection, 3 ) +{ +} + +LineCubicIntersectionType::~LineCubicIntersectionType() +{ +} + +const LineCubicIntersectionType* LineCubicIntersectionType::instance() +{ + static const LineCubicIntersectionType t; + return &t; +} + +ObjectImp* LineCubicIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int which = static_cast<const IntImp*>( parents[2] )->data(); + bool valid = true; + const Coordinate c = calcCubicLineIntersect( + static_cast<const CubicImp*>( parents[0] )->data(), + static_cast<const AbstractLineImp*>( parents[1] )->data(), + which, valid ); + if ( valid ) return new PointImp( c ); + else return new InvalidImp; +} + +const ObjectImpType* ConicLineIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* ConicLineOtherIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* LineLineIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* LineCubicIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +static const ArgsParser::spec argsspecCircleCircleIntersection[] = +{ + { CircleImp::stype(), I18N_NOOP( "Intersect with this circle" ), + "SHOULD NOT BE SEEN", true }, + { CircleImp::stype(), I18N_NOOP( "Intersect with this circle" ), + "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleCircleIntersectionType ) + +CircleCircleIntersectionType::CircleCircleIntersectionType() + : ArgsParserObjectType( "CircleCircleIntersection", + argsspecCircleCircleIntersection, 3 ) +{ +} + +CircleCircleIntersectionType::~CircleCircleIntersectionType() +{ +} + +const CircleCircleIntersectionType* CircleCircleIntersectionType::instance() +{ + static const CircleCircleIntersectionType t; + return &t; +} + +ObjectImp* CircleCircleIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int side = static_cast<const IntImp*>( parents[2] )->data(); + assert( side == 1 || side == -1 ); + const CircleImp* c1 = static_cast<const CircleImp*>( parents[0] ); + const CircleImp* c2 = static_cast<const CircleImp*>( parents[1] ); + const Coordinate o1 = c1->center(); + const Coordinate o2 = c2->center(); + const double r1sq = c1->squareRadius(); + const Coordinate a = calcCircleRadicalStartPoint( + o1, o2, r1sq, c2->squareRadius() + ); + const LineData line = LineData (a, Coordinate ( a.x -o2.y + o1.y, a.y + o2.x - o1.x )); + Coordinate ret = calcCircleLineIntersect( o1, r1sq, line, side ); + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +const ObjectImpType* CircleCircleIntersectionType::resultId() const +{ + return PointImp::stype(); +} + +static const ArgsParser::spec argsspecArcLineIntersection[] = +{ + { ArcImp::stype(), I18N_NOOP( "Intersect with this arc" ), + "SHOULD NOT BE SEEN", true }, + { AbstractLineImp::stype(), intersectlinestat, "SHOULD NOT BE SEEN", true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcLineIntersectionType ) + +ArcLineIntersectionType::ArcLineIntersectionType() + : ArgsParserObjectType( "ArcLineIntersection", + argsspecArcLineIntersection, 3 ) +{ +} + +ArcLineIntersectionType::~ArcLineIntersectionType() +{ +} + +const ArcLineIntersectionType* ArcLineIntersectionType::instance() +{ + static const ArcLineIntersectionType t; + return &t; +} + +ObjectImp* ArcLineIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + int side = static_cast<const IntImp*>( parents[2] )->data(); + assert( side == 1 || side == -1 ); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + const ArcImp* c = static_cast<const ArcImp*>( parents[0] ); + const double r = c->radius(); + Coordinate ret = calcArcLineIntersect( c->center(), r*r, c->startAngle(), + c->angle(), line, side ); + if ( ret.valid() ) return new PointImp( ret ); + else return new InvalidImp; +} + +const ObjectImpType* ArcLineIntersectionType::resultId() const +{ + return PointImp::stype(); +} diff --git a/kig/objects/intersection_types.h b/kig/objects/intersection_types.h new file mode 100644 index 00000000..9e1df62e --- /dev/null +++ b/kig/objects/intersection_types.h @@ -0,0 +1,105 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_INTERSECTION_TYPES_H +#define KIG_OBJECTS_INTERSECTION_TYPES_H + +#include "object_type.h" + +/** + * conic line intersection. This also serves as circle-line + * intersection, in which case it uses the easier way to calc + * ... There is no separate CircleLineIntersectionPoint, since the + * difference between both types is quite small ( same number of + * intersections with a line, for example.. ), and since with + * transformations, Circles might dynamically change types to + * Conics.. + */ +class ConicLineIntersectionType + : public ArgsParserObjectType +{ + ConicLineIntersectionType(); + ~ConicLineIntersectionType(); +public: + static const ConicLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * conic line 'other' intersection. In case we already know one of the + * two intersections + */ +class ConicLineOtherIntersectionType + : public ArgsParserObjectType +{ + ConicLineOtherIntersectionType(); + ~ConicLineOtherIntersectionType(); +public: + static const ConicLineOtherIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class LineLineIntersectionType + : public ArgsParserObjectType +{ + LineLineIntersectionType(); + ~LineLineIntersectionType(); +public: + static const LineLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class LineCubicIntersectionType + : public ArgsParserObjectType +{ + LineCubicIntersectionType(); + ~LineCubicIntersectionType(); +public: + static const LineCubicIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class CircleCircleIntersectionType + : public ArgsParserObjectType +{ + CircleCircleIntersectionType(); + ~CircleCircleIntersectionType(); +public: + static const CircleCircleIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * arc line intersection. + */ +class ArcLineIntersectionType + : public ArgsParserObjectType +{ + ArcLineIntersectionType(); + ~ArcLineIntersectionType(); +public: + static const ArcLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/inversion_type.cc b/kig/objects/inversion_type.cc new file mode 100644 index 00000000..0ab77780 --- /dev/null +++ b/kig/objects/inversion_type.cc @@ -0,0 +1,412 @@ +// Copyright (C) 2005 Maurizio Paolini <paolini@dmf.unicatt.it> + +// 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 "inversion_type.h" +#include "point_imp.h" +#include "line_imp.h" +#include "circle_imp.h" +#include "other_imp.h" +#include "bogus_imp.h" + +#include "../misc/common.h" + +#include <klocale.h> + +static const char str1[] = I18N_NOOP( "Invert with respect to this circle" ); +static const char str2[] = I18N_NOOP( "Select the circle we want to invert against..." ); + +static const ArgsParser::spec argsspecInvertPoint[] = +{ + { PointImp::stype(), I18N_NOOP( "Compute the inversion of this point" ), + I18N_NOOP( "Select the point to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertPointType ) + +InvertPointType::InvertPointType() + : ArgsParserObjectType( "InvertPoint", argsspecInvertPoint, 2 ) +{ +} + +InvertPointType::~InvertPointType() +{ +} + +const InvertPointType* InvertPointType::instance() +{ + static const InvertPointType s; + return &s; +} + +const ObjectImpType* InvertPointType::resultId() const +{ + return PointImp::stype(); +} + +ObjectImp* InvertPointType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( args[1] ); + Coordinate center = c->center(); + Coordinate relp = static_cast<const PointImp*>( args[0] )->coordinate() - center; + double radiussq = c->squareRadius(); + double normsq = relp.x*relp.x + relp.y*relp.y; + if ( normsq == 0 ) return new InvalidImp; + return new PointImp( center + (radiussq/normsq)*relp ); +} + +/* + * inversion of a line + */ + +static const ArgsParser::spec argsspecInvertLine[] = +{ + { LineImp::stype(), I18N_NOOP( "Compute the inversion of this line" ), + I18N_NOOP( "Select the line to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertLineType ) + +InvertLineType::InvertLineType() + : ArgsParserObjectType( "InvertLine", argsspecInvertLine, 2 ) +{ +} + +InvertLineType::~InvertLineType() +{ +} + +const InvertLineType* InvertLineType::instance() +{ + static const InvertLineType s; + return &s; +} + +const ObjectImpType* InvertLineType::resultId() const +{ + return CircleImp::stype(); +} + +ObjectImp* InvertLineType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( args[1] ); + Coordinate center = c->center(); + double radiussq = c->squareRadius(); + const LineData line = static_cast<const AbstractLineImp*>( args[0] )->data(); + Coordinate relb = line.b - center; + Coordinate ab = line.b - line.a; + double t = (relb.x*ab.x + relb.y*ab.y)/(ab.x*ab.x + ab.y*ab.y); + Coordinate relh = relb - t*ab; + double normhsq = relh.x*relh.x + relh.y*relh.y; + if ( normhsq < 1e-12*radiussq ) return new LineImp( line.a, line.b ); + Coordinate newcenter = center + 0.5*radiussq/normhsq*relh; + double newradius = 0.5*radiussq/sqrt(normhsq); + + return new CircleImp( newcenter, newradius ); +} + +/* + * inversion of a segment + */ + +static const ArgsParser::spec argsspecInvertSegment[] = +{ + { SegmentImp::stype(), I18N_NOOP( "Compute the inversion of this segment" ), + I18N_NOOP( "Select the segment to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertSegmentType ) + +InvertSegmentType::InvertSegmentType() + : ArgsParserObjectType( "InvertSegment", argsspecInvertSegment, 2 ) +{ +} + +InvertSegmentType::~InvertSegmentType() +{ +} + +const InvertSegmentType* InvertSegmentType::instance() +{ + static const InvertSegmentType s; + return &s; +} + +const ObjectImpType* InvertSegmentType::resultId() const +{ + return ArcImp::stype(); +} + +ObjectImp* InvertSegmentType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( args[1] ); + Coordinate center = c->center(); + double radiussq = c->squareRadius(); + const LineData line = static_cast<const AbstractLineImp*>( args[0] )->data(); + Coordinate rela = line.a - center; + Coordinate relb = line.b - center; + Coordinate ab = relb - rela; + double t = (relb.x*ab.x + relb.y*ab.y)/(ab.x*ab.x + ab.y*ab.y); + Coordinate relh = relb - t*ab; + double normhsq = relh.x*relh.x + relh.y*relh.y; + + /* + * compute the inversion of the two endpoints + */ + + Coordinate newcenterrel = 0.5*radiussq/normhsq*relh; + Coordinate relainv = radiussq/rela.squareLength() * rela; + Coordinate relbinv = radiussq/relb.squareLength() * relb; + + if ( normhsq < 1e-12*radiussq ) + { + if ( rela.squareLength() < 1e-12 ) + { + return new RayImp( relbinv + center, 2*relbinv + center ); + } + if ( relb.squareLength() < 1e-12 ) + { + return new RayImp( relainv + center, 2*relainv + center ); + } + if ( relb.x*rela.x + relb.y*rela.y > 0 ) + { + return new SegmentImp( relainv + center, relbinv + center ); + } + return new InvalidImp(); + } + double newradius = 0.5*radiussq/sqrt(normhsq); + + relainv -= newcenterrel; + relbinv -= newcenterrel; + double angle1 = atan2( relainv.y, relainv.x ); + double angle2 = atan2( relbinv.y, relbinv.x ); + double angle = angle2 - angle1; + if ( ab.x*rela.y - ab.y*rela.x > 0 ) + { + angle1 = angle2; + angle = -angle; + } + while ( angle1 < 0 ) angle1 += 2*M_PI; + while ( angle1 >= 2*M_PI ) angle1 -= 2*M_PI; + while ( angle < 0 ) angle += 2*M_PI; + while ( angle >= 2*M_PI ) angle -= 2*M_PI; + return new ArcImp( newcenterrel + center, newradius, angle1, angle ); +} + +/* + * inversion of a circle + */ + +static const ArgsParser::spec argsspecInvertCircle[] = +{ + { CircleImp::stype(), I18N_NOOP( "Compute the inversion of this circle" ), + I18N_NOOP( "Select the circle to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertCircleType ) + +InvertCircleType::InvertCircleType() + : ArgsParserObjectType( "InvertCircle", argsspecInvertCircle, 2 ) +{ +} + +InvertCircleType::~InvertCircleType() +{ +} + +const InvertCircleType* InvertCircleType::instance() +{ + static const InvertCircleType s; + return &s; +} + +const ObjectImpType* InvertCircleType::resultId() const +{ + return CircleImp::stype(); +} + +ObjectImp* InvertCircleType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* refcircle = static_cast<const CircleImp*>( args[1] ); + Coordinate refc = refcircle->center(); + double refrsq = refcircle->squareRadius(); + const CircleImp* circle = static_cast<const CircleImp*>( args[0] ); + Coordinate c = circle->center() - refc; + double clength = c.length(); + Coordinate cnorm = Coordinate (1.,0.); + if ( clength != 0.0 ) cnorm = c/clength; + double r = circle->radius(); + Coordinate tc = r*cnorm; + Coordinate b = c + tc; //(1 + t)*c; + double bsq = b.x*b.x + b.y*b.y; + Coordinate bprime = refrsq*b/bsq; + if ( std::fabs( clength - r ) < 1e-6*clength ) // circle through origin -> line + { + return new LineImp( bprime+refc, bprime+refc+Coordinate( -c.y, c.x ) ); + } + + Coordinate a = c - tc; + double asq = a.x*a.x + a.y*a.y; + Coordinate aprime = refrsq*a/asq; + + Coordinate cprime = 0.5*(aprime + bprime); + double rprime = 0.5*( bprime - aprime ).length(); + + return new CircleImp( cprime + refc, rprime ); +} + +/* + * inversion of an arc + */ + +static const ArgsParser::spec argsspecInvertArc[] = +{ + { ArcImp::stype(), I18N_NOOP( "Compute the inversion of this arc" ), + I18N_NOOP( "Select the arc to invert..." ), false }, + { CircleImp::stype(), str1, str2, false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InvertArcType ) + +InvertArcType::InvertArcType() + : ArgsParserObjectType( "InvertArc", argsspecInvertArc, 2 ) +{ +} + +InvertArcType::~InvertArcType() +{ +} + +const InvertArcType* InvertArcType::instance() +{ + static const InvertArcType s; + return &s; +} + +const ObjectImpType* InvertArcType::resultId() const +{ + return ArcImp::stype(); +} + +ObjectImp* InvertArcType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const CircleImp* refcircle = static_cast<const CircleImp*>( args[1] ); + Coordinate refc = refcircle->center(); + double refrsq = refcircle->squareRadius(); + const ArcImp* arc = static_cast<const ArcImp*>( args[0] ); + Coordinate c = arc->center() - refc; + double clength = c.length(); + Coordinate cnorm = Coordinate (1.,0.); + if ( clength != 0.0 ) cnorm = c/clength; + double r = arc->radius(); + /* + * r > clength means center of inversion circle inside of circle supporting arc + */ + Coordinate tc = r*cnorm; + Coordinate b = c + tc; + double bsq = b.x*b.x + b.y*b.y; + Coordinate bprime = refrsq*b/bsq; + if ( std::fabs( clength - r ) < 1e-6*clength ) // support circle through origin -> + // segment, ray or invalid + // (reversed segment, union of two rays) + { + bool valid1 = false; + bool valid2 = false; + Coordinate ep1 = arc->firstEndPoint() - refc; + Coordinate ep2 = arc->secondEndPoint() - refc; + Coordinate ep1inv = Coordinate::invalidCoord(); + Coordinate ep2inv = Coordinate::invalidCoord(); + double ep1sq = ep1.squareLength(); + if ( ep1sq > 1e-12 ) + { + valid1 = true; + ep1inv = refrsq/ep1sq * ep1; + } + Coordinate rayendp = ep1inv; + int sign = 1; + double ep2sq = ep2.squareLength(); + if ( ep2sq > 1e-12 ) + { + valid2 = true; + ep2inv = refrsq/ep2sq * ep2; + rayendp = ep2inv; + sign = -1; + } + if ( valid1 || valid2 ) + { + if ( valid1 && valid2 ) + { + // this gives either a segment or the complement of a segment (relative + // to its support line). We return a segment in any case (fixme) + double ang = atan2( -c.y, -c.x ); + double sa = arc->startAngle(); + if ( ang < sa ) ang += 2*M_PI; + if ( ang - sa - arc->angle() < 0 ) return new InvalidImp(); + return new SegmentImp( ep1inv + refc, ep2inv + refc ); + } else + return new RayImp ( rayendp + refc, + rayendp + refc + sign*Coordinate( -c.y, c.x ) ); // this should give a Ray + } else + return new LineImp( bprime+refc, bprime+refc+Coordinate( -c.y, c.x ) ); + } + + Coordinate a = c - tc; + double asq = a.x*a.x + a.y*a.y; + Coordinate aprime = refrsq*a/asq; + + Coordinate cprime = 0.5*(aprime + bprime); + double rprime = 0.5*( bprime - aprime ).length(); + + Coordinate ep1 = arc->firstEndPoint() - refc; + double ang1 = arc->startAngle(); + double newstartangle = 2*atan2(ep1.y,ep1.x) - ang1; + Coordinate ep2 = arc->secondEndPoint() - refc; + double ang2 = ang1 + arc->angle(); + double newendangle = 2*atan2(ep2.y,ep2.x) - ang2; + double newangle = newendangle - newstartangle; + + /* + * newstartangle and newendangle might have to be exchanged: + * this is the case if the circle supporting our arc does not + * contain the center of the inversion circle + */ + if ( r < clength ) + { + newstartangle = newendangle - M_PI; + newangle = - newangle; + // newendangle is no-longer valid + } + while ( newstartangle < 0 ) newstartangle += 2*M_PI; + while ( newstartangle >= 2*M_PI ) newstartangle -= 2*M_PI; + while ( newangle < 0 ) newangle += 2*M_PI; + while ( newangle >= 2*M_PI ) newangle -= 2*M_PI; + return new ArcImp( cprime + refc, rprime, newstartangle, newangle ); +} + diff --git a/kig/objects/inversion_type.h b/kig/objects/inversion_type.h new file mode 100644 index 00000000..d7b97957 --- /dev/null +++ b/kig/objects/inversion_type.h @@ -0,0 +1,86 @@ +// Copyright (C) 2005 Maurizio Paolini <paolini@dmf.unicatt.it> + +// 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. + +#ifndef KIG_OBJECTS_INVERSION_TYPE_H +#define KIG_OBJECTS_INVERSION_TYPE_H + +#include "base_type.h" + +/** + * Inversion of a point, line + */ +class InvertPointType + : public ArgsParserObjectType +{ + InvertPointType(); + ~InvertPointType(); +public: + static const InvertPointType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertLineType + : public ArgsParserObjectType +{ + InvertLineType(); + ~InvertLineType(); +public: + static const InvertLineType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertSegmentType + : public ArgsParserObjectType +{ + InvertSegmentType(); + ~InvertSegmentType(); +public: + static const InvertSegmentType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertCircleType + : public ArgsParserObjectType +{ + InvertCircleType(); + ~InvertCircleType(); +public: + static const InvertCircleType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InvertArcType + : public ArgsParserObjectType +{ + InvertArcType(); + ~InvertArcType(); +public: + static const InvertArcType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/line_imp.cc b/kig/objects/line_imp.cc new file mode 100644 index 00000000..6f3c6002 --- /dev/null +++ b/kig/objects/line_imp.cc @@ -0,0 +1,571 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "line_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" + +#include "../misc/rect.h" +#include "../misc/common.h" +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> +using namespace std; + +AbstractLineImp::AbstractLineImp( const Coordinate& a, const Coordinate& b ) + : mdata( a, b ) +{ +} + +AbstractLineImp::~AbstractLineImp() +{ +} + +bool AbstractLineImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + return lineInRect( r, mdata.a, mdata.b, width, this, w ); +} + +const uint AbstractLineImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 2; +} + +const ObjectImpType* AbstractLineImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return AbstractLineImp::stype(); +} + +const char* AbstractLineImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() ) + return "slope"; // slope + if ( which == Parent::numberOfProperties() + 1 ) + return "kig_text"; // equation + else assert( false ); + return ""; +} + +ObjectImp* AbstractLineImp::property( uint which, const KigDocument& w ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() ) + return new DoubleImp( slope() ); + if ( which == Parent::numberOfProperties() + 1 ) + return new StringImp( equationString() ); + else assert( false ); + return new InvalidImp; +} + +const QCStringList AbstractLineImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "slope"; + l << "equation"; + assert( l.size() == AbstractLineImp::numberOfProperties() ); + return l; +} + +const QCStringList AbstractLineImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Slope" ); + l << I18N_NOOP( "Equation" ); + assert( l.size() == AbstractLineImp::numberOfProperties() ); + return l; +} + +const uint SegmentImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 4; +} + +const QCStringList SegmentImp::propertiesInternalNames() const +{ + QCStringList s = Parent::propertiesInternalNames(); + s << "length"; + s << "mid-point"; + s << "end-point-A"; + s << "end-point-B"; + assert( s.size() == SegmentImp::numberOfProperties() ); + return s; +} + +const QCStringList SegmentImp::properties() const +{ + QCStringList s = Parent::properties(); + s << I18N_NOOP( "Length" ); + s << I18N_NOOP( "Mid Point" ); + s << I18N_NOOP( "First End Point" ); + s << I18N_NOOP( "Second End Point" ); + assert( s.size() == SegmentImp::numberOfProperties() ); + return s; +} + +const ObjectImpType* SegmentImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return SegmentImp::stype(); +} + +const char* SegmentImp::iconForProperty( uint which ) const +{ + int pnum = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "distance"; // length + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "segment_midpoint"; // mid point + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "endpoint1"; // mid point + else if ( which == Parent::numberOfProperties() + pnum++ ) + return "endpoint2"; // mid point + else assert( false ); + return ""; +} + +ObjectImp* SegmentImp::property( uint which, const KigDocument& w ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new DoubleImp( mdata.dir().length() ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( ( mdata.a + mdata.b ) / 2 ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( mdata.a ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return new PointImp( mdata.b ); + else assert( false ); + return new InvalidImp; +} + +double AbstractLineImp::slope() const +{ + Coordinate diff = mdata.dir(); + return diff.y / diff.x; +} + +const QString AbstractLineImp::equationString() const +{ + Coordinate p = mdata.a; + Coordinate q = mdata.b; + + double m = ( q.y - p.y ) / ( q.x - p.x ); + double r = - ( q.y - p.y ) * p.x / ( q.x - p.x ) + p.y; + + QString ret = QString::fromUtf8( "y = %1x " ) + + QString::fromUtf8( r > 0 ? "+" : "-" ) + + QString::fromUtf8( " %2" ); + + ret = ret.arg( m, 0, 'g', 3 ); + ret = ret.arg( abs( r ), 0, 'g', 3 ); + + return ret; +} + +void SegmentImp::draw( KigPainter& p ) const +{ + p.drawSegment( mdata ); +} + +bool SegmentImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +void RayImp::draw( KigPainter& p ) const +{ + p.drawRay( mdata ); +} + +bool RayImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +void LineImp::draw( KigPainter& p ) const +{ + p.drawLine( mdata ); +} + +bool LineImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +SegmentImp::SegmentImp( const Coordinate& a, const Coordinate& b ) + : AbstractLineImp( a, b ) +{ +} + +RayImp::RayImp( const Coordinate& a, const Coordinate& b ) + : AbstractLineImp( a, b ) +{ +} + +LineImp::LineImp( const Coordinate& a, const Coordinate& b ) + : AbstractLineImp( a, b ) +{ +} + +SegmentImp* SegmentImp::copy() const +{ + return new SegmentImp( mdata ); +} + +RayImp* RayImp::copy() const +{ + return new RayImp( mdata ); +} + +LineImp* LineImp::copy() const +{ + return new LineImp( mdata ); +} + +const Coordinate SegmentImp::getPoint( double param, const KigDocument& ) const +{ + return mdata.a + mdata.dir()*param; +} + +double SegmentImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + Coordinate pt = calcPointOnPerpend( data(), p ); + pt = calcIntersectionPoint( data(), LineData( p, pt ) ); + // if pt is over the end of the segment ( i.e. it's on the line + // which the segment is a part of, but not of the segment itself..; + // ) we set it to one of the end points of the segment... + if ((pt - mdata.a).length() > mdata.dir().length() ) + pt = mdata.b; + else if ( (pt- mdata.b).length() > mdata.dir().length() ) + pt = mdata.a; + if (mdata.b == mdata.a) return 0; + return ((pt - mdata.a).length())/(mdata.dir().length()); +} + +LineData AbstractLineImp::data() const +{ + return mdata; +} + +const Coordinate RayImp::getPoint( double param, const KigDocument& ) const +{ + param = 1.0/param - 1.0; + return mdata.a + mdata.dir()*param; +} + +double RayImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + const LineData ld = data(); + Coordinate pt = calcPointOnPerpend( ld, p ); + pt = calcIntersectionPoint( ld, LineData( p, pt )); + // if pt is over the end of the ray ( i.e. it's on the line + // which the ray is a part of, but not of the ray itself..; + // ) we set it to the start point of the ray... + Coordinate dir = ld.dir(); + pt -= ld.a; + double param; + if ( dir.x != 0 ) param = pt.x / dir.x; + else if ( dir.y != 0 ) param = pt.y / dir.y; + else param = 0.; + if ( param < 0. ) param = 0.; + + // mp: let's try with 1/(x+1), this reverses the mapping, but + // should allow to take advantage of the tightness of floating point + // numbers near zero, in order to get more possible positions near + // infinity + + param = 1./( param + 1. ); + + assert( param >= 0. && param <= 1. ); + return param; +} + +const Coordinate LineImp::getPoint( double p, const KigDocument& ) const +{ + // inspired upon KSeg + + // we need to spread the points over the line, it should also come near + // the (infinite) end of the line, but most points should be near + // the two points we contain... + if ( p <= 0. ) p = 1e-6; + if ( p >= 1. ) p = 1 - 1e-6; + p = 2*p - 1; + if (p > 0) p = p/(1 - p); + else p = p/(1 + p); +// p *= 1024; // such multiplying factor could be useful in order to + // have more points near infinity, at the expense of + // points near ma and mb + return mdata.a + p*mdata.dir(); +} + +double LineImp::getParam( const Coordinate& point, const KigDocument& ) const +{ + // somewhat the reverse of getPoint, although it also supports + // points not on the line... + + Coordinate pa = point - mdata.a; + Coordinate ba = mdata.dir(); + double balsq = ba.x*ba.x + ba.y*ba.y; + assert (balsq > 0); + + double p = (pa.x*ba.x + pa.y*ba.y)/balsq; +// p /= 1024; + if (p > 0) p = p/(1+p); + else p = p/(1-p); + + return 0.5*(p + 1); +} + +ObjectImp* SegmentImp::transform( const Transformation& t ) const +{ + if ( ! t.isAffine() ) /* we need to check the position of the two points */ + { + if ( t.getProjectiveIndicator( mdata.a ) * + t.getProjectiveIndicator( mdata.b ) < 0 ) + return new InvalidImp(); + } + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply( mdata.b ); + if( na.valid() && nb.valid() ) return new SegmentImp( na, nb ); + else return new InvalidImp(); +} + +ObjectImp* LineImp::transform( const Transformation& t ) const +{ + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply( mdata.b ); + if ( na.valid() && nb.valid() ) return new LineImp( na, nb ); + else return new InvalidImp(); +} + +ObjectImp* RayImp::transform( const Transformation& t ) const +{ + if ( ! t.isAffine() ) /* we need to check the position of the two points */ + { + double pa = t.getProjectiveIndicator( mdata.a ); + double pb = t.getProjectiveIndicator( mdata.b ); + if ( pa < 0 ) pb = -pb; + if ( pb < fabs (pa) ) return new InvalidImp(); + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply0( mdata.b - mdata.a ); + if ( na.valid() && nb.valid() ) return new SegmentImp( na, nb ); + else return new InvalidImp(); + } + Coordinate na = t.apply( mdata.a ); + Coordinate nb = t.apply( mdata.b ); + if ( na.valid() && nb.valid() ) return new RayImp( na, nb ); + else return new InvalidImp(); +} + +AbstractLineImp::AbstractLineImp( const LineData& d ) + : mdata( d ) +{ +} + +SegmentImp::SegmentImp( const LineData& d ) + : AbstractLineImp( d ) +{ +} + +RayImp::RayImp( const LineData& d ) + : AbstractLineImp( d ) +{ +} + +LineImp::LineImp( const LineData& d ) + : AbstractLineImp( d ) +{ +} + +double SegmentImp::length() const +{ + return mdata.length(); +} + +void SegmentImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void RayImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void LineImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool AbstractLineImp::equals( const ObjectImp& rhs ) const +{ + return rhs.type() == type() && + static_cast<const AbstractLineImp&>( rhs ).data() == data(); +} + +const ObjectImpType* AbstractLineImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "line", I18N_NOOP( "line" ), + I18N_NOOP( "Select a Line" ), 0, 0, 0, 0, 0, 0, 0 ); + return &t; +} + +const ObjectImpType* LineImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "line", + I18N_NOOP( "line" ), + I18N_NOOP( "Select this line" ), + I18N_NOOP( "Select line %1" ), + I18N_NOOP( "Remove a Line" ), + I18N_NOOP( "Add a Line" ), + I18N_NOOP( "Move a Line" ), + I18N_NOOP( "Attach to this line" ), + I18N_NOOP( "Show a Line" ), + I18N_NOOP( "Hide a Line" ) + ); + return &t; +} + +const ObjectImpType* SegmentImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "segment", + I18N_NOOP( "segment" ), + I18N_NOOP( "Select this segment" ), + I18N_NOOP( "Select segment %1" ), + I18N_NOOP( "Remove a Segment" ), + I18N_NOOP( "Add a Segment" ), + I18N_NOOP( "Move a Segment" ), + I18N_NOOP( "Attach to this segment" ), + I18N_NOOP( "Show a Segment" ), + I18N_NOOP( "Hide a Segment" ) + ); + return &t; +} + +const ObjectImpType* RayImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "ray", + I18N_NOOP( "half-line" ), + I18N_NOOP( "Select this half-line" ), + I18N_NOOP( "Select half-line %1" ), + I18N_NOOP( "Remove a Half-Line" ), + I18N_NOOP( "Add a Half-Line" ), + I18N_NOOP( "Move a Half-Line" ), + I18N_NOOP( "Attach to this half-line" ), + I18N_NOOP( "Show a Half-Line" ), + I18N_NOOP( "Hide a Half-Line" ) + ); + return &t; +} + +const ObjectImpType* SegmentImp::type() const +{ + return SegmentImp::stype(); +} + +const ObjectImpType* RayImp::type() const +{ + return RayImp::stype(); +} + +const ObjectImpType* LineImp::type() const +{ + return LineImp::stype(); +} + +bool SegmentImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool SegmentImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnSegment( p, mdata.a, mdata.b, threshold ); +} + +bool RayImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool RayImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnRay( p, mdata.a, mdata.b, threshold ); +} + +bool LineImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool LineImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnLine( p, mdata.a, mdata.b, threshold ); +} + +bool AbstractLineImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + int pnum = 0; + + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + else if ( which == Parent::numberOfProperties() + pnum++ ) + return false; + else if ( which == Parent::numberOfProperties() + pnum++ ) + return true; + else if ( which == Parent::numberOfProperties() + pnum++ ) + return true; + else if ( which == Parent::numberOfProperties() + pnum++ ) + return true; + else assert( false ); + return false; +} + +Rect SegmentImp::surroundingRect() const +{ + return Rect( mdata.a, mdata.b ); +} + +Rect RayImp::surroundingRect() const +{ + return Rect::invalidRect(); +} + +Rect LineImp::surroundingRect() const +{ + return Rect::invalidRect(); +} diff --git a/kig/objects/line_imp.h b/kig/objects/line_imp.h new file mode 100644 index 00000000..c9014613 --- /dev/null +++ b/kig/objects/line_imp.h @@ -0,0 +1,215 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_LINE_IMP_H +#define KIG_OBJECTS_LINE_IMP_H + +#include "curve_imp.h" + +#include "../misc/common.h" + +class LineData; + +/** + * An ObjectImp class that is the base of the line-like ObjectImp's: + * SegmentImp, LineImp and RayImp.. + */ +class AbstractLineImp + : public CurveImp +{ +protected: + LineData mdata; + AbstractLineImp( const LineData& d ); + AbstractLineImp( const Coordinate& a, const Coordinate& b ); + +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the AbstractLineImp + * type.. + */ + static const ObjectImpType* stype(); + + ~AbstractLineImp(); + + bool inRect( const Rect& r, int width, const KigWidget& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + /** + * Get the slope of this AbstractLineImp.. For a line through + * points a( xa, ya ) and b ( xb, yb ), this means the value ( yb - + * ya ) / ( xb - xa ). + */ + double slope() const; + /** + * Get a string containing the equation of this line in the form "y + * = a * x + b ". + */ + const QString equationString() const; + /** + * Get the LineData for this AbstractLineImp. + */ + LineData data() const; + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * An ObjectImp representing a segment + */ +class SegmentImp + : public AbstractLineImp +{ +public: + typedef AbstractLineImp Parent; + /** + * Returns the ObjectImpType representing the SegmentImp + * type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a new segment from point a to point b. + */ + SegmentImp( const Coordinate& a, const Coordinate& b ); + /** + * Construct a new segment from a LineData. + */ + SegmentImp( const LineData& d ); + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + + SegmentImp* copy() const; + + /** + * Get the length of this segment. + */ + double length() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An ObjectImp representing a ray. This means half of a line, it is + * infinite in one direction, but ends at a certain point in the other + * direction.. + */ +class RayImp + : public AbstractLineImp +{ +public: + typedef AbstractLineImp Parent; + /** + * Returns the ObjectImpType representing the RayImp + * type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a ray, starting at a, and going through b. + */ + RayImp( const Coordinate& a, const Coordinate& b ); + /** + * Construct a ray from a LineData. + */ + RayImp( const LineData& d ); + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + RayImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An ObjectImp representing a line. + */ +class LineImp + : public AbstractLineImp +{ +public: + typedef AbstractLineImp Parent; + + /** + * Returns the ObjectImpType representing the LineImp + * type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a LineImp going through points a and b. + */ + LineImp( const Coordinate& a, const Coordinate& b ); + /** + * Construct a LineImp from a LineData. + */ + LineImp( const LineData& d ); + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + LineImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +#endif diff --git a/kig/objects/line_type.cc b/kig/objects/line_type.cc new file mode 100644 index 00000000..a2c0734b --- /dev/null +++ b/kig/objects/line_type.cc @@ -0,0 +1,334 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "line_type.h" + +#include "bogus_imp.h" +#include "line_imp.h" +#include "object_holder.h" +#include "other_imp.h" +#include "point_imp.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_part.h" +#include "../kig/kig_commands.h" +#include "../misc/common.h" +#include "../misc/calcpaths.h" + +#include <qstringlist.h> + +#include <klocale.h> + +static const ArgsParser::spec argsspecSegmentAB[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct a segment starting at this point" ), + I18N_NOOP( "Select the start point of the new segment..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct a segment ending at this point" ), + I18N_NOOP( "Select the end point of the new segment..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( SegmentABType ) + +SegmentABType::SegmentABType() + : ObjectABType( "SegmentAB", argsspecSegmentAB, 2 ) +{ +} + +SegmentABType::~SegmentABType() +{ +} + +const SegmentABType* SegmentABType::instance() +{ + static const SegmentABType s; + return &s; +} + +ObjectImp* SegmentABType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new SegmentImp( a, b ); +} + +static const char constructlineabstat[] = I18N_NOOP( "Construct a line through this point" ); + +static const ArgsParser::spec argsspecLineAB[] = +{ + { PointImp::stype(), constructlineabstat, + I18N_NOOP( "Select a point for the line to go through..." ), true }, + { PointImp::stype(), constructlineabstat, + I18N_NOOP( "Select another point for the line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineABType ) + +LineABType::LineABType() + : ObjectABType( "LineAB", argsspecLineAB, 2 ) +{ +} + +LineABType::~LineABType() +{ +} + +const LineABType* LineABType::instance() +{ + static const LineABType s; + return &s; +} + +ObjectImp* LineABType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new LineImp( a, b ); +} + +static const char constructhalflinestartingstat[] = I18N_NOOP( "Construct a half-line starting at this point" ); + +static const ArgsParser::spec argsspecRayAB[] = +{ + { PointImp::stype(), constructhalflinestartingstat, + I18N_NOOP( "Select the start point of the new half-line..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct a half-line through this point" ), + I18N_NOOP( "Select a point for the half-line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( RayABType ) + +RayABType::RayABType() + : ObjectABType( "RayAB", argsspecRayAB, 2 ) +{ +} + +RayABType::~RayABType() +{ +} + +const RayABType* RayABType::instance() +{ + static const RayABType s; + return &s; +} + +ObjectImp* RayABType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new RayImp( a, b ); +} + +LinePerpendLPType* LinePerpendLPType::instance() +{ + static LinePerpendLPType l; + return &l; +} + +ObjectImp* LinePerpendLPType::calc( + const LineData& a, + const Coordinate& b ) const +{ + Coordinate p = calcPointOnPerpend( a, b ); + return new LineImp( b, p ); +} + +static const ArgsParser::spec argsspecLineParallel[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a line parallel to this line" ), + I18N_NOOP( "Select a line parallel to the new line..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the parallel line through this point" ), + I18N_NOOP( "Select a point for the new line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineParallelLPType ) + +LineParallelLPType::LineParallelLPType() + : ObjectLPType( "LineParallel", argsspecLineParallel, 2 ) +{ +} + +LineParallelLPType::~LineParallelLPType() +{ +} + +LineParallelLPType* LineParallelLPType::instance() +{ + static LineParallelLPType l; + return &l; +} + +ObjectImp* LineParallelLPType::calc( + const LineData& a, + const Coordinate& b ) const +{ + Coordinate r = calcPointOnParallel( a, b ); + return new LineImp( r, b ); +} + +static const ArgsParser::spec argsspecLinePerpend[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Construct a line perpendicular to this line" ), + I18N_NOOP( "Select a line perpendicular to the new line..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct a perpendicular line through this point" ), + I18N_NOOP( "Select a point for the new line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LinePerpendLPType ) + +LinePerpendLPType::LinePerpendLPType() + : ObjectLPType( "LinePerpend", argsspecLinePerpend, 2 ) +{ +} + +LinePerpendLPType::~LinePerpendLPType() +{ +} + +const ObjectImpType* SegmentABType::resultId() const +{ + return SegmentImp::stype(); +} + +const ObjectImpType* LineABType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* RayABType::resultId() const +{ + return RayImp::stype(); +} + +const ObjectImpType* LinePerpendLPType::resultId() const +{ + return LineImp::stype(); +} + +const ObjectImpType* LineParallelLPType::resultId() const +{ + return LineImp::stype(); +} + +QStringList SegmentABType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set &Length..." ); + return ret; +} + +void SegmentABType::executeAction( int i, ObjectHolder&, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& ) const +{ + assert( i == 0 ); + // pretend to use this var.. + (void) i; + + std::vector<ObjectCalcer*> parents = c.parents(); + assert( margsparser.checkArgs( parents ) ); + + Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + + bool ok = true; + double length = getDoubleFromUser( + i18n( "Set Segment Length" ), i18n( "Choose the new length: " ), + (b-a).length(), &w, &ok, -2147483647, 2147483647, 3 ); + if ( ! ok ) return; + + Coordinate nb = a + ( b - a ).normalize( length ); + + MonitorDataObjects mon( getAllParents( parents ) ); + parents[1]->move( nb, d.document() ); + KigCommand* cd = new KigCommand( d, i18n( "Resize Segment" ) ); + mon.finish( cd ); + d.history()->addCommand( cd ); +} + +static const ArgsParser::spec argsspecLineByVector[] = +{ + { VectorImp::stype(), I18N_NOOP( "Construct a line by this vector" ), + I18N_NOOP( "Select a vector in the direction of the new line..." ), true }, + { PointImp::stype(), constructlineabstat, + I18N_NOOP( "Select a point for the new line to go through..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineByVectorType ) + +LineByVectorType::LineByVectorType() + : ArgsParserObjectType( "LineByVector", argsspecLineByVector, 2 ) +{ +} + +LineByVectorType::~LineByVectorType() +{ +} + +const LineByVectorType* LineByVectorType::instance() +{ + static const LineByVectorType s; + return &s; +} + +ObjectImp* LineByVectorType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const VectorImp& a = *static_cast<const VectorImp*>( args[0] ); + const PointImp& b = *static_cast<const PointImp*>( args[1] ); + + return new LineImp( b.coordinate(), b.coordinate() + a.dir() ); +} + +const ObjectImpType* LineByVectorType::resultId() const +{ + return LineImp::stype(); +} + +static const ArgsParser::spec argsspecHalflineByVector[] = +{ + { VectorImp::stype(), I18N_NOOP( "Construct a half-line by this vector" ), + I18N_NOOP( "Select a vector in the direction of the new half-line..." ), true }, + { PointImp::stype(), constructhalflinestartingstat, + I18N_NOOP( "Select the start point of the new half-line..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HalflineByVectorType ) + +HalflineByVectorType::HalflineByVectorType() + : ArgsParserObjectType( "HalflineByVector", argsspecHalflineByVector, 2 ) +{ +} + +HalflineByVectorType::~HalflineByVectorType() +{ +} + +const HalflineByVectorType* HalflineByVectorType::instance() +{ + static const HalflineByVectorType s; + return &s; +} + +ObjectImp* HalflineByVectorType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const VectorImp& a = *static_cast<const VectorImp*>( args[0] ); + const PointImp& b = *static_cast<const PointImp*>( args[1] ); + + return new RayImp( b.coordinate(), b.coordinate() + a.dir() ); +} + +const ObjectImpType* HalflineByVectorType::resultId() const +{ + return RayImp::stype(); +} diff --git a/kig/objects/line_type.h b/kig/objects/line_type.h new file mode 100644 index 00000000..a3ded322 --- /dev/null +++ b/kig/objects/line_type.h @@ -0,0 +1,110 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_SEGMENT_H +#define KIG_OBJECTS_SEGMENT_H + +#include "base_type.h" + +class LineData; + +class SegmentABType + : public ObjectABType +{ + SegmentABType(); + ~SegmentABType(); +public: + static const SegmentABType* instance(); + + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + /** + * execute the \p i 'th action from the specialActions above.. + */ + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class LineABType + : public ObjectABType +{ + LineABType(); + ~LineABType(); +public: + static const LineABType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class RayABType + : public ObjectABType +{ + RayABType(); + ~RayABType(); +public: + static const RayABType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class LinePerpendLPType + : public ObjectLPType +{ + LinePerpendLPType(); + ~LinePerpendLPType(); +public: + static LinePerpendLPType* instance(); + ObjectImp* calc( const LineData& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class LineParallelLPType + : public ObjectLPType +{ + LineParallelLPType(); + ~LineParallelLPType(); +public: + static LineParallelLPType* instance(); + ObjectImp* calc( const LineData& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class LineByVectorType + : public ArgsParserObjectType +{ + LineByVectorType(); + ~LineByVectorType(); +public: + static const LineByVectorType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class HalflineByVectorType + : public ArgsParserObjectType +{ + HalflineByVectorType(); + ~HalflineByVectorType(); +public: + static const HalflineByVectorType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/locus_imp.cc b/kig/objects/locus_imp.cc new file mode 100644 index 00000000..edbdc88b --- /dev/null +++ b/kig/objects/locus_imp.cc @@ -0,0 +1,397 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "locus_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "../misc/object_hierarchy.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate.h" +#include "../misc/common.h" + +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> + +using namespace std; + +static double cachedparam = 0.0; + +LocusImp::~LocusImp() +{ + delete mcurve; +} + +ObjectImp* LocusImp::transform( const Transformation& t ) const +{ + return new LocusImp( mcurve->copy(), mhier.transformFinalObject( t ) ); +} + +void LocusImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool LocusImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ), w.document() ); +} + +bool LocusImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO ? + return false; +} + +const Coordinate LocusImp::getPoint( double param, const KigDocument& doc ) const +{ + Coordinate arg = mcurve->getPoint( param, doc ); + if ( ! arg.valid() ) return arg; + PointImp argimp( arg ); + Args args; + args.push_back( &argimp ); + vector<ObjectImp*> calcret = mhier.calc( args, doc ); + assert( calcret.size() == 1 ); + ObjectImp* imp = calcret.front(); + Coordinate ret; + if ( imp->inherits( PointImp::stype() ) ) + { + cachedparam = param; + ret = static_cast<PointImp*>( imp )->coordinate(); + } + else + ret = Coordinate::invalidCoord(); + + delete imp; + return ret; +} + +LocusImp::LocusImp( CurveImp* curve, const ObjectHierarchy& hier ) + : mcurve( curve ), mhier( hier ) +{ +} + +const uint LocusImp::numberOfProperties() const +{ + return Parent::numberOfProperties(); +} + +const QCStringList LocusImp::propertiesInternalNames() const +{ + return Parent::propertiesInternalNames(); +} + +const QCStringList LocusImp::properties() const +{ + return Parent::properties(); +} + +const ObjectImpType* LocusImp::impRequirementForProperty( uint which ) const +{ + return Parent::impRequirementForProperty( which ); +} + +const char* LocusImp::iconForProperty( uint which ) const +{ + return Parent::iconForProperty( which ); +} + +ObjectImp* LocusImp::property( uint which, const KigDocument& w ) const +{ + return Parent::property( which, w ); +} + +LocusImp* LocusImp::copy() const +{ + return new LocusImp( mcurve->copy(), mhier ); +} + +const CurveImp* LocusImp::curve() const +{ + return mcurve; +} + +const ObjectHierarchy& LocusImp::hierarchy() const +{ + return mhier; +} + +/** + * This function returns the distance between the point with parameter + * param and point p. param is allowed to not be between 0 and 1, in + * which case we consider only the decimal part. + */ +double LocusImp::getDist(double param, const Coordinate& p, const KigDocument& doc) const +{ + param = fmod( param, 1 ); + if( param < 0 ) param += 1.; + Coordinate p1 = getPoint( param, doc ); + // i don't think the p1.valid() switch is really necessary, but I + // prefer to not take any chances :) + return p1.valid() ? ( p1 - p ).length() : +double_inf; +} + +/** + * This function searches starting from x1 for the first interval in + * which the function of the distance from the point at coordinate x + * starts to increase. The range found is returned in the parameters + * x1 and x2: [x1,x2]. + */ +void LocusImp::getInterval( double& x1, double& x2, + double incr,const Coordinate& p, + const KigDocument& doc) const +{ + double mm = getDist( x1, p, doc); + double mm1 = getDist( x2, p, doc); + if( mm <= mm1 ) return; + else + { + double x3 = x2 + incr; + double mm2 = getDist (x3, p, doc); + while( mm > mm1 & mm1 > mm2 ) + { + x1 = x2; + x2 = x3; + x3 = x2 + incr; + mm = mm1; + mm1 = mm2; + mm2 = getDist (x3, p, doc); + } + x2=x3; + } +} + +double LocusImp::getParam( const Coordinate& p, const KigDocument& doc ) const +{ + // this function ( and related functions like getInterval etc. ) is + // written by Franco Pasquarelli <pasqui@dmf.bs.unicatt.it>. + // I ( domi ) have adapted and documented it a bit. + + if ( cachedparam >= 0. && cachedparam <= 1. && + getPoint ( cachedparam, doc ) == p ) return cachedparam; + + // consider the function that returns the distance for a point at + // parameter x to the locus for a given parameter x. What we do + // here is look for the global minimum of this function. We do that + // by dividing the range ( [0,1] ) into N parts, and start looking + // for a local minimum from there on. If we find one, we keep it if + // it is the lowest of all the ones we've already found.. + + const int N = 50; + const double incr = 1. / (double) N; + + // xm is the best parameter we've found so far, fxm is the distance + // to the locus from that point. We start with some + // pseudo-values. + // (mp) note that if the distance is actually increasing in the + // whole interval [0,1] this value will be returned in the end. + double xm = 0.; + double fxm = getDist( xm, p, doc ); + + int j = 0; + double mm = fxm; + + while( j < N ) + { + // [x1,x2] is the range we're currently considering.. + double x1 = j * incr; + double x2 = x1 + incr; + + // check the range x1,x2 for the first local maximum.. + double mm1 = getDist( x2, p, doc); + double mm2; + j++; + if( mm < mm1 ) + mm = mm1; + + else + { + if ( mm > mm1 ) + { + double x3 = x2 + incr; + mm2 = getDist (x3, p, doc); + j++; + while( mm1 > mm2 & j <= N ) + { + x1 = x2; + x2 = x3; + x3 = x2 + incr; + mm = mm1; + mm1 = mm2; + mm2 = getDist (x3, p, doc); + j++; + } + x2 = x3; + } + else + mm2 = mm1; + + if ( mm1 <= mm2 ) + { + mm = mm2; + + double xm1 = getParamofmin( x1, x2, p, doc); + double fxm1 = getDist( xm1, p, doc ); + if( fxm1 < fxm ) + { + // we found a new minimum, save it.. + xm=xm1; + fxm=fxm1; + } + } + } + } + return xm; +} + +/** + * This function calculates the parameter of the point that realizes the + * minimum in [a,b] of the distance between the points of the locus and + * the point of coordinate p, using the golden ration method. + */ +double LocusImp::getParamofmin( double a, double b, + const Coordinate& p, + const KigDocument& doc ) const +{ + double epsilons = 1.e-08; + double epsilonl = 2.e-02; + + //assert( a < b && a >= 0. && b <= 1.0); + assert( a < b && a >= 0.); + + double r2 = ( sqrt( 5. ) - 1 ) / 2.; // golden ratio + double r1 = 1. - r2; + + double t2 = a + r2 * ( b - a ); + double t1 = a + r1 * ( b - a ); + Coordinate p1 = getPoint( fmod( t1, 1. ), doc); + double f1 = (p1 - p).length(); + Coordinate p2 = getPoint( fmod( t2, 1. ), doc); + double f2 = (p2 - p).length(); + + double fmin, tmin; + if (f1 < f2) + { + b = t2; + fmin = f1; + tmin = t1; + } + else + { + a = t1; + fmin = f2; + tmin = t2; + } + + while ( ( b - a ) > epsilons && + ( (p1 - p2).length() > 0.4 * fmin + || (b - a) > epsilonl) && + fmin > 1.e-8 ) + { + if ( f1 < f2 ) + { + t2 = t1; + t1 = a + r1*(b - a); + f2 = f1; + p1 = getPoint( fmod( t1, 1. ), doc); + f1 = (p1 - p).length(); + } + else + { + t1 = t2; + t2 = a + r2*(b - a); + f1 = f2; + p2 = getPoint( fmod( t2, 1. ), doc); + f2 = (p2 - p).length(); + } + if ( f1 < f2 ) + { + b = t2; + fmin = f1; + tmin = t1; + } + else + { + a = t1; + fmin = f2; + tmin = t2; + } + } + + return(tmin); +} + +void LocusImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool LocusImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( LocusImp::stype() ) && + static_cast<const LocusImp&>( rhs ).curve()->equals( *curve() ) && + static_cast<const LocusImp&>( rhs ).hierarchy() == hierarchy(); +} + +const ObjectImpType* LocusImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "locus", + I18N_NOOP( "locus" ), + I18N_NOOP( "Select this locus" ), + I18N_NOOP( "Select locus %1" ), + I18N_NOOP( "Remove a Locus" ), + I18N_NOOP( "Add a Locus" ), + I18N_NOOP( "Move a Locus" ), + I18N_NOOP( "Attach to this locus" ), + I18N_NOOP( "Show a Locus" ), + I18N_NOOP( "Hide a Locus" ) + ); + return &t; +} + +const ObjectImpType* LocusImp::type() const +{ + return LocusImp::stype(); +} + +bool LocusImp::containsPoint( const Coordinate& p, const KigDocument& doc ) const +{ + return internalContainsPoint( p, test_threshold, doc ); +} + +bool LocusImp::internalContainsPoint( const Coordinate& p, double threshold, const KigDocument& doc ) const +{ + double param = getParam( p, doc ); + double dist = getDist( param, p, doc ); + return fabs( dist ) <= threshold; +} + +bool LocusImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect LocusImp::surroundingRect() const +{ + // it's probably possible to calculate this, if it exists, but we + // don't for now. + return Rect::invalidRect(); +} diff --git a/kig/objects/locus_imp.h b/kig/objects/locus_imp.h new file mode 100644 index 00000000..568e0e7c --- /dev/null +++ b/kig/objects/locus_imp.h @@ -0,0 +1,96 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_LOCUS_IMP_H +#define KIG_OBJECTS_LOCUS_IMP_H + +#include "curve_imp.h" +#include "../misc/object_hierarchy.h" + +/** + * LocusImp is an imp that consists of a copy of the curveimp that the + * moving point moves over, and an ObjectHierarchy that can calc ( + * given a point, and optionally some more parent objects the position + * of the moving point. The hierarchy should take the moving point as + * its *first* argument and all others after that. The others are + * used to make it possible for Locus to be updated when some of the + * other objects that appear in the path from the moving point to the + * dependent point change. + * + * This may seem rather complicated, but I think it is absolutely + * necessary to support locuses using Kig's object system. It would + * be very bad for LocusImp to have to keep references to its parents + * as Objects ( since only the objects know how they are related to + * their parents ). This is how we used to do it, but the current + * method is far superior. First and foremost because the separation + * between ObjectImp and Object is something that Kig depends on very + * much, and because every ObjectImp should contain all the data it + * needs itself. ObjectImp's are entirely independent objects. + * That's also why we don't keep a pointer to the old CurveImp, but a + * copy of it.. + * + * i hope this is a bit clear, if not, feel free to ask for + * explanation of what you don't understand.. + */ +class LocusImp + : public CurveImp +{ + CurveImp* mcurve; + const ObjectHierarchy mhier; + + double getDist(double param, const Coordinate& p, const KigDocument& doc) const; + void getInterval(double& x1,double& x2,double incr,const Coordinate& p, const KigDocument& doc) const; + double getParamofmin(double a, double b, const Coordinate& p, const KigDocument& doc) const; +public: + typedef CurveImp Parent; + static const ObjectImpType* stype(); + + LocusImp( CurveImp*, const ObjectHierarchy& ); + ~LocusImp(); + LocusImp* copy() const; + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + Rect surroundingRect() const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + double getParam( const Coordinate& point, const KigDocument& ) const; + const Coordinate getPoint( double param, const KigDocument& ) const; + + // TODO ? + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const CurveImp* curve() const; + const ObjectHierarchy& hierarchy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& d ) const; + bool internalContainsPoint( const Coordinate& p, double threshold, const KigDocument& doc ) const; +}; + +#endif diff --git a/kig/objects/object_calcer.cc b/kig/objects/object_calcer.cc new file mode 100644 index 00000000..40545ed1 --- /dev/null +++ b/kig/objects/object_calcer.cc @@ -0,0 +1,323 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "object_calcer.h" + +#include "object_holder.h" +#include "object_imp.h" +#include "object_type.h" +#include "../misc/coordinate.h" +#include "common.h" + +#include <algorithm> +#include <set> + +void ObjectTypeCalcer::calc( const KigDocument& doc ) +{ + Args a; + a.reserve( mparents.size() ); + std::transform( mparents.begin(), mparents.end(), + std::back_inserter( a ), std::mem_fun( &ObjectCalcer::imp ) ); + ObjectImp* n = mtype->calc( a, doc ); + delete mimp; + mimp = n; +} + +ObjectTypeCalcer::ObjectTypeCalcer( const ObjectType* type, + const std::vector<ObjectCalcer*>& parents, bool sort ) + : mparents( ( sort )?type->sortArgs( parents ):parents ), mtype( type ), mimp( 0 ) +{ + std::for_each( mparents.begin(), mparents.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::addChild ), this ) ); +} + +ObjectCalcer::~ObjectCalcer() +{ +} + +ObjectConstCalcer::ObjectConstCalcer( ObjectImp* imp ) + : mimp( imp ) +{ +} + +ObjectConstCalcer::~ObjectConstCalcer() +{ + delete mimp; +} + +const ObjectImp* ObjectConstCalcer::imp() const +{ + return mimp; +} + +void ObjectConstCalcer::calc( const KigDocument& ) +{ +} + +std::vector<ObjectCalcer*> ObjectConstCalcer::parents() const +{ + // we have no parents.. + return std::vector<ObjectCalcer*>(); +} + +void ObjectCalcer::ref() +{ + ++refcount; +} + +void ObjectCalcer::deref() +{ + if ( --refcount <= 0 ) delete this; +} + +void intrusive_ptr_add_ref( ObjectCalcer* p ) +{ + p->ref(); +} + +void intrusive_ptr_release( ObjectCalcer* p ) +{ + p->deref(); +} + +const ObjectImp* ObjectTypeCalcer::imp() const +{ + return mimp; +} + +std::vector<ObjectCalcer*> ObjectTypeCalcer::parents() const +{ + return mparents; +} + +void ObjectCalcer::addChild( ObjectCalcer* c ) +{ + mchildren.push_back( c ); + ref(); +} + +void ObjectCalcer::delChild( ObjectCalcer* c ) +{ + std::vector<ObjectCalcer*>::iterator i = std::find( mchildren.begin(), mchildren.end(), c ); + assert( i != mchildren.end() ); + + mchildren.erase( i ); + deref(); +} + +ObjectTypeCalcer::~ObjectTypeCalcer() +{ + std::for_each( mparents.begin(), mparents.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::delChild ), this ) ); + delete mimp; +} + +const ObjectType* ObjectTypeCalcer::type() const +{ + return mtype; +} + +ObjectPropertyCalcer::ObjectPropertyCalcer( ObjectCalcer* parent, int propid ) + : mimp( 0 ), mparent( parent ), mpropid( propid ) +{ + // Some weird C++ thing prevents me from calling protected members + // of ObjectCalcer on mparent.. This is an ugly workaround.. + ( mparent->*&ObjectCalcer::addChild )( this ); + //mparent->addChild( this ); +} + +ObjectPropertyCalcer::~ObjectPropertyCalcer() +{ + // Some weird C++ thing prevents me from calling protected members + // of ObjectCalcer on mparent.. This is an ugly workaround.. + ( mparent->*&ObjectCalcer::delChild )( this ); + //mparent->delChild( this ); + delete mimp; +} + +const ObjectImp* ObjectPropertyCalcer::imp() const +{ + return mimp; +} + +std::vector<ObjectCalcer*> ObjectPropertyCalcer::parents() const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( mparent ); + return ret; +} + +void ObjectPropertyCalcer::calc( const KigDocument& doc ) +{ + ObjectImp* n = mparent->imp()->property( mpropid, doc ); + delete mimp; + mimp = n; +} + +ObjectImp* ObjectConstCalcer::switchImp( ObjectImp* newimp ) +{ + ObjectImp* ret = mimp; + mimp = newimp; + return ret; +} + +std::vector<ObjectCalcer*> ObjectCalcer::children() const +{ + return mchildren; +} + +const ObjectImpType* ObjectPropertyCalcer::impRequirement( + ObjectCalcer*, const std::vector<ObjectCalcer*>& ) const +{ + return mparent->imp()->impRequirementForProperty( mpropid ); +} + +const ObjectImpType* ObjectConstCalcer::impRequirement( + ObjectCalcer*, const std::vector<ObjectCalcer*>& ) const +{ + assert( false ); + return ObjectImp::stype(); +} + +const ObjectImpType* ObjectTypeCalcer::impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const +{ + Args args; + args.reserve( mparents.size() ); + std::transform( + os.begin(), os.end(), + std::back_inserter( args ), + std::mem_fun( &ObjectCalcer::imp ) ); + assert( std::find( args.begin(), args.end(), o->imp() ) != args.end() ); + return mtype->impRequirement( o->imp(), args ); +} + +int ObjectPropertyCalcer::propId() const +{ + return mpropid; +} + +void ObjectConstCalcer::setImp( ObjectImp* newimp ) +{ + delete switchImp( newimp ); +} + +void ObjectTypeCalcer::setParents( const std::vector<ObjectCalcer*> np ) +{ + std::for_each( np.begin(), np.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::addChild ), this ) ); + std::for_each( mparents.begin(), mparents.end(), + std::bind2nd( std::mem_fun( &ObjectCalcer::delChild ), this ) ); + mparents = np; +} + +void ObjectTypeCalcer::setType( const ObjectType* t ) +{ + mtype = t; +} + +bool ObjectCalcer::canMove() const +{ + return false; +} + +bool ObjectCalcer::isFreelyTranslatable() const +{ + return false; +} + +Coordinate ObjectCalcer::moveReferencePoint() const +{ + assert( false ); + return Coordinate::invalidCoord(); +} + +void ObjectCalcer::move( const Coordinate&, const KigDocument& ) +{ + assert( false ); +} + +bool ObjectTypeCalcer::canMove() const +{ + return mtype->canMove( *this ); +} + +bool ObjectTypeCalcer::isFreelyTranslatable() const +{ + return mtype->isFreelyTranslatable( *this ); +} + +Coordinate ObjectTypeCalcer::moveReferencePoint() const +{ + return mtype->moveReferencePoint( *this ); +} + +void ObjectTypeCalcer::move( const Coordinate& to, const KigDocument& doc ) +{ + // we need to check if type can in fact move, because this check is + // not done for us in all circumstances ( e.g. LineABType::move uses + // move on its parents to move them ), and the ObjectType's depend + // on move only being called if canMove() returns true.. + if ( mtype->canMove( *this ) ) + mtype->move( *this, to, doc ); +} + +ObjectCalcer* ObjectPropertyCalcer::parent() const +{ + return mparent; +} + +ObjectCalcer::ObjectCalcer() + : refcount( 0 ) +{ +} + +std::vector<ObjectCalcer*> ObjectCalcer::movableParents() const +{ + return std::vector<ObjectCalcer*>(); +} + +std::vector<ObjectCalcer*> ObjectTypeCalcer::movableParents() const +{ + return mtype->movableParents( *this ); +} + +bool ObjectConstCalcer::isDefinedOnOrThrough( const ObjectCalcer* ) const +{ + return false; +} + +bool ObjectPropertyCalcer::isDefinedOnOrThrough( const ObjectCalcer* o ) const +{ + return o == mparent && + mparent->imp()->isPropertyDefinedOnOrThroughThisImp( propId() ); +} + +bool ObjectTypeCalcer::isDefinedOnOrThrough( const ObjectCalcer* o ) const +{ + Args args; + args.reserve( mparents.size() ); + std::transform( + mparents.begin(), mparents.end(), + std::back_inserter( args ), + std::mem_fun( &ObjectCalcer::imp ) ); + if ( std::find( args.begin(), args.end(), o->imp() ) == args.end() ) + return false; + + return mtype->isDefinedOnOrThrough( o->imp(), args ); +} + diff --git a/kig/objects/object_calcer.h b/kig/objects/object_calcer.h new file mode 100644 index 00000000..6df94fe8 --- /dev/null +++ b/kig/objects/object_calcer.h @@ -0,0 +1,301 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_CALCER_H +#define KIG_OBJECTS_OBJECT_CALCER_H + +#include "common.h" +#include "../misc/boost_intrusive_pointer.hpp" + +class ObjectCalcer; + +void intrusive_ptr_add_ref( ObjectCalcer* p ); +void intrusive_ptr_release( ObjectCalcer* p ); + +/** + * An ObjectCalcer is an object that represents an algorithm for + * calculating an ObjectImp from other ObjectImp's. It is also a node + * in the dependency graph of a certain document. E.g. a LineImp can + * be calculated from the two PointImp's it has to go through; every + * time either of them moves, this calculation is redone. In this + * case, there would be an ObjectCalcer that keeps a reference to its + * two parents ( the ObjectCalcer's representing the points ), and + * that will calculate its ObjectImp value every time it is asked to + * do so ( i.e. every time one of its parents moves.. ). + * + * Each ObjectHolder keeps its ObjectImp itself, and recalculates it + * from its parents in its calc() method ( if necessary ). + * + * Because of the complex relations that ObjectCalcer's hold to other + * ObjectCalcer's and to other classes, they have been made + * reference-counted. This means that they keep a count internally of + * how much times a pointer to them is held. If this count reaches 0, + * this means that nobody needs them anymore, and they delete + * themselves. E.g. an ObjectCalcer always keeps a reference to its + * parents, to ensure that those aren't deleted before it is deleted. + * + * At runtime, there will be an entire graph of ObjectCalcer that + * depend on their parents.. At the bottom, there are Calcer's that + * the user is aware of, and that are held by ObjectHolder's. At the + * top, there are Calcer's without parents that serve only to hold + * some data. Those are most likely ObjectConstCalcer's. There are + * some algorithms to work with the dependency graph in various ways + * in ../misc/calcpath.h + * + * ObjectCalcer's also decide how an object should be allowed to + * move. If the user selects a point, and tries to move it, then its + * ObjectCalcer will be asked whether it can move, and if so, will be + * asked to move. See below with the canMove(), move() and + * moveReferencePoint() methods.. + */ +class ObjectCalcer +{ +protected: + /** + * ObjectCalcer's are reference counted.. They all take a reference + * to their parents, and some other classes like ObjectHolder take a + * reference to some ObjectCalcer's that they don't want to see + * deleted.. + */ + friend void intrusive_ptr_add_ref( ObjectCalcer* p ); + friend void intrusive_ptr_release( ObjectCalcer* p ); + int refcount; + void ref(); + void deref(); + + // we keep track of our children, so algorithms can easily walk over + // the dependency graph.. + + std::vector<ObjectCalcer*> mchildren; + + ObjectCalcer(); +public: + /** + * a calcer should call this to register itself as a child of this + * calcer. This automatically takes a reference. + */ + void addChild( ObjectCalcer* c ); + /** + * a calcer should call this in its destructor, to inform its parent + * that it is no longer a child of this calcer. This will release + * the reference taken in addChild.. + */ + void delChild( ObjectCalcer* c ); + + // use this pointer type to keep a reference to an ObjectCalcer... + typedef myboost::intrusive_ptr<ObjectCalcer> shared_ptr; + + /** + * Returns the child ObjectCalcer's of this ObjectCalcer. + */ + std::vector<ObjectCalcer*> children() const; + + virtual ~ObjectCalcer(); + /** + * Returns the parent ObjectCalcer's of this ObjectCalcer. + */ + virtual std::vector<ObjectCalcer*> parents() const = 0; + /** + * Returns the ObjectImp of this ObjectCalcer. + */ + virtual const ObjectImp* imp() const = 0; + /** + * Makes the ObjectCalcer recalculate its ObjectImp from its + * parents. + */ + virtual void calc( const KigDocument& ) = 0; + + /** + * An ObjectCalcer expects its parents to have an ObjectImp of a + * certain type. This method returns the ObjectImpType that \p o + * should have. \p os is a list of all the parents in order, and + * \p o is part of it. This method will return the ObjectImpType + * that the parent should *at least* be. For example, a Translated + * object can translate any sort of object, so it will return + * ObjectImp::stype() here ( the topmost ObjectImpType, that all + * other ObjectImpType's inherit ). + */ + virtual const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const = 0; + + /** + * Returns whether this ObjectCalcer supports moving. + */ + virtual bool canMove() const; + /** + * Returns whether this ObjectCalcer can be translated at any position + * in the coordinate plane. Note that a ConstrainedPointType can be + * moved, but cannot be moved everywhere. + */ + virtual bool isFreelyTranslatable() const; + /** + * Moving an object most of the time signifies invoking changes in + * some of its parents. This method returns the set of parents that + * will be changed in the move() method. The object itself should + * not be included. + */ + virtual std::vector<ObjectCalcer*> movableParents() const; + /** + * In order to support simultaneously moving objects that are in + * different locations, we need for each object a location that it + * is assumed to be at, at the moment the moving starts. This + * method returns such a point. + */ + virtual Coordinate moveReferencePoint() const; + /** + * This is the method that does the real moving work. It will be + * invoked to tell the object to move to the new location to. After + * this, the calc() method will be calced, so you only need to do + * the real changes in this method, updating the ObjectImp should be + * done in the calc() method, not here. + */ + virtual void move( const Coordinate& to, const KigDocument& doc ); + + /** + * If this ObjectCalcer represents a curve, return true if the given + * point is by construction on this curve. If this ObjectCalcer + * represents a point, return true if this point is by construction + * on the given curve. + */ + virtual bool isDefinedOnOrThrough( const ObjectCalcer* o ) const = 0; +}; + +/** + * This is an ObjectCalcer that uses one of the various ObjectType's + * to calculate its ObjectImp. It basically forwards all of its + * functionality to the ObjectType that it holds a pointer to. + */ +class ObjectTypeCalcer + : public ObjectCalcer +{ + std::vector<ObjectCalcer*> mparents; + const ObjectType* mtype; + ObjectImp* mimp; +public: + typedef myboost::intrusive_ptr<ObjectTypeCalcer> shared_ptr; + /** + * Construct a new ObjectTypeCalcer with a given type and parents. + */ +// ObjectTypeCalcer( const ObjectType* type, const std::vector<ObjectCalcer*>& parents ); + /** + * the sort boolean tells whether the sortArgs method should be invoked or not; + * if not present + */ + ObjectTypeCalcer( const ObjectType* type, const std::vector<ObjectCalcer*>& parents, bool sort=true ); + ~ObjectTypeCalcer(); + + const ObjectImp* imp() const; + std::vector<ObjectCalcer*> parents() const; + void calc( const KigDocument& doc ); + + /** + * Set the parents of this ObjectTypeCalcer to np. This object will + * release the reference it had to its old parents, and take a new + * one on the new parents. + */ + void setParents( const std::vector<ObjectCalcer*> np ); + void setType( const ObjectType* t ); + + const ObjectType* type() const; + + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; + bool isDefinedOnOrThrough( const ObjectCalcer* o ) const; + bool canMove() const; + bool isFreelyTranslatable() const; + std::vector<ObjectCalcer*> movableParents() const; + Coordinate moveReferencePoint() const; + void move( const Coordinate& to, const KigDocument& doc ); +}; + +/** + * This is an ObjectCalcer that keeps an ObjectImp, and never + * calculates a new one. It is a trivial, but very useful + * ObjectCalcer. It is used often in Kig, for holding data to be used + * by other ObjectCalcer's. + */ +class ObjectConstCalcer + : public ObjectCalcer +{ + ObjectImp* mimp; +public: + typedef myboost::intrusive_ptr<ObjectConstCalcer> shared_ptr; + + /** + * Construct a new ObjectConstCalcer with the given imp as the + * stored ObjectImp. + * + * This class takes ownership of the imp you pass it, it should have + * been constructed using new, and this class is responsible for + * deleting it. + */ + ObjectConstCalcer( ObjectImp* imp ); + ~ObjectConstCalcer(); + + const ObjectImp* imp() const; + void calc( const KigDocument& doc ); + std::vector<ObjectCalcer*> parents() const; + + /** + * Set the ObjectImp of this ObjectConstCalcer to the given + * newimp. The old one will be deleted. + */ + void setImp( ObjectImp* newimp ); + /** + * Set the ObjectImp of this ObjectConstCalcer to the given + * newimp. The old one will not be deleted, but returned. + */ + ObjectImp* switchImp( ObjectImp* newimp ); + + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; + bool isDefinedOnOrThrough( const ObjectCalcer* o ) const; +}; + +/** + * This is an ObjectCalcer that has a single parent, and gets a + * certain property from it in its calc() method. + * + * \see ObjectImp::property + */ +class ObjectPropertyCalcer + : public ObjectCalcer +{ + ObjectImp* mimp; + ObjectCalcer* mparent; + int mpropid; +public: + /** + * Construct a new ObjectPropertyCalcer, that will get the property + * from parent with number propid. + */ + ObjectPropertyCalcer( ObjectCalcer* parent, int propid ); + ~ObjectPropertyCalcer(); + + const ObjectImp* imp() const; + std::vector<ObjectCalcer*> parents() const; + void calc( const KigDocument& doc ); + + int propId() const; + ObjectCalcer* parent() const; + + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; + bool isDefinedOnOrThrough( const ObjectCalcer* o ) const; +}; + +#endif diff --git a/kig/objects/object_drawer.cc b/kig/objects/object_drawer.cc new file mode 100644 index 00000000..0989d4f2 --- /dev/null +++ b/kig/objects/object_drawer.cc @@ -0,0 +1,204 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "object_drawer.h" + +#include "object_imp.h" +#include "../misc/kigpainter.h" + +#include <qpen.h> +#include <qnamespace.h> +#include <cassert> + +#include <kdebug.h> + +void ObjectDrawer::draw( const ObjectImp& imp, KigPainter& p, bool sel ) const +{ + bool nv = p.getNightVision( ); + if ( mshown || nv ) + { + p.setBrushStyle( Qt::NoBrush ); + p.setBrushColor( sel ? Qt::red : ( mshown?mcolor:Qt::gray ) ); + p.setPen( QPen ( sel ? Qt::red : ( mshown?mcolor:Qt::gray ), 1) ); + p.setWidth( mwidth ); + p.setStyle( mstyle ); + p.setPointStyle( mpointstyle ); + imp.draw( p ); + } +} + +bool ObjectDrawer::contains( const ObjectImp& imp, const Coordinate& pt, const KigWidget& w, bool nv ) const +{ + bool shownornv = mshown || nv; + return shownornv && imp.contains( pt, mwidth, w ); +} + +bool ObjectDrawer::shown( ) const +{ + return mshown; +} + +QColor ObjectDrawer::color() const +{ + return mcolor; +} + +ObjectDrawer* ObjectDrawer::getCopyShown( bool s ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = s; + ret->mwidth = mwidth; + ret->mstyle = mstyle; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyColor( const QColor& c ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = c; + ret->mshown = mshown; + ret->mwidth = mwidth; + ret->mstyle = mstyle; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyWidth( int w ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = mshown; + ret->mwidth = w; + ret->mstyle = mstyle; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyStyle( Qt::PenStyle s ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = mshown; + ret->mwidth = mwidth; + ret->mstyle = s; + ret->mpointstyle = mpointstyle; + return ret; +} + +ObjectDrawer* ObjectDrawer::getCopyPointStyle( int p ) const +{ + ObjectDrawer* ret = new ObjectDrawer; + ret->mcolor = mcolor; + ret->mshown = mshown; + ret->mwidth = mwidth; + ret->mstyle = mstyle; + ret->mpointstyle = p; + return ret; +} + +int ObjectDrawer::width() const +{ + return mwidth; +} + +Qt::PenStyle ObjectDrawer::style() const +{ + return mstyle; +} + +int ObjectDrawer::pointStyle() const +{ + return mpointstyle; +} + +ObjectDrawer::ObjectDrawer( const QColor& color, int width, bool shown, Qt::PenStyle style, int pointStyle ) + : mcolor( color ), mshown( shown ), mwidth( width ), mstyle( style ), mpointstyle( pointStyle ) +{ +} + +ObjectDrawer::ObjectDrawer() + : mcolor( Qt::blue ), mshown( true ), mwidth( -1 ), mstyle( Qt::SolidLine ), mpointstyle( 0 ) +{ +} + +bool ObjectDrawer::inRect( const ObjectImp& imp, const Rect& r, const KigWidget& w ) const +{ + return mshown && imp.inRect( r, mwidth, w ); +} + +int ObjectDrawer::pointStyleFromString( const QString& style ) +{ + if ( style == "Round" ) + return 0; + else if ( style == "RoundEmpty" ) + return 1; + else if ( style == "Rectangular" ) + return 2; + else if ( style == "RectangularEmpty" ) + return 3; + else if ( style == "Cross" ) + return 4; + return 0; +} + +QString ObjectDrawer::pointStyleToString() const +{ + if ( mpointstyle == 0 ) + return "Round"; + else if ( mpointstyle == 1 ) + return "RoundEmpty"; + else if ( mpointstyle == 2 ) + return "Rectangular"; + else if ( mpointstyle == 3 ) + return "RectangularEmpty"; + else if ( mpointstyle == 4 ) + return "Cross"; + assert( false ); + return QString::null; +} + +Qt::PenStyle ObjectDrawer::styleFromString( const QString& style ) +{ + if ( style == "SolidLine" ) + return Qt::SolidLine; + else if ( style == "DashLine" ) + return Qt::DashLine; + else if ( style == "DotLine" ) + return Qt::DotLine; + else if ( style == "DashDotLine" ) + return Qt::DashDotLine; + else if ( style == "DashDotDotLine" ) + return Qt::DashDotDotLine; + else return Qt::SolidLine; +} + +QString ObjectDrawer::styleToString() const +{ + if ( mstyle == Qt::SolidLine ) + return "SolidLine"; + else if ( mstyle == Qt::DashLine ) + return "DashLine"; + else if ( mstyle == Qt::DotLine ) + return "DotLine"; + else if ( mstyle == Qt::DashDotLine ) + return "DashDotLine"; + else if ( mstyle == Qt::DashDotDotLine ) + return "DashDotDotLine"; + return "SolidLine"; +} diff --git a/kig/objects/object_drawer.h b/kig/objects/object_drawer.h new file mode 100644 index 00000000..2781acdc --- /dev/null +++ b/kig/objects/object_drawer.h @@ -0,0 +1,146 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_DRAWER_H +#define KIG_OBJECTS_OBJECT_DRAWER_H + +#include <qcolor.h> +#include <qnamespace.h> + +class ObjectImp; +class KigPainter; +class Coordinate; +class KigWidget; +class Rect; + +/** + * A class holding some information about how a certain object is + * drawn on the window. + * + * An ObjectDrawer is used by an ObjectHolder to keep information + * about how to draw an ObjectImp on the window. It is really nothing + * more than a struct with some convenience methods. It does not have + * any virtual methods, or have any complex semantics. It keeps + * information like the thickness of an object, its color, and whether + * or not it is hidden. + * + * \note The default width of an object depends on its type. E.g. A + * point is by default drawn at width 5, a line at width 1. + * Therefore, there is a special width -1, which means "the default + * width for this object". + */ +class ObjectDrawer +{ + QColor mcolor; + bool mshown; + int mwidth; + Qt::PenStyle mstyle; + int mpointstyle; +public: + /** + * Construct a new ObjectDrawer with a default color ( Qt::blue ), + * width ( -1 ), shown state ( true ), PenStyle ( Qt::SolidLine ), + * and pointstyle ( 0 ) + */ + ObjectDrawer(); + ObjectDrawer( const QColor& color, int width = -1, bool shown = true, Qt::PenStyle = Qt::SolidLine, int pointStyle = 0 ); + /** + * Draw the object \p imp on kigpainter \p p . If \p selected is true, it is + * drawn in red, otherwise in its normal color. + */ + void draw( const ObjectImp& imp, KigPainter& p, bool selected ) const; + /** + * returns whether the object \p imp contains coordinate \p p . This is + * dependent on whether it is shown ( when it will never contain + * anything ), and on its width.. + */ + bool contains( const ObjectImp& imp, const Coordinate& pt, const KigWidget& w, bool nv = false ) const; + /** + * returns whether the object \p imp is in the rectangle \p r . This is + * dependent on whether it is shown and on its width.. + */ + bool inRect( const ObjectImp& imp, const Rect& r, const KigWidget& w ) const; + + /** + * returns whether the object this ObjectDrawer is responsible for + * will be drawn or not.. + */ + bool shown() const; + /** + * returns the color that the object will be drawn in + */ + QColor color() const; + /** + * return the width of the object + */ + int width() const; + /** + * return PenStyle for all objects except points + */ + Qt::PenStyle style() const; + /** + * return pointStyle for points + */ + int pointStyle() const; + /** + * return pointStyle trasnformed in a string + */ + QString pointStyleToString() const; + /** + * return style trasnformed in a string + */ + QString styleToString() const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the shown state is set to \p s .. + */ + ObjectDrawer* getCopyShown( bool s ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the color is set to \p c .. + */ + ObjectDrawer* getCopyColor( const QColor& c ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the width is set to \p w .. + */ + ObjectDrawer* getCopyWidth( int w ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the PenStyle state is set to \p s .. + */ + ObjectDrawer* getCopyStyle( Qt::PenStyle s ) const; + /** + * returns a new ObjectDrawer that is identical to this one.. except + * that the pointStyle state is set to \p p .. + */ + ObjectDrawer* getCopyPointStyle( int p ) const; + /** + * Note that this returns a valid point style in every case, even if + * the given \p style string is unknown. In that case it returns a + * default value. + */ + static int pointStyleFromString( const QString& style ); + /** + * Note that this returns a valid style in every case, even if the + * given \p style string is unknown. In that case it returns a default + * value. + */ + static Qt::PenStyle styleFromString( const QString& style ); +}; + +#endif diff --git a/kig/objects/object_factory.cc b/kig/objects/object_factory.cc new file mode 100644 index 00000000..a54e01f0 --- /dev/null +++ b/kig/objects/object_factory.cc @@ -0,0 +1,369 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "object_factory.h" + +#include "bogus_imp.h" +#include "curve_imp.h" +#include "intersection_types.h" +#include "line_imp.h" +#include "object_drawer.h" +#include "object_holder.h" +#include "other_type.h" +#include "point_imp.h" +#include "point_type.h" +#include "text_type.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" +#include "../misc/calcpaths.h" +#include "../misc/coordinate.h" +#include "../misc/object_hierarchy.h" + +#include <algorithm> +#include <functional> + +ObjectHolder* ObjectFactory::fixedPoint( const Coordinate& c ) const +{ + ObjectHolder* o = new ObjectHolder( fixedPointCalcer( c ) ); + return o; +} + +ObjectTypeCalcer* ObjectFactory::fixedPointCalcer( const Coordinate& c ) const +{ + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectConstCalcer( new DoubleImp( c.x ) ) ); + args.push_back( new ObjectConstCalcer( new DoubleImp( c.y ) ) ); + ObjectTypeCalcer* oc = new ObjectTypeCalcer( FixedPointType::instance(), args ); + return oc; +} + +ObjectTypeCalcer* ObjectFactory::cursorPointCalcer( const Coordinate& c ) const +{ + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectConstCalcer( new DoubleImp( c.x ) ) ); + args.push_back( new ObjectConstCalcer( new DoubleImp( c.y ) ) ); + ObjectTypeCalcer* oc = new ObjectTypeCalcer( CursorPointType::instance(), args ); + return oc; +} + +const ObjectFactory* ObjectFactory::instance() +{ + static ObjectFactory f; + return &f; +} + +ObjectTypeCalcer* ObjectFactory::sensiblePointCalcer( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const +{ + std::vector<ObjectHolder*> os = d.whatAmIOn( c, w ); + if ( os.size() == 2 ) + { + // we can calc intersection point *olny* between two objects... + std::vector<ObjectCalcer*> args; + args.push_back( os[0]->calcer() ); + args.push_back( os[1]->calcer() ); + // the simpliest case: two lines... + if ( ( os[0]->imp()->inherits( AbstractLineImp::stype() ) ) && + ( os[1]->imp()->inherits( AbstractLineImp::stype() ) ) ) + return new ObjectTypeCalcer( LineLineIntersectionType::instance(), args ); + // other cases will follow... + } + for ( std::vector<ObjectHolder*>::iterator i = os.begin(); i != os.end(); ++i ) + if ( (*i)->imp()->inherits( CurveImp::stype() ) ) + return constrainedPointCalcer( (*i)->calcer(), c, d ); + return fixedPointCalcer( c ); +} + +ObjectHolder* ObjectFactory::sensiblePoint( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const +{ + return new ObjectHolder( sensiblePointCalcer( c, d, w ) ); +} + +ObjectTypeCalcer* ObjectFactory::relativePointCalcer( + ObjectCalcer* o, const Coordinate& loc ) const +{ + Coordinate reference = + static_cast<const ObjectImp*>( o->imp() )->attachPoint(); + assert( reference.valid() ); + + double x = 0.0; + double y = 0.0; + if ( loc.valid() ) + { + x = loc.x - reference.x; + y = loc.y - reference.y; + } + std::vector<ObjectCalcer*> parents; + parents.push_back( new ObjectConstCalcer( new DoubleImp( x ) ) ); + parents.push_back( new ObjectConstCalcer( new DoubleImp( y ) ) ); + parents.push_back( o ); + return new ObjectTypeCalcer( RelativePointType::instance(), parents ); +} + +ObjectTypeCalcer* ObjectFactory::constrainedPointCalcer( + ObjectCalcer* curve, double param ) const +{ + assert( curve->imp()->inherits( CurveImp::stype() ) ); + std::vector<ObjectCalcer*> parents; + parents.push_back( new ObjectConstCalcer( new DoubleImp( param ) ) ); + parents.push_back( curve ); + return new ObjectTypeCalcer( ConstrainedPointType::instance(), parents ); +} + +ObjectHolder* ObjectFactory::constrainedPoint( + ObjectCalcer* curve, double param ) const +{ + return new ObjectHolder( constrainedPointCalcer( curve, param ) ); +} + +ObjectTypeCalcer* ObjectFactory::constrainedPointCalcer( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& d ) const +{ + assert( curve->imp()->inherits( CurveImp::stype() ) ); + double param = static_cast<const CurveImp*>( curve->imp() )->getParam( c, d ); + return constrainedPointCalcer( curve, param ); +} + +ObjectHolder* ObjectFactory::constrainedPoint( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& d ) const +{ + return new ObjectHolder( constrainedPointCalcer( curve, c, d ) ); +} + +ObjectTypeCalcer* ObjectFactory::locusCalcer( + ObjectCalcer* a, ObjectCalcer* b ) const +{ + assert( dynamic_cast<const ObjectTypeCalcer*>( a ) ); + ObjectTypeCalcer* constrained = static_cast<ObjectTypeCalcer*>( a ); + assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) ); + assert( constrained->parents().size() == 2 ); + ObjectCalcer* curve = a->parents().back(); + + const ObjectCalcer* moving = b; + + std::vector<ObjectCalcer*> hierparents; + hierparents.push_back( constrained ); + std::vector<ObjectCalcer*> sideOfTree = sideOfTreePath( hierparents, moving ); + std::copy( sideOfTree.begin(), sideOfTree.end(), std::back_inserter( hierparents ) ); + + ObjectHierarchy hier( hierparents, moving ); + + std::vector<ObjectCalcer*> realparents( 2 + sideOfTree.size(), 0 ); + realparents[0] = new ObjectConstCalcer( new HierarchyImp( hier ) ); + realparents[1] = curve; + copy( sideOfTree.begin(), sideOfTree.end(), realparents.begin() + 2 ); + + return new ObjectTypeCalcer( LocusType::instance(), realparents ); +} + +ObjectHolder* ObjectFactory::locus( ObjectCalcer* a, ObjectCalcer* b ) const +{ + return new ObjectHolder( locusCalcer( a, b ) ); +} + +ObjectHolder* ObjectFactory::label( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + return new ObjectHolder( labelCalcer( s, loc, needframe, parents, doc ) ); +} + +ObjectTypeCalcer* ObjectFactory::labelCalcer( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + return attachedLabelCalcer( s, 0, loc, needframe, parents, doc ); +} + +ObjectTypeCalcer* ObjectFactory::attachedLabelCalcer( + const QString& s, ObjectCalcer* p, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& nparents, + const KigDocument& doc ) const +{ + std::vector<ObjectCalcer*> parents; + parents.reserve( nparents.size() + 3 ); + parents.push_back( new ObjectConstCalcer( new IntImp( needframe ? 1 : 0 ) ) ); + + parents.push_back( getAttachPoint( p, loc, doc ) ); + + parents.push_back( new ObjectConstCalcer( new StringImp( s ) ) ); + std::copy( nparents.begin(), nparents.end(), std::back_inserter( parents ) ); + ObjectTypeCalcer* ret = new ObjectTypeCalcer( TextType::instance(), parents ); + ret->calc( doc ); + return ret; +} + +ObjectCalcer* ObjectFactory::getAttachPoint( + ObjectCalcer* p, + const Coordinate& loc, + const KigDocument& doc ) const +{ +/* + * mp: (changes to add relative-attachment). Now an object is tested + * as follows: + * - if attachPoint() returns a valid coordinate, then we use the new method + * - if it is a point: 'old-style' treatment (we can change this shortly) + * - if it is a curve: 'old-style' treatment (we could use the new approach, + * which can be better/worse depending on personal taste, I think) + * + * the first condition that matches determines the behaviour. + * the new method works similarly to the curve case, but we generate a new + * RelativePointType instead of a ConstrainedPointType; this will in turn make use + * of the new attachPoint() method for objects. + * + * changed the preference order 2005/01/21 (now attachPoint has preference over points) + * + * NOTE: changes in the tests performed should be matched also in + * modes/popup.cc (addNameLabel) and in label.cc (TextLabelModeBase::mouseMoved) + */ + + if ( p && p->imp()->attachPoint().valid() ) + { + ObjectCalcer* o = relativePointCalcer( p, loc ); + o->calc( doc ); + return o; + } + else if ( p && p->imp()->inherits( PointImp::stype() ) ) + { + return p; + } + else if ( p && p->imp()->inherits( CurveImp::stype() ) ) + { + double param = 0.5; + if ( loc.valid() ) + param = static_cast<const CurveImp*>( p->imp() )->getParam( loc, doc ); + + ObjectCalcer* o = constrainedPointCalcer( p, param ); + o->calc( doc ); + return o; + } + else + { + if ( loc.valid() ) + return new ObjectConstCalcer( new PointImp( loc ) ); + else + return new ObjectConstCalcer( new PointImp( Coordinate( 0, 0 ) ) ); + } +} + +ObjectHolder* ObjectFactory::attachedLabel( + const QString& s, ObjectCalcer* locationparent, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const +{ + return new ObjectHolder( attachedLabelCalcer( s, locationparent, loc, needframe, parents, doc ) ); +} + +ObjectPropertyCalcer* ObjectFactory::propertyObjectCalcer( + ObjectCalcer* o, const char* p ) const +{ + int wp = o->imp()->propertiesInternalNames().findIndex( p ); + if ( wp == -1 ) return 0; + return new ObjectPropertyCalcer( o, wp ); +} + +ObjectHolder* ObjectFactory::propertyObject( + ObjectCalcer* o, const char* p ) const +{ + return new ObjectHolder( propertyObjectCalcer( o, p ) ); +} + +void ObjectFactory::redefinePoint( + ObjectTypeCalcer* point, const Coordinate& c, + KigDocument& doc, const KigWidget& w ) const +{ + std::vector<ObjectHolder*> hos = doc.whatAmIOn( c, w ); + std::vector<ObjectCalcer*> os; + ObjectCalcer* (ObjectHolder::*calcmeth)() = &ObjectHolder::calcer; + std::transform( hos.begin(), hos.end(), std::back_inserter( os ), + std::mem_fun( calcmeth ) ); + ObjectCalcer* v = 0; + + // we don't want one of our children as a parent... + std::set<ObjectCalcer*> children = getAllChildren( point ); + for ( std::vector<ObjectCalcer*>::iterator i = os.begin(); + i != os.end(); ++i ) + if ( (*i)->imp()->inherits( CurveImp::stype() ) && + children.find( *i ) == children.end() ) + { + v = *i; + break; + }; + + if ( v ) + { + // we want a constrained point... + const CurveImp* curveimp = static_cast<const CurveImp*>( v->imp() ); + double newparam = curveimp->getParam( c, doc ); + + if ( point->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + // point already was constrained -> simply update the param + // DataObject and make sure point is on the right curve... + ObjectCalcer* dataobj = 0; + std::vector<ObjectCalcer*> parents = point->parents(); + assert( parents.size() == 2 ); + assert ( parents[0]->imp()->inherits( DoubleImp::stype() ) ); + dataobj = parents[0]; + + parents.clear(); + parents.push_back( dataobj ); + parents.push_back( v ); + point->setParents( parents ); + + assert( dynamic_cast<ObjectConstCalcer*>( dataobj ) ); + static_cast<ObjectConstCalcer*>( dataobj )->setImp( + new DoubleImp( newparam ) ); + } + else + { + // point used to be fixed -> add a new DataObject etc. + std::vector<ObjectCalcer*> args; + args.push_back( new ObjectConstCalcer( new DoubleImp( newparam ) ) ); + args.push_back( v ); + point->setType( ConstrainedPointType::instance() ); + point->setParents( args ); + } + } + else + { + // a fixed point... + if ( point->type()->inherits( ObjectType::ID_ConstrainedPointType ) ) + { + // point used to be constrained.. + std::vector<ObjectCalcer*> a; + a.push_back( new ObjectConstCalcer( new DoubleImp( c.x ) ) ); + a.push_back( new ObjectConstCalcer( new DoubleImp( c.y ) ) ); + + point->setType( FixedPointType::instance() ); + point->setParents( a ); + } + else + { + // point used to be fixed -> simply update the DataObject's + // we can use the point's move function for that.. + point->move( c, doc ); + }; + } +} + diff --git a/kig/objects/object_factory.h b/kig/objects/object_factory.h new file mode 100644 index 00000000..0ce6ce62 --- /dev/null +++ b/kig/objects/object_factory.h @@ -0,0 +1,145 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_FACTORY_H +#define KIG_OBJECTS_OBJECT_FACTORY_H + +#include "common.h" + +class ObjectFactory +{ +public: + + static const ObjectFactory* instance(); + + /** + * this returns a fixed point. Note that the returned object is + * not added to the document.. + */ + ObjectHolder* fixedPoint( const Coordinate& c ) const; + ObjectTypeCalcer* fixedPointCalcer( const Coordinate& c ) const; + + /** + * this returns a CursorPointType; this is used during special + * constructions (e.g. regular polygons) where the constructor + * wants to use the cursor position without actually generating + * an object depending on a new point there. + */ + ObjectTypeCalcer* cursorPointCalcer( const Coordinate& c ) const; + + /** + * this returns a relative point (to an object). Note that the returned object + * is not added to the document.. + */ + ObjectTypeCalcer* relativePointCalcer( ObjectCalcer* o, const Coordinate& loc ) const; + + /** + * this returns a constrained point. Note that the returned object + * is not added to the document.. + */ + ObjectHolder* constrainedPoint( ObjectCalcer* curve, double param ) const; + ObjectTypeCalcer* constrainedPointCalcer( ObjectCalcer* curve, double param ) const; + /** + * \overload, changes nothing to the semantics, only calcs the param + * value for you.. + */ + ObjectTypeCalcer* constrainedPointCalcer( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& ) const; + ObjectHolder* constrainedPoint( + ObjectCalcer* curve, const Coordinate& c, const KigDocument& ) const; + + /** + * this returns a "sensible point". + * By a "sensible point", I mean a point that would be about what + * the user expects when he asks for a point at point \p c . This is a + * constrained point if \p c is on a curve, and otherwise a fixed + * point. I might add the possibility for an intersection point + * sometime.. Note that the returned object is not added to + * the document.. + */ + ObjectTypeCalcer* sensiblePointCalcer( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const; + ObjectHolder* sensiblePoint( + const Coordinate& c, const KigDocument& d, const KigWidget& w ) const; + + /** + * set point to what sensiblePoint would have returned.. + */ + void redefinePoint( ObjectTypeCalcer* point, const Coordinate& c, + KigDocument& d, const KigWidget& w ) const; + + /** + * return a locus, defined by the two points ( one constrained, and + * one following ) \p a and \p b . \p a should be the constrained point, + * and thus, it has to be of type ObjectTypeCalcer where a->type() is of + * type ConstrainedPointType. The semantics of LocusType are a bit + * weird ( but I believe correct :) ), so this function takes care + * of the complication there.. + */ + ObjectTypeCalcer* locusCalcer( ObjectCalcer* a, ObjectCalcer* b ) const; + ObjectHolder* locus( ObjectCalcer* a, ObjectCalcer* b ) const; + + /** + * returns a label with text \p s at point \p c .. It ( and its parents ) + * is calced already... + */ + ObjectHolder* label( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + ObjectTypeCalcer* labelCalcer( + const QString& s, const Coordinate& loc, + bool needframe, const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + + /** + * this one does the same as the above, only that the new label is + * attached to locationparent if it is non-null.. + */ + ObjectTypeCalcer* attachedLabelCalcer( + const QString& s, ObjectCalcer* locationparent, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + /** + * this has been added because it comes handy when redefining + * a text label, we move here all the code for getting an + * attach point from the method above + */ + ObjectCalcer* getAttachPoint( + ObjectCalcer* locationparent, + const Coordinate& loc, + const KigDocument& doc ) const; + ObjectHolder* attachedLabel( + const QString& s, ObjectCalcer* locationparent, + const Coordinate& loc, bool needframe, + const std::vector<ObjectCalcer*>& parents, + const KigDocument& doc ) const; + + /** + * returns a property object for the property \p p of object \p o . + * + * \note + * \p o should have already been calc'd, or this will fail and + * return 0.. The returned object also needs to be calced after + * this.. + */ + ObjectPropertyCalcer* propertyObjectCalcer( ObjectCalcer* o, const char* p ) const; + ObjectHolder* propertyObject( ObjectCalcer* o, const char* p ) const; +}; + +#endif diff --git a/kig/objects/object_holder.cc b/kig/objects/object_holder.cc new file mode 100644 index 00000000..5a2c0765 --- /dev/null +++ b/kig/objects/object_holder.cc @@ -0,0 +1,164 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "object_holder.h" + +#include "bogus_imp.h" +#include "object_calcer.h" +#include "object_drawer.h" + +#include "../misc/coordinate.h" + +ObjectHolder::ObjectHolder( ObjectCalcer* calcer ) + : mcalcer( calcer ), mdrawer( new ObjectDrawer ), mnamecalcer( 0 ) +{ +} + +ObjectHolder::ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer, + ObjectConstCalcer* namecalcer ) + : mcalcer( calcer ), mdrawer( drawer ), mnamecalcer( namecalcer ) +{ + assert( !namecalcer || namecalcer->imp()->inherits( StringImp::stype() ) ); +} + +ObjectHolder::ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer ) + : mcalcer( calcer ), mdrawer( drawer ), mnamecalcer( 0 ) +{ +} + +ObjectHolder::~ObjectHolder() +{ + delete mdrawer; +} + +const ObjectImp* ObjectHolder::imp() const +{ + return mcalcer->imp(); +} + +const ObjectCalcer* ObjectHolder::calcer() const +{ + return mcalcer.get(); +} + +const ObjectDrawer* ObjectHolder::drawer() const +{ + return mdrawer; +} + +const ObjectConstCalcer* ObjectHolder::nameCalcer() const +{ + return mnamecalcer.get(); +} + +void ObjectHolder::setDrawer( ObjectDrawer* d ) +{ + delete switchDrawer( d ); +} + +void ObjectHolder::calc( const KigDocument& d ) +{ + mcalcer->calc( d ); +} + +void ObjectHolder::draw( KigPainter& p, bool selected ) const +{ + mdrawer->draw( *imp(), p, selected ); +} + +bool ObjectHolder::contains( const Coordinate& pt, const KigWidget& w, bool nv ) const +{ + return mdrawer->contains( *imp(), pt, w, nv ); +} + +bool ObjectHolder::inRect( const Rect& r, const KigWidget& w ) const +{ + return mdrawer->inRect( *imp(), r, w ); +} + +ObjectCalcer* ObjectHolder::calcer() +{ + return mcalcer.get(); +} + +ObjectDrawer* ObjectHolder::drawer() +{ + return mdrawer; +} + +ObjectConstCalcer* ObjectHolder::nameCalcer() +{ + return mnamecalcer.get(); +} + +const Coordinate ObjectHolder::moveReferencePoint() const +{ + return mcalcer->moveReferencePoint(); +} + +void ObjectHolder::move( const Coordinate& to, const KigDocument& doc ) +{ + mcalcer->move( to, doc ); +} + +bool ObjectHolder::canMove() const +{ + return mcalcer->canMove(); +} + +bool ObjectHolder::isFreelyTranslatable() const +{ + return mcalcer->isFreelyTranslatable(); +} + +ObjectDrawer* ObjectHolder::switchDrawer( ObjectDrawer* d ) +{ + ObjectDrawer* tmp = mdrawer; + mdrawer = d; + return tmp; +} + +bool ObjectHolder::shown( ) const +{ + return mdrawer->shown( ); +} + +const QString ObjectHolder::name() const +{ + if ( mnamecalcer ) + { + assert( mnamecalcer->imp()->inherits( StringImp::stype() ) ); + return static_cast<const StringImp*>( mnamecalcer->imp() )->data(); + } + else + return QString::null; +} + +void ObjectHolder::setNameCalcer( ObjectConstCalcer* namecalcer ) +{ + assert( !mnamecalcer ); + mnamecalcer = namecalcer; +} + +QString ObjectHolder::selectStatement() const +{ + const QString n = name(); + if ( n.isEmpty() ) + return i18n( imp()->type()->selectStatement() ); + else + return i18n( imp()->type()->selectNameStatement() ).arg( n ); +} diff --git a/kig/objects/object_holder.h b/kig/objects/object_holder.h new file mode 100644 index 00000000..9b30453d --- /dev/null +++ b/kig/objects/object_holder.h @@ -0,0 +1,145 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_HOLDER_H +#define KIG_OBJECTS_OBJECT_HOLDER_H + +#include "object_calcer.h" + +#include <qstring.h> + +/** + * An ObjectHolder represents an object as it is known to the + * document. It keeps a pointer to an ObjectCalcer, where it gets its + * data ( the ObjectImp that the ObjectCalcer holds ) from. It also + * holds information about how to draw this ObjectImp on the window, + * by keeping a pointer to an ObjectDrawer ( see below ). In its draw + * method, it gets the ObjectImp from the ObjectCalcer, and passes it + * to the ObjectDrawer, asking it to draw the ObjectImp on the window. + * + * The document ( check the KigDocument class ) holds a list of these + * ObjectHolder's. This is its only link with the ObjectCalcer + * dependency graph. + * + * An ObjectHolder keeps a reference to its * ObjectCalcer. + */ +class ObjectHolder +{ + ObjectCalcer::shared_ptr mcalcer; + ObjectDrawer* mdrawer; + ObjectConstCalcer::shared_ptr mnamecalcer; + +public: + /** + * Construct a new ObjectHolder with a given ObjectCalcer and + * ObjectDrawer, with a given name calcer. + */ + ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer, ObjectConstCalcer* namecalcer ); + /** + * Construct a new ObjectHolder with a given ObjectCalcer and + * ObjectDrawer. + */ + ObjectHolder( ObjectCalcer* calcer, ObjectDrawer* drawer ); + /** + * equivalent to the previous constructor, but with a default + * ObjectDrawer and no name. + */ + ObjectHolder( ObjectCalcer* calcer ); + ~ObjectHolder(); + + const ObjectImp* imp() const; + const ObjectCalcer* calcer() const; + ObjectCalcer* calcer(); + const ObjectDrawer* drawer() const; + ObjectDrawer* drawer(); + // the following two return zero if no name is set. + const ObjectConstCalcer* nameCalcer() const; + ObjectConstCalcer* nameCalcer(); + /** + * Setting the namecalcer is only allowed if previously none was + * set. This way, we avoid keeping a useless namecalcer around if + * no name is set. + */ + void setNameCalcer( ObjectConstCalcer* namecalcer ); + + /** + * returns QString::null if no name is set. + */ + const QString name() const; + /** + * Set the ObjectDrawer of this ObjectHolder to \p d , the old + * ObjectDrawer is deleted. + */ + void setDrawer( ObjectDrawer* d ); + /** + * Set the ObjectDrawer of this ObjectHolder to \p d , the old + * ObjectDrawer is not deleted, but returned. + */ + ObjectDrawer* switchDrawer( ObjectDrawer* d ); + + /** + * Make our ObjectCalcer recalculate its ObjectImp. + */ + void calc( const KigDocument& ); + /** + * Draw this object on the given KigPainter. If \p selected is true, + * then it will be drawn in red, instead of its normal color. + */ + void draw( KigPainter& p, bool selected ) const; + /** + * Returns whether this object contains the point \p p . + */ + bool contains( const Coordinate& p, const KigWidget& w, bool nv = false ) const; + /** + * Returns whether this object is in the rectangle \p r . + */ + bool inRect( const Rect& r, const KigWidget& w ) const; + /** + * Returns whether this object is shown. + */ + bool shown() const; + + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::moveReferencePoint() for more info. + */ + const Coordinate moveReferencePoint() const; + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::move() for more info. + */ + void move( const Coordinate& to, const KigDocument& ); + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::canMove() for more info. + */ + bool canMove() const; + /** + * This call is simply forwarded to the ObjectCalcer. Check the + * documentation of ObjectCalcer::isFreelyTranslatable() for more info. + */ + bool isFreelyTranslatable() const; + + /** + * Return a statement saying something like "select this segment" or + * "select segment ab" depending on whether this ObjectHolder has a + * name. + */ + QString selectStatement() const; +}; + +#endif diff --git a/kig/objects/object_imp.cc b/kig/objects/object_imp.cc new file mode 100644 index 00000000..6cb6650f --- /dev/null +++ b/kig/objects/object_imp.cc @@ -0,0 +1,308 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "object_imp.h" + +#include "bogus_imp.h" + +#include "../misc/coordinate.h" + +#include <klocale.h> +#include <map> + +class ObjectImpType::StaticPrivate +{ +public: + std::map<QCString, const ObjectImpType*> namemap; +}; + +ObjectImp::ObjectImp() +{ +} + +ObjectImp::~ObjectImp() +{ +} + +bool ObjectImp::valid() const +{ + return ! type()->inherits( InvalidImp::stype() ); +} + +void ObjectImp::fillInNextEscape( QString&, const KigDocument& ) const +{ + assert( false ); +} + +const QCStringList ObjectImp::properties() const +{ + QCStringList ret; + ret << I18N_NOOP( "Object Type" ); + return ret; +} + +const uint ObjectImp::numberOfProperties() const +{ + return 1; +} + +const QCStringList ObjectImp::propertiesInternalNames() const +{ + QCStringList ret; + ret << "base-object-type"; + return ret; +} + +ObjectImp* ObjectImp::property( uint i, const KigDocument& ) const +{ + if ( i == 0 ) return new StringImp( type()->translatedName() ); + return new InvalidImp; +} + +const ObjectImpType* ObjectImp::impRequirementForProperty( uint ) const +{ + return ObjectImp::stype(); +} + +void ObjectImpVisitor::visit( const ObjectImp* imp ) +{ + imp->visit( this ); +} + +void ObjectImpVisitor::visit( const IntImp* ) +{ +} + +void ObjectImpVisitor::visit( const DoubleImp* ) +{ +} + +void ObjectImpVisitor::visit( const StringImp* ) +{ +} + +void ObjectImpVisitor::visit( const InvalidImp* ) +{ +} + +void ObjectImpVisitor::visit( const HierarchyImp* ) +{ +} + +void ObjectImpVisitor::visit( const LineImp* ) +{ +} + +void ObjectImpVisitor::visit( const PointImp* ) +{ +} + +void ObjectImpVisitor::visit( const TextImp* ) +{ +} + +void ObjectImpVisitor::visit( const AngleImp* ) +{ +} + +void ObjectImpVisitor::visit( const VectorImp* ) +{ +} + +void ObjectImpVisitor::visit( const LocusImp* ) +{ +} + +void ObjectImpVisitor::visit( const CircleImp* ) +{ +} + +void ObjectImpVisitor::visit( const ConicImp* ) +{ +} + +void ObjectImpVisitor::visit( const CubicImp* ) +{ +} + +void ObjectImpVisitor::visit( const SegmentImp* ) +{ +} + +void ObjectImpVisitor::visit( const RayImp* ) +{ +} + +void ObjectImpVisitor::visit( const ArcImp* ) +{ +} + +void ObjectImpVisitor::visit( const PolygonImp* ) +{ +} + +ObjectImpVisitor::~ObjectImpVisitor() +{ + +} + +void ObjectImpVisitor::visit( const TransformationImp* ) +{ +} + +void ObjectImpVisitor::visit( const TestResultImp* ) +{ +} + +const char* ObjectImp::iconForProperty( uint ) const +{ + return "kig_text"; +} + +bool ObjectImp::canFillInNextEscape() const +{ + return false; +} + +ObjectImpType::ObjectImpType( const ObjectImpType* parent, + const char* internalname, + const char* translatedname, + const char* selectstatement, + const char* selectnamestatement, + const char* removeastatement, + const char* addastatement, + const char* moveastatement, + const char* attachtothisstatement, + const char* showastatement, + const char* hideastatement ) + : mparent( parent ), minternalname( internalname ), + mtranslatedname( translatedname ), mselectstatement( selectstatement ), + mselectnamestatement( selectnamestatement ), + mremoveastatement( removeastatement ), maddastatement( addastatement ), + mmoveastatement( moveastatement ), + mattachtothisstatement( attachtothisstatement ), + mshowastatement( showastatement ), + mhideastatement( hideastatement ) +{ + sd()->namemap[minternalname] = this; +} + +ObjectImpType::~ObjectImpType() +{ +} + +bool ObjectImpType::inherits( const ObjectImpType* t ) const +{ + return t == this || (mparent && mparent->inherits( t ) ); +} + +const char* ObjectImpType::internalName() const +{ + return minternalname; +} + +QString ObjectImpType::translatedName() const +{ + return i18n( mtranslatedname ); +} + +const char* ObjectImpType::selectStatement() const +{ + return mselectstatement; +} + +const char* ObjectImpType::selectNameStatement() const +{ + return mselectnamestatement; +} + +QString ObjectImpType::removeAStatement() const +{ + return i18n( mremoveastatement ); +} + +QString ObjectImpType::addAStatement() const +{ + return i18n( maddastatement ); +} + +QString ObjectImpType::moveAStatement() const +{ + return i18n( mmoveastatement ); +} + +const ObjectImpType* ObjectImpType::typeFromInternalName( const char* string ) +{ + QCString s( string ); + std::map<QCString, const ObjectImpType*>::iterator i = sd()->namemap.find( s ); + if ( i == sd()->namemap.end() ) + return 0; + else return i->second; +} + +bool ObjectImp::inherits( const ObjectImpType* t ) const +{ + return type()->inherits( t ); +} + +const ObjectImpType* ObjectImp::stype() +{ + static const ObjectImpType t( + 0, "any", + I18N_NOOP( "Object" ), + I18N_NOOP( "Select this object" ), + I18N_NOOP( "Select object %1" ), + I18N_NOOP( "Remove an object" ), + I18N_NOOP( "Add an object" ), + I18N_NOOP( "Move an object" ), + I18N_NOOP( "Attach to this object" ), + I18N_NOOP( "Show an object" ), + I18N_NOOP( "Hide an object" ) ); + return &t; +} + +ObjectImpType::StaticPrivate* ObjectImpType::sd() +{ + static StaticPrivate d; + return &d; +} + +bool ObjectImp::isCache() const +{ + return false; +} + +QString ObjectImpType::attachToThisStatement() const +{ + return i18n( mattachtothisstatement ); +} + +QString ObjectImpType::showAStatement() const +{ + return i18n( mshowastatement ); +} + +QString ObjectImpType::hideAStatement() const +{ + return i18n( mhideastatement ); +} + +bool ObjectImp::isPropertyDefinedOnOrThroughThisImp( uint ) const +{ + return false; +} + diff --git a/kig/objects/object_imp.h b/kig/objects/object_imp.h new file mode 100644 index 00000000..2c032f99 --- /dev/null +++ b/kig/objects/object_imp.h @@ -0,0 +1,360 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_IMP_H +#define KIG_OBJECTS_OBJECT_IMP_H + +#include "common.h" + +class IntImp; +class DoubleImp; +class StringImp; +class InvalidImp; +class HierarchyImp; +class TransformationImp; +class TestResultImp; +class CurveImp; +class LineImp; +class PointImp; +class TextImp; +class AngleImp; +class VectorImp; +class LocusImp; +class CircleImp; +class ConicImp; +class CubicImp; +class SegmentImp; +class RayImp; +class ArcImp; +class PolygonImp; + +/** + * \internal This is some OO magic commonly referred to as "double + * dispatch". If you need to do some action on an ObjectImp, and you + * need to do something different dependent on the type of o, then + * make a Visitor class that inherits this interface, and implements + * the appropriate functions properly, and call "o->visit( my_visitor + * );". + */ +class ObjectImpVisitor +{ +public: + virtual ~ObjectImpVisitor(); + void visit( const ObjectImp* imp ); + virtual void visit( const IntImp* imp ); + virtual void visit( const DoubleImp* imp ); + virtual void visit( const StringImp* imp ); + virtual void visit( const InvalidImp* imp ); + virtual void visit( const HierarchyImp* imp ); + virtual void visit( const TransformationImp* imp ); + virtual void visit( const TestResultImp* imp ); + virtual void visit( const LineImp* imp ); + virtual void visit( const PointImp* imp ); + virtual void visit( const TextImp* imp ); + virtual void visit( const AngleImp* imp ); + virtual void visit( const VectorImp* imp ); + virtual void visit( const LocusImp* imp ); + virtual void visit( const CircleImp* imp ); + virtual void visit( const ConicImp* imp ); + virtual void visit( const CubicImp* imp ); + virtual void visit( const SegmentImp* imp ); + virtual void visit( const RayImp* imp ); + virtual void visit( const ArcImp* imp ); + virtual void visit( const PolygonImp* imp ); +}; + +typedef unsigned int uint; + +/** + * Instances of this class represent a certain ObjectImp type. Every + * ObjectImp type has a static ObjectImpType member, that it returns a + * reference to in its type() function.. Think of it as a nice enum, + * that you can also get some data from.. + */ +class ObjectImpType +{ + const ObjectImpType* mparent; + const char* minternalname; + const char* mtranslatedname; + const char* mselectstatement; + const char* mselectnamestatement; + const char* mremoveastatement; + const char* maddastatement; + const char* mmoveastatement; + const char* mattachtothisstatement; + const char* mshowastatement; + const char* mhideastatement; + class StaticPrivate; + static StaticPrivate* sd(); +public: + /** + * Returns the type with name n. + * + * \internal Do *not* call this from functions that can be called at + * static initializer time ! It depends on information that is only + * available after that stage and will crash if used too early.. + */ + static const ObjectImpType* typeFromInternalName( const char* n ); + + /** + * \internal Construct an ObjectImpType, with a lot of data about + * your ObjectImp type. + * + * translatedname is a translatable string like "segment" + * selectstatement is a translatable string like "Select this segment" + * selectnamestatement is a translatable string like "Select segment %1" + * removeastatement is a translatable string like "Remove a Segment" + * addastatement is a translatable string like "Add a Segment" + * moveastatement is a translatable string like "Move a Segment" + * attachtothisstatement is a translatable string like "Attach to + * this segment" + * showastatement is a translatable string like "Show a Segment" + * hideastatement is a translatable string like "Hide a Segment" + * + * All translatable strings should have + * I18N_NOOP around them ! @param parent is the ObjectImpType of + * your parent ObjectImp type. Never give 0 as parent, except for + * the top ObjectImp ObjectImpType.. + */ + ObjectImpType( + const ObjectImpType* parent, const char* internalname, + const char* translatedname, + const char* selectstatement, + const char* selectnamestatement, + const char* removeastatement, + const char* addastatement, + const char* moveastatement, + const char* attachtothisstatement, + const char* showastatement, + const char* hideastatement ); + ~ObjectImpType(); + + /** + * Does the ObjectImp type represented by this instance inherit the + * ObjectImp type represented by t ? + */ + bool inherits( const ObjectImpType* t ) const; + + /** + * Returns an internal name for this ObjectImp type. This name is + * guaranteed unique, and mostly corresponds with the class name of + * the corresponding ObjectImp.. + */ + const char* internalName() const; + /** + * The name of this type, translated to the currently used language. + */ + QString translatedName() const; + /** + * Returns a translatable string of the form "Select this %1". + * E.g. "Select this segment". Note that users of this function + * should use i18n on the returned string before using it. + */ + const char* selectStatement() const; + + /** + * Returns a translatable string of the form "Select point %1". %1 + * will be filled in by whomever calls this function with the name + * of the object in question. This function should be used as + * follows: i18n( x->selectNameStatement() ).arg( xname ). + */ + const char* selectNameStatement() const; + + /** + * Returns a translated string of the form "Remove a xxx". + * E.g. "Remove a Segment". + */ + QString removeAStatement() const; + /** + * Returns a translated string of the form "Add a xxx". + * E.g. "Add a Segment". + */ + QString addAStatement() const; + /** + * Returns a translated string of the form "Move a xxx". + * E.g. "Move a Segment". + */ + QString moveAStatement() const; + /** + * Returns a translated string of the form "Attach to this xxx". + * E.g. "Attach to this segment". + * \internal This is used by the text label construction mode + */ + QString attachToThisStatement() const; + + /** + * Returns a translated string of the form "Show a xxx". + * E.g. "Show a Segment". + */ + QString showAStatement() const; + + /** + * Returns a translated string of the form "Hide a xxx". + * E.g. "Hide a Segment". + */ + QString hideAStatement() const; +}; + +/** + * The ObjectImp class represents the behaviour of an object after it + * is calculated. This means how to draw() it, whether it claims to + * contain a certain point etc. It is also the class where the + * ObjectType's get their information from.. + */ +class ObjectImp +{ +protected: + ObjectImp(); +public: + /** + * The ObjectImpType representing the base ObjectImp class. All + * other ObjectImp's inherit from this type. + */ + static const ObjectImpType* stype(); + + virtual ~ObjectImp(); + + /** + * Returns true if this ObjectImp inherits the ObjectImp type + * represented by t. + * E.g. you can check whether an ObjectImp is a LineImp by doing: + * \if creating-python-scripting-doc + * \code + * if object.inherits( LineImp.stype() ): + * \endcode + * \else + * \code + * if( object.inherits( LineImp::stype() ) + * \endcode + * \endif + */ + bool inherits( const ObjectImpType* t ) const; + + /** + * Returns a reference point where to attach labels; when this + * returns an invalidCoord then the attachment is either not + * done at all, or done in a specific way (like for curves, + * or for points) The treatment of points could also take + * advantage of this attachment mechanism. + * + * If this method returns a valid Coordinate, then this is + * interpreted as a pivot point for the label, which can still + * be moved relative to that point, but follows the object when + * the object changes. + * In practice a new RelativePointType is created (position of + * the string), this type in turn depends on the object (to get + * its attachPoint) and two DoubleImp that are interpreted as + * relative displacement (x and y) + */ + virtual Coordinate attachPoint( ) const = 0; + + /** + * Return this ObjectImp, transformed by the transformation t. + */ + virtual ObjectImp* transform( const Transformation& t ) const = 0; + + virtual void draw( KigPainter& p ) const = 0; + virtual bool contains( const Coordinate& p, int width, + const KigWidget& si ) const = 0; + virtual bool inRect( const Rect& r, int width, + const KigWidget& si ) const = 0; + virtual Rect surroundingRect() const = 0; + + /** + * Returns true if this is a valid ObjectImp. + * If you want to return an invalid ObjectImp, you should return an + * InvalidImp instance. + */ + bool valid() const; + + virtual const uint numberOfProperties() const; + // the names of the properties as perceived by the user.. put + // I18N_NOOP's around them here.. + virtual const QCStringList properties() const; + // the names of the properties as known only by kig internally. No + // need for I18N_NOOP. Preferably choose some lowercase name with + // only letters and dashes, no spaces.. + virtual const QCStringList propertiesInternalNames() const; + virtual ObjectImp* property( uint which, const KigDocument& d ) const; + // Sometimes we need to know which type an imp needs to be at least + // in order to have the imp with number which. Macro's need it + // foremost. This function answers that question.. + virtual const ObjectImpType* impRequirementForProperty( uint which ) const; + // Return whether the property with number which is by construction + // always a point on this curve ( if this is a curve ), or always a + // curve through this point ( if this is a curve ). + virtual bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + // What icon should be shown when talking about this property ? + virtual const char* iconForProperty( uint which ) const; + + /** + * Returns the lowermost ObjectImpType that this object is an + * instantiation of. + * E.g. if you want to get a string containing the internal name of + * the type of an object, you can do: + * \if creating-python-scripting-doc + * \code + * tn = object.type().internalName() + * \endcode + * \else + * \code + * std::string typename = object.type()->internalName(); + * \endcode + * \endif + */ + virtual const ObjectImpType* type() const = 0; + virtual void visit( ObjectImpVisitor* vtor ) const = 0; + + /** + * Returns a copy of this ObjectImp. + * The copy is an exact copy. Changes to the copy don't affect the + * original. + */ + virtual ObjectImp* copy() const = 0; + + // s is a string with at least one escape ( "%N" where N is a + // number ) somewhere. This function replaces the first escape it + // sees with the "value" of this imp ( using the QString::arg + // functions ). This is e.g. used by TextType to turn its variable + // args into strings.. + // if you implement this, then you should return true in + // canFillInEscape() ( standard implementation returns false ), and + // override fillInNextEscape() ( standard implementation does an + // assert( false ) ).. + virtual bool canFillInNextEscape() const; + virtual void fillInNextEscape( QString& s, const KigDocument& ) const; + + /** + * Returns true if this ObjectImp is equal to rhs. + * This function checks whether rhs is of the same ObjectImp type, + * and whether it contains the same data as this ObjectImp. + * \internal It is used e.g. by the KigCommand stuff to see what the + * user has changed during a move.. + */ + virtual bool equals( const ObjectImp& rhs ) const = 0; + + /** + * \internal Return true if this imp is just a cache imp. This + * means that it will never be considered to be stored in a file or + * in an ObjectHierarchy. This is useful for objects which cannot + * (easily and usefully) be (de)serialized, like e.g. + * PythonCompiledScriptImp.. For normal objects, the default + * implementation returns false, which is fine. + */ + virtual bool isCache() const; +}; +#endif diff --git a/kig/objects/object_imp_factory.cc b/kig/objects/object_imp_factory.cc new file mode 100644 index 00000000..bfeb1358 --- /dev/null +++ b/kig/objects/object_imp_factory.cc @@ -0,0 +1,510 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "object_imp_factory.h" + +#include "object_imp.h" +#include "bogus_imp.h" +#include "circle_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +#include "line_imp.h" +#include "locus_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "text_imp.h" + +#include "../misc/coordinate.h" + +#include <qdom.h> + +#include <klocale.h> + +const ObjectImpFactory* ObjectImpFactory::instance() +{ + static const ObjectImpFactory t; + return &t; +} + +ObjectImpFactory::ObjectImpFactory() +{ +} + +ObjectImpFactory::~ObjectImpFactory() +{ +} + +static void addXYElements( const Coordinate& c, QDomElement& parent, QDomDocument& doc ) +{ + QDomElement xe = doc.createElement( "x" ); + xe.appendChild( + doc.createTextNode( + QString::number( c.x ) ) ); + parent.appendChild( xe ); + QDomElement ye = doc.createElement( "y" ); + ye.appendChild( + doc.createTextNode( + QString::number( c.y ) ) ); + parent.appendChild( ye ); +} + +static void addDoubleElement( const char* name, double d, QDomElement& parent, QDomDocument& doc ) +{ + QDomElement e = doc.createElement( name ); + e.appendChild( doc.createTextNode( QString::number( d ) ) ); + parent.appendChild( e ); +} + +static void addCoordinateElement( const char* name, const Coordinate& d, QDomElement& p, QDomDocument& doc ) +{ + QDomElement e = doc.createElement( name ); + addXYElements( d, e, doc ); + p.appendChild( e ); +} + +QString ObjectImpFactory::serialize( const ObjectImp& d, QDomElement& parent, + QDomDocument& doc ) const +{ + if( d.inherits( IntImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + QString::number( static_cast<const IntImp&>( d ).data() ) ) ); + return QString::fromLatin1( "int" ); + } + else if ( d.inherits( DoubleImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + QString::number( static_cast<const DoubleImp&>( d ).data() ) ) ); + return QString::fromLatin1( "double" ); + } + else if( d.inherits( StringImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + static_cast<const StringImp&>( d ).data() ) ); + return QString::fromLatin1( "string" ); + } + else if ( d.inherits( TestResultImp::stype() ) ) + { + parent.appendChild( + doc.createTextNode( + static_cast<const TestResultImp&>( d ).data() ) ); + return QString::fromLatin1( "testresult" ); + } + else if( d.inherits( HierarchyImp::stype() ) ) + { + static_cast<const HierarchyImp&>( d ).data().serialize( parent, doc ); + return QString::fromLatin1( "hierarchy" ); + } + else if ( d.inherits( TransformationImp::stype() ) ) + { + const Transformation& trans = static_cast<const TransformationImp&>( d ).data(); + + QDomElement matrixe = doc.createElement( "matrix" ); + for ( int i = 0; i < 3; ++i ) + { + for ( int j = 0; j < 3; ++j ) + { + QDomElement elel = doc.createElement( "element" ); + elel.setAttribute( "row", QString::number( i ) ); + elel.setAttribute( "column", QString::number( j ) ); + elel.appendChild( doc.createTextNode( QString::number( trans.data( i, j ) ) ) ); + matrixe.appendChild( elel ); + }; + } + parent.appendChild( matrixe ); + + QDomElement homothetye = doc.createElement( "homothetic" ); + const char* ishomothety = trans.isHomothetic() ? "true" : "false"; + homothetye.appendChild( doc.createTextNode( ishomothety ) ); + parent.appendChild( homothetye ); + + return QString::fromLatin1( "transformation" ); + } + else if( d.inherits( AbstractLineImp::stype() ) ) + { + LineData l = static_cast<const AbstractLineImp&>( d ).data(); + addCoordinateElement( "a", l.a, parent, doc ); + addCoordinateElement( "b", l.b, parent, doc ); + if( d.inherits( SegmentImp::stype() ) ) + return QString::fromLatin1( "segment" ); + else if( d.inherits( RayImp::stype() ) ) + return QString::fromLatin1( "ray" ); + else return QString::fromLatin1( "line" ); + } + else if( d.inherits( PointImp::stype() ) ) + { + addXYElements( static_cast<const PointImp&>( d ).coordinate(), + parent, doc ); + return QString::fromLatin1( "point" ); + } + else if( d.inherits( TextImp::stype() ) ) + { + QString text = static_cast<const TextImp&>( d ).text(); + parent.appendChild( + doc.createTextNode( text ) ); + return QString::fromLatin1( "text" ); + } + else if( d.inherits( AngleImp::stype() ) ) + { + addDoubleElement( "size", static_cast<const AngleImp&>( d ).size(), parent, doc ); + return QString::fromLatin1( "angle" ); + } + else if ( d.inherits( ArcImp::stype() ) ) + { + const ArcImp& a = static_cast<const ArcImp&>( d ); + addCoordinateElement( "center", a.center(), parent, doc ); + addDoubleElement( "radius", a.radius(), parent, doc ); + addDoubleElement( "startangle", a.startAngle(), parent, doc ); + addDoubleElement( "angle", a.angle(), parent, doc ); + return QString::fromLatin1( "arc" ); + } + else if( d.inherits( VectorImp::stype() ) ) + { + Coordinate dir = static_cast<const VectorImp&>( d ).dir(); + addXYElements( dir, parent, doc ); + return QString::fromLatin1( "vector" ); + } + else if( d.inherits( LocusImp::stype() ) ) + { + const LocusImp& locus = static_cast<const LocusImp&>( d ); + + // serialize the curve.. + QDomElement curve = doc.createElement( "curve" ); + const CurveImp& curveimp = *locus.curve(); + QString type = serialize( curveimp, curve, doc ); + curve.setAttribute( "type", type ); + parent.appendChild( curve ); + + // serialize the hierarchy.. + QDomElement hier = doc.createElement( "calculation" ); + locus.hierarchy().serialize( hier, doc ); + parent.appendChild( hier ); + + return QString::fromLatin1( "locus" ); + } + else if( d.inherits( CircleImp::stype() ) ) + { + const CircleImp& c = static_cast<const CircleImp&>( d ); + addCoordinateElement( "center", c.center(), parent, doc ); + addDoubleElement( "radius", c.radius(), parent, doc ); + return QString::fromLatin1( "circle" ); + } + else if( d.inherits( ConicImp::stype() ) ) + { + const ConicPolarData data = static_cast<const ConicImp&>( d ).polarData(); + addCoordinateElement( "focus1", data.focus1, parent, doc ); + addDoubleElement( "pdimen", data.pdimen, parent, doc ); + addDoubleElement( "ecostheta0", data.ecostheta0, parent, doc ); + addDoubleElement( "esintheta0", data.esintheta0, parent, doc ); + return QString::fromLatin1( "conic" ); + } + else if( d.inherits( CubicImp::stype() ) ) + { + const CubicCartesianData data = static_cast<const CubicImp&>( d ).data(); + QDomElement coeffs = doc.createElement( "coefficients" ); + addDoubleElement( "a000", data.coeffs[0], coeffs, doc ); + addDoubleElement( "a001", data.coeffs[1], coeffs, doc ); + addDoubleElement( "a002", data.coeffs[2], coeffs, doc ); + addDoubleElement( "a011", data.coeffs[3], coeffs, doc ); + addDoubleElement( "a012", data.coeffs[4], coeffs, doc ); + addDoubleElement( "a022", data.coeffs[5], coeffs, doc ); + addDoubleElement( "a111", data.coeffs[6], coeffs, doc ); + addDoubleElement( "a112", data.coeffs[7], coeffs, doc ); + addDoubleElement( "a122", data.coeffs[8], coeffs, doc ); + addDoubleElement( "a222", data.coeffs[9], coeffs, doc ); + parent.appendChild( coeffs ); + return QString::fromLatin1( "cubic" ); + } + assert( false ); + return QString::null; +} + +static Coordinate readXYElements( const QDomElement& e, bool& ok ) +{ + double x, y; + ok = true; + QDomElement xe = e.firstChild().toElement(); + if ( xe.isNull() || xe.tagName() != "x" ) + { + ok = false; + return Coordinate(); + } + else x = xe.text().toDouble( &ok ); + + QDomElement ye = xe.nextSibling().toElement(); + if ( ye.isNull() || ye.tagName() != "y" ) + { + ok = false; + return Coordinate(); + } + else y = ye.text().toDouble( &ok ); + + return Coordinate( x, y ); +} + +static Coordinate readCoordinateElement( QDomNode n, bool& ok, + const char* tagname ) +{ + QDomElement e = n.toElement(); + if ( e.isNull() || e.tagName() != tagname ) + { + ok = false; + Coordinate ret; + return ret; + } + return readXYElements( e, ok ); +} + +static double readDoubleElement( QDomNode n, bool& ok, + const char* tagname ) +{ + QDomElement e = n.toElement(); + if ( e.isNull() || e.tagName() != tagname ) + { + ok = false; + return 0.; + }; + return e.text().toDouble( &ok ); +} + +ObjectImp* ObjectImpFactory::deserialize( const QString& type, + const QDomElement& parent, + QString& error ) const +{ +#define KIG_GENERIC_PARSE_ERROR \ + { \ + error = i18n( "An error was encountered at line %1 in file %2." ) \ + .arg( __LINE__ ).arg( __FILE__ ); \ + return 0; \ + } + + bool ok = true; + if ( type == "int" ) + { + int ret = parent.text().toInt( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new IntImp( ret ); + } + else if ( type == "double" ) + { + double ret = parent.text().toDouble( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new DoubleImp( ret ); + } + else if ( type == "string" ) + { + return new StringImp( parent.text() ); + } + else if ( type == "testresult" ) + { + return new TestResultImp( parent.text() ); + } + else if ( type == "hierarchy" ) + { + ObjectHierarchy* hier = ObjectHierarchy::buildSafeObjectHierarchy( parent, error ); + if ( ! hier ) return 0; + HierarchyImp* imp = new HierarchyImp( *hier ); + delete hier; + return imp; + } + else if ( type == "transformation" ) + { + double data[3][3]; + bool homothetic = false; + for ( QDomElement childe = parent.firstChild().toElement(); + ! childe.isNull(); childe = childe.nextSibling().toElement() ) + { + if ( childe.tagName() == "matrix" ) + { + for ( QDomElement elel = childe.firstChild().toElement(); + ! elel.isNull(); elel = elel.nextSibling().toElement() ) + { + if ( elel.tagName() != "element" ) KIG_GENERIC_PARSE_ERROR; + bool ok = true; + int row = elel.attribute( "row" ).toInt( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + int column = elel.attribute( "column" ).toInt( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + data[row][column] = elel.text().toDouble( &ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + }; + } + else if ( childe.tagName() == "homothetic" ) + { + homothetic = childe.text() == "true"; + } + else continue; + }; + Transformation trans( data, homothetic ); + return new TransformationImp( trans ); + } + else if ( type == "point" ) + { + Coordinate ret = readXYElements( parent, ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new PointImp( ret ); + } + else if ( type == "line" || type == "segment" || type == "ray" ) + { + QDomNode n = parent.firstChild(); + Coordinate a = readCoordinateElement( n, ok, "a" ); + if ( !ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + Coordinate b = readCoordinateElement( n, ok, "b" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + if ( type == "line" ) return new LineImp( a, b ); + else if ( type == "segment" ) return new SegmentImp( a, b ); + else return new RayImp( a, b ); + } + else if( type == "angle" ) + { + double size = readDoubleElement( parent.firstChild(), ok, "size" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new AngleImp( Coordinate(), 0, size ); + } + else if ( type == "arc" ) + { + QDomNode n = parent.firstChild(); + Coordinate center = readCoordinateElement( n, ok, "center" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + double radius = readDoubleElement( n, ok, "radius" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + double startangle = readDoubleElement( n, ok, "startangle" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + n = n.nextSibling(); + double angle = readDoubleElement( n, ok, "angle" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new ArcImp( center, radius, startangle, angle ); + } + else if( type == "vector" ) + { + Coordinate dir = readXYElements( parent, ok ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + return new VectorImp( Coordinate(), dir ); + } + else if( type == "locus" ) + { + QDomElement curvee = parent.firstChild().toElement(); + if ( curvee.isNull() || curvee.tagName() != "curve" ) KIG_GENERIC_PARSE_ERROR; + QString type = curvee.attribute( "type" ); + ObjectImp* oi = deserialize( type, curvee, error ); + if ( ! oi || ! oi->inherits( CurveImp::stype() ) ) KIG_GENERIC_PARSE_ERROR; + //CurveImp* curvei = static_cast<CurveImp*>( oi ); + + QDomElement hiere = curvee.nextSibling().toElement(); + if ( hiere.isNull() || hiere.tagName() != "calculation" ) KIG_GENERIC_PARSE_ERROR; + assert( false ); // TODO +// return new LocusImp( curvei, hier ); + } + else if( type == "circle" ) + { + QDomNode n = parent.firstChild(); + Coordinate center = readCoordinateElement( n, ok, "center" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double radius = readDoubleElement( n, ok, "radius" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + return new CircleImp( center, radius ); + } + else if( type == "conic" ) + { + QDomNode n = parent.firstChild(); + Coordinate focus1 = readCoordinateElement( n, ok, "focus1" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double pdimen = readDoubleElement( n, ok, "pdimen" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double ecostheta0 = readDoubleElement( n, ok, "ecostheta0" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double esintheta0 = readDoubleElement( n, ok, "esintheta0" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + return new ConicImpPolar( + ConicPolarData( focus1, pdimen, ecostheta0, esintheta0 ) ); + } + else if( type == "cubic" ) + { + QDomElement coeffse = parent.firstChild().toElement(); + if ( coeffse.isNull() || coeffse.tagName() != "coefficients" ) + KIG_GENERIC_PARSE_ERROR; + + QDomNode n = coeffse.firstChild(); + double a000 = readDoubleElement( n, ok, "a000" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a001 = readDoubleElement( n, ok, "a001" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a002 = readDoubleElement( n, ok, "a002" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a011 = readDoubleElement( n, ok, "a011" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a012 = readDoubleElement( n, ok, "a012" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a022 = readDoubleElement( n, ok, "a022" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a111 = readDoubleElement( n, ok, "a111" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a112 = readDoubleElement( n, ok, "a112" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a122 = readDoubleElement( n, ok, "a112" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + n = n.nextSibling(); + double a222 = readDoubleElement( n, ok, "a222" ); + if ( ! ok ) KIG_GENERIC_PARSE_ERROR; + + return new CubicImp( CubicCartesianData( a000, a001, a002, + a011, a012, a022, + a111, a112, a122, + a222 ) ); + } + + error = i18n( "This Kig file uses an object of type \"%1\", " + "which this Kig version does not support." + "Perhaps you have compiled Kig without support " + "for this object type," + "or perhaps you are using an older Kig version." ).arg( type ); + return 0; +} + diff --git a/kig/objects/object_imp_factory.h b/kig/objects/object_imp_factory.h new file mode 100644 index 00000000..1ab82bb4 --- /dev/null +++ b/kig/objects/object_imp_factory.h @@ -0,0 +1,40 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_IMP_FACTORY_H +#define KIG_OBJECTS_OBJECT_IMP_FACTORY_H + +#include "common.h" + +class ObjectImpFactory +{ + ObjectImpFactory(); + ~ObjectImpFactory(); +public: + static const ObjectImpFactory* instance(); + /** + * loads data from \p parent , and returns a new ObjectImp from the type + * string \p type . + */ + ObjectImp* deserialize( const QString& type, const QDomElement& parent, QString& error ) const; + /** + * adds data to \p parent , and returns a type string.. + */ + QString serialize( const ObjectImp& d, QDomElement& parent, QDomDocument& doc ) const; +}; + +#endif diff --git a/kig/objects/object_type.cc b/kig/objects/object_type.cc new file mode 100644 index 00000000..9ac2845b --- /dev/null +++ b/kig/objects/object_type.cc @@ -0,0 +1,125 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "object_type.h" + +#include "bogus_imp.h" +#include "object_type_factory.h" + +#include "../misc/coordinate.h" + +#include <qstringlist.h> + +#include <klocale.h> + +const char* ObjectType::fullName() const +{ + return mfulltypename; +} + +ObjectType::~ObjectType() +{ +} + +ObjectType::ObjectType( const char fulltypename[] ) + : mfulltypename( fulltypename ) +{ + ObjectTypeFactory::instance()->add( this ); +} + +bool ObjectType::canMove( const ObjectTypeCalcer& ) const +{ + return false; +} + +bool ObjectType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return false; +} + +void ObjectType::move( ObjectTypeCalcer&, const Coordinate&, const KigDocument& ) const +{ + // we can't do an assert here, because sometimes ( like in + // ObjectABType::move, ObjectType::move is called without checking + // the object's canMove(). +// assert( false ); +} + +bool ObjectType::inherits( int ) const +{ + return false; +} + +ArgsParserObjectType::ArgsParserObjectType( const char fulltypename[], + const struct ArgsParser::spec argsspec[], + int n ) + : ObjectType( fulltypename ), margsparser( argsspec, n ) +{ +} + +const ObjectImpType* ArgsParserObjectType::impRequirement( const ObjectImp* o, const Args& parents ) const +{ + return margsparser.impRequirement( o, parents ); +} + +const ArgsParser& ArgsParserObjectType::argsParser() const +{ + return margsparser; +} + +bool ObjectType::isTransform() const +{ + return false; +} + +QStringList ObjectType::specialActions() const +{ + return QStringList(); +} + +void ObjectType::executeAction( int, ObjectHolder&, ObjectTypeCalcer&, KigPart&, KigWidget&, + NormalMode& ) const +{ + assert( false ); +} + +const Coordinate ObjectType::moveReferencePoint( const ObjectTypeCalcer& ) const +{ + assert( false ); + return Coordinate::invalidCoord(); +} + +std::vector<ObjectCalcer*> ArgsParserObjectType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return margsparser.parse( args ); +} + +Args ArgsParserObjectType::sortArgs( const Args& args ) const +{ + return margsparser.parse( args ); +} + +std::vector<ObjectCalcer*> ObjectType::movableParents( const ObjectTypeCalcer& ) const +{ + return std::vector<ObjectCalcer*>(); +} + +bool ArgsParserObjectType::isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const +{ + return margsparser.isDefinedOnOrThrough( o, parents ); +} + diff --git a/kig/objects/object_type.h b/kig/objects/object_type.h new file mode 100644 index 00000000..f0ac49af --- /dev/null +++ b/kig/objects/object_type.h @@ -0,0 +1,130 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OBJECT_TYPE_H +#define KIG_OBJECTS_OBJECT_TYPE_H + +#include "common.h" +#include "../misc/argsparser.h" + +class ObjectTypeCalcer; + +/** + * The ObjectType class is a thing that represents the "behaviour" for + * a certain type.. This basically means that it decides what + * \ref ObjectImp the object gets in the calc() function, how the + * object move()'s etc. + */ +class ObjectType +{ + const char* mfulltypename; +protected: + ObjectType( const char fulltypename[] ); +public: + virtual ~ObjectType(); + + enum { + ID_ConstrainedPointType, + ID_LocusType, + ID_FixedPointType + }; + + virtual bool inherits( int type ) const; + + virtual ObjectImp* calc( const Args& parents, const KigDocument& d ) const = 0; + + virtual bool canMove( const ObjectTypeCalcer& ourobj ) const; + virtual bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + virtual std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + virtual const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + virtual void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& d ) const; + + const char* fullName() const; + + /** + * Supposing that \p parents would be given as parents to + * this type's calc function, this function returns the ObjectImp id + * that \p o should at least have.. ( \p o should be part of \p parents ) + */ + virtual const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const = 0; + + /** + * Supposing that \p parents would be given as parents to this type's + * calc function, this function returns whether the returned + * ObjectImp will be, by construction, on \p o ( if \p o is a curve ), or + * through \p o ( if \p o is a point ). + */ + virtual bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const = 0; + + /** + * returns the ObjectImp id of the ObjectImp's produced by this + * ObjectType.. if the ObjectType can return different sorts of + * ObjectImp's, it should return the biggest common id, or + * ID_AnyImp.. + */ + virtual const ObjectImpType* resultId() const = 0; + + virtual std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const = 0; + + virtual Args sortArgs( const Args& args ) const = 0; + + /** + * is this object type a transformation type. We want to know this + * cause transform types are shown separately in an object's RMB + * menu.. + */ + virtual bool isTransform() const; + + // ObjectType's can define some special actions, that are strictly + // specific to the type at hand. E.g. a text label allows to toggle + // the display of a frame around the text. Constrained and fixed + // points can be redefined etc. + + /** + * return i18n'd names for the special actions.. + */ + virtual QStringList specialActions() const; + /** + * execute the \p i 'th action from the specialActions above.. + */ + virtual void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +/** + * This is a convenience subclass of ObjectType that a type should + * inherit from if its parents can be specified in an ArgsParser.. + */ +class ArgsParserObjectType + : public ObjectType +{ +protected: + const ArgsParser margsparser; + ArgsParserObjectType( const char fulltypename[], + const struct ArgsParser::spec argsspec[], + int n ); +public: + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ArgsParser& argsParser() const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; +}; + +#endif diff --git a/kig/objects/object_type_factory.cc b/kig/objects/object_type_factory.cc new file mode 100644 index 00000000..ee3024fd --- /dev/null +++ b/kig/objects/object_type_factory.cc @@ -0,0 +1,148 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "object_type_factory.h" + +#include <config.h> + +#include "object_type.h" +#include "circle_type.h" +#include "conic_types.h" +#include "cubic_type.h" +#include "intersection_types.h" +#include "line_type.h" +#include "text_type.h" +#include "other_type.h" +#include "transform_types.h" +#include "point_type.h" +#include "tests_type.h" + +#ifdef KIG_ENABLE_PYTHON_SCRIPTING +#include "../scripting/python_type.h" +#endif + +#include <qdom.h> +#include <string> + +ObjectTypeFactory::ObjectTypeFactory() + : malreadysetup( false ) +{ + setupBuiltinTypes(); +} + +ObjectTypeFactory::~ObjectTypeFactory() +{ +} + +ObjectTypeFactory* ObjectTypeFactory::instance() +{ + static ObjectTypeFactory fact; + return &fact; +} + +void ObjectTypeFactory::add( const ObjectType* type ) +{ + assert( mmap.find( std::string( type->fullName() ) ) == mmap.end() ); + mmap[std::string( type->fullName() )] = type; +} + +const ObjectType* ObjectTypeFactory::find( const char* name ) const +{ + maptype::const_iterator i = mmap.find( std::string( name ) ); + if ( i == mmap.end() ) return 0; + else return i->second; +} + +void ObjectTypeFactory::setupBuiltinTypes() +{ +// assert( ! malreadysetup ); +// malreadysetup = true; + +// // circle_type.h +// add( CircleBCPType::instance() ); +// add( CircleBPRType::instance() ); +// add( CircleBTPType::instance() ); + +// // conic_types.h +// add( ConicB5PType::instance() ); +// add( ConicBAAPType::instance() ); +// add( EllipseBFFPType::instance() ); +// add( HyperbolaBFFPType::instance() ); +// add( ConicBDFPType::instance() ); +// add( ParabolaBTPType::instance() ); +// add( EquilateralHyperbolaB4PType::instance() ); +// add( ConicPolarPointType::instance() ); +// add( ConicPolarLineType::instance() ); +// add( ConicDirectrixType::instance() ); +// add( ParabolaBDPType::instance() ); +// add( ConicAsymptoteType::instance() ); +// add( ConicRadicalType::instance() ); + +// // cubic_type.h +// add( CubicB9PType::instance() ); +// add( CubicNodeB6PType::instance() ); +// add( CubicCuspB4PType::instance() ); + +// // intersection_types.h +// add( ConicLineIntersectionType::instance() ); +// add( ConicLineOtherIntersectionType::instance() ); +// add( LineLineIntersectionType::instance() ); +// add( LineCubicIntersectionType::instance() ); +// add( CircleCircleIntersectionType::instance() ); + +// // line_type.h +// add( SegmentABType::instance() ); +// add( LineABType::instance() ); +// add( RayABType::instance() ); +// add( LinePerpendLPType::instance() ); +// add( LineParallelLPType::instance() ); + +// // other_type.h +// add( AngleType::instance() ); +// add( VectorType::instance() ); +// add( LocusType::instance() ); +// add( ArcBTPType::instance() ); +// add( CopyObjectType::instance() ); + +// // point_type.h +// add( FixedPointType::instance() ); +// add( ConstrainedPointType::instance() ); +// add( MidPointType::instance() ); +// add( MeasureTransportType::instance() ); + +// // text_type.h +// add( TextType::instance() ); + +// // tests_type.h +// add( AreParallelType::instance() ); + +// // transform_types.h +// add( TranslatedType::instance() ); +// add( PointReflectionType::instance() ); +// add( LineReflectionType::instance() ); +// add( RotationType::instance() ); +// add( ScalingOverCenterType::instance() ); +// add( ScalingOverLineType::instance() ); +// add( ProjectiveRotationType::instance() ); +// add( CastShadowType::instance() ); + +// #ifdef KIG_ENABLE_PYTHON_SCRIPTING +// // python types +// add( PythonCompileType::instance() ); +// add( PythonExecuteType::instance() ); +// #endif +} diff --git a/kig/objects/object_type_factory.h b/kig/objects/object_type_factory.h new file mode 100644 index 00000000..c1371d67 --- /dev/null +++ b/kig/objects/object_type_factory.h @@ -0,0 +1,40 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef OBJECT_TYPE_FACTORY_H +#define OBJECT_TYPE_FACTORY_H + +#include "common.h" + +#include <map> +#include <string> + +class ObjectTypeFactory +{ + typedef std::map<std::string, const ObjectType*> maptype; + maptype mmap; + bool malreadysetup; + ObjectTypeFactory(); + ~ObjectTypeFactory(); + void setupBuiltinTypes(); +public: + static ObjectTypeFactory* instance(); + void add( const ObjectType* type ); + const ObjectType* find( const char* name ) const; +}; + +#endif diff --git a/kig/objects/other_imp.cc b/kig/objects/other_imp.cc new file mode 100644 index 00000000..137a3e93 --- /dev/null +++ b/kig/objects/other_imp.cc @@ -0,0 +1,710 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "other_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "line_imp.h" + +#include "../misc/screeninfo.h" +#include "../misc/common.h" +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../misc/goniometry.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> +#include <utility> +using namespace std; + +AngleImp::~AngleImp() +{ +} + +ObjectImp* AngleImp::transform( const Transformation& ) const +{ + // TODO ? + return new InvalidImp; +} + +void AngleImp::draw( KigPainter& p ) const +{ + p.drawAngle( mpoint, mstartangle, mangle ); +} + +AngleImp::AngleImp( const Coordinate& pt, double start_angle_in_radials, + double angle_in_radials ) + : mpoint( pt ), mstartangle( start_angle_in_radials ), + mangle( angle_in_radials ) +{ +} + +bool AngleImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + double radius = 50*w.screenInfo().pixelWidth(); + + if ( fabs( (p-mpoint).length() - radius ) > w.screenInfo().normalMiss( width ) ) + return false; + + // and next we check if the angle is appropriate... + Coordinate vect = p - mpoint; + double angle = atan2( vect.y, vect.x ); + while ( angle < mstartangle ) angle += 2*M_PI; + return angle <= mstartangle + mangle; +} + +bool AngleImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + // TODO ? + return r.contains( mpoint, w.screenInfo().normalMiss( width ) ); +} + +Coordinate AngleImp::attachPoint() const +{ + return mpoint; +} + +const uint AngleImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 3; +} + +const QCStringList AngleImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "angle-radian"; + l << "angle-degrees"; + l << "angle-bisector"; + assert( l.size() == AngleImp::numberOfProperties() ); + return l; +} + +const QCStringList AngleImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Angle in Radians" ); + l << I18N_NOOP( "Angle in Degrees" ); + l << I18N_NOOP( "Angle Bisector" ); + assert( l.size() == AngleImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* AngleImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return AngleImp::stype(); +} + +const char* AngleImp::iconForProperty( uint which ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; // size in radians + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; // size in degrees + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_bisector"; // angle bisector.. + else assert( false ); + return ""; +} + +ObjectImp* AngleImp::property( uint which, const KigDocument& w ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( size() ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( Goniometry::convert( size(), Goniometry::Rad, Goniometry::Deg ) ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + { + const double angle = mstartangle + mangle / 2; + Coordinate p2 = mpoint + Coordinate( cos( angle ), sin( angle ) ) * 10; + return new RayImp( mpoint, p2 ); + } + else assert( false ); + return new InvalidImp; +} + +const double AngleImp::size() const +{ + return mangle; +} + +ObjectImp* AngleImp::copy() const +{ + return new AngleImp( mpoint, mstartangle, mangle ); +} + +VectorImp::VectorImp( const Coordinate& a, const Coordinate& b ) + : mdata( a, b ) +{ +} + +VectorImp::~VectorImp() +{ +} + +ObjectImp* VectorImp::transform( const Transformation& t ) const +{ + Coordinate ta = t.apply( mdata.a ); + Coordinate tb = t.apply( mdata.b ); + if ( ta.valid() && tb.valid() ) return new VectorImp( ta, tb ); + else return new InvalidImp; +} + +void VectorImp::draw( KigPainter& p ) const +{ + p.drawVector( mdata.a, mdata.b ); +} + +bool VectorImp::contains( const Coordinate& o, int width, const KigWidget& w ) const +{ + return internalContainsPoint( o, w.screenInfo().normalMiss( width ) ); +} + +bool VectorImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + return lineInRect( r, mdata.a, mdata.b, width, this, w ); +} + +const uint VectorImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 5; +} + +const QCStringList VectorImp::propertiesInternalNames() const +{ + QCStringList ret = Parent::propertiesInternalNames(); + ret << "length"; + ret << "vect-mid-point"; + ret << "length-x"; + ret << "length-y"; + ret << "vector-opposite"; + assert( ret.size() == VectorImp::numberOfProperties() ); + return ret; +} + +const QCStringList VectorImp::properties() const +{ + QCStringList ret = Parent::properties(); + ret << I18N_NOOP( "Length" ); + ret << I18N_NOOP( "Midpoint" ); + ret << I18N_NOOP( "X length" ); + ret << I18N_NOOP( "Y length" ); + ret << I18N_NOOP( "Opposite Vector" ); + assert( ret.size() == VectorImp::numberOfProperties() ); + return ret; +} + +const ObjectImpType* VectorImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return VectorImp::stype(); +} + +const char* VectorImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() ) + return "distance"; // length + else if ( which == Parent::numberOfProperties() + 1 ) + return "bisection"; // mid point + else if ( which == Parent::numberOfProperties() + 2 ) + return "distance"; // length-x + else if ( which == Parent::numberOfProperties() + 3 ) + return "distance"; // length-y + else if ( which == Parent::numberOfProperties() + 4 ) + return "opposite-vector"; // opposite vector + else assert( false ); + return ""; +} + +ObjectImp* VectorImp::property( uint which, const KigDocument& w ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() ) + return new DoubleImp( length() ); + else if ( which == Parent::numberOfProperties() + 1 ) + return new PointImp( ( mdata.a + mdata.b ) / 2 ); + else if ( which == Parent::numberOfProperties() + 2 ) + return new DoubleImp( fabs( mdata.a.x - mdata.b.x ) ); + else if ( which == Parent::numberOfProperties() + 3 ) + return new DoubleImp( fabs( mdata.a.y - mdata.b.y ) ); + else if ( which == Parent::numberOfProperties() + 4 ) // opposite + return new VectorImp( mdata.a, 2*mdata.a-mdata.b ); + else assert( false ); + return new InvalidImp; +} + +VectorImp* VectorImp::copy() const +{ + return new VectorImp( mdata.a, mdata.b ); +} + +const Coordinate VectorImp::dir() const +{ + return mdata.dir(); +} + +void AngleImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +void VectorImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +const double VectorImp::length() const +{ + return ( mdata.a - mdata.b ).length(); +} + +ArcImp::ArcImp( const Coordinate& center, const double radius, + const double startangle, const double angle ) + : CurveImp(), mcenter( center ), mradius( radius ), + msa( startangle ), ma( angle ) +{ + if ( ma < 0 ) + { + // we want a positive angle.. + msa = msa + ma; + ma = -ma; + }; +} + +ArcImp::~ArcImp() +{ +} + +ArcImp* ArcImp::copy() const +{ + return new ArcImp( mcenter, mradius, msa, ma ); +} + +ObjectImp* ArcImp::transform( const Transformation& t ) const +{ + // + // we don't have conic arcs! So it is invalid to transform an arc + // with a nonhomothetic transformation + // + if ( ! t.isHomothetic() ) return new InvalidImp(); + + Coordinate nc = t.apply( mcenter ); + double nr = t.apply( mradius ); + // transform msa... + double nmsa = msa; + if ( t.getAffineDeterminant() > 0 ) + { + nmsa = msa - t.getRotationAngle(); + } else + { + Coordinate ar = t.apply2by2only( Coordinate( cos(msa), sin(msa) ) ); + nmsa = atan2( ar.y, ar.x ); + nmsa -= ma; + } + while ( nmsa < -M_PI ) nmsa += 2*M_PI; + while ( nmsa > M_PI ) nmsa -= 2*M_PI; + if ( nc.valid() ) return new ArcImp( nc, nr, nmsa, ma ); + else return new InvalidImp; +} + +void ArcImp::draw( KigPainter& p ) const +{ + p.drawArc( mcenter, mradius, msa, ma ); +} + +bool ArcImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ) ); +} + +bool ArcImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO + return false; +} + +bool ArcImp::valid() const +{ + return true; +} + +const uint ArcImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 9; +} + +const QCStringList ArcImp::properties() const +{ + QCStringList ret = Parent::properties(); + ret << I18N_NOOP( "Center" ); + ret << I18N_NOOP( "Radius" ); + ret << I18N_NOOP( "Angle" ); + ret << I18N_NOOP( "Angle in Degrees" ); + ret << I18N_NOOP( "Angle in Radians" ); + ret << I18N_NOOP( "Sector Surface" ); + ret << I18N_NOOP( "Arc Length" ); + ret << I18N_NOOP( "First End Point" ); + ret << I18N_NOOP( "Second End Point" ); + assert( ret.size() == ArcImp::numberOfProperties() ); + return ret; +} + +const QCStringList ArcImp::propertiesInternalNames() const +{ + QCStringList ret = Parent::propertiesInternalNames(); + ret << "center"; + ret << "radius"; + ret << "angle"; + ret << "angle-degrees"; + ret << "angle-radians"; + ret << "sector-surface"; + ret << "arc-length"; + ret << "end-point-A"; + ret << "end-point-B"; + return ret; +} + +const char* ArcImp::iconForProperty( uint which ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "arc_center"; // center + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle"; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return "angle_size"; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else if ( which == Parent::numberOfProperties() + numprop++ ) + return ""; + else assert( false ); + return ""; +} + +ObjectImp* ArcImp::property( uint which, const KigDocument& d ) const +{ + int numprop = 0; + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, d ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new PointImp( mcenter ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( mradius ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new AngleImp( mcenter, msa, ma ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new IntImp( static_cast<int>( Goniometry::convert( ma, Goniometry::Rad, Goniometry::Deg ) ) ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( ma ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( sectorSurface() ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new DoubleImp( mradius * ma ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new PointImp( firstEndPoint() ); + else if ( which == Parent::numberOfProperties() + numprop++ ) + return new PointImp( secondEndPoint() ); + else assert( false ); + return new InvalidImp; +} + +const double ArcImp::sectorSurface() const +{ + return mradius * mradius * ma / 2; +} + +const ObjectImpType* ArcImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else + return ArcImp::stype(); +} + +void ArcImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +double ArcImp::getParam( const Coordinate& c, const KigDocument& ) const +{ + Coordinate d = (c - mcenter).normalize(); + double angle = atan2( d.y, d.x ); + angle -= msa; +// mp: problems with large arcs + while ( angle > ma/2 + M_PI ) angle -= 2*M_PI; + while ( angle < ma/2 - M_PI ) angle += 2*M_PI; +// + angle = max( 0., min( angle, ma ) ); + angle /= ma; + return angle; +} + +const Coordinate ArcImp::getPoint( double p, const KigDocument& ) const +{ + double angle = msa + p * ma; + Coordinate d = Coordinate( cos( angle ), sin( angle ) ) * mradius; + return mcenter + d; +} + +const Coordinate ArcImp::center() const +{ + return mcenter; +} + +double ArcImp::radius() const +{ + return mradius; +} + +double ArcImp::startAngle() const +{ + return msa; +} + +double ArcImp::angle() const +{ + return ma; +} + +Coordinate ArcImp::firstEndPoint() const +{ + double angle = msa; + return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius; +} + +Coordinate ArcImp::secondEndPoint() const +{ + double angle = msa + ma; + return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius; +} + +const Coordinate VectorImp::a() const +{ + return mdata.a; +} + +const Coordinate VectorImp::b() const +{ + return mdata.b; +} + +bool ArcImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( ArcImp::stype() ) && + static_cast<const ArcImp&>( rhs ).radius() == radius() && + static_cast<const ArcImp&>( rhs ).startAngle() == startAngle() && + static_cast<const ArcImp&>( rhs ).angle() == angle(); +} + +bool AngleImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( AngleImp::stype() ) && + static_cast<const AngleImp&>( rhs ).point() == point() && + static_cast<const AngleImp&>( rhs ).startAngle() == startAngle() && + static_cast<const AngleImp&>( rhs ).angle() == angle(); +} + +bool VectorImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( VectorImp::stype() ) && + static_cast<const VectorImp&>( rhs ).a() == a() && + static_cast<const VectorImp&>( rhs ).b() == b(); +} + +const ObjectImpType* AngleImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "angle", + I18N_NOOP( "angle" ), + I18N_NOOP( "Select this angle" ), + I18N_NOOP( "Select angle %1" ), + I18N_NOOP( "Remove an Angle" ), + I18N_NOOP( "Add an Angle" ), + I18N_NOOP( "Move an Angle" ), + I18N_NOOP( "Attach to this angle" ), + I18N_NOOP( "Show an Angle" ), + I18N_NOOP( "Hide an Angle" ) + ); + return &t; +} +const ObjectImpType* VectorImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "vector", + I18N_NOOP( "vector" ), + I18N_NOOP( "Select this vector" ), + I18N_NOOP( "Select vector %1" ), + I18N_NOOP( "Remove a Vector" ), + I18N_NOOP( "Add a Vector" ), + I18N_NOOP( "Move a Vector" ), + I18N_NOOP( "Attach to this vector" ), + I18N_NOOP( "Show a Vector" ), + I18N_NOOP( "Hide a Vector" ) + ); + return &t; +} +const ObjectImpType* ArcImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "arc", + I18N_NOOP( "arc" ), + I18N_NOOP( "Select this arc" ), + I18N_NOOP( "Select arc %1" ), + I18N_NOOP( "Remove an Arc" ), + I18N_NOOP( "Add an Arc" ), + I18N_NOOP( "Move an Arc" ), + I18N_NOOP( "Attach to this arc" ), + I18N_NOOP( "Show an Arc" ), + I18N_NOOP( "Hide an Arc" ) + ); + return &t; +} + +const ObjectImpType* AngleImp::type() const +{ + return AngleImp::stype(); +} + +const ObjectImpType* VectorImp::type() const +{ + return VectorImp::stype(); +} + +const ObjectImpType* ArcImp::type() const +{ + return ArcImp::stype(); +} + +bool ArcImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool ArcImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnArc( p, mcenter, mradius, msa, ma, threshold ); +} + +bool AngleImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +bool VectorImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +bool ArcImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + else if ( which == Parent::numberOfProperties() ) + return true; + else + return false; +} + +Rect AngleImp::surroundingRect() const +{ + return Rect( mpoint, 0, 0 ); +} + +Rect VectorImp::surroundingRect() const +{ + return Rect( mdata.a, mdata.b ); +} + +Rect ArcImp::surroundingRect() const +{ + // the returned rect should contain the center point(?), the two end + // points, and all extreme x and y positions in between. + //Rect ret( mcenter, 0, 0 ); + double a = msa; + //ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) ); + Rect ret ( mcenter + mradius*Coordinate( cos( a ), sin( a ) ), 0, 0 ); + a = msa + ma; + ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) ); + for ( a = -2*M_PI; a <= 2*M_PI; a+=M_PI/2 ) + { + Coordinate d = mcenter + mradius*Coordinate( cos( a ), sin( a ) ); + if ( msa <= a && a <= msa + ma ) + ret.setContains( d ); + } + return ret; +} + +const Coordinate VectorImp::getPoint( double param, const KigDocument& ) const +{ + return mdata.a + mdata.dir() * param; +} + +double VectorImp::getParam( const Coordinate& p, const KigDocument& ) const +{ + Coordinate pt = calcPointOnPerpend( mdata, p ); + pt = calcIntersectionPoint( mdata, LineData( p, pt ) ); + // if pt is over the end of the vector we set it to one of the end + // points of the vector... + if ( ( pt - mdata.a ).length() > dir().length() ) + pt = mdata.b; + else if ( ( pt - mdata.b ).length() > dir().length() ) + pt = mdata.a; + if ( mdata.b == mdata.a ) return 0; + return ( ( pt - mdata.a ).length() ) / ( dir().length() ); +} + +bool VectorImp::containsPoint( const Coordinate& p, const KigDocument& ) const +{ + return internalContainsPoint( p, test_threshold ); +} + +bool VectorImp::internalContainsPoint( const Coordinate& p, double threshold ) const +{ + return isOnSegment( p, mdata.a, mdata.b, threshold ); +} + +LineData VectorImp::data() const +{ + return mdata; +} diff --git a/kig/objects/other_imp.h b/kig/objects/other_imp.h new file mode 100644 index 00000000..8e716fa6 --- /dev/null +++ b/kig/objects/other_imp.h @@ -0,0 +1,243 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_OTHER_IMP_H +#define KIG_OBJECTS_OTHER_IMP_H + +#include "curve_imp.h" +#include "../misc/common.h" +#include "../misc/coordinate.h" + +/** + * An ObjectImp representing an angle. + */ +class AngleImp + : public ObjectImp +{ + const Coordinate mpoint; + const double mstartangle; + const double mangle; +public: + typedef ObjectImp Parent; + /** + * Returns the ObjectImpType representing the AngleImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct an Angle with a given center, start angle and + * dimension (both in radians). + */ + AngleImp( const Coordinate& pt, double start_angle_in_radials, + double angle_in_radials ); + ~AngleImp(); + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + Rect surroundingRect() const; + + Coordinate attachPoint() const; + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + ObjectImp* copy() const; + + /** + * Return the size in radians of this angle. + */ + const double size() const; + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + /** + * Return the center of this angle. + */ + const Coordinate point() const { return mpoint; } + /** + * Return the start angle in radians of this angle. + */ + const double startAngle() const { return mstartangle; } + /** + * Return the dimension in radians of this angle. + */ + const double angle() const { return mangle; } + + bool equals( const ObjectImp& rhs ) const; +}; + +/** + * An ObjectImp representing a vector. + */ +class VectorImp + : public CurveImp +{ + LineData mdata; +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the VectorImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct a Vector with a given start point and end point. + */ + VectorImp( const Coordinate& a, const Coordinate& b ); + ~VectorImp(); + + ObjectImp* transform( const Transformation& ) const; + + const Coordinate getPoint( double param, const KigDocument& ) const; + double getParam( const Coordinate&, const KigDocument& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + VectorImp* copy() const; + + /** + * Return the direction of this vector. + */ + const Coordinate dir() const; + /** + * Return the start point of this vector. + */ + const Coordinate a() const; + /** + * Return the end point of this vector. + */ + const Coordinate b() const; + /** + * Return the length of this vector. + */ + const double length() const; + /** + * Get the LineData for this vector. + */ + LineData data() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +/** + * An ObjectImp representing an arc. + */ +class ArcImp + : public CurveImp +{ + Coordinate mcenter; + double mradius; + double msa; + double ma; +public: + typedef CurveImp Parent; + /** + * Returns the ObjectImpType representing the ArcImp type.. + */ + static const ObjectImpType* stype(); + + /** + * Construct an Arc with a given center, radius, start angle and + * dimension (both in radians). + */ + ArcImp( const Coordinate& center, const double radius, + const double startangle, const double angle ); + ~ArcImp(); + ArcImp* copy() const; + + ObjectImp* transform( const Transformation& t ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& w ) const; + bool inRect( const Rect& r, int width, const KigWidget& si ) const; + Rect surroundingRect() const; + bool valid() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + double getParam( const Coordinate& c, const KigDocument& d ) const; + const Coordinate getPoint( double p, const KigDocument& d ) const; + + /** + * Return the center of this arc. + */ + const Coordinate center() const; + /** + * Return the radius of this arc. + */ + double radius() const; + /** + * Return the start angle in radians of this arc. + */ + double startAngle() const; + /** + * Return the dimension in radians of this arc. + */ + double angle() const; + /** + * Return the start point of this arc. + */ + Coordinate firstEndPoint() const; + /** + * Return the end point of this arc. + */ + Coordinate secondEndPoint() const; + /** + * Return the size of the sector surface of this arc. + */ + const double sectorSurface() const; + + bool equals( const ObjectImp& rhs ) const; + + bool containsPoint( const Coordinate& p, const KigDocument& doc ) const; + bool internalContainsPoint( const Coordinate& p, double threshold ) const; +}; + +#endif diff --git a/kig/objects/other_type.cc b/kig/objects/other_type.cc new file mode 100644 index 00000000..27787986 --- /dev/null +++ b/kig/objects/other_type.cc @@ -0,0 +1,189 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "other_type.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "locus_imp.h" + +#include "../misc/common.h" +#include "../misc/calcpaths.h" +#include "../misc/goniometry.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" + +#include <functional> +#include <algorithm> +#include <cmath> + +using std::find; + +static const struct ArgsParser::spec argsspecLocus[] = +{ + { HierarchyImp::stype(), "hierarchy", "SHOULD NOT BE SEEN", false }, + { CurveImp::stype(), "curve", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LocusType ) + +LocusType::LocusType() + : ArgsParserObjectType( "Locus", argsspecLocus, 2 ) +{ +} + +LocusType::~LocusType() +{ +} + +ObjectImp* LocusType::calc( const Args& args, const KigDocument& ) const +{ + using namespace std; + + assert( args.size() >= 2 ); + const Args firsttwo( args.begin(), args.begin() + 2 ); + Args fixedargs( args.begin() + 2, args.end() ); + + if ( ! margsparser.checkArgs( firsttwo ) ) return new InvalidImp; + for ( Args::iterator i = fixedargs.begin(); i != fixedargs.end(); ++i ) + if ( ! (*i)->valid() ) + return new InvalidImp; + + const ObjectHierarchy& hier = + static_cast<const HierarchyImp*>( args[0] )->data(); + const CurveImp* curveimp = static_cast<const CurveImp*>( args[1] ); + + return new LocusImp( curveimp->copy(), hier.withFixedArgs( fixedargs ) ); +} + +bool LocusType::inherits( int type ) const +{ + return type == ID_LocusType ? true : Parent::inherits( type ); +} + +const ObjectImpType* LocusType::resultId() const +{ + return LocusImp::stype(); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CopyObjectType ) + +CopyObjectType::CopyObjectType() + : ObjectType( "Copy" ) +{ +} + +CopyObjectType::~CopyObjectType() +{ +} + +CopyObjectType* CopyObjectType::instance() +{ + static CopyObjectType t; + return &t; +} + +bool CopyObjectType::inherits( int ) const +{ + return false; +} + +ObjectImp* CopyObjectType::calc( const Args& parents, const KigDocument& ) const +{ + assert( parents.size() == 1 ); + return parents[0]->copy(); +} + +const ObjectImpType* CopyObjectType::impRequirement( const ObjectImp*, const Args& ) const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* CopyObjectType::resultId() const +{ + // we don't know what we return.. + return ObjectImp::stype(); +} + +const ObjectImpType* LocusType::impRequirement( const ObjectImp* o, const Args& parents ) const +{ + assert( parents.size() >= 2 ); + Args firsttwo( parents.begin(), parents.begin() + 2 ); + if ( o == parents[0] || o == parents[1] ) + return margsparser.impRequirement( o, firsttwo ); + else + { + const HierarchyImp* h = dynamic_cast<const HierarchyImp*>( parents[0] ); + if ( h ) + { + PointImp* p = new PointImp( Coordinate() ); + Args hargs( parents.begin()+ 2, parents.end() ); + hargs.push_back( p ); + ArgsParser hparser = h->data().argParser(); + const ObjectImpType* ret = hparser.impRequirement( o, hargs ); + delete p; + return ret; + } + else + return ObjectImp::stype(); + }; +} + +const LocusType* LocusType::instance() +{ + static const LocusType t; + return &t; +} + +std::vector<ObjectCalcer*> CopyObjectType::sortArgs( const std::vector<ObjectCalcer*>& os ) const +{ + assert( os.size() == 1 ); + return os; +} + +std::vector<ObjectCalcer*> LocusType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + assert( args.size() >= 2 ); + std::vector<ObjectCalcer*> firsttwo( args.begin(), args.begin() + 2 ); + firsttwo = margsparser.parse( firsttwo ); + std::copy( args.begin() + 2, args.end(), std::back_inserter( firsttwo ) ); + return firsttwo; +} + +Args LocusType::sortArgs( const Args& args ) const +{ + assert( args.size() >= 2 ); + Args firsttwo( args.begin(), args.begin() + 2 ); + firsttwo = margsparser.parse( firsttwo ); + std::copy( args.begin() + 2, args.end(), std::back_inserter( firsttwo ) ); + return firsttwo; +} + +Args CopyObjectType::sortArgs( const Args& args ) const +{ + assert( args.size() == 1 ); + return args; +} + +bool CopyObjectType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + // TODO: vragen aan parent ? + // TODO: translate the above TODO ? + return false; +} + diff --git a/kig/objects/other_type.h b/kig/objects/other_type.h new file mode 100644 index 00000000..6bbcead1 --- /dev/null +++ b/kig/objects/other_type.h @@ -0,0 +1,61 @@ +// Copyright (C) 2003-2004 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_MISC_OTHER_TYPE_H +#define KIG_MISC_OTHER_TYPE_H + +#include "base_type.h" +#include "../misc/object_hierarchy.h" + +class LocusType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + LocusType(); + ~LocusType(); +public: + static const LocusType* instance(); + + ObjectImp* calc( const Args& args, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + + bool inherits( int type ) const; + const ObjectImpType* resultId() const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; +}; + +class CopyObjectType + : public ObjectType +{ +protected: + CopyObjectType(); + ~CopyObjectType(); +public: + static CopyObjectType* instance(); + bool inherits( int type ) const; + ObjectImp* calc( const Args& parents, const KigDocument& d ) const; + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ObjectImpType* resultId() const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& os ) const; + Args sortArgs( const Args& args ) const; +}; + +#endif diff --git a/kig/objects/point_imp.cc b/kig/objects/point_imp.cc new file mode 100644 index 00000000..02d4d360 --- /dev/null +++ b/kig/objects/point_imp.cc @@ -0,0 +1,223 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "point_imp.h" + +#include "bogus_imp.h" +#include "../misc/kigtransform.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate_system.h" +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +PointImp::PointImp( const Coordinate& c ) + : mc( c ) +{ +} + +Coordinate PointImp::attachPoint() const +{ + return mc; +// return Coordinate::invalidCoord(); +} + +void PointImp::draw( KigPainter& p ) const +{ + p.drawFatPoint( mc ); +} + +bool PointImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + int twidth = width == -1 ? 5 : width; + return (p - mc).length() - twidth*w.screenInfo().pixelWidth() < 0; +} + +bool PointImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + double am = w.screenInfo().normalMiss( width ); + return r.contains( mc, am ); +} + +const uint PointImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 3; +} + +const QCStringList PointImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l << "coordinate"; + l << "coordinate-x"; + l << "coordinate-y"; + assert( l.size() == PointImp::numberOfProperties() ); + return l; +} + +const QCStringList PointImp::properties() const +{ + QCStringList l = Parent::properties(); + l << I18N_NOOP( "Coordinate" ); + l << I18N_NOOP( "X coordinate" ); + l << I18N_NOOP( "Y coordinate" ); + assert( l.size() == PointImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* PointImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return PointImp::stype(); +} + +const char* PointImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + if ( which == Parent::numberOfProperties() ) + return "pointxy"; // coordinate + if ( which == Parent::numberOfProperties() + 1 ) + return "pointxy"; // coordinate-x + if ( which == Parent::numberOfProperties() + 2 ) + return "pointxy"; // coordinate-y + else assert( false ); + return ""; +} + +ObjectImp* PointImp::property( uint which, const KigDocument& d ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, d ); + if ( which == Parent::numberOfProperties() ) + return new PointImp( mc ); + if ( which == Parent::numberOfProperties() + 1 ) + return new DoubleImp( mc.x ); + if ( which == Parent::numberOfProperties() + 2 ) + return new DoubleImp( mc.y ); +// else assert( false ); + return new InvalidImp; +} + +PointImp::~PointImp() +{ +} + +PointImp* PointImp::copy() const +{ + return new PointImp( mc ); +} + +ObjectImp* PointImp::transform( const Transformation& t ) const +{ + Coordinate nc = t.apply( mc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp(); +} + +void PointImp::setCoordinate( const Coordinate& c ) +{ + mc = c; +} + +void PointImp::fillInNextEscape( QString& s, const KigDocument& doc ) const +{ + s = s.arg( doc.coordinateSystem().fromScreen( mc, doc ) ); +} + +void PointImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool PointImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( PointImp::stype() ) && + static_cast<const PointImp&>( rhs ).coordinate() == coordinate(); +} + +bool PointImp::canFillInNextEscape() const +{ + return true; +} + +const ObjectImpType* PointImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "point", + I18N_NOOP( "point" ), + I18N_NOOP( "Select this point" ), + I18N_NOOP( "Select point %1" ), + I18N_NOOP( "Remove a Point" ), + I18N_NOOP( "Add a Point" ), + I18N_NOOP( "Move a Point" ), + I18N_NOOP( "Attach to this point" ), + I18N_NOOP( "Show a Point" ), + I18N_NOOP( "Hide a Point" ) + ); + return &t; +} + +const ObjectImpType* PointImp::type() const +{ + return PointImp::stype(); +} + +bool PointImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect PointImp::surroundingRect() const +{ + return Rect( mc, 0., 0. ); +} + +/* + */ + +BogusPointImp::BogusPointImp( const Coordinate& c ) + : PointImp( c ) +{ +} + +BogusPointImp::~BogusPointImp() +{ +} + +const ObjectImpType* BogusPointImp::stype() +{ + static const ObjectImpType t( + 0, "boguspoint", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN", + "SHOULDNOTBESEEN" + ); + return &t; +} + +const ObjectImpType* BogusPointImp::type() const +{ + return BogusPointImp::stype(); +} diff --git a/kig/objects/point_imp.h b/kig/objects/point_imp.h new file mode 100644 index 00000000..a5e8eb98 --- /dev/null +++ b/kig/objects/point_imp.h @@ -0,0 +1,91 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_POINT_IMP_H +#define KIG_OBJECTS_POINT_IMP_H + +#include "object_imp.h" +#include "../misc/coordinate.h" + +/** + * An ObjectImp representing a point.. + */ +class PointImp + : public ObjectImp +{ + Coordinate mc; +public: + typedef ObjectImp Parent; + /** + * Returns the ObjectImpType representing PointImp's. + */ + static const ObjectImpType* stype(); + + /** + * Construct a PointImp with coordinate c. + */ + PointImp( const Coordinate& c ); + ~PointImp(); + + Rect surroundingRect() const; + Coordinate attachPoint() const; + + /** + * Get the coordinate of this PointImp. + */ + const Coordinate& coordinate() const { return mc; } + /** + * Set the coordinate of this PointImp. + */ + void setCoordinate( const Coordinate& c ); + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& d ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + ObjectImp* transform( const Transformation& ) const; + + PointImp* copy() const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + void fillInNextEscape( QString& s, const KigDocument& ) const; + bool canFillInNextEscape() const; + + bool equals( const ObjectImp& rhs ) const; +}; + +class BogusPointImp + : public PointImp +{ +public: + BogusPointImp( const Coordinate& c ); + ~BogusPointImp(); + static const ObjectImpType* stype(); + const ObjectImpType* type() const; +}; + +#endif diff --git a/kig/objects/point_type.cc b/kig/objects/point_type.cc new file mode 100644 index 00000000..04f8272c --- /dev/null +++ b/kig/objects/point_type.cc @@ -0,0 +1,665 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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 "point_type.h" + +#include "point_imp.h" +#include "curve_imp.h" +#include "line_imp.h" +#include "other_imp.h" +#include "bogus_imp.h" + +#include "../modes/moving.h" +#include "../misc/coordinate_system.h" +#include "../misc/common.h" +#include "../misc/calcpaths.h" +#include "../misc/kiginputdialog.h" +#include "../kig/kig_part.h" +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" +#include "../kig/kig_commands.h" + +#include <klocale.h> + +static const ArgsParser::spec argsspecFixedPoint[] = +{ + { DoubleImp::stype(), "x", "SHOULD NOT BE SEEN", false }, + { DoubleImp::stype(), "y", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( FixedPointType ) + +FixedPointType::FixedPointType() + : ArgsParserObjectType( "FixedPoint", argsspecFixedPoint, 2 ) +{ +} + +FixedPointType::~FixedPointType() +{ +} + +ObjectImp* FixedPointType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + double a = static_cast<const DoubleImp*>( parents[0] )->data(); + double b = static_cast<const DoubleImp*>( parents[1] )->data(); + + return new PointImp( Coordinate( a, b ) ); +} + +static const ArgsParser::spec argsspecRelativePoint[] = +{ + { DoubleImp::stype(), "relative-x", "SHOULD NOT BE SEEN", false }, + { DoubleImp::stype(), "relative-y", "SHOULD NOT BE SEEN", false }, + { ObjectImp::stype(), "object", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( RelativePointType ) + +RelativePointType::RelativePointType() + : ArgsParserObjectType( "RelativePoint", argsspecRelativePoint, 3 ) +{ +} + +RelativePointType::~RelativePointType() +{ +} + +ObjectImp* RelativePointType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + if ( ! parents[2]->attachPoint().valid() ) return new InvalidImp; + + Coordinate reference = static_cast<const ObjectImp*>( parents[2] )->attachPoint(); + double a = static_cast<const DoubleImp*>( parents[0] )->data(); + double b = static_cast<const DoubleImp*>( parents[1] )->data(); + + return new PointImp( reference + Coordinate( a, b ) ); +} + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CursorPointType ) + +CursorPointType::CursorPointType() + : ObjectType( "CursorPoint" ) +{ +} + +CursorPointType::~CursorPointType() +{ +} + +const CursorPointType* CursorPointType::instance() +{ + static const CursorPointType t; + return &t; +} + +ObjectImp* CursorPointType::calc( const Args& parents, const KigDocument& ) const +{ + assert ( parents[0]->inherits( DoubleImp::stype() ) ); + assert ( parents[1]->inherits( DoubleImp::stype() ) ); + double a = static_cast<const DoubleImp*>( parents[0] )->data(); + double b = static_cast<const DoubleImp*>( parents[1] )->data(); + + return new BogusPointImp( Coordinate( a, b ) ); +} + +const ObjectImpType* CursorPointType::resultId() const +{ + return BogusPointImp::stype(); +} + +ObjectImp* ConstrainedPointType::calc( const Args& parents, const KigDocument& doc ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + double param = static_cast<const DoubleImp*>( parents[0] )->data(); + const Coordinate nc = static_cast<const CurveImp*>( parents[1] )->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; +} + +const ArgsParser::spec argsspecConstrainedPoint[] = +{ + { DoubleImp::stype(), "parameter", "SHOULD NOT BE SEEN", false }, + { CurveImp::stype(), "Constrain the point to this curve", "SHOULD NOT BE SEEN", true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConstrainedPointType ) + +ConstrainedPointType::ConstrainedPointType() + : ArgsParserObjectType( "ConstrainedPoint", argsspecConstrainedPoint, 2 ) +{ +} + +ConstrainedPointType::~ConstrainedPointType() +{ +} + +void FixedPointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const +{ + // fetch the old coord..; + std::vector<ObjectCalcer*> pa = ourobj.parents(); + assert( margsparser.checkArgs( pa ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.front() ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.back() ) ); + + ObjectConstCalcer* ox = static_cast<ObjectConstCalcer*>( pa.front() ); + ObjectConstCalcer* oy = static_cast<ObjectConstCalcer*>( pa.back() ); + + ox->setImp( new DoubleImp( to.x ) ); + oy->setImp( new DoubleImp( to.y ) ); +} + +void RelativePointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const +{ + // fetch the attach point..; + // this routine is tightly paired with what moveReferencePoint returns! + // right now moveReferencePoint always returns the origin + std::vector<ObjectCalcer*> pa = ourobj.parents(); + assert( margsparser.checkArgs( pa ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa[0] ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa[1] ) ); + + ObjectConstCalcer* ox = static_cast<ObjectConstCalcer*>( pa[0] ); + ObjectConstCalcer* oy = static_cast<ObjectConstCalcer*>( pa[1] ); + ObjectCalcer* ob = static_cast<ObjectCalcer*>( pa[2] ); + + Coordinate attach = ob->imp()->attachPoint(); + ox->setImp( new DoubleImp( to.x - attach.x ) ); + oy->setImp( new DoubleImp( to.y - attach.y ) ); +} + +void CursorPointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const +{ + // fetch the old coord..; + + std::vector<ObjectCalcer*> pa = ourobj.parents(); + assert( pa.size() == 2 ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.front() ) ); + assert( dynamic_cast<ObjectConstCalcer*>( pa.back() ) ); + + ObjectConstCalcer* ox = static_cast<ObjectConstCalcer*>( pa.front() ); + ObjectConstCalcer* oy = static_cast<ObjectConstCalcer*>( pa.back() ); + + ox->setImp( new DoubleImp( to.x ) ); + oy->setImp( new DoubleImp( to.y ) ); +} + +void ConstrainedPointType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& d ) const +{ + // fetch the CurveImp.. + std::vector<ObjectCalcer*> parents = ourobj.parents(); + assert( margsparser.checkArgs( parents ) ); + + assert( dynamic_cast<ObjectConstCalcer*>( parents[0] ) ); + ObjectConstCalcer* paramo = static_cast<ObjectConstCalcer*>( parents[0] ); + const CurveImp* ci = static_cast<const CurveImp*>( parents[1]->imp() ); + + // fetch the new param.. + const double np = ci->getParam( to, d ); + + paramo->setImp( new DoubleImp( np ) ); +} + +bool ConstrainedPointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool ConstrainedPointType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return false; +} + +bool FixedPointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool FixedPointType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool RelativePointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool RelativePointType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool CursorPointType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +static const ArgsParser::spec argsspecMidPoint[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another point" ), + I18N_NOOP( "Select the first of the two points of which you want to construct the midpoint..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another point" ), + I18N_NOOP( "Select the other of the two points of which you want to construct the midpoint..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MidPointType ) + +MidPointType::MidPointType() + : ObjectABType( "MidPoint", argsspecMidPoint, 2 ) +{ +} + +MidPointType::~MidPointType() +{ +} + +const MidPointType* MidPointType::instance() +{ + static const MidPointType t; + return &t; +} + +ObjectImp* MidPointType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new PointImp( ( a + b ) / 2 ); +} + +bool ConstrainedPointType::inherits( int type ) const +{ + return type == ID_ConstrainedPointType; +} + +const ConstrainedPointType* ConstrainedPointType::instance() +{ + static const ConstrainedPointType t; + return &t; +} + +bool FixedPointType::inherits( int type ) const +{ + return type == ID_FixedPointType; +} + +const FixedPointType* FixedPointType::instance() +{ + static const FixedPointType t; + return &t; +} + +const ObjectImpType* FixedPointType::resultId() const +{ + return PointImp::stype(); +} + +const RelativePointType* RelativePointType::instance() +{ + static const RelativePointType t; + return &t; +} + +const ObjectImpType* RelativePointType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* ConstrainedPointType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* CursorPointType::impRequirement( const ObjectImp* o, const Args& ) const +{ + if ( o->inherits( DoubleImp::stype() ) ) + return DoubleImp::stype(); + + if ( o->inherits( PointImp::stype() ) ) + return PointImp::stype(); + + return 0; +} + +bool CursorPointType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; +} + +std::vector<ObjectCalcer*> CursorPointType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; +} + +Args CursorPointType::sortArgs( const Args& args ) const +{ + return args; +} + +const ObjectImpType* MidPointType::resultId() const +{ + return PointImp::stype(); +} + +QStringList FixedPointType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set &Coordinate..." ); + ret << i18n( "Redefine" ); + return ret; +} + +QStringList ConstrainedPointType::specialActions() const +{ + QStringList ret; + ret << i18n( "Set &Parameter..." ); + ret << i18n( "Redefine" ); + return ret; +} + +static void redefinePoint( ObjectHolder* o, KigPart& d, KigWidget& w ) +{ + PointRedefineMode pm( o, d, w ); + d.runMode( &pm ); +} + +void FixedPointType::executeAction( + int i, ObjectHolder& oh, ObjectTypeCalcer& o, + KigPart& d, KigWidget& w, NormalMode& ) const +{ + switch( i ) + { + case 0: + { + bool ok = true; + assert ( o.imp()->inherits( PointImp::stype() ) ); + Coordinate oldc = static_cast<const PointImp*>( o.imp() )->coordinate(); + KigInputDialog::getCoordinate( + i18n( "Set Coordinate" ), + i18n( "Enter the new coordinate." ) + QString::fromLatin1( "<br>" ) + + d.document().coordinateSystem().coordinateFormatNoticeMarkup(), + &w, &ok, d.document(), &oldc ); + if ( ! ok ) break; + + MonitorDataObjects mon( getAllParents( &o ) ); + o.move( oldc, d.document() ); + KigCommand* kc = new KigCommand( d, PointImp::stype()->moveAStatement() ); + mon.finish( kc ); + + d.history()->addCommand( kc ); + break; + }; + case 1: + redefinePoint( &oh, d, w ); + break; + default: + assert( false ); + }; +} + +void ConstrainedPointType::executeAction( + int i, ObjectHolder& oh, ObjectTypeCalcer& o, KigPart& d, KigWidget& w, + NormalMode& ) const +{ + switch( i ) + { + case 1: + redefinePoint( &oh, d, w ); + break; + case 0: + { + std::vector<ObjectCalcer*> parents = o.parents(); + assert( dynamic_cast<ObjectConstCalcer*>( parents[0] ) && + parents[0]->imp()->inherits( DoubleImp::stype() ) ); + + ObjectConstCalcer* po = static_cast<ObjectConstCalcer*>( parents[0] ); + double oldp = static_cast<const DoubleImp*>( po->imp() )->data(); + + bool ok = true; + double newp = getDoubleFromUser( + i18n( "Set Point Parameter" ), i18n( "Choose the new parameter: " ), + oldp, &w, &ok, 0, 1, 4 ); + if ( ! ok ) return; + + MonitorDataObjects mon( parents ); + po->setImp( new DoubleImp( newp ) ); + KigCommand* kc = new KigCommand( d, i18n( "Change Parameter of Constrained Point" ) ); + mon.finish( kc ); + d.history()->addCommand( kc ); + break; + }; + default: + assert( false ); + }; +} + +const Coordinate FixedPointType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( PointImp::stype() ) ); + return static_cast<const PointImp*>( ourobj.imp() )->coordinate(); +} + +const Coordinate RelativePointType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( PointImp::stype() ) ); +// return static_cast<const PointImp*>( ourobj.imp() )->coordinate(); + return Coordinate( 0., 0. ); +} + +const Coordinate ConstrainedPointType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( PointImp::stype() ) ); + return static_cast<const PointImp*>( ourobj.imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> FixedPointType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + return ourobj.parents(); +} + +std::vector<ObjectCalcer*> RelativePointType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( ourobj.parents()[0] ); + ret.push_back( ourobj.parents()[1] ); + return ret; +} + +std::vector<ObjectCalcer*> ConstrainedPointType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( ourobj.parents()[0] ); + return ret; +} + +/* ----------------- Transport of measure ------------------------------ */ + +ObjectImp* MeasureTransportType::calc( const Args& parents, const KigDocument& doc ) const +{ + double measure; + + if ( parents.size() != 3 ) return new InvalidImp; + + if ( parents[0]->inherits (SegmentImp::stype()) ) + { + const SegmentImp* s = static_cast<const SegmentImp*>( parents[0] ); + measure = s->length(); + } else if ( parents[0]->inherits (ArcImp::stype()) ) + { + const ArcImp* s = static_cast<const ArcImp*>( parents[0] ); + measure = s->radius()*s->angle(); + } else return new InvalidImp; + + const Coordinate& p = static_cast<const PointImp*>( parents[2] )->coordinate(); + if ( parents[1]->inherits (LineImp::stype()) ) + { + const LineImp* c = static_cast<const LineImp*>( parents[1] ); + + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + const LineData line = c->data(); + const Coordinate dir = line.dir()/line.length(); + const Coordinate nc = p + measure*dir; + + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; + } else if ( parents[1]->inherits (CircleImp::stype()) ) + { + const CircleImp* c = static_cast<const CircleImp*>( parents[1] ); + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + double param = c->getParam( p, doc ); + measure /= 2*c->radius()*M_PI; + param += measure; + while (param > 1) param -= 1; + + const Coordinate nc = c->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; + } + + return new InvalidImp; +} + +// I18N_NOOP( "Select the segment/arc to transport on the circle/line..." ), false }, +// I18N_NOOP( "Select the circle/line on which to transport a measure..." ), true }, +// I18N_NOOP( "Select a point on the circle/line..." ), false } + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MeasureTransportType ) + +MeasureTransportType::MeasureTransportType() + : ObjectType( "TransportOfMeasure" ) +{ +} + +MeasureTransportType::~MeasureTransportType() +{ +} + +const MeasureTransportType* MeasureTransportType::instance() +{ + static const MeasureTransportType t; + return &t; +} + +const ObjectImpType* MeasureTransportType::resultId() const +{ + return PointImp::stype(); +} + +const ObjectImpType* MeasureTransportType::impRequirement( const ObjectImp* obj, const Args& ) const +{ + if ( obj->inherits( PointImp::stype () ) ) + return PointImp::stype (); + + if ( obj->inherits( LineImp::stype () ) ) + return LineImp::stype (); + + if ( obj->inherits( CircleImp::stype () ) ) + return CircleImp::stype (); + + if ( obj->inherits( SegmentImp::stype () ) ) + return SegmentImp::stype (); + + if ( obj->inherits( ArcImp::stype () ) ) + return ArcImp::stype (); + + return 0; +} + +bool MeasureTransportType::isDefinedOnOrThrough( const ObjectImp* o, const Args& ) const +{ + if ( o->inherits( LineImp::stype() ) ) return true; + if ( o->inherits( CircleImp::stype() ) ) return true; + return false; +} + +std::vector<ObjectCalcer*> MeasureTransportType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; /* should already be in correct order */ +} + +Args MeasureTransportType::sortArgs( const Args& args ) const +{ + return args; +} + +/* - transport of measure (old, for compatibility with prev. kig files) - */ + +ObjectImp* MeasureTransportTypeOld::calc( const Args& parents, const KigDocument& doc ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const CircleImp* c = static_cast<const CircleImp*>( parents[0] ); + const Coordinate& p = static_cast<const PointImp*>( parents[1] )->coordinate(); + + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + const SegmentImp* s = static_cast<const SegmentImp*>( parents[2] ); + double param = c->getParam( p, doc ); + double measure = s->length(); + measure /= 2*c->radius()*M_PI; + param += measure; + while (param > 1) param -= 1; + + const Coordinate nc = c->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; +} + +static const ArgsParser::spec argsspecMeasureTransportOld[] = +{ + { CircleImp::stype(), "Transport a measure on this circle", + I18N_NOOP( "Select the circle on which to transport a measure..." ), true }, + { PointImp::stype(), "Start transport from this point of the circle", + I18N_NOOP( "Select a point on the circle..." ), false }, + { SegmentImp::stype(), "Segment to transport", + I18N_NOOP( "Select the segment to transport on the circle..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( MeasureTransportTypeOld ) + +MeasureTransportTypeOld::MeasureTransportTypeOld() + : ArgsParserObjectType( "MeasureTransport", argsspecMeasureTransportOld, 3 ) +{ +} + +MeasureTransportTypeOld::~MeasureTransportTypeOld() +{ +} + +const MeasureTransportTypeOld* MeasureTransportTypeOld::instance() +{ + static const MeasureTransportTypeOld t; + return &t; +} + +const ObjectImpType* MeasureTransportTypeOld::resultId() const +{ + return PointImp::stype(); +} + +/* ----------------- end transport of measure ------------------------- */ + diff --git a/kig/objects/point_type.h b/kig/objects/point_type.h new file mode 100644 index 00000000..231ad6c5 --- /dev/null +++ b/kig/objects/point_type.h @@ -0,0 +1,159 @@ +// Copyright (C) 2002 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_POINT_TYPE_H +#define KIG_OBJECTS_POINT_TYPE_H + +#include "base_type.h" +#include "common.h" +#include "circle_imp.h" + +class FixedPointType + : public ArgsParserObjectType +{ + FixedPointType(); + ~FixedPointType(); + + static const ArgsParser::spec argsspec[1]; +public: + static const FixedPointType* instance(); + + bool inherits( int type ) const; + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, + KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class RelativePointType + : public ArgsParserObjectType +{ + RelativePointType(); + ~RelativePointType(); + + static const ArgsParser::spec argsspec[1]; +public: + static const RelativePointType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; + +// QStringList specialActions() const; +// void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& t, +// KigPart& d, KigWidget& w, NormalMode& m ) const; +}; + +class CursorPointType + : public ObjectType +{ + CursorPointType(); + ~CursorPointType(); + +public: + static const CursorPointType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + bool canMove( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConstrainedPointType + : public ArgsParserObjectType +{ + ConstrainedPointType(); + ~ConstrainedPointType(); +public: + static const ConstrainedPointType* instance(); + + bool inherits( int type ) const; + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + const ObjectImpType* resultId() const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder&, ObjectTypeCalcer& o, KigPart& d, KigWidget& w, + NormalMode& m ) const; +}; + +class MidPointType + : public ObjectABType +{ + MidPointType(); + ~MidPointType(); +public: + static const MidPointType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class MeasureTransportType + : public ObjectType +{ + MeasureTransportType(); + ~MeasureTransportType(); +public: + static const MeasureTransportType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args )const; + Args sortArgs( const Args& args ) const; +}; + +class MeasureTransportTypeOld + : public ArgsParserObjectType +{ + MeasureTransportTypeOld(); + ~MeasureTransportTypeOld(); +public: + static const MeasureTransportTypeOld* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/polygon_imp.cc b/kig/objects/polygon_imp.cc new file mode 100644 index 00000000..08215bfb --- /dev/null +++ b/kig/objects/polygon_imp.cc @@ -0,0 +1,550 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// 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 "polygon_imp.h" + +#include "bogus_imp.h" +#include "line_imp.h" +#include "point_imp.h" + +#include "../misc/common.h" +#include "../misc/coordinate.h" +#include "../misc/kigpainter.h" +#include "../misc/kigtransform.h" + +#include "../kig/kig_document.h" +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> + +PolygonImp::PolygonImp( uint npoints, const std::vector<Coordinate>& points, + const Coordinate& centerofmass ) + : mnpoints( npoints ), mpoints( points ), mcenterofmass( centerofmass ) +{ +// mpoints = points; +} + +PolygonImp::PolygonImp( const std::vector<Coordinate>& points ) +{ + uint npoints = points.size(); + Coordinate centerofmassn = Coordinate( 0, 0 ); + + for ( uint i = 0; i < npoints; ++i ) + { + centerofmassn += points[i]; + } + mpoints = points; + mcenterofmass = centerofmassn/npoints; + mnpoints = npoints; +} + +PolygonImp::~PolygonImp() +{ +} + +Coordinate PolygonImp::attachPoint() const +{ + return mcenterofmass; +} + +ObjectImp* PolygonImp::transform( const Transformation& t ) const +{ +/*mp: + * any projective transformation makes sense for a polygon, + * since segments transform into segments (but see below...) + * of course regular polygons will no longer be + * regular if t is not homothetic. + * for projective transformations the polygon could transform to + * an unbounded nonconnected polygon; this happens if some side + * of the polygon crosses the critical line that maps to infinity + * this can be easily checked using the getProjectiveIndicator + * function + */ +// if ( ! t.isHomothetic() ) +// return new InvalidImp(); + + if ( ! t.isAffine() ) /* in this case we need a more extensive test */ + { + double maxp = -1.0; + double minp = 1.0; + for ( uint i = 0; i < mpoints.size(); ++i ) + { + double p = t.getProjectiveIndicator( mpoints[i] ); + if ( p > maxp ) maxp = p; + if ( p < minp ) minp = p; + } + if ( maxp > 0 && minp < 0 ) return new InvalidImp; + } + std::vector<Coordinate> np; + for ( uint i = 0; i < mpoints.size(); ++i ) + { + Coordinate nc = t.apply( mpoints[i] ); + if ( !nc.valid() ) + return new InvalidImp; + np.push_back( nc ); + } + return new PolygonImp( np ); +} + +void PolygonImp::draw( KigPainter& p ) const +{ + p.drawPolygon( mpoints ); +} + +bool PolygonImp::isInPolygon( const Coordinate& p ) const +{ + // (algorithm sent to me by domi) + // We intersect with the horizontal ray from point to the right and + // count the number of intersections. That, along with some + // minor optimalisations of the intersection test.. + bool inside_flag = false; + double cx = p.x; + double cy = p.y; + + Coordinate prevpoint = mpoints.back(); + bool prevpointbelow = mpoints.back().y >= cy; + for ( uint i = 0; i < mpoints.size(); ++i ) + { + Coordinate point = mpoints[i]; + bool pointbelow = point.y >= cy; + if ( prevpointbelow != pointbelow ) + { + // possibility of intersection: points on different side from + // the X axis + //bool rightofpt = point.x >= cx; + // mp: we need to be a little bit more conservative here, in + // order to treat properly the case when the point is on the + // boundary + //if ( rightofpt == ( prevpoint.x >= cx ) ) + if ( ( point.x - cx )*(prevpoint.x - cx ) > 0 ) + { + // points on same side of Y axis -> easy to test intersection + // intersection iff one point to the right of c + if ( point.x >= cx ) + inside_flag = !inside_flag; + } + else + { + // points on different sides of Y axis -> we need to calculate + // the intersection. + // mp: we want to check if the point is on the boundary, and + // return false in such case + double num = ( point.y - cy )*( prevpoint.x - point.x ); + double den = prevpoint.y - point.y; + if ( num == den*( point.x - cx ) ) return false; + if ( num/den <= point.x - cx ) + inside_flag = !inside_flag; + } + } + prevpoint = point; + prevpointbelow = pointbelow; + } + return inside_flag; +} +#define selectpolygonwithinside 1 +#ifdef selectpolygonwithinside +bool PolygonImp::contains( const Coordinate& p, int, const KigWidget& ) const +{ + return isInPolygon( p ); +} +#else +bool PolygonImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + bool ret = false; + uint reduceddim = mpoints.size() - 1; + for ( uint i = 0; i < reduceddim; ++i ) + { + ret |= isOnSegment( p, mpoints[i], mpoints[i+1], w.screenInfo().normalMiss( width ) ); + } + ret |= isOnSegment( p, mpoints[reduceddim], mpoints[0], w.screenInfo().normalMiss( width ) ); + + return ret; +} +#endif + +bool PolygonImp::inRect( const Rect& r, int width, const KigWidget& w ) const +{ + bool ret = false; + uint reduceddim = mpoints.size() - 1; + for ( uint i = 0; i < reduceddim; ++i ) + { + SegmentImp* s = new SegmentImp( mpoints[i], mpoints[i+1] ); + ret |= lineInRect( r, mpoints[i], mpoints[i+1], width, s, w ); + delete s; + s = 0; + } + SegmentImp* t = new SegmentImp( mpoints[reduceddim], mpoints[0] ); + ret |= lineInRect( r, mpoints[reduceddim], mpoints[0], width, t, w ); + delete t; + t = 0; + + return ret; +} + +bool PolygonImp::valid() const +{ + return true; +} + +const uint PolygonImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 5; +} + +const QCStringList PolygonImp::propertiesInternalNames() const +{ + QCStringList l = Parent::propertiesInternalNames(); + l += "polygon-number-of-sides"; + l += "polygon-perimeter"; + l += "polygon-surface"; + l += "polygon-center-of-mass"; + l += "polygon-winding-number"; + assert( l.size() == PolygonImp::numberOfProperties() ); + return l; +} + +const QCStringList PolygonImp::properties() const +{ + QCStringList l = Parent::properties(); + l += I18N_NOOP( "Number of sides" ); + l += I18N_NOOP( "Perimeter" ); + l += I18N_NOOP( "Surface" ); + l += I18N_NOOP( "Center of Mass of the Vertices" ); + l += I18N_NOOP( "Winding Number" ); + assert( l.size() == PolygonImp::numberOfProperties() ); + return l; +} + +const ObjectImpType* PolygonImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + else return PolygonImp::stype(); +} + +const char* PolygonImp::iconForProperty( uint which ) const +{ + assert( which < PolygonImp::numberOfProperties() ); + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() ) + return "en"; // number of sides + else if ( which == Parent::numberOfProperties() + 1 ) + return "circumference"; // perimeter + else if ( which == Parent::numberOfProperties() + 2 ) + return "areaCircle"; // surface + else if ( which == Parent::numberOfProperties() + 3 ) + return "point"; // center of mass + else if ( which == Parent::numberOfProperties() + 4 ) + return "w"; // winding number + else assert( false ); + return ""; +} + +ObjectImp* PolygonImp::property( uint which, const KigDocument& w ) const +{ + assert( which < PolygonImp::numberOfProperties() ); + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() ) + { + // number of points + return new IntImp( mnpoints ); + } + else if ( which == Parent::numberOfProperties() + 1) + { + double circumference = 0.; + // circumference + for ( uint i = 0; i < mpoints.size(); ++i ) + { + uint prev = ( i + mpoints.size() - 1 ) % mpoints.size(); + circumference += ( mpoints[i] - mpoints[prev] ).length(); + } + return new DoubleImp( circumference ); + } + else if ( which == Parent::numberOfProperties() + 2) + { + int wn = windingNumber (); // not able to compute area for such polygons... + if ( wn < 0 ) wn = -wn; + if ( wn != 1 ) return new InvalidImp; + double surface2 = 0.0; + Coordinate prevpoint = mpoints.back(); + for ( uint i = 0; i < mpoints.size(); ++i ) + { + Coordinate point = mpoints[i]; + surface2 += ( point.x - prevpoint.x ) * ( point.y + prevpoint.y ); + prevpoint = point; + } + return new DoubleImp( fabs( surface2 / 2 ) ); + } + else if ( which == Parent::numberOfProperties() + 3 ) + { + return new PointImp( mcenterofmass ); + } + else if ( which == Parent::numberOfProperties() + 4 ) + { + // winding number + return new IntImp( windingNumber() ); + } + else assert( false ); + return new InvalidImp; +} + +const std::vector<Coordinate> PolygonImp::points() const +{ + std::vector<Coordinate> np; + np.reserve( mpoints.size() ); + std::copy( mpoints.begin(), mpoints.end(), std::back_inserter( np ) ); + return np; +} + +const uint PolygonImp::npoints() const +{ + return mnpoints; +} + +PolygonImp* PolygonImp::copy() const +{ + return new PolygonImp( mpoints ); +} + +void PolygonImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool PolygonImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( PolygonImp::stype() ) && + static_cast<const PolygonImp&>( rhs ).points() == mpoints; +} + +const ObjectImpType* PolygonImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "polygon", + I18N_NOOP( "polygon" ), + I18N_NOOP( "Select this polygon" ), + I18N_NOOP( "Select polygon %1" ), + I18N_NOOP( "Remove a Polygon" ), + I18N_NOOP( "Add a Polygon" ), + I18N_NOOP( "Move a Polygon" ), + I18N_NOOP( "Attach to this polygon" ), + I18N_NOOP( "Show a Polygon" ), + I18N_NOOP( "Hide a Polygon" ) + ); + + return &t; +} + +const ObjectImpType* PolygonImp::stype3() +{ + static const ObjectImpType t3( + PolygonImp::stype(), "triangle", + I18N_NOOP( "triangle" ), + I18N_NOOP( "Select this triangle" ), + I18N_NOOP( "Select triangle %1" ), + I18N_NOOP( "Remove a Triangle" ), + I18N_NOOP( "Add a Triangle" ), + I18N_NOOP( "Move a Triangle" ), + I18N_NOOP( "Attach to this triangle" ), + I18N_NOOP( "Show a Triangle" ), + I18N_NOOP( "Hide a Triangle" ) + ); + + return &t3; +} + +const ObjectImpType* PolygonImp::stype4() +{ + static const ObjectImpType t4( + PolygonImp::stype(), "quadrilateral", + I18N_NOOP( "quadrilateral" ), + I18N_NOOP( "Select this quadrilateral" ), + I18N_NOOP( "Select quadrilateral %1" ), + I18N_NOOP( "Remove a Quadrilateral" ), + I18N_NOOP( "Add a Quadrilateral" ), + I18N_NOOP( "Move a Quadrilateral" ), + I18N_NOOP( "Attach to this quadrilateral" ), + I18N_NOOP( "Show a Quadrilateral" ), + I18N_NOOP( "Hide a Quadrilateral" ) + ); + + return &t4; +} + +const ObjectImpType* PolygonImp::type() const +{ + uint n = mpoints.size(); + + if ( n == 3 ) return PolygonImp::stype3(); + if ( n == 4 ) return PolygonImp::stype4(); + return PolygonImp::stype(); +} + +bool PolygonImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + assert( which < PolygonImp::numberOfProperties() ); + if ( which < Parent::numberOfProperties() ) + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); + return false; +} + +Rect PolygonImp::surroundingRect() const +{ + Rect r( 0., 0., 0., 0. ); + for ( uint i = 0; i < mpoints.size(); ++i ) + { + r.setContains( mpoints[i] ); + } + return r; +} + +int PolygonImp::windingNumber() const +{ + /* + * this is defined as the sum of the external angles while at + * all vertices, then normalized by 2pi. The external angle + * is the angle we steer at each vertex while we walk along the + * boundary of the polygon. + * In the end we only need to count how many time we cross the (1,0) + * direction (positive x-axis) with a positive sign if we cross while + * steering left and a negative sign viceversa + */ + + int winding = 0; + uint npoints = mpoints.size(); + Coordinate prevside = mpoints[0] - mpoints[npoints-1]; + for ( uint i = 0; i < npoints; ++i ) + { + uint nexti = i + 1; + if ( nexti >= npoints ) nexti = 0; + Coordinate side = mpoints[nexti] - mpoints[i]; + double vecprod = side.x*prevside.y - side.y*prevside.x; + int steeringdir = ( vecprod > 0 ) ? 1 : -1; + if ( vecprod == 0.0 || side.y*prevside.y > 0 ) + { + prevside = side; + continue; // cannot cross the (1,0) direction + } + if ( side.y*steeringdir < 0 && prevside.y*steeringdir >= 0 ) + winding -= steeringdir; + prevside = side; + } + return winding; +} + +bool PolygonImp::isMonotoneSteering() const +{ + /* + * returns true if while walking along the boundary, + * steering is always in the same direction + */ + + uint npoints = mpoints.size(); + Coordinate prevside = mpoints[0] - mpoints[npoints-1]; + int prevsteeringdir = 0; + for ( uint i = 0; i < npoints; ++i ) + { + uint nexti = i + 1; + if ( nexti >= npoints ) nexti = 0; + Coordinate side = mpoints[nexti] - mpoints[i]; + double vecprod = side.x*prevside.y - side.y*prevside.x; + int steeringdir = ( vecprod > 0 ) ? 1 : -1; + if ( vecprod == 0.0 ) + { + prevside = side; + continue; // going straight + } + if ( prevsteeringdir*steeringdir < 0 ) return false; + prevside = side; + prevsteeringdir = steeringdir; + } + return true; +} + +bool PolygonImp::isConvex() const +{ + if ( ! isMonotoneSteering() ) return false; + int winding = windingNumber(); + if ( winding < 0 ) winding = -winding; + assert ( winding > 0 ); + return winding == 1; +} + +std::vector<Coordinate> computeConvexHull( const std::vector<Coordinate>& points ) +{ + /* + * compute the convex hull of the set of points, the resulting list + * is the vertices of the resulting polygon listed in a counter clockwise + * order. This algorithm is on order n^2, probably suboptimal, but + * we don't expect to have large values for n. + */ + + if ( points.size() < 3 ) return points; + std::vector<Coordinate> worklist = points; + std::vector<Coordinate> result; + + double ymin = worklist[0].y; + uint imin = 0; + for ( uint i = 1; i < worklist.size(); ++i ) + { + if ( worklist[i].y < ymin ) + { + ymin = worklist[i].y; + imin = i; + } + } + + // worklist[imin] is definitely on the convex hull, let's start from there + result.push_back( worklist[imin] ); + Coordinate startpoint = worklist[imin]; + Coordinate apoint = worklist[imin]; + double aangle = 0.0; + + while ( ! worklist.empty() ) + { + int besti = -1; + double anglemin = 10000.0; + for ( uint i = 0; i < worklist.size(); ++i ) + { + if ( worklist[i] == apoint ) continue; + Coordinate v = worklist[i] - apoint; + double angle = std::atan2( v.y, v.x ); + while ( angle < aangle ) angle += 2*M_PI; + if ( angle < anglemin ) + { // found a better point + besti = i; + anglemin = angle; + } + } + + if ( besti < 0 ) return result; // this happens, e.g. if all points coincide + apoint = worklist[besti]; + aangle = anglemin; + if ( apoint == startpoint ) + { + return result; + } + result.push_back( apoint ); + worklist.erase( worklist.begin() + besti, worklist.begin() + besti + 1 ); + } + assert( false ); + return result; +} diff --git a/kig/objects/polygon_imp.h b/kig/objects/polygon_imp.h new file mode 100644 index 00000000..9a25d516 --- /dev/null +++ b/kig/objects/polygon_imp.h @@ -0,0 +1,94 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// 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. + +#ifndef KIG_OBJECTS_POLYGON_IMP_H +#define KIG_OBJECTS_POLYGON_IMP_H + +#include "object_imp.h" +#include "../misc/coordinate.h" +#include <vector> + +/** + * An ObjectImp representing a polygon. + */ +class PolygonImp + : public ObjectImp +{ + uint mnpoints; + std::vector<Coordinate> mpoints; + Coordinate mcenterofmass; +public: + typedef ObjectImp Parent; + /** + * Returns the ObjectImpType representing the PolygonImp type.. + */ + static const ObjectImpType* stype(); + static const ObjectImpType* stype3(); + static const ObjectImpType* stype4(); + + /** + * Constructs a polygon. + */ + PolygonImp( const std::vector<Coordinate>& points ); + PolygonImp( const uint nsides, const std::vector<Coordinate>& points, + const Coordinate& centerofmass ); + ~PolygonImp(); + PolygonImp* copy() const; + + Coordinate attachPoint() const; + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + /** + * Returns the vector with polygon points. + */ + const std::vector<Coordinate> points() const; + /** + * Returns the center of mass of the polygon. + */ + const Coordinate centerOfMass() const; + /** + * Returns the number of points of this polygon. + */ + const uint npoints() const; + + bool equals( const ObjectImp& rhs ) const; + bool isInPolygon( const Coordinate& p ) const; + int windingNumber() const; + bool isMonotoneSteering() const; + bool isConvex() const; +}; + +std::vector<Coordinate> computeConvexHull( const std::vector<Coordinate>& points ); + +#endif diff --git a/kig/objects/polygon_type.cc b/kig/objects/polygon_type.cc new file mode 100644 index 00000000..bca867da --- /dev/null +++ b/kig/objects/polygon_type.cc @@ -0,0 +1,669 @@ +// Copyright (C) 2003 Maurizio Paolini <paolini@dmf.unicatt.it> + +// 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 "polygon_type.h" + +#include "bogus_imp.h" +#include "line_imp.h" +#include "point_imp.h" +#include "polygon_imp.h" +#include "object_calcer.h" + +#include "../misc/common.h" + +#include <klocale.h> +#include <cmath> +#include <vector> + +/* + * triangle by its vertices + */ + +static const char triangle_constructstatement[] = I18N_NOOP( "Construct a triangle with this vertex" ); +static const char triangle_constructstatement2[] = I18N_NOOP( "Select a point to be a vertex of the new triangle..." ); + +static const struct ArgsParser::spec argsspecTriangleB3P[] = +{ + { PointImp::stype(), triangle_constructstatement, triangle_constructstatement2, true }, + { PointImp::stype(), triangle_constructstatement, triangle_constructstatement2, true }, + { PointImp::stype(), triangle_constructstatement, triangle_constructstatement2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TriangleB3PType ) + +TriangleB3PType::TriangleB3PType() + : ArgsParserObjectType( "TriangleB3P", argsspecTriangleB3P, 3 ) +{ +} + +TriangleB3PType::~TriangleB3PType() +{ +} + +const TriangleB3PType* TriangleB3PType::instance() +{ + static const TriangleB3PType s; + return &s; +} + +ObjectImp* TriangleB3PType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents, 1 ) ) return new InvalidImp; + std::vector<Coordinate> points; + + Coordinate centerofmass3 = Coordinate( 0, 0 ); + for ( Args::const_iterator i = parents.begin(); i != parents.end(); ++i ) + { + Coordinate point = static_cast<const PointImp*>( *i )->coordinate(); + centerofmass3 += point; + points.push_back( point ); + } + return new PolygonImp( 3, points, centerofmass3/3 ); +} + +const ObjectImpType* TriangleB3PType::resultId() const +{ + return PolygonImp::stype(); +} + +bool TriangleB3PType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +} + +bool TriangleB3PType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return parents[0]->isFreelyTranslatable() && + parents[1]->isFreelyTranslatable() && + parents[2]->isFreelyTranslatable(); +} + +void TriangleB3PType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + const Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + const Coordinate c = static_cast<const PointImp*>( parents[2]->imp() )->coordinate(); + if ( parents[0]->canMove() ) + parents[0]->move( to, d ); + if ( parents[1]->canMove() ) + parents[1]->move( to + b - a, d ); + if ( parents[2]->canMove() ) + parents[2]->move( to + c - a, d ); +} + +const Coordinate TriangleB3PType::moveReferencePoint( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + assert( margsparser.checkArgs( parents ) ); + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> TriangleB3PType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + std::vector<ObjectCalcer*> tmp = parents[0]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[1]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[2]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + ret.insert( parents.begin(), parents.end() ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +/* + * generic polygon + */ + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonBNPType ) + +PolygonBNPType::PolygonBNPType() + : ObjectType( "PolygonBNP" ) +{ +} + +PolygonBNPType::~PolygonBNPType() +{ +} + +const PolygonBNPType* PolygonBNPType::instance() +{ + static const PolygonBNPType s; + return &s; +} + +ObjectImp* PolygonBNPType::calc( const Args& parents, const KigDocument& ) const +{ + uint count = parents.size(); + assert (count >= 3); /* non sono ammessi poligoni con meno di tre lati */ +// if ( parents[0] != parents[count] ) return new InvalidImp; + std::vector<Coordinate> points; + + uint npoints = 0; + Coordinate centerofmassn = Coordinate( 0, 0 ); + + for ( uint i = 0; i < count; ++i ) + { + npoints++; + if ( ! parents[i]->inherits( PointImp::stype() ) ) return new InvalidImp; + Coordinate point = static_cast<const PointImp*>( parents[i] )->coordinate(); + centerofmassn += point; + points.push_back( point ); + } + return new PolygonImp( npoints, points, centerofmassn/npoints ); +} + +const ObjectImpType* PolygonBNPType::resultId() const +{ + return PolygonImp::stype(); +} + +const ObjectImpType* PolygonBNPType::impRequirement( const ObjectImp*, const Args& ) const +{ + return PointImp::stype(); +} + +bool PolygonBNPType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; /* should be true? */ +} + +std::vector<ObjectCalcer*> PolygonBNPType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; /* should already be in correct order */ +} + +Args PolygonBNPType::sortArgs( const Args& args ) const +{ + return args; +} + +bool PolygonBNPType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +} + +bool PolygonBNPType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + for ( uint i = 0; i < parents.size(); ++i ) + { + if ( !parents[i]->isFreelyTranslatable() ) return false; + } + return true; +} + +void PolygonBNPType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + const Coordinate ref = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + for ( uint i = 0; i < parents.size(); ++i ) + { + const Coordinate a = static_cast<const PointImp*>( parents[i]->imp() )->coordinate(); + parents[i]->move( to + a - ref, d ); + } +} + +const Coordinate PolygonBNPType::moveReferencePoint( const ObjectTypeCalcer& o +) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> PolygonBNPType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + for ( uint i = 0; i < parents.size(); ++i ) + { + std::vector<ObjectCalcer*> tmp = parents[i]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + } + ret.insert( parents.begin(), parents.end() ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +/* + * regular polygon by center and vertex + */ + +//static const char constructpoligonthroughpointstat[] = I18N_NOOP( "Construct a polygon with this vertex" ); +// +//static const char constructpoligonwithcenterstat[] = I18N_NOOP( "Construct a polygon with this center" ); +// +//static const ArgsParser::spec argsspecPoligonBCV[] = +//{ +// { PointImp::stype(), constructpoligonwithcenterstat, +// I18N_NOOP( "Select the center of the new polygon..." ), false }, +// { PointImp::stype(), constructpoligonthroughpointstat, +// I18N_NOOP( "Select a vertex for the new polygon..." ), true }, +// { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +//}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonBCVType ) + +PolygonBCVType::PolygonBCVType() + : ObjectType( "PoligonBCV" ) +// we keep the name "PoligonBCV" although syntactically incorrect for +// compatibility reasons with old kig files +// : ArgsParserObjectType( "PoligonBCV", argsspecPoligonBCV, 3 ) +{ +} + +PolygonBCVType::~PolygonBCVType() +{ +} + +const PolygonBCVType* PolygonBCVType::instance() +{ + static const PolygonBCVType s; + return &s; +} + +ObjectImp* PolygonBCVType::calc( const Args& parents, const KigDocument& ) const +{ + if ( parents.size() < 3 || parents.size() > 4 ) return new InvalidImp; + + if ( ( ! parents[0]->inherits( PointImp::stype() ) ) || + ( ! parents[1]->inherits( PointImp::stype() ) ) || + ( ! parents[2]->inherits( IntImp::stype() ) ) ) + return new InvalidImp; + + const Coordinate center = + static_cast<const PointImp*>( parents[0] )->coordinate(); + const Coordinate vertex = + static_cast<const PointImp*>( parents[1] )->coordinate(); + const int sides = + static_cast<const IntImp*>( parents[2] )->data(); + int twist = 1; + if ( parents.size() == 4 ) + { + if ( ! parents[3]->inherits( IntImp::stype() ) ) return new InvalidImp; + twist = static_cast<const IntImp*>( parents[3] )->data(); + } + std::vector<Coordinate> vertexes; + + double dx = vertex.x - center.x; + double dy = vertex.y - center.y; + + for ( int i = 1; i <= sides; i++ ) + { + double alfa = 2*twist*M_PI/sides; + double theta1 = alfa*i - alfa; + double ctheta1 = cos(theta1); + double stheta1 = sin(theta1); + + Coordinate v1 = center + Coordinate( ctheta1*dx - stheta1*dy, + stheta1*dx + ctheta1*dy ); + vertexes.push_back( v1 ); + } + return new PolygonImp( uint (sides), vertexes, center ); +} + +const ObjectImpType* PolygonBCVType::resultId() const +{ + return SegmentImp::stype(); +} + +const ObjectImpType* PolygonBCVType::impRequirement( const ObjectImp* obj, const Args& ) const +{ + if ( obj->inherits( PointImp::stype() ) ) + return PointImp::stype(); + + if ( obj->inherits( IntImp::stype() ) ) + return IntImp::stype(); + + return 0; +} + +bool PolygonBCVType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; /* should be true? */ +} + +std::vector<ObjectCalcer*> PolygonBCVType::sortArgs( const std::vector<ObjectCalcer*>& args ) const +{ + return args; /* should already be in correct order */ +} + +Args PolygonBCVType::sortArgs( const Args& args ) const +{ + return args; +} + +bool PolygonBCVType::canMove( const ObjectTypeCalcer& o ) const +{ + return isFreelyTranslatable( o ); +} + +bool PolygonBCVType::isFreelyTranslatable( const ObjectTypeCalcer& o ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + return parents[0]->isFreelyTranslatable() && + parents[1]->isFreelyTranslatable(); +} + +void PolygonBCVType::move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + // assert( margsparser.checkArgs( parents ) ); + if ( ! parents[0]->imp()->inherits( PointImp::stype() ) || + ! parents[1]->imp()->inherits( PointImp::stype() ) ) return; + + const Coordinate a = static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); + const Coordinate b = static_cast<const PointImp*>( parents[1]->imp() )->coordinate(); + parents[0]->move( to, d ); + parents[1]->move( to + b - a, d ); +} + +const Coordinate PolygonBCVType::moveReferencePoint( const ObjectTypeCalcer& o) const +{ + std::vector<ObjectCalcer*> parents = o.parents(); + // assert( margsparser.checkArgs( parents ) ); + if ( ! parents[0]->imp()->inherits( PointImp::stype() ) ) return Coordinate::invalidCoord(); + + return static_cast<const PointImp*>( parents[0]->imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> PolygonBCVType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + std::vector<ObjectCalcer*> parents = ourobj.parents(); + std::set<ObjectCalcer*> ret; + std::vector<ObjectCalcer*> tmp = parents[0]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + tmp = parents[1]->movableParents(); + ret.insert( tmp.begin(), tmp.end() ); + ret.insert( &parents[0], &parents[1] ); + return std::vector<ObjectCalcer*>( ret.begin(), ret.end() ); +} + +/* polygon-line intersection */ + +static const ArgsParser::spec argsspecPolygonLineIntersection[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Intersect this polygon with a line" ), + I18N_NOOP( "Select the polygon of which you want the intersection with a line..." ), false }, + { AbstractLineImp::stype(), "Intersect this line with a polygon", "Select the line of which you want the intersection with a polygon...", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonLineIntersectionType ) + +PolygonLineIntersectionType::PolygonLineIntersectionType() + : ArgsParserObjectType( "PolygonLineIntersection", argsspecPolygonLineIntersection, 2 ) +{ +} + +PolygonLineIntersectionType::~PolygonLineIntersectionType() +{ +} + +const PolygonLineIntersectionType* PolygonLineIntersectionType::instance() +{ + static const PolygonLineIntersectionType t; + return &t; +} + +/* + * Intersection of a polygon and a line/ray/segment. + * + * geometrically speaking the result is always a collection of + * collinear nonintersecting open segments (at most one if the + * polygon is convex). Since we don't know in advance how many + * segments will result, the obvious choice is to return an + * InvalidImp in cases when the result is *not* a single segment + * + * computing the two ends of this segment is more tricky then one + * expects especially when intersecting segments/rays. + * + * particularly "difficult" situations are those where we intersect + * a segment/ray with an/the endpoint coinciding with a vertex of + * the polygon, especially if that vertex is a "reentrant" (concave) + * vertex of the polygon. + */ + +ObjectImp* PolygonLineIntersectionType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const PolygonImp* polygon = static_cast<const PolygonImp*>( parents[0] ); + const std::vector<Coordinate> ppoints = polygon->points(); + const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data(); + Coordinate intersections[2]; + uint whichintersection = 0; + + bool boundleft = false; + bool boundright = false; + if ( parents[1]->inherits( SegmentImp::stype() ) ) + { + boundleft = boundright = true; + } + if ( parents[1]->inherits( RayImp::stype() ) ) + { + boundleft = true; + } + Coordinate a = line.a; + double abx = line.b.x - a.x; + double aby = line.b.y - a.y; + + double leftendinside = false; + double rightendinside = false; + Coordinate prevpoint = ppoints.back() - a; + bool prevpointbelow = ( abx*prevpoint.y <= aby*prevpoint.x ); + for ( uint i = 0; i < ppoints.size(); ++i ) + { + Coordinate point = ppoints[i] - a; + bool pointbelow = ( abx*point.y <= aby*point.x ); + if ( pointbelow != prevpointbelow ) + { + /* found an intersection with the support line + * compute the value of the parameter... + */ + double dcx = point.x - prevpoint.x; + double dcy = point.y - prevpoint.y; + double num = point.x*dcy - point.y*dcx; + double den = abx*dcy - aby*dcx; + if ( std::fabs( den ) <= 1.e-6*std::fabs( num ) ) continue; //parallel + double t = num/den; + if ( boundleft && t <= 0 ) + { + leftendinside = !leftendinside; + } + else if ( boundright && t >= 1 ) + { + rightendinside = !rightendinside; + } + else + { + if ( whichintersection >= 2 ) return new InvalidImp; + intersections[whichintersection++] = a + t*Coordinate( abx, aby ); + } + } + prevpoint = point; + prevpointbelow = pointbelow; + } + + if ( leftendinside ) + { + if ( whichintersection >= 2 ) return new InvalidImp; + intersections[whichintersection++] = a; + } + + if ( rightendinside ) + { + if ( whichintersection >= 2 ) return new InvalidImp; + intersections[whichintersection++] = line.b; + } + + switch (whichintersection) + { + case 1: /* just for completeness: this should never happen */ + return new PointImp( intersections[0] ); + break; + case 2: + return new SegmentImp( intersections[0], intersections[1] ); + break; + case 0: + default: + return new InvalidImp; + break; + } +} + +const ObjectImpType* PolygonLineIntersectionType::resultId() const +{ + return SegmentImp::stype(); +} + +/* polygon vertices */ + +static const ArgsParser::spec argsspecPolygonVertex[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Construct the vertices of this polygon" ), + I18N_NOOP( "Select the polygon of which you want to construct the vertices..." ), true }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonVertexType ) + +PolygonVertexType::PolygonVertexType() + : ArgsParserObjectType( "PolygonVertex", argsspecPolygonVertex, 2 ) +{ +} + +PolygonVertexType::~PolygonVertexType() +{ +} + +const PolygonVertexType* PolygonVertexType::instance() +{ + static const PolygonVertexType t; + return &t; +} + +ObjectImp* PolygonVertexType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const std::vector<Coordinate> ppoints = static_cast<const PolygonImp*>( parents[0] )->points(); + const uint i = static_cast<const IntImp*>( parents[1] )->data(); + + if ( i >= ppoints.size() ) return new InvalidImp; + + return new PointImp( ppoints[i] ); +} + +const ObjectImpType* PolygonVertexType::resultId() const +{ + return PointImp::stype(); +} + +/* polygon sides */ + +static const ArgsParser::spec argsspecPolygonSide[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Construct the sides of this polygon" ), + I18N_NOOP( "Select the polygon of which you want to construct the sides..." ), false }, + { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonSideType ) + +PolygonSideType::PolygonSideType() + : ArgsParserObjectType( "PolygonSide", argsspecPolygonSide, 2 ) +{ +} + +PolygonSideType::~PolygonSideType() +{ +} + +const PolygonSideType* PolygonSideType::instance() +{ + static const PolygonSideType t; + return &t; +} + +ObjectImp* PolygonSideType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const std::vector<Coordinate> ppoints = static_cast<const PolygonImp*>( parents[0] )->points(); + const uint i = static_cast<const IntImp*>( parents[1] )->data(); + + if ( i >= ppoints.size() ) return new InvalidImp; + + uint nexti = i + 1; + if ( nexti >= ppoints.size() ) nexti = 0; + + return new SegmentImp( ppoints[i], ppoints[nexti] ); +} + +const ObjectImpType* PolygonSideType::resultId() const +{ + return SegmentImp::stype(); +} + +/* convex hull of a polygon */ + +static const ArgsParser::spec argsspecConvexHull[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Construct the convex hull of this polygon" ), + I18N_NOOP( "Select the polygon of which you want to construct the convex hull..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConvexHullType ) + +ConvexHullType::ConvexHullType() + : ArgsParserObjectType( "ConvexHull", argsspecConvexHull, 1 ) +{ +} + +ConvexHullType::~ConvexHullType() +{ +} + +const ConvexHullType* ConvexHullType::instance() +{ + static const ConvexHullType t; + return &t; +} + +ObjectImp* ConvexHullType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + + const std::vector<Coordinate> ppoints = static_cast<const PolygonImp*>( parents[0] )->points(); + + if ( ppoints.size() < 3 ) return new InvalidImp; + + std::vector<Coordinate> hull = computeConvexHull( ppoints ); + if ( hull.size() < 3 ) return new InvalidImp; + return new PolygonImp( hull ); +} + +const ObjectImpType* ConvexHullType::resultId() const +{ + return PolygonImp::stype(); +} diff --git a/kig/objects/polygon_type.h b/kig/objects/polygon_type.h new file mode 100644 index 00000000..a49100bd --- /dev/null +++ b/kig/objects/polygon_type.h @@ -0,0 +1,139 @@ +// Copyright (C) 2003 Maurizio Paolini <paolini@dmf.unicatt.it> + +// 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. + +#ifndef KIG_OBJECTS_POLIGON_TYPE_H +#define KIG_OBJECTS_POLIGON_TYPE_H + +#include "base_type.h" + +/** + * Triangle by its vertices + */ +class TriangleB3PType + : public ArgsParserObjectType +{ + TriangleB3PType(); + ~TriangleB3PType(); +public: + static const TriangleB3PType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; +}; + +/** + * Polygon by its vertices + */ +class PolygonBNPType + : public ObjectType +{ + PolygonBNPType(); + ~PolygonBNPType(); +public: + static const PolygonBNPType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; +}; + +/** + * Polygon by center and vertex + */ +class PolygonBCVType + : public ObjectType +{ + PolygonBCVType(); + ~PolygonBCVType(); +public: + static const PolygonBCVType* instance(); + + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& args ) const; + Args sortArgs( const Args& args ) const; + bool canMove( const ObjectTypeCalcer& o ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& o ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& o, const Coordinate& to, + const KigDocument& d ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& o ) const; +}; + +class PolygonLineIntersectionType + : public ArgsParserObjectType +{ + PolygonLineIntersectionType(); + ~PolygonLineIntersectionType(); +public: + static const PolygonLineIntersectionType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class PolygonVertexType + : public ArgsParserObjectType +{ + PolygonVertexType(); + ~PolygonVertexType(); +public: + static const PolygonVertexType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class PolygonSideType + : public ArgsParserObjectType +{ + PolygonSideType(); + ~PolygonSideType(); +public: + static const PolygonSideType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConvexHullType + : public ArgsParserObjectType +{ + ConvexHullType(); + ~ConvexHullType(); +public: + static const ConvexHullType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; +#endif diff --git a/kig/objects/special_calcers.cc b/kig/objects/special_calcers.cc new file mode 100644 index 00000000..e70bd4e9 --- /dev/null +++ b/kig/objects/special_calcers.cc @@ -0,0 +1,84 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@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. + +// 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_calcers.h" + +static const ArgsParser::spec argsspecMeasureTransport[] = +{ + { CircleImp::stype(), I18N_NOOP( "Transport a measure on this circle" ) }, + { PointImp::stype(), I18N_NOOP( "Project this point onto the circle" ) }, + { SegmentImp::stype(), I18N_NOOP( "Segment to transport" ) } +}; + +static ArgsParser measuretransportargsparser( argsspecMeasureTransport, 3 ); + +std::vector<ObjectCalcer*> MeasureTransportCalcer::parents() const +{ + std::vector<ObjectCalcer*> ret; + ret.push_back( mcircle ); + ret.push_back( mpoint ); + ret.push_back( msegment ); + return ret; +} + +MeasureTransportCalcer::MeasureTransportCalcer(ObjectCalcer* circle, ObjectCalcer* point, ObjectCalcer* segment ) + : mcircle( circle ), mpoint( point ), msegment( segment ), mimp( 0 ) +{ +} + +MeasureTransportCalcer::~MeasureTransportCalcer() +{ +} + +void MeasureTransportCalcer::calc( const KigDocument& ) +{ + if ( ! measuretransportargsparser.checkArgs( parents() ) ) + return new InvalidImp(); + + if ( ! isPointOnCurve( mpoint, mcircle ) ) + return new InvalidImp(); + + const CircleImp* c = static_cast<const CircleImp*>( mcircle->imp() ); + const PointImp* p = static_cast<const PointImp*>( mpoint->imp() ); + const SegmentImp* s = static_cast<const SegmentImp*>( msegment->imp() ); + double param = c->getParam( p->coordinate(), doc ); + double measure = s->length(); + measure /= 2*c->radius()*M_PI; + param += measure; + while (param > 1) param -= 1; + + const Coordinate nc = c->getPoint( param, doc ); + if ( nc.valid() ) return new PointImp( nc ); + else return new InvalidImp; +} + +const ObjectImpType* MeasureTransportCalcer::impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const +{ + if ( o->imp()->inherits( CircleImp::stype() ) ) + return CircleImp::stype(); + else if ( o->imp()->inherits( PointImp::stype() ) ) + return PointImp::stype(); + else if ( o->imp()->inherits( SegmentImp::stype() ) ) + return SegmentImp::stype(); + else + { + assert( false ); + return 0; + } +} + diff --git a/kig/objects/special_calcers.h b/kig/objects/special_calcers.h new file mode 100644 index 00000000..640587cc --- /dev/null +++ b/kig/objects/special_calcers.h @@ -0,0 +1,38 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_SPECIAL_CALCERS_H +#define KIG_OBJECTS_SPECIAL_CALCERS_H + +class MeasureTransportCalcer + : public ObjectCalcer +{ + ObjectCalcer* mcircle; + ObjectCalcer* mpoint; + ObjectCalcer* msegment; + ObjectImp* mimp; +public: + MeasureTransportCalcer(ObjectCalcer* circle, ObjectCalcer* point, ObjectCalcer* segment ); + ~MeasureTransportCalcer(); + + std::vector<ObjectCalcer*> parents() const; + void calc( const KigDocument& ); + const ObjectImpType* impRequirement( + ObjectCalcer* o, const std::vector<ObjectCalcer*>& os ) const; +}; + +#endif diff --git a/kig/objects/tangent_type.cc b/kig/objects/tangent_type.cc new file mode 100644 index 00000000..12ebda23 --- /dev/null +++ b/kig/objects/tangent_type.cc @@ -0,0 +1,285 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// 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 "tangent_type.h" + +#include "bogus_imp.h" +#include "conic_imp.h" +#include "cubic_imp.h" +#include "curve_imp.h" +#include "other_imp.h" +#include "point_imp.h" +#include "line_imp.h" + +#include "../misc/common.h" +#include "../misc/conic-common.h" +//#include "../misc/calcpaths.h" +#include "../kig/kig_part.h" +#include "../kig/kig_view.h" + +static const char constructlinetangentpoint[] = "SHOULDNOTBESEEN"; +static const char selecttangent1[] = + I18N_NOOP( "Select the curve..." ); +static const char selecttangent2[] = + I18N_NOOP( "Select the point for the tangent to go through..." ); + +static const ArgsParser::spec argsspecTangentConic[] = +{ + { ConicImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentConicType ) + +TangentConicType::TangentConicType() + : ArgsParserObjectType( "TangentConic", argsspecTangentConic, 2 ) +{ +} + +TangentConicType::~TangentConicType() +{ +} + +const TangentConicType* TangentConicType::instance() +{ + static const TangentConicType t; + return &t; +} + +ObjectImp* TangentConicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const ConicImp* c = static_cast<const ConicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !c->containsPoint( p, doc ) ) + return new InvalidImp; + + bool ok; + const LineData tangent = calcConicPolarLine( c->cartesianData(), p, ok ); + + if ( !ok ) + return new InvalidImp; + + return new LineImp( tangent ); +} + +const ObjectImpType* TangentConicType::resultId() const +{ + return LineImp::stype(); +} + +/*** Arc starts here ***/ + +static const ArgsParser::spec argsspecTangentArc[] = +{ + { ArcImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentArcType ) + +TangentArcType::TangentArcType() + : ArgsParserObjectType( "TangentArc", argsspecTangentArc, 2 ) +{ +} + +TangentArcType::~TangentArcType() +{ +} + +const TangentArcType* TangentArcType::instance() +{ + static const TangentArcType t; + return &t; +} + +ObjectImp* TangentArcType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const ArcImp* arc = static_cast<const ArcImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !arc->containsPoint( p, doc ) ) + return new InvalidImp; + + Coordinate c = arc->center(); + double sqr = arc->radius(); + sqr *= sqr; + ConicCartesianData data( 1.0, 1.0, 0.0, -2*c.x, -2*c.y, c.x*c.x + c.y*c.y - sqr ); + + bool ok; + const LineData tangent = calcConicPolarLine( data, p, ok ); + + if ( !ok ) + return new InvalidImp; + + return new LineImp( tangent ); +} + +const ObjectImpType* TangentArcType::resultId() const +{ + return LineImp::stype(); +} + +/**** Cubic starts here ****/ + +static const ArgsParser::spec argsspecTangentCubic[] = +{ + { CubicImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentCubicType ) + +TangentCubicType::TangentCubicType() + : ArgsParserObjectType( "TangentCubic", argsspecTangentCubic, 2 ) +{ +} + +TangentCubicType::~TangentCubicType() +{ +} + +const TangentCubicType* TangentCubicType::instance() +{ + static const TangentCubicType t; + return &t; +} + +ObjectImp* TangentCubicType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CubicImp* cubic = static_cast<const CubicImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + + if ( !cubic->containsPoint( p, doc ) ) + return new InvalidImp; + + double x = p.x; + double y = p.y; + CubicCartesianData data = cubic->data(); +// double aconst = data.coeffs[0]; + double ax = data.coeffs[1]; + double ay = data.coeffs[2]; + double axx = data.coeffs[3]; + double axy = data.coeffs[4]; + double ayy = data.coeffs[5]; + double axxx = data.coeffs[6]; + double axxy = data.coeffs[7]; + double axyy = data.coeffs[8]; + double ayyy = data.coeffs[9]; + + /* mp: the tangent vector (-gy,gx) is orthogonal to the gradient (gx,gy) + * which is easy to compute from the CartesianData + * + * note: same thing could be done for conics, which would be + * much more efficient... + */ + + Coordinate tangvec = Coordinate ( + - axxy*x*x - 2*axyy*x*y - 3*ayyy*y*y - axy*x - 2*ayy*y - ay, + 3*axxx*x*x + 2*axxy*x*y + axyy*y*y + 2*axx*x + axy*y + ax + ); + const LineData tangent = LineData( p, p + tangvec ); + + return new LineImp( tangent ); +} + +const ObjectImpType* TangentCubicType::resultId() const +{ + return LineImp::stype(); +} + +/**** Curve (locus) starts here ****/ + +static const ArgsParser::spec argsspecTangentCurve[] = +{ + { CurveImp::stype(), "SHOULDNOTBESEEN", selecttangent1, false }, + { PointImp::stype(), constructlinetangentpoint, selecttangent2, true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TangentCurveType ) + +TangentCurveType::TangentCurveType() + : ArgsParserObjectType( "TangentCurve", argsspecTangentCurve, 2 ) +{ +} + +TangentCurveType::~TangentCurveType() +{ +} + +const TangentCurveType* TangentCurveType::instance() +{ + static const TangentCurveType t; + return &t; +} + +ObjectImp* TangentCurveType::calc( const Args& args, const KigDocument& doc ) const +{ + if ( !margsparser.checkArgs( args ) ) + return new InvalidImp; + + const CurveImp* curve = static_cast<const CurveImp*>( args[0] ); + const Coordinate& p = static_cast<const PointImp*>( args[1] )->coordinate(); + if ( !curve->containsPoint( p, doc ) ) + return new InvalidImp; + + const double t = curve->getParam( p, doc ); + const double tau0 = 1e-3; + const double sigma = 1e-5; + const int maxiter = 20; + + double tau = tau0; + Coordinate tang, err; + double tplus = t + tau; + double tminus = t - tau; + if ( tplus > 1 ) {tplus = 1; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; tplus = 2*tau;} + Coordinate tangold = (curve->getPoint( tplus, doc ) - curve->getPoint( tminus, doc ))/(2*tau); + + for (int i = 0; i < maxiter; i++) + { + tau = tau/2; + tplus = t + tau; + tminus = t - tau; + if ( tplus > 1 ) {tplus = 1; tminus = 1 - 2*tau;} + if ( tminus < 0 ) {tminus = 0; tplus = 2*tau;} + tang = (curve->getPoint( tplus, doc ) - curve->getPoint( tminus, doc ))/(2*tau); + err = (tangold - tang)/3; + if (err.length() < sigma) + { + tang = (4*tang - tangold)/3; + const LineData tangent = LineData( p, p + tang ); + return new LineImp( tangent ); + } + tangold = tang; + } + return new InvalidImp; +} + +const ObjectImpType* TangentCurveType::resultId() const +{ + return LineImp::stype(); +} diff --git a/kig/objects/tangent_type.h b/kig/objects/tangent_type.h new file mode 100644 index 00000000..ccc00dea --- /dev/null +++ b/kig/objects/tangent_type.h @@ -0,0 +1,83 @@ +// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it> + +// 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. + +#ifndef KIG_OBJECTS_TANGENT_TYPE_H +#define KIG_OBJECTS_TANGENT_TYPE_H + +#include "base_type.h" + +/** + * the line tangent to a generic conic + */ +class TangentConicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentConicType(); + ~TangentConicType(); +public: + static const TangentConicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the line tangent to an arc + */ +class TangentArcType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentArcType(); + ~TangentArcType(); +public: + static const TangentArcType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the line tangent to a cubic + */ +class TangentCubicType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentCubicType(); + ~TangentCubicType(); +public: + static const TangentCubicType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +/** + * the line tangent to a curve + */ +class TangentCurveType + : public ArgsParserObjectType +{ + typedef ArgsParserObjectType Parent; + TangentCurveType(); + ~TangentCurveType(); +public: + static const TangentCurveType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/tests_type.cc b/kig/objects/tests_type.cc new file mode 100644 index 00000000..e85c111e --- /dev/null +++ b/kig/objects/tests_type.cc @@ -0,0 +1,382 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@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. + +// 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 "tests_type.h" + +#include "line_imp.h" +#include "polygon_imp.h" +#include "point_imp.h" +#include "bogus_imp.h" +#include "other_imp.h" + +#include <cmath> + +static const ArgsParser::spec argsspecAreParallel[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Is this line parallel?" ), + I18N_NOOP( "Select the first of the two possibly parallel lines..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Parallel to this line?" ), + I18N_NOOP( "Select the other of the two possibly parallel lines..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AreParallelType ) + +AreParallelType::AreParallelType() + : ArgsParserObjectType( "AreParallel", + argsspecAreParallel, 2 ) +{ +} + +AreParallelType::~AreParallelType() +{ +} + +const AreParallelType* AreParallelType::instance() +{ + static const AreParallelType t; + return &t; +} + +ObjectImp* AreParallelType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const LineData& l1 = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const LineData& l2 = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + if ( l1.isParallelTo( l2 ) ) + return new TestResultImp( i18n( "These lines are parallel." ) ); + else + return new TestResultImp( i18n( "These lines are not parallel." ) ); + +} + +const ObjectImpType* AreParallelType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec argsspecAreOrthogonal[] = +{ + { AbstractLineImp::stype(), I18N_NOOP( "Is this line orthogonal?" ), + I18N_NOOP( "Select the first of the two possibly orthogonal lines..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Orthogonal to this line?" ), + I18N_NOOP( "Select the other of the two possibly orthogonal lines..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AreOrthogonalType ) + +AreOrthogonalType::AreOrthogonalType() + : ArgsParserObjectType( "AreOrthogonal", + argsspecAreOrthogonal, 2 ) +{ +} + +AreOrthogonalType::~AreOrthogonalType() +{ +} + +const AreOrthogonalType* AreOrthogonalType::instance() +{ + static const AreOrthogonalType t; + return &t; +} + +ObjectImp* AreOrthogonalType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const LineData& l1 = static_cast<const AbstractLineImp*>( parents[0] )->data(); + const LineData& l2 = static_cast<const AbstractLineImp*>( parents[1] )->data(); + + if ( l1.isOrthogonalTo( l2 ) ) + return new TestResultImp( i18n( "These lines are orthogonal." ) ); + else + return new TestResultImp( i18n( "These lines are not orthogonal." ) ); + +} + +const ObjectImpType* AreOrthogonalType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec argsspecAreCollinear[] = +{ + { PointImp::stype(), I18N_NOOP( "Check collinearity of this point" ), + I18N_NOOP( "Select the first of the three possibly collinear points..." ), false }, + { PointImp::stype(), I18N_NOOP( "and this second point" ), + I18N_NOOP( "Select the second of the three possibly collinear points..." ), false }, + { PointImp::stype(), I18N_NOOP( "with this third point" ), + I18N_NOOP( "Select the last of the three possibly collinear points..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AreCollinearType ) + +AreCollinearType::AreCollinearType() + : ArgsParserObjectType( "AreCollinear", + argsspecAreCollinear, 3 ) +{ +} + +AreCollinearType::~AreCollinearType() +{ +} + +const AreCollinearType* AreCollinearType::instance() +{ + static const AreCollinearType t; + return &t; +} + +ObjectImp* AreCollinearType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p1 = static_cast<const PointImp*>( parents[0] )->coordinate(); + const Coordinate& p2 = static_cast<const PointImp*>( parents[1] )->coordinate(); + const Coordinate& p3 = static_cast<const PointImp*>( parents[2] )->coordinate(); + + if ( areCollinear( p1, p2, p3 ) ) + return new TestResultImp( i18n( "These points are collinear." ) ); + else + return new TestResultImp( i18n( "These points are not collinear." ) ); +} + +const ObjectImpType* AreCollinearType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec containsTestArgsSpec[] = +{ + { PointImp::stype(), I18N_NOOP( "Check whether this point is on a curve" ), + I18N_NOOP( "Select the point you want to test..." ), false }, + { CurveImp::stype(), I18N_NOOP( "Check whether the point is on this curve" ), + I18N_NOOP( "Select the curve that the point might be on..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ContainsTestType ) + +ContainsTestType::ContainsTestType() + : ArgsParserObjectType( "ContainsTest", containsTestArgsSpec, 2 ) +{ +} + +ContainsTestType::~ContainsTestType() +{ +} + +const ContainsTestType* ContainsTestType::instance() +{ + static const ContainsTestType t; + return &t; +} + +ObjectImp* ContainsTestType::calc( const Args& parents, const KigDocument& doc ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p = static_cast<const PointImp*>( parents[0] )->coordinate(); + const CurveImp* c = static_cast<const CurveImp*>( parents[1] ); + + if ( c->containsPoint( p, doc ) ) + return new TestResultImp( i18n( "This curve contains the point." ) ); + else + return new TestResultImp( i18n( "This curve does not contain the point." ) ); +} + +const ObjectImpType* ContainsTestType::resultId() const +{ + return TestResultImp::stype(); +} + +/* + * containment test of a point in a polygon + */ + +static const ArgsParser::spec InPolygonTestArgsSpec[] = +{ + { PointImp::stype(), I18N_NOOP( "Check whether this point is in a polygon" ), + I18N_NOOP( "Select the point you want to test..." ), false }, + { PolygonImp::stype(), I18N_NOOP( "Check whether the point is in this polygon" ), + I18N_NOOP( "Select the polygon that the point might be in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( InPolygonTestType ) + +InPolygonTestType::InPolygonTestType() + : ArgsParserObjectType( "InPolygonTest", InPolygonTestArgsSpec, 2 ) +{ +} + +InPolygonTestType::~InPolygonTestType() +{ +} + +const InPolygonTestType* InPolygonTestType::instance() +{ + static const InPolygonTestType t; + return &t; +} + +ObjectImp* InPolygonTestType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p = static_cast<const PointImp*>( parents[0] )->coordinate(); + const PolygonImp* pol = static_cast<const PolygonImp*>( parents[1] ); + + if ( pol->isInPolygon( p ) ) + return new TestResultImp( i18n( "This polygon contains the point." ) ); + else + return new TestResultImp( i18n( "This polygon does not contain the point." ) ); +} + +const ObjectImpType* InPolygonTestType::resultId() const +{ + return TestResultImp::stype(); +} + +/* + * test if a polygon is convex + */ + +static const ArgsParser::spec ConvexPolygonTestArgsSpec[] = +{ + { PolygonImp::stype(), I18N_NOOP( "Check whether this polygon is convex" ), + I18N_NOOP( "Select the polygon you want to test for convexity..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConvexPolygonTestType ) + +ConvexPolygonTestType::ConvexPolygonTestType() + : ArgsParserObjectType( "ConvexPolygonTest", ConvexPolygonTestArgsSpec, 1 ) +{ +} + +ConvexPolygonTestType::~ConvexPolygonTestType() +{ +} + +const ConvexPolygonTestType* ConvexPolygonTestType::instance() +{ + static const ConvexPolygonTestType t; + return &t; +} + +ObjectImp* ConvexPolygonTestType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const PolygonImp* pol = static_cast<const PolygonImp*>( parents[0] ); + + if ( pol->isConvex() ) + return new TestResultImp( i18n( "This polygon is convex." ) ); + else + return new TestResultImp( i18n( "This polygon is not convex." ) ); +} + +const ObjectImpType* ConvexPolygonTestType::resultId() const +{ + return TestResultImp::stype(); +} + +/* + * same distance test + */ + +static const ArgsParser::spec argsspecSameDistanceType[] = +{ + { PointImp::stype(), I18N_NOOP( "Check if this point has the same distance" ), + I18N_NOOP( "Select the point which might have the same distance from two other points..." ), false }, + { PointImp::stype(), I18N_NOOP( "from this point" ), + I18N_NOOP( "Select the first of the two other points..." ), false }, + { PointImp::stype(), I18N_NOOP( "and from this second point" ), + I18N_NOOP( "Select the other of the two other points..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( SameDistanceType ) + +SameDistanceType::SameDistanceType() + : ArgsParserObjectType( "SameDistanceType", argsspecSameDistanceType, 3 ) +{ +} + +SameDistanceType::~SameDistanceType() +{ +} + +const SameDistanceType* SameDistanceType::instance() +{ + static const SameDistanceType t; + return &t; +} + +ObjectImp* SameDistanceType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& p1 = static_cast<const PointImp*>( parents[0] )->coordinate(); + const Coordinate& p2 = static_cast<const PointImp*>( parents[1] )->coordinate(); + const Coordinate& p3 = static_cast<const PointImp*>( parents[2] )->coordinate(); + + if ( fabs( ( p1 - p2 ).length() - ( p1 - p3 ).length() ) < 10e-5 ) + return new TestResultImp( i18n( "The two distances are the same." ) ); + else + return new TestResultImp( i18n( "The two distances are not the same." ) ); +} + +const ObjectImpType* SameDistanceType::resultId() const +{ + return TestResultImp::stype(); +} + +static const ArgsParser::spec vectorEqualityArgsSpec[] = +{ + { VectorImp::stype(), I18N_NOOP( "Check whether this vector is equal to another vector" ), + I18N_NOOP( "Select the first of the two possibly equal vectors..." ), false }, + { VectorImp::stype(), I18N_NOOP( "Check whether this vector is equal to the other vector" ), + I18N_NOOP( "Select the other of the two possibly equal vectors..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( VectorEqualityTestType ) + +VectorEqualityTestType::VectorEqualityTestType() + : ArgsParserObjectType( "VectorEquality", vectorEqualityArgsSpec, 2 ) +{ +} + +VectorEqualityTestType::~VectorEqualityTestType() +{ +} + +const VectorEqualityTestType* VectorEqualityTestType::instance() +{ + static const VectorEqualityTestType t; + return &t; +} + +ObjectImp* VectorEqualityTestType::calc( const Args& parents, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp; + const Coordinate& v1 = static_cast<const VectorImp*>( parents[0] )->dir(); + const Coordinate& v2 = static_cast<const VectorImp*>( parents[1] )->dir(); + + if ( ( v1 - v2 ).length() < 10e-5 ) + return new TestResultImp( i18n( "The two vectors are the same." ) ); + else + return new TestResultImp( i18n( "The two vectors are not the same." ) ); +} + +const ObjectImpType* VectorEqualityTestType::resultId() const +{ + return TestResultImp::stype(); +} diff --git a/kig/objects/tests_type.h b/kig/objects/tests_type.h new file mode 100644 index 00000000..7498fc4f --- /dev/null +++ b/kig/objects/tests_type.h @@ -0,0 +1,111 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_TESTS_TYPE_H +#define KIG_OBJECTS_TESTS_TYPE_H + +#include "base_type.h" + +class AreParallelType + : public ArgsParserObjectType +{ + AreParallelType(); + ~AreParallelType(); +public: + static const AreParallelType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class AreOrthogonalType + : public ArgsParserObjectType +{ + AreOrthogonalType(); + ~AreOrthogonalType(); +public: + static const AreOrthogonalType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class AreCollinearType + : public ArgsParserObjectType +{ + AreCollinearType(); + ~AreCollinearType(); +public: + static const AreCollinearType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ContainsTestType + : public ArgsParserObjectType +{ + ContainsTestType(); + ~ContainsTestType(); +public: + static const ContainsTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class InPolygonTestType + : public ArgsParserObjectType +{ + InPolygonTestType(); + ~InPolygonTestType(); +public: + static const InPolygonTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class ConvexPolygonTestType + : public ArgsParserObjectType +{ + ConvexPolygonTestType(); + ~ConvexPolygonTestType(); +public: + static const ConvexPolygonTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class SameDistanceType + : public ArgsParserObjectType +{ + SameDistanceType(); + ~SameDistanceType(); +public: + static const SameDistanceType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +class VectorEqualityTestType + : public ArgsParserObjectType +{ + VectorEqualityTestType(); + ~VectorEqualityTestType(); +public: + static const VectorEqualityTestType* instance(); + ObjectImp* calc( const Args& parents, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif diff --git a/kig/objects/text_imp.cc b/kig/objects/text_imp.cc new file mode 100644 index 00000000..f7b2f1be --- /dev/null +++ b/kig/objects/text_imp.cc @@ -0,0 +1,173 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "text_imp.h" + +#include "bogus_imp.h" +#include "../misc/kigpainter.h" + +TextImp::TextImp( const QString& text, const Coordinate& loc, bool frame ) + : mtext( text), mloc( loc ), mframe( frame ), mboundrect( Rect::invalidRect() ) +{ +} + +TextImp* TextImp::copy() const +{ + return new TextImp( mtext, mloc ); +} + +TextImp::~TextImp() +{ +} + +Coordinate TextImp::attachPoint() const +{ + return Coordinate::invalidCoord(); +} + +ObjectImp* TextImp::transform( const Transformation& t ) const +{ + Coordinate nloc = t.apply( mloc ); + return new TextImp( mtext, nloc, mframe ); +} + +void TextImp::draw( KigPainter& p ) const +{ + mboundrect = p.simpleBoundingRect( mloc, mtext ); + p.drawTextFrame( mboundrect, mtext, mframe ); +} + +bool TextImp::contains( const Coordinate& p, int, const KigWidget& ) const +{ + return mboundrect.contains( p ); +} + +bool TextImp::inRect( const Rect& r, int, const KigWidget& ) const +{ + return mboundrect.intersects( r ); +} + +bool TextImp::valid() const +{ + return true; +} + +const uint TextImp::numberOfProperties() const +{ + return Parent::numberOfProperties() + 1; +} + +const QCStringList TextImp::propertiesInternalNames() const +{ + QCStringList ret = Parent::propertiesInternalNames(); + ret << "kig_text"; + return ret; +} + +const QCStringList TextImp::properties() const +{ + QCStringList ret = Parent::properties(); + ret << I18N_NOOP( "Text" ); + return ret; +} + +const ObjectImpType* TextImp::impRequirementForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::impRequirementForProperty( which ); + return TextImp::stype(); +} + +const char* TextImp::iconForProperty( uint which ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::iconForProperty( which ); + else if ( which == Parent::numberOfProperties() ) + return "text"; // text + else assert( false ); + return ""; +} + +ObjectImp* TextImp::property( uint which, const KigDocument& w ) const +{ + if ( which < Parent::numberOfProperties() ) + return Parent::property( which, w ); + else if ( which == Parent::numberOfProperties() ) + return new StringImp( text() ); + else assert( false ); + return new InvalidImp; +} + +QString TextImp::text() const +{ + return mtext; +} + +void TextImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +const Coordinate TextImp::coordinate() const +{ + return mloc; +} + +bool TextImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( TextImp::stype() ) && + static_cast<const TextImp&>( rhs ).coordinate() == coordinate() && + static_cast<const TextImp&>( rhs ).text() == text() && + static_cast<const TextImp&>( rhs ).hasFrame() == hasFrame(); +} + +bool TextImp::hasFrame() const +{ + return mframe; +} + +const ObjectImpType* TextImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "label", + I18N_NOOP( "label" ), + I18N_NOOP( "Select this label" ), + I18N_NOOP( "Select label %1" ), + I18N_NOOP( "Remove a Label" ), + I18N_NOOP( "Add a Label" ), + I18N_NOOP( "Move a Label" ), + I18N_NOOP( "Attach to this label" ), + I18N_NOOP( "Show a Label" ), + I18N_NOOP( "Hide a Label" ) + ); + return &t; +} + +const ObjectImpType* TextImp::type() const +{ + return TextImp::stype(); +} + +bool TextImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect TextImp::surroundingRect() const +{ + return mboundrect; +} diff --git a/kig/objects/text_imp.h b/kig/objects/text_imp.h new file mode 100644 index 00000000..8ad92b84 --- /dev/null +++ b/kig/objects/text_imp.h @@ -0,0 +1,70 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_TEXT_IMP_H +#define KIG_OBJECTS_TEXT_IMP_H + +#include "object_imp.h" + +#include "../misc/coordinate.h" +#include "../misc/rect.h" + +class TextImp + : public ObjectImp +{ + QString mtext; + Coordinate mloc; + bool mframe; + // with this var, we keep track of the place we drew in, for use in + // the contains() function.. + mutable Rect mboundrect; +public: + typedef ObjectImp Parent; + static const ObjectImpType* stype(); + + Coordinate attachPoint() const; + TextImp( const QString& text, const Coordinate& loc, bool frame = false ); + TextImp* copy() const; + ~TextImp(); + + ObjectImp* transform( const Transformation& ) const; + + void draw( KigPainter& p ) const; + bool contains( const Coordinate& p, int width, const KigWidget& ) const; + bool inRect( const Rect& r, int width, const KigWidget& ) const; + bool valid() const; + Rect surroundingRect() const; + + const uint numberOfProperties() const; + const QCStringList properties() const; + const QCStringList propertiesInternalNames() const; + ObjectImp* property( uint which, const KigDocument& w ) const; + const char* iconForProperty( uint which ) const; + const ObjectImpType* impRequirementForProperty( uint which ) const; + bool isPropertyDefinedOnOrThroughThisImp( uint which ) const; + + const ObjectImpType* type() const; + void visit( ObjectImpVisitor* vtor ) const; + + QString text() const; + const Coordinate coordinate() const; + bool hasFrame() const; + + bool equals( const ObjectImp& rhs ) const; +}; + +#endif diff --git a/kig/objects/text_type.cc b/kig/objects/text_type.cc new file mode 100644 index 00000000..6594c05a --- /dev/null +++ b/kig/objects/text_type.cc @@ -0,0 +1,215 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "text_type.h" + +#include "text_imp.h" +#include "bogus_imp.h" +#include "point_imp.h" +#include "line_imp.h" + +#include "../kig/kig_view.h" +#include "../kig/kig_part.h" +#include "../kig/kig_commands.h" +#include "../modes/label.h" +#include "../misc/coordinate_system.h" + +#include <algorithm> + +#include <qclipboard.h> +#include <qstringlist.h> + +#include <kapplication.h> + +#include <cmath> + +static const ArgsParser::spec arggspeccs[] = +{ + { IntImp::stype(), "UNUSED", "SHOULD NOT BE SEEN", false }, + { PointImp::stype(), "UNUSED", "SHOULD NOT BE SEEN", false }, + { StringImp::stype(), "UNUSED", "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TextType ) + +TextType::TextType() + : ObjectType( "Label" ), mparser( arggspeccs, 3 ) +{ +} + +TextType::~TextType() +{ +} + +const TextType* TextType::instance() +{ + static const TextType t; + return &t; +} + +const ObjectImpType* TextType::resultId() const +{ + return TextImp::stype(); +} + +const ObjectImpType* TextType::impRequirement( const ObjectImp* o, const Args& args ) const +{ + assert( args.size() >= 3 ); + Args firstthree( args.begin(), args.begin() + 3 ); + if ( o == args[0] || o == args[1] || o == args[2] ) + return mparser.impRequirement( o, firstthree ); + else + return ObjectImp::stype(); +} + +ObjectImp* TextType::calc( const Args& parents, const KigDocument& doc ) const +{ + if( parents.size() < 3 ) return new InvalidImp; + Args firstthree( parents.begin(), parents.begin() + 3 ); + Args varargs( parents.begin() + 3, parents.end() ); + + if ( ! mparser.checkArgs( firstthree ) ) return new InvalidImp; + + int frame = static_cast<const IntImp*>( firstthree[0] )->data(); + bool needframe = frame != 0; + const Coordinate t = static_cast<const PointImp*>( firstthree[1] )->coordinate(); + QString s = static_cast<const StringImp*>( firstthree[2] )->data(); + + for ( Args::iterator i = varargs.begin(); i != varargs.end(); ++i ) + (*i)->fillInNextEscape( s, doc ); + + return new TextImp( s, t, needframe ); +} + +bool TextType::canMove( const ObjectTypeCalcer& ) const +{ + return true; +} + +bool TextType::isFreelyTranslatable( const ObjectTypeCalcer& ) const +{ + return true; +} + +void TextType::move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& d ) const +{ + const std::vector<ObjectCalcer*> parents = ourobj.parents(); + assert( parents.size() >= 3 ); + const std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 ); + if( dynamic_cast<ObjectConstCalcer*>( firstthree[1] ) ) + { + ObjectConstCalcer* c = static_cast<ObjectConstCalcer*>( firstthree[1] ); + c->setImp( new PointImp( to ) ); + } + else + firstthree[1]->move( to, d ); +} + +QStringList TextType::specialActions() const +{ + QStringList ret; + ret << i18n( "&Copy Text" ); + ret << i18n( "&Toggle Frame" ); + ret << i18n( "&Redefine..." ); + return ret; +} + +void TextType::executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& doc, KigWidget&, + NormalMode& ) const +{ + std::vector<ObjectCalcer*> parents = c.parents(); + assert( parents.size() >= 3 ); + + std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 ); + + assert( mparser.checkArgs( firstthree ) ); + assert( dynamic_cast<ObjectConstCalcer*>( firstthree[0] ) ); + assert( dynamic_cast<ObjectConstCalcer*>( firstthree[2] ) ); + + if ( i == 0 ) + { + QClipboard* cb = kapp->clipboard(); + + // copy the text into the clipboard + const TextImp* ti = static_cast<const TextImp*>( c.imp() ); + cb->setText( ti->text(), QClipboard::Clipboard ); + } + else if ( i == 1 ) + { + // toggle label frame + int n = (static_cast<const IntImp*>( firstthree[0]->imp() )->data() + 1) % 2; + KigCommand* kc = new KigCommand( doc, i18n( "Toggle Label Frame" ) ); + kc->addTask( new ChangeObjectConstCalcerTask( + static_cast<ObjectConstCalcer*>( firstthree[0] ), + new IntImp( n ) ) ); + doc.history()->addCommand( kc ); + } + else if ( i == 2 ) + { + assert( dynamic_cast<ObjectTypeCalcer*>( o.calcer() ) ); + // redefine.. + TextLabelRedefineMode m( doc, static_cast<ObjectTypeCalcer*>( o.calcer() ) ); + doc.runMode( &m ); + } + else assert( false ); +} + +const ArgsParser& TextType::argParser() const +{ + return mparser; +} + +const Coordinate TextType::moveReferencePoint( const ObjectTypeCalcer& ourobj ) const +{ + assert( ourobj.imp()->inherits( TextImp::stype() ) ); + return static_cast<const TextImp*>( ourobj.imp() )->coordinate(); +} + +std::vector<ObjectCalcer*> TextType::sortArgs( const std::vector<ObjectCalcer*>& os ) const +{ + assert( os.size() >= 3 ); + std::vector<ObjectCalcer*> ret( os.begin(), os.begin() + 3 ); + ret = mparser.parse( ret ); + std::copy( os.begin() + 3, os.end(), std::back_inserter( ret ) ); + return ret; +} + +Args TextType::sortArgs( const Args& args ) const +{ + assert( args.size() >= 3 ); + Args ret( args.begin(), args.begin() + 3 ); + ret = mparser.parse( ret ); + std::copy( args.begin() + 3, args.end(), std::back_inserter( ret ) ); + return ret; +} + +std::vector<ObjectCalcer*> TextType::movableParents( const ObjectTypeCalcer& ourobj ) const +{ + const std::vector<ObjectCalcer*> parents = ourobj.parents(); + assert( parents.size() >= 3 ); + std::vector<ObjectCalcer*> ret = parents[1]->movableParents(); + ret.push_back( parents[1] ); + return ret; +} + +bool TextType::isDefinedOnOrThrough( const ObjectImp*, const Args& ) const +{ + return false; +} + diff --git a/kig/objects/text_type.h b/kig/objects/text_type.h new file mode 100644 index 00000000..6368cafa --- /dev/null +++ b/kig/objects/text_type.h @@ -0,0 +1,55 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_TEXT_TYPE_H +#define KIG_OBJECTS_TEXT_TYPE_H + +#include "object_type.h" + +class TextType + : public ObjectType +{ + const ArgsParser mparser; + TextType(); + ~TextType(); +public: + static const TextType* instance(); + + const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const; + bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const; + const ObjectImpType* resultId() const; + + ObjectImp* calc( const Args& parents, const KigDocument& d ) const; + + std::vector<ObjectCalcer*> sortArgs( const std::vector<ObjectCalcer*>& os ) const; + Args sortArgs( const Args& args ) const; + + bool canMove( const ObjectTypeCalcer& ourobj ) const; + bool isFreelyTranslatable( const ObjectTypeCalcer& ourobj ) const; + std::vector<ObjectCalcer*> movableParents( const ObjectTypeCalcer& ourobj ) const; + const Coordinate moveReferencePoint( const ObjectTypeCalcer& ourobj ) const; + void move( ObjectTypeCalcer& ourobj, const Coordinate& to, + const KigDocument& ) const; + + QStringList specialActions() const; + void executeAction( int i, ObjectHolder& o, ObjectTypeCalcer& c, + KigPart& d, KigWidget& w, NormalMode& m ) const; + + const ArgsParser& argParser() const; +}; + +#endif diff --git a/kig/objects/transform_types.cc b/kig/objects/transform_types.cc new file mode 100644 index 00000000..3a8d32db --- /dev/null +++ b/kig/objects/transform_types.cc @@ -0,0 +1,874 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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 "transform_types.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "line_imp.h" +#include "other_imp.h" +#include "polygon_imp.h" +#include "../misc/coordinate.h" +#include "../misc/kigtransform.h" + +#include <cmath> + +static const ArgsParser::spec argsspecTranslation[] = +{ + { ObjectImp::stype(), I18N_NOOP("Translate this object"), + I18N_NOOP( "Select the object to translate..." ), false }, + { VectorImp::stype(), I18N_NOOP("Translate by this vector"), + I18N_NOOP( "Select the vector to translate by..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TranslatedType ) + +TranslatedType::TranslatedType() + : ArgsParserObjectType( "Translation", argsspecTranslation, 2 ) +{ +} + +TranslatedType::~TranslatedType() +{ +} + +const TranslatedType* TranslatedType::instance() +{ + static const TranslatedType t; + return &t; +} + +ObjectImp* TranslatedType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate dir = static_cast<const VectorImp*>( args[1] )->dir(); + Transformation t = Transformation::translation( dir ); + + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecPointReflection[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Reflect this object" ), + I18N_NOOP( "Select the object to reflect..." ), false }, + { PointImp::stype(), I18N_NOOP( "Reflect in this point" ), + I18N_NOOP( "Select the point to reflect in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PointReflectionType ) + +PointReflectionType::PointReflectionType() + : ArgsParserObjectType( "PointReflection", argsspecPointReflection, 2 ) +{ +} + +PointReflectionType::~PointReflectionType() +{ +} + +const PointReflectionType* PointReflectionType::instance() +{ + static const PointReflectionType t; + return &t; +} + +ObjectImp* PointReflectionType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + Transformation t = Transformation::pointReflection( center ); + + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecLineReflection[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Reflect this object" ), + I18N_NOOP( "Select the object to reflect..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Reflect in this line" ), + I18N_NOOP( "Select the line to reflect in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( LineReflectionType ) + +LineReflectionType::LineReflectionType() + : ArgsParserObjectType( "LineReflection", argsspecLineReflection, 2 ) +{ +} + +LineReflectionType::~LineReflectionType() +{ +} + +const LineReflectionType* LineReflectionType::instance() +{ + static const LineReflectionType t; + return &t; +} + +ObjectImp* LineReflectionType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + LineData d = static_cast<const AbstractLineImp*>( args[1] )->data(); + Transformation t = Transformation::lineReflection( d ); + + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecRotation[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Rotate this object" ), + I18N_NOOP( "Select the object to rotate..." ), false }, + { PointImp::stype(), I18N_NOOP( "Rotate around this point" ), + I18N_NOOP( "Select the center point of the rotation..." ), false }, + { AngleImp::stype(), I18N_NOOP( "Rotate by this angle" ), + I18N_NOOP( "Select the angle of the rotation..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( RotationType ) + +RotationType::RotationType() + : ArgsParserObjectType( "Rotation", argsspecRotation, 3 ) +{ +} + +RotationType::~RotationType() +{ +} + +const RotationType* RotationType::instance() +{ + static const RotationType t; + return &t; +} + +ObjectImp* RotationType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + double angle = static_cast<const AngleImp*>( args[2] )->size(); + + return args[0]->transform( Transformation::rotation( angle, center ) ); +} + +static const ArgsParser::spec argsspecScalingOverCenter[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), + I18N_NOOP( "Select the object to scale..." ), false }, + { PointImp::stype(), I18N_NOOP( "Scale with this center" ), + I18N_NOOP( "Select the center point of the scaling..." ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale by the length of this segment" ), + I18N_NOOP( "Select a segment whose length is the factor of the scaling..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverCenterType ) + +ScalingOverCenterType::ScalingOverCenterType() + : ArgsParserObjectType( "ScalingOverCenter", argsspecScalingOverCenter, 3 ) +{ +} + +ScalingOverCenterType::~ScalingOverCenterType() +{ +} + +const ScalingOverCenterType* ScalingOverCenterType::instance() +{ + static const ScalingOverCenterType t; + return &t; +} + +ObjectImp* ScalingOverCenterType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + double ratio = static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverPoint( ratio, center ) ); +} + +static const ArgsParser::spec argsspecScalingOverCenter2[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), + I18N_NOOP( "Select the object to scale..." ), false }, + { PointImp::stype(), I18N_NOOP( "Scale with this center" ), + I18N_NOOP( "Select the center point of the scaling..." ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale the length of this segment..." ), + I18N_NOOP( "Select the first of two segments whose ratio is the factor of the scaling..." ), false }, + { SegmentImp::stype(), I18N_NOOP( "...to the length of this other segment" ), + I18N_NOOP( "Select the second of two segments whose ratio is the factor of the scaling..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverCenter2Type ) + +ScalingOverCenter2Type::ScalingOverCenter2Type() + : ArgsParserObjectType( "ScalingOverCenter2", argsspecScalingOverCenter2, 4 ) +{ +} + +ScalingOverCenter2Type::~ScalingOverCenter2Type() +{ +} + +const ScalingOverCenter2Type* ScalingOverCenter2Type::instance() +{ + static const ScalingOverCenter2Type t; + return &t; +} + +ObjectImp* ScalingOverCenter2Type::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + double ratio = static_cast<const SegmentImp*>( args[3] )->length()/ + static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverPoint( ratio, center ) ); +} + +static const ArgsParser::spec argsspecScalingOverLine[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), I18N_NOOP( "Select the object to scale" ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Scale over this line" ), I18N_NOOP( "Select the line to scale over" ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale by the length of this segment" ), I18N_NOOP( "Select a segment whose length is the factor for the scaling" ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverLineType ) + +ScalingOverLineType::ScalingOverLineType() + : ArgsParserObjectType( "ScalingOverLine", argsspecScalingOverLine, 3 ) +{ +} + +ScalingOverLineType::~ScalingOverLineType() +{ +} + +const ScalingOverLineType* ScalingOverLineType::instance() +{ + static const ScalingOverLineType t; + return &t; +} + +ObjectImp* ScalingOverLineType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + LineData line = static_cast<const AbstractLineImp*>( args[1] )->data(); + double ratio = static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverLine( ratio, line ) ); +} + +static const ArgsParser::spec argsspecScalingOverLine2[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Scale this object" ), I18N_NOOP( "Select the object to scale" ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Scale over this line" ), I18N_NOOP( "Select the line to scale over" ), false }, + { SegmentImp::stype(), I18N_NOOP( "Scale the length of this segment..." ), I18N_NOOP( "Select the first of two segments whose ratio is the factor for the scaling" ), false }, + { SegmentImp::stype(), I18N_NOOP( "...to the length of this segment" ), I18N_NOOP( "Select the second of two segments whose ratio is the factor for the scaling" ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ScalingOverLine2Type ) + +ScalingOverLine2Type::ScalingOverLine2Type() + : ArgsParserObjectType( "ScalingOverLine2", argsspecScalingOverLine2, 4 ) +{ +} + +ScalingOverLine2Type::~ScalingOverLine2Type() +{ +} + +const ScalingOverLine2Type* ScalingOverLine2Type::instance() +{ + static const ScalingOverLine2Type t; + return &t; +} + +ObjectImp* ScalingOverLine2Type::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + LineData line = static_cast<const AbstractLineImp*>( args[1] )->data(); + double ratio = static_cast<const SegmentImp*>( args[3] )->length()/ + static_cast<const SegmentImp*>( args[2] )->length(); + + return args[0]->transform( Transformation::scalingOverLine( ratio, line ) ); +} + +static const ArgsParser::spec argsspecProjectiveRotation[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Projectively rotate this object" ), I18N_NOOP( "Select the object to rotate projectively" ), false }, + { RayImp::stype(), I18N_NOOP( "Projectively rotate with this half-line" ), I18N_NOOP( "Select the half line of the projective rotation that you want to apply to the object" ), false }, + { AngleImp::stype(), I18N_NOOP( "Projectively rotate by this angle" ), I18N_NOOP( "Select the angle of the projective rotation that you want to apply to the object" ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ProjectiveRotationType ) + +ProjectiveRotationType::ProjectiveRotationType() + : ArgsParserObjectType( "ProjectiveRotation", argsspecProjectiveRotation, 3 ) +{ +} + +ProjectiveRotationType::~ProjectiveRotationType() +{ +} + +const ProjectiveRotationType* ProjectiveRotationType::instance() +{ + static const ProjectiveRotationType t; + return &t; +} + +ObjectImp* ProjectiveRotationType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const RayImp* ray = static_cast<const RayImp*>( args[1] ); + Coordinate c1 = ray->data().a; + Coordinate dir = ray->data().dir().normalize(); + double alpha = static_cast<const AngleImp*>( args[2] )->size(); + + return args[0]->transform( + Transformation::projectiveRotation( alpha, dir, c1 ) ); +} + +static const ArgsParser::spec argsspecHarmonicHomology[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Harmonic Homology of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "Harmonic Homology with this center" ), + I18N_NOOP( "Select the center point of the harmonic homology..." ), false }, + { AbstractLineImp::stype(), I18N_NOOP( "Harmonic Homology with this axis" ), + I18N_NOOP( "Select the axis of the harmonic homology..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( HarmonicHomologyType ) + +HarmonicHomologyType::HarmonicHomologyType() + : ArgsParserObjectType( "HarmonicHomology", argsspecHarmonicHomology, 3 ) +{ +} + +HarmonicHomologyType::~HarmonicHomologyType() +{ +} + +const HarmonicHomologyType* HarmonicHomologyType::instance() +{ + static const HarmonicHomologyType t; + return &t; +} + +ObjectImp* HarmonicHomologyType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate center = static_cast<const PointImp*>( args[1] )->coordinate(); + LineData axis = static_cast<const AbstractLineImp*>( args[2] )->data(); + return args[0]->transform( + Transformation::harmonicHomology( center, axis ) ); +} + +static const ArgsParser::spec argsspecAffinityB2Tr[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic affinity of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PolygonImp::stype3(), I18N_NOOP( "Map this triangle" ), + I18N_NOOP( "Select the triangle that has to be transformed onto a given triangle..." ), false }, + { PolygonImp::stype3(), I18N_NOOP( "onto this other triangle" ), + I18N_NOOP( "Select the triangle that is the image by the affinity of the first triangle..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AffinityB2TrType ) + +AffinityB2TrType::AffinityB2TrType() + : ArgsParserObjectType( "AffinityB2Tr", argsspecAffinityB2Tr, 3 ) +{ +} + +AffinityB2TrType::~AffinityB2TrType() +{ +} + +const AffinityB2TrType* AffinityB2TrType::instance() +{ + static const AffinityB2TrType t; + return &t; +} + +ObjectImp* AffinityB2TrType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints = static_cast<const PolygonImp*>( args[1] )->points(); + std::vector<Coordinate> topoints = static_cast<const PolygonImp*>( args[2] )->points(); + + bool valid = true; + Transformation t = Transformation::affinityGI3P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecAffinityGI3P[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic affinity of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "First of 3 starting points" ), + I18N_NOOP( "Select the first of the three starting points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Second of 3 starting points" ), + I18N_NOOP( "Select the second of the three starting points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Third of 3 starting points" ), + I18N_NOOP( "Select the third of the three starting points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of first point" ), + I18N_NOOP( "Select the first of the three end points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of second point" ), + I18N_NOOP( "Select the second of the three end points of the generic affinity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of third point" ), + I18N_NOOP( "Select the third of the three end points of the generic affinity..." ), false }, +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( AffinityGI3PType ) + +AffinityGI3PType::AffinityGI3PType() + : ArgsParserObjectType( "AffinityGI3P", argsspecAffinityGI3P, 7 ) +{ +} + +AffinityGI3PType::~AffinityGI3PType() +{ +} + +const AffinityGI3PType* AffinityGI3PType::instance() +{ + static const AffinityGI3PType t; + return &t; +} + +ObjectImp* AffinityGI3PType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints; + std::vector<Coordinate> topoints; + for ( uint i = 0; i < 3; ++i ) + { + frompoints.push_back( + static_cast<const PointImp*>( args[i+1] )->coordinate() ); + topoints.push_back( + static_cast<const PointImp*>( args[i+4] )->coordinate() ); + } + + bool valid = true; + Transformation t = Transformation::affinityGI3P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecProjectivityB2Qu[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic projective transformation of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PolygonImp::stype4(), I18N_NOOP( "Map this quadrilateral" ), + I18N_NOOP( "Select the quadrilateral that has to be transformed onto a given quadrilateral..." ), false }, + { PolygonImp::stype4(), I18N_NOOP( "onto this other quadrilateral" ), + I18N_NOOP( "Select the quadrilateral that is the image by the projective transformation of the first quadrilateral..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ProjectivityB2QuType ) + +ProjectivityB2QuType::ProjectivityB2QuType() + : ArgsParserObjectType( "ProjectivityB2Qu", argsspecProjectivityB2Qu, 3 ) +{ +} + +ProjectivityB2QuType::~ProjectivityB2QuType() +{ +} + +const ProjectivityB2QuType* ProjectivityB2QuType::instance() +{ + static const ProjectivityB2QuType t; + return &t; +} + +ObjectImp* ProjectivityB2QuType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints = static_cast<const PolygonImp*>( args[1] )->points(); + std::vector<Coordinate> topoints = static_cast<const PolygonImp*>( args[2] )->points(); + + bool valid = true; + Transformation t = Transformation::projectivityGI4P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecProjectivityGI4P[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Generic projective transformation of this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "First of 4 starting points" ), + I18N_NOOP( "Select the first of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Second of 4 starting points" ), + I18N_NOOP( "Select the second of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Third of 4 starting points" ), + I18N_NOOP( "Select the third of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Fourth of 4 starting points" ), + I18N_NOOP( "Select the fourth of the four starting points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of first point" ), + I18N_NOOP( "Select the first of the four end points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of second point" ), + I18N_NOOP( "Select the second of the four end points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of third point" ), + I18N_NOOP( "Select the third of the four end points of the generic projectivity..." ), false }, + { PointImp::stype(), I18N_NOOP( "Transformed position of fourth point" ), + I18N_NOOP( "Select the fourth of the four end points of the generic projectivity..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ProjectivityGI4PType ) + +ProjectivityGI4PType::ProjectivityGI4PType() + : ArgsParserObjectType( "ProjectivityGI4P", argsspecProjectivityGI4P, 9 ) +{ +} + +ProjectivityGI4PType::~ProjectivityGI4PType() +{ +} + +const ProjectivityGI4PType* ProjectivityGI4PType::instance() +{ + static const ProjectivityGI4PType t; + return &t; +} + +ObjectImp* ProjectivityGI4PType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + std::vector<Coordinate> frompoints; + std::vector<Coordinate> topoints; + for ( uint i = 0; i < 4; ++i ) + { + frompoints.push_back( + static_cast<const PointImp*>( args[i+1] )->coordinate() ); + topoints.push_back( + static_cast<const PointImp*>( args[i+5] )->coordinate() ); + } + + bool valid = true; + Transformation t = Transformation::projectivityGI4P( frompoints, topoints, + valid ); + + if (valid == false) return new InvalidImp; + return args[0]->transform( t ); +} + +static const ArgsParser::spec argsspecCastShadow[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Cast the shadow of this object" ), + I18N_NOOP( "Select the object of which you want to construct the shadow..." ), false }, + { PointImp::stype(), I18N_NOOP( "Cast a shadow from this light source" ), + I18N_NOOP( "Select the light source from which the shadow should originate..." ), false }, + { AbstractLineImp::stype(), + I18N_NOOP( "Cast a shadow on the horizon represented by this line" ), + I18N_NOOP( "Select the horizon for the shadow..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CastShadowType ) + +CastShadowType::CastShadowType() + : ArgsParserObjectType( "CastShadow", argsspecCastShadow, 3 ) +{ +} + +CastShadowType::~CastShadowType() +{ +} + +const CastShadowType* CastShadowType::instance() +{ + static const CastShadowType t; + return &t; +} + +ObjectImp* CastShadowType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate lightsrc = static_cast<const PointImp*>( args[1] )->coordinate(); + LineData d = static_cast<const AbstractLineImp*>( args[2] )->data(); + return args[0]->transform( + Transformation::castShadow( lightsrc, d ) ); +} + +const ObjectImpType* TranslatedType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* PointReflectionType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* LineReflectionType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* RotationType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverCenterType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverCenter2Type::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverLineType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ScalingOverLine2Type::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ProjectiveRotationType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* HarmonicHomologyType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* AffinityB2TrType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* AffinityGI3PType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ProjectivityB2QuType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* ProjectivityGI4PType::resultId() const +{ + return ObjectImp::stype(); +} + +const ObjectImpType* CastShadowType::resultId() const +{ + return ObjectImp::stype(); +} + +bool TranslatedType::isTransform() const +{ + return true; +} + +bool PointReflectionType::isTransform() const +{ + return true; +} + +bool LineReflectionType::isTransform() const +{ + return true; +} + +bool RotationType::isTransform() const +{ + return true; +} + +bool ScalingOverCenterType::isTransform() const +{ + return true; +} + +bool ScalingOverCenter2Type::isTransform() const +{ + return true; +} + +bool ScalingOverLineType::isTransform() const +{ + return true; +} + +bool ScalingOverLine2Type::isTransform() const +{ + return true; +} + +bool ProjectiveRotationType::isTransform() const +{ + return true; +} + +bool HarmonicHomologyType::isTransform() const +{ + return true; +} + +bool AffinityB2TrType::isTransform() const +{ + return true; +} + +bool AffinityGI3PType::isTransform() const +{ + return true; +} + +bool ProjectivityB2QuType::isTransform() const +{ + return true; +} + +bool ProjectivityGI4PType::isTransform() const +{ + return true; +} + +bool CastShadowType::isTransform() const +{ + return true; +} + +static const ArgsParser::spec argsspecApplyTransformation[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Transform this object" ), "SHOULD NOT BE SEEN", false }, + { TransformationImp::stype(), I18N_NOOP( "Transform using this transformation" ), "SHOULD NOT BE SEEN", false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ApplyTransformationObjectType ) + +ApplyTransformationObjectType::ApplyTransformationObjectType() + : ArgsParserObjectType( "ApplyTransformation", argsspecApplyTransformation, 2 ) +{ +} + +ApplyTransformationObjectType::~ApplyTransformationObjectType() +{ +} + +const ApplyTransformationObjectType* ApplyTransformationObjectType::instance() +{ + static const ApplyTransformationObjectType t; + return &t; +} + +ObjectImp* ApplyTransformationObjectType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + return args[0]->transform( static_cast<const TransformationImp*>( args[1] )->data() ); +} + +const ObjectImpType* ApplyTransformationObjectType::resultId() const +{ + return ObjectImp::stype(); +} + +bool ApplyTransformationObjectType::isTransform() const +{ + return true; +} + +bool SimilitudeType::isTransform() const +{ + return true; +} + +const ObjectImpType* SimilitudeType::resultId() const +{ + return ObjectImp::stype(); +} + +const SimilitudeType* SimilitudeType::instance() +{ + static const SimilitudeType t; + return &t; +} + +ObjectImp* SimilitudeType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + Coordinate c = static_cast<const PointImp*>( args[1] )->coordinate(); + Coordinate a = static_cast<const PointImp*>( args[2] )->coordinate(); + Coordinate b = static_cast<const PointImp*>( args[3] )->coordinate(); + a -= c; + b -= c; + double factor = sqrt( b.squareLength()/a.squareLength() ); + double theta = atan2( b.y, b.x ) - atan2( a.y, a.x ); + + return args[0]->transform( Transformation::similitude( c, theta, factor ) ); +} + +SimilitudeType::~SimilitudeType() +{ +} + +static const ArgsParser::spec argsspecSimilitude[] = +{ + { ObjectImp::stype(), I18N_NOOP( "Apply a similitude to this object" ), + I18N_NOOP( "Select the object to transform..." ), false }, + { PointImp::stype(), I18N_NOOP( "Apply a similitude with this center" ), + I18N_NOOP( "Select the center for the similitude..." ), false }, + { PointImp::stype(), I18N_NOOP( "Apply a similitude mapping this point onto another point" ), + I18N_NOOP( "Select the point which the similitude should map onto another point..." ), false }, + { PointImp::stype(), I18N_NOOP( "Apply a similitude mapping a point onto this point" ), + I18N_NOOP( "Select the point onto which the similitude should map the first point..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( SimilitudeType ) + +SimilitudeType::SimilitudeType() + : ArgsParserObjectType( "Similitude", argsspecSimilitude, 4 ) +{ +} diff --git a/kig/objects/transform_types.h b/kig/objects/transform_types.h new file mode 100644 index 00000000..038be068 --- /dev/null +++ b/kig/objects/transform_types.h @@ -0,0 +1,243 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_TRANSFORM_TYPES_H +#define KIG_OBJECTS_TRANSFORM_TYPES_H + +#include "object_type.h" + +class TranslatedType + : public ArgsParserObjectType +{ + TranslatedType(); + ~TranslatedType(); +public: + static const TranslatedType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class PointReflectionType + : public ArgsParserObjectType +{ + PointReflectionType(); + ~PointReflectionType(); +public: + static const PointReflectionType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class LineReflectionType + : public ArgsParserObjectType +{ + LineReflectionType(); + ~LineReflectionType(); +public: + static const LineReflectionType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class RotationType + : public ArgsParserObjectType +{ + RotationType(); + ~RotationType(); +public: + static const RotationType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverCenterType + : public ArgsParserObjectType +{ + ScalingOverCenterType(); + ~ScalingOverCenterType(); +public: + static const ScalingOverCenterType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverCenter2Type + : public ArgsParserObjectType +{ + ScalingOverCenter2Type(); + ~ScalingOverCenter2Type(); +public: + static const ScalingOverCenter2Type* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverLineType + : public ArgsParserObjectType +{ + ScalingOverLineType(); + ~ScalingOverLineType(); +public: + static const ScalingOverLineType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ScalingOverLine2Type + : public ArgsParserObjectType +{ + ScalingOverLine2Type(); + ~ScalingOverLine2Type(); +public: + static const ScalingOverLine2Type* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ProjectiveRotationType + : public ArgsParserObjectType +{ + ProjectiveRotationType(); + ~ProjectiveRotationType(); +public: + static const ProjectiveRotationType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class HarmonicHomologyType + : public ArgsParserObjectType +{ + HarmonicHomologyType(); + ~HarmonicHomologyType(); +public: + static const HarmonicHomologyType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class AffinityB2TrType + : public ArgsParserObjectType +{ + AffinityB2TrType(); + ~AffinityB2TrType(); +public: + static const AffinityB2TrType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class AffinityGI3PType + : public ArgsParserObjectType +{ + AffinityGI3PType(); + ~AffinityGI3PType(); +public: + static const AffinityGI3PType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ProjectivityB2QuType + : public ArgsParserObjectType +{ + ProjectivityB2QuType(); + ~ProjectivityB2QuType(); +public: + static const ProjectivityB2QuType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ProjectivityGI4PType + : public ArgsParserObjectType +{ + ProjectivityGI4PType(); + ~ProjectivityGI4PType(); +public: + static const ProjectivityGI4PType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class CastShadowType + : public ArgsParserObjectType +{ + CastShadowType(); + ~CastShadowType(); +public: + static const CastShadowType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +class ApplyTransformationObjectType + : public ArgsParserObjectType +{ + ApplyTransformationObjectType(); + ~ApplyTransformationObjectType(); +public: + static const ApplyTransformationObjectType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + bool isTransform() const; +}; + +class SimilitudeType + : public ArgsParserObjectType +{ + SimilitudeType(); + ~SimilitudeType(); +public: + static const SimilitudeType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; + + bool isTransform() const; +}; + +#endif diff --git a/kig/objects/vector_type.cc b/kig/objects/vector_type.cc new file mode 100644 index 00000000..d96be07b --- /dev/null +++ b/kig/objects/vector_type.cc @@ -0,0 +1,100 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@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. + +// 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 "vector_type.h" + +#include "point_imp.h" +#include "other_imp.h" +#include "bogus_imp.h" + +static const ArgsParser::spec argsspecVector[] = +{ + { PointImp::stype(), I18N_NOOP( "Construct a vector from this point" ), + I18N_NOOP( "Select the start point of the new vector..." ), true }, + { PointImp::stype(), I18N_NOOP( "Construct a vector to this point" ), + I18N_NOOP( "Select the end point of the new vector..." ), true } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( VectorType ) + +VectorType::VectorType() + : ObjectABType( "Vector", argsspecVector, 2 ) +{ +} + +VectorType::~VectorType() +{ +} + +const VectorType* VectorType::instance() +{ + static const VectorType t; + return &t; +} + +ObjectImp* VectorType::calc( const Coordinate& a, const Coordinate& b ) const +{ + return new VectorImp( a, b ); +} + +const ObjectImpType* VectorType::resultId() const +{ + return VectorImp::stype(); +} + +static const ArgsParser::spec argsspecVectorSum[] = +{ + { VectorImp::stype(), I18N_NOOP( "Construct the vector sum of this vector and another one." ), + I18N_NOOP( "Select the first of the two vectors of which you want to construct the sum..." ), false }, + { VectorImp::stype(), I18N_NOOP( "Construct the vector sum of this vector and the other one." ), + I18N_NOOP( "Select the other of the two vectors of which you want to construct the sum..." ), false }, + { PointImp::stype(), I18N_NOOP( "Construct the vector sum starting at this point." ), + I18N_NOOP( "Select the point to construct the sum vector in..." ), false } +}; + +KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( VectorSumType ) + +VectorSumType::VectorSumType() + : ArgsParserObjectType( "VectorSum", argsspecVectorSum, 3 ) +{ +} + +VectorSumType::~VectorSumType() +{ +} + +const VectorSumType* VectorSumType::instance() +{ + static const VectorSumType t; + return &t; +} + +ObjectImp* VectorSumType::calc( const Args& args, const KigDocument& ) const +{ + if ( ! margsparser.checkArgs( args ) ) return new InvalidImp; + + const VectorImp& a = *static_cast<const VectorImp*>( args[0] ); + const VectorImp& b = *static_cast<const VectorImp*>( args[1] ); + const PointImp& p = *static_cast<const PointImp*>( args[2] ); + + return new VectorImp( p.coordinate(), p.coordinate() + a.dir() + b.dir() ); +} + +const ObjectImpType* VectorSumType::resultId() const +{ + return VectorImp::stype(); +} diff --git a/kig/objects/vector_type.h b/kig/objects/vector_type.h new file mode 100644 index 00000000..e1756ba5 --- /dev/null +++ b/kig/objects/vector_type.h @@ -0,0 +1,45 @@ +// Copyright (C) 2004 Dominique Devriese <devriese@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. + +// 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. + +#ifndef KIG_OBJECTS_VECTOR_TYPE_H +#define KIG_OBJECTS_VECTOR_TYPE_H + +#include "base_type.h" + +class VectorType + : public ObjectABType +{ + VectorType(); + ~VectorType(); +public: + static const VectorType* instance(); + ObjectImp* calc( const Coordinate& a, const Coordinate& b ) const; + const ObjectImpType* resultId() const; +}; + +class VectorSumType + : public ArgsParserObjectType +{ + VectorSumType(); + ~VectorSumType(); +public: + static const VectorSumType* instance(); + ObjectImp* calc( const Args& args, const KigDocument& ) const; + const ObjectImpType* resultId() const; +}; + +#endif |