summaryrefslogtreecommitdiffstats
path: root/kig/misc
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce599e4f9f94b4eb00c1b5edb85bce5431ab3df2 (patch)
treed3bb9f5d25a2dc09ca81adecf39621d871534297 /kig/misc
downloadtdeedu-ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2.tar.gz
tdeedu-ce599e4f9f94b4eb00c1b5edb85bce5431ab3df2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeedu@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kig/misc')
-rw-r--r--kig/misc/Makefile.am50
-rw-r--r--kig/misc/argsparser.cpp267
-rw-r--r--kig/misc/argsparser.h187
-rw-r--r--kig/misc/boost_intrusive_pointer.hpp256
-rw-r--r--kig/misc/builtin_stuff.cc596
-rw-r--r--kig/misc/builtin_stuff.h23
-rw-r--r--kig/misc/calcpaths.cc303
-rw-r--r--kig/misc/calcpaths.h88
-rw-r--r--kig/misc/common.cpp520
-rw-r--r--kig/misc/common.h291
-rw-r--r--kig/misc/conic-common.cpp888
-rw-r--r--kig/misc/conic-common.h278
-rw-r--r--kig/misc/coordinate.cpp184
-rw-r--r--kig/misc/coordinate.h169
-rw-r--r--kig/misc/coordinate_system.cpp720
-rw-r--r--kig/misc/coordinate_system.h134
-rw-r--r--kig/misc/cubic-common.cc527
-rw-r--r--kig/misc/cubic-common.h120
-rw-r--r--kig/misc/goniometry.cc137
-rw-r--r--kig/misc/goniometry.h72
-rw-r--r--kig/misc/guiaction.cc367
-rw-r--r--kig/misc/guiaction.h174
-rw-r--r--kig/misc/kigfiledialog.cc81
-rw-r--r--kig/misc/kigfiledialog.h77
-rw-r--r--kig/misc/kiginputdialog.cc283
-rw-r--r--kig/misc/kiginputdialog.h123
-rw-r--r--kig/misc/kignumerics.cpp389
-rw-r--r--kig/misc/kignumerics.h47
-rw-r--r--kig/misc/kigpainter.cpp953
-rw-r--r--kig/misc/kigpainter.h291
-rw-r--r--kig/misc/kigtransform.cpp810
-rw-r--r--kig/misc/kigtransform.h190
-rw-r--r--kig/misc/lists.cc389
-rw-r--r--kig/misc/lists.h170
-rw-r--r--kig/misc/object_constructor.cc609
-rw-r--r--kig/misc/object_constructor.h396
-rw-r--r--kig/misc/object_hierarchy.cc774
-rw-r--r--kig/misc/object_hierarchy.h111
-rw-r--r--kig/misc/rect.cc308
-rw-r--r--kig/misc/rect.h140
-rw-r--r--kig/misc/screeninfo.cc92
-rw-r--r--kig/misc/screeninfo.h57
-rw-r--r--kig/misc/special_constructors.cc1628
-rw-r--r--kig/misc/special_constructors.h320
44 files changed, 14589 insertions, 0 deletions
diff --git a/kig/misc/Makefile.am b/kig/misc/Makefile.am
new file mode 100644
index 00000000..d97d7989
--- /dev/null
+++ b/kig/misc/Makefile.am
@@ -0,0 +1,50 @@
+INCLUDES=$(all_includes)
+noinst_LTLIBRARIES=libmisc.la
+noinst_HEADERS = \
+ argsparser.h \
+ builtin_stuff.h \
+ calcpaths.h \
+ common.h \
+ conic-common.h \
+ coordinate.h \
+ coordinate_system.h \
+ cubic-common.h \
+ goniometry.h \
+ guiaction.h \
+ kigfiledialog.h \
+ kiginputdialog.h \
+ kignumerics.h \
+ kigpainter.h \
+ kigtransform.h \
+ lists.h \
+ object_constructor.h \
+ object_hierarchy.h \
+ rect.h \
+ screeninfo.h \
+ special_constructors.h
+
+libmisc_la_SOURCES = \
+ argsparser.cpp \
+ builtin_stuff.cc \
+ calcpaths.cc \
+ common.cpp \
+ conic-common.cpp \
+ coordinate.cpp \
+ coordinate_system.cpp \
+ cubic-common.cc \
+ goniometry.cc \
+ guiaction.cc \
+ kigfiledialog.cc \
+ kiginputdialog.cc \
+ kignumerics.cpp \
+ kigpainter.cpp \
+ kigtransform.cpp \
+ lists.cc \
+ object_constructor.cc \
+ object_hierarchy.cc \
+ rect.cc \
+ screeninfo.cc \
+ special_constructors.cc
+
+libmisc_la_LIBADD=-lm
+METASOURCES=AUTO
diff --git a/kig/misc/argsparser.cpp b/kig/misc/argsparser.cpp
new file mode 100644
index 00000000..c2387970
--- /dev/null
+++ b/kig/misc/argsparser.cpp
@@ -0,0 +1,267 @@
+// 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 "argsparser.h"
+
+#include "../objects/object_imp.h"
+#include "../objects/object_holder.h"
+
+#include <cassert>
+#include <algorithm>
+#include <kdebug.h>
+
+void ArgsParser::initialize( const struct spec* args, int n )
+{
+ std::vector<spec> vect( args, args + n );
+ initialize( vect );
+}
+
+ArgsParser::ArgsParser()
+{
+}
+
+ArgsParser::ArgsParser( const std::vector<spec>& args )
+{
+ initialize( args );
+}
+
+void ArgsParser::initialize( const std::vector<spec>& args )
+{
+ margs = args;
+}
+
+ArgsParser::ArgsParser( const spec* args, int n )
+{
+ initialize( args, n );
+}
+
+static bool hasimp( const ObjectCalcer& o, const ObjectImpType* imptype )
+{
+ return o.imp()->inherits( imptype );
+}
+
+static bool hasimp( const ObjectImp& o, const ObjectImpType* imptype )
+{
+ return o.inherits( imptype );
+}
+
+static bool isvalid( const ObjectImp& o )
+{
+ return o.valid();
+}
+
+static bool isvalid( const ObjectCalcer& o )
+{
+ return o.imp()->valid();
+}
+
+// we use a template method that is used for both Objects and Args to
+// not have to write the same thing twice..
+template <class Collection>
+static int check( const Collection& c, const std::vector<ArgsParser::spec>& margs )
+{
+ std::vector<bool> found( margs.size() );
+
+ for ( typename Collection::const_iterator o = c.begin(); o != c.end(); ++o )
+ {
+ for ( uint i = 0; i < margs.size(); ++i )
+ {
+ if ( hasimp( **o, margs[i].type ) && !found[i] )
+ {
+ // object o is of a type that we're looking for
+ found[i] = true;
+ goto matched;
+ };
+ };
+ return ArgsParser::Invalid;
+ matched:
+ ;
+ };
+ for( uint i = 0; i < margs.size(); ++i )
+ if ( !found[i] ) return ArgsParser::Valid;
+ return ArgsParser::Complete;
+}
+
+int ArgsParser::check( const Args& os ) const
+{
+ return ::check( os, margs );
+}
+
+int ArgsParser::check( const std::vector<ObjectCalcer*>& os ) const
+{
+ return ::check( os, margs );
+}
+
+template <typename Collection>
+static Collection parse( const Collection& os,
+ const std::vector<ArgsParser::spec> margs )
+{
+ Collection ret( margs.size(), static_cast<typename Collection::value_type>( 0 ) );
+
+ for ( typename Collection::const_iterator o = os.begin(); o != os.end(); ++o )
+ {
+ for( uint i = 0; i < margs.size(); ++i )
+ if ( hasimp( **o, margs[i].type ) && ret[i] == 0 )
+ {
+ // object o is of a type that we're looking for
+ ret[i] = *o;
+ goto added;
+ }
+ added:
+ ;
+ };
+ // remove 0's from the output..
+ ret.erase(
+ std::remove( ret.begin(), ret.end(),
+ static_cast<typename Collection::value_type>( 0 ) ),
+ ret.end() );
+ return ret;
+}
+
+Args ArgsParser::parse( const Args& os ) const
+{
+ return ::parse( os, margs );
+}
+
+std::vector<ObjectCalcer*> ArgsParser::parse( const std::vector<ObjectCalcer*>& os ) const
+{
+ return ::parse( os, margs );
+}
+
+ArgsParser ArgsParser::without( const ObjectImpType* type ) const
+{
+ std::vector<spec> ret;
+ ret.reserve( margs.size() - 1 );
+ for ( uint i = 0; i < margs.size(); ++i )
+ if ( margs[i].type != type )
+ ret.push_back( margs[i] );
+ return ArgsParser( ret );
+}
+
+ArgsParser::spec ArgsParser::findSpec( const ObjectImp* obj, const Args& parents ) const
+{
+ spec ret;
+ ret.type = 0;
+
+ std::vector<bool> found( margs.size(), false );
+
+ for ( Args::const_iterator o = parents.begin();
+ o != parents.end(); ++o )
+ {
+ for ( uint i = 0; i < margs.size(); ++i )
+ {
+ if ( (*o)->inherits( margs[i].type ) && !found[i] )
+ {
+ // object o is of a type that we're looking for
+ found[i] = true;
+ if ( *o == obj ) return margs[i];
+ // i know that "goto's are *evil*", but they're very useful
+ // and completely harmless if you use them as better "break;"
+ // statements.. trust me ;)
+ goto matched;
+ };
+ };
+ matched:
+ ;
+ };
+ kdDebug() << k_funcinfo << "no proper spec found :(" << endl;
+ return ret;
+}
+
+const ObjectImpType* ArgsParser::impRequirement(
+ const ObjectImp* o, const Args& parents ) const
+{
+ spec s = findSpec( o, parents );
+ return s.type;
+}
+
+std::string ArgsParser::usetext( const ObjectImp* obj, const Args& sel ) const
+{
+ spec s = findSpec( obj, sel );
+ return s.usetext;
+}
+
+template<typename Collection>
+static bool checkArgs( const Collection& os, uint min, const std::vector<ArgsParser::spec>& argsspec )
+{
+ assert( os.size() <= argsspec.size() );
+ if( os.size() < min ) return false;
+ uint checknum = os.size();
+ for ( uint i = 0; i < checknum; ++i )
+ {
+ if( !isvalid( *os[i] ) ) return false;
+ if( !hasimp( *os[i], argsspec[i].type ) ) return false;
+ }
+ return true;
+}
+
+bool ArgsParser::checkArgs( const Args& os ) const
+{
+ return checkArgs( os, margs.size() );
+}
+
+bool ArgsParser::checkArgs( const Args& os, uint min ) const
+{
+ return ::checkArgs( os, min, margs );
+}
+
+bool ArgsParser::checkArgs( const std::vector<ObjectCalcer*>& os ) const
+{
+ return checkArgs( os, margs.size() );
+}
+
+bool ArgsParser::checkArgs( const std::vector<ObjectCalcer*>& os, uint minobjects ) const
+{
+ return ::checkArgs( os, minobjects, margs );
+}
+
+ArgsParser::~ArgsParser()
+{
+}
+
+bool ArgsParser::isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const
+{
+ spec s = findSpec( o, parents );
+ return s.onOrThrough;
+}
+
+std::string ArgsParser::selectStatement( const Args& selection ) const
+{
+ std::vector<bool> found( margs.size(), false );
+
+ for ( Args::const_iterator o = selection.begin();
+ o != selection.end(); ++o )
+ {
+ for ( uint i = 0; i < margs.size(); ++i )
+ {
+ if ( (*o)->inherits( margs[i].type ) && !found[i] )
+ {
+ // object o is of a type that we're looking for
+ found[i] = true;
+ break;
+ }
+ }
+ }
+ for ( uint i = 0; i < margs.size(); ++i )
+ {
+ if ( !found[i] )
+ return margs[i].selectstat;
+ }
+ kdDebug() << k_funcinfo << "no proper select statement found :(" << endl;
+ return 0;
+}
+
diff --git a/kig/misc/argsparser.h b/kig/misc/argsparser.h
new file mode 100644
index 00000000..001d9359
--- /dev/null
+++ b/kig/misc/argsparser.h
@@ -0,0 +1,187 @@
+// 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_MISC_ARGSPARSER_H
+#define KIG_MISC_ARGSPARSER_H
+
+#include "../objects/common.h"
+
+#include <string>
+
+class ObjectImpType;
+
+/**
+ * This class is meant to take care of checking the types of the
+ * parents to ObjectCalcer's, and to put them in the correct order.
+ * An ObjectType should construct an ArgsParser with a specification
+ * of the arguments it wants. This specification is given as an array
+ * of ArgsParser::spec structs. This struct contains a pointer to an
+ * ObjectImpType ( which is the type you want the argument to have ),
+ * a string ( which is an I18N_NOOP'd string describing what you will
+ * be using the argument for ) and a boolean ( which says whether the
+ * constructed object is by construction on the curve argument ( if
+ * the constructed object is a point ), or whether the constructed
+ * object is by construction through the point argument ( if the
+ * constructed object is a curve ) ).
+ *
+ * An ObjectType using an ArgsParser to take care of the various
+ * things that it can handle ( impRequirement, the sortArgs functions
+ * and the isDefinedOnOrThrough stuff ), should inherit from
+ * ArgsParserObjectType, which takes care of calling the ArgsParser
+ * for these things... It also allows you to use a convenient
+ * ObjectConstructor for your type.
+ *
+ * E.g., let's see what CircleBCPType has for its arguments spec:
+ * here's some code:
+ * \code
+ * static const ArgsParser::spec argsspecTranslation[] =
+ * {
+ * { ObjectImp::stype(), I18N_NOOP("Translate this object"), false },
+ * { VectorImp::stype(), I18N_NOOP("Translate by this vector"), false }
+ * };
+ *
+ * TranslatedType::TranslatedType()
+ * : ArgsParserObjectType( "Translation", argsspecTranslation, 2 )
+ * {
+ * }
+ *
+ * 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 );
+ * }
+ * \endcode
+ *
+ * As you can see above, the argsspec can be declared right in the
+ * cpp-file. The usetexts explain to the user what the argument in
+ * question will be used for. The boolean says that in this case, the
+ * constructed object is not by construction on or through one of its
+ * arguments. In the constructor, you simply call the
+ * ArgsParserObjectType with the argsspec struct you defined, and the
+ * number of arguments in the argsspec ( in this case 2 ).
+ *
+ * In the calc function, you can rely on the arguments already being
+ * in the correct order ( the same order as you put them in in the
+ * arguments spec. You should use the checkArgs function to check if
+ * all the arguments are valid, and if they aren't return a
+ * InvalidImp. All objects can always become invalid ( e.g. an
+ * intersection point of two non-intersecting conics can become valid
+ * again when the conics move ), and you should always check for this.
+ *
+ * An interesting to note here is that the first argument is of a more
+ * general type than the second. A VectorImp is *also* an ObjectImp.
+ * In general, when this happens, you should put the more general type
+ * first, as in general this produces the results that the user
+ * expects. I have no formal proof for this, just talking from
+ * experience. It might be that you experience different things, but
+ * unless you're sure of the results, put the more general type first.
+ *
+ * This class uses a pretty basic algorithm for doing the parsing (
+ * e.g. if a match fails in one order, it does not try a different
+ * order, which could perhaps be necessary in the case of having more
+ * general argument types in the same argument spec ). However, the
+ * current algorithm works in all the situation where I've tested it,
+ * and I don't feel the need to change it. Feel free to do so if you
+ * like, but even if you do, I'm not sure if I will include it in
+ * mainline Kig.
+ */
+class ArgsParser
+{
+public:
+ /**
+ * this are some enum values that we return from some functions.
+ */
+ enum { Invalid = 0, Valid = 1, Complete = 2 };
+ struct spec { const ObjectImpType* type; std::string usetext; std::string selectstat; bool onOrThrough;};
+private:
+ /**
+ * the args spec..
+ */
+ std::vector<spec> margs;
+
+ spec findSpec( const ObjectImp* o, const Args& parents ) const;
+public:
+ ArgsParser( const struct spec* args, int n );
+ ArgsParser( const std::vector<spec>& args );
+ ArgsParser();
+ ~ArgsParser();
+
+ void initialize( const std::vector<spec>& args );
+ void initialize( const struct spec* args, int n );
+
+ /**
+ * returns a new ArgsParser that wants the same args, except for the
+ * ones of the given type..
+ */
+ ArgsParser without( const ObjectImpType* type ) const;
+ // checks if os matches the argument list this parser should parse..
+ int check( const Args& os ) const;
+ int check( const std::vector<ObjectCalcer*>& os ) const;
+ /**
+ * returns the usetext for the argument that o would be used for,
+ * if sel were used as parents..
+ * \p o should be in \p sel ...
+ */
+ std::string usetext( const ObjectImp* o, const Args& sel ) const;
+
+ /**
+ * returns the select statement for the next selectable argument
+ * when the given args are selected.
+ */
+ std::string selectStatement( const Args& sel ) const;
+
+ // this reorders the objects or args so that they are in the same
+ // order as the requested arguments..
+ Args parse( const Args& os ) const;
+ std::vector<ObjectCalcer*> parse( const std::vector<ObjectCalcer*>& os ) const;
+
+ /**
+ * returns the minimal ObjectImp ID that \p o needs to inherit in order
+ * to be useful.. \p o should be part of \p parents .
+ */
+ const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const;
+
+ /**
+ * Supposing that \p parents would be given as parents, 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 ).
+ */
+ bool isDefinedOnOrThrough( const ObjectImp* o, const Args& parents ) const;
+
+ // Checks the args according to this args specification. If the
+ // objects should never have occurred, then an assertion failure
+ // will happen, if one of the args is invalid, then false will be
+ // returned, if all is fine, then true is returned..
+ // assert that the objects are of the right types, and in the right
+ // order as what would be returned by parse( os ).. If minobjects
+ // is provided, then not all objects are needed, and it is enough if
+ // at least minobjects are available.. Use this for object types
+ // that can calc a temporary example object using less than the
+ // required args. These args need to be at the end of argsspec +
+ // anyobjsspec. If minobjects is not provided, then it is assumed
+ // that all args are necessary.
+ bool checkArgs( const std::vector<ObjectCalcer*>& os ) const;
+ bool checkArgs( const std::vector<ObjectCalcer*>& os, uint minobjects ) const;
+ bool checkArgs( const Args& os ) const;
+ bool checkArgs( const Args& os, uint minobjects ) const;
+};
+
+#endif
diff --git a/kig/misc/boost_intrusive_pointer.hpp b/kig/misc/boost_intrusive_pointer.hpp
new file mode 100644
index 00000000..a278e736
--- /dev/null
+++ b/kig/misc/boost_intrusive_pointer.hpp
@@ -0,0 +1,256 @@
+// 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.
+
+
+
+// This code comes from the boost::intrusive_ptr. I adapted it to
+// suit my needs ( no dependencies on other boost libs, change the
+// namespace to avoid conflicts,
+
+#ifndef MYBOOST_INTRUSIVE_PTR_HPP_INCLUDED
+#define MYBOOST_INTRUSIVE_PTR_HPP_INCLUDED
+
+//
+// intrusive_ptr.hpp
+//
+// Copyright (c) 2001, 2002 Peter Dimov
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all copies.
+// This software is provided "as is" without express or implied
+// warranty, and with no claim as to its suitability for any purpose.
+//
+// See http://www.boost.org/libs/smart_ptr/intrusive_ptr.html for documentation.
+//
+
+#include <functional> // for std::less
+#include <iosfwd> // for std::basic_ostream
+
+
+namespace myboost
+{
+
+//
+// intrusive_ptr
+//
+// A smart pointer that uses intrusive reference counting.
+//
+// Relies on unqualified calls to
+//
+// void intrusive_ptr_add_ref(T * p);
+// void intrusive_ptr_release(T * p);
+//
+// (p != 0)
+//
+// The object is responsible for destroying itself.
+//
+
+template<class T> class intrusive_ptr
+{
+private:
+
+ typedef intrusive_ptr this_type;
+
+public:
+
+ typedef T element_type;
+
+ intrusive_ptr(): p_(0)
+ {
+ }
+
+ intrusive_ptr(T * p, bool add_ref = true): p_(p)
+ {
+ if(p_ != 0 && add_ref) intrusive_ptr_add_ref(p_);
+ }
+
+#if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES)
+
+ template<class U> intrusive_ptr(intrusive_ptr<U> const & rhs): p_(rhs.get())
+ {
+ if(p_ != 0) intrusive_ptr_add_ref(p_);
+ }
+
+#endif
+
+ intrusive_ptr(intrusive_ptr const & rhs): p_(rhs.p_)
+ {
+ if(p_ != 0) intrusive_ptr_add_ref(p_);
+ }
+
+ ~intrusive_ptr()
+ {
+ if(p_ != 0) intrusive_ptr_release(p_);
+ }
+
+#if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES)
+
+ template<class U> intrusive_ptr & operator=(intrusive_ptr<U> const & rhs)
+ {
+ this_type(rhs).swap(*this);
+ return *this;
+ }
+
+#endif
+
+ intrusive_ptr & operator=(intrusive_ptr const & rhs)
+ {
+ this_type(rhs).swap(*this);
+ return *this;
+ }
+
+ intrusive_ptr & operator=(T * rhs)
+ {
+ this_type(rhs).swap(*this);
+ return *this;
+ }
+
+ T * get() const
+ {
+ return p_;
+ }
+
+ T & operator*() const
+ {
+ return *p_;
+ }
+
+ T * operator->() const
+ {
+ return p_;
+ }
+
+ typedef T * (intrusive_ptr::*unspecified_bool_type) () const;
+
+ operator unspecified_bool_type () const
+ {
+ return p_ == 0? 0: &intrusive_ptr::get;
+ }
+
+ // operator! is a Borland-specific workaround
+ bool operator! () const
+ {
+ return p_ == 0;
+ }
+
+ void swap(intrusive_ptr & rhs)
+ {
+ T * tmp = p_;
+ p_ = rhs.p_;
+ rhs.p_ = tmp;
+ }
+
+private:
+
+ T * p_;
+};
+
+template<class T, class U> inline bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b)
+{
+ return a.get() == b.get();
+}
+
+template<class T, class U> inline bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b)
+{
+ return a.get() != b.get();
+}
+
+template<class T> inline bool operator==(intrusive_ptr<T> const & a, T * b)
+{
+ return a.get() == b;
+}
+
+template<class T> inline bool operator!=(intrusive_ptr<T> const & a, T * b)
+{
+ return a.get() != b;
+}
+
+template<class T> inline bool operator==(T * a, intrusive_ptr<T> const & b)
+{
+ return a == b.get();
+}
+
+template<class T> inline bool operator!=(T * a, intrusive_ptr<T> const & b)
+{
+ return a != b.get();
+}
+
+#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96
+
+// Resolve the ambiguity between our op!= and the one in rel_ops
+
+template<class T> inline bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b)
+{
+ return a.get() != b.get();
+}
+
+#endif
+
+template<class T> inline bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b)
+{
+ return std::less<T *>()(a.get(), b.get());
+}
+
+template<class T> void swap(intrusive_ptr<T> & lhs, intrusive_ptr<T> & rhs)
+{
+ lhs.swap(rhs);
+}
+
+// mem_fn support
+
+template<class T> T * get_pointer(intrusive_ptr<T> const & p)
+{
+ return p.get();
+}
+
+template<class T, class U> intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & p)
+{
+ return static_cast<T *>(p.get());
+}
+
+template<class T, class U> intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & p)
+{
+ return dynamic_cast<T *>(p.get());
+}
+
+// operator<<
+
+#if defined(__GNUC__) && (__GNUC__ < 3)
+
+template<class Y> std::ostream & operator<< (std::ostream & os, intrusive_ptr<Y> const & p)
+{
+ os << p.get();
+ return os;
+}
+
+#else
+
+template<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, intrusive_ptr<Y> const & p)
+{
+ os << p.get();
+ return os;
+}
+
+#endif
+
+} // namespace myboost
+
+#ifdef BOOST_MSVC
+# pragma warning(pop)
+#endif
+
+#endif // #ifndef MYBOOST_INTRUSIVE_PTR_HPP_INCLUDED
diff --git a/kig/misc/builtin_stuff.cc b/kig/misc/builtin_stuff.cc
new file mode 100644
index 00000000..e162e7ac
--- /dev/null
+++ b/kig/misc/builtin_stuff.cc
@@ -0,0 +1,596 @@
+// 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 "builtin_stuff.h"
+
+#include <config.h>
+
+#include "object_constructor.h"
+#include "lists.h"
+#include "special_constructors.h"
+#include "guiaction.h"
+
+#include "../objects/angle_type.h"
+#include "../objects/arc_type.h"
+#include "../objects/circle_type.h"
+#include "../objects/conic_types.h"
+#include "../objects/cubic_type.h"
+#include "../objects/intersection_types.h"
+#include "../objects/inversion_type.h"
+#include "../objects/line_imp.h"
+#include "../objects/line_type.h"
+#include "../objects/object_imp.h"
+#include "../objects/other_imp.h"
+#include "../objects/other_type.h"
+#include "../objects/point_type.h"
+#include "../objects/tests_type.h"
+#include "../objects/transform_types.h"
+#include "../objects/vector_type.h"
+#include "../objects/polygon_type.h"
+
+#include <klocale.h>
+
+void setupBuiltinStuff()
+{
+ static bool done = false;
+ if ( ! done )
+ {
+ ObjectConstructorList* ctors = ObjectConstructorList::instance();
+ GUIActionList* actions = GUIActionList::instance();
+ ObjectConstructor* c = 0;
+
+ // segment...
+ c = new SimpleObjectTypeConstructor(
+ SegmentABType::instance(), I18N_NOOP( "Segment" ),
+ I18N_NOOP( "A segment constructed from its start and end point" ),
+ "segment" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_segment", Qt::Key_S ) );
+
+ // line by two points..
+ c = new SimpleObjectTypeConstructor(
+ LineABType::instance(), I18N_NOOP( "Line by Two Points" ),
+ I18N_NOOP( "A line constructed through two points"), "line" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_linettp", Qt::Key_L ) );
+
+ // ray by two points..
+ c = new SimpleObjectTypeConstructor(
+ RayABType::instance(), I18N_NOOP( "Half-Line" ),
+ I18N_NOOP( "A half-line by its start point, and another point somewhere on it." ),
+ "ray" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_ray", Qt::Key_R ) );
+
+ // perpendicular line
+ c = new SimpleObjectTypeConstructor(
+ LinePerpendLPType::instance(), I18N_NOOP( "Perpendicular" ),
+ I18N_NOOP( "A line constructed through a point, perpendicular to another line or segment." ),
+ "perpendicular" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_lineperpend" ) );
+
+ // parallel line
+ c = new SimpleObjectTypeConstructor(
+ LineParallelLPType::instance(), I18N_NOOP( "Parallel" ),
+ I18N_NOOP( "A line constructed through a point, and parallel to another line or segment" ),
+ "parallel" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_lineparallel" ) );
+
+ // circle
+ c = new SimpleObjectTypeConstructor(
+ CircleBCPType::instance(), I18N_NOOP( "Circle by Center && Point" ),
+ I18N_NOOP( "A circle constructed by its center and a point that pertains to it" ),
+ "circlebcp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_circlebcp", Qt::Key_C ) );
+
+ c = new SimpleObjectTypeConstructor(
+ CircleBTPType::instance(), I18N_NOOP( "Circle by Three Points" ),
+ I18N_NOOP( "A circle constructed through three points" ),
+ "circlebtp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_circlebtp" ) );
+
+ // declare this object static to this function, so it gets deleted
+ // at the end of the program, without us having to wonder about
+ // deleting it.. We don't want to register this
+ // object-constructor, because that way, "construct the bisector"
+ // would appear twice in the angle popup menu: once as the generic
+ // construct a property stuff, and once because of this ctor..
+ // we only register the guiaction, cause it makes sense to have a
+ // toolbar icon for this..
+ static PropertyObjectConstructor anglebisectionctor(
+ AngleImp::stype(),
+ I18N_NOOP( "Construct Bisector of This Angle" ),
+ I18N_NOOP( "Select the angle you want to construct the bisector of..." ),
+ I18N_NOOP( "Angle Bisector" ),
+ I18N_NOOP( "The bisector of an angle" ),
+ "angle_bisector",
+ "angle-bisector" );
+ actions->add( new ConstructibleAction( &anglebisectionctor, "objects_new_angle_bisector" ) );
+
+ // conic stuff
+ c = new SimpleObjectTypeConstructor(
+ ConicB5PType::instance(), I18N_NOOP( "Conic by Five Points" ),
+ I18N_NOOP( "A conic constructed through five points" ),
+ "conicb5p" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_conicb5p" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ConicBAAPType::instance(),
+ I18N_NOOP( "Hyperbola by Asymptotes && Point" ),
+ I18N_NOOP( "A hyperbola with given asymptotes through a point" ),
+ "conicbaap" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_conicbaap" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ EllipseBFFPType::instance(),
+ I18N_NOOP( "Ellipse by Focuses && Point" ), // focuses is used in preference to foci
+ I18N_NOOP( "An ellipse constructed by its focuses and a point that pertains to it" ),
+ "ellipsebffp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_ellipsebffp" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ HyperbolaBFFPType::instance(),
+ I18N_NOOP( "Hyperbola by Focuses && Point" ), // focuses is used in preference to foci
+ I18N_NOOP( "A hyperbola constructed by its focuses and a point that pertains to it" ),
+ "hyperbolabffp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_hyperbolabffp" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ConicBDFPType::instance(),
+ I18N_NOOP( "Conic by Directrix, Focus && Point" ),
+ I18N_NOOP( "A conic with given directrix and focus, through a point" ),
+ "conicbdfp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_conicbdfp" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ParabolaBTPType::instance(),
+ I18N_NOOP( "Vertical Parabola by Three Points" ),
+ I18N_NOOP( "A vertical parabola constructed through three points" ),
+ "parabolabtp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_parabolabtp" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ CubicB9PType::instance(),
+ I18N_NOOP( "Cubic Curve by Nine Points" ),
+ I18N_NOOP( "A cubic curve constructed through nine points" ),
+ "cubicb9p" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_cubicb9p" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ConicPolarPointType::instance(),
+ I18N_NOOP( "Polar Point of a Line" ),
+ I18N_NOOP( "The polar point of a line with respect to a conic." ),
+ "polarpoint" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_pointpolar" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ConicPolarLineType::instance(),
+ I18N_NOOP( "Polar Line of a Point" ),
+ I18N_NOOP( "The polar line of a point with respect to a conic." ),
+ "polarline" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_linepolar" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ CubicNodeB6PType::instance(),
+ I18N_NOOP( "Cubic Curve with Node by Six Points" ),
+ I18N_NOOP( "A cubic curve with a nodal point at the origin through six points" ),
+ "cubicnodeb6p" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_cubicnodeb6p" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ CubicCuspB4PType::instance(),
+ I18N_NOOP( "Cubic Curve with Cusp by Four Points" ),
+ I18N_NOOP( "A cubic curve with a horizontal cusp at the origin through four points" ),
+ "cubiccuspb4p" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_cubiccuspb4p" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ConicDirectrixType::instance(),
+ I18N_NOOP( "Directrix of a Conic" ),
+ I18N_NOOP( "The directrix line of a conic." ),
+ "directrix" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_linedirectrix" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ AngleType::instance(),
+ I18N_NOOP( "Angle by Three Points" ),
+ I18N_NOOP( "An angle defined by three points" ),
+ "angle" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_angle", Qt::Key_A ) );
+
+ c = new SimpleObjectTypeConstructor(
+ EquilateralHyperbolaB4PType::instance(),
+ I18N_NOOP( "Equilateral Hyperbola by Four Points" ),
+ I18N_NOOP( "An equilateral hyperbola constructed through four points" ),
+ "equilateralhyperbolab4p" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_equilateralhyperbolab4p" ) );
+
+ {
+ // now for the Mid Point action. It does both the mid point of
+ // a segment, and the mid point of two points. The midpoint of
+ // two segments just shows the mid point property, and therefore
+ // doesn't need to be added to the ctors, because there are
+ // already facilities to construct an object's properties..
+ // therefore, we add only an mpotp to the ctors, and add the
+ // merged constructor only to the actions..
+ ctors->add( new MidPointOfTwoPointsConstructor() );
+
+ ObjectConstructor* mpotp = new MidPointOfTwoPointsConstructor();
+ ObjectConstructor* mpos = new PropertyObjectConstructor(
+ SegmentImp::stype(), I18N_NOOP( "Construct the midpoint of this segment" ),
+ "", "", "", "", "mid-point" );
+
+ // make this a static object, so it gets deleted at the end of
+ // the program.
+ static MergeObjectConstructor m(
+ I18N_NOOP( "Mid Point" ),
+ I18N_NOOP( "The midpoint of a segment or two other points" ),
+ "bisection" );
+ m.merge( mpotp );
+ m.merge( mpos );
+ actions->add( new ConstructibleAction( &m, "objects_new_midpoint", Qt::Key_M ) );
+ };
+
+ c = new SimpleObjectTypeConstructor(
+ VectorType::instance(),
+ I18N_NOOP( "Vector" ),
+ I18N_NOOP( "Construct a vector from two given points." ),
+ "vector" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_vector", Qt::Key_V ) );
+
+ c = new SimpleObjectTypeConstructor(
+ VectorSumType::instance(),
+ I18N_NOOP( "Vector Sum" ),
+ I18N_NOOP( "Construct the vector sum of two vectors." ),
+ "vectorsum" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_vectorsum", 0 ) );
+
+ c = new SimpleObjectTypeConstructor(
+ LineByVectorType::instance(),
+ I18N_NOOP( "Line by Vector" ),
+ I18N_NOOP( "Construct the line by a given vector though a given point." ),
+ "linebyvector" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_linebyvector", 0 ) );
+
+ c = new SimpleObjectTypeConstructor(
+ HalflineByVectorType::instance(),
+ I18N_NOOP( "Half-Line by Vector" ),
+ I18N_NOOP( "Construct the half-line by a given vector starting at given point." ),
+ "halflinebyvector" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_halflinebyvector", 0 ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ArcBTPType::instance(),
+ I18N_NOOP( "Arc by Three Points" ),
+ I18N_NOOP( "Construct an arc through three points." ),
+ "arc" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_arcbtp" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ArcBCPAType::instance(),
+ I18N_NOOP( "Arc by Center, Angle && Point" ),
+ I18N_NOOP( "Construct an arc by its center and a given angle, "
+ "starting at a given point" ),
+ "arcbcpa" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_arcbcpa" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ParabolaBDPType::instance(),
+ I18N_NOOP( "Parabola by Directrix && Focus" ),
+ I18N_NOOP( "A parabola defined by its directrix and focus" ),
+ "parabolabdp" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_parabolabdp" ) );
+
+ // Transformation stuff..
+ c = new InversionConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_inversion" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ TranslatedType::instance(),
+ I18N_NOOP( "Translate" ),
+ I18N_NOOP( "The translation of an object by a vector" ),
+ "translation" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_translation" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ PointReflectionType::instance(),
+ I18N_NOOP( "Reflect in Point" ),
+ I18N_NOOP( "An object reflected in a point" ),
+ "centralsymmetry" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_pointreflection" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ LineReflectionType::instance(),
+ I18N_NOOP( "Reflect in Line" ),
+ I18N_NOOP( "An object reflected in a line" ),
+ "mirrorpoint" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_linereflection" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ RotationType::instance(),
+ I18N_NOOP( "Rotate" ),
+ I18N_NOOP( "An object rotated by an angle around a point" ),
+ "rotation" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_rotation" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ScalingOverCenterType::instance(),
+ I18N_NOOP( "Scale" ),
+ I18N_NOOP( "Scale an object over a point, by the ratio given by the length of a segment" ),
+ "scale" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_scalingovercenter" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ScalingOverLineType::instance(),
+ I18N_NOOP( "Scale over Line" ),
+ I18N_NOOP( "An object scaled over a line, by the ratio given by the length of a segment" ),
+ "stretch" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_scalingoverline" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ScalingOverCenter2Type::instance(),
+ I18N_NOOP( "Scale (ratio given by two segments)" ),
+ I18N_NOOP( "Scale an object over a point, by the ratio given by the length of two segments" ),
+ "scale" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_scalingovercenter2" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ ScalingOverLine2Type::instance(),
+ I18N_NOOP( "Scale over Line (ratio given by two segments)" ),
+ I18N_NOOP( "An object scaled over a line, by the ratio given by the length of two segments" ),
+ "stretch" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_scalingoverline2" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ SimilitudeType::instance(),
+ I18N_NOOP( "Apply Similitude" ),
+ I18N_NOOP( "Apply a similitude to an object ( the sequence of a scaling and rotation around a center )" ),
+ "similitude" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_similitude" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ HarmonicHomologyType::instance(),
+ I18N_NOOP( "Harmonic Homology" ),
+ I18N_NOOP( "The harmonic homology with a given center and a given axis (this is a projective transformation)" ),
+ "harmonichomology" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_harmonichomology" ) );
+
+ c = new GenericAffinityConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_genericaffinity" ) );
+
+ c = new GenericProjectivityConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_genericprojectivity" ) );
+
+ c = new SimpleObjectTypeConstructor(
+ CastShadowType::instance(),
+ I18N_NOOP( "Draw Projective Shadow" ),
+ I18N_NOOP( "The shadow of an object with a given light source and projection plane (indicated by a line)" ),
+ "castshadow" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_castshadow" ) );
+
+// c = new SimpleObjectTypeConstructor(
+// ProjectiveRotationType::instance(),
+// I18N_NOOP( "Rotate Projectively" ),
+// I18N_NOOP( "An object projectively rotated by an angle and a half-line" ),
+// "projectiverotation" );
+// ctors->add( c );
+// actions->add( new ConstructibleAction( c, "objects_new_projectiverotation" ) );
+
+ c = new MultiObjectTypeConstructor(
+ ConicAsymptoteType::instance(),
+ I18N_NOOP( "Asymptotes of a Hyperbola" ),
+ I18N_NOOP( "The two asymptotes of a hyperbola." ),
+ "conicasymptotes", -1, 1 );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_lineconicasymptotes" ) );
+
+ c = new ConicRadicalConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_lineconicradical") );
+
+ /* ----------- start polygons --------- */
+
+ c = new SimpleObjectTypeConstructor(
+ TriangleB3PType::instance(),
+ I18N_NOOP( "Triangle by Its Vertices" ),
+ I18N_NOOP( "Construct a triangle given its three vertices." ),
+ "triangle" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_trianglebtp" ) );
+
+ c = new PolygonBNPTypeConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_polygonbnp" ));
+
+ c = new PolygonBCVConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_polygonbcv" ) );
+
+ c = new PolygonVertexTypeConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_polygonvertices" ));
+
+ c = new PolygonSideTypeConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_polygonsides" ));
+
+ c = new SimpleObjectTypeConstructor(
+ ConvexHullType::instance(), I18N_NOOP( "Convex Hull" ),
+ I18N_NOOP( "A polygon that corresponds to the convex hull of another polygon" ),
+ "convexhull" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_convexhull" ) );
+
+ /* ----------- end polygons --------- */
+
+ c = new LocusConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_locus" ) );
+
+ // tests
+ c = new TestConstructor(
+ AreParallelType::instance(),
+ I18N_NOOP( "Parallel Test" ),
+ I18N_NOOP( "Test whether two given lines are parallel" ),
+ "testparallel" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_areparallel" ) );
+
+ c = new TestConstructor(
+ AreOrthogonalType::instance(),
+ I18N_NOOP( "Orthogonal Test" ),
+ I18N_NOOP( "Test whether two given lines are orthogonal" ),
+ "testorthogonal" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_areorthogonal" ) );
+
+ c = new TestConstructor(
+ AreCollinearType::instance(),
+ I18N_NOOP( "Collinear Test" ),
+ I18N_NOOP( "Test whether three given points are collinear" ),
+ "testcollinear" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_arecollinear" ) );
+
+ c = new TestConstructor(
+ ContainsTestType::instance(),
+ I18N_NOOP( "Contains Test" ),
+ I18N_NOOP( "Test whether a given curve contains a given point" ),
+ "testcontains" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_containstest" ) );
+
+ c = new TestConstructor(
+ InPolygonTestType::instance(),
+ I18N_NOOP( "In Polygon Test" ),
+ I18N_NOOP( "Test whether a given polygon contains a given point" ),
+ "test" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_inpolygontest" ) );
+
+ c = new TestConstructor(
+ ConvexPolygonTestType::instance(),
+ I18N_NOOP( "Convex Polygon Test" ),
+ I18N_NOOP( "Test whether a given polygon is convex" ),
+ "test" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_convexpolygontest" ) );
+
+ c = new TestConstructor(
+ SameDistanceType::instance(),
+ I18N_NOOP( "Distance Test" ),
+ I18N_NOOP( "Test whether a given point have the same distance from a given point "
+ "and from another given point" ),
+ "testdistance" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_distancetest" ) );
+
+ c = new TestConstructor(
+ VectorEqualityTestType::instance(),
+ I18N_NOOP( "Vector Equality Test" ),
+ I18N_NOOP( "Test whether two vectors are equal" ),
+ "test" );
+// "testequal" );
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_vectorequalitytest" ) );
+
+ c = new MeasureTransportConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_measuretransport" ));
+
+// c = new SimpleObjectTypeConstructor(
+// MeasureTransportType::instance(),
+// I18N_NOOP( "Measure Transport" ),
+// I18N_NOOP( "Transport the measure of a segment or arc over a line or circle." ),
+// "measuretransport" );
+// ctors->add( c );
+// actions->add( new ConstructibleAction( c, "objects_new_measuretransport" ) );
+
+ // the generic intersection constructor..
+ c = new GenericIntersectionConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_intersection", Qt::Key_I ) );
+
+ // the generic tangent constructor
+ c = new TangentConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_tangent", Qt::Key_T ) );
+
+ // the generic center of curvature constructor
+ c = new CocConstructor();
+ ctors->add( c );
+ actions->add( new ConstructibleAction( c, "objects_new_centerofcurvature" ) );
+
+ actions->add( new ConstructPointAction( "objects_new_normalpoint" ) );
+ actions->add( new ConstructTextLabelAction( "objects_new_textlabel" ) );
+ actions->add( new AddFixedPointAction( "objects_new_point_xy" ) );
+
+#ifdef KIG_ENABLE_PYTHON_SCRIPTING
+#include "../scripting/script-common.h"
+ actions->add( new NewScriptAction(
+ I18N_NOOP( "Python Script" ),
+ I18N_NOOP( "Construct a new Python script." ),
+ "objects_new_script_python",
+ ScriptType::Python ) );
+#endif
+
+#if 0
+ actions->add( new TestAction( "test_stuff" ) );
+#endif
+ };
+
+ done = true;
+}
diff --git a/kig/misc/builtin_stuff.h b/kig/misc/builtin_stuff.h
new file mode 100644
index 00000000..198886fe
--- /dev/null
+++ b/kig/misc/builtin_stuff.h
@@ -0,0 +1,23 @@
+// 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_MISC_BUILTIN_STUFF_H
+#define KIG_MISC_BUILTIN_STUFF_H
+
+void setupBuiltinStuff();
+
+#endif
diff --git a/kig/misc/calcpaths.cc b/kig/misc/calcpaths.cc
new file mode 100644
index 00000000..1532715b
--- /dev/null
+++ b/kig/misc/calcpaths.cc
@@ -0,0 +1,303 @@
+// 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 "calcpaths.h"
+
+#include "../objects/object_calcer.h"
+#include "../objects/object_imp.h"
+
+#include <algorithm>
+
+// mp:
+// The previous algorithm by Dominique had an exponential complexity
+// for some constructions (e.g. a sequence of "n" triangles each inscribed
+// into the previous).
+// The new version is directly taken from a book of Alan Bertossi
+// "Algoritmi e strutture dati"
+
+// temporarily disabling the new algorithm due to the freeze:
+// I previously misunderstood the semantics of this function
+// and thought that the os vector had to be completed with all
+// the subtree generated by it. On the contrary, the os vector
+// contains *all* the objects that we want, we only have to
+// reorder them. Now it *should* work, however we postpone
+// activating this to a more proper moment
+
+// to deactivate the new algorithm change "define" into "undef"
+
+#define NEWCALCPATH
+#ifdef NEWCALCPATH
+void localdfs( ObjectCalcer* obj,
+ std::vector<ObjectCalcer*>& visited,
+ std::vector<ObjectCalcer*>& all);
+
+std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& os )
+{
+ // "all" is the Objects var we're building, in reverse ordering
+ std::vector<ObjectCalcer*> visited;
+ std::vector<ObjectCalcer*> all;
+
+ for ( std::vector<ObjectCalcer*>::const_iterator i = os.begin(); i != os.end(); ++i )
+ {
+ if ( std::find( visited.begin(), visited.end(), *i ) == visited.end() )
+ {
+ localdfs( *i, visited, all );
+ }
+ }
+
+ // now, we need to remove all objects that are not in os
+ // (forgot to do this in previous fix :-( )
+ std::vector<ObjectCalcer*> ret;
+ for ( std::vector<ObjectCalcer*>::reverse_iterator i = all.rbegin(); i != all.rend(); ++i )
+ {
+ // we only add objects that appear in os
+ if ( std::find( os.begin(), os.end(), *i ) != os.end() ) ret.push_back( *i );
+ };
+ return ret;
+}
+
+void localdfs( ObjectCalcer* obj,
+ std::vector<ObjectCalcer*>& visited,
+ std::vector<ObjectCalcer*>& all)
+{
+ visited.push_back( obj );
+ const std::vector<ObjectCalcer*> o = obj->children();
+ for ( std::vector<ObjectCalcer*>::const_iterator i = o.begin(); i != o.end(); ++i )
+ {
+ if ( std::find( visited.begin(), visited.end(), *i ) == visited.end() )
+ localdfs( *i, visited, all );
+ }
+ all.push_back( obj );
+}
+
+// old calcPath commented out...
+
+#else
+// these first two functions were written before i read stuff about
+// graph theory and algorithms, so i'm sure they're far from optimal.
+// However, they seem to work fine, and i don't think there's a real
+// need for optimisation here..
+std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& os )
+{
+ // this is a little experiment of mine, i don't know if it is the
+ // fastest way to do it, but it seems logical to me...
+
+ // the general idea here:
+ // first we build a new Objects variable. For every object in os,
+ // we put all of its children at the end of it, and we do the same
+ // for the ones we add..
+
+ // "all" is the Objects var we're building...
+ std::vector<ObjectCalcer*> all = os;
+ // tmp is the var containing the objects we're iterating over. The
+ // first time around this is the os variable, the next time, this
+ // contains the variables we added in the first round...
+ std::vector<ObjectCalcer*> tmp = os;
+ // tmp2 is a temporary var. During a round, it receives all the
+ // variables we add ( to "all" ) in that round, and at the end of
+ // the round, it is assigned to tmp.
+ std::vector<ObjectCalcer*> tmp2;
+ while ( ! tmp.empty() )
+ {
+ for ( std::vector<ObjectCalcer*>::const_iterator i = tmp.begin(); i != tmp.end(); ++i )
+ {
+ const std::vector<ObjectCalcer*> o = (*i)->children();
+ std::copy( o.begin(), o.end(), std::back_inserter( all ) );
+ std::copy( o.begin(), o.end(), std::back_inserter( tmp2 ) );
+ };
+ tmp = tmp2;
+ tmp2.clear();
+ };
+
+ // now we know that if all objects appear at least once after all of
+ // their parents. So, we take all, and of every object, we remove
+ // every reference except the last one...
+ std::vector<ObjectCalcer*> ret;
+ ret.reserve( os.size() );
+ for ( std::vector<ObjectCalcer*>::reverse_iterator i = all.rbegin(); i != all.rend(); ++i )
+ {
+ // we only add objects that appear in os and only if they are not
+ // already in ret..
+ if ( std::find( ret.begin(), ret.end(), *i ) == ret.end() &&
+ std::find( os.begin(), os.end(), *i ) != os.end() ) ret.push_back( *i );
+ };
+ std::reverse( ret.begin(), ret.end() );
+ return ret;
+}
+#endif
+
+bool addBranch( const std::vector<ObjectCalcer*>& o, const ObjectCalcer* to, std::vector<ObjectCalcer*>& ret )
+{
+ bool rb = false;
+ for ( std::vector<ObjectCalcer*>::const_iterator i = o.begin(); i != o.end(); ++i )
+ {
+ if ( *i == to )
+ rb = true;
+ else
+ if ( addBranch( (*i)->children(), to, ret ) )
+ {
+ rb = true;
+ ret.push_back( *i );
+ };
+ };
+ return rb;
+}
+
+std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to )
+{
+ std::vector<ObjectCalcer*> all;
+
+ for ( std::vector<ObjectCalcer*>::const_iterator i = from.begin(); i != from.end(); ++i )
+ {
+ (void) addBranch( (*i)->children(), to, all );
+ };
+
+ std::vector<ObjectCalcer*> ret;
+ for ( std::vector<ObjectCalcer*>::iterator i = all.begin(); i != all.end(); ++i )
+ {
+ if ( std::find( ret.begin(), ret.end(), *i ) == ret.end() )
+ ret.push_back( *i );
+ };
+ return std::vector<ObjectCalcer*>( ret.rbegin(), ret.rend() );
+}
+
+static void addNonCache( ObjectCalcer* o, std::vector<ObjectCalcer*>& ret )
+{
+ if ( ! o->imp()->isCache() )
+ if ( std::find( ret.begin(), ret.end(), o ) == ret.end() )
+ ret.push_back( o );
+ else
+ {
+ std::vector<ObjectCalcer*> parents = o->parents();
+ for ( uint i = 0; i < parents.size(); ++i )
+ addNonCache( parents[i], ret );
+ };
+}
+
+static bool visit( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& from, std::vector<ObjectCalcer*>& ret )
+{
+ // this function returns true if the visited object depends on one
+ // of the objects in from. If we encounter objects that are on the
+ // side of the tree path ( they do not depend on from themselves,
+ // but their direct children do ), then we add them to ret.
+ if ( std::find( from.begin(), from.end(), o ) != from.end() ) return true;
+
+ std::vector<bool> deps( o->parents().size(), false );
+ bool somedepend = false;
+ bool alldepend = true;
+ std::vector<ObjectCalcer*> parents = o->parents();
+ for ( uint i = 0; i < parents.size(); ++i )
+ {
+ bool v = visit( parents[i], from, ret );
+ somedepend |= v;
+ alldepend &= v;
+ deps[i] = v;
+ };
+ if ( somedepend && ! alldepend )
+ {
+ for ( uint i = 0; i < deps.size(); ++i )
+ if ( ! deps[i] )
+ addNonCache( parents[i], ret );
+ };
+
+ return somedepend;
+}
+
+std::vector<ObjectCalcer*> sideOfTreePath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to )
+{
+ std::vector<ObjectCalcer*> ret;
+ visit( to, from, ret );
+ return ret;
+}
+
+std::vector<ObjectCalcer*> getAllParents( const std::vector<ObjectCalcer*>& objs )
+{
+ using namespace std;
+ std::set<ObjectCalcer*> ret( objs.begin(),objs.end() );
+ std::set<ObjectCalcer*> cur = ret;
+ while ( ! cur.empty() )
+ {
+ std::set<ObjectCalcer*> next;
+ for ( std::set<ObjectCalcer*>::const_iterator i = cur.begin(); i != cur.end(); ++i )
+ {
+ std::vector<ObjectCalcer*> parents = (*i)->parents();
+ next.insert( parents.begin(), parents.end() );
+ };
+
+ ret.insert( next.begin(), next.end() );
+ cur = next;
+ };
+ return std::vector<ObjectCalcer*>( ret.begin(), ret.end() );
+}
+
+std::vector<ObjectCalcer*> getAllParents( ObjectCalcer* obj )
+{
+ std::vector<ObjectCalcer*> objs;
+ objs.push_back( obj );
+ return getAllParents( objs );
+}
+
+bool isChild( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& os )
+{
+ std::vector<ObjectCalcer*> parents = o->parents();
+ std::set<ObjectCalcer*> cur( parents.begin(), parents.end() );
+ while ( ! cur.empty() )
+ {
+ std::set<ObjectCalcer*> next;
+ for ( std::set<ObjectCalcer*>::const_iterator i = cur.begin(); i != cur.end(); ++i )
+ {
+ if ( std::find( os.begin(), os.end(), *i ) != os.end() ) return true;
+ std::vector<ObjectCalcer*> parents = (*i)->parents();
+ next.insert( parents.begin(), parents.end() );
+ };
+ cur = next;
+ };
+ return false;
+}
+
+std::set<ObjectCalcer*> getAllChildren( ObjectCalcer* obj )
+{
+ std::vector<ObjectCalcer*> objs;
+ objs.push_back( obj );
+ return getAllChildren( objs );
+}
+
+std::set<ObjectCalcer*> getAllChildren( const std::vector<ObjectCalcer*> objs )
+{
+ std::set<ObjectCalcer*> ret;
+ // objects to iterate over...
+ std::set<ObjectCalcer*> cur( objs.begin(), objs.end() );
+ while( !cur.empty() )
+ {
+ // contains the objects to iterate over the next time around...
+ std::set<ObjectCalcer*> next;
+ for( std::set<ObjectCalcer*>::iterator i = cur.begin();
+ i != cur.end(); ++i )
+ {
+ ret.insert( *i );
+ std::vector<ObjectCalcer*> children = (*i)->children();
+ next.insert( children.begin(), children.end() );
+ };
+ cur = next;
+ };
+ return ret;
+}
+
+bool isPointOnCurve( const ObjectCalcer* point, const ObjectCalcer* curve )
+{
+ return point->isDefinedOnOrThrough( curve ) || curve->isDefinedOnOrThrough( point );
+}
diff --git a/kig/misc/calcpaths.h b/kig/misc/calcpaths.h
new file mode 100644
index 00000000..620558a3
--- /dev/null
+++ b/kig/misc/calcpaths.h
@@ -0,0 +1,88 @@
+// 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_MISC_CALCPATHS_H
+#define KIG_MISC_CALCPATHS_H
+
+#include "../objects/common.h"
+
+/**
+ * This function sorts \p os such that they're in the right order for
+ * calc()-ing. This means that child objects must appear after their
+ * parents ( for you graph people, this is just a topological sort.. )
+ */
+std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& os );
+
+/**
+ * This is a different function for more or less the same purpose. It
+ * takes a few Objects, which are considered to have been calced
+ * already. Then, it puts the necessary part of their children in the
+ * right order, so that calc()-ing correctly updates all of their data
+ * ( they're calc'ed in the right order, i mean... ). The objects in
+ * from are normally not included in the output, unless they appear
+ * somewhere in the middle of the calc-path towards to...
+ */
+std::vector<ObjectCalcer*> calcPath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to );
+
+/**
+ * This function returns all objects on the side of the path through
+ * the dependency tree from from down to to. This means that we look
+ * for any objects that don't depend on any of the objects in from
+ * themselves, but of which one of the direct children does. We need
+ * this function for Locus stuff...
+ */
+std::vector<ObjectCalcer*> sideOfTreePath( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to );
+
+/**
+ * This function returns all objects above the \p given in the
+ * dependency graph. The \p given objects are also included
+ * themselves..
+ */
+std::vector<ObjectCalcer*> getAllParents( const std::vector<ObjectCalcer*>& objs );
+/**
+ * \overload
+ */
+std::vector<ObjectCalcer*> getAllParents( ObjectCalcer* obj );
+
+/**
+ * This function returns all objects below the objects in \p objs in the
+ * dependency graphy. The objects in \p objs are also included
+ * themselves..
+ */
+std::set<ObjectCalcer*> getAllChildren( const std::vector<ObjectCalcer*> objs );
+
+/**
+ * \overload
+ */
+std::set<ObjectCalcer*> getAllChildren( ObjectCalcer* obj );
+
+/**
+ * Returns true if \p o is a descendant of any of the objects in \p os..
+ */
+bool isChild( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& os );
+
+/**
+ * Return true if the given \p point is ( by construction ) on the given
+ * \p curve. This means that it is either a constrained point on the
+ * curve, or the curve is constructed through the point, or the point
+ * is an intersection point of the curve with another curve.
+ * Note that it is assumed that the given point is in fact a point and the
+ * given curve is in fact a curve.
+ */
+bool isPointOnCurve( const ObjectCalcer* point, const ObjectCalcer* curve );
+
+#endif
diff --git a/kig/misc/common.cpp b/kig/misc/common.cpp
new file mode 100644
index 00000000..fccd384f
--- /dev/null
+++ b/kig/misc/common.cpp
@@ -0,0 +1,520 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ 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 "common.h"
+
+#include "../kig/kig_view.h"
+#include "../objects/object_imp.h"
+
+#include <cmath>
+
+#include <kdebug.h>
+#include <knumvalidator.h>
+#include <klocale.h>
+#if KDE_IS_VERSION( 3, 1, 90 )
+#include <kinputdialog.h>
+#else
+#include <klineeditdlg.h>
+#endif
+
+Coordinate calcPointOnPerpend( const LineData& l, const Coordinate& t )
+{
+ return calcPointOnPerpend( l.b - l.a, t );
+}
+
+Coordinate calcPointOnPerpend( const Coordinate& dir, const Coordinate& t )
+{
+ return t + ( dir ).orthogonal();
+}
+
+Coordinate calcPointOnParallel( const LineData& l, const Coordinate& t )
+{
+ return calcPointOnParallel( l.b - l.a, t );
+}
+
+Coordinate calcPointOnParallel( const Coordinate& dir, const Coordinate& t )
+{
+ return t + dir*5;
+}
+
+Coordinate calcIntersectionPoint( const LineData& l1, const LineData& l2 )
+{
+ const Coordinate& pa = l1.a;
+ const Coordinate& pb = l1.b;
+ const Coordinate& pc = l2.a;
+ const Coordinate& pd = l2.b;
+
+ double
+ xab = pb.x - pa.x,
+ xdc = pd.x - pc.x,
+ xac = pc.x - pa.x,
+ yab = pb.y - pa.y,
+ ydc = pd.y - pc.y,
+ yac = pc.y - pa.y;
+
+ double det = xab*ydc - xdc*yab;
+ double detn = xac*ydc - xdc*yac;
+
+ // test for parallelism
+ if ( fabs (det) < 1e-6 ) return Coordinate::invalidCoord();
+ double t = detn/det;
+
+ return pa + t*(pb - pa);
+}
+
+void calcBorderPoints( Coordinate& p1, Coordinate& p2, const Rect& r )
+{
+ calcBorderPoints( p1.x, p1.y, p2.x, p2.y, r );
+}
+
+const LineData calcBorderPoints( const LineData& l, const Rect& r )
+{
+ LineData ret( l );
+ calcBorderPoints( ret.a.x, ret.a.y, ret.b.x, ret.b.y, r );
+ return ret;
+}
+
+void calcBorderPoints( double& xa, double& ya, double& xb, double& yb, const Rect& r )
+{
+ // we calc where the line through a(xa,ya) and b(xb,yb) intersects with r:
+ double left = (r.left()-xa)*(yb-ya)/(xb-xa)+ya;
+ double right = (r.right()-xa)*(yb-ya)/(xb-xa)+ya;
+ double top = (r.top()-ya)*(xb-xa)/(yb-ya)+xa;
+ double bottom = (r.bottom()-ya)*(xb-xa)/(yb-ya)+xa;
+
+ // now we go looking for valid points
+ int novp = 0; // number of valid points we have already found
+
+ if (!(top < r.left() || top > r.right())) {
+ // the line intersects with the top side of the rect.
+ ++novp;
+ xa = top; ya = r.top();
+ };
+ if (!(left < r.bottom() || left > r.top())) {
+ // the line intersects with the left side of the rect.
+ if (novp++) { xb = r.left(); yb=left; }
+ else { xa = r.left(); ya=left; };
+ };
+ if (!(right < r.bottom() || right > r.top())) {
+ // the line intersects with the right side of the rect.
+ if (novp++) { xb = r.right(); yb=right; }
+ else { xa = r.right(); ya=right; };
+ };
+ if (!(bottom < r.left() || bottom > r.right())) {
+ // the line intersects with the bottom side of the rect.
+ ++novp;
+ xb = bottom; yb = r.bottom();
+ };
+ if (novp < 2) {
+ // line is completely outside of the window...
+ xa = ya = xb = yb = 0;
+ };
+}
+
+void calcRayBorderPoints( const Coordinate& a, Coordinate& b, const Rect& r )
+{
+ calcRayBorderPoints( a.x, a.y, b.x, b.y, r );
+}
+
+void calcRayBorderPoints( const double xa, const double ya, double& xb,
+ double& yb, const Rect& r )
+{
+ // we calc where the line through a(xa,ya) and b(xb,yb) intersects with r:
+ double left = (r.left()-xa)*(yb-ya)/(xb-xa)+ya;
+ double right = (r.right()-xa)*(yb-ya)/(xb-xa)+ya;
+ double top = (r.top()-ya)*(xb-xa)/(yb-ya)+xa;
+ double bottom = (r.bottom()-ya)*(xb-xa)/(yb-ya)+xa;
+
+ // now we see which we can use...
+ if(
+ // the ray intersects with the top side of the rect..
+ top >= r.left() && top <= r.right()
+ // and b is above a
+ && yb > ya )
+ {
+ xb = top;
+ yb = r.top();
+ return;
+ };
+ if(
+ // the ray intersects with the left side of the rect...
+ left >= r.bottom() && left <= r.top()
+ // and b is on the left of a..
+ && xb < xa )
+ {
+ xb = r.left();
+ yb=left;
+ return;
+ };
+ if (
+ // the ray intersects with the right side of the rect...
+ right >= r.bottom() && right <= r.top()
+ // and b is to the right of a..
+ && xb > xa )
+ {
+ xb = r.right();
+ yb=right;
+ return;
+ };
+ if(
+ // the ray intersects with the bottom side of the rect...
+ bottom >= r.left() && bottom <= r.right()
+ // and b is under a..
+ && yb < ya ) {
+ xb = bottom;
+ yb = r.bottom();
+ return;
+ };
+ kdError() << k_funcinfo << "damn" << endl;
+}
+
+bool isOnLine( const Coordinate& o, const Coordinate& a,
+ const Coordinate& b, const double fault )
+{
+ double x1 = a.x;
+ double y1 = a.y;
+ double x2 = b.x;
+ double y2 = b.y;
+
+ // check your math theory ( homogeneous coördinates ) for this
+ double tmp = fabs( o.x * (y1-y2) + o.y*(x2-x1) + (x1*y2-y1*x2) );
+ return tmp < ( fault * (b-a).length());
+ // if o is on the line ( if the determinant of the matrix
+ // |---|---|---|
+ // | x | y | z |
+ // |---|---|---|
+ // | x1| y1| z1|
+ // |---|---|---|
+ // | x2| y2| z2|
+ // |---|---|---|
+ // equals 0, then p(x,y,z) is on the line containing points
+ // p1(x1,y1,z1) and p2 here, we're working with normal coords, no
+ // homogeneous ones, so all z's equal 1
+}
+
+bool isOnSegment( const Coordinate& o, const Coordinate& a,
+ const Coordinate& b, const double fault )
+{
+ return isOnLine( o, a, b, fault )
+ // not too far to the right
+ && (o.x - kigMax(a.x,b.x) < fault )
+ // not too far to the left
+ && ( kigMin (a.x, b.x) - o.x < fault )
+ // not too high
+ && ( kigMin (a.y, b.y) - o.y < fault )
+ // not too low
+ && ( o.y - kigMax (a.y, b.y) < fault );
+}
+
+bool isOnRay( const Coordinate& o, const Coordinate& a,
+ const Coordinate& b, const double fault )
+{
+ return isOnLine( o, a, b, fault )
+ // not too far in front of a horizontally..
+// && ( a.x - b.x < fault ) == ( a.x - o.x < fault )
+ && ( ( a.x < b.x ) ? ( a.x - o.x < fault ) : ( a.x - o.x > -fault ) )
+ // not too far in front of a vertically..
+// && ( a.y - b.y < fault ) == ( a.y - o.y < fault );
+ && ( ( a.y < b.y ) ? ( a.y - o.y < fault ) : ( a.y - o.y > -fault ) );
+}
+
+bool isOnArc( const Coordinate& o, const Coordinate& c, const double r,
+ const double sa, const double a, const double fault )
+{
+ if ( fabs( ( c - o ).length() - r ) > fault )
+ return false;
+ Coordinate d = o - c;
+ double angle = atan2( d.y, d.x );
+
+ if ( angle < sa ) angle += 2 * M_PI;
+ return angle - sa - a < 1e-4;
+}
+
+const Coordinate calcMirrorPoint( const LineData& l,
+ const Coordinate& p )
+{
+ Coordinate m =
+ calcIntersectionPoint( l,
+ LineData( p,
+ calcPointOnPerpend( l, p )
+ )
+ );
+ return 2*m - p;
+}
+
+const Coordinate calcCircleLineIntersect( const Coordinate& c,
+ const double sqr,
+ const LineData& l,
+ int side )
+{
+ Coordinate proj = calcPointProjection( c, l );
+ Coordinate hvec = proj - c;
+ Coordinate lvec = -l.dir();
+
+ double sqdist = hvec.squareLength();
+ double sql = sqr - sqdist;
+ if ( sql < 0.0 )
+ return Coordinate::invalidCoord();
+ else
+ {
+ double l = sqrt( sql );
+ lvec = lvec.normalize( l );
+ lvec *= side;
+
+ return proj + lvec;
+ };
+}
+
+const Coordinate calcArcLineIntersect( const Coordinate& c, const double sqr,
+ const double sa, const double angle,
+ const LineData& l, int side )
+{
+ const Coordinate possiblepoint = calcCircleLineIntersect( c, sqr, l, side );
+ if ( isOnArc( possiblepoint, c, sqrt( sqr ), sa, angle, test_threshold ) )
+ return possiblepoint;
+ else return Coordinate::invalidCoord();
+}
+
+const Coordinate calcPointProjection( const Coordinate& p,
+ const LineData& l )
+{
+ Coordinate orth = l.dir().orthogonal();
+ return p + orth.normalize( calcDistancePointLine( p, l ) );
+}
+
+double calcDistancePointLine( const Coordinate& p,
+ const LineData& l )
+{
+ double xa = l.a.x;
+ double ya = l.a.y;
+ double xb = l.b.x;
+ double yb = l.b.y;
+ double x = p.x;
+ double y = p.y;
+ double norm = l.dir().length();
+ return ( yb * x - ya * x - xb * y + xa * y + xb * ya - yb * xa ) / norm;
+}
+
+Coordinate calcRotatedPoint( const Coordinate& a, const Coordinate& c, const double arc )
+{
+ // we take a point p on a line through c and parallel with the
+ // X-axis..
+ Coordinate p( c.x + 5, c.y );
+ // we then calc the arc that ac forms with cp...
+ Coordinate d = a - c;
+ d = d.normalize();
+ double aarc = std::acos( d.x );
+ if ( d.y < 0 ) aarc = 2*M_PI - aarc;
+
+ // we now take the sum of the two arcs to find the arc between
+ // pc and ca
+ double asum = aarc + arc;
+
+ Coordinate ret( std::cos( asum ), std::sin( asum ) );
+ ret = ret.normalize( ( a -c ).length() );
+ return ret + c;
+}
+
+Coordinate calcCircleRadicalStartPoint( const Coordinate& ca, const Coordinate& cb,
+ double sqra, double sqrb )
+{
+ Coordinate direc = cb - ca;
+ Coordinate m = (ca + cb)/2;
+
+ double dsq = direc.squareLength();
+ double lambda = dsq == 0.0 ? 0.0
+ : (sqra - sqrb) / (2*dsq);
+
+ direc *= lambda;
+ return m + direc;
+}
+
+double getDoubleFromUser( const QString& caption, const QString& label, double value,
+ QWidget* parent, bool* ok, double min, double max, int decimals )
+{
+#ifdef KIG_USE_KDOUBLEVALIDATOR
+ KDoubleValidator vtor( min, max, decimals, 0, 0 );
+#else
+ KFloatValidator vtor( min, max, (QWidget*) 0, 0 );
+#endif
+#if KDE_IS_VERSION( 3, 1, 90 )
+ QString input = KInputDialog::getText(
+ caption, label, KGlobal::locale()->formatNumber( value, decimals ),
+ ok, parent, "getDoubleFromUserDialog", &vtor );
+#else
+ QString input =
+ KLineEditDlg::getText( caption, label,
+ KGlobal::locale()->formatNumber( value, decimals ),
+ ok, parent, &vtor );
+#endif
+
+ bool myok = true;
+ double ret = KGlobal::locale()->readNumber( input, &myok );
+ if ( ! myok )
+ ret = input.toDouble( & myok );
+ if ( ok ) *ok = myok;
+ return ret;
+}
+
+const Coordinate calcCenter(
+ const Coordinate& a, const Coordinate& b, const Coordinate& c )
+{
+ // this algorithm is written by my brother, Christophe Devriese
+ // <oelewapperke@ulyssis.org> ...
+ // I don't get it myself :)
+
+ double xdo = b.x-a.x;
+ double ydo = b.y-a.y;
+
+ double xao = c.x-a.x;
+ double yao = c.y-a.y;
+
+ double a2 = xdo*xdo + ydo*ydo;
+ double b2 = xao*xao + yao*yao;
+
+ double numerator = (xdo * yao - xao * ydo);
+ if ( numerator == 0 )
+ {
+ // problem: xdo * yao == xao * ydo <=> xdo/ydo == xao / yao
+ // this means that the lines ac and ab have the same direction,
+ // which means they're the same line..
+ // FIXME: i would normally throw an error here, but KDE doesn't
+ // use exceptions, so i'm returning a bogus point :(
+ return a.invalidCoord();
+ /* return (a+c)/2; */
+ };
+ double denominator = 0.5 / numerator;
+
+ double centerx = a.x - (ydo * b2 - yao * a2) * denominator;
+ double centery = a.y + (xdo * b2 - xao * a2) * denominator;
+
+ return Coordinate(centerx, centery);
+}
+
+bool lineInRect( const Rect& r, const Coordinate& a, const Coordinate& b,
+ const int width, const ObjectImp* imp, const KigWidget& w )
+{
+ double miss = w.screenInfo().normalMiss( width );
+
+//mp: the following test didn't work for vertical segments;
+// fortunately the ieee floating point standard allows us to avoid
+// the test altogether, since it would produce an infinity value that
+// makes the final r.contains to fail
+// in any case the corresponding test for a.y - b.y was missing.
+
+// if ( fabs( a.x - b.x ) <= 1e-7 )
+// {
+// // too small to be useful..
+// return r.contains( Coordinate( a.x, r.center().y ), miss );
+// }
+
+// in case we have a segment we need also to check for the case when
+// the segment is entirely contained in the rect, in which case the
+// final tests all fail.
+// it is ok to just check for the midpoint in the rect since:
+// - if we have a segment completely contained in the rect this is true
+// - if the midpoint is in the rect than returning true is safe (also
+// in the cases where we have a ray or a line)
+
+ if ( r.contains( 0.5*( a + b ), miss ) ) return true;
+
+ Coordinate dir = b - a;
+ double m = dir.y / dir.x;
+ double lefty = a.y + m * ( r.left() - a.x );
+ double righty = a.y + m * ( r.right() - a.x );
+ double minv = dir.x / dir.y;
+ double bottomx = a.x + minv * ( r.bottom() - a.y );
+ double topx = a.x + minv * ( r.top() - a.y );
+
+ // these are the intersections between the line, and the lines
+ // defined by the sides of the rectangle.
+ Coordinate leftint( r.left(), lefty );
+ Coordinate rightint( r.right(), righty );
+ Coordinate bottomint( bottomx, r.bottom() );
+ Coordinate topint( topx, r.top() );
+
+ // For each intersection, we now check if we contain the
+ // intersection ( this might not be the case for a segment, when the
+ // intersection is not between the begin and end point.. ) and if
+ // the rect contains the intersection.. If it does, we have a winner..
+ return
+ ( imp->contains( leftint, width, w ) && r.contains( leftint, miss ) ) ||
+ ( imp->contains( rightint, width, w ) && r.contains( rightint, miss ) ) ||
+ ( imp->contains( bottomint, width, w ) && r.contains( bottomint, miss ) ) ||
+ ( imp->contains( topint, width, w ) && r.contains( topint, miss ) );
+}
+
+bool operator==( const LineData& l, const LineData& r )
+{
+ return l.a == r.a && l.b == r.b;
+}
+
+bool LineData::isParallelTo( const LineData& l ) const
+{
+ const Coordinate& p1 = a;
+ const Coordinate& p2 = b;
+ const Coordinate& p3 = l.a;
+ const Coordinate& p4 = l.b;
+
+ double dx1 = p2.x - p1.x;
+ double dy1 = p2.y - p1.y;
+ double dx2 = p4.x - p3.x;
+ double dy2 = p4.y - p3.y;
+
+ return isSingular( dx1, dy1, dx2, dy2 );
+}
+
+bool LineData::isOrthogonalTo( const LineData& l ) const
+{
+ const Coordinate& p1 = a;
+ const Coordinate& p2 = b;
+ const Coordinate& p3 = l.a;
+ const Coordinate& p4 = l.b;
+
+ double dx1 = p2.x - p1.x;
+ double dy1 = p2.y - p1.y;
+ double dx2 = p4.x - p3.x;
+ double dy2 = p4.y - p3.y;
+
+ return isSingular( dx1, dy1, -dy2, dx2 );
+}
+
+bool areCollinear( const Coordinate& p1,
+ const Coordinate& p2, const Coordinate& p3 )
+{
+ return isSingular( p2.x - p1.x, p2.y - p1.y, p3.x - p1.x, p3.y - p1.y );
+}
+
+bool isSingular( const double& a, const double& b,
+ const double& c, const double& d )
+{
+ double det = a*d - b*c;
+ double norm1 = std::fabs(a) + std::fabs(b);
+ double norm2 = std::fabs(c) + std::fabs(d);
+
+/*
+ * test must be done relative to the magnitude of the two
+ * row (or column) vectors!
+ */
+ return ( std::fabs(det) < test_threshold*norm1*norm2 );
+}
+
+const double double_inf = HUGE_VAL;
+const double test_threshold = 1e-6;
diff --git a/kig/misc/common.h b/kig/misc/common.h
new file mode 100644
index 00000000..77a1faa2
--- /dev/null
+++ b/kig/misc/common.h
@@ -0,0 +1,291 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ 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_MISC_COMMON_H
+#define KIG_MISC_COMMON_H
+
+#include "coordinate.h"
+#include "rect.h"
+
+#include <qrect.h>
+#include <kdeversion.h>
+
+#include <vector>
+#include <assert.h>
+
+#ifdef KDE_IS_VERSION
+#if KDE_IS_VERSION( 3, 1, 0 )
+#define KIG_USE_KDOUBLEVALIDATOR
+#else
+#undef KIG_USE_KDOUBLEVALIDATOR
+#endif
+#else
+#undef KIG_USE_KDOUBLEVALIDATOR
+#endif
+
+class ObjectImp;
+class KigWidget;
+
+extern const double double_inf;
+
+/**
+ * Here, we define some algorithms which we need in
+ * various places...
+ */
+
+double getDoubleFromUser( const QString& caption, const QString& label, double value,
+ QWidget* parent, bool* ok, double min, double max, int decimals );
+
+/**
+ * Simple class representing a line. Used by various functions in Kig.
+ */
+class LineData {
+public:
+ /**
+ * \ifnot creating-python-scripting-doc
+ * Default constructor. Sets a and b to the origin.
+ * \endif
+ */
+ LineData() : a(), b() {}
+ /**
+ * Constructor. Sets a and b to the given Coordinates.
+ */
+ LineData( const Coordinate& na, const Coordinate& nb ) : a( na ), b( nb ) {}
+ /**
+ * One point on the line.
+ */
+ Coordinate a;
+ /**
+ * Another point on the line.
+ */
+ Coordinate b;
+ /**
+ * The direction of the line. Equivalent to b - a.
+ */
+ const Coordinate dir() const { return b - a; }
+ /**
+ * The length from a to b.
+ */
+ double length() const { return ( b - a ).length(); }
+
+ /**
+ * Return true if this line is parallel to l.
+ */
+ bool isParallelTo( const LineData& l ) const;
+
+ /**
+ * Return true if this line is orthogonal to l.
+ */
+ bool isOrthogonalTo( const LineData& l ) const;
+};
+
+/**
+ * Equality. Tests two LineData's for equality.
+ */
+bool operator==( const LineData& l, const LineData& r );
+
+/**
+ * This calcs the rotation of point a around point c by arc arc. Arc
+ * is in radians, in the range 0 < arc < 2*pi ...
+ */
+Coordinate calcRotatedPoint( const Coordinate& a, const Coordinate& c, const double arc );
+
+/**
+ * this returns a point, so that the line through point t
+ * and the point returned is perpendicular to the line l.
+ */
+Coordinate calcPointOnPerpend( const LineData& l, const Coordinate& t );
+
+/**
+ * this returns a point, so that the line through point t and the
+ * point returned is perpendicular to the direction given in dir...
+ */
+Coordinate calcPointOnPerpend( const Coordinate& dir, const Coordinate& t );
+
+/**
+ * this returns a point, so that the line through point t
+ * and the point returned is parallel with the line l
+ */
+Coordinate calcPointOnParallel( const LineData& l, const Coordinate& t );
+
+/**
+ * this returns a point, so that the line through point t
+ * and the point returned is parallel with the direction given in dir...
+ */
+Coordinate calcPointOnParallel( const Coordinate& dir, const Coordinate& t );
+
+
+/**
+ * this calcs the point where the lines l and m intersect...
+ */
+Coordinate calcIntersectionPoint( const LineData& l, const LineData& m );
+
+/**
+ * this calcs the intersection points of the circle with center c and
+ * radius sqrt( r ), and the line l. As a circle and a
+ * line have two intersection points, side tells us which one we
+ * need... It should be 1 or -1. If the line and the circle have no
+ * intersection, valid is set to false, otherwise to true...
+ * Note that sqr is the _square_ of the radius. We do this to avoid
+ * rounding errors...
+ */
+const Coordinate calcCircleLineIntersect( const Coordinate& c,
+ const double sqr,
+ const LineData& l,
+ int side );
+
+/**
+ * this calcs the intersection points of the arc with center c,
+ * radius sqrt( r ), start angle sa and angle angle, and the line l.
+ * As a arc and a line can have max two intersection points, side
+ * tells us which one we need... It should be 1 or -1. If the line
+ * and the arc have no intersection, valid is set to false, otherwise
+ * to true... Note that sqr is the _square_ of the radius. We do
+ * this to avoid rounding errors...
+ */
+const Coordinate calcArcLineIntersect( const Coordinate& c, const double sqr,
+ const double sa, const double angle,
+ const LineData& l, int side );
+
+/**
+ * this calculates the perpendicular projection of point p on line
+ * ab...
+ */
+const Coordinate calcPointProjection( const Coordinate& p,
+ const LineData& l );
+
+/**
+ * calc the distance of point p to the line through a and b...
+ */
+double calcDistancePointLine( const Coordinate& p,
+ const LineData& l );
+
+/**
+ * this sets p1 and p2 to p1' and p2' so that p1'p2' is the same line
+ * as p1p2, and so that p1' and p2' are on the border of the Rect...
+ */
+void calcBorderPoints( Coordinate& p1, Coordinate& p2, const Rect& r );
+/**
+ * overload...
+ */
+void calcBorderPoints( double& xa, double& xb, double& ya, double& yb, const Rect& r);
+/**
+ * cleaner overload, intended to replace the above two...
+ */
+const LineData calcBorderPoints( const LineData& l, const Rect& r );
+
+/**
+ * this does the same as the above function, but only for b..
+ */
+void calcRayBorderPoints( const Coordinate& a, Coordinate& b, const Rect& r );
+
+/**
+ * This function calculates the center of the circle going through the
+ * three given points..
+ */
+const Coordinate calcCenter(
+ const Coordinate& a, const Coordinate& b, const Coordinate& c );
+
+/**
+ * overload...
+ */
+void calcRayBorderPoints( const double xa, const double xb, double& ya,
+ double& yb, const Rect& r );
+
+/**
+ * calc the mirror point of p over the line l
+ */
+const Coordinate calcMirrorPoint( const LineData& l,
+ const Coordinate& p );
+
+/**
+ * test collinearity of three points
+ */
+bool areCollinear( const Coordinate& p1, const Coordinate& p2,
+ const Coordinate& p3 );
+
+/**
+ * test if a 2x2 matrix is singular (relatively to the
+ * norm of the two row vectors)
+ */
+bool isSingular( const double& a, const double& b,
+ const double& c, const double& d );
+
+/**
+ * is o on the line defined by point a and point b ?
+ * fault is the allowed difference...
+ */
+bool isOnLine( const Coordinate& o, const Coordinate& a,
+ const Coordinate& b, const double fault );
+
+/**
+ * is o on the segment defined by point a and point b ?
+ * this calls isOnLine(), but also checks if o is "between" a and b...
+ * fault is the allowed difference...
+ */
+bool isOnSegment( const Coordinate& o, const Coordinate& a,
+ const Coordinate& b, const double fault );
+
+bool isOnRay( const Coordinate& o, const Coordinate& a,
+ const Coordinate& b, const double fault );
+
+bool isOnArc( const Coordinate& o, const Coordinate& c, const double r,
+ const double sa, const double a, const double fault );
+
+Coordinate calcCircleRadicalStartPoint( const Coordinate& ca,
+ const Coordinate& cb,
+ double sqra, double sqrb );
+
+/**
+ * Is the line, segment, ray or vector inside r ? We need the imp to
+ * distinguish between rays, lines, segments or whatever.. ( we use
+ * their contains functions actually.. )
+ */
+bool lineInRect( const Rect& r, const Coordinate& a, const Coordinate& b,
+ const int width, const ObjectImp* imp, const KigWidget& w );
+
+template <typename T>
+T kigMin( const T& a, const T& b )
+{
+ return a < b ? a : b;
+}
+
+template <typename T>
+T kigMax( const T& a, const T& b )
+{
+ return a > b ? a : b;
+}
+
+template <typename T>
+T kigAbs( const T& a )
+{
+ return a >= 0 ? a : -a;
+}
+
+template <typename T>
+int kigSgn( const T& a )
+{
+ return a == 0 ? 0 : a > 0 ? +1 : -1;
+}
+
+extern const double test_threshold;
+
+#endif
diff --git a/kig/misc/conic-common.cpp b/kig/misc/conic-common.cpp
new file mode 100644
index 00000000..3dde7449
--- /dev/null
+++ b/kig/misc/conic-common.cpp
@@ -0,0 +1,888 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 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 <config.h>
+
+#include "conic-common.h"
+
+#include "common.h"
+#include "kigtransform.h"
+
+#include <cmath>
+#include <algorithm>
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+ConicCartesianData::ConicCartesianData(
+ const ConicPolarData& polardata
+ )
+{
+ double ec = polardata.ecostheta0;
+ double es = polardata.esintheta0;
+ double p = polardata.pdimen;
+ double fx = polardata.focus1.x;
+ double fy = polardata.focus1.y;
+
+ double a = 1 - ec*ec;
+ double b = 1 - es*es;
+ double c = - 2*ec*es;
+ double d = - 2*p*ec;
+ double e = - 2*p*es;
+ double f = - p*p;
+
+ f += a*fx*fx + b*fy*fy + c*fx*fy - d*fx - e*fy;
+ d -= 2*a*fx + c*fy;
+ e -= 2*b*fy + c*fx;
+
+ coeffs[0] = a;
+ coeffs[1] = b;
+ coeffs[2] = c;
+ coeffs[3] = d;
+ coeffs[4] = e;
+ coeffs[5] = f;
+}
+
+ConicPolarData::ConicPolarData( const ConicCartesianData& cartdata )
+{
+ double a = cartdata.coeffs[0];
+ double b = cartdata.coeffs[1];
+ double c = cartdata.coeffs[2];
+ double d = cartdata.coeffs[3];
+ double e = cartdata.coeffs[4];
+ double f = cartdata.coeffs[5];
+
+ // 1. Compute theta (tilt of conic)
+ double theta = std::atan2(c, b - a)/2;
+
+ // now I should possibly add pi/2...
+ double costheta = std::cos(theta);
+ double sintheta = std::sin(theta);
+ // compute new coefficients (c should now be zero)
+ double aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta;
+ double bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta;
+ if (aa*bb < 0)
+ { // we have a hyperbola we need to check the correct orientation
+ double dd = d*costheta - e*sintheta;
+ double ee = d*sintheta + e*costheta;
+ double xc = - dd / ( 2*aa );
+ double yc = - ee / ( 2*bb );
+ double ff = f + aa*xc*xc + bb*yc*yc + dd*xc + ee*yc;
+ if (ff*aa > 0) // wrong orientation
+ {
+ if (theta > 0) theta -= M_PI/2;
+ else theta += M_PI/2;
+ costheta = cos(theta);
+ sintheta = sin(theta);
+ aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta;
+ bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta;
+ }
+ }
+ else
+ {
+ if ( std::fabs (bb) < std::fabs (aa) )
+ {
+ if (theta > 0) theta -= M_PI/2;
+ else theta += M_PI/2;
+ costheta = cos(theta);
+ sintheta = sin(theta);
+ aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta;
+ bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta;
+ }
+ }
+
+ double cc = 2*(a - b)*sintheta*costheta +
+ c*(costheta*costheta - sintheta*sintheta);
+ // cc should be zero!!! cout << "cc = " << cc << "\n";
+ double dd = d*costheta - e*sintheta;
+ double ee = d*sintheta + e*costheta;
+
+ a = aa;
+ b = bb;
+ c = cc;
+ d = dd;
+ e = ee;
+
+ // now b cannot be zero (otherwise conic is degenerate)
+ a /= b;
+ c /= b;
+ d /= b;
+ e /= b;
+ f /= b;
+ b = 1.0;
+
+ // 2. compute y coordinate of focuses
+
+ double yf = - e/2;
+
+ // new values:
+ f += yf*yf + e*yf;
+ e += 2*yf; // this should be zero!
+
+ // now: a > 0 -> ellipse
+ // a = 0 -> parabula
+ // a < 0 -> hyperbola
+
+ double eccentricity = sqrt(1.0 - a);
+
+ double sqrtdiscrim = sqrt(d*d - 4*a*f);
+ if (d < 0.0) sqrtdiscrim = -sqrtdiscrim;
+ double xf = (4*a*f - 4*f - d*d)/(d + eccentricity*sqrtdiscrim) / 2;
+
+ // 3. the focus needs to be rotated back into position
+ focus1.x = xf*costheta + yf*sintheta;
+ focus1.y = -xf*sintheta + yf*costheta;
+
+ // 4. final touch: the pdimen
+ pdimen = -sqrtdiscrim/2;
+
+ ecostheta0 = eccentricity*costheta;
+ esintheta0 = -eccentricity*sintheta;
+ if ( pdimen < 0)
+ {
+ pdimen = -pdimen;
+ ecostheta0 = -ecostheta0;
+ esintheta0 = -esintheta0;
+ }
+}
+
+const ConicCartesianData calcConicThroughPoints (
+ const std::vector<Coordinate>& points,
+ const LinearConstraints c1,
+ const LinearConstraints c2,
+ const LinearConstraints c3,
+ const LinearConstraints c4,
+ const LinearConstraints c5 )
+{
+ assert( 0 < points.size() && points.size() <= 5 );
+ // points is a vector of up to 5 points through which the conic is
+ // constrained.
+ // this routine should compute the coefficients in the cartesian equation
+ // a x^2 + b y^2 + c xy + d x + e y + f = 0
+ // they are defined up to a multiplicative factor.
+ // since we don't know (in advance) which one of them is nonzero, we
+ // simply keep all 6 parameters, obtaining a 5x6 linear system which
+ // we solve using gaussian elimination with complete pivoting
+ // If there are too few, then we choose some cool way to fill in the
+ // empty parts in the matrix according to the LinearConstraints
+ // given..
+
+ // 5 rows, 6 columns..
+ double row0[6];
+ double row1[6];
+ double row2[6];
+ double row3[6];
+ double row4[6];
+ double *matrix[5] = {row0, row1, row2, row3, row4};
+ double solution[6];
+ int scambio[6];
+ LinearConstraints constraints[] = {c1, c2, c3, c4, c5};
+
+ int numpoints = points.size();
+ int numconstraints = 5;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = xi*xi;
+ matrix[i][1] = yi*yi;
+ matrix[i][2] = xi*yi;
+ matrix[i][3] = xi;
+ matrix[i][4] = yi;
+ matrix[i][5] = 1.0;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 5) break; // don't add constraints if we have enough
+ for (int j = 0; j < 6; ++j) matrix[numpoints][j] = 0.0;
+ // force the symmetry axes to be
+ // parallel to the coordinate system (zero tilt): c = 0
+ if (constraints[i] == zerotilt) matrix[numpoints][2] = 1.0;
+ // force a parabula (if zerotilt): b = 0
+ if (constraints[i] == parabolaifzt) matrix[numpoints][1] = 1.0;
+ // force a circle (if zerotilt): a = b
+ if (constraints[i] == circleifzt) {
+ matrix[numpoints][0] = 1.0;
+ matrix[numpoints][1] = -1.0; }
+ // force an equilateral hyperbola: a + b = 0
+ if (constraints[i] == equilateral) {
+ matrix[numpoints][0] = 1.0;
+ matrix[numpoints][1] = 1.0; }
+ // force symmetry about y-axis: d = 0
+ if (constraints[i] == ysymmetry) matrix[numpoints][3] = 1.0;
+ // force symmetry about x-axis: e = 0
+ if (constraints[i] == xsymmetry) matrix[numpoints][4] = 1.0;
+
+ if (constraints[i] != noconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 6, scambio ) )
+ return ConicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 6, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return ConicCartesianData( solution );
+}
+
+const ConicPolarData calcConicBFFP(
+ const std::vector<Coordinate>& args,
+ int type )
+{
+ assert( args.size() >= 2 && args.size() <= 3 );
+ assert( type == 1 || type == -1 );
+
+ ConicPolarData ret;
+
+ Coordinate f1 = args[0];
+ Coordinate f2 = args[1];
+ Coordinate d;
+ double eccentricity, d1, d2, dl;
+
+ Coordinate f2f1 = f2 - f1;
+ double f2f1l = f2f1.length();
+ ret.ecostheta0 = f2f1.x / f2f1l;
+ ret.esintheta0 = f2f1.y / f2f1l;
+
+ if ( args.size() == 3 )
+ {
+ d = args[2];
+ d1 = ( d - f1 ).length();
+ d2 = ( d - f2 ).length();
+ dl = fabs( d1 + type * d2 );
+ eccentricity = f2f1l/dl;
+ }
+ else
+ {
+ if ( type > 0 ) eccentricity = 0.7; else eccentricity = 2.0;
+ dl = f2f1l/eccentricity;
+ }
+
+ double rhomax = (dl + f2f1l) /2.0;
+
+ ret.ecostheta0 *= eccentricity;
+ ret.esintheta0 *= eccentricity;
+ ret.pdimen = type*(1 - eccentricity)*rhomax;
+ ret.focus1 = type == 1 ? f1 : f2;
+ return ret;
+}
+
+const LineData calcConicPolarLine (
+ const ConicCartesianData& data,
+ const Coordinate& cpole,
+ bool& valid )
+{
+ double x = cpole.x;
+ double y = cpole.y;
+ double a = data.coeffs[0];
+ double b = data.coeffs[1];
+ double c = data.coeffs[2];
+ double d = data.coeffs[3];
+ double e = data.coeffs[4];
+ double f = data.coeffs[5];
+
+ double alpha = 2*a*x + c*y + d;
+ double beta = c*x + 2*b*y + e;
+ double gamma = d*x + e*y + 2*f;
+
+ double normsq = alpha*alpha + beta*beta;
+
+ if (normsq < 1e-10) // line at infinity
+ {
+ valid = false;
+ return LineData();
+ }
+ valid = true;
+
+ Coordinate reta = -gamma/normsq * Coordinate (alpha, beta);
+ Coordinate retb = reta + Coordinate (-beta, alpha);
+ return LineData( reta, retb );
+}
+
+const Coordinate calcConicPolarPoint (
+ const ConicCartesianData& data,
+ const LineData& polar )
+{
+ Coordinate p1 = polar.a;
+ Coordinate p2 = polar.b;
+
+ double alpha = p2.y - p1.y;
+ double beta = p1.x - p2.x;
+ double gamma = p1.y*p2.x - p1.x*p2.y;
+
+ double a11 = data.coeffs[0];
+ double a22 = data.coeffs[1];
+ double a12 = data.coeffs[2]/2.0;
+ double a13 = data.coeffs[3]/2.0;
+ double a23 = data.coeffs[4]/2.0;
+ double a33 = data.coeffs[5];
+
+// double detA = a11*a22*a33 - a11*a23*a23 - a22*a13*a13 - a33*a12*a12 +
+// 2*a12*a23*a13;
+
+ double a11inv = a22*a33 - a23*a23;
+ double a22inv = a11*a33 - a13*a13;
+ double a33inv = a11*a22 - a12*a12;
+ double a12inv = a23*a13 - a12*a33;
+ double a23inv = a12*a13 - a23*a11;
+ double a13inv = a12*a23 - a13*a22;
+
+ double x = a11inv*alpha + a12inv*beta + a13inv*gamma;
+ double y = a12inv*alpha + a22inv*beta + a23inv*gamma;
+ double z = a13inv*alpha + a23inv*beta + a33inv*gamma;
+
+ if (fabs(z) < 1e-10) // point at infinity
+ {
+ return Coordinate::invalidCoord();
+ }
+
+ x /= z;
+ y /= z;
+ return Coordinate (x, y);
+}
+
+const Coordinate calcConicLineIntersect( const ConicCartesianData& c,
+ const LineData& l,
+ double knownparam,
+ int which )
+{
+ assert( which == 1 || which == -1 || which == 0 );
+
+ double aa = c.coeffs[0];
+ double bb = c.coeffs[1];
+ double cc = c.coeffs[2];
+ double dd = c.coeffs[3];
+ double ee = c.coeffs[4];
+ double ff = c.coeffs[5];
+
+ double x = l.a.x;
+ double y = l.a.y;
+ double dx = l.b.x - l.a.x;
+ double dy = l.b.y - l.a.y;
+
+ double aaa = aa*dx*dx + bb*dy*dy + cc*dx*dy;
+ double bbb = 2*aa*x*dx + 2*bb*y*dy + cc*x*dy + cc*y*dx + dd*dx + ee*dy;
+ double ccc = aa*x*x + bb*y*y + cc*x*y + dd*x + ee*y + ff;
+
+ double t;
+ if ( which == 0 ) /* i.e. we have a known intersection */
+ {
+ t = - bbb/aaa - knownparam;
+ return l.a + t*(l.b - l.a);
+ }
+
+ double discrim = bbb*bbb - 4*aaa*ccc;
+ if (discrim < 0.0)
+ {
+ return Coordinate::invalidCoord();
+ }
+ else
+ {
+ if ( which*bbb > 0 )
+ {
+ t = bbb + which*sqrt(discrim);
+ t = - 2*ccc/t;
+ } else {
+ t = -bbb + which*sqrt(discrim);
+ t /= 2*aaa;
+ }
+
+ return l.a + t*(l.b - l.a);
+ }
+}
+
+ConicPolarData::ConicPolarData(
+ const Coordinate& f, double d,
+ double ec, double es )
+ : focus1( f ), pdimen( d ), ecostheta0( ec ), esintheta0( es )
+{
+}
+
+ConicPolarData::ConicPolarData()
+ : focus1(), pdimen( 0 ), ecostheta0( 0 ), esintheta0( 0 )
+{
+}
+
+const ConicPolarData calcConicBDFP(
+ const LineData& directrix,
+ const Coordinate& cfocus,
+ const Coordinate& cpoint )
+{
+ ConicPolarData ret;
+
+ Coordinate ba = directrix.dir();
+ double bal = ba.length();
+ ret.ecostheta0 = -ba.y / bal;
+ ret.esintheta0 = ba.x / bal;
+
+ Coordinate pa = cpoint - directrix.a;
+
+ double distpf = (cpoint - cfocus).length();
+ double distpd = ( pa.y*ba.x - pa.x*ba.y)/bal;
+
+ double eccentricity = distpf/distpd;
+ ret.ecostheta0 *= eccentricity;
+ ret.esintheta0 *= eccentricity;
+
+ Coordinate fa = cfocus - directrix.a;
+ ret.pdimen = ( fa.y*ba.x - fa.x*ba.y )/bal;
+ ret.pdimen *= eccentricity;
+ ret.focus1 = cfocus;
+
+ return ret;
+}
+
+ConicCartesianData::ConicCartesianData( const double incoeffs[6] )
+{
+ std::copy( incoeffs, incoeffs + 6, coeffs );
+}
+
+const LineData calcConicAsymptote(
+ const ConicCartesianData data,
+ int which, bool &valid )
+{
+ assert( which == -1 || which == 1 );
+
+ LineData ret;
+ double a=data.coeffs[0];
+ double b=data.coeffs[1];
+ double c=data.coeffs[2];
+ double d=data.coeffs[3];
+ double e=data.coeffs[4];
+
+ double normabc = a*a + b*b + c*c;
+ double delta = c*c - 4*a*b;
+ if (fabs(delta) < 1e-6*normabc) { valid = false; return ret; }
+
+ double yc = (2*a*e - c*d)/delta;
+ double xc = (2*b*d - c*e)/delta;
+ // let c be nonnegative; we no longer need d, e, f.
+ if (c < 0)
+ {
+ c *= -1;
+ a *= -1;
+ b *= -1;
+ }
+
+ if ( delta < 0 )
+ {
+ valid = false;
+ return ret;
+ }
+
+ double sqrtdelta = sqrt(delta);
+ Coordinate displacement;
+ if (which > 0)
+ displacement = Coordinate(-2*b, c + sqrtdelta);
+ else
+ displacement = Coordinate(c + sqrtdelta, -2*a);
+ ret.a = Coordinate(xc, yc);
+ ret.b = ret.a + displacement;
+ return ret;
+}
+
+const ConicCartesianData calcConicByAsymptotes(
+ const LineData& line1,
+ const LineData& line2,
+ const Coordinate& p )
+{
+ Coordinate p1 = line1.a;
+ Coordinate p2 = line1.b;
+ double x = p.x;
+ double y = p.y;
+
+ double c1 = p1.x*p2.y - p2.x*p1.y;
+ double b1 = p2.x - p1.x;
+ double a1 = p1.y - p2.y;
+
+ p1 = line2.a;
+ p2 = line2.b;
+
+ double c2 = p1.x*p2.y - p2.x*p1.y;
+ double b2 = p2.x - p1.x;
+ double a2 = p1.y - p2.y;
+
+ double a = a1*a2;
+ double b = b1*b2;
+ double c = a1*b2 + a2*b1;
+ double d = a1*c2 + a2*c1;
+ double e = b1*c2 + c1*b2;
+
+ double f = a*x*x + b*y*y + c*x*y + d*x + e*y;
+ f = -f;
+
+ return ConicCartesianData( a, b, c, d, e, f );
+}
+
+const LineData calcConicRadical( const ConicCartesianData& cequation1,
+ const ConicCartesianData& cequation2,
+ int which, int zeroindex, bool& valid )
+{
+ assert( which == 1 || which == -1 );
+ assert( 0 < zeroindex && zeroindex < 4 );
+ LineData ret;
+ valid = true;
+
+ double a = cequation1.coeffs[0];
+ double b = cequation1.coeffs[1];
+ double c = cequation1.coeffs[2];
+ double d = cequation1.coeffs[3];
+ double e = cequation1.coeffs[4];
+ double f = cequation1.coeffs[5];
+
+ double a2 = cequation2.coeffs[0];
+ double b2 = cequation2.coeffs[1];
+ double c2 = cequation2.coeffs[2];
+ double d2 = cequation2.coeffs[3];
+ double e2 = cequation2.coeffs[4];
+ double f2 = cequation2.coeffs[5];
+
+// background: the family of conics c + lambda*c2 has members that
+// degenerate into a union of two lines. The values of lambda giving
+// such degenerate conics is the solution of a third degree equation.
+// The coefficients of such equation are given by:
+// (Thanks to Dominique Devriese for the suggestion of this approach)
+// domi: (And thanks to Maurizio for implementing it :)
+
+ double df = 4*a*b*f - a*e*e - b*d*d - c*c*f + c*d*e;
+ double cf = 4*a2*b*f + 4*a*b2*f + 4*a*b*f2
+ - 2*a*e*e2 - 2*b*d*d2 - 2*f*c*c2
+ - a2*e*e - b2*d*d - f2*c*c
+ + c2*d*e + c*d2*e + c*d*e2;
+ double bf = 4*a*b2*f2 + 4*a2*b*f2 + 4*a2*b2*f
+ - 2*a2*e2*e - 2*b2*d2*d - 2*f2*c2*c
+ - a*e2*e2 - b*d2*d2 - f*c2*c2
+ + c*d2*e2 + c2*d*e2 + c2*d2*e;
+ double af = 4*a2*b2*f2 - a2*e2*e2 - b2*d2*d2 - c2*c2*f2 + c2*d2*e2;
+
+// assume both conics are nondegenerate, renormalize so that af = 1
+
+ df /= af;
+ cf /= af;
+ bf /= af;
+ af = 1.0; // not needed, just for consistency
+
+// computing the coefficients of the Sturm sequence
+
+ double p1a = 2*bf*bf - 6*cf;
+ double p1b = bf*cf - 9*df;
+ double p0a = cf*p1a*p1a + p1b*(3*p1b - 2*bf*p1a);
+ double fval, fpval, lambda;
+
+ if (p0a < 0 && p1a < 0)
+ {
+ // -+-- ---+
+ valid = false;
+ return ret;
+ }
+
+ lambda = -bf/3.0; //inflection point
+ double displace = 1.0;
+ if (p1a > 0) // with two stationary points
+ {
+ displace += sqrt(p1a); // should be enough. The important
+ // thing is that it is larger than the
+ // semidistance between the stationary points
+ }
+ // compute the value at the inflection point using Horner scheme
+ fval = bf + lambda; // b + x
+ fval = cf + lambda*fval; // c + xb + xx
+ fval = df + lambda*fval; // d + xc + xxb + xxx
+
+ if (fabs(p0a) < 1e-7)
+ { // this is the case if we intersect two vertical parabulas!
+ p0a = 1e-7; // fall back to the one zero case
+ }
+ if (p0a < 0)
+ {
+ // we have three roots..
+ // we select the one we want ( defined by mzeroindex.. )
+ lambda += ( 2 - zeroindex )* displace;
+ }
+ else
+ {
+ // we have just one root
+ if( zeroindex > 1 ) // cannot find second and third root
+ {
+ valid = false;
+ return ret;
+ }
+
+ if (fval > 0) // zero on the left
+ {
+ lambda -= displace;
+ } else { // zero on the right
+ lambda += displace;
+ }
+
+ // p0a = 0 means we have a root with multiplicity two
+ }
+
+//
+// find a root of af*lambda^3 + bf*lambda^2 + cf*lambda + df = 0
+// (use a Newton method starting from lambda = 0. Hope...)
+//
+
+ double delta;
+
+ int iterations = 0;
+ const int maxiterations = 30;
+ while (iterations++ < maxiterations) // using Newton, iterations should be very few
+ {
+ // compute value of function and polinomial
+ fval = fpval = af;
+ fval = bf + lambda*fval; // b + xa
+ fpval = fval + lambda*fpval; // b + 2xa
+ fval = cf + lambda*fval; // c + xb + xxa
+ fpval = fval + lambda*fpval; // c + 2xb + 3xxa
+ fval = df + lambda*fval; // d + xc + xxb + xxxa
+
+ delta = fval/fpval;
+ lambda -= delta;
+ if (fabs(delta) < 1e-6) break;
+ }
+ if (iterations >= maxiterations) { valid = false; return ret; }
+
+ // now we have the degenerate conic: a, b, c, d, e, f
+
+ a += lambda*a2;
+ b += lambda*b2;
+ c += lambda*c2;
+ d += lambda*d2;
+ e += lambda*e2;
+ f += lambda*f2;
+
+ // domi:
+ // this is the determinant of the matrix of the new conic. It
+ // should be zero, for the new conic to be degenerate.
+ df = 4*a*b*f - a*e*e - b*d*d - c*c*f + c*d*e;
+
+ //lets work in homogeneous coordinates...
+
+ double dis1 = e*e - 4*b*f;
+ double maxval = fabs(dis1);
+ int maxind = 1;
+ double dis2 = d*d - 4*a*f;
+ if (fabs(dis2) > maxval)
+ {
+ maxval = fabs(dis2);
+ maxind = 2;
+ }
+ double dis3 = c*c - 4*a*b;
+ if (fabs(dis3) > maxval)
+ {
+ maxval = fabs(dis3);
+ maxind = 3;
+ }
+ // one of these must be nonzero (otherwise the matrix is ...)
+ // exchange elements so that the largest is the determinant of the
+ // first 2x2 minor
+ double temp;
+ switch (maxind)
+ {
+ case 1: // exchange 1 <-> 3
+ temp = a; a = f; f = temp;
+ temp = c; c = e; e = temp;
+ temp = dis1; dis1 = dis3; dis3 = temp;
+ break;
+
+ case 2: // exchange 2 <-> 3
+ temp = b; b = f; f = temp;
+ temp = c; c = d; d = temp;
+ temp = dis2; dis2 = dis3; dis3 = temp;
+ break;
+ }
+
+ // domi:
+ // this is the negative of the determinant of the top left of the
+ // matrix. If it is 0, then the conic is a parabola, if it is < 0,
+ // then the conic is an ellipse, if positive, the conic is a
+ // hyperbola. In this case, it should be positive, since we have a
+ // degenerate conic, which is a degenerate case of a hyperbola..
+ // note that it is negative if there is no valid conic to be
+ // found ( e.g. not enough intersections.. )
+ // double discrim = c*c - 4*a*b;
+
+ if (dis3 < 0)
+ {
+ // domi:
+ // i would put an assertion here, but well, i guess it doesn't
+ // really matter, and this prevents crashes if the math i still
+ // recall from high school happens to be wrong :)
+ valid = false;
+ return ret;
+ };
+
+ double r[3]; // direction of the null space
+ r[0] = 2*b*d - c*e;
+ r[1] = 2*a*e - c*d;
+ r[2] = dis3;
+
+ // now remember the switch:
+ switch (maxind)
+ {
+ case 1: // exchange 1 <-> 3
+ temp = a; a = f; f = temp;
+ temp = c; c = e; e = temp;
+ temp = dis1; dis1 = dis3; dis3 = temp;
+ temp = r[0]; r[0] = r[2]; r[2] = temp;
+ break;
+
+ case 2: // exchange 2 <-> 3
+ temp = b; b = f; f = temp;
+ temp = c; c = d; d = temp;
+ temp = dis2; dis2 = dis3; dis3 = temp;
+ temp = r[1]; r[1] = r[2]; r[2] = temp;
+ break;
+ }
+
+ // Computing a Householder reflection transformation that
+ // maps r onto [0, 0, k]
+
+ double w[3];
+ double rnormsq = r[0]*r[0] + r[1]*r[1] + r[2]*r[2];
+ double k = sqrt( rnormsq );
+ if ( k*r[2] < 0) k = -k;
+ double wnorm = sqrt( 2*rnormsq + 2*k*r[2] );
+ w[0] = r[0]/wnorm;
+ w[1] = r[1]/wnorm;
+ w[2] = (r[2] + k)/wnorm;
+
+ // matrix transformation using Householder matrix, the resulting
+ // matrix is zero on third row and column
+ // [q0,q1,q2]^t = A w
+ // alpha = w^t A w
+ double q0 = a*w[0] + c*w[1]/2 + d*w[2]/2;
+ double q1 = b*w[1] + c*w[0]/2 + e*w[2]/2;
+ double alpha = a*w[0]*w[0] + b*w[1]*w[1] + c*w[0]*w[1] +
+ d*w[0]*w[2] + e*w[1]*w[2] + f*w[2]*w[2];
+ double a00 = a - 4*w[0]*q0 + 4*w[0]*w[0]*alpha;
+ double a11 = b - 4*w[1]*q1 + 4*w[1]*w[1]*alpha;
+ double a01 = c/2 - 2*w[1]*q0 - 2*w[0]*q1 + 4*w[0]*w[1]*alpha;
+
+ double dis = a01*a01 - a00*a11;
+ assert ( dis >= 0 );
+ double sqrtdis = sqrt( dis );
+ double px, py;
+ if ( which*a01 > 0 )
+ {
+ px = a01 + which*sqrtdis;
+ py = a11;
+ } else {
+ px = a00;
+ py = a01 - which*sqrtdis;
+ }
+ double p[3]; // vector orthogonal to one of the two planes
+ double pscalw = w[0]*px + w[1]*py;
+ p[0] = px - 2*pscalw*w[0];
+ p[1] = py - 2*pscalw*w[1];
+ p[2] = - 2*pscalw*w[2];
+
+ // "r" is the solution of the equation A*(x,y,z) = (0,0,0) where
+ // A is the matrix of the degenerate conic. This is what we
+ // called in the conic theory we saw in high school a "double
+ // point". It has the unique property that any line going through
+ // it is a "polar line" of the conic at hand. It only exists for
+ // degenerate conics. It has another unique property that if you
+ // take any other point on the conic, then the line between it and
+ // the double point is part of the conic.
+ // this is what we use here: we find the double point ( ret.a
+ // ), and then find another points on the conic.
+
+ ret.a = -p[2]/(p[0]*p[0] + p[1]*p[1]) * Coordinate (p[0],p[1]);
+ ret.b = ret.a + Coordinate (-p[1],p[0]);
+ valid = true;
+
+ return ret;
+}
+
+const ConicCartesianData calcConicTransformation (
+ const ConicCartesianData& data, const Transformation& t, bool& valid )
+{
+ double a[3][3];
+ double b[3][3];
+
+ a[1][1] = data.coeffs[0];
+ a[2][2] = data.coeffs[1];
+ a[1][2] = a[2][1] = data.coeffs[2]/2;
+ a[0][1] = a[1][0] = data.coeffs[3]/2;
+ a[0][2] = a[2][0] = data.coeffs[4]/2;
+ a[0][0] = data.coeffs[5];
+
+ Transformation ti = t.inverse( valid );
+ if ( ! valid ) return ConicCartesianData();
+
+ double supnorm = 0.0;
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ b[i][j] = 0.;
+ for (int ii = 0; ii < 3; ii++)
+ {
+ for (int jj = 0; jj < 3; jj++)
+ {
+ b[i][j] += a[ii][jj]*ti.data( ii, i )*ti.data( jj, j );
+ }
+ }
+ if ( std::fabs( b[i][j] ) > supnorm ) supnorm = std::fabs( b[i][j] );
+ }
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ b[i][j] /= supnorm;
+ }
+ }
+
+ return ConicCartesianData ( b[1][1], b[2][2], b[1][2] + b[2][1],
+ b[0][1] + b[1][0], b[0][2] + b[2][0], b[0][0] );
+}
+
+ConicCartesianData::ConicCartesianData()
+{
+}
+
+bool operator==( const ConicPolarData& lhs, const ConicPolarData& rhs )
+{
+ return lhs.focus1 == rhs.focus1 &&
+ lhs.pdimen == rhs.pdimen &&
+ lhs.ecostheta0 == rhs.ecostheta0 &&
+ lhs.esintheta0 == rhs.esintheta0;
+}
+
+ConicCartesianData ConicCartesianData::invalidData()
+{
+ ConicCartesianData ret;
+ ret.coeffs[0] = double_inf;
+ return ret;
+}
+
+bool ConicCartesianData::valid() const
+{
+ return finite( coeffs[0] );
+}
+
diff --git a/kig/misc/conic-common.h b/kig/misc/conic-common.h
new file mode 100644
index 00000000..bcad5b6b
--- /dev/null
+++ b/kig/misc/conic-common.h
@@ -0,0 +1,278 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 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_MISC_CONIC_COMMON_H
+#define KIG_MISC_CONIC_COMMON_H
+
+#include "coordinate.h"
+#include <vector>
+#include "kignumerics.h"
+
+class ConicPolarData;
+class Transformation;
+class LineData;
+
+/**
+ * Cartesian Conic Data. This class represents an equation of a conic
+ * in the form "ax^2 + by^2 + cxy + dx + ey + f = 0".
+ * \internal The coefficients are stored in the order a - f.
+ */
+class ConicCartesianData
+{
+public:
+ double coeffs[6];
+ ConicCartesianData();
+ /**
+ * Construct a ConicCartesianData from a ConicPolarData.
+ * Construct a ConicCartesianData that is the cartesian
+ * representation of the conic represented by d.
+ */
+ explicit ConicCartesianData( const ConicPolarData& d );
+ /**
+ * Construct a ConicCartesianData from its coefficients
+ * Construct a ConicCartesianData using the coefficients a through f
+ * from the equation "ax^2 + by^2 + cxy + dx + ey + f = 0"
+ */
+ ConicCartesianData( double a, double b, double c,
+ double d, double e, double f )
+ {
+ coeffs[0] = a;
+ coeffs[1] = b;
+ coeffs[2] = c;
+ coeffs[3] = d;
+ coeffs[4] = e;
+ coeffs[5] = f;
+ }
+ ConicCartesianData( const double incoeffs[6] );
+
+ /**
+ * Invalid conic.
+ * Return a ConicCartesianData representing an invalid conic.
+ * \see valid()
+ */
+ static ConicCartesianData invalidData();
+ /**
+ * Test validity.
+ * Return whether this is a valid conic.
+ * \see invalidData()
+ */
+ bool valid() const;
+};
+
+/**
+ * This class represents an equation of a conic in the form
+ * \f$ \rho(\theta) = \frac{p}{1 - e \cos\theta}\f$. focus and the
+ * ecostheta stuff represent the coordinate system in which the
+ * equation yields the good result..
+ */
+class ConicPolarData
+{
+public:
+ /**
+ * Construct a ConicPolarData from a ConicCartesianData.
+ *
+ * Construct a ConicPolarData that is the polar
+ * representation of the conic represented by d.
+ */
+ explicit ConicPolarData( const ConicCartesianData& data );
+ explicit ConicPolarData();
+ /**
+ * Construct a ConicPolarData using the parameters from the equation
+ * \f$ \rho(\theta) = \frac{p}{1 - e \cos\theta}\f$
+ */
+ ConicPolarData( const Coordinate& focus1, double dimen,
+ double ecostheta0, double esintheta0 );
+
+ /**
+ * The first focus of this conic.
+ */
+ Coordinate focus1;
+ /**
+ * The pdimen value from the polar equation.
+ */
+ double pdimen;
+ /**
+ * The ecostheta0 value from the polar equation.
+ */
+ double ecostheta0;
+ /**
+ * The esintheta0 value from the polar equation.
+ */
+ double esintheta0;
+};
+
+bool operator==( const ConicPolarData& lhs, const ConicPolarData& rhs );
+
+/**
+ * These are the constraint values that can be passed to the
+ * calcConicThroughPoints function. Their meaning is as follows:
+ * noconstraint: no additional points will be calculated.
+ * zerotilt: force the symmetry axes to be parallel to the coordinate
+ * system ( zero tilt ).
+ * parabolaifzt: the returned conic should be a parabola ( if used in
+ * combination with zerotilt )
+ * circleifzt: the returned conic should be a circle ( if used in
+ * combination with zerotilt )
+ * equilateral: the returned conic should be equilateral
+ * ysymmetry: the returned conic should be symmetric over the Y-axis.
+ * xsymmetry: the returned conic should be symmetric over the X-axis.
+ */
+enum LinearConstraints {
+ noconstraint, zerotilt, parabolaifzt, circleifzt,
+ equilateral, ysymmetry, xsymmetry
+};
+
+/**
+ * Calculate a conic through a given set of points. points should
+ * contain at least one, and at most five points. If there are five
+ * points, then the conic is completely defined. If there are less,
+ * then additional points will be calculated according to the
+ * constraints given. See above for the various constraints.
+ *
+ * An invalid ConicCartesianData is returned if there is no conic
+ * through the given set of points, or if not enough constraints are
+ * given for a conic to be calculated.
+ */
+const ConicCartesianData calcConicThroughPoints (
+ const std::vector<Coordinate>& points,
+ const LinearConstraints c1 = noconstraint,
+ const LinearConstraints c2 = noconstraint,
+ const LinearConstraints c3 = noconstraint,
+ const LinearConstraints c4 = noconstraint,
+ const LinearConstraints c5 = noconstraint);
+
+/**
+ * This function is used by ConicBFFP. It calcs the polar equation
+ * for a hyperbola ( type == -1 ) or ellipse ( type == 1 ) with
+ * focuses args[0] and args[1], and with args[2] on the edge of the
+ * conic. args.size() should be two or three. If it is two, the two
+ * points are taken to be the focuses, and a third point is chosen in
+ * a sensible way..
+ */
+const ConicPolarData calcConicBFFP(
+ const std::vector<Coordinate>& args,
+ int type );
+
+/**
+ * function used by ConicBDFP. It calcs the conic with directrix d,
+ * focus f, and point p on the conic..
+ */
+const ConicPolarData calcConicBDFP(
+ const LineData& d, const Coordinate& f, const Coordinate& p );
+
+/**
+ * This calcs the hyperbola defined by its two asymptotes line1 and
+ * line2, and a point p on the edge.
+ */
+const ConicCartesianData calcConicByAsymptotes(
+ const LineData& line1,
+ const LineData& line2,
+ const Coordinate& p );
+
+/**
+ * This function calculates the polar line of the point cpole with
+ * respect to the given conic data. As the last argument, you should
+ * pass a reference to a boolean. This boolean will be set to true if
+ * the returned LineData is valid, and to false if the returned line
+ * is not valid. The latter condition only occurs if a "line at
+ * infinity" would have had to be returned.
+ */
+const LineData calcConicPolarLine (
+ const ConicCartesianData& data,
+ const Coordinate& cpole,
+ bool& valid );
+
+/**
+ * This function calculates the polar point of the line polar with
+ * respect to the given conic data. As the last argument, you should
+ * pass a reference to a boolean. This boolean will be set to true if
+ * the returned LineData is valid, and to false if the returned line
+ * is not valid. The latter condition only occurs if a "point at
+ * infinity" would have had to be returned.
+ */
+const Coordinate calcConicPolarPoint (
+ const ConicCartesianData& data,
+ const LineData& polar );
+
+/**
+ * This function calculates the intersection of a given line ( l ) and
+ * a given conic ( c ). A line and a conic have two intersections in
+ * general, and as such, which should be set to -1 or 1 depending on
+ * which intersection you want. As the last argument, you should pass
+ * a reference to a boolean. This boolean will be set to true if the
+ * returned point is valid, and to false if the returned point is not
+ * valid. The latter condition only occurs if the given conic and
+ * line do not have the specified intersection.
+ *
+ * knownparam is something special: If you already know one
+ * intersection of the line and the conic, and you want the other one,
+ * then you should set which to 0, knownparam to the curve parameter
+ * of the point you already know ( i.e. the value returned by
+ * conicimp->getParam( otherpoint ) ).
+ */
+const Coordinate calcConicLineIntersect( const ConicCartesianData& c,
+ const LineData& l,
+ double knownparam,
+ int which );
+
+/**
+ * This function calculates the asymptote of the given conic ( data ).
+ * A conic has two asymptotes in general, so which should be set to +1
+ * or -1 depending on which asymptote you want. As the last argument,
+ * you should pass a reference to a boolean. This boolean will be set
+ * to true if the returned line is valid, and to false if the returned
+ * line is not valid. The latter condition only occurs if the given
+ * conic does not have the specified asymptote.
+ */
+const LineData calcConicAsymptote(
+ const ConicCartesianData data,
+ int which, bool &valid );
+
+/**
+ * This function calculates the radical line of two conics. A radical
+ * line is the line that goes through two of the intersections of two
+ * conics. Since two conics have up to four intersections in general,
+ * there are three sets of two radical lines. zeroindex specifies
+ * which set of radical lines you want ( set it to 1, 2 or 3 ), and
+ * which is set to -1 or +1 depending on which of the two radical
+ * lines in the set you want. As the last argument, you should pass a
+ * reference to a boolean. This boolean will be set to true if the
+ * returned line is valid, and to false if the returned line is not
+ * valid. The latter condition only occurs if the given conics do not
+ * have the specified radical line.
+ */
+const LineData calcConicRadical( const ConicCartesianData& cequation1,
+ const ConicCartesianData& cequation2,
+ int which, int zeroindex, bool& valid );
+
+/**
+ * This calculates the image of the given conic ( data ) through the
+ * given transformation ( t ). As the last argument, you should pass
+ * a reference to a boolean. This boolean will be set to true if the
+ * returned line is valid, and to false if the returned line is not
+ * valid. The latter condition only occurs if the given
+ * transformation is singular, and as such, the transformation of the
+ * conic cannot be calculated.
+ */
+const ConicCartesianData calcConicTransformation (
+ const ConicCartesianData& data,
+ const Transformation& t, bool& valid );
+
+#endif // KIG_MISC_CONIC_COMMON_H
diff --git a/kig/misc/coordinate.cpp b/kig/misc/coordinate.cpp
new file mode 100644
index 00000000..13501bc9
--- /dev/null
+++ b/kig/misc/coordinate.cpp
@@ -0,0 +1,184 @@
+// 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 "coordinate.h"
+
+#include <qglobal.h>
+#include <cmath>
+#include <kdebug.h>
+
+#include "common.h"
+
+using namespace std;
+
+Coordinate Coordinate::fromQPoint( const QPoint& p )
+{
+ return Coordinate( p.x(), p.y() );
+}
+
+kdbgstream& operator<<( kdbgstream& s, const Coordinate& t )
+{
+ s << "x: " << t.x << " y: " << t.y << endl;
+ return s;
+}
+
+const Coordinate operator+ ( const Coordinate& a, const Coordinate& b )
+{
+ return Coordinate ( a.x + b.x, a.y + b.y );
+}
+
+const Coordinate operator- ( const Coordinate& a, const Coordinate& b )
+{
+ return Coordinate ( a.x - b.x, a.y - b.y );
+}
+
+const Coordinate operator* ( const Coordinate& a, double r )
+{
+ return Coordinate ( r*a.x, r*a.y );
+}
+
+const Coordinate operator* ( double r, const Coordinate& a )
+{
+ return Coordinate ( r*a.x, r*a.y );
+}
+
+const Coordinate operator/ ( const Coordinate& a, double r )
+{
+ return Coordinate ( a.x/r, a.y/r );
+}
+
+bool operator==( const Coordinate& a, const Coordinate& b )
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+bool operator!=( const Coordinate& a, const Coordinate& b )
+{
+ return !operator==( a, b );
+}
+
+Coordinate::Coordinate()
+ : x(0),
+ y(0)
+{
+}
+
+Coordinate::Coordinate( double nx, double ny )
+ : x( nx ),
+ y( ny )
+{
+}
+
+Coordinate::Coordinate( const Coordinate& p )
+ : x( p.x ),
+ y( p.y )
+{
+}
+
+const Coordinate Coordinate::operator-() const
+{
+ return Coordinate ( -x, -y );
+}
+
+Coordinate& Coordinate::operator=( const Coordinate& p )
+{
+ x = p.x;
+ y = p.y;
+ return *this;
+}
+
+Coordinate& Coordinate::operator+=( const Coordinate& p )
+{
+ x += p.x;
+ y += p.y;
+ return *this;
+}
+
+Coordinate& Coordinate::operator-=( const Coordinate& p )
+{
+ x -= p.x;
+ y -= p.y;
+ return *this;
+}
+
+Coordinate& Coordinate::operator*=( double r )
+{
+ x *= r;
+ y *= r;
+ return *this;
+}
+
+Coordinate& Coordinate::operator*=( int r )
+{
+ x *= r;
+ y *= r;
+ return *this;
+}
+
+Coordinate& Coordinate::operator/=( double r )
+{
+ x /= r;
+ y /= r;
+ return *this;
+}
+
+double Coordinate::distance( const Coordinate& p ) const
+{
+ return (p - *this).length();
+}
+
+double Coordinate::length() const
+{
+ return sqrt(x*x+y*y);
+}
+
+const Coordinate Coordinate::orthogonal() const
+{
+ return Coordinate( -y, x );
+}
+
+const Coordinate Coordinate::normalize( double l ) const
+{
+ double oldlength = length();
+ return ( *this * l ) / oldlength;
+}
+
+const Coordinate Coordinate::round() const
+{
+ return Coordinate( qRound( x ), qRound( y ) );
+}
+
+QPoint Coordinate::toQPoint() const
+{
+ Coordinate t = round();
+ return QPoint( (int) t.x, (int) t.y );
+}
+
+Coordinate Coordinate::invalidCoord()
+{
+ return Coordinate( double_inf, double_inf );
+}
+
+bool Coordinate::valid() const
+{
+ return abs( x ) != double_inf && abs( y ) != double_inf;
+}
+
+double operator*( const Coordinate& a, const Coordinate& b )
+{
+ return a.x * b.x + a.y * b.y;
+}
diff --git a/kig/misc/coordinate.h b/kig/misc/coordinate.h
new file mode 100644
index 00000000..a56edb76
--- /dev/null
+++ b/kig/misc/coordinate.h
@@ -0,0 +1,169 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ 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_MISC_COORDINATE_H
+#define KIG_MISC_COORDINATE_H
+
+class QPoint;
+class kdbgstream;
+
+/**
+ * The Coordinate class is the basic class representing a 2D location
+ * by its x and y components. It has all relevant arithmetic
+ * operators properly defined, and should be straightforward to use..
+ */
+class Coordinate
+{
+public:
+ static Coordinate fromQPoint( const QPoint& p );
+
+ /** Constructor. Construct a new Coordinate, with a given x and y
+ * value.
+ */
+ Coordinate( double x, double y );
+ /** Copy Constructor. Construct a new Coordinate, and give it the
+ * same value as p.
+ */
+ Coordinate( const Coordinate& p );
+ /**
+ * \ifnot creating-python-scripting-doc
+ * \brief Default Constructor
+ *
+ * Constructs a new Coordinate, with x and y initialized to 0.
+ * \endif
+ */
+ Coordinate();
+ ~Coordinate() {}
+
+ /** Create an invalid Coordinate. This is a special value of a
+ * Coordinate that signals that something went wrong..
+ *
+ * \see Coordinate::valid
+ *
+ * \internal We represent an invalid coordinate by setting x or y to
+ * positive or negative infinity. This is handy, since it doesn't
+ * require us to adapt most of the functions, it doesn't need extra
+ * space, and most of the times that we should get an invalid coord,
+ * we get one automatically..
+ */
+ static Coordinate invalidCoord();
+ /** Return whether this is a valid Coordinate.
+ * \see Coordinate::invalidCoord
+ */
+ bool valid() const;
+
+ /** Distance to another Coordinate.
+ */
+ double distance ( const Coordinate& p ) const;
+ /** Length. Returns the length or norm of this coordinate.
+ * I.e. return the distance from this Coordinate to the origin.
+ * \see squareLength
+ */
+ double length () const;
+ /** Square length. Equivalent to the square of \ref length, but a
+ * bit more efficient because no square root has to be calculated.
+ * \see length
+ */
+ inline double squareLength() const;
+ /** Inverse. Returns the inverse of this Coordinate.
+ */
+ const Coordinate operator- () const;
+ /** Orthogonal. Returns a vector which is orthogonal on this vector.
+ * This relation always holds:
+ * <pre>
+ * Coordinate a = ...;
+ * assert( a*a.orthogonal() ) == 0;
+ * </pre>
+ */
+ const Coordinate orthogonal() const;
+ /** Round. Returns this coordinate, rounded to the nearest integral
+ * values.
+ */
+ const Coordinate round() const;
+ /** Normalize. This sets the length to length, while keeping the
+ * x/y ratio untouched...
+ */
+ const Coordinate normalize( double length = 1 ) const;
+ QPoint toQPoint() const;
+
+ Coordinate& operator= ( const Coordinate& c );
+ /** Add. Add c to this Coordinate
+ */
+ Coordinate& operator+= ( const Coordinate& c );
+ /** Subtract. Subtract c from this Coordinate
+ */
+ Coordinate& operator-= ( const Coordinate& c );
+ /** Scale. Scales this Coordinate by a factor r
+ */
+ Coordinate& operator*= ( double r );
+ /** Scale. Scales this Coordinate by a factor r
+ */
+ Coordinate& operator*= ( int r );
+ /** Scale. Scales this Coordinate by a factor 1/r
+ */
+ Coordinate& operator/= ( double r );
+public:
+ /** X Component. The X Component of this Coordinate.
+ */
+ double x;
+ /** Y Component. The Y Component of this Coordinate.
+ */
+ double y;
+
+ friend kdbgstream& operator<<( kdbgstream& s, const Coordinate& t );
+ /** Add. Returns the sum of a and b.
+ */
+ friend const Coordinate operator+ ( const Coordinate& a, const Coordinate& b );
+ /** Subtract. Returns the difference between a and b.
+ */
+ friend const Coordinate operator- ( const Coordinate& a, const Coordinate& b );
+ /** Scale. Returns this a, scaled by a factor of r.
+ */
+ friend const Coordinate operator* ( const Coordinate& a, double r );
+ /** Scale. Returns a, scaled by a factor of 1/r.
+ */
+ friend const Coordinate operator/ ( const Coordinate& a, double r );
+ /** Scalar Product. Returns the scalar product of a and b.
+ */
+ friend double operator*( const Coordinate& a, const Coordinate& b );
+ /** Equal. Tests two Coordinates for equality.
+ */
+ friend bool operator==( const Coordinate&, const Coordinate& );
+ /** Not Equal. Tests two Coordinates for inequality.
+ */
+ friend bool operator!=( const Coordinate&, const Coordinate& );
+};
+
+const Coordinate operator/ ( const Coordinate& a, double r );
+kdbgstream& operator<<( kdbgstream& s, const Coordinate& t );
+const Coordinate operator+ ( const Coordinate& a, const Coordinate& b );
+const Coordinate operator- ( const Coordinate& a, const Coordinate& b );
+const Coordinate operator* ( const Coordinate& a, double r );
+const Coordinate operator* ( double r, const Coordinate& a );
+double operator*( const Coordinate& a, const Coordinate& b );
+
+double Coordinate::squareLength() const
+{
+ return x*x+y*y;
+}
+
+#endif
+
diff --git a/kig/misc/coordinate_system.cpp b/kig/misc/coordinate_system.cpp
new file mode 100644
index 00000000..dd5181c2
--- /dev/null
+++ b/kig/misc/coordinate_system.cpp
@@ -0,0 +1,720 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ 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 "coordinate_system.h"
+
+#include "../kig/kig_document.h"
+#include "../kig/kig_view.h"
+
+#include "common.h"
+#include "coordinate.h"
+#include "goniometry.h"
+#include "kigpainter.h"
+
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <knumvalidator.h>
+
+#include <string>
+#include <math.h>
+
+class CoordinateValidator
+ : public QValidator
+{
+ bool mpolar;
+#ifdef KIG_USE_KDOUBLEVALIDATOR
+ KDoubleValidator mdv;
+#else
+ KFloatValidator mdv;
+#endif
+ mutable QRegExp mre;
+public:
+ CoordinateValidator( bool polar );
+ ~CoordinateValidator();
+ State validate ( QString & input, int & pos ) const;
+ void fixup ( QString & input ) const;
+};
+
+
+CoordinateValidator::CoordinateValidator( bool polar )
+ : QValidator( 0, 0 ), mpolar( polar ), mdv( 0, 0 ),
+ mre( polar ? "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?°? ?\\)?"
+ : "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?\\)?" )
+{
+}
+
+CoordinateValidator::~CoordinateValidator()
+{
+}
+
+QValidator::State CoordinateValidator::validate( QString & input, int & pos ) const
+{
+ QString tinput = input;
+ if ( tinput[tinput.length() - 1 ] == ')' ) tinput.truncate( tinput.length() - 1 );
+ if ( mpolar )
+ {
+ if ( tinput[tinput.length() - 1 ] == ' ' ) tinput.truncate( tinput.length() - 1 );
+ if ( tinput[tinput.length() - 1 ] == '°' ) tinput.truncate( tinput.length() - 1 );
+ };
+ if( tinput[tinput.length() - 1 ] == ' ' ) tinput.truncate( tinput.length() - 1 );
+ if ( tinput[0] == '(' ) tinput = tinput.mid( 1 );
+ if( tinput[0] == ' ' ) tinput = tinput.mid( 1 );
+ int scp = tinput.find( ';' );
+ if ( scp == -1 ) return mdv.validate( tinput, pos ) == Invalid ? Invalid : Valid;
+ else
+ {
+ QString p1 = tinput.left( scp );
+ QString p2 = tinput.mid( scp + 1 );
+
+ State ret = Acceptable;
+
+ int boguspos = 0;
+ ret = kigMin( ret, mdv.validate( p1, boguspos ) );
+
+ boguspos = 0;
+ ret = kigMin( ret, mdv.validate( p2, boguspos ) );
+
+ return ret;
+ };
+}
+
+void CoordinateValidator::fixup( QString & input ) const
+{
+ int nsc = input.contains( ';' );
+ if ( nsc > 1 )
+ {
+ // where is the second ';'
+ int i = input.find( ';' );
+ i = input.find( ';', i );
+ input = input.left( i );
+ };
+ // now the string has at most one semicolon left..
+ int sc = input.find( ';' );
+ if ( sc == -1 )
+ {
+ sc = input.length();
+ KLocale* l = KGlobal::locale();
+ if ( mpolar )
+ input.append( QString::fromLatin1( ";" ) + l->positiveSign() +
+ QString::fromLatin1( "0°" ) );
+ else
+ input.append( QString::fromLatin1( ";" ) + l->positiveSign() +
+ QString::fromLatin1( "0" ) + l->decimalSymbol() +
+ QString::fromLatin1( "0" ) );
+ };
+ mre.exactMatch( input );
+ QString ds1 = mre.cap( 1 );
+ mdv.fixup( ds1 );
+ QString ds2 = mre.cap( 2 );
+ mdv.fixup( ds2 );
+ input = ds1 + QString::fromLatin1( "; " ) + ds2;
+}
+
+EuclideanCoords::EuclideanCoords()
+{
+}
+
+QString EuclideanCoords::fromScreen( const Coordinate& p, const KigDocument& d ) const
+{
+ // i used to use the widget size here, but that's no good idea,
+ // since an object isn't asked to recalc every time the widget size
+ // changes.. might be a good idea to do that, but well, maybe some
+ // other time :)
+ Rect sr = d.suggestedRect();
+ double m = kigMax( sr.width(), sr.height() );
+ int l = kigMax( 0, (int) ( 3 - log10( m ) ) );
+ QString xs = KGlobal::locale()->formatNumber( p.x, l );
+ QString ys = KGlobal::locale()->formatNumber( p.y, l );
+ return QString::fromLatin1( "( %1; %2 )" ).arg( xs ).arg( ys );
+}
+
+Coordinate EuclideanCoords::toScreen(const QString& s, bool& ok) const
+{
+ QRegExp r( "\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?\\)?" );
+ ok = ( r.search(s) == 0 );
+ if (ok)
+ {
+ QString xs = r.cap(1);
+ QString ys = r.cap(2);
+ KLocale* l = KGlobal::locale();
+ double x = l->readNumber( xs, &ok );
+ if ( ! ok ) x = xs.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ double y = l->readNumber( ys, &ok );
+ if ( ! ok ) y = ys.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ return Coordinate( x, y );
+ }
+ return Coordinate();
+}
+
+/**
+ * copied and adapted from a ( public domain ) function i found in the
+ * first Graphics Gems book. Credits to Paul S. Heckbert, who wrote
+ * the "Nice number for graph labels" gem.
+ * find a "nice" number approximately equal to x. We look for
+ * 1, 2 or 5, multiplied by a power of 10.
+ */
+static double nicenum( double x, bool round )
+{
+ int exp = (int) log10( x );
+ double f = x/pow( 10., exp );
+ double nf;
+ if ( round )
+ {
+ if ( f < 1.5 ) nf = 1.;
+ else if ( f < 3. ) nf = 2.;
+ else if ( f < 7. ) nf = 5.;
+ else nf = 10.;
+ }
+ else
+ {
+ if ( f <= 1. ) nf = 1.;
+ else if ( f <= 2. ) nf = 2.;
+ else if ( f <= 5. ) nf = 5.;
+ else nf = 10.;
+ };
+ return nf * pow( 10., exp );
+}
+
+void EuclideanCoords::drawGrid( KigPainter& p, bool showgrid, bool showaxes ) const
+{
+ p.setWholeWinOverlay();
+
+ // this instruction in not necessary, but there is a little
+ // optimization when there are no grid and no axes.
+ if ( !( showgrid || showaxes ) )
+ return;
+
+ // this function is inspired upon ( public domain ) code from the
+ // first Graphics Gems book. Credits to Paul S. Heckbert, who wrote
+ // the "Nice number for graph labels" gem.
+
+ const double hmax = ceil( p.window().right() );
+ const double hmin = floor( p.window().left() );
+ const double vmax = ceil( p.window().top() );
+ const double vmin = floor( p.window().bottom() );
+
+ // the number of intervals we would like to have:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / p.pixelWidth() / 40. ) + 1;
+
+ double hrange = nicenum( hmax - hmin, false );
+ double vrange = nicenum( vmax - vmin, false );
+ const double newrange = kigMin( hrange, vrange );
+ hrange = newrange;
+ vrange = newrange;
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ const double hgraphmin = ceil( hmin / hd) * hd;
+ const double hgraphmax = floor( hmax / hd ) * hd;
+ const double vgraphmin = ceil( vmin / vd ) * vd;
+ const double vgraphmax = floor( vmax / vd ) * vd;
+
+ const int hnfrac = kigMax( (int) - floor( log10( hd ) ), 0 );
+ const int vnfrac = kigMax( (int) - floor( log10( vd ) ), 0 );
+
+ /****** the grid lines ******/
+ if ( showgrid )
+ {
+ p.setPen( QPen( lightGray, 0, DotLine ) );
+ // vertical lines...
+ for ( double i = hgraphmin; i <= hgraphmax + hd/2; i += hd )
+ p.drawSegment( Coordinate( i, vgraphmin ),
+ Coordinate( i, vgraphmax ) );
+ // horizontal lines...
+ for ( double i = vgraphmin; i <= vgraphmax + vd/2; i += vd )
+ p.drawSegment( Coordinate( hgraphmin, i ),
+ Coordinate( hgraphmax, i ) );
+ }
+
+ /****** the axes ******/
+ if ( showaxes )
+ {
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ // x axis
+ p.drawSegment( Coordinate( hmin, 0 ), Coordinate( hmax, 0 ) );
+ // y axis
+ p.drawSegment( Coordinate( 0, vmin ), Coordinate( 0, vmax ) );
+
+ /****** the numbers ******/
+
+ // x axis
+ for( double i = hgraphmin; i <= hgraphmax + hd / 2; i += hd )
+ {
+ // we skip 0 since that would look stupid... (the axes going
+ // through the 0 etc. )
+ if( fabs( i ) < 1e-8 ) continue;
+
+ p.drawText(
+ Rect( Coordinate( i, 0 ), hd, -2*vd ).normalized(),
+ KGlobal::locale()->formatNumber( i, hnfrac ),
+ AlignLeft | AlignTop
+ );
+ };
+ // y axis...
+ for ( double i = vgraphmin; i <= vgraphmax + vd/2; i += vd )
+ {
+ if( fabs( i ) < 1e-8 ) continue;
+ p.drawText ( Rect( Coordinate( 0, i ), 2*hd, vd ).normalized(),
+ KGlobal::locale()->formatNumber( i, vnfrac ),
+ AlignBottom | AlignLeft
+ );
+ };
+ // arrows on the ends of the axes...
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ p.setBrush( QBrush( Qt::gray ) );
+ std::vector<Coordinate> a;
+
+ // the arrow on the right end of the X axis...
+ a.reserve( 3 );
+ double u = p.pixelWidth();
+ a.push_back( Coordinate( hmax - 6 * u, -3 * u) );
+ a.push_back( Coordinate( hmax, 0 ) );
+ a.push_back( Coordinate( hmax - 6 * u, 3 * u ) );
+ p.drawArea( a );
+// p.drawPolygon( a, true );
+
+ // the arrow on the top end of the Y axis...
+ a.clear();
+ a.reserve( 3 );
+ a.push_back( Coordinate( 3 * u, vmax - 6 * u ) );
+ a.push_back( Coordinate( 0, vmax ) );
+ a.push_back( Coordinate( -3 * u, vmax - 6 * u ) );
+ p.drawArea( a );
+// p.drawPolygon( a, true );
+ }; // if( showaxes )
+}
+
+QString EuclideanCoords::coordinateFormatNotice() const
+{
+ return i18n( "Enter coordinates in the following format: \"x;y\",\n"
+ "where x is the x coordinate, and y is the y coordinate." );
+}
+
+QString EuclideanCoords::coordinateFormatNoticeMarkup() const
+{
+ return i18n( "Enter coordinates in the following format: <b>\"x;y\"</b>, "
+ "where x is the x coordinate, and y is the y coordinate." );
+}
+
+EuclideanCoords::~EuclideanCoords()
+{
+}
+
+CoordinateSystem::~CoordinateSystem()
+{
+}
+
+CoordinateSystem::CoordinateSystem()
+{
+}
+
+PolarCoords::PolarCoords()
+{
+}
+
+PolarCoords::~PolarCoords()
+{
+}
+
+QString PolarCoords::fromScreen( const Coordinate& pt, const KigDocument& d ) const
+{
+ Rect sr = d.suggestedRect();
+ double m = kigMax( sr.width(), sr.height() );
+ int l = kigMax( 0, (int) ( 3 - log10( m ) ) );
+
+ double r = pt.length();
+ double theta = Goniometry::convert( atan2( pt.y, pt.x ), Goniometry::Rad, Goniometry::Deg );
+
+ QString rs = KGlobal::locale()->formatNumber( r, l );
+ QString ts = KGlobal::locale()->formatNumber( theta, 0 );
+
+ return QString::fromLatin1("( %1; %2° )").arg( rs ).arg( ts );
+}
+
+QString PolarCoords::coordinateFormatNotice() const
+{
+ // \xCE\xB8 is utf8 for the greek theta sign..
+ return i18n( "Enter coordinates in the following format: \"r; \xCE\xB8°\",\n"
+ "where r and \xCE\xB8 are the polar coordinates." );
+}
+
+QString PolarCoords::coordinateFormatNoticeMarkup() const
+{
+ // \xCE\xB8 is utf8 for the greek theta sign..
+ return i18n( "Enter coordinates in the following format: <b>\"r; \xCE\xB8°\"</b>, "
+ "where r and \xCE\xB8 are the polar coordinates." );
+}
+
+Coordinate PolarCoords::toScreen(const QString& s, bool& ok) const
+{
+ QRegExp regexp("\\(? ?([0-9.,+-]+); ?([0-9.,+-]+) ?°? ?\\)?" );
+ ok = ( regexp.search( s ) == 0 );
+ if (ok)
+ {
+ QString rs = regexp.cap( 1 );
+ double r = KGlobal::locale()->readNumber( rs, &ok );
+ if ( ! ok ) r = rs.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ QString ts = regexp.cap( 2 );
+ double theta = KGlobal::locale()->readNumber( ts, &ok );
+ if ( ! ok ) theta = ts.toDouble( &ok );
+ if ( ! ok ) return Coordinate();
+ theta *= M_PI;
+ theta /= 180;
+ return Coordinate( cos( theta ) * r, sin( theta ) * r );
+ }
+ else return Coordinate();
+}
+
+void PolarCoords::drawGrid( KigPainter& p, bool showgrid, bool showaxes ) const
+{
+ p.setWholeWinOverlay();
+
+ // this instruction in not necessary, but there is a little
+ // optimization when there are no grid and no axes.
+ if ( !( showgrid || showaxes ) )
+ return;
+
+ // we multiply by sqrt( 2 ) cause we don't want to miss circles in
+ // the corners, that intersect with the axes outside of the
+ // screen..
+
+ const double hmax = M_SQRT2*p.window().right();
+ const double hmin = M_SQRT2*p.window().left();
+ const double vmax = M_SQRT2*p.window().top();
+ const double vmin = M_SQRT2*p.window().bottom();
+
+ // the intervals:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / p.pixelWidth() / 40 ) + 1;
+
+ const double hrange = nicenum( hmax - hmin, false );
+ const double vrange = nicenum( vmax - vmin, false );
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ const double hgraphmin = floor( hmin / hd) * hd;
+ const double hgraphmax = ceil( hmax / hd ) * hd;
+ const double vgraphmin = floor( vmin / vd ) * vd;
+ const double vgraphmax = ceil( vmax / vd ) * vd;
+
+ const int hnfrac = kigMax( (int) - floor( log10( hd ) ), 0 );
+ const int vnfrac = kigMax( (int) - floor( log10( vd ) ), 0 );
+ const int nfrac = kigMax( hnfrac, vnfrac );
+
+ /****** the grid lines ******/
+ if ( showgrid )
+ {
+ double d = kigMin( hd, vd );
+ double begin = kigMin( kigAbs( hgraphmin ), kigAbs( vgraphmin ) );
+ if ( kigSgn( hgraphmin ) != kigSgn( hgraphmax ) && kigSgn( vgraphmin ) != kigSgn( vgraphmax ) )
+ begin = d;
+ double end = kigMax( hgraphmax, vgraphmax );
+
+ // we also want the circles that don't fit entirely in the
+ // screen..
+ Coordinate c( 0, 0 );
+ p.setPen( QPen( lightGray, 0, DotLine ) );
+ for ( double i = begin; i <= end + d / 2; i += d )
+ drawGridLine( p, c, fabs( i ) );
+ }
+
+ /****** the axes ******/
+ if ( showaxes )
+ {
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ // x axis
+ p.drawSegment( Coordinate( hmin, 0 ), Coordinate( hmax, 0 ) );
+ // y axis
+ p.drawSegment( Coordinate( 0, vmin ), Coordinate( 0, vmax ) );
+
+ /****** the numbers ******/
+
+ // x axis
+ for( double i = hgraphmin; i <= hgraphmax + hd / 2; i += hd )
+ {
+ // we skip 0 since that would look stupid... (the axes going
+ // through the 0 etc. )
+ if( fabs( i ) < 1e-8 ) continue;
+
+ QString is = KGlobal::locale()->formatNumber( fabs( i ), nfrac );
+ p.drawText(
+ Rect( Coordinate( i, 0 ), hd, -2*vd ).normalized(),
+ is, AlignLeft | AlignTop );
+ };
+ // y axis...
+ for ( double i = vgraphmin; i <= vgraphmax + vd / 2; i += vd )
+ {
+ if( fabs( i ) < 1e-8 ) continue;
+
+ QString is = KGlobal::locale()->formatNumber( fabs( i ), nfrac );
+
+ p.drawText ( Rect( Coordinate( 0, i ), hd, vd ).normalized(),
+ is, AlignBottom | AlignLeft
+ );
+ };
+ // arrows on the ends of the axes...
+ p.setPen( QPen( Qt::gray, 1, Qt::SolidLine ) );
+ p.setBrush( QBrush( Qt::gray ) );
+ std::vector<Coordinate> a;
+
+ // the arrow on the right end of the X axis...
+ a.reserve( 3 );
+ double u = p.pixelWidth();
+ a.push_back( Coordinate( hmax - 6 * u, -3 * u) );
+ a.push_back( Coordinate( hmax, 0 ) );
+ a.push_back( Coordinate( hmax - 6 * u, 3 * u ) );
+// p.drawPolygon( a, true );
+ p.drawArea( a );
+
+ // the arrow on the top end of the Y axis...
+ a.clear();
+ a.reserve( 3 );
+ a.push_back( Coordinate( 3 * u, vmax - 6 * u ) );
+ a.push_back( Coordinate( 0, vmax ) );
+ a.push_back( Coordinate( -3 * u, vmax - 6 * u ) );
+// p.drawPolygon( a, true );
+ p.drawArea( a );
+ }; // if( showaxes )
+}
+
+QValidator* EuclideanCoords::coordinateValidator() const
+{
+ return new CoordinateValidator( false );
+}
+
+QValidator* PolarCoords::coordinateValidator() const
+{
+ return new CoordinateValidator( true );
+}
+
+QStringList CoordinateSystemFactory::names()
+{
+ QStringList ret;
+ ret << i18n( "&Euclidean" )
+ << i18n( "&Polar" );
+ return ret;
+}
+
+CoordinateSystem* CoordinateSystemFactory::build( int which )
+{
+ if ( which == Euclidean )
+ return new EuclideanCoords;
+ else if ( which == Polar )
+ return new PolarCoords;
+ else return 0;
+}
+
+static const char euclideanTypeString[] = "Euclidean";
+static const char polarTypeString[] = "Polar";
+
+CoordinateSystem* CoordinateSystemFactory::build( const char* type )
+{
+ if ( std::string( euclideanTypeString ) == type )
+ return new EuclideanCoords;
+ if ( std::string( polarTypeString ) == type )
+ return new PolarCoords;
+ else return 0;
+}
+
+const char* EuclideanCoords::type() const
+{
+ return euclideanTypeString;
+}
+
+const char* PolarCoords::type() const
+{
+ return polarTypeString;
+}
+
+int EuclideanCoords::id() const
+{
+ return CoordinateSystemFactory::Euclidean;
+}
+
+int PolarCoords::id() const
+{
+ return CoordinateSystemFactory::Polar;
+}
+
+QString CoordinateSystemFactory::setCoordinateSystemStatement( int id )
+{
+ switch( id )
+ {
+ case Euclidean:
+ return i18n( "Set Euclidean Coordinate System" );
+ case Polar:
+ return i18n( "Set Polar Coordinate System" );
+ default:
+ assert( false );
+ return QString::null;
+ }
+}
+
+Coordinate EuclideanCoords::snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const
+{
+ Rect rect = w.showingRect();
+ // we recalc the interval stuff since there is no way to cache it..
+
+ // this function is again inspired upon ( public domain ) code from
+ // the first Graphics Gems book. Credits to Paul S. Heckbert, who
+ // wrote the "Nice number for graph labels" gem.
+
+ const double hmax = rect.right();
+ const double hmin = rect.left();
+ const double vmax = rect.top();
+ const double vmin = rect.bottom();
+
+ // the number of intervals we would like to have:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / w.pixelWidth() / 40. ) + 1;
+
+ const double hrange = nicenum( hmax - hmin, false );
+ const double vrange = nicenum( vmax - vmin, false );
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ const double hgraphmin = ceil( hmin / hd) * hd;
+ const double vgraphmin = ceil( vmin / vd ) * vd;
+
+ const double nx = qRound( ( c.x - hgraphmin ) / hd ) * hd + hgraphmin;
+ const double ny = qRound( ( c.y - vgraphmin ) / vd ) * vd + vgraphmin;
+ return Coordinate( nx, ny );
+}
+
+Coordinate PolarCoords::snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const
+{
+ // we reuse the drawGrid code to find
+
+ // we multiply by sqrt( 2 ) cause we don't want to miss circles in
+ // the corners, that intersect with the axes outside of the
+ // screen..
+
+ Rect r = w.showingRect();
+
+ const double hmax = M_SQRT2 * r.right();
+ const double hmin = M_SQRT2 * r.left();
+ const double vmax = M_SQRT2 * r.top();
+ const double vmin = M_SQRT2 * r.bottom();
+
+ // the intervals:
+ // we try to have one of them per 40 pixels or so..
+ const int ntick = static_cast<int>(
+ kigMax( hmax - hmin, vmax - vmin ) / w.pixelWidth() / 40 ) + 1;
+
+ const double hrange = nicenum( hmax - hmin, false );
+ const double vrange = nicenum( vmax - vmin, false );
+
+ const double hd = nicenum( hrange / ( ntick - 1 ), true );
+ const double vd = nicenum( vrange / ( ntick - 1 ), true );
+
+ double d = kigMin( hd, vd );
+
+ double dist = c.length();
+ double ndist = qRound( dist / d ) * d;
+ return c.normalize( ndist );
+}
+
+void PolarCoords::drawGridLine( KigPainter& p, const Coordinate& c,
+ double r ) const
+{
+ Rect rect = p.window();
+
+ struct iterdata_t
+ {
+ int xd;
+ int yd;
+ Coordinate ( Rect::*point )() const;
+ Coordinate ( Rect::*oppositepoint )() const;
+ double horizAngle;
+ double vertAngle;
+ };
+
+ static const iterdata_t iterdata[] =
+ {
+ { +1, +1, &Rect::topRight, &Rect::bottomLeft, 0, M_PI/2 },
+ { -1, +1, &Rect::topLeft, &Rect::bottomRight, M_PI, M_PI / 2 },
+ { -1, -1, &Rect::bottomLeft, &Rect::topRight, M_PI, 3*M_PI/2 },
+ { +1, -1, &Rect::bottomRight, &Rect::topLeft, 2*M_PI, 3*M_PI/2 }
+ };
+ for ( int i = 0; i < 4; ++i )
+ {
+ int xd = iterdata[i].xd;
+ int yd = iterdata[i].yd;
+ Coordinate point = ( rect.*iterdata[i].point )();
+ Coordinate opppoint = ( rect.*iterdata[i].oppositepoint )();
+ double horizangle = iterdata[i].horizAngle;
+ double vertangle = iterdata[i].vertAngle;
+
+ if ( ( c.x - point.x )*xd > 0 || ( c.y - point.y )*yd > 0 )
+ continue;
+ if ( ( c.x - opppoint.x )*-xd > r || ( c.y - opppoint.y )*-yd > r )
+ continue;
+
+ int posdir = xd*yd;
+ double hd = ( point.x - c.x )*xd;
+ assert( hd >= 0 );
+ if ( hd < r )
+ {
+ double anglediff = acos( hd/r );
+ horizangle += posdir * anglediff;
+ }
+
+ hd = ( c.x - opppoint.x )*-xd;
+ if ( hd >= 0 )
+ {
+ double anglediff = asin( hd/r );
+ vertangle -= posdir * anglediff;
+ }
+
+ double vd = ( point.y - c.y )*yd;
+ assert( vd >= 0 );
+ if ( vd < r )
+ {
+ double anglediff = acos( vd/r );
+ vertangle -= posdir * anglediff;
+ }
+
+ vd = ( c.y - opppoint.y ) * -xd;
+ if ( vd >= 0 )
+ {
+ double anglediff = asin( hd/r );
+ horizangle += posdir * anglediff;
+ }
+
+ p.drawArc( c, r, kigMin( horizangle, vertangle ), kigMax( horizangle, vertangle ) );
+ }
+// p.drawCircle( c, r );
+}
diff --git a/kig/misc/coordinate_system.h b/kig/misc/coordinate_system.h
new file mode 100644
index 00000000..af426909
--- /dev/null
+++ b/kig/misc/coordinate_system.h
@@ -0,0 +1,134 @@
+/*
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ 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_MISC_COORDINATE_SYSTEM_H
+#define KIG_MISC_COORDINATE_SYSTEM_H
+
+#include <qnamespace.h>
+
+class KigPainter;
+class KigDocument;
+class KigWidget;
+class CoordinateSystem;
+class QValidator;
+class Coordinate;
+class QString;
+class QStringList;
+class QWidget;
+
+/**
+ * a factory to build a CoordinateSystem and a small handle to the
+ * existant CoordinateSystem's...
+ */
+class CoordinateSystemFactory
+{
+public:
+ enum { Euclidean = 0, Polar = 1 };
+
+ static QStringList names();
+ static QString setCoordinateSystemStatement( int id );
+ static CoordinateSystem* build( int which );
+ static CoordinateSystem* build( const char* type );
+};
+
+/**
+ * a CoordinateSystem is what the user sees: it is kept by KigPart to
+ * show the user a grid, and to show the coordinates of points... it
+ * allows for weird CoordinateSystem's like homogeneous or
+ * projective...
+ * internally, it does nothing, it could almost have been an ordinary
+ * Object..., mapping coordinates from and to the screen to and from
+ * the internal coordinates is done elsewhere ( KigPainter and
+ * KigWidget... )
+ */
+class CoordinateSystem
+ : public Qt
+{
+public:
+ CoordinateSystem();
+ virtual ~CoordinateSystem();
+
+ virtual QString fromScreen ( const Coordinate& pt, const KigDocument& w ) const = 0;
+ /**
+ * This returns a notice to say in which format coordinates should
+ * be entered. This should be something like:
+ * i18n( "Enter coordinates in the following form: \"(x,y)\", where
+ * x is the x coordinate, and y is the y coordinate." );
+ */
+ virtual QString coordinateFormatNotice() const = 0;
+ /**
+ * Like \ref coordinateFormatNotice(), but with HTML tags useful to
+ * have a rich text...
+ */
+ virtual QString coordinateFormatNoticeMarkup() const = 0;
+ virtual Coordinate toScreen (const QString& pt, bool& ok) const = 0;
+ virtual void drawGrid ( KigPainter& p, bool showgrid = true,
+ bool showaxes = true ) const = 0;
+ virtual QValidator* coordinateValidator() const = 0;
+ virtual Coordinate snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const = 0;
+
+ virtual const char* type() const = 0;
+ virtual int id() const = 0;
+};
+
+class EuclideanCoords
+ : public CoordinateSystem
+{
+public:
+ EuclideanCoords();
+ ~EuclideanCoords();
+ QString fromScreen( const Coordinate& pt, const KigDocument& w ) const;
+ QString coordinateFormatNotice() const;
+ QString coordinateFormatNoticeMarkup() const;
+ Coordinate toScreen (const QString& pt, bool& ok) const;
+ void drawGrid ( KigPainter& p, bool showgrid = true,
+ bool showaxes = true ) const;
+ QValidator* coordinateValidator() const;
+ Coordinate snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const;
+
+ const char* type() const;
+ int id() const;
+};
+
+class PolarCoords
+ : public CoordinateSystem
+{
+ void drawGridLine( KigPainter& p, const Coordinate& center,
+ double radius ) const;
+public:
+ PolarCoords();
+ ~PolarCoords();
+ QString fromScreen( const Coordinate& pt, const KigDocument& w ) const;
+ QString coordinateFormatNotice() const;
+ QString coordinateFormatNoticeMarkup() const;
+ Coordinate toScreen (const QString& pt, bool& ok) const;
+ void drawGrid ( KigPainter& p, bool showgrid = true,
+ bool showaxes = true ) const;
+ QValidator* coordinateValidator() const;
+ Coordinate snapToGrid( const Coordinate& c,
+ const KigWidget& w ) const;
+
+ const char* type() const;
+ int id() const;
+};
+
+#endif
diff --git a/kig/misc/cubic-common.cc b/kig/misc/cubic-common.cc
new file mode 100644
index 00000000..029f1194
--- /dev/null
+++ b/kig/misc/cubic-common.cc
@@ -0,0 +1,527 @@
+// 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 <config.h>
+
+#include "cubic-common.h"
+#include "kignumerics.h"
+#include "kigtransform.h"
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+/*
+ * coefficients of the cartesian equation for cubics
+ */
+
+CubicCartesianData::CubicCartesianData()
+{
+ std::fill( coeffs, coeffs + 10, 0 );
+}
+
+CubicCartesianData::CubicCartesianData(
+ const double incoeffs[10] )
+{
+ std::copy( incoeffs, incoeffs + 10, coeffs );
+}
+
+const CubicCartesianData calcCubicThroughPoints (
+ const std::vector<Coordinate>& points )
+{
+ // points is a vector of at most 9 points through which the cubic is
+ // constrained.
+ // this routine should compute the coefficients in the cartesian equation
+ // they are defined up to a multiplicative factor.
+ // since we don't know (in advance) which one of them is nonzero, we
+ // simply keep all 10 parameters, obtaining a 9x10 linear system which
+ // we solve using gaussian elimination with complete pivoting
+ // If there are too few, then we choose some cool way to fill in the
+ // empty parts in the matrix according to the LinearConstraints
+ // given..
+
+ // 9 rows, 10 columns..
+ double row0[10];
+ double row1[10];
+ double row2[10];
+ double row3[10];
+ double row4[10];
+ double row5[10];
+ double row6[10];
+ double row7[10];
+ double row8[10];
+ double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8};
+ double solution[10];
+ int scambio[10];
+
+ int numpoints = points.size();
+ int numconstraints = 9;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = 1.0;
+ matrix[i][1] = xi;
+ matrix[i][2] = yi;
+ matrix[i][3] = xi*xi;
+ matrix[i][4] = xi*yi;
+ matrix[i][5] = yi*yi;
+ matrix[i][6] = xi*xi*xi;
+ matrix[i][7] = xi*xi*yi;
+ matrix[i][8] = xi*yi*yi;
+ matrix[i][9] = yi*yi*yi;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 9) break; // don't add constraints if we have enough
+ for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0;
+ bool addedconstraint = true;
+ switch (i)
+ {
+ case 0:
+ matrix[numpoints][7] = 1.0;
+ matrix[numpoints][8] = -1.0;
+ break;
+ case 1:
+ matrix[numpoints][7] = 1.0;
+ break;
+ case 2:
+ matrix[numpoints][9] = 1.0;
+ break;
+ case 3:
+ matrix[numpoints][4] = 1.0;
+ break;
+ case 4:
+ matrix[numpoints][5] = 1.0;
+ break;
+ case 5:
+ matrix[numpoints][3] = 1.0;
+ break;
+ case 6:
+ matrix[numpoints][1] = 1.0;
+ break;
+
+ default:
+ addedconstraint = false;
+ break;
+ }
+
+ if (addedconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) )
+ return CubicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 10, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return CubicCartesianData( solution );
+}
+
+const CubicCartesianData calcCubicCuspThroughPoints (
+ const std::vector<Coordinate>& points )
+{
+ // points is a vector of at most 4 points through which the cubic is
+ // constrained. Moreover the cubic is required to have a cusp at the
+ // origin.
+
+ // 9 rows, 10 columns..
+ double row0[10];
+ double row1[10];
+ double row2[10];
+ double row3[10];
+ double row4[10];
+ double row5[10];
+ double row6[10];
+ double row7[10];
+ double row8[10];
+ double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8};
+ double solution[10];
+ int scambio[10];
+
+ int numpoints = points.size();
+ int numconstraints = 9;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = 1.0;
+ matrix[i][1] = xi;
+ matrix[i][2] = yi;
+ matrix[i][3] = xi*xi;
+ matrix[i][4] = xi*yi;
+ matrix[i][5] = yi*yi;
+ matrix[i][6] = xi*xi*xi;
+ matrix[i][7] = xi*xi*yi;
+ matrix[i][8] = xi*yi*yi;
+ matrix[i][9] = yi*yi*yi;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 9) break; // don't add constraints if we have enough
+ for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0;
+ bool addedconstraint = true;
+ switch (i)
+ {
+ case 0:
+ matrix[numpoints][0] = 1.0; // through the origin
+ break;
+ case 1:
+ matrix[numpoints][1] = 1.0;
+ break;
+ case 2:
+ matrix[numpoints][2] = 1.0; // no first degree term
+ break;
+ case 3:
+ matrix[numpoints][3] = 1.0; // a011 (x^2 coeff) = 0
+ break;
+ case 4:
+ matrix[numpoints][4] = 1.0; // a012 (xy coeff) = 0
+ break;
+ case 5:
+ matrix[numpoints][7] = 1.0;
+ matrix[numpoints][8] = -1.0;
+ break;
+ case 6:
+ matrix[numpoints][7] = 1.0;
+ break;
+ case 7:
+ matrix[numpoints][9] = 1.0;
+ break;
+ case 8:
+ matrix[numpoints][6] = 1.0;
+ break;
+
+ default:
+ addedconstraint = false;
+ break;
+ }
+
+ if (addedconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) )
+ return CubicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 10, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return CubicCartesianData( solution );
+}
+
+const CubicCartesianData calcCubicNodeThroughPoints (
+ const std::vector<Coordinate>& points )
+{
+ // points is a vector of at most 6 points through which the cubic is
+ // constrained. Moreover the cubic is required to have a node at the
+ // origin.
+
+ // 9 rows, 10 columns..
+ double row0[10];
+ double row1[10];
+ double row2[10];
+ double row3[10];
+ double row4[10];
+ double row5[10];
+ double row6[10];
+ double row7[10];
+ double row8[10];
+ double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8};
+ double solution[10];
+ int scambio[10];
+
+ int numpoints = points.size();
+ int numconstraints = 9;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = 1.0;
+ matrix[i][1] = xi;
+ matrix[i][2] = yi;
+ matrix[i][3] = xi*xi;
+ matrix[i][4] = xi*yi;
+ matrix[i][5] = yi*yi;
+ matrix[i][6] = xi*xi*xi;
+ matrix[i][7] = xi*xi*yi;
+ matrix[i][8] = xi*yi*yi;
+ matrix[i][9] = yi*yi*yi;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 9) break; // don't add constraints if we have enough
+ for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0;
+ bool addedconstraint = true;
+ switch (i)
+ {
+ case 0:
+ matrix[numpoints][0] = 1.0;
+ break;
+ case 1:
+ matrix[numpoints][1] = 1.0;
+ break;
+ case 2:
+ matrix[numpoints][2] = 1.0;
+ break;
+ case 3:
+ matrix[numpoints][7] = 1.0;
+ matrix[numpoints][8] = -1.0;
+ break;
+ case 4:
+ matrix[numpoints][7] = 1.0;
+ break;
+ case 5:
+ matrix[numpoints][9] = 1.0;
+ break;
+ case 6:
+ matrix[numpoints][4] = 1.0;
+ break;
+ case 7:
+ matrix[numpoints][5] = 1.0;
+ break;
+ case 8:
+ matrix[numpoints][3] = 1.0;
+ break;
+
+ default:
+ addedconstraint = false;
+ break;
+ }
+
+ if (addedconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) )
+ return CubicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 10, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return CubicCartesianData( solution );
+}
+
+/*
+ * computation of the y value corresponding to some x value
+ */
+
+double calcCubicYvalue ( double x, double ymin, double ymax, int root,
+ CubicCartesianData data, bool& valid,
+ int &numroots )
+{
+ valid = true;
+
+ // compute the third degree polinomial:
+ double a000 = data.coeffs[0];
+ double a001 = data.coeffs[1];
+ double a002 = data.coeffs[2];
+ double a011 = data.coeffs[3];
+ double a012 = data.coeffs[4];
+ double a022 = data.coeffs[5];
+ double a111 = data.coeffs[6];
+ double a112 = data.coeffs[7];
+ double a122 = data.coeffs[8];
+ double a222 = data.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;
+
+ return calcCubicRoot ( ymin, ymax, a, b, c, d, root, valid, numroots );
+}
+
+const Coordinate calcCubicLineIntersect( const CubicCartesianData& cu,
+ const LineData& l,
+ int root, bool& valid )
+{
+ assert( root == 1 || root == 2 || root == 3 );
+
+ double a, b, c, d;
+ calcCubicLineRestriction ( cu, l.a, l.b-l.a, a, b, c, d );
+ int numroots;
+ double param =
+ calcCubicRoot ( -1e10, 1e10, a, b, c, d, root, valid, numroots );
+ return l.a + param*(l.b - l.a);
+}
+
+/*
+ * calculate the cubic polynomial resulting from the restriction
+ * of a cubic to a line (defined by two "Coordinates": a point and a
+ * direction)
+ */
+
+void calcCubicLineRestriction ( CubicCartesianData data,
+ Coordinate p, Coordinate v,
+ double& a, double& b, double& c, double& d )
+{
+ a = b = c = d = 0;
+
+ double a000 = data.coeffs[0];
+ double a001 = data.coeffs[1];
+ double a002 = data.coeffs[2];
+ double a011 = data.coeffs[3];
+ double a012 = data.coeffs[4];
+ double a022 = data.coeffs[5];
+ double a111 = data.coeffs[6];
+ double a112 = data.coeffs[7];
+ double a122 = data.coeffs[8];
+ double a222 = data.coeffs[9];
+
+ // zero degree term
+ d += a000;
+
+ // first degree terms
+ d += a001*p.x + a002*p.y;
+ c += a001*v.x + a002*v.y;
+
+ // second degree terms
+ d += a011*p.x*p.x + a012*p.x*p.y + a022*p.y*p.y;
+ c += 2*a011*p.x*v.x + a012*(p.x*v.y + v.x*p.y) + 2*a022*p.y*v.y;
+ b += a011*v.x*v.x + a012*v.x*v.y + a022*v.y*v.y;
+
+ // third degree terms: a111 x^3 + a222 y^3
+ d += a111*p.x*p.x*p.x + a222*p.y*p.y*p.y;
+ c += 3*(a111*p.x*p.x*v.x + a222*p.y*p.y*v.y);
+ b += 3*(a111*p.x*v.x*v.x + a222*p.y*v.y*v.y);
+ a += a111*v.x*v.x*v.x + a222*v.y*v.y*v.y;
+
+ // third degree terms: a112 x^2 y + a122 x y^2
+ d += a112*p.x*p.x*p.y + a122*p.x*p.y*p.y;
+ c += a112*(p.x*p.x*v.y + 2*p.x*v.x*p.y) + a122*(v.x*p.y*p.y + 2*p.x*p.y*v.y);
+ b += a112*(v.x*v.x*p.y + 2*v.x*p.x*v.y) + a122*(p.x*v.y*v.y + 2*v.x*v.y*p.y);
+ a += a112*v.x*v.x*v.y + a122*v.x*v.y*v.y;
+}
+
+
+const CubicCartesianData calcCubicTransformation (
+ const CubicCartesianData& data,
+ const Transformation& t, bool& valid )
+{
+ double a[3][3][3];
+ double b[3][3][3];
+ CubicCartesianData dataout;
+
+ int icount = 0;
+ for (int i=0; i < 3; i++)
+ {
+ for (int j=i; j < 3; j++)
+ {
+ for (int k=j; k < 3; k++)
+ {
+ a[i][j][k] = data.coeffs[icount++];
+ if ( i < k )
+ {
+ if ( i == j ) // case aiik
+ {
+ a[i][i][k] /= 3.;
+ a[i][k][i] = a[k][i][i] = a[i][i][k];
+ }
+ else if ( j == k ) // case aijj
+ {
+ a[i][j][j] /= 3.;
+ a[j][i][j] = a[j][j][i] = a[i][j][j];
+ }
+ else // case aijk (i<j<k)
+ {
+ a[i][j][k] /= 6.;
+ a[i][k][j] = a[j][i][k] = a[j][k][i] =
+ a[k][i][j] = a[k][j][i] = a[i][j][k];
+ }
+ }
+ }
+ }
+ }
+
+ Transformation ti = t.inverse( valid );
+ if ( ! valid ) return dataout;
+
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ for (int k = 0; k < 3; k++)
+ {
+ b[i][j][k] = 0.;
+ for (int ii = 0; ii < 3; ii++)
+ {
+ for (int jj = 0; jj < 3; jj++)
+ {
+ for (int kk = 0; kk < 3; kk++)
+ {
+ b[i][j][k] += a[ii][jj][kk]*ti.data( ii, i )*ti.data( jj, j )*ti.data( kk, k );
+ }
+ }
+ }
+ }
+ }
+ }
+
+// assert (fabs(b[0][1][2] - b[1][2][0]) < 1e-8); // test a couple of cases
+// assert (fabs(b[0][1][1] - b[1][1][0]) < 1e-8);
+
+ // apparently, the above assertions are wrong ( due to rounding
+ // errors, Maurizio and I hope :) ), so since the symmetry is not
+ // present, we just take the sum of the parts of the matrix elements
+ // that should be symmetric, instead of taking one of them, and
+ // multiplying it..
+
+ dataout.coeffs[0] = b[0][0][0];
+ dataout.coeffs[1] = b[0][0][1] + b[0][1][0] + b[1][0][0];
+ dataout.coeffs[2] = b[0][0][2] + b[0][2][0] + b[2][0][0];
+ dataout.coeffs[3] = b[0][1][1] + b[1][0][1] + b[1][1][0];
+ dataout.coeffs[4] = b[0][1][2] + b[0][2][1] + b[1][2][0] + b[1][0][2] + b[2][1][0] + b[2][0][1];
+ dataout.coeffs[5] = b[0][2][2] + b[2][0][2] + b[2][2][0];
+ dataout.coeffs[6] = b[1][1][1];
+ dataout.coeffs[7] = b[1][1][2] + b[1][2][1] + b[2][1][1];
+ dataout.coeffs[8] = b[1][2][2] + b[2][1][2] + b[2][2][1];
+ dataout.coeffs[9] = b[2][2][2];
+
+ return dataout;
+}
+
+bool operator==( const CubicCartesianData& lhs, const CubicCartesianData& rhs )
+{
+ for ( int i = 0; i < 10; ++i )
+ if ( lhs.coeffs[i] != rhs.coeffs[i] )
+ return false;
+ return true;
+}
+
+CubicCartesianData CubicCartesianData::invalidData()
+{
+ CubicCartesianData ret;
+ ret.coeffs[0] = double_inf;
+ return ret;
+}
+
+bool CubicCartesianData::valid() const
+{
+ return finite( coeffs[0] );
+}
diff --git a/kig/misc/cubic-common.h b/kig/misc/cubic-common.h
new file mode 100644
index 00000000..8fbcd1a2
--- /dev/null
+++ b/kig/misc/cubic-common.h
@@ -0,0 +1,120 @@
+// 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_MISC_CUBIC_COMMON_H
+#define KIG_MISC_CUBIC_COMMON_H
+
+#include "common.h"
+
+class Transformation;
+
+/**
+ * This class represents an equation of a cubic in the form
+ * \f$ a_{ijk} x_i x_j x_k = 0 \f$ (in homogeneous coordinates,
+ * \f$ i,j,k = 0,1,2 \f$), \f$ i <= j <= k \f$.
+ * The coefficients are stored in lessicografic order.
+ */
+class CubicCartesianData
+{
+public:
+ double coeffs[10];
+ /**
+ * \ifnot creating-python-scripting-doc
+ * \brief Default Constructor
+ *
+ * Constructs a new CubicCartesianData, with all the coeffs
+ * initialized to 0.
+ * \endif
+ */
+ explicit CubicCartesianData();
+ /**
+ * Constructor. Construct a new CubicCartesianData, with the given
+ * values as coeffs.
+ */
+ CubicCartesianData( double a000, double a001, double a002,
+ double a011, double a012, double a022,
+ double a111, double a112, double a122,
+ double a222 )
+ {
+ coeffs[0] = a000;
+ coeffs[1] = a001;
+ coeffs[2] = a002;
+ coeffs[3] = a011;
+ coeffs[4] = a012;
+ coeffs[5] = a022;
+ coeffs[6] = a111;
+ coeffs[7] = a112;
+ coeffs[8] = a122;
+ coeffs[9] = a222;
+ }
+ CubicCartesianData( const double incoeffs[10] );
+
+ /**
+ * Create an invalid CubicCartesianData. This is a special state of a
+ * CubicCartesianData that signals that something went wrong..
+ *
+ * \see CubicCartesianData::valid
+ *
+ * \internal We represent an invalid CubicCartesianData by setting all
+ * the coeffs to positive or negative infinity. This is handy, since
+ * it doesn't require us to adapt most of the functions, it doesn't
+ * need extra space, and most of the times that we should get an
+ * invalid CubicCartesianData, we get one automatically..
+ */
+ static CubicCartesianData invalidData();
+ /**
+ * Return whether this is a valid CubicCartesianData.
+ *
+ * \see CubicCartesianData::invalidData
+ */
+ bool valid() const;
+};
+
+bool operator==( const CubicCartesianData& lhs, const CubicCartesianData& rhs );
+
+/**
+ * This function calcs a cartesian cubic equation such that the
+ * given points are on the cubic. There can be at most 9 and at
+ * least 2 point. If there are less than 9, than the coefficients
+ * will be chosen to 1.0 if possible
+ */
+const CubicCartesianData calcCubicThroughPoints (
+ const std::vector<Coordinate>& points );
+
+const CubicCartesianData calcCubicCuspThroughPoints (
+ const std::vector<Coordinate>& points );
+
+const CubicCartesianData calcCubicNodeThroughPoints (
+ const std::vector<Coordinate>& points );
+
+double calcCubicYvalue ( double x, double ymin, double ymax,
+ int root, CubicCartesianData data,
+ bool& valid, int& numroots );
+
+const Coordinate calcCubicLineIntersect( const CubicCartesianData& c,
+ const LineData& l,
+ int root, bool& valid );
+
+void calcCubicLineRestriction ( CubicCartesianData data,
+ Coordinate p1, Coordinate dir,
+ double& a, double& b, double& c, double& d );
+
+const CubicCartesianData calcCubicTransformation (
+ const CubicCartesianData& data,
+ const Transformation& t, bool& valid );
+
+#endif
diff --git a/kig/misc/goniometry.cc b/kig/misc/goniometry.cc
new file mode 100644
index 00000000..13d72fdb
--- /dev/null
+++ b/kig/misc/goniometry.cc
@@ -0,0 +1,137 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 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
+**/
+
+
+#include "goniometry.h"
+
+#include <qstringlist.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <cmath>
+
+Goniometry::Goniometry()
+{
+ mvalue = 0.0;
+ msys = Rad;
+}
+
+Goniometry::Goniometry( double value, Goniometry::System system )
+{
+ mvalue = value;
+ msys = system;
+}
+
+Goniometry::~Goniometry()
+{
+}
+
+void Goniometry::setValue( double value )
+{
+ mvalue = value;
+}
+
+const double Goniometry::value() const
+{
+ return mvalue;
+}
+
+void Goniometry::setSystem( Goniometry::System system )
+{
+ msys = system;
+}
+
+void Goniometry::convertTo( Goniometry::System system )
+{
+ mvalue = convert( mvalue, msys, system );
+ msys = system;
+}
+
+const Goniometry::System Goniometry::system() const
+{
+ return msys;
+}
+
+double Goniometry::getValue( Goniometry::System system )
+{
+ return convert( mvalue, msys, system );
+}
+
+Goniometry& Goniometry::operator=( const Goniometry& g )
+{
+ mvalue = g.value();
+ msys = g.system();
+ return *this;
+}
+
+double Goniometry::convert( const double angle, const Goniometry::System from, const Goniometry::System to )
+{
+ switch( from )
+ {
+ case Deg:
+ {
+ if ( to == Rad )
+ return angle * M_PI / 180;
+ if ( to == Grad )
+ return angle * 10 / 9;
+ break;
+ }
+ case Rad:
+ {
+ if ( to == Deg )
+ return angle * 180 / M_PI;
+ if ( to == Grad )
+ return angle * 200 / M_PI;
+ break;
+ }
+ case Grad:
+ {
+ if ( to == Deg )
+ return angle * 9 / 10;
+ if ( to == Rad )
+ return angle * M_PI / 200;
+ break;
+ }
+ }
+ return angle;
+}
+
+QStringList Goniometry::systemList()
+{
+ QStringList sl;
+ sl << i18n( "Translators: Degrees", "Deg" );
+ sl << i18n( "Translators: Radians", "Rad" );
+ sl << i18n( "Translators: Gradians", "Grad" );
+ return sl;
+}
+
+Goniometry::System Goniometry::intToSystem( const int index )
+{
+ if( index == 0 )
+ return Deg;
+ else if( index == 1 )
+ return Rad;
+ else if( index == 2 )
+ return Grad;
+ kdDebug() << "No goniometric system with index " << index << endl;
+ return Rad;
+}
diff --git a/kig/misc/goniometry.h b/kig/misc/goniometry.h
new file mode 100644
index 00000000..eae84ced
--- /dev/null
+++ b/kig/misc/goniometry.h
@@ -0,0 +1,72 @@
+// This file is part of Kig, a KDE program for Interactive Geometry...
+// Copyright (C) 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_GONIOMETRY_H
+#define KIG_MISC_GONIOMETRY_H
+
+#include <qstringlist.h>
+
+/**
+ * Manage an angle and convert it from/to other goniometric systems.
+ */
+class Goniometry
+{
+public:
+ enum System { Deg, Rad, Grad };
+ Goniometry();
+ Goniometry( double value, Goniometry::System system );
+ ~Goniometry();
+ void setValue( double value );
+ const double value() const;
+ /**
+ * Set the system of the current angle to \p system, but it doesn't
+ * convert the value to the new system.
+ *
+ * \see convertTo()
+ */
+ void setSystem( Goniometry::System system );
+ /**
+ * Set the system of the current angle to \p system and convert the
+ * value to the new system using \ref convert().
+ *
+ * \see setSystem()
+ */
+ void convertTo( Goniometry::System system );
+ const Goniometry::System system() const;
+ double getValue( Goniometry::System system );
+ /**
+ * The most useful method of this class: convert the specified
+ * \p angle from the system \p from to the system \p to.
+ */
+ static double convert( const double angle, const Goniometry::System from, const Goniometry::System to );
+ /**
+ * Get a list of the supported goniometric systems.
+ */
+ static QStringList systemList();
+ static Goniometry::System intToSystem( const int index );
+
+ Goniometry& operator= ( const Goniometry& g );
+
+private:
+ double mvalue;
+ typedef Goniometry::System goniosys;
+ goniosys msys;
+};
+
+#endif
diff --git a/kig/misc/guiaction.cc b/kig/misc/guiaction.cc
new file mode 100644
index 00000000..d4be4ded
--- /dev/null
+++ b/kig/misc/guiaction.cc
@@ -0,0 +1,367 @@
+// 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 "guiaction.h"
+#include "guiaction.moc"
+
+#include "coordinate_system.h"
+#include "coordinate.h"
+#include "object_constructor.h"
+
+#include "../kig/kig_part.h"
+#include "../kig/kig_document.h"
+#include "../misc/kiginputdialog.h"
+#include "../modes/construct_mode.h"
+#include "../modes/label.h"
+#include "../objects/object_holder.h"
+#include "../objects/object_factory.h"
+#include "../objects/bogus_imp.h"
+
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <qregexp.h>
+
+int GUIAction::shortcut() const
+{
+ return 0;
+}
+
+GUIAction::~GUIAction()
+{
+}
+
+ConstructibleAction::~ConstructibleAction()
+{
+}
+
+ConstructibleAction::ConstructibleAction(
+ ObjectConstructor* ctor,
+ const QCString& actionname,
+ int shortcut )
+ : GUIAction(), mctor( ctor ), mactionname( actionname ), mshortcut( shortcut )
+{
+}
+
+QString ConstructibleAction::description() const
+{
+ return mctor->description();
+}
+
+QCString ConstructibleAction::iconFileName() const
+{
+ return mctor->iconFileName();
+}
+
+QString ConstructibleAction::descriptiveName() const
+{
+ return mctor->descriptiveName();
+}
+
+void ConstructibleAction::act( KigPart& d )
+{
+ BaseConstructMode* m = mctor->constructMode( d );
+ d.runMode( m );
+ delete m;
+}
+
+KigGUIAction::KigGUIAction( GUIAction* act,
+ KigPart& doc,
+ QObject* parent )
+ : KAction( act->descriptiveName(),
+ doc.instance()->iconLoader()->loadIcon(
+ act->iconFileName(), KIcon::Toolbar, 0, KIcon::DefaultState, 0L, true ),
+ act->shortcut(),
+ 0, 0, // no slot connection
+ parent, act->actionName() ),
+ mact( act ),
+ mdoc( doc )
+{
+ setWhatsThis( act->description() );
+ QString tooltip = act->descriptiveName();
+ tooltip.replace( QRegExp( "&&" ), "&" );
+ setToolTip( tooltip );
+}
+
+void KigGUIAction::slotActivated()
+{
+ mact->act( mdoc );
+}
+
+const char* ConstructibleAction::actionName() const
+{
+ return mactionname;
+}
+
+ConstructPointAction::~ConstructPointAction()
+{
+}
+
+QString ConstructPointAction::description() const
+{
+ return i18n(
+ "A normal point, i.e. one that is either independent or attached "
+ "to a line, circle, segment."
+ );
+}
+
+QCString ConstructPointAction::iconFileName() const
+{
+ return "point";
+}
+
+QString ConstructPointAction::descriptiveName() const
+{
+ return i18n("Point");
+}
+
+const char* ConstructPointAction::actionName() const
+{
+ return mactionname;
+}
+
+int ConstructPointAction::shortcut() const
+{
+ return Qt::Key_P;
+}
+
+void ConstructPointAction::act( KigPart& d )
+{
+ PointConstructMode m( d );
+ d.runMode( &m );
+}
+
+ConstructPointAction::ConstructPointAction( const char* actionname )
+ : mactionname( actionname )
+{
+}
+
+GUIAction* KigGUIAction::guiAction()
+{
+ return mact;
+}
+
+void KigGUIAction::plug( KigPart* doc )
+{
+ mact->plug( doc, this );
+}
+
+void ConstructibleAction::plug( KigPart* doc, KigGUIAction* kact )
+{
+ mctor->plug( doc, kact );
+}
+
+QString ConstructTextLabelAction::description() const
+{
+ return i18n( "Construct a text label." );
+}
+
+QCString ConstructTextLabelAction::iconFileName() const
+{
+ return "kig_text";
+}
+
+QString ConstructTextLabelAction::descriptiveName() const
+{
+ return i18n( "Text Label" );
+}
+
+const char* ConstructTextLabelAction::actionName() const
+{
+ return mactionname;
+}
+
+void ConstructTextLabelAction::act( KigPart& d )
+{
+ TextLabelConstructionMode m( d );
+ d.runMode( &m );
+}
+
+ConstructTextLabelAction::ConstructTextLabelAction( const char* actionname )
+ : mactionname( actionname )
+{
+}
+
+QString AddFixedPointAction::description() const
+{
+ return i18n( "Construct a Point by its Coordinates" );
+}
+
+QCString AddFixedPointAction::iconFileName() const
+{
+ return "pointxy";
+}
+
+QString AddFixedPointAction::descriptiveName() const
+{
+ return i18n( "Point by Coordinates" );
+}
+
+const char* AddFixedPointAction::actionName() const
+{
+ return mactionname;
+}
+
+void AddFixedPointAction::act( KigPart& doc )
+{
+ bool ok;
+ Coordinate c = Coordinate::invalidCoord();
+ KigInputDialog::getCoordinate(
+ i18n( "Fixed Point" ),
+ i18n( "Enter the coordinates for the new point." ) +
+ QString::fromLatin1( "<br>" ) +
+ doc.document().coordinateSystem().coordinateFormatNoticeMarkup(),
+ doc.widget(), &ok, doc.document(), &c );
+ if ( ! ok ) return;
+ ObjectHolder* p = ObjectFactory::instance()->fixedPoint( c );
+ p->calc( doc.document() );
+ doc.addObject( p );
+}
+
+AddFixedPointAction::AddFixedPointAction( const char* actionname )
+ : mactionname( actionname )
+{
+}
+
+AddFixedPointAction::~AddFixedPointAction()
+{
+}
+
+void GUIAction::plug( KigPart*, KigGUIAction* )
+{
+}
+
+int ConstructibleAction::shortcut() const
+{
+ return mshortcut;
+}
+
+int ConstructTextLabelAction::shortcut() const
+{
+ return Qt::Key_B;
+}
+
+int AddFixedPointAction::shortcut() const
+{
+ return Qt::Key_F;
+}
+
+#if 0
+TestAction::TestAction( const char* actionname )
+ : mactionname( actionname )
+{
+}
+
+TestAction::~TestAction()
+{
+}
+
+QString TestAction::description() const
+{
+ return QString::fromLatin1( "Test stuff !!!" );
+}
+
+QCString TestAction::iconFileName() const
+{
+ return "new";
+}
+
+QString TestAction::descriptiveName() const
+{
+ return QString::fromLatin1( "Test stuff !!!" );
+}
+
+const char* TestAction::actionName() const
+{
+ return mactionname;
+}
+
+void TestAction::act( KigPart& doc )
+{
+ const char* script =
+ "def calc( a ):\n\treturn Point( a.coordinate() + Coordinate( 2, 0 ) )\n";
+ Object* constantpoint = ObjectFactory::instance()->fixedPoint( Coordinate( -1, -1 ) );
+ constantpoint->calc( doc );
+
+ Object* codeobject = new DataObject( new StringImp( QString::fromLatin1( script ) ) );
+ Object* compiledcode = new RealObject( PythonCompileType::instance(), Objects( codeobject ) );
+ compiledcode->calc( doc );
+
+ Objects args( compiledcode );
+ args.push_back( constantpoint );
+ Object* scriptobject = new RealObject( PythonExecuteType::instance(), args );
+ scriptobject->calc( doc );
+
+ doc.addObject( constantpoint );
+ doc.addObject( scriptobject );
+}
+
+#endif // if 0 ( TestAction )
+
+#ifdef KIG_ENABLE_PYTHON_SCRIPTING
+#include "../scripting/python_type.h"
+#include "../scripting/script_mode.h"
+
+NewScriptAction::NewScriptAction( const char* descname, const char* description,
+ const char* actionname, const ScriptType::Type type,
+ const char* icon )
+ : GUIAction(), mactionname( actionname ), mdescname( descname ),
+ mdescription( description ), micon( icon ), mtype( type )
+{
+ if ( QString( micon ).isEmpty() )
+ {
+ micon = ScriptType::icon( type );
+ }
+}
+
+NewScriptAction::~NewScriptAction()
+{
+}
+
+QString NewScriptAction::description() const
+{
+ return i18n( mdescription );
+}
+
+QCString NewScriptAction::iconFileName() const
+{
+ return micon;
+}
+
+QString NewScriptAction::descriptiveName() const
+{
+ return i18n( mdescname );
+}
+
+const char* NewScriptAction::actionName() const
+{
+ return mactionname;
+}
+
+void NewScriptAction::act( KigPart& doc )
+{
+ ScriptCreationMode m( doc );
+ m.setScriptType( mtype );
+ doc.runMode( &m );
+}
+
+int NewScriptAction::shortcut() const
+{
+ return 0;
+}
+
+#endif // if KIG_ENABLE_PYTHON_SCRIPTING ( NewScriptAction )
diff --git a/kig/misc/guiaction.h b/kig/misc/guiaction.h
new file mode 100644
index 00000000..c188a492
--- /dev/null
+++ b/kig/misc/guiaction.h
@@ -0,0 +1,174 @@
+// 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_MISC_GUIACTION_H
+#define KIG_MISC_GUIACTION_H
+
+#include <config.h>
+
+#ifdef KIG_ENABLE_PYTHON_SCRIPTING
+#include "../scripting/script-common.h"
+#endif
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <kaction.h>
+
+class GUIAction;
+class KigPart;
+
+class KigGUIAction
+ : public KAction
+{
+ Q_OBJECT
+ GUIAction* mact;
+ KigPart& mdoc;
+public:
+ KigGUIAction( GUIAction* act,
+ KigPart& doc,
+ QObject* parent );
+ void slotActivated();
+
+ GUIAction* guiAction();
+
+ void plug( KigPart* doc );
+};
+
+class GUIAction
+{
+public:
+ virtual ~GUIAction();
+
+ virtual QString description() const = 0;
+ virtual QCString iconFileName() const = 0;
+ virtual QString descriptiveName() const = 0;
+ virtual const char* actionName() const = 0;
+ virtual int shortcut() const = 0;
+ virtual void act( KigPart& ) = 0;
+
+ virtual void plug( KigPart* doc, KigGUIAction* kact );
+};
+
+class ObjectConstructor;
+
+class ConstructibleAction
+ : public GUIAction
+{
+ ObjectConstructor* mctor;
+ QCString mactionname;
+ int mshortcut;
+public:
+ ConstructibleAction( ObjectConstructor* ctor, const QCString& actionname,
+ int shortcut = 0 );
+ ~ConstructibleAction();
+ QString description() const;
+ QCString iconFileName() const;
+ QString descriptiveName() const;
+ const char* actionName() const;
+ int shortcut() const;
+ void act( KigPart& );
+ void plug( KigPart* doc, KigGUIAction* kact );
+};
+
+class ConstructPointAction
+ : public GUIAction
+{
+ const char* mactionname;
+public:
+ ConstructPointAction( const char* actionname );
+ ~ConstructPointAction();
+
+ QString description() const;
+ QCString iconFileName() const;
+ QString descriptiveName() const;
+ const char* actionName() const;
+ int shortcut() const;
+ void act( KigPart& );
+};
+
+class ConstructTextLabelAction
+ : public GUIAction
+{
+ const char* mactionname;
+public:
+ ConstructTextLabelAction( const char* actionname );
+
+ QString description() const;
+ QCString iconFileName() const;
+ QString descriptiveName() const;
+ const char* actionName() const;
+ int shortcut() const;
+ void act( KigPart& );
+};
+
+class AddFixedPointAction
+ : public GUIAction
+{
+ const char* mactionname;
+public:
+ AddFixedPointAction( const char* actionname );
+ ~AddFixedPointAction();
+ QString description() const;
+ QCString iconFileName() const;
+ QString descriptiveName() const;
+ const char* actionName() const;
+ int shortcut() const;
+ void act( KigPart& );
+};
+
+#if 0
+class TestAction
+ : public GUIAction
+{
+ const char* mactionname;
+public:
+ TestAction( const char* actionname );
+ ~TestAction();
+ QString description() const;
+ QCString iconFileName() const;
+ QString descriptiveName() const;
+ const char* actionName() const;
+ void act( KigPart& );
+};
+#endif
+
+#ifdef KIG_ENABLE_PYTHON_SCRIPTING
+
+class NewScriptAction
+ : public GUIAction
+{
+ const char* mactionname;
+ const char* mdescname;
+ const char* mdescription;
+ const char* micon;
+ const ScriptType::Type mtype;
+public:
+ NewScriptAction( const char* descname, const char* description,
+ const char* actionname, const ScriptType::Type type,
+ const char* icon = "" );
+ ~NewScriptAction();
+ QString description() const;
+ QCString iconFileName() const;
+ QString descriptiveName() const;
+ const char* actionName() const;
+ void act( KigPart& );
+ int shortcut() const;
+};
+
+#endif // KIG_ENABLE_PYTHON_SCRIPTING
+
+#endif
diff --git a/kig/misc/kigfiledialog.cc b/kig/misc/kigfiledialog.cc
new file mode 100644
index 00000000..6b8d8cb4
--- /dev/null
+++ b/kig/misc/kigfiledialog.cc
@@ -0,0 +1,81 @@
+// Copyright (C) 2005 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 "kigfiledialog.h"
+#include "kigfiledialog.moc"
+
+#include <qfile.h>
+#include <qpoint.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+KigFileDialog::KigFileDialog( const QString& startDir, const QString& filter,
+ const QString& caption, QWidget* parent )
+ : KFileDialog( startDir, filter, parent, "kigfiledialog", true ),
+ mow( 0L )
+{
+ setCaption( caption );
+ setOperationMode( Saving );
+ setMode( KFile::File | KFile::LocalOnly );
+ moptcaption = i18n( "Options" );
+}
+
+void KigFileDialog::setOptionsWidget( QWidget* w )
+{
+ mow = w;
+}
+
+void KigFileDialog::accept()
+{
+ // i know this is an ugly hack, but i hadn't found other ways to get
+ // the selected file name _before_ the dialog is accept()'ed or
+ // reject()'ed... in every case, below we make sure to accept() or
+ // reject()...
+ setResult( QDialog::Accepted );
+
+ QString sFile = selectedFile();
+ if ( QFile::exists( sFile ) )
+ {
+ int ret = KMessageBox::warningContinueCancel( this,
+ i18n( "The file \"%1\" already exists. Do you wish to overwrite it?" )
+ .arg( sFile ), i18n( "Overwrite File?" ), i18n("Overwrite") );
+ if ( ret != KMessageBox::Continue )
+ {
+ KFileDialog::reject();
+ return;
+ }
+ }
+ if ( mow )
+ {
+ KDialogBase* optdlg = new KDialogBase(
+ this, "optdlg", true, moptcaption, Cancel|Ok, Cancel, true );
+ mow->reparent( optdlg, QPoint() );
+ optdlg->setMainWidget( mow );
+ optdlg->exec() == QDialog::Accepted ? KFileDialog::accept() : KFileDialog::reject();
+ }
+ else
+ KFileDialog::accept();
+}
+
+void KigFileDialog::setOptionCaption( const QString& caption )
+{
+ if ( caption.isEmpty() )
+ return;
+
+ moptcaption = caption;
+}
diff --git a/kig/misc/kigfiledialog.h b/kig/misc/kigfiledialog.h
new file mode 100644
index 00000000..0337236d
--- /dev/null
+++ b/kig/misc/kigfiledialog.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2005 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_KIGFILEDIALOG_H
+#define KIG_MISC_KIGFILEDIALOG_H
+
+#include <kfiledialog.h>
+
+/**
+ * This file dialog is pretty like KFileDialog, but allow us to make an option
+ * widget popup to the user.
+ */
+class KigFileDialog
+ : public KFileDialog
+{
+ Q_OBJECT
+
+private:
+ /**
+ * Options widget
+ */
+ QWidget* mow;
+
+ QString moptcaption;
+
+public:
+ /**
+ * Construct a new KigFileDialog.
+ *
+ * \param startDir the start dir of the file dialog. Consult the
+ * documentation of KFileDialog for more help about this
+ * \param filter the filter for the file dialog
+ * \param caption the caption of this file dialog
+ * \param parent the parent for this file dialog
+ */
+ KigFileDialog( const QString& startDir, const QString& filter,
+ const QString& caption, QWidget *parent );
+
+ /**
+ * Use this to set the widget containing the options of eg an export filter.
+ * The option widget will be popped up in a dialog right after the user
+ * presses OK and before the dialog is closed.
+ *
+ * You can construct the option widget with no parent, as it will be
+ * reparented.
+ *
+ * \param w the option widget
+ */
+ void setOptionsWidget( QWidget* w );
+
+ /**
+ * Set the caption of the option dialog
+ *
+ * \param caption the caption of the option dialog
+ */
+ void setOptionCaption( const QString& caption );
+
+protected slots:
+ virtual void accept();
+
+};
+
+#endif
diff --git a/kig/misc/kiginputdialog.cc b/kig/misc/kiginputdialog.cc
new file mode 100644
index 00000000..8a2c38e5
--- /dev/null
+++ b/kig/misc/kiginputdialog.cc
@@ -0,0 +1,283 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2005 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 "kiginputdialog.h"
+#include "kiginputdialog.moc"
+
+#include "coordinate.h"
+#include "coordinate_system.h"
+#include "goniometry.h"
+
+#include "../kig/kig_document.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qvalidator.h>
+#include <qwhatsthis.h>
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <ktextedit.h>
+
+class KigInputDialogPrivate
+{
+public:
+ KigInputDialogPrivate();
+
+ QLabel* m_label;
+ KLineEdit* m_lineEditFirst;
+ KLineEdit* m_lineEditSecond;
+ KComboBox* m_comboBox;
+ KTextEdit* m_textEdit;
+
+ Coordinate m_coord1;
+ Coordinate m_coord2;
+ KigDocument m_doc;
+ QValidator* m_vtor;
+ Goniometry m_gonio;
+ bool m_gonioIsNum;
+};
+
+KigInputDialogPrivate::KigInputDialogPrivate()
+ : m_label( 0L ), m_lineEditFirst( 0L ), m_lineEditSecond( 0L ), m_comboBox( 0L ),
+ m_textEdit( 0L )
+{
+}
+
+KigInputDialog::KigInputDialog( const QString& caption, const QString& label,
+ QWidget* parent, const KigDocument& doc, Coordinate* c1, Coordinate* c2 )
+ : KDialogBase( parent, "kigdialog", true, caption, Ok|Cancel, Cancel, true ),
+ d( new KigInputDialogPrivate() )
+{
+ d->m_coord1 = c1 ? Coordinate( *c1 ) : Coordinate::invalidCoord();
+ d->m_coord2 = c2 ? Coordinate( *c2 ) : Coordinate::invalidCoord();
+ d->m_doc = doc;
+ d->m_vtor = d->m_doc.coordinateSystem().coordinateValidator();
+
+ int deltay = 0;
+ bool ok = false;
+
+ QFrame* frame = makeMainWidget();
+ QVBoxLayout* mainlay = new QVBoxLayout( frame, 0, spacingHint() );
+ mainlay->activate();
+
+ d->m_textEdit = new KTextEdit( frame );
+ d->m_textEdit->setText( label );
+ d->m_textEdit->setReadOnly( true );
+ d->m_textEdit->setFocusPolicy( NoFocus );
+// d->m_textEdit->setAlignment( d->m_textEdit->alignment() | Qt::WordBreak );
+ d->m_textEdit->setFrameStyle( QFrame::NoFrame );
+ mainlay->addWidget( d->m_textEdit );
+
+ d->m_lineEditFirst = new KLineEdit( frame );
+// d->m_lineEditFirst->setValidator( d->m_vtor );
+ if ( d->m_coord1.valid() )
+ {
+ d->m_lineEditFirst->setText( d->m_doc.coordinateSystem().fromScreen( d->m_coord1, d->m_doc ) );
+ ok = true;
+ }
+ mainlay->addWidget( d->m_lineEditFirst );
+
+ connect( d->m_lineEditFirst, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotCoordsChanged(const QString&)) );
+
+ if ( d->m_coord2.valid() )
+ {
+ d->m_lineEditSecond = new KLineEdit( frame );
+// d->m_lineEditSecond->setValidator( d->m_vtor );
+ d->m_lineEditSecond->setText( d->m_doc.coordinateSystem().fromScreen( d->m_coord2, d->m_doc ) );
+ mainlay->addWidget( d->m_lineEditSecond );
+
+ connect( d->m_lineEditSecond, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotCoordsChanged(const QString&)) );
+
+ deltay += d->m_lineEditSecond->height() + spacingHint();
+ }
+
+ resize( 400, 160 + deltay );
+
+ d->m_lineEditFirst->setFocus();
+
+ enableButtonOK( ok );
+}
+
+KigInputDialog::KigInputDialog( QWidget* parent, const Goniometry& g )
+ : KDialogBase( parent, "kigdialog", true, i18n( "Set Angle Size" ), Ok|Cancel, Cancel, true ),
+ d( new KigInputDialogPrivate() )
+{
+ d->m_gonio = g;
+ d->m_gonioIsNum = true;
+
+ QFrame* frame = makeMainWidget();
+ QVBoxLayout* mainlay = new QVBoxLayout( frame, 0, spacingHint() );
+ mainlay->activate();
+
+ d->m_label = new QLabel( frame );
+ d->m_label->setText( i18n( "Insert the new size of this angle:" ) );
+ mainlay->addWidget( d->m_label );
+
+ QHBoxLayout* horlay = new QHBoxLayout( 0, 0, spacingHint() );
+ horlay->activate();
+
+ d->m_lineEditFirst = new KLineEdit( frame );
+ d->m_lineEditFirst->setText( QString::number( d->m_gonio.value() ) );
+ QWhatsThis::add(
+ d->m_lineEditFirst,
+ i18n( "Use this edit field to modify the size of this angle." ) );
+ horlay->addWidget( d->m_lineEditFirst );
+
+ d->m_comboBox = new KComboBox( frame );
+ d->m_comboBox->insertStringList( Goniometry::systemList() );
+ d->m_comboBox->setCurrentItem( d->m_gonio.system() );
+ QWhatsThis::add(
+ d->m_comboBox,
+ i18n( "Choose from this list the goniometric unit you want to use to "
+ "modify the size of this angle.<br>\n"
+ "If you switch to another unit, the value in the edit field on "
+ "the left will be converted to the new selected unit." ) );
+ horlay->addWidget( d->m_comboBox );
+
+ mainlay->addLayout( horlay );
+
+ connect( d->m_lineEditFirst, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotGonioTextChanged(const QString&)) );
+ connect( d->m_comboBox, SIGNAL(activated(int)),
+ this, SLOT(slotGonioSystemChanged(int)) );
+
+ resize( 350, 100 );
+
+ d->m_lineEditFirst->setFocus();
+}
+
+void KigInputDialog::keyPressEvent( QKeyEvent* e )
+{
+ if ( ( e->key() == Qt::Key_Return ) && ( e->state() == 0 ) )
+ {
+ if ( actionButton( Ok )->isEnabled() )
+ {
+ actionButton( Ok )->animateClick();
+ e->accept();
+ return;
+ }
+ }
+ else if ( ( e->key() == Qt::Key_Escape ) && ( e->state() == 0 ) )
+ {
+ actionButton( Cancel )->animateClick();
+ e->accept();
+ return;
+ }
+
+}
+
+void KigInputDialog::slotCoordsChanged( const QString& )
+{
+ int p = 0;
+ QString t = d->m_lineEditFirst->text();
+ bool ok = d->m_vtor->validate( t, p ) == QValidator::Acceptable;
+ if ( ok )
+ d->m_coord1 = d->m_doc.coordinateSystem().toScreen( t, ok );
+ if ( d->m_lineEditSecond )
+ {
+ p = 0;
+ t = d->m_lineEditSecond->text();
+ ok &= d->m_vtor->validate( t, p ) == QValidator::Acceptable;
+ if ( ok )
+ d->m_coord2 = d->m_doc.coordinateSystem().toScreen( t, ok );
+ }
+
+ enableButtonOK( ok );
+}
+
+void KigInputDialog::slotGonioSystemChanged( int index )
+{
+ if ( d->m_gonioIsNum )
+ {
+ Goniometry::System newsys = Goniometry::intToSystem( index );
+ d->m_gonio.convertTo( newsys );
+ d->m_lineEditFirst->setText( QString::number( d->m_gonio.value() ) );
+ }
+}
+
+void KigInputDialog::slotGonioTextChanged( const QString& txt )
+{
+ if ( txt.isNull() )
+ d->m_gonioIsNum = false;
+ else
+ {
+ double v = txt.toDouble( &(d->m_gonioIsNum) );
+ d->m_gonio.setValue( v );
+ }
+ enableButtonOK( d->m_gonioIsNum );
+}
+
+
+Coordinate KigInputDialog::coordinateFirst() const
+{
+ return d->m_coord1;
+}
+
+Coordinate KigInputDialog::coordinateSecond() const
+{
+ return d->m_coord2;
+}
+
+Goniometry KigInputDialog::goniometry() const
+{
+ return d->m_gonio;
+}
+
+void KigInputDialog::getCoordinate( const QString& caption, const QString& label,
+ QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue )
+{
+ getTwoCoordinates( caption, label, parent, ok, doc, cvalue, 0 );
+}
+
+void KigInputDialog::getTwoCoordinates( const QString& caption, const QString& label,
+ QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue,
+ Coordinate* cvalue2 )
+{
+ KigInputDialog dlg( caption, label, parent, doc, cvalue, cvalue2 );
+
+ *ok = ( dlg.exec() == Accepted );
+
+ if ( *ok )
+ {
+ Coordinate a = dlg.coordinateFirst();
+ *cvalue = a;
+ if ( cvalue2 )
+ {
+ Coordinate b = dlg.coordinateSecond();
+ *cvalue2 = b;
+ }
+ }
+
+}
+
+Goniometry KigInputDialog::getAngle( QWidget* parent, bool* ok, const Goniometry& g )
+{
+ KigInputDialog dlg( parent, g );
+
+ *ok = ( dlg.exec() == Accepted );
+
+ return dlg.goniometry();
+}
diff --git a/kig/misc/kiginputdialog.h b/kig/misc/kiginputdialog.h
new file mode 100644
index 00000000..afdd303d
--- /dev/null
+++ b/kig/misc/kiginputdialog.h
@@ -0,0 +1,123 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2005 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_KIGINPUTDIALOG_H
+#define KIG_MISC_KIGINPUTDIALOG_H
+
+class QString;
+class Coordinate;
+class Goniometry;
+class KigDocument;
+class KigInputDialogPrivate;
+
+#include <kdialogbase.h>
+
+/**
+ * The KigInputDialog class provides easy ways of interaction with the user.
+ * For example, it provides a flexible way to get one or two coordinates at
+ * once.
+ *
+ * It provides several static convenience functions: getCoordinate(),
+ * getTwoCoordinates(), getAngle().
+ */
+class KigInputDialog
+ : KDialogBase
+{
+Q_OBJECT
+
+public:
+
+private:
+ KigInputDialog( const QString& caption, const QString& label, QWidget* parent,
+ const KigDocument& doc, Coordinate* c1, Coordinate* c2 );
+ KigInputDialog( QWidget* parent, const Goniometry& g );
+
+ virtual void keyPressEvent( QKeyEvent* e );
+
+ KigInputDialogPrivate* const d;
+ friend class KInputDialogPrivate;
+
+ Coordinate coordinateFirst() const;
+ Coordinate coordinateSecond() const;
+ Goniometry goniometry() const;
+
+private slots:
+ void slotCoordsChanged( const QString& );
+ void slotGonioSystemChanged( int index );
+ void slotGonioTextChanged( const QString& txt );
+
+public:
+ /**
+ * Static convenience function to get a Coordinate from the user.
+ *
+ * \param caption caption of the dialog
+ * \param label text of the label of the dialog
+ * \param parent parent of the dialog widget
+ * \param ok it will be set to true if the user pressed Ok after inserting a
+ * well-formatted Coordinate
+ * \param doc the actual Kig document
+ * \param cvalue a pointer to a Coordinate class. If the user inserted
+ * successfully a new Coordinate, the value will be stored
+ * here. If this points to a valid Coordinate, then it will be
+ * displayed as initial value of the correspondenting text edit
+ */
+ static void getCoordinate( const QString& caption, const QString& label,
+ QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue );
+
+ /**
+ * Static convenience function to get two Coordinates at once from the user.
+ *
+ * \param caption caption of the dialog
+ * \param label text of the label of the dialog
+ * \param parent parent of the dialog widget
+ * \param ok it will be set to true if the user pressed Ok after inserting
+ * well-formatted Coordinates
+ * \param doc the actual Kig document
+ * \param cvalue a pointer to a Coordinate class. If the user inserted
+ * successfully new Coordinates, the value of the first
+ * Coordinate will be stored here. If this points to a valid
+ * Coordinate, then it will be displayed as initial value of
+ * the text edit representing the first Coordinate.
+ * \param cvalue2 a pointer to a Coordinate class. If the user inserted
+ * successfully new Coordinates, the value of the second
+ * Coordinate will be stored here. If this points to a valid
+ * Coordinate, then it will be displayed as initial value of
+ * the text edit representing the second Coordinate.
+ */
+ static void getTwoCoordinates( const QString& caption, const QString& label,
+ QWidget* parent, bool* ok, const KigDocument& doc, Coordinate* cvalue,
+ Coordinate* cvalue2 );
+
+ /**
+ * Static convenience function to get an angle incapsulated in a Goniometry
+ * class.
+ *
+ * \param parent parent of the dialog widget
+ * \param ok it will be set to true if the user pressed Ok after inserting a
+ * well-formatted angle
+ * \param g the Goniometry class containing the original angle we are going
+ * to modify.
+ *
+ * \return a Goniometry class containing the new angle
+ */
+ static Goniometry getAngle( QWidget* parent, bool* ok, const Goniometry& g );
+};
+
+#endif
diff --git a/kig/misc/kignumerics.cpp b/kig/misc/kignumerics.cpp
new file mode 100644
index 00000000..4711d058
--- /dev/null
+++ b/kig/misc/kignumerics.cpp
@@ -0,0 +1,389 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 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 "kignumerics.h"
+#include "common.h"
+
+/*
+ * compute one of the roots of a cubic polynomial
+ * if xmin << 0 or xmax >> 0 then autocompute a bound for all the
+ * roots
+ */
+
+double calcCubicRoot ( double xmin, double xmax, double a,
+ double b, double c, double d, int root, bool& valid, int& numroots )
+{
+ // renormalize: positive a and infinity norm = 1
+
+ double infnorm = fabs(a);
+ if ( infnorm < fabs(b) ) infnorm = fabs(b);
+ if ( infnorm < fabs(c) ) infnorm = fabs(c);
+ if ( infnorm < fabs(d) ) infnorm = fabs(d);
+ if ( a < 0 ) infnorm = -infnorm;
+ a /= infnorm;
+ b /= infnorm;
+ c /= infnorm;
+ d /= infnorm;
+
+ const double small = 1e-7;
+ valid = false;
+ if ( fabs(a) < small )
+ {
+ if ( fabs(b) < small )
+ {
+ if ( fabs(c) < small )
+ { // degree = 0;
+ numroots = 0;
+ return 0.0;
+ }
+ // degree = 1
+ double rootval = -d/c;
+ numroots = 1;
+ if ( rootval < xmin || xmax < rootval ) numroots--;
+ if ( root > numroots ) return 0.0;
+ valid = true;
+ return rootval;
+ }
+ // degree = 2
+ if ( b < 0 ) { b = -b; c = -c; d = -d; }
+ double discrim = c*c - 4*b*d;
+ numroots = 2;
+ if ( discrim < 0 )
+ {
+ numroots = 0;
+ return 0.0;
+ }
+ discrim = sqrt(discrim)/(2*fabs(b));
+ double rootmiddle = -c/(2*b);
+ if ( rootmiddle - discrim < xmin ) numroots--;
+ if ( rootmiddle + discrim > xmax ) numroots--;
+ if ( rootmiddle + discrim < xmin ) numroots--;
+ if ( rootmiddle - discrim > xmax ) numroots--;
+ if ( root > numroots ) return 0.0;
+ valid = true;
+ if ( root == 2 || rootmiddle - discrim < xmin ) return rootmiddle + discrim;
+ return rootmiddle - discrim;
+ }
+
+ if ( xmin < -1e8 || xmax > 1e8 )
+ {
+
+ // compute a bound for all the real roots:
+
+ xmax = fabs(d/a);
+ if ( fabs(c/a) + 1 > xmax ) xmax = fabs(c/a) + 1;
+ if ( fabs(b/a) + 1 > xmax ) xmax = fabs(b/a) + 1;
+ xmin = -xmax;
+ }
+
+ // 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);
+
+ int varbottom = calcCubicVariations (xmin, a, b, c, d, p1a, p1b, p0a);
+ int vartop = calcCubicVariations (xmax, a, b, c, d, p1a, p1b, p0a);
+ numroots = vartop - varbottom;
+ valid = false;
+ if (root <= varbottom || root > vartop ) return 0.0;
+
+ valid = true;
+
+ // now use bisection to separate the required root
+ double dx = (xmax - xmin)/2;
+ while ( vartop - varbottom > 1 )
+ {
+ if ( fabs( dx ) < 1e-8 ) return (xmin + xmax)/2;
+ double xmiddle = xmin + dx;
+ int varmiddle = calcCubicVariations (xmiddle, a, b, c, d, p1a, p1b, p0a);
+ if ( varmiddle < root ) // I am below
+ {
+ xmin = xmiddle;
+ varbottom = varmiddle;
+ } else {
+ xmax = xmiddle;
+ vartop = varmiddle;
+ }
+ dx /= 2;
+ }
+
+ /*
+ * now [xmin, xmax] enclose a single root, try using Newton
+ */
+ if ( vartop - varbottom == 1 )
+ {
+ double fval1 = a; // double check...
+ double fval2 = a;
+ fval1 = b + xmin*fval1;
+ fval2 = b + xmax*fval2;
+ fval1 = c + xmin*fval1;
+ fval2 = c + xmax*fval2;
+ fval1 = d + xmin*fval1;
+ fval2 = d + xmax*fval2;
+ assert ( fval1 * fval2 <= 0 );
+ return calcCubicRootwithNewton ( xmin, xmax, a, b, c, d, 1e-8 );
+ }
+ else // probably a double root here!
+ return ( xmin + xmax )/2;
+}
+
+/*
+ * computation of the number of sign changes in the sturm sequence for
+ * a third degree polynomial at x. This number counts the number of
+ * roots of the polynomial on the left of point x.
+ *
+ * a, b, c, d: coefficients of the third degree polynomial (a*x^3 + ...)
+ *
+ * the second degree polynomial in the sturm sequence is just minus the
+ * derivative, so we don't need to compute it.
+ *
+ * p1a*x + p1b: is the third (first degree) polynomial in the sturm sequence.
+ *
+ * p0a: is the (constant) fourth polynomial of the sturm sequence.
+ */
+
+int calcCubicVariations (double x, double a, double b, double c,
+ double d, double p1a, double p1b, double p0a)
+{
+ double fval, fpval;
+ fval = fpval = a;
+ fval = b + x*fval;
+ fpval = fval + x*fpval;
+ fval = c + x*fval;
+ fpval = fval + x*fpval;
+ fval = d + x*fval;
+
+ double f1val = p1a*x + p1b;
+
+ bool f3pos = fval >= 0;
+ bool f2pos = fpval <= 0;
+ bool f1pos = f1val >= 0;
+ bool f0pos = p0a >= 0;
+
+ int variations = 0;
+ if ( f3pos != f2pos ) variations++;
+ if ( f2pos != f1pos ) variations++;
+ if ( f1pos != f0pos ) variations++;
+ return variations;
+}
+
+/*
+ * use newton to solve a third degree equation with already isolated
+ * root
+ */
+
+inline void calcCubicDerivatives ( double x, double a, double b, double c,
+ double d, double& fval, double& fpval, double& fppval )
+{
+ fval = fpval = fppval = a;
+ fval = b + x*fval;
+ fpval = fval + x*fpval;
+ fppval = fpval + x*fppval; // this is really half the second derivative
+ fval = c + x*fval;
+ fpval = fval + x*fpval;
+ fval = d + x*fval;
+}
+
+double calcCubicRootwithNewton ( double xmin, double xmax, double a,
+ double b, double c, double d, double tol )
+{
+ double fval, fpval, fppval;
+
+ double fval1, fval2, fpval1, fpval2, fppval1, fppval2;
+ calcCubicDerivatives ( xmin, a, b, c, d, fval1, fpval1, fppval1 );
+ calcCubicDerivatives ( xmax, a, b, c, d, fval2, fpval2, fppval2 );
+ assert ( fval1 * fval2 <= 0 );
+
+ assert ( xmax > xmin );
+ while ( xmax - xmin > tol )
+ {
+ // compute the values of function, derivative and second derivative:
+ assert ( fval1 * fval2 <= 0 );
+ if ( fppval1 * fppval2 < 0 || fpval1 * fpval2 < 0 )
+ {
+ double xmiddle = (xmin + xmax)/2;
+ calcCubicDerivatives ( xmiddle, a, b, c, d, fval, fpval, fppval );
+ if ( fval1*fval <= 0 )
+ {
+ xmax = xmiddle;
+ fval2 = fval;
+ fpval2 = fpval;
+ fppval2 = fppval;
+ } else {
+ xmin = xmiddle;
+ fval1 = fval;
+ fpval1 = fpval;
+ fppval1 = fppval;
+ }
+ } else
+ {
+ // now we have first and second derivative of constant sign, we
+ // can start with Newton from the Fourier point.
+ double x = xmin;
+ if ( fval2*fppval2 > 0 ) x = xmax;
+ double p = 1.0;
+ int iterations = 0;
+ while ( fabs(p) > tol && iterations++ < 100 )
+ {
+ calcCubicDerivatives ( x, a, b, c, d, fval, fpval, fppval );
+ p = fval/fpval;
+ x -= p;
+ }
+ if( iterations >= 100 )
+ {
+ // Newton scheme did not converge..
+ // we should end up with an invalid Coordinate
+ return double_inf;
+ };
+ return x;
+ }
+ }
+
+ // we cannot apply Newton, (perhaps we are at an inflection point)
+
+ return (xmin + xmax)/2;
+}
+
+/*
+ * This function computes the LU factorization of a mxn matrix, with
+ * m typically less then n. This is done with complete pivoting; the
+ * exchanges in columns are recorded in the integer vector "exchange"
+ */
+bool GaussianElimination( double *matrix[], int numrows,
+ int numcols, int exchange[] )
+{
+ // start gaussian elimination
+ for ( int k = 0; k < numrows; ++k )
+ {
+ // ricerca elemento di modulo massimo
+ double maxval = -double_inf;
+ int imax = k;
+ int jmax = k;
+ for( int i = k; i < numrows; ++i )
+ {
+ for( int j = k; j < numcols; ++j )
+ {
+ if (fabs(matrix[i][j]) > maxval)
+ {
+ maxval = fabs(matrix[i][j]);
+ imax = i;
+ jmax = j;
+ }
+ }
+ }
+
+ // row exchange
+ if ( imax != k )
+ for( int j = k; j < numcols; ++j )
+ {
+ double t = matrix[k][j];
+ matrix[k][j] = matrix[imax][j];
+ matrix[imax][j] = t;
+ }
+
+ // column exchange
+ if ( jmax != k )
+ for( int i = 0; i < numrows; ++i )
+ {
+ double t = matrix[i][k];
+ matrix[i][k] = matrix[i][jmax];
+ matrix[i][jmax] = t;
+ }
+
+ // remember this column exchange at step k
+ exchange[k] = jmax;
+
+ // we can't usefully eliminate a singular matrix..
+ if ( maxval == 0. ) return false;
+
+ // ciclo sulle righe
+ for( int i = k+1; i < numrows; ++i)
+ {
+ double mik = matrix[i][k]/matrix[k][k];
+ matrix[i][k] = mik; //ricorda il moltiplicatore... (not necessary)
+ // ciclo sulle colonne
+ for( int j = k+1; j < numcols; ++j )
+ {
+ matrix[i][j] -= mik*matrix[k][j];
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * solve an undetermined homogeneous triangular system. the matrix is nonzero
+ * on its diagonal. The last unknown(s) are chosen to be 1. The
+ * vector "exchange" contains exchanges to be performed on the
+ * final solution components.
+ */
+
+void BackwardSubstitution( double *matrix[], int numrows, int numcols,
+ int exchange[], double solution[] )
+{
+ // the system is homogeneous and underdetermined, the last unknown(s)
+ // are chosen = 1
+ for ( int j = numrows; j < numcols; ++j )
+ {
+ solution[j] = 1.0; // other choices are possible here
+ };
+
+ for( int k = numrows - 1; k >= 0; --k )
+ {
+ // backward substitution
+ solution[k] = 0.0;
+ for ( int j = k+1; j < numcols; ++j)
+ {
+ solution[k] -= matrix[k][j]*solution[j];
+ }
+ solution[k] /= matrix[k][k];
+ }
+
+ // ultima fase: riordinamento incognite
+
+ for( int k = numrows - 1; k >= 0; --k )
+ {
+ int jmax = exchange[k];
+ double t = solution[k];
+ solution[k] = solution[jmax];
+ solution[jmax] = t;
+ }
+}
+
+bool Invert3by3matrix ( const double m[3][3], double inv[3][3] )
+{
+ double det = m[0][0]*(m[1][1]*m[2][2] - m[1][2]*m[2][1]) -
+ m[0][1]*(m[1][0]*m[2][2] - m[1][2]*m[2][0]) +
+ m[0][2]*(m[1][0]*m[2][1] - m[1][1]*m[2][0]);
+ if (det == 0) return false;
+
+ for (int i=0; i < 3; i++)
+ {
+ for (int j=0; j < 3; j++)
+ {
+ int i1 = (i+1)%3;
+ int i2 = (i+2)%3;
+ int j1 = (j+1)%3;
+ int j2 = (j+2)%3;
+ inv[j][i] = (m[i1][j1]*m[i2][j2] - m[i1][j2]*m[i2][j1])/det;
+ }
+ }
+ return true;
+}
diff --git a/kig/misc/kignumerics.h b/kig/misc/kignumerics.h
new file mode 100644
index 00000000..7beef59f
--- /dev/null
+++ b/kig/misc/kignumerics.h
@@ -0,0 +1,47 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 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_MISC_KIGNUMERICS_H
+#define KIG_MISC_KIGNUMERICS_H
+
+#include <cmath>
+
+double calcCubicRoot ( double xmin, double xmax, double a,
+ double b, double c, double d, int root, bool& valid, int& numroots );
+
+int calcCubicVariations (double x, double a, double b, double c,
+ double d, double p1a, double p1b, double p0a);
+
+double calcCubicRootwithNewton ( double ymin, double ymax, double a,
+ double b, double c, double d, double tol );
+
+/**
+ * Gaussian Elimination. We return false if the matrix is singular,
+ * and can't be usefully eliminated..
+ */
+bool GaussianElimination( double *matrix[], int numrows, int numcols,
+ int scambio[] );
+
+void BackwardSubstitution( double *matrix[], int numrows, int numcols,
+ int scambio[], double solution[] );
+
+bool Invert3by3matrix ( const double m[3][3], double inv[3][3] );
+
+#endif // KIG_MISC_KIGNUMERICS_H
diff --git a/kig/misc/kigpainter.cpp b/kig/misc/kigpainter.cpp
new file mode 100644
index 00000000..e2b2f440
--- /dev/null
+++ b/kig/misc/kigpainter.cpp
@@ -0,0 +1,953 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002-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 "kigpainter.h"
+
+#include "../kig/kig_view.h"
+#include "../kig/kig_document.h"
+#include "../misc/goniometry.h"
+#include "../objects/object_holder.h"
+#include "../objects/curve_imp.h"
+#include "../objects/point_imp.h"
+#include "object_hierarchy.h"
+#include "common.h"
+#include "conic-common.h"
+#include "cubic-common.h"
+#include "coordinate_system.h"
+
+#include <qpen.h>
+
+#include <cmath>
+#include <stack>
+#include <functional>
+#include <algorithm>
+
+KigPainter::KigPainter( const ScreenInfo& si, QPaintDevice* device,
+ const KigDocument& doc, bool no )
+ : mP ( device ),
+ color( Qt::blue ),
+ style( Qt::SolidLine ),
+ pointstyle( 0 ),
+ width( -1 ),
+ brushStyle( Qt::NoBrush ),
+ brushColor( Qt::blue ),
+ mdoc( doc ),
+ msi( si ),
+ mNeedOverlay( no ),
+ overlayenlarge( 0 )
+{
+ mP.setBackgroundColor( Qt::white );
+}
+
+KigPainter::~KigPainter()
+{
+}
+
+void KigPainter::drawRect( const Rect& r )
+{
+ Rect rt = r.normalized();
+ QRect qr = toScreen(rt);
+ qr.normalize();
+ mP.drawRect(qr);
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+}
+
+void KigPainter::drawRect( const QRect& r )
+{
+ mP.drawRect(r);
+ if( mNeedOverlay ) mOverlay.push_back( r );
+}
+
+void KigPainter::drawCircle( const Coordinate& center, const double radius )
+{
+ Coordinate bottomLeft = center - Coordinate(radius, radius);
+ Coordinate topRight = center + Coordinate(radius, radius);
+ Rect r( bottomLeft, topRight );
+ QRect qr = toScreen( r );
+ mP.drawEllipse ( qr );
+ if( mNeedOverlay ) circleOverlay( center, radius );
+}
+
+void KigPainter::drawSegment( const Coordinate& from, const Coordinate& to )
+{
+ QPoint tF = toScreen(from), tT = toScreen(to);
+ mP.drawLine( tF, tT );
+ if( mNeedOverlay ) segmentOverlay( from, to );
+}
+
+void KigPainter::drawFatPoint( const Coordinate& p )
+{
+ int twidth = width == -1 ? 5 : width;
+ mP.setPen( QPen( color, 1, style ) );
+ switch ( pointstyle )
+ {
+ case 0:
+ {
+ double radius = twidth * pixelWidth();
+ setBrushStyle( Qt::SolidPattern );
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect qr = toScreen( r );
+ mP.drawEllipse( qr );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ case 1:
+ {
+ double radius = twidth * pixelWidth();
+ setBrushStyle( Qt::NoBrush );
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect qr = toScreen( r );
+ mP.drawEllipse( qr );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ case 2:
+ {
+ double radius = twidth * pixelWidth();
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect qr = toScreen( r );
+ mP.drawRect( qr );
+ mP.fillRect( qr, QBrush( color, Qt::SolidPattern ) );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ case 3:
+ {
+ double radius = twidth * pixelWidth();
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect qr = toScreen( r );
+ mP.drawRect( qr );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ case 4:
+ {
+ double radius = twidth * pixelWidth();
+ Coordinate rad( radius, radius );
+ rad /= 2;
+ Coordinate tl = p - rad;
+ Coordinate br = p + rad;
+ Rect r( tl, br );
+ QRect qr = toScreen( r );
+ mP.setPen( QPen( color, 2 ) );
+ mP.drawLine( qr.topLeft(), qr.bottomRight() );
+ mP.drawLine( qr.topRight(), qr.bottomLeft() );
+ if( mNeedOverlay ) mOverlay.push_back( qr );
+ break;
+ }
+ }
+ mP.setPen( QPen( color, twidth, style ) );
+}
+
+void KigPainter::drawPoint( const Coordinate& p )
+{
+ mP.drawPoint( toScreen(p) );
+ if( mNeedOverlay ) pointOverlay( p );
+}
+
+void KigPainter::drawLine( const Coordinate& p1, const Coordinate& p2 )
+{
+ drawLine( LineData( p1, p2 ) );
+}
+
+void KigPainter::drawText( const Rect p, const QString s, int textFlags, int len )
+{
+ QRect t = toScreen(p);
+ int tf = textFlags;
+ t.moveBy( 2, 2 );
+ t.setWidth( t.width() - 4 );
+ t.setHeight( t.height() - 4 );
+ mP.drawText( t, tf, s, len );
+ if( mNeedOverlay ) textOverlay( t, s, tf, len );
+}
+
+void KigPainter::textOverlay( const QRect& r, const QString s, int textFlags, int len )
+{
+ // kdDebug() << Rect::fromQRect( mP.boundingRect( r, textFlags, s, len ) ) << endl;
+ QRect newr( mP.boundingRect( r, textFlags, s, len ) );
+ newr.setWidth( newr.width() + 4 );
+ newr.setHeight( newr.height() + 4 );
+ mOverlay.push_back( newr );
+}
+
+const Rect KigPainter::boundingRect( const Rect& r, const QString s,
+ int f, int l ) const
+{
+ QRect qr = mP.boundingRect( toScreen( r ), f, s, l );
+ qr.setWidth( qr.width() + 4 );
+ qr.setHeight( qr.height() + 4 );
+ return fromScreen( qr );
+}
+
+void KigPainter::setColor( const QColor& c )
+{
+ color = c;
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+void KigPainter::setStyle( const PenStyle c )
+{
+ style = c;
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+void KigPainter::setWidth( const int c )
+{
+ width = c;
+ if (c > 0) overlayenlarge = c - 1;
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+void KigPainter::setPointStyle( const int p )
+{
+ pointstyle = p;
+}
+
+void KigPainter::setPen( const QPen& p )
+{
+ color = p.color();
+ width = p.width();
+ style = p.style();
+ mP.setPen(p);
+}
+
+void KigPainter::setBrush( const QBrush& b )
+{
+ brushStyle = b.style();
+ brushColor = b.color();
+ mP.setBrush( b );
+}
+
+void KigPainter::setBrushStyle( const BrushStyle c )
+{
+ brushStyle = c;
+ mP.setBrush( QBrush( brushColor, brushStyle ) );
+}
+
+void KigPainter::setBrushColor( const QColor& c )
+{
+ brushColor = c;
+ mP.setBrush( QBrush( brushColor, brushStyle ) );
+}
+
+bool KigPainter::getNightVision( ) const
+{
+ return mdoc.getNightVision();
+}
+
+QColor KigPainter::getColor() const
+{
+ return color;
+}
+
+/*
+static void setContains( QRect& r, const QPoint& p )
+{
+ if ( r.left() > p.x() ) r.setLeft( p.x() );
+ if ( r.right() < p.x() ) r.setRight( p.x() );
+ // this is correct, i think. In qt the bottom has the highest y
+ // coord...
+ if ( r.bottom() > p.y() ) r.setBottom( p.y() );
+ if ( r.top() < p.y() ) r.setTop( p.y() );
+}
+*/
+
+void KigPainter::drawPolygon( const std::vector<QPoint>& pts,
+ bool winding, int index, int npoints )
+{
+ QPen oldpen = mP.pen();
+ QBrush oldbrush = mP.brush();
+ setBrush( QBrush( color, Dense4Pattern ) );
+ setPen( Qt::NoPen );
+ // i know this isn't really fast, but i blame it all on Qt with its
+ // stupid container classes... what's wrong with the STL ?
+ QPointArray t( pts.size() );
+ int c = 0;
+ for( std::vector<QPoint>::const_iterator i = pts.begin(); i != pts.end(); ++i )
+ {
+ t.putPoints( c++, 1, i->x(), i->y() );
+ };
+ mP.drawPolygon( t, winding, index, npoints );
+ setPen( oldpen );
+ setBrush( oldbrush );
+ if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() );
+}
+
+void KigPainter::drawArea( const std::vector<Coordinate>& pts, bool border )
+{
+ QPen oldpen = mP.pen();
+ QBrush oldbrush = mP.brush();
+ setBrush( QBrush( color, SolidPattern ) );
+ if ( border )
+ setPen( QPen( color, width == -1 ? 1 : width ) );
+ else
+ setPen( Qt::NoPen );
+ QPointArray t( pts.size() );
+ int c = 0;
+ for( std::vector<Coordinate>::const_iterator i = pts.begin(); i != pts.end(); ++i )
+ {
+ QPoint p = toScreen( *i );
+ t.putPoints( c++, 1, p.x(), p.y() );
+ }
+ mP.drawPolygon( t );
+ setPen( oldpen );
+ setBrush( oldbrush );
+ if( mNeedOverlay ) mOverlay.push_back( t.boundingRect() );
+}
+
+Rect KigPainter::window()
+{
+ return msi.shownRect();
+}
+
+void KigPainter::circleOverlayRecurse( const Coordinate& centre,
+ double radiussq,
+ const Rect& cr )
+{
+ Rect currentRect = cr.normalized();
+
+ if( !currentRect.intersects( window() ) ) return;
+
+ // this code is an adaptation of Marc Bartsch's code, from KGeo
+ Coordinate tl = currentRect.topLeft();
+ Coordinate br = currentRect.bottomRight();
+ Coordinate tr = currentRect.topRight();
+ Coordinate bl = currentRect.bottomLeft();
+ Coordinate c = currentRect.center();
+
+ // mp: we compute the minimum and maximum distance from the center
+ // of the circle and this rect
+ double distxmin = 0, distxmax = 0, distymin = 0, distymax = 0;
+ if ( centre.x >= tr.x ) distxmin = centre.x - tr.x;
+ if ( centre.x <= bl.x ) distxmin = bl.x - centre.x;
+ if ( centre.y >= tr.y ) distymin = centre.y - tr.y;
+ if ( centre.y <= bl.y ) distymin = bl.y - centre.y;
+ distxmax = fabs(centre.x - c.x) + currentRect.width()/2;
+ distymax = fabs(centre.y - c.y) + currentRect.height()/2;
+ // this should take into account the thickness of the line...
+ distxmin -= pixelWidth();
+ if (distxmin < 0) distxmin = 0;
+ distxmax += pixelWidth();
+ distymin -= pixelWidth();
+ if (distymin < 0) distymin = 0;
+ distymax += pixelWidth();
+ double distminsq = distxmin*distxmin + distymin*distymin;
+ double distmaxsq = distxmax*distxmax + distymax*distymax;
+
+ // if the circle doesn't touch this rect, we return
+ // too far from the centre
+ if (distminsq > radiussq) return;
+
+ // too near to the centre
+ if (distmaxsq < radiussq) return;
+
+ // the rect contains some of the circle
+ // -> if it's small enough, we keep it
+ if( currentRect.width() < overlayRectSize() ) {
+ mOverlay.push_back( toScreenEnlarge( currentRect) );
+ } else {
+ // this func works recursive: we subdivide the current rect, and if
+ // it is of a good size, we keep it, otherwise we handle it again
+ double width = currentRect.width() / 2;
+ double height = currentRect.height() / 2;
+ Rect r1 ( c, -width, -height);
+ r1.normalize();
+ circleOverlayRecurse(centre, radiussq, r1);
+ Rect r2 ( c, width, -height);
+ r2.normalize();
+ circleOverlayRecurse(centre, radiussq, r2);
+ Rect r3 ( c, -width, height);
+ r3.normalize();
+ circleOverlayRecurse(centre, radiussq, r3);
+ Rect r4 ( c, width, height);
+ r4.normalize();
+ circleOverlayRecurse(centre, radiussq, r4);
+ };
+}
+
+void KigPainter::circleOverlay( const Coordinate& centre, double radius )
+{
+ double t = radius + pixelWidth();
+ Coordinate r( t, t );
+ Coordinate bottomLeft = centre - r;
+ Coordinate topRight = centre + r;
+ Rect rect( bottomLeft, topRight );
+ circleOverlayRecurse ( centre , radius*radius, rect );
+}
+
+void KigPainter::segmentOverlay( const Coordinate& p1, const Coordinate& p2 )
+{
+ // this code is based upon what Marc Bartsch wrote for KGeo
+
+ // some stuff we may need:
+ Coordinate p3 = p2 - p1;
+ Rect border = window();
+// double length = p3.length();
+ // mp: using the l-infinity distance is more natural here
+ double length = fabs(p3.x);
+ if ( fabs( p3.y ) > length ) length = fabs( p3.y );
+ if ( length < pixelWidth() )
+ {
+ // hopefully prevent SIGZERO's
+ mOverlay.push_back( toScreen( Rect( p1, p2 ) ) );
+ return;
+ };
+ p3 *= overlayRectSize();
+ p3 /= length;
+
+ int counter = 0;
+
+ Rect r(p1, p2);
+ r.normalize();
+
+ for (;;) {
+ Rect tR( Coordinate( 0, 0 ), overlayRectSize(), overlayRectSize() );
+ Coordinate tP = p1+p3*counter;
+ tR.setCenter(tP);
+ if (!tR.intersects(r))
+ {
+ //kdDebug()<< "stopped after "<< counter << " passes." << endl;
+ break;
+ }
+ if (tR.intersects(border)) mOverlay.push_back( toScreenEnlarge( tR ) );
+ if (++counter > 100)
+ {
+ kdDebug()<< k_funcinfo << "counter got too big :( " << endl;
+ break;
+ }
+ }
+}
+
+double KigPainter::overlayRectSize()
+{
+ return 20 * pixelWidth();
+}
+
+void KigPainter::pointOverlay( const Coordinate& p1 )
+{
+ Rect r( p1, 3*pixelWidth(), 3*pixelWidth());
+ r.setCenter( p1 );
+ mOverlay.push_back( toScreen( r) );
+}
+
+double KigPainter::pixelWidth()
+{
+ return msi.pixelWidth();
+}
+
+void KigPainter::setWholeWinOverlay()
+{
+ mOverlay.clear();
+ mOverlay.push_back( mP.viewport() );
+ // don't accept any more overlay's...
+ mNeedOverlay = false;
+}
+
+QPoint KigPainter::toScreen( const Coordinate p ) const
+{
+ return msi.toScreen( p );
+}
+
+void KigPainter::drawGrid( const CoordinateSystem& c, bool showGrid, bool showAxes )
+{
+ c.drawGrid( *this, showGrid, showAxes );
+ setWholeWinOverlay();
+}
+
+void KigPainter::drawObject( const ObjectHolder* o, bool ss )
+{
+ o->draw( *this, ss );
+}
+
+void KigPainter::drawObjects( const std::vector<ObjectHolder*>& os, bool sel )
+{
+ drawObjects( os.begin(), os.end(), sel );
+}
+
+void KigPainter::drawFilledRect( const QRect& r )
+{
+ QPen pen( Qt::black, 1, Qt::DotLine );
+ setPen( pen );
+ setBrush( QBrush( Qt::cyan, Dense6Pattern ) );
+ drawRect( r.normalize() );
+}
+
+void KigPainter::drawTextStd( const QPoint& p, const QString& s )
+{
+ if ( s.isNull() ) return;
+ // tf = text formatting flags
+ int tf = AlignLeft | AlignTop | DontClip | WordBreak;
+ // we need the rect where we're going to paint text
+ setPen(QPen(Qt::blue, 1, SolidLine));
+ setBrush(Qt::NoBrush);
+ drawText( Rect(
+ msi.fromScreen(p), window().bottomRight()
+ ).normalized(), s, tf );
+
+}
+
+QRect KigPainter::toScreen( const Rect r ) const
+{
+ return msi.toScreen( r );
+}
+
+QRect KigPainter::toScreenEnlarge( const Rect r ) const
+{
+ if ( overlayenlarge == 0 ) return msi.toScreen( r );
+
+ QRect qr = msi.toScreen( r );
+ qr.moveBy ( -overlayenlarge, -overlayenlarge );
+ int w = qr.width();
+ int h = qr.height();
+ qr.setWidth (w + 2*overlayenlarge);
+ qr.setHeight (h + 2*overlayenlarge);
+ return qr;
+}
+
+void KigPainter::drawSimpleText( const Coordinate& c, const QString s )
+{
+ int tf = AlignLeft | AlignTop | DontClip | WordBreak;
+ drawText( c, s, tf);
+}
+
+void KigPainter::drawText( const Coordinate p, const QString s,
+ int textFlags, int len )
+{
+ drawText( Rect( p, mP.window().right(), mP.window().top() ),
+ s, textFlags, len );
+}
+const Rect KigPainter::simpleBoundingRect( const Coordinate& c, const QString s )
+{
+ int tf = AlignLeft | AlignTop | DontClip | WordBreak;
+ return boundingRect( c, s, tf );
+}
+
+const Rect KigPainter::boundingRect( const Coordinate& c, const QString s,
+ int f, int l ) const
+{
+ return boundingRect( Rect( c, mP.window().right(), mP.window().top() ),
+ s, f, l );
+}
+
+Coordinate KigPainter::fromScreen( const QPoint& p ) const
+{
+ return msi.fromScreen( p );
+}
+
+Rect KigPainter::fromScreen( const QRect& r ) const
+{
+ return msi.fromScreen( r );
+}
+
+void KigPainter::drawRay( const Coordinate& a, const Coordinate& b )
+{
+ Coordinate tb = b;
+ calcRayBorderPoints( a, tb, window() );
+ drawSegment( a, tb );
+}
+
+typedef std::pair<double,Coordinate> coordparampair;
+
+struct workitem
+{
+ workitem( coordparampair f, coordparampair s, Rect *o) :
+ first(f), second(s), overlay(o) {}
+ coordparampair first;
+ coordparampair second;
+ Rect *overlay;
+};
+
+void KigPainter::drawLine( const LineData& d )
+{
+ if ( d.a != d.b )
+ {
+ LineData l = calcBorderPoints( d, window() );
+ drawSegment( l.a, l.b );
+ }
+}
+
+void KigPainter::drawSegment( const LineData& d )
+{
+ drawSegment( d.a, d.b );
+}
+
+void KigPainter::drawRay( const LineData& d )
+{
+ drawRay( d.a, d.b );
+}
+
+void KigPainter::drawAngle( const Coordinate& cpoint, const double dstartangle,
+ const double dangle )
+{
+ // convert to 16th of degrees...
+ const int startangle = static_cast<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) );
+ const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) );
+
+ QPoint point = toScreen( cpoint );
+
+// int radius = mP.window().width() / 5;
+ int radius = 50;
+ QRect surroundingRect( 0, 0, radius*2, radius*2 );
+ surroundingRect.moveCenter( point );
+
+ mP.drawArc( surroundingRect, startangle, angle );
+
+ // now for the arrow...
+ QPoint end( static_cast<int>( point.x() + radius * cos( dstartangle + dangle ) ),
+ static_cast<int>( point.y() - radius * sin( dstartangle + dangle ) ) );
+ QPoint vect = (end - point);
+ double vectlen = sqrt( float( vect.x() * vect.x() + vect.y() * vect.y() ) );
+ QPoint orthvect( -vect.y(), vect.x() );
+ vect = vect * 6 / vectlen;
+ orthvect = orthvect * 6 / vectlen;
+
+ QPointArray arrow( 3 );
+ arrow.setPoint( 0, end );
+ arrow.setPoint( 1, end + orthvect + vect );
+ arrow.setPoint( 2, end + orthvect - vect );
+// std::vector<QPoint> arrow;
+// arrow.push_back( end );
+// arrow.push_back( end + orthvect + vect );
+// arrow.push_back( end + orthvect - vect );
+
+ setBrushStyle( Qt::SolidPattern );
+// drawPolygon( arrow );
+ mP.drawPolygon( arrow, false, 0, -1 );
+
+// if ( mNeedOverlay ) mOverlay.push_back( toScreen( r ) );
+ setWholeWinOverlay(); //mp: ugly! why not compute a correct overlay?
+ // mOverlay.push_back( arrow.boundingRect() );
+}
+
+void KigPainter::drawPolygon( const std::vector<Coordinate>& pts,
+ bool winding, int index, int npoints )
+{
+ using namespace std;
+ vector<QPoint> points;
+ for ( uint i = 0; i < pts.size(); ++i )
+ points.push_back( toScreen( pts[i] ) );
+ drawPolygon( points, winding, index, npoints );
+}
+
+void KigPainter::drawVector( const Coordinate& a, const Coordinate& b )
+{
+ // bugfix...
+ if ( a == b ) return;
+ // the segment
+ drawSegment( a, b );
+ // the arrows...
+ Coordinate dir = b - a;
+ Coordinate perp( dir.y, -dir.x );
+ double length = perp.length();
+ perp *= 10* pixelWidth();
+ perp /= length;
+ dir *= 10 * pixelWidth();
+ dir /= length;
+ Coordinate c = b - dir + perp;
+ Coordinate d = b - dir - perp;
+ // draw the arrow lines with a normal style
+ mP.setPen( QPen( color, width == -1 ? 1 : width, Qt::SolidLine ) );
+ drawSegment( b, c );
+ drawSegment( b, d );
+ // setting again the original style
+ mP.setPen( QPen( color, width == -1 ? 1 : width, style ) );
+}
+
+/* *** this function is commented out ***
+inline Coordinate locusGetCoord( double p, const CurveImp* curve, const ObjectHierarchy& h,
+ bool& valid, const KigDocument& doc )
+{
+ Coordinate pt = curve->getPoint( p, valid, doc );
+ if ( ! valid ) return Coordinate();
+ PointImp pimp( pt );
+ Args args;
+ args.push_back( &pimp );
+ std::vector<ObjectImp*> calced = h.calc( args, doc );
+ assert( calced.size() == 1 );
+ ObjectImp* o = calced.front();
+ Coordinate ret;
+ if ( o->inherits( ObjectImp::ID_PointImp ) )
+ {
+ valid = true;
+ ret = static_cast<PointImp*>( o )->coordinate();
+ }
+ else
+ valid = false;
+ delete o;
+ return ret;
+};
+*/
+
+class CurveImpPointCalcer
+{
+ const CurveImp* curve;
+public:
+ CurveImpPointCalcer( const CurveImp* c )
+ : curve( c )
+ {
+ }
+ static const double endinterval;
+ inline const Coordinate getPoint( double param, const KigDocument& d ) const {
+ return curve->getPoint( param, d );
+ }
+};
+
+const double CurveImpPointCalcer::endinterval = 1.;
+
+void KigPainter::drawCurve( const CurveImp* curve )
+{
+ // we manage our own overlay
+ bool tNeedOverlay = mNeedOverlay;
+ mNeedOverlay = false;
+
+ QPen pen = mP.pen();
+
+ // this stack contains pairs of Coordinates ( parameter intervals )
+ // that we still need to process:
+ std::stack<workitem> workstack;
+ // mp: this stack contains all the generated overlays:
+ // the strategy for generating the overlay structure is the same
+ // recursive-like used to draw the segments: a new rectangle is
+ // generated whenever the length of a segment becomes lower than
+ // overlayRectSize(), or if the segment would be drawn anyway
+ // to avoid strange things from happening we impose that the distance
+ // in parameter space be less than a threshold before generating
+ // any overlay.
+ //
+ // The third parameter in workitem is a pointer into a stack of
+ // all generated rectangles (in real coordinate space); if 0
+ // there is no rectangles associated to that segment yet.
+ //
+ // Using the final mOverlay stack would be much more efficient, but
+ // 1. needs transformations into window space
+ // 2. would be more difficult to drop rectangles not intersecting
+ // the window.
+ std::stack<Rect> overlaystack;
+
+ // mp: the original version in which an initial set of 20 intervals
+ // were pushed onto the stack is replaced by a single interval and
+ // by forcing subdivision till h < hmax (with more or less the same
+ // final result).
+ // First push the [0,1] interval into the stack:
+
+ Coordinate coo1 = curve->getPoint( 0., mdoc );
+ Coordinate coo2 = curve->getPoint( 1., mdoc );
+ workstack.push( workitem(
+ coordparampair( 0., coo1 ),
+ coordparampair( 1., coo2 ),
+ 0 ) );
+
+ // maxlength is the square of the maximum size that we allow
+ // between two points..
+ double maxlength = 1.5 * pixelWidth();
+ maxlength *= maxlength;
+ // error squared is required to be less that sigma (half pixel)
+ double sigma = maxlength/4;
+ // distance between two parameter values cannot be too small
+ double hmin = 3e-5;
+ // distance between two parameter values cannot be too large
+ double hmax = 1./40;
+ double hmaxoverlay = 1./8;
+
+ int count = 1; // the number of segments we've already
+ // visited...
+ static const int maxnumberofpoints = 1000;
+
+ const Rect& sr = window();
+
+ // what this algorithm does is approximating the curve with a set of
+ // segments. we don't draw the individual segments, but use
+ // QPainter::drawPolyline() so that the line styles work properly.
+ // Possibly there are performance advantages as well ? this array
+ // is a buffer of the polyline approximation of the part of the
+ // curve that we are currently processing.
+ QPointArray curpolyline( 1000 );
+ int curpolylinenextfree = 0;
+
+ // we don't use recursion, but a stack based approach for efficiency
+ // concerns...
+ while ( ! workstack.empty() && count < maxnumberofpoints )
+ {
+ workitem curitem = workstack.top();
+ workstack.pop();
+ bool curitemok = true;
+ while ( curitemok && count++ < maxnumberofpoints )
+ {
+ double t0 = curitem.first.first;
+ double t1 = curitem.second.first;
+ Coordinate p0 = curitem.first.second;
+ bool valid0 = p0.valid();
+ Coordinate p1 = curitem.second.second;
+ bool valid1 = p1.valid();
+
+ // we take the middle parameter of the two previous points...
+ double t2 = ( t0 + t1 ) / 2;
+ double h = fabs( t1 - t0 ) /2;
+
+ // if exactly one of the two endpoints is invalid, then
+ // we prefer to find an internal value of the parameter
+ // separating valid points from invalid points. We use
+ // a bisection strategy (this is not implemented yet!)
+// if ( ( valid0 && ! valid1 ) || ( valid1 && ! valid0 ) )
+// {
+// while ( h >= hmin )
+// {
+// .......................................
+// }
+// }
+
+ Rect *overlaypt = curitem.overlay;
+ Coordinate p2 = curve->getPoint( t2, mdoc );
+ bool allvalid = p2.valid() && valid0 && valid1;
+ bool dooverlay = ! overlaypt && h < hmaxoverlay && valid0 && valid1
+ && fabs( p0.x - p1.x ) <= overlayRectSize()
+ && fabs( p0.y - p1.y ) <= overlayRectSize();
+ bool addn = sr.contains( p2 ) || h >= hmax;
+ // estimated error between the curve and the segments
+ double errsq = 1e21;
+ if ( allvalid ) errsq = (0.5*p0 + 0.5*p1 - p2).squareLength();
+ errsq /= 4;
+ curitemok = false;
+// bool dodraw = allvalid && h < hmax && ( errsq < sigma || h < hmin );
+ bool dodraw = allvalid && h < hmax && errsq < sigma;
+ if ( tNeedOverlay && ( dooverlay || dodraw ) )
+ {
+ Rect newoverlay( p0, p1 );
+ overlaystack.push( newoverlay );
+ overlaypt = &overlaystack.top();
+ }
+ if ( overlaypt ) overlaypt->setContains( p2 );
+ if ( dodraw )
+ {
+ // draw the two segments
+ QPoint tp0 = toScreen(p0);
+ QPoint tp1 = toScreen(p1);
+ QPoint tp2 = toScreen(p2);
+ if ( curpolylinenextfree > 0 && curpolyline[curpolylinenextfree - 1] != tp1 )
+ {
+ // flush the current part of the curve
+ mP.drawPolyline( curpolyline, 0, curpolylinenextfree );
+ curpolylinenextfree = 0;
+ }
+ if ( curpolylinenextfree == 0 )
+ curpolyline[curpolylinenextfree++] = tp1;
+ curpolyline[curpolylinenextfree++] = tp2;
+ curpolyline[curpolylinenextfree++] = tp0;
+ }
+ else if ( h >= hmin ) // we do not continue to subdivide indefinitely!
+ {
+ // push into stack in order to process both subintervals
+ if ( addn || ( valid0 && sr.contains( p0 ) ) )
+ workstack.push( workitem( curitem.first, coordparampair( t2, p2 ),
+ overlaypt ) );
+ if ( addn || ( valid1 && sr.contains( p1 ) ) )
+ {
+ curitem = workitem( coordparampair( t2, p2 ), curitem.second ,
+ overlaypt );
+ curitemok = true;
+ }
+ }
+ }
+ }
+ // flush the rest of the curve
+ mP.drawPolyline( curpolyline, 0, curpolylinenextfree );
+ curpolylinenextfree = 0;
+
+ if ( ! workstack.empty () )
+ kdDebug() << "Stack not empty in KigPainter::drawCurve!\n" << endl;
+ assert ( tNeedOverlay || overlaystack.empty() );
+ if ( tNeedOverlay )
+ {
+ Rect border = window();
+ while ( ! overlaystack.empty() )
+ {
+ Rect overlay = overlaystack.top();
+ overlaystack.pop();
+ if (overlay.intersects( border ))
+ mOverlay.push_back( toScreenEnlarge( overlay ) );
+ }
+ }
+ mNeedOverlay = tNeedOverlay;
+}
+
+void KigPainter::drawTextFrame( const Rect& frame,
+ const QString& s, bool needframe )
+{
+ QPen oldpen = mP.pen();
+ QBrush oldbrush = mP.brush();
+ if ( needframe )
+ {
+ // inspired upon kgeo, thanks to Marc Bartsch, i've taken some of
+ // his code too..
+ setPen( QPen( Qt::black, 1 ) );
+ setBrush( QBrush( QColor( 255, 255, 222 ) ) );
+ drawRect( frame );
+ setPen( QPen( QColor( 197, 194, 197 ), 1, Qt::SolidLine ) );
+
+ QRect qr = toScreen( frame );
+
+ mP.drawLine( qr.topLeft(), qr.topRight() );
+ mP.drawLine( qr.topLeft(), qr.bottomLeft() );
+ };
+ setPen( oldpen );
+ setBrush( oldbrush );
+ drawText( frame, s, Qt::AlignVCenter | Qt::AlignLeft );
+}
+
+void KigPainter::drawArc( const Coordinate& center, const double radius,
+ const double dstartangle, const double dangle )
+{
+ // convert to 16th of degrees...
+ const int startangle = static_cast<int>( Goniometry::convert( 16 * dstartangle, Goniometry::Rad, Goniometry::Deg ) );
+ const int angle = static_cast<int>( Goniometry::convert( 16 * dangle, Goniometry::Rad, Goniometry::Deg ) );
+
+ if ( angle <= 16 )
+ {
+ Coordinate a = center + radius * Coordinate( cos( dstartangle ), sin( dstartangle ));
+ Coordinate b = center + radius * Coordinate( cos( dstartangle + dangle ), sin( dstartangle + dangle ));
+ drawSegment ( a , b );
+ }
+ else
+ {
+ Rect krect( 0, 0, 2*radius, 2*radius );
+ krect.setCenter( center );
+ QRect rect = toScreen( krect );
+
+ mP.drawArc( rect, startangle, angle );
+ setWholeWinOverlay();
+ }
+}
+
diff --git a/kig/misc/kigpainter.h b/kig/misc/kigpainter.h
new file mode 100644
index 00000000..e7f884f4
--- /dev/null
+++ b/kig/misc/kigpainter.h
@@ -0,0 +1,291 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002-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 KIGPAINTER_H
+#define KIGPAINTER_H
+
+#include "coordinate.h"
+#include "rect.h"
+#include "screeninfo.h"
+
+#include <qpainter.h>
+#include <qcolor.h>
+
+#include <vector>
+
+class KigWidget;
+class QPaintDevice;
+class CoordinateSystem;
+class ObjectHierarchy;
+class ConicPolarData;
+class CubicCartesianData;
+class LineData;
+class CurveImp;
+class KigDocument;
+class ObjectHolder;
+
+/**
+ * KigPainter is an extended QPainter.
+ *
+ * Currently the only difference is that it translates coordinates
+ * from and to the internal coordinates/ the widget coordinates...
+ *
+ * It calls KigWidget::appendOverlay() for all of the places it draws in...
+ */
+class KigPainter
+ : public Qt
+{
+protected:
+ // don't blaim me for this mutable hack. It's TT that hasn't got
+ // its consts correctly...
+ mutable QPainter mP;
+
+ QColor color;
+ PenStyle style;
+ int pointstyle;
+ int width;
+ BrushStyle brushStyle;
+ QColor brushColor;
+
+ const KigDocument& mdoc;
+ ScreenInfo msi;
+
+ bool mNeedOverlay;
+ int overlayenlarge;
+public:
+ /**
+ * construct a new KigPainter:
+ * the ScreenInfo is used to map the document coordinates to the
+ * widget coordinates. This is done transparently to the objects.
+ * needOverlay sets whether we try to remember the places we're
+ * drawing on using the various overlay methods. @see overlay()
+ */
+ KigPainter( const ScreenInfo& r, QPaintDevice* device, const KigDocument& doc,
+ bool needOverlay = true );
+ ~KigPainter();
+
+ /**
+ * what rect are we drawing on ?
+ */
+ Rect window();
+
+ QPoint toScreen( const Coordinate p ) const;
+ QRect toScreen( const Rect r ) const;
+ QRect toScreenEnlarge( const Rect r ) const;
+ Coordinate fromScreen( const QPoint& p ) const;
+ Rect fromScreen( const QRect& r ) const;
+
+ // colors and stuff...
+ void setStyle( const PenStyle c );
+ void setColor( const QColor& c );
+ /**
+ * setting this to -1 means to use the default width for the object
+ * being drawn.. a point -> 5, other objects -> 1
+ */
+ void setWidth( const int c );
+ void setPointStyle( const int p );
+ void setPen( const QPen& p );
+ void setBrushStyle( const BrushStyle c );
+ void setBrush( const QBrush& b );
+ void setBrushColor( const QColor& c );
+
+ QColor getColor() const;
+ bool getNightVision( ) const;
+
+ double pixelWidth();
+
+ /**
+ * this is called by some drawing functions that modify the 'entire'
+ * screen, i.e. they do so many changes that it's easier to just
+ * update the entire screen, or else i have been to lazy to
+ * implement an appropriate overlay function ;)
+ * it clears mOverlay, and sets it to the entire widget...
+ */
+ void setWholeWinOverlay();
+
+ /**
+ * draw an object ( by calling its draw function.. )
+ */
+ void drawObject( const ObjectHolder* o, bool sel );
+ void drawObjects( const std::vector<ObjectHolder*>& os, bool sel );
+ template<typename iter>
+ void drawObjects( iter begin, iter end, bool sel )
+ {
+ for ( ; begin != end; ++begin )
+ drawObject( *begin, sel );
+ }
+
+ /**
+ * draw a generic curve...
+ */
+ void drawCurve( const CurveImp* curve );
+
+ /**
+ * draws text in a standard manner, convenience function...
+ */
+ void drawTextStd( const QPoint& p, const QString& s );
+
+ /**
+ * draws a rect filled up with a pattern of cyan lines...
+ */
+ void drawFilledRect( const QRect& );
+
+ /**
+ * draw a rect..
+ */
+ void drawRect( const Rect& r );
+
+ /**
+ * overload, mainly for drawing the selection rectangle by
+ * KigWidget...
+ */
+ void drawRect( const QRect& r );
+
+ /**
+ * draw a circle...
+ */
+ void drawCircle( const Coordinate& center, const double radius );
+
+ /**
+ * draw a segment...
+ */
+ void drawSegment ( const Coordinate& from, const Coordinate& to );
+ void drawSegment( const LineData& d );
+
+ /**
+ * draw a ray...
+ */
+ void drawRay( const Coordinate& a, const Coordinate& b );
+ void drawRay( const LineData& d );
+
+ /**
+ * draw a line...
+ */
+ void drawLine ( const Coordinate& p1, const Coordinate& p2 );
+ void drawLine( const LineData& d );
+
+ /**
+ * draw a point... This means a single point, as in
+ * QPainter::drawPoint(), unlike drawFatPoint()...
+ */
+ void drawPoint( const Coordinate& p );
+
+ /**
+ * draw a thick point.. This is what the user sees when he draws a
+ * point. In fact it isn't a point, but a filled circle of a
+ * certain radius...
+ */
+ void drawFatPoint( const Coordinate& p );
+
+ /**
+ * draw a polygon defined by the points in pts...
+ */
+ void drawPolygon( const std::vector<QPoint>& pts, bool winding = false, int index = 0, int npoints = -1 );
+ void drawPolygon( const std::vector<Coordinate>& pts, bool winding = false, int index = 0, int npoints = -1 );
+
+ /**
+ * draw an area defined by the points in pts filled with the set
+ * color...
+ */
+ void drawArea( const std::vector<Coordinate>& pts, bool border = true );
+
+ /**
+ * draw the angle with center point, with size angle, starting
+ * at the angle startAngle.. Angles should be in radians.
+ */
+ void drawAngle( const Coordinate& point, const double startangle,
+ const double angle );
+
+ /**
+ * draw the arc ( a part of a circle ), of the circle with center
+ * center, radius radius, with size angle, starting at the angle
+ * startAngle.. Angles should be in radians..
+ */
+ void drawArc( const Coordinate& center, const double radius,
+ const double startangle, const double angle );
+
+ /**
+ * draw a vector ( with an arrow etc. )
+ */
+
+ void drawVector( const Coordinate& a, const Coordinate& b );
+
+ /**
+ * draw text...
+ * \see QPainter::drawText()
+ */
+ void drawText( const Rect r, const QString s, int textFlags = 0,
+ int len = -1);
+ void drawText( const Coordinate p, const QString s,
+ int textFlags = 0, int len = -1);
+
+ void drawSimpleText( const Coordinate& c, const QString s );
+ void drawTextFrame( const Rect& frame, const QString& s, bool needframe );
+
+ const Rect boundingRect( const Rect& r, const QString s,
+ int f = 0, int l = -1 ) const;
+
+ const Rect boundingRect( const Coordinate& c, const QString s,
+ int f = 0, int l = -1 ) const;
+
+ const Rect simpleBoundingRect( const Coordinate& c, const QString s );
+
+ void drawGrid( const CoordinateSystem& c, bool showGrid = true, bool showAxes = true );
+
+ const std::vector<QRect>& overlay() { return mOverlay; }
+
+protected:
+ /**
+ * adds a number of rects to mOverlay so that the rects entirely
+ * contain the circle...
+ * \see mOverlay
+ */
+ void circleOverlay( const Coordinate& centre, double radius );
+ // this works recursively...
+ void circleOverlayRecurse( const Coordinate& centre, double radius, const Rect& currentRect );
+
+ /**
+ * adds some rects to mOverlay, so that they cover the segment p1p2
+ * completely...
+ * \see Object::getOverlay()
+ */
+ void segmentOverlay( const Coordinate& p1, const Coordinate& p2 );
+
+ /**
+ * ...
+ */
+ void pointOverlay( const Coordinate& p1 );
+
+ /**
+ * ...
+ * \see drawText(), QPainter::boundingRect()
+ */
+ void textOverlay( const QRect& r, const QString s, int textFlags, int len );
+
+ /**
+ * the size we want the overlay rects to be...
+ */
+ double overlayRectSize();
+
+ std::vector<QRect> mOverlay;
+};
+
+#endif
diff --git a/kig/misc/kigtransform.cpp b/kig/misc/kigtransform.cpp
new file mode 100644
index 00000000..a297ed6e
--- /dev/null
+++ b/kig/misc/kigtransform.cpp
@@ -0,0 +1,810 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it>
+ 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 "kigtransform.h"
+
+#include "kignumerics.h"
+#include "common.h"
+
+#include <cmath>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+// Transformation getProjectiveTransformation ( int argsnum,
+// Object *transforms[], bool& valid )
+// {
+// valid = true;
+
+// assert ( argsnum > 0 );
+// int argn = 0;
+// Object* transform = transforms[argn++];
+// if (transform->toVector())
+// {
+// // translation
+// assert (argn == argsnum);
+// Vector* v = transform->toVector();
+// Coordinate dir = v->getDir();
+// return Transformation::translation( dir );
+// }
+
+// if (transform->toPoint())
+// {
+// // point reflection ( or is point symmetry the correct term ? )
+// assert (argn == argsnum);
+// Point* p = transform->toPoint();
+// return Transformation::pointReflection( p->getCoord() );
+// }
+
+// if (transform->toLine())
+// {
+// // line reflection ( or is it line symmetry ? )
+// Line* line = transform->toLine();
+// assert (argn == argsnum);
+// return Transformation::lineReflection( line->lineData() );
+// }
+
+// if (transform->toRay())
+// {
+// // domi: sorry, but what kind of transformation does this do ?
+// // i'm guessing it's some sort of rotation, but i'm not
+// // really sure..
+// Ray* line = transform->toRay();
+// Coordinate d = line->direction().normalize();
+// Coordinate t = line->p1();
+// double alpha = 0.1*M_PI/2; // a small angle for the DrawPrelim
+// if (argn < argsnum)
+// {
+// Angle* angle = transforms[argn++]->toAngle();
+// alpha = angle->size();
+// }
+// assert (argn == argsnum);
+// return Transformation::projectiveRotation( alpha, d, t );
+// }
+
+// if (transform->toAngle())
+// {
+// // rotation..
+// Coordinate center = Coordinate( 0., 0. );
+// if (argn < argsnum)
+// {
+// Object* arg = transforms[argn++];
+// assert (arg->toPoint());
+// center = arg->toPoint()->getCoord();
+// }
+// Angle* angle = transform->toAngle();
+// double alpha = angle->size();
+
+// assert (argn == argsnum);
+
+// return Transformation::rotation( alpha, center );
+// }
+
+// if (transform->toSegment()) // this is a scaling
+// {
+// Segment* segment = transform->toSegment();
+// Coordinate p = segment->p2() - segment->p1();
+// double s = p.length();
+// if (argn < argsnum)
+// {
+// Object* arg = transforms[argn++];
+// if (arg->toSegment()) // s is the length of the first segment
+// // divided by the length of the second..
+// {
+// Segment* segment = arg->toSegment();
+// Coordinate p = segment->p2() - segment->p1();
+// s /= p.length();
+// if (argn < argsnum) arg = transforms[argn++];
+// }
+// if (arg->toPoint()) // scaling w.r. to a point
+// {
+// Point* p = arg->toPoint();
+// assert (argn == argsnum);
+// return Transformation::scaling( s, p->getCoord() );
+// }
+// if (arg->toLine()) // scaling w.r. to a line
+// {
+// Line* line = arg->toLine();
+// assert( argn == argsnum );
+// return Transformation::scaling( s, line->lineData() );
+// }
+// }
+
+// return Transformation::scaling( s, Coordinate( 0., 0. ) );
+// }
+
+// valid = false;
+// return Transformation::identity();
+// }
+
+// tWantArgsResult WantTransformation ( Objects::const_iterator& i,
+// const Objects& os )
+// {
+// Object* o = *i++;
+// if (o->toVector()) return tComplete;
+// if (o->toPoint()) return tComplete;
+// if (o->toLine()) return tComplete;
+// if (o->toAngle())
+// {
+// if ( i == os.end() ) return tNotComplete;
+// o = *i++;
+// if (o->toPoint()) return tComplete;
+// if (o->toLine()) return tComplete;
+// return tNotGood;
+// }
+// if (o->toRay())
+// {
+// if ( i == os.end() ) return tNotComplete;
+// o = *i++;
+// if (o->toAngle()) return tComplete;
+// return tNotGood;
+// }
+// if (o->toSegment())
+// {
+// if ( i == os.end() ) return tNotComplete;
+// o = *i++;
+// if ( o->toSegment() )
+// {
+// if ( i == os.end() ) return tNotComplete;
+// o = *i++;
+// }
+// if (o->toPoint()) return tComplete;
+// if (o->toLine()) return tComplete;
+// return tNotGood;
+// }
+// return tNotGood;
+// }
+
+// QString getTransformMessage ( const Objects& os, const Object *o )
+// {
+// int size = os.size();
+// switch (size)
+// {
+// case 1:
+// if (o->toVector()) return i18n("translate by this vector");
+// if (o->toPoint()) return i18n("central symmetry by this point. You"
+// " can obtain different transformations by clicking on lines (mirror),"
+// " vectors (translation), angles (rotation), segments (scaling) and rays"
+// " (projective transformation)");
+// if (o->toLine()) return i18n("reflect in this line");
+// if (o->toAngle()) return i18n("rotate by this angle");
+// if (o->toSegment()) return i18n("scale using the length of this vector");
+// if (o->toRay()) return i18n("a projective transformation in the direction"
+// " indicated by this ray, it is a rotation in the projective plane"
+// " about a point at infinity");
+// return i18n("Use this transformation");
+
+// case 2: // we ask for the first parameter of the transformation
+// case 3:
+// if (os[1]->toAngle())
+// {
+// if (o->toPoint()) return i18n("about this point");
+// assert (false);
+// }
+// if (os[1]->toSegment())
+// {
+// if (o->toSegment())
+// return i18n("relative to the length of this other vector");
+// if (o->toPoint())
+// return i18n("about this point");
+// if (o->toLine())
+// return i18n("about this line");
+// }
+// if (os[1]->toRay())
+// {
+// if (o->toAngle()) return i18n("rotate by this angle in the projective"
+// " plane");
+// }
+// return i18n("Using this object");
+
+// default: assert(false);
+// }
+
+// return i18n("Use this transformation");
+// }
+
+
+/* domi: not necessary anymore, homotheticness is kept as a bool in
+ * the Transformation class..
+ * keeping it here, in case a need for it arises some time in the
+ * future...
+ * decide if the given transformation is homotetic
+ */
+// bool isHomoteticTransformation ( double transformation[3][3] )
+// {
+// if (transformation[0][1] != 0 || transformation[0][2] != 0) return (false);
+// // test the orthogonality of the matrix 2x2 of second and third rows
+// // and columns
+// if (fabs(fabs(transformation[1][1]) -
+// fabs(transformation[2][2])) > 1e-8) return (false);
+// if (fabs(fabs(transformation[1][2]) -
+// fabs(transformation[2][1])) > 1e-8) return (false);
+
+// return transformation[1][2] * transformation[2][1] *
+// transformation[1][1] * transformation[2][2] <= 0.;
+// }
+
+const Transformation Transformation::identity()
+{
+ Transformation ret;
+ for ( int i = 0; i < 3; ++i )
+ for ( int j = 0; j < 3; ++j )
+ ret.mdata[i][j] = ( i == j ? 1 : 0 );
+ ret.mIsHomothety = ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation Transformation::scalingOverPoint( double factor, const Coordinate& center )
+{
+ Transformation ret;
+ for ( int i = 0; i < 3; ++i )
+ for ( int j = 0; j < 3; ++j )
+ ret.mdata[i][j] = ( i == j ? factor : 0 );
+ ret.mdata[0][0] = 1;
+ ret.mdata[1][0] = center.x - factor * center.x;
+ ret.mdata[2][0] = center.y - factor * center.y;
+ ret.mIsHomothety = ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation Transformation::translation( const Coordinate& c )
+{
+ Transformation ret = identity();
+ ret.mdata[1][0] = c.x;
+ ret.mdata[2][0] = c.y;
+
+ // this is already set in the identity() constructor, but just for
+ // clarity..
+ ret.mIsHomothety = ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation Transformation::pointReflection( const Coordinate& c )
+{
+ Transformation ret = scalingOverPoint( -1, c );
+ ret.mIsHomothety = ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation operator*( const Transformation& a, const Transformation& b )
+{
+ // just multiply the two matrices..
+ Transformation ret;
+
+ for ( int i = 0; i < 3; ++i )
+ for ( int j = 0; j < 3; ++j )
+ {
+ ret.mdata[i][j] = 0;
+ for ( int k = 0; k < 3; ++k )
+ ret.mdata[i][j] += a.mdata[i][k] * b.mdata[k][j];
+ };
+
+ // combination of two homotheties is a homothety..
+
+ ret.mIsHomothety = a.mIsHomothety && b.mIsHomothety;
+
+ // combination of two affinities is affine..
+
+ ret.mIsAffine = a.mIsAffine && b.mIsAffine;
+
+ return ret;
+}
+
+const Transformation Transformation::lineReflection( const LineData& l )
+{
+ Transformation ret = scalingOverLine( -1, l );
+ // a reflection is a homothety...
+ ret.mIsHomothety = ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation Transformation::scalingOverLine( double factor, const LineData& l )
+{
+ Transformation ret = identity();
+
+ Coordinate a = l.a;
+ Coordinate d = l.dir();
+ double dirnormsq = d.squareLength();
+ ret.mdata[1][1] = (d.x*d.x + factor*d.y*d.y)/dirnormsq;
+ ret.mdata[2][2] = (d.y*d.y + factor*d.x*d.x)/dirnormsq;
+ ret.mdata[1][2] = ret.mdata[2][1] = (d.x*d.y - factor*d.x*d.y)/dirnormsq;
+
+ ret.mdata[1][0] = a.x - ret.mdata[1][1]*a.x - ret.mdata[1][2]*a.y;
+ ret.mdata[2][0] = a.y - ret.mdata[2][1]*a.x - ret.mdata[2][2]*a.y;
+
+ // domi: is 1e-8 a good value ?
+ ret.mIsHomothety = ( fabs( factor - 1 ) < 1e-8 || fabs ( factor + 1 ) < 1e-8 );
+ ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation Transformation::harmonicHomology(
+ const Coordinate& center, const LineData& axis )
+{
+ // this is a well known projective transformation. We find it by first
+ // computing the homogeneous equation of the axis ax + by + cz = 0
+ // then a straightforward computation shows that the 3x3 matrix describing
+ // the transformation is of the form:
+ //
+ // (r . C) Id - 2 (C tensor r)
+ //
+ // where r = [c, a, b], C = [1, Cx, Cy], Cx and Cy are the coordinates of
+ // the center, '.' denotes the scalar product, Id is the identity matrix,
+ // 'tensor' is the tensor product producing a 3x3 matrix.
+ //
+ // note: here we decide to use coordinate '0' in place of the third coordinate
+ // in homogeneous notation; e.g. C = [1, cx, cy]
+
+ Coordinate pointa = axis.a;
+ Coordinate pointb = axis.b;
+
+ double a = pointa.y - pointb.y;
+ double b = pointb.x - pointa.x;
+ double c = pointa.x*pointb.y - pointa.y*pointb.x;
+
+ double cx = center.x;
+ double cy = center.y;
+
+ double scalprod = a*cx + b*cy + c;
+ scalprod *= 0.5;
+ Transformation ret;
+
+ ret.mdata[0][0] = c - scalprod;
+ ret.mdata[0][1] = a;
+ ret.mdata[0][2] = b;
+
+ ret.mdata[1][0] = c*cx;
+ ret.mdata[1][1] = a*cx - scalprod;
+ ret.mdata[1][2] = b*cx;
+
+ ret.mdata[2][0] = c*cy;
+ ret.mdata[2][1] = a*cy;
+ ret.mdata[2][2] = b*cy - scalprod;
+
+ ret.mIsHomothety = ret.mIsAffine = false;
+ return ret;
+}
+
+const Transformation Transformation::affinityGI3P(
+ const std::vector<Coordinate>& FromPoints,
+ const std::vector<Coordinate>& ToPoints,
+ bool& valid )
+{
+ // construct the (generically) unique affinity that transforms 3 given
+ // point into 3 other given points; i.e. it depends on the coordinates of
+ // a total of 6 points. This actually amounts in solving a 6x6 linear
+ // system to find the entries of a 2x2 linear transformation matrix T
+ // and of a translation vector t.
+ // If Pi denotes one of the starting points and Qi the corresponding
+ // final position we actually have to solve: Qi = t + T Pi, for i=1,2,3
+ // (each one is a vector equation, so that it really gives 2 equations).
+ // In our context T and t are used to build a 3x3 projective transformation
+ // as follows:
+ //
+ // [ 1 0 0 ]
+ // [ t1 T11 T12 ]
+ // [ t2 T21 T22 ]
+ //
+ // In order to take advantage of the two functions "GaussianElimination"
+ // and "BackwardSubstitution", which are specifically aimed at solving
+ // homogeneous underdetermined linear systems, we just add a further
+ // unknown m and solve for t + T Pi - m Qi = 0. Since our functions
+ // returns a nonzero solution we shall have a nonzero 'm' in the end and
+ // can build the 3x3 matrix as follows:
+ //
+ // [ m 0 0 ]
+ // [ t1 T11 T12 ]
+ // [ t2 T21 T22 ]
+ //
+ // we order the unknowns as follows: m, t1, t2, T11, T12, T21, T22
+
+ double row0[7], row1[7], row2[7], row3[7], row4[7], row5[7];
+
+ double *matrix[6] = {row0, row1, row2, row3, row4, row5};
+
+ double solution[7];
+ int scambio[7];
+
+ assert (FromPoints.size() == 3);
+ assert (ToPoints.size() == 3);
+
+ // fill in the matrix elements
+ for ( int i = 0; i < 6; i++ )
+ {
+ for ( int j = 0; j < 7; j++ )
+ {
+ matrix[i][j] = 0.0;
+ }
+ }
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ Coordinate p = FromPoints[i];
+ Coordinate q = ToPoints[i];
+ matrix[i][0] = -q.x;
+ matrix[i][1] = 1.0;
+ matrix[i][3] = p.x;
+ matrix[i][4] = p.y;
+ matrix[i+3][0] = -q.y;
+ matrix[i+3][2] = 1.0;
+ matrix[i+3][5] = p.x;
+ matrix[i+3][6] = p.y;
+ }
+
+ Transformation ret;
+ valid = true;
+ if ( ! GaussianElimination( matrix, 6, 7, scambio ) )
+ { valid = false; return ret; }
+
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, 6, 7, scambio, solution );
+
+ // now we can build the 3x3 transformation matrix; remember that
+ // unknown 0 is the multiplicator 'm'
+
+ ret.mdata[0][0] = solution[0];
+ ret.mdata[0][1] = ret.mdata[0][2] = 0.0;
+ ret.mdata[1][0] = solution[1];
+ ret.mdata[2][0] = solution[2];
+ ret.mdata[1][1] = solution[3];
+ ret.mdata[1][2] = solution[4];
+ ret.mdata[2][1] = solution[5];
+ ret.mdata[2][2] = solution[6];
+
+ ret.mIsHomothety = false;
+ ret.mIsAffine = true;
+ return ret;
+}
+
+const Transformation Transformation::projectivityGI4P(
+ const std::vector<Coordinate>& FromPoints,
+ const std::vector<Coordinate>& ToPoints,
+ bool& valid )
+{
+ // construct the (generically) unique projectivity that transforms 4 given
+ // point into 4 other given points; i.e. it depends on the coordinates of
+ // a total of 8 points. This actually amounts in solving an underdetermined
+ // homogeneous linear system.
+
+ double
+ row0[13], row1[13], row2[13], row3[13], row4[13], row5[13], row6[13], row7[13],
+ row8[13], row9[13], row10[13], row11[13];
+
+ double *matrix[12] = {row0, row1, row2, row3, row4, row5, row6, row7,
+ row8, row9, row10, row11};
+
+ double solution[13];
+ int scambio[13];
+
+ assert (FromPoints.size() == 4);
+ assert (ToPoints.size() == 4);
+
+ // fill in the matrix elements
+ for ( int i = 0; i < 12; i++ )
+ {
+ for ( int j = 0; j < 13; j++ )
+ {
+ matrix[i][j] = 0.0;
+ }
+ }
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ Coordinate p = FromPoints[i];
+ Coordinate q = ToPoints[i];
+ matrix[i][0] = matrix[4+i][3] = matrix[8+i][6] = 1.0;
+ matrix[i][1] = matrix[4+i][4] = matrix[8+i][7] = p.x;
+ matrix[i][2] = matrix[4+i][5] = matrix[8+i][8] = p.y;
+ matrix[i][9+i] = -1.0;
+ matrix[4+i][9+i] = -q.x;
+ matrix[8+i][9+i] = -q.y;
+ }
+
+ Transformation ret;
+ valid = true;
+ if ( ! GaussianElimination( matrix, 12, 13, scambio ) )
+ { valid = false; return ret; }
+
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, 12, 13, scambio, solution );
+
+ // now we can build the 3x3 transformation matrix; remember that
+ // unknowns from 9 to 13 are just multiplicators that we don't need here
+
+ int k = 0;
+ for ( int i = 0; i < 3; i++ )
+ {
+ for ( int j = 0; j < 3; j++ )
+ {
+ ret.mdata[i][j] = solution[k++];
+ }
+ }
+
+ ret.mIsHomothety = ret.mIsAffine = false;
+ return ret;
+}
+
+const Transformation Transformation::castShadow(
+ const Coordinate& lightsrc, const LineData& l )
+{
+ // first deal with the line l, I need to find an appropriate reflection
+ // that transforms l onto the x-axis
+
+ Coordinate d = l.dir();
+ Coordinate a = l.a;
+ double k = d.length();
+ if ( d.x < 0 ) k *= -1; // for numerical stability
+ Coordinate w = d + Coordinate( k, 0 );
+ // w /= w.length();
+ // w defines a Householder transformation, but we don't need to normalize
+ // it here.
+ // warning: this w is the orthogonal of the w of the textbooks!
+ // this is fine for us since in this way it indicates the line direction
+ Coordinate ra = Coordinate ( a.x + w.y*a.y/(2*w.x), a.y/2 );
+ Transformation sym = lineReflection ( LineData( ra, ra + w ) );
+
+ // in the new coordinates the line is the x-axis
+ // I must transform the point
+
+ Coordinate modlightsrc = sym.apply ( lightsrc );
+ Transformation ret = identity();
+ // parameter t indicates the distance of the light source from
+ // the plane of the drawing. A negative value means that the light
+ // source is behind the plane.
+ double t = -1.0;
+ // double t = -modlightsrc.y; <-- this gives the old transformation!
+ double e = modlightsrc.y - t;
+ ret.mdata[0][0] = e;
+ ret.mdata[0][2] = -1;
+ ret.mdata[1][1] = e;
+ ret.mdata[1][2] = -modlightsrc.x;
+ ret.mdata[2][2] = -t;
+
+ ret.mIsHomothety = ret.mIsAffine = false;
+ return sym*ret*sym;
+// return translation( t )*ret*translation( -t );
+}
+
+const Transformation Transformation::projectiveRotation(
+ double alpha, const Coordinate& d, const Coordinate& t )
+{
+ Transformation ret;
+ double cosalpha = cos( alpha );
+ double sinalpha = sin( alpha );
+ ret.mdata[0][0] = cosalpha;
+ ret.mdata[1][1] = cosalpha*d.x*d.x + d.y*d.y;
+ ret.mdata[0][1] = -sinalpha*d.x;
+ ret.mdata[1][0] = sinalpha*d.x;
+ ret.mdata[0][2] = -sinalpha*d.y;
+ ret.mdata[2][0] = sinalpha*d.y;
+ ret.mdata[1][2] = cosalpha*d.x*d.y - d.x*d.y;
+ ret.mdata[2][1] = cosalpha*d.x*d.y - d.x*d.y;
+ ret.mdata[2][2] = cosalpha*d.y*d.y + d.x*d.x;
+
+ ret.mIsHomothety = ret.mIsAffine = false;
+ return translation( t )*ret*translation( -t );
+}
+
+const Coordinate Transformation::apply( const double x0,
+ const double x1,
+ const double x2) const
+{
+ double phom[3] = {x0, x1, x2};
+ double rhom[3] = {0., 0., 0.};
+
+
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ rhom[i] += mdata[i][j]*phom[j];
+ }
+ }
+
+ if (rhom[0] == 0.)
+ return Coordinate::invalidCoord();
+
+ return Coordinate (rhom[1]/rhom[0], rhom[2]/rhom[0]);
+}
+
+const Coordinate Transformation::apply( const Coordinate& p ) const
+{
+ return apply( 1., p.x, p.y );
+// double phom[3] = {1., p.x, p.y};
+// double rhom[3] = {0., 0., 0.};
+//
+// for (int i = 0; i < 3; i++)
+// {
+// for (int j = 0; j < 3; j++)
+// {
+// rhom[i] += mdata[i][j]*phom[j];
+// }
+// }
+//
+// if (rhom[0] == 0.)
+// return Coordinate::invalidCoord();
+//
+// return Coordinate (rhom[1]/rhom[0], rhom[2]/rhom[0]);
+}
+
+const Coordinate Transformation::apply0( const Coordinate& p ) const
+{
+ return apply( 0., p.x, p.y );
+}
+
+const Transformation Transformation::rotation( double alpha, const Coordinate& center )
+{
+ Transformation ret = identity();
+
+ double x = center.x;
+ double y = center.y;
+
+ double cosalpha = cos( alpha );
+ double sinalpha = sin( alpha );
+
+ ret.mdata[1][1] = ret.mdata[2][2] = cosalpha;
+ ret.mdata[1][2] = -sinalpha;
+ ret.mdata[2][1] = sinalpha;
+ ret.mdata[1][0] = x - ret.mdata[1][1]*x - ret.mdata[1][2]*y;
+ ret.mdata[2][0] = y - ret.mdata[2][1]*x - ret.mdata[2][2]*y;
+
+ // this is already set in the identity() constructor, but just for
+ // clarity..
+ ret.mIsHomothety = ret.mIsAffine = true;
+
+ return ret;
+}
+
+bool Transformation::isHomothetic() const
+{
+ return mIsHomothety;
+}
+
+bool Transformation::isAffine() const
+{
+ return mIsAffine;
+}
+
+/*
+ *mp:
+ * this function has the property that it changes sign if computed
+ * on two points that lie on either sides with respect to the critical
+ * line (this is the line that goes to the line at infinity).
+ * For affine transformations the result has always the same sign.
+ * NOTE: the result is *not* invariant under rescaling of all elements
+ * of the transformation matrix.
+ * The typical use is to determine whether a segment is transformed
+ * into a segment or a couple of half-lines.
+ */
+
+double Transformation::getProjectiveIndicator( const Coordinate& c ) const
+{
+ return mdata[0][0] + mdata[0][1]*c.x + mdata[0][2]*c.y;
+}
+
+// assuming that this is an affine transformation, return its
+// determinant. What is really important here is just the sign
+// of the determinant.
+double Transformation::getAffineDeterminant() const
+{
+ return mdata[1][1]*mdata[2][2] - mdata[1][2]*mdata[2][1];
+}
+
+// this assumes that the 2x2 affine part of the matrix is of the
+// form [ cos a, sin a; -sin a, cos a] or a multiple
+double Transformation::getRotationAngle() const
+{
+ return atan2( mdata[1][2], mdata[1][1] );
+}
+
+const Coordinate Transformation::apply2by2only( const Coordinate& p ) const
+{
+ double x = p.x;
+ double y = p.y;
+ double nx = mdata[1][1]*x + mdata[1][2]*y;
+ double ny = mdata[2][1]*x + mdata[2][2]*y;
+ return Coordinate( nx, ny );
+}
+
+double Transformation::data( int r, int c ) const
+{
+ return mdata[r][c];
+}
+
+const Transformation Transformation::inverse( bool& valid ) const
+{
+ Transformation ret;
+
+ valid = Invert3by3matrix( mdata, ret.mdata );
+
+ // the inverse of a homothety is a homothety, same for affinities..
+ ret.mIsHomothety = mIsHomothety;
+ ret.mIsAffine = mIsAffine;
+
+ return ret;
+}
+
+Transformation::Transformation()
+{
+ // this is the constructor used by the static Transformation
+ // creation functions, so mIsHomothety is in general false
+ mIsHomothety = mIsAffine = false;
+ for ( int i = 0; i < 3; ++i )
+ for ( int j = 0; j < 3; ++j )
+ mdata[i][j] = ( i == j ) ? 1 : 0;
+}
+
+Transformation::~Transformation()
+{
+}
+
+double Transformation::apply( double length ) const
+{
+ assert( isHomothetic() );
+ double det = mdata[1][1]*mdata[2][2] -
+ mdata[1][2]*mdata[2][1];
+ return sqrt( fabs( det ) ) * length;
+}
+
+Transformation::Transformation( double data[3][3], bool ishomothety )
+ : mIsHomothety( ishomothety )
+{
+ for ( int i = 0; i < 3; ++i )
+ for ( int j = 0; j < 3; ++j )
+ mdata[i][j] = data[i][j];
+
+ //mp: a test for affinity is used to initialize mIsAffine...
+ mIsAffine = false;
+ if ( fabs(mdata[0][1]) + fabs(mdata[0][2]) < 1e-8 * fabs(mdata[0][0]) )
+ mIsAffine = true;
+}
+
+bool operator==( const Transformation& lhs, const Transformation& rhs )
+{
+ for ( int i = 0; i < 3; ++i )
+ for ( int j = 0; j < 3; ++j )
+ if ( lhs.data( i, j ) != rhs.data( i, j ) )
+ return false;
+ return true;
+}
+
+const Transformation Transformation::similitude(
+ const Coordinate& center, double theta, double factor )
+{
+ //kdDebug() << k_funcinfo << "theta: " << theta << " factor: " << factor << endl;
+ Transformation ret;
+ ret.mIsHomothety = true;
+ double costheta = cos( theta );
+ double sintheta = sin( theta );
+ ret.mdata[0][0] = 1;
+ ret.mdata[0][1] = 0;
+ ret.mdata[0][2] = 0;
+ ret.mdata[1][0] = ( 1 - factor*costheta )*center.x + factor*sintheta*center.y;
+ ret.mdata[1][1] = factor*costheta;
+ ret.mdata[1][2] = -factor*sintheta;
+ ret.mdata[2][0] = -factor*sintheta*center.x + ( 1 - factor*costheta )*center.y;
+ ret.mdata[2][1] = factor*sintheta;
+ ret.mdata[2][2] = factor*costheta;
+ // fails for factor == infinity
+ //assert( ( ret.apply( center ) - center ).length() < 1e-5 );
+ ret.mIsHomothety = ret.mIsAffine = true;
+ return ret;
+}
diff --git a/kig/misc/kigtransform.h b/kig/misc/kigtransform.h
new file mode 100644
index 00000000..252a0f72
--- /dev/null
+++ b/kig/misc/kigtransform.h
@@ -0,0 +1,190 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ Copyright (C) 2002 Maurizio Paolini <paolini@dmf.unicatt.it>
+ 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_MISC_KIGTRANSFORM_H
+#define KIG_MISC_KIGTRANSFORM_H
+
+#include "coordinate.h"
+#include <vector>
+
+class LineData;
+
+/**
+ * Class representing a transformation. More specifically, this class
+ * represents a pretty generic 2-dimensional transformation. Various
+ * common transformations can be used. Construct a Transformation by
+ * using one of its static members, and use it either with its
+ * Transformation::apply method, or the ObjectImp::transform method.
+ */
+class Transformation
+{
+ double mdata[3][3];
+ bool mIsHomothety;
+ bool mIsAffine;
+ Transformation();
+public:
+ ~Transformation();
+ Transformation( double data[3][3], bool ishomothety );
+
+ /**
+ * Apply this Tranformation. Apply this transformation to the
+ * Coordinate c. Can return an invalid Coordinate.
+ * apply0 assumes that c indicates a point at infinity, having
+ * [0, c.x, c.y] as homogeneous coordinates
+ */
+ const Coordinate apply( const double x0, const double x1, const double x2 ) const;
+ const Coordinate apply( const Coordinate& c ) const;
+ const Coordinate apply0( const Coordinate& c ) const;
+
+ /**
+ * Returns whether this is a homothetic (affine) transformation.
+ */
+ bool isHomothetic() const;
+ bool isAffine() const;
+ double getProjectiveIndicator( const Coordinate& c ) const;
+ double getAffineDeterminant() const;
+ double getRotationAngle() const;
+ const Coordinate apply2by2only( const Coordinate& c ) const;
+ /**
+ * \ifnot creating-python-scripting-doc
+ * a homothetic transformation maintains the ratio's of lengths.
+ * This means that every length is multiplied by a fixed number when
+ * it is projected... This function does that calculation for
+ * you..
+ * \endif
+ */
+ double apply( double length ) const;
+ double data( int r, int c ) const;
+ /**
+ * The inverse Transformation. Returns the inverse Transformation
+ * of this Transformation.
+ */
+ const Transformation inverse( bool& valid ) const;
+
+ /**
+ * Identity. Returns the Identity Transformation, i.e. a
+ * Transformation that doesn't do anything.
+ */
+ static const Transformation identity();
+ /**
+ * Scaling over Point. Returns a Transformation that scales points
+ * by a certain factor with relation to a center point.
+ */
+ static const Transformation scalingOverPoint( double factor, const Coordinate& center = Coordinate() );
+ /**
+ * Scaling over Line. Returns a Transformation that scales points
+ * by a certain factor with relation to a line. Note: This is not a
+ * homothetic transformation.
+ */
+ static const Transformation scalingOverLine( double factor, const LineData& l );
+ /**
+ * Translation. Returns a Translation by a vector c.
+ */
+ static const Transformation translation( const Coordinate& c );
+ /**
+ * Rotation. Returns a Rotation by a certain angle, around a
+ * certain center.
+ */
+ static const Transformation rotation( double angle, const Coordinate& center = Coordinate() );
+ /**
+ * Point Reflection. Returns a reflection over a point
+ * \note This equals scaling( -1, c );
+ */
+ static const Transformation pointReflection( const Coordinate& c );
+ /**
+ * Line Reflection. Returns a reflection over a line
+ * \note This equals scaling( -1, l );
+ */
+ static const Transformation lineReflection( const LineData& l );
+ /**
+ * Harmonic Homology. Returns a Transformation that transforms points in
+ * such a way that it appears to cast a shadow, given a certain
+ * light source (center), and a line (axis) indicating a plane.
+ */
+ static const Transformation harmonicHomology( const Coordinate& center,
+ const LineData& axis );
+ /**
+ * Affinity given the image of 3 points. Returns the unique
+ * affinity that transforms 3 given points into 3 given points.
+ */
+ static const Transformation affinityGI3P(
+ const std::vector<Coordinate>& FromPoints,
+ const std::vector<Coordinate>& ToPoints,
+ bool& valid );
+ /**
+ * Projectivity given the image of 4 points. Returns the unique
+ * projectivity that transforms 4 given points into 4 given points.
+ */
+ static const Transformation projectivityGI4P(
+ const std::vector<Coordinate>& FromPoints,
+ const std::vector<Coordinate>& ToPoints,
+ bool& valid );
+ /**
+ * Cast Shadow. Returns a Transformation that transforms points in
+ * such a way that it appears to cast a shadow, given a certain
+ * light source, and a line indicating a plane.
+ */
+ static const Transformation castShadow( const Coordinate& ls,
+ const LineData& d );
+ /**
+ * Projective Rotation. This is really only a test example of a
+ * projective non-affine transformation...
+ */
+ static const Transformation projectiveRotation( double alpha,
+ const Coordinate& d,
+ const Coordinate& t );
+
+ /**
+ * Similitude. Sequence of a rotation and a scaling with relation
+ * to a certain center.
+ */
+ static const Transformation similitude(
+ const Coordinate& center, double theta, double factor );
+
+ /**
+ * Sequence. This creates a Transformation that executes first
+ * transformation b, and then a.
+ */
+ friend const Transformation operator*( const Transformation& a, const Transformation& b );
+
+ /**
+ * Equality. Tests two Transformation's for equality.
+ */
+ friend bool operator==( const Transformation& lhs, const Transformation& rhs );
+};
+
+const Transformation operator*( const Transformation&, const Transformation& );
+bool operator==( const Transformation& lhs, const Transformation& rhs );
+
+// enum tWantArgsResult { tComplete, tNotComplete, tNotGood };
+
+// Transformation getProjectiveTransformation(
+// int transformationsnum, Object *mtransformations[],
+// bool& valid );
+
+// tWantArgsResult WantTransformation ( Objects::const_iterator& i,
+// const Objects& os );
+
+// QString getTransformMessage ( const Objects& os, const Object *o );
+
+// bool isHomoteticTransformation ( double transformation[3][3] );
+
+#endif // KIG_MISC_KIGTRANSFORM_H
diff --git a/kig/misc/lists.cc b/kig/misc/lists.cc
new file mode 100644
index 00000000..e93700a1
--- /dev/null
+++ b/kig/misc/lists.cc
@@ -0,0 +1,389 @@
+// 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 "lists.h"
+
+#include "object_constructor.h"
+#include "guiaction.h"
+#include "object_hierarchy.h"
+#include "../kig/kig_part.h"
+
+#include "config.h"
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdom.h>
+#include <qregexp.h>
+#include <algorithm>
+using namespace std;
+
+template<typename T>
+void vect_remove( std::vector<T>& v, const T& t )
+{
+ typename std::vector<T>::iterator new_end = std::remove( v.begin(), v.end(), t );
+ v.erase( new_end, v.end() );
+}
+
+GUIActionList* GUIActionList::instance()
+{
+ static GUIActionList l;
+ return &l;
+}
+
+GUIActionList::~GUIActionList()
+{
+ for ( avectype::iterator i = mactions.begin(); i != mactions.end(); ++i )
+ delete *i;
+}
+
+GUIActionList::GUIActionList()
+{
+}
+
+void GUIActionList::regDoc( KigPart* d )
+{
+ mdocs.insert( d );
+}
+
+void GUIActionList::unregDoc( KigPart* d )
+{
+ mdocs.erase( d );
+}
+
+void GUIActionList::add( const std::vector<GUIAction*>& a )
+{
+ copy( a.begin(), a.end(), inserter( mactions, mactions.begin() ) );
+ for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
+ {
+ KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
+ for ( uint j = 0; j < a.size(); ++j )
+ (*i)->actionAdded( a[j], t );
+ (*i)->endGUIActionUpdate( t );
+ };
+}
+
+void GUIActionList::add( GUIAction* a )
+{
+ mactions.insert( a );
+ for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
+ {
+ KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
+ (*i)->actionAdded( a, t );
+ (*i)->endGUIActionUpdate( t );
+ };
+}
+
+void GUIActionList::remove( const std::vector<GUIAction*>& a )
+{
+ for ( uint i = 0; i < a.size(); ++i )
+ {
+ mactions.erase( a[i] );
+ };
+ for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
+ {
+ KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
+ for ( uint j = 0; j < a.size(); ++j )
+ (*i)->actionRemoved( a[j], t );
+ (*i)->endGUIActionUpdate( t );
+ };
+ delete_all( a.begin(), a.end() );
+}
+
+void GUIActionList::remove( GUIAction* a )
+{
+ mactions.erase( a );
+ for ( dvectype::iterator i = mdocs.begin(); i != mdocs.end(); ++i )
+ {
+ KigPart::GUIUpdateToken t = (*i)->startGUIActionUpdate();
+ (*i)->actionRemoved( a, t );
+ (*i)->endGUIActionUpdate( t );
+ };
+ delete a;
+}
+
+ObjectConstructorList::ObjectConstructorList()
+{
+}
+
+ObjectConstructorList::~ObjectConstructorList()
+{
+ for ( vectype::iterator i = mctors.begin(); i != mctors.end(); ++i )
+ delete *i;
+}
+
+ObjectConstructorList* ObjectConstructorList::instance()
+{
+ static ObjectConstructorList s;
+ return &s;
+}
+
+ObjectConstructorList::vectype ObjectConstructorList::ctorsThatWantArgs(
+ const std::vector<ObjectCalcer*>& os, const KigDocument& d,
+ const KigWidget& w, bool co ) const
+{
+ vectype ret;
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ {
+ int r = (*i)->wantArgs( os, d, w );
+ if ( r == ArgsParser::Complete || ( !co && r == ArgsParser::Valid ) )
+ ret.push_back( *i );
+ };
+ return ret;
+}
+
+void ObjectConstructorList::remove( ObjectConstructor* a )
+{
+ vect_remove( mctors, a );
+ delete a;
+}
+
+void ObjectConstructorList::add( ObjectConstructor* a )
+{
+ mctors.push_back( a );
+}
+
+Macro::Macro( GUIAction* a, MacroConstructor* c )
+ : action( a ), ctor( c )
+{
+}
+
+bool operator==( const Macro& l, const Macro& r )
+{
+ return ( l.action->descriptiveName() == r.action->descriptiveName() ) &&
+ ( l.action->description() == r.action->description() ) &&
+ ( l.action->iconFileName() == r.action->iconFileName() );
+}
+
+MacroList::MacroList()
+{
+}
+
+MacroList::~MacroList()
+{
+ std::vector<GUIAction*> actions;
+ std::vector<ObjectConstructor*> ctors;
+ for ( vectype::iterator i = mdata.begin(); i != mdata.end(); ++i )
+ {
+ Macro* m = *i;
+ GUIAction* a = m->action;
+ actions.push_back( a );
+ ObjectConstructor* c = m->ctor;
+ ctors.push_back( c );
+ delete m;
+ };
+ mdata.clear();
+ GUIActionList::instance()->remove( actions );
+ for ( uint i = 0; i < ctors.size(); ++i )
+ ObjectConstructorList::instance()->remove( ctors[i] );
+}
+
+MacroList* MacroList::instance()
+{
+ static MacroList t;
+ return &t;
+}
+
+void MacroList::add( const std::vector<Macro*>& ms )
+{
+ copy( ms.begin(), ms.end(), back_inserter( mdata ) );
+ std::vector<GUIAction*> acts;
+ for ( uint i = 0; i < ms.size(); ++i )
+ {
+ ObjectConstructorList::instance()->add( ms[i]->ctor );
+ acts.push_back( ms[i]->action );
+ };
+ GUIActionList::instance()->add( acts );
+}
+
+void MacroList::add( Macro* m )
+{
+ mdata.push_back( m );
+ ObjectConstructorList::instance()->add( m->ctor );
+ GUIActionList::instance()->add( m->action );
+}
+
+void MacroList::remove( Macro* m )
+{
+ GUIAction* a = m->action;
+ ObjectConstructor* c = m->ctor;
+ mdata.erase( std::remove( mdata.begin(), mdata.end(), m ),
+ mdata.end() );
+ delete m;
+ GUIActionList::instance()->remove( a );
+ ObjectConstructorList::instance()->remove( c );
+}
+
+const MacroList::vectype& MacroList::macros() const
+{
+ return mdata;
+}
+
+Macro::~Macro()
+{
+}
+
+bool MacroList::save( Macro* m, const QString& f )
+{
+ std::vector<Macro*> ms;
+ ms.push_back( m );
+ return save( ms, f );
+}
+
+bool MacroList::save( const std::vector<Macro*>& ms, const QString& f )
+{
+ QDomDocument doc( "KigMacroFile" );
+
+ QDomElement docelem = doc.createElement( "KigMacroFile" );
+ docelem.setAttribute( "Version", KIGVERSION );
+ docelem.setAttribute( "Number", ms.size() );
+
+ for ( uint i = 0; i < ms.size(); ++i )
+ {
+ MacroConstructor* ctor = ms[i]->ctor;
+
+ QDomElement macroelem = doc.createElement( "Macro" );
+
+ // name
+ QDomElement nameelem = doc.createElement( "Name" );
+ nameelem.appendChild( doc.createTextNode( ctor->descriptiveName() ) );
+ macroelem.appendChild( nameelem );
+
+ // desc
+ QDomElement descelem = doc.createElement( "Description" );
+ descelem.appendChild( doc.createTextNode( ctor->description() ) );
+ macroelem.appendChild( descelem );
+
+ // icon
+ QCString icon = ctor->iconFileName( true );
+ if ( !icon.isNull() )
+ {
+ QDomElement descelem = doc.createElement( "IconFileName" );
+ descelem.appendChild( doc.createTextNode( icon ) );
+ macroelem.appendChild( descelem );
+ }
+
+ // data
+ QDomElement hierelem = doc.createElement( "Construction" );
+ ctor->hierarchy().serialize( hierelem, doc );
+ macroelem.appendChild( hierelem );
+
+ docelem.appendChild( macroelem );
+ };
+
+ doc.appendChild( docelem );
+
+ QFile file( f );
+ if ( ! file.open( IO_WriteOnly ) )
+ return false;
+ QTextStream stream( &file );
+ stream << doc.toCString();
+ return true;
+}
+
+bool MacroList::load( const QString& f, std::vector<Macro*>& ret, const KigPart& kdoc )
+{
+ QFile file( f );
+ if ( ! file.open( IO_ReadOnly ) )
+ {
+ KMessageBox::sorry( 0, i18n( "Could not open macro file '%1'" ).arg( f ) );
+ return false;
+ }
+ QDomDocument doc( "KigMacroFile" );
+ if ( !doc.setContent( &file ) )
+ {
+ KMessageBox::sorry( 0, i18n( "Could not open macro file '%1'" ).arg( f ) );
+ return false;
+ }
+ file.close();
+ QDomElement main = doc.documentElement();
+
+ if ( main.tagName() == "KigMacroFile" )
+ return loadNew( main, ret, kdoc );
+ else
+ {
+ KMessageBox::detailedSorry(
+ 0, i18n( "Kig cannot open the macro file \"%1\"." ).arg( f ),
+ i18n( "This file was created by a very old Kig version (pre-0.4). "
+ "Support for this format has been removed from recent Kig versions. "
+ "You can try to import this macro using a previous Kig version "
+ "(0.4 to 0.6) and then export it again in the new format." ),
+ i18n( "Not Supported" ) );
+ return false;
+ }
+}
+
+bool MacroList::loadNew( const QDomElement& docelem, std::vector<Macro*>& ret, const KigPart& )
+{
+ bool sok = true;
+ // unused..
+// int number = docelem.attribute( "Number" ).toInt( &sok );
+ if ( ! sok ) return false;
+
+ QString version = docelem.attribute( "Version" );
+// QRegExp re( "(\\d+)\\.(\\d+)\\.(\\d+)" );
+// re.match( version );
+ // unused..
+// int major = re.cap( 1 ).toInt( &sok );
+// int minor = re.cap( 2 ).toInt( &sok );
+// int mminor = re.cap( 3 ).toInt( &sok );
+// if ( ! sok ) return false;
+
+ int unnamedindex = 1;
+ QString tmp;
+
+ for ( QDomElement macroelem = docelem.firstChild().toElement();
+ ! macroelem.isNull(); macroelem = macroelem.nextSibling().toElement() )
+ {
+ QString name, description;
+ ObjectHierarchy* hierarchy = 0;
+ QCString actionname, iconfile;
+ if ( macroelem.tagName() != "Macro" ) continue; // forward compat ?
+ for ( QDomElement dataelem = macroelem.firstChild().toElement();
+ ! dataelem.isNull(); dataelem = dataelem.nextSibling().toElement() )
+ {
+ if ( dataelem.tagName() == "Name" )
+ name = dataelem.text();
+ else if ( dataelem.tagName() == "Description" )
+ description = dataelem.text();
+ else if ( dataelem.tagName() == "Construction" )
+ hierarchy = ObjectHierarchy::buildSafeObjectHierarchy( dataelem, tmp );
+ else if ( dataelem.tagName() == "ActionName" )
+ actionname = dataelem.text().latin1();
+ else if ( dataelem.tagName() == "IconFileName" )
+ iconfile = dataelem.text().latin1();
+ else continue;
+ };
+ assert( hierarchy );
+ // if the macro has no name, we give it a bogus name...
+ if ( name.isEmpty() )
+ name = i18n( "Unnamed Macro #%1" ).arg( unnamedindex++ );
+ MacroConstructor* ctor =
+ new MacroConstructor( *hierarchy, i18n( name.latin1() ), i18n( description.latin1() ), iconfile );
+ delete hierarchy;
+ GUIAction* act = new ConstructibleAction( ctor, actionname );
+ Macro* macro = new Macro( act, ctor );
+ ret.push_back( macro );
+ };
+ return true;
+}
+
+const ObjectConstructorList::vectype& ObjectConstructorList::constructors() const
+{
+ return mctors;
+}
diff --git a/kig/misc/lists.h b/kig/misc/lists.h
new file mode 100644
index 00000000..a3f97f1d
--- /dev/null
+++ b/kig/misc/lists.h
@@ -0,0 +1,170 @@
+// 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_MISC_LISTS_H
+#define KIG_MISC_LISTS_H
+
+#include <vector>
+#include <set>
+
+class GUIAction;
+class ObjectConstructor;
+class MacroConstructor;
+class KigDocument;
+class KigPart;
+class KigWidget;
+class QString;
+class QDomElement;
+class ObjectCalcer;
+
+/**
+ * List of GUIActions for the parts to show. Note that the list owns
+ * the actions it receives..
+ */
+class GUIActionList
+{
+public:
+ typedef std::set<GUIAction*> avectype;
+ typedef std::set<KigPart*> dvectype;
+private:
+ avectype mactions;
+ dvectype mdocs;
+ GUIActionList();
+ ~GUIActionList();
+public:
+ static GUIActionList* instance();
+ const avectype& actions() const { return mactions; }
+
+ /**
+ * register this document, so that it receives notifications for
+ * added and removed actions..
+ */
+ void regDoc( KigPart* d );
+ void unregDoc( KigPart* d );
+
+ void add( GUIAction* a );
+ void add( const std::vector<GUIAction*>& a );
+ void remove( GUIAction* a );
+ void remove( const std::vector<GUIAction*>& a );
+};
+
+/**
+ * The list of object constructors for use in various places, e.g. RMB
+ * menu's. Note that the list owns the ctors it gets..
+ */
+class ObjectConstructorList
+{
+public:
+ typedef std::vector<ObjectConstructor*> vectype;
+private:
+ vectype mctors;
+ ObjectConstructorList();
+ ~ObjectConstructorList();
+public:
+ static ObjectConstructorList* instance();
+ void add( ObjectConstructor* a );
+ void remove( ObjectConstructor* a );
+ vectype ctorsThatWantArgs( const std::vector<ObjectCalcer*>&, const KigDocument&,
+ const KigWidget&, bool completeOnly = false
+ ) const;
+ const vectype& constructors() const;
+};
+
+/**
+ * this is just a simple data struct. doesn't have any functionality
+ * of its own..
+ */
+class Macro
+{
+public:
+ GUIAction* action;
+ MacroConstructor* ctor;
+ Macro( GUIAction* a, MacroConstructor* c );
+ ~Macro();
+};
+
+/**
+ * a simply equality operator for Macro class.
+ */
+bool operator==( const Macro& l, const Macro& r );
+
+/**
+ * This class keeps a list of all macro's, and allows them to be
+ * easily accessed, added etc. Macro objects are owned by this
+ * list..
+ */
+class MacroList
+{
+public:
+ typedef std::vector<Macro*> vectype;
+private:
+ vectype mdata;
+ MacroList();
+ ~MacroList();
+public:
+ /**
+ * MacroList is a singleton
+ */
+ static MacroList* instance();
+
+ /**
+ * Add a Macro \p m . MacroList takes care of adding the action and
+ * ctor in the relevant places..
+ */
+ void add( Macro* m );
+ /**
+ * Add the Macro's \p ms. MacroList takes care of adding the action
+ * and ctor in the relevant places..
+ */
+ void add( const vectype& ms );
+
+ /**
+ * Remove macro \p m . Macrolist takes care of deleting everything, and
+ * unregistering the action and ctor from the relevant places..
+ */
+ void remove( Macro* m );
+
+ /**
+ * Save macro \p m to file \p f ..
+ */
+ bool save( Macro* m, const QString& f );
+ /**
+ * Save macros \p ms to file \p f ..
+ */
+ bool save( const vectype& ms, const QString& f );
+
+ /**
+ * load macro's from file \p f ..
+ * note that this just returns the loaded macro's, and doesn't add
+ * them to the various lists. Use add() if you want
+ * that behaviour..
+ * The fact that this functions requires a KigPart argument is
+ * semantically incorrect, but i haven't been able to work around
+ * it..
+ */
+ bool load( const QString& f, vectype& ret, const KigPart& );
+
+ /**
+ * get access to the list of macro's..
+ */
+ const vectype& macros() const;
+
+private:
+ bool loadNew( const QDomElement& docelem, std::vector<Macro*>& ret, const KigPart& );
+};
+
+#endif
diff --git a/kig/misc/object_constructor.cc b/kig/misc/object_constructor.cc
new file mode 100644
index 00000000..5634d0d2
--- /dev/null
+++ b/kig/misc/object_constructor.cc
@@ -0,0 +1,609 @@
+// 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_constructor.h"
+
+#include "argsparser.h"
+#include "kigpainter.h"
+#include "guiaction.h"
+
+#include "../kig/kig_part.h"
+#include "../kig/kig_view.h"
+
+#include "../objects/object_holder.h"
+#include "../objects/object_drawer.h"
+#include "../objects/object_type.h"
+#include "../objects/other_type.h"
+#include "../objects/object_imp.h"
+#include "../objects/bogus_imp.h"
+#include "../objects/line_imp.h"
+#include "../objects/circle_imp.h"
+#include "../objects/point_imp.h"
+
+#include "../modes/construct_mode.h"
+
+#include <qpen.h>
+
+#include <klocale.h>
+
+#include <algorithm>
+#include <functional>
+
+const QString StandardConstructorBase::descriptiveName() const
+{
+ return i18n( mdescname );
+}
+
+const QString StandardConstructorBase::description() const
+{
+ return i18n( mdesc );
+}
+
+const QCString StandardConstructorBase::iconFileName( const bool ) const
+{
+ return miconfile;
+}
+
+const bool StandardConstructorBase::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) const
+{
+ return false;
+}
+
+StandardConstructorBase::StandardConstructorBase(
+ const char* descname, const char* desc,
+ const char* iconfile, const ArgsParser& parser )
+ : mdescname( descname ),
+ mdesc( desc ),
+ miconfile( iconfile ),
+ margsparser( parser )
+{
+}
+
+const int StandardConstructorBase::wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument&,
+ const KigWidget& ) const
+{
+ return margsparser.check( os );
+}
+
+void StandardConstructorBase::handleArgs(
+ const std::vector<ObjectCalcer*>& os, KigPart& d,
+ KigWidget& v ) const
+{
+ std::vector<ObjectHolder*> bos = build( os, d.document(), v );
+ for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
+ i != bos.end(); ++i )
+ {
+ (*i)->calc( d.document() );
+ }
+
+ d.addObjects( bos );
+}
+
+void StandardConstructorBase::handlePrelim(
+ KigPainter& p, const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d, const KigWidget&
+ ) const
+{
+ assert ( margsparser.check( os ) != ArgsParser::Invalid );
+ std::vector<ObjectCalcer*> args = margsparser.parse( os );
+ p.setBrushStyle( Qt::NoBrush );
+ p.setBrushColor( Qt::red );
+ p.setPen( QPen ( Qt::red, 1) );
+ p.setWidth( -1 ); // -1 means the default width for the object being
+ // drawn..
+
+ ObjectDrawer drawer( Qt::red );
+ drawprelim( drawer, p, args, d );
+}
+
+SimpleObjectTypeConstructor::SimpleObjectTypeConstructor(
+ const ArgsParserObjectType* t, const char* descname,
+ const char* desc, const char* iconfile )
+ : StandardConstructorBase( descname, desc, iconfile,
+ t->argsParser() ),
+ mtype( t )
+{
+}
+
+SimpleObjectTypeConstructor::~SimpleObjectTypeConstructor()
+{
+}
+
+void SimpleObjectTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& doc ) const
+{
+ Args args;
+ using namespace std;
+ transform( parents.begin(), parents.end(),
+ back_inserter( args ), mem_fun( &ObjectCalcer::imp ) );
+ ObjectImp* data = mtype->calc( args, doc );
+ drawer.draw( *data, p, true );
+ delete data;
+}
+
+std::vector<ObjectHolder*> SimpleObjectTypeConstructor::build(
+ const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const
+{
+ ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, os );
+ ObjectHolder* h = new ObjectHolder( calcer );
+ std::vector<ObjectHolder*> ret;
+ ret.push_back( h );
+ return ret;
+}
+
+StandardConstructorBase::~StandardConstructorBase()
+{
+}
+
+MultiObjectTypeConstructor::MultiObjectTypeConstructor(
+ const ArgsParserObjectType* t, const char* descname,
+ const char* desc, const char* iconfile,
+ const std::vector<int>& params )
+ : StandardConstructorBase( descname, desc, iconfile, mparser ),
+ mtype( t ), mparams( params ),
+ mparser( t->argsParser().without( IntImp::stype() ) )
+{
+}
+
+MultiObjectTypeConstructor::MultiObjectTypeConstructor(
+ const ArgsParserObjectType* t, const char* descname,
+ const char* desc, const char* iconfile,
+ int a, int b, int c, int d )
+ : StandardConstructorBase( descname, desc, iconfile, mparser ),
+ mtype( t ), mparams(),
+ mparser( t->argsParser().without( IntImp::stype() ) )
+{
+ mparams.push_back( a );
+ mparams.push_back( b );
+ if ( c != -999 ) mparams.push_back( c );
+ if ( d != -999 ) mparams.push_back( d );
+}
+
+MultiObjectTypeConstructor::~MultiObjectTypeConstructor()
+{
+}
+
+void MultiObjectTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& doc ) const
+{
+ Args args;
+ using namespace std;
+ transform( parents.begin(), parents.end(),
+ back_inserter( args ), mem_fun( &ObjectCalcer::imp ) );
+
+ for ( vector<int>::const_iterator i = mparams.begin(); i != mparams.end(); ++i )
+ {
+ IntImp param( *i );
+ args.push_back( &param );
+ ObjectImp* data = mtype->calc( args, doc );
+ drawer.draw( *data, p, true );
+ delete data;
+ args.pop_back();
+ };
+}
+
+std::vector<ObjectHolder*> MultiObjectTypeConstructor::build(
+ const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const
+{
+ std::vector<ObjectHolder*> ret;
+ for ( std::vector<int>::const_iterator i = mparams.begin();
+ i != mparams.end(); ++i )
+ {
+ ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( *i ) );
+
+ std::vector<ObjectCalcer*> args( os );
+ args.push_back( d );
+
+ ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
+ };
+ return ret;
+}
+
+MergeObjectConstructor::~MergeObjectConstructor()
+{
+ for ( vectype::iterator i = mctors.begin(); i != mctors.end(); ++i )
+ delete *i;
+}
+
+MergeObjectConstructor::MergeObjectConstructor(
+ const char* descname, const char* desc, const char* iconfilename )
+ : ObjectConstructor(), mdescname( descname ), mdesc( desc ),
+ miconfilename( iconfilename ), mctors()
+{
+}
+
+ObjectConstructor::~ObjectConstructor()
+{
+}
+
+void MergeObjectConstructor::merge( ObjectConstructor* e )
+{
+ mctors.push_back( e );
+}
+
+const QString MergeObjectConstructor::descriptiveName() const
+{
+ return i18n( mdescname );
+}
+
+const QString MergeObjectConstructor::description() const
+{
+ return i18n( mdesc );
+}
+
+const QCString MergeObjectConstructor::iconFileName( const bool ) const
+{
+ return miconfilename;
+}
+
+const bool MergeObjectConstructor::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) const
+{
+ return false;
+}
+
+const int MergeObjectConstructor::wantArgs(
+ const std::vector<ObjectCalcer*>& os, const KigDocument& d, const KigWidget& v ) const
+{
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ {
+ int w = (*i)->wantArgs( os, d, v );
+ if ( w != ArgsParser::Invalid ) return w;
+ };
+ return ArgsParser::Invalid;
+}
+
+void MergeObjectConstructor::handleArgs(
+ const std::vector<ObjectCalcer*>& os, KigPart& d, KigWidget& v ) const
+{
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ {
+ int w = (*i)->wantArgs( os, d.document(), v );
+ if ( w == ArgsParser::Complete )
+ {
+ (*i)->handleArgs( os, d, v );
+ return;
+ };
+ };
+ assert( false );
+}
+
+void MergeObjectConstructor::handlePrelim(
+ KigPainter& p, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v ) const
+{
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ {
+ int w = (*i)->wantArgs( sel, d, v );
+ if ( w != ArgsParser::Invalid )
+ {
+ (*i)->handlePrelim( p, sel, d, v );
+ return;
+ };
+ };
+}
+
+QString StandardConstructorBase::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument&, const KigWidget& ) const
+{
+ using namespace std;
+ Args args;
+ transform( sel.begin(), sel.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) );
+
+ std::string ret = margsparser.usetext( o.imp(), args );
+ if ( ret.empty() ) return QString::null;
+ return i18n( ret.c_str() );
+}
+
+QString StandardConstructorBase::selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument&,
+ const KigWidget& ) const
+{
+ using namespace std;
+ Args args;
+ transform( sel.begin(), sel.end(), back_inserter( args ), mem_fun( &ObjectCalcer::imp ) );
+
+ std::string ret = margsparser.selectStatement( args );
+ if ( ret.empty() ) return QString::null;
+ return i18n( ret.c_str() );
+}
+
+QString MergeObjectConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v ) const
+{
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ {
+ std::vector<ObjectCalcer*> args( sel );
+ int w = (*i)->wantArgs( args, d, v );
+ if ( w != ArgsParser::Invalid ) return (*i)->useText( o, sel, d, v );
+ };
+ return QString::null;
+}
+
+QString MergeObjectConstructor::selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const
+{
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ {
+ std::vector<ObjectCalcer*> args( sel );
+ int wa = (*i)->wantArgs( args, d, w );
+ if ( wa != ArgsParser::Invalid ) return (*i)->selectStatement( sel, d, w );
+ };
+ return QString::null;
+}
+
+MacroConstructor::MacroConstructor( const ObjectHierarchy& hier, const QString& name,
+ const QString& desc, const QCString& iconfile )
+ : ObjectConstructor(), mhier( hier ), mname( name ), mdesc( desc ),
+ mbuiltin( false ), miconfile( iconfile ),
+ mparser( mhier.argParser() )
+{
+}
+
+MacroConstructor::MacroConstructor(
+ const std::vector<ObjectCalcer*>& input, const std::vector<ObjectCalcer*>& output,
+ const QString& name, const QString& description,
+ const QCString& iconfile )
+ : ObjectConstructor(), mhier( input, output ),
+ mname( name ), mdesc( description ), mbuiltin( false ),
+ miconfile( iconfile ),
+ mparser( mhier.argParser() )
+{
+}
+
+MacroConstructor::~MacroConstructor()
+{
+}
+
+const QString MacroConstructor::descriptiveName() const
+{
+ return mname;
+}
+
+const QString MacroConstructor::description() const
+{
+ return mdesc;
+}
+
+const QCString MacroConstructor::iconFileName( const bool canBeNull ) const
+{
+ return ( miconfile.isNull() && !canBeNull ) ? QCString( "gear" ) : miconfile;
+}
+
+const bool MacroConstructor::isAlreadySelectedOK( const std::vector<ObjectCalcer*>&, const int& ) const
+{
+ return false;
+}
+
+const int MacroConstructor::wantArgs( const std::vector<ObjectCalcer*>& os, const KigDocument&,
+ const KigWidget& ) const
+{
+ return mparser.check( os );
+}
+
+void MacroConstructor::handleArgs( const std::vector<ObjectCalcer*>& os, KigPart& d,
+ KigWidget& ) const
+{
+ std::vector<ObjectCalcer*> args = mparser.parse( os );
+ std::vector<ObjectCalcer*> bos = mhier.buildObjects( args, d.document() );
+ std::vector<ObjectHolder*> hos;
+ for ( std::vector<ObjectCalcer*>::iterator i = bos.begin();
+ i != bos.end(); ++i )
+ {
+ hos.push_back( new ObjectHolder( *i ) );
+ hos.back()->calc( d.document() );
+ }
+
+ d.addObjects( hos );
+}
+
+QString MacroConstructor::selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument&,
+ const KigWidget& ) const
+{
+ using namespace std;
+ Args args;
+ transform( sel.begin(), sel.end(), back_inserter( args ),
+ mem_fun( &ObjectCalcer::imp ) );
+ std::string ret = mparser.selectStatement( args );
+ if ( ret.empty() ) return QString::null;
+ else return i18n( ret.c_str() );
+}
+
+QString MacroConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument&, const KigWidget&
+ ) const
+{
+ using namespace std;
+ Args args;
+ transform( sel.begin(), sel.end(), back_inserter( args ),
+ mem_fun( &ObjectCalcer::imp ) );
+ std::string ret = mparser.usetext( o.imp(), args );
+ if ( ret.empty() ) return QString::null;
+ else return i18n( ret.c_str() );
+}
+
+void MacroConstructor::handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& doc, const KigWidget&
+ ) const
+{
+ if ( sel.size() != mhier.numberOfArgs() ) return;
+
+ using namespace std;
+ Args args;
+ transform( sel.begin(), sel.end(), back_inserter( args ),
+ mem_fun( &ObjectCalcer::imp ) );
+ args = mparser.parse( args );
+ std::vector<ObjectImp*> ret = mhier.calc( args, doc );
+ for ( uint i = 0; i < ret.size(); ++i )
+ {
+ ObjectDrawer d;
+ d.draw( *ret[i], p, true );
+ ret[i]->draw( p );
+ delete ret[i];
+ };
+}
+
+void SimpleObjectTypeConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+void MultiObjectTypeConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+void MergeObjectConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+void MacroConstructor::plug( KigPart* doc, KigGUIAction* kact )
+{
+ if ( mbuiltin ) return;
+ if ( mhier.numberOfResults() != 1 )
+ doc->aMNewOther.append( kact );
+ else
+ {
+ if ( mhier.idOfLastResult() == SegmentImp::stype() )
+ doc->aMNewSegment.append( kact );
+ else if ( mhier.idOfLastResult() == PointImp::stype() )
+ doc->aMNewPoint.append( kact );
+ else if ( mhier.idOfLastResult() == CircleImp::stype() )
+ doc->aMNewCircle.append( kact );
+ else if ( mhier.idOfLastResult()->inherits( AbstractLineImp::stype() ) )
+ // line or ray
+ doc->aMNewLine.append( kact );
+ else if ( mhier.idOfLastResult() == ConicImp::stype() )
+ doc->aMNewConic.append( kact );
+ else doc->aMNewOther.append( kact );
+ };
+ doc->aMNewAll.append( kact );
+}
+
+const ObjectHierarchy& MacroConstructor::hierarchy() const
+{
+ return mhier;
+}
+
+bool SimpleObjectTypeConstructor::isTransform() const
+{
+ return mtype->isTransform();
+}
+
+bool MultiObjectTypeConstructor::isTransform() const
+{
+ return mtype->isTransform();
+}
+
+bool MergeObjectConstructor::isTransform() const
+{
+ bool ret = false;
+ for ( vectype::const_iterator i = mctors.begin(); i != mctors.end(); ++i )
+ ret |= (*i)->isTransform();
+ return ret;
+}
+
+bool MacroConstructor::isTransform() const
+{
+ return false;
+}
+
+void MacroConstructor::setBuiltin( bool builtin )
+{
+ mbuiltin = builtin;
+}
+
+bool ObjectConstructor::isIntersection() const
+{
+ return false;
+}
+
+PropertyObjectConstructor::PropertyObjectConstructor(
+ const ObjectImpType* imprequirement, const char* usetext,
+ const char* selectstat, const char* descname, const char* desc,
+ const char* iconfile, const char* propertyinternalname )
+ : StandardConstructorBase( descname, desc, iconfile, mparser ),
+ mpropinternalname( propertyinternalname )
+{
+ ArgsParser::spec argsspec[1];
+ argsspec[0].type = imprequirement;
+ argsspec[0].usetext = usetext;
+ argsspec[0].selectstat = selectstat;
+ mparser.initialize( argsspec, 1 );
+}
+
+PropertyObjectConstructor::~PropertyObjectConstructor()
+{
+}
+
+void PropertyObjectConstructor::drawprelim(
+ const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& d ) const
+{
+ int index = parents[0]->imp()->propertiesInternalNames().findIndex( mpropinternalname );
+ assert ( index != -1 );
+ ObjectImp* imp = parents[0]->imp()->property( index, d );
+ drawer.draw( *imp, p, true );
+ delete imp;
+}
+
+std::vector<ObjectHolder*> PropertyObjectConstructor::build(
+ const std::vector<ObjectCalcer*>& parents, KigDocument&,
+ KigWidget& ) const
+{
+ int index = parents[0]->imp()->propertiesInternalNames().findIndex( mpropinternalname );
+ assert( index != -1 );
+ std::vector<ObjectHolder*> ret;
+ ret.push_back(
+ new ObjectHolder(
+ new ObjectPropertyCalcer( parents[0], index ) ) );
+ return ret;
+}
+
+void PropertyObjectConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool PropertyObjectConstructor::isTransform() const
+{
+ return false;
+}
+
+bool ObjectConstructor::isTest() const
+{
+ return false;
+}
+
+BaseConstructMode* ObjectConstructor::constructMode( KigPart& doc )
+{
+ return new ConstructMode( doc, this );
+}
+
+void MacroConstructor::setName( const QString& name )
+{
+ mname = name;
+}
+
+void MacroConstructor::setDescription( const QString& desc )
+{
+ mdesc = desc;
+}
+
+void MacroConstructor::setIcon( QCString& icon )
+{
+ miconfile = icon;
+}
diff --git a/kig/misc/object_constructor.h b/kig/misc/object_constructor.h
new file mode 100644
index 00000000..57261c69
--- /dev/null
+++ b/kig/misc/object_constructor.h
@@ -0,0 +1,396 @@
+// Copyright (C) 2002-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_MISC_OBJECT_CONSTRUCTOR_H
+#define KIG_MISC_OBJECT_CONSTRUCTOR_H
+
+#include "argsparser.h"
+#include "object_hierarchy.h"
+
+class KigPainter;
+class KigDocument;
+class KigGUIAction;
+class KigWidget;
+class ArgsParserObjectType;
+class ObjectType;
+class BaseConstructMode;
+
+class QString;
+class QCString;
+
+/**
+ * This class represents a way to construct a set of objects from a
+ * set of other objects. There are some important child classes, like
+ * MacroConstructor, StandardObjectConstructor etc. ( see below )
+ * Actually, it is more generic than that, it provides a way to do
+ * _something_ with a set of objects, but for now, i only use it to
+ * construct objects. Maybe some day, i'll find something more
+ * interesting to do with it, who knows... ;)
+ */
+class ObjectConstructor
+{
+public:
+ virtual ~ObjectConstructor();
+
+ virtual const QString descriptiveName() const = 0;
+ virtual const QString description() const = 0;
+ virtual const QCString iconFileName( const bool canBeNull = false ) const = 0;
+
+ /**
+ * the following function is called in case of duplication of arguments
+ * and returns true if this is acceptable; this will return false for
+ * typical objects
+ */
+ virtual const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const = 0;
+ /**
+ * can this constructor do something useful with \p os ? return
+ * ArgsParser::Complete, Valid or NotGood
+ */
+ virtual const int wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const = 0;
+
+ /**
+ * do something fun with \p os .. This func is only called if wantArgs
+ * returned Complete.. handleArgs should <i>not</i> do any
+ * drawing.. after somebody calls this function, he should
+ * redrawScreen() himself..
+ */
+ virtual void handleArgs( const std::vector<ObjectCalcer*>& os,
+ KigPart& d,
+ KigWidget& v
+ ) const = 0;
+
+ /**
+ * return a string describing what you would use \p o for if it were
+ * selected... \p o should be part of \p sel .
+ */
+ virtual QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const = 0;
+
+ /**
+ * return a string describing what argument you want next, if the
+ * given selection of objects were selected.
+ */
+ virtual QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const = 0;
+
+ /**
+ * show a preliminary version of what you would do when \ref handleArgs
+ * would be called.. E.g. if this constructor normally constructs a
+ * locus through some 5 points, then it will try to draw a locus
+ * through whatever number of points it gets..
+ */
+ virtual void handlePrelim( KigPainter& p,
+ const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const = 0;
+
+ virtual void plug( KigPart* doc, KigGUIAction* kact ) = 0;
+
+ virtual bool isTransform() const = 0;
+ virtual bool isTest() const;
+ virtual bool isIntersection() const;
+
+ /**
+ * Which construct mode should be used for this ObjectConstructor.
+ * In fact, this is not a pretty design. The Kig
+ * GUIAction-ObjectConstructor stuff should be reworked into a
+ * general GUIAction, which just models something which can be
+ * executed given a certain number of arguments. The code for
+ * drawPrelim and such should all be in the ConstructMode, and the
+ * new GUIAction should just start the correct KigMode with the
+ * correct arguments.
+ *
+ * This function is only overridden in TestConstructor.
+ */
+ virtual BaseConstructMode* constructMode( KigPart& doc );
+};
+
+/**
+ * This class provides wraps ObjectConstructor in a more simple
+ * interface for the most common object types..
+ */
+class StandardConstructorBase
+ : public ObjectConstructor
+{
+ const char* mdescname;
+ const char* mdesc;
+ const char* miconfile;
+ const ArgsParser& margsparser;
+public:
+ StandardConstructorBase( const char* descname,
+ const char* desc,
+ const char* iconfile,
+ const ArgsParser& parser );
+
+ virtual ~StandardConstructorBase();
+
+ const QString descriptiveName() const;
+ const QString description() const;
+ const QCString iconFileName( const bool canBeNull = false ) const;
+
+ const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const;
+ virtual const int wantArgs(
+ const std::vector<ObjectCalcer*>& os, const KigDocument& d,
+ const KigWidget& v
+ ) const;
+
+ void handleArgs( const std::vector<ObjectCalcer*>& os,
+ KigPart& d,
+ KigWidget& v
+ ) const;
+
+ void handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const;
+
+ virtual void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const = 0;
+
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v ) const;
+
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+
+ virtual std::vector<ObjectHolder*> build(
+ const std::vector<ObjectCalcer*>& os,
+ KigDocument& d, KigWidget& w
+ ) const = 0;
+};
+
+/**
+ * A standard implementation of StandardConstructorBase for simple
+ * types..
+ */
+class SimpleObjectTypeConstructor
+ : public StandardConstructorBase
+{
+ const ArgsParserObjectType* mtype;
+public:
+ SimpleObjectTypeConstructor(
+ const ArgsParserObjectType* t, const char* descname,
+ const char* desc, const char* iconfile );
+
+ ~SimpleObjectTypeConstructor();
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const;
+
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os,
+ KigDocument& d,
+ KigWidget& w ) const;
+
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+/**
+ * A standard implementation of StandardConstructorBase for property
+ * objects...
+ */
+class PropertyObjectConstructor
+ : public StandardConstructorBase
+{
+ ArgsParser mparser;
+ const char* mpropinternalname;
+public:
+ PropertyObjectConstructor(
+ const ObjectImpType* imprequirement, const char* usetext,
+ const char* selectstat, const char* descname, const char* desc,
+ const char* iconfile, const char* propertyinternalname );
+
+ ~PropertyObjectConstructor();
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const;
+
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os,
+ KigDocument& d, KigWidget& w ) const;
+
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+/**
+ * This class is the equivalent of \ref SimpleObjectTypeConstructor
+ * for object types that are constructed in groups of more than one.
+ * For example, the intersection of a circle and line in general
+ * produces two points, in general. Internally, we differentiate
+ * betweem them by passing them a parameter of ( in this case ) 1 or
+ * -1. There are still other object types that work the same, and
+ * they all require this sort of parameter.
+ * E.g. CubicLineIntersectionType takes a parameter between 1 and 3.
+ * This class knows about that, and constructs the objects along this
+ * scheme..
+ */
+class MultiObjectTypeConstructor
+ : public StandardConstructorBase
+{
+ const ArgsParserObjectType* mtype;
+ std::vector<int> mparams;
+ ArgsParser mparser;
+public:
+ MultiObjectTypeConstructor(
+ const ArgsParserObjectType* t, const char* descname,
+ const char* desc, const char* iconfile,
+ const std::vector<int>& params );
+ MultiObjectTypeConstructor(
+ const ArgsParserObjectType* t, const char* descname,
+ const char* desc, const char* iconfile,
+ int a, int b, int c = -999, int d = -999 );
+ ~MultiObjectTypeConstructor();
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const;
+
+ std::vector<ObjectHolder*> build(
+ const std::vector<ObjectCalcer*>& os,
+ KigDocument& d, KigWidget& w ) const;
+
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+/**
+ * This class is a collection of some other ObjectConstructors, that
+ * makes them appear to the user as a single ObjectConstructor. It is
+ * e.g. used for the "intersection" constructor.
+ */
+class MergeObjectConstructor
+ : public ObjectConstructor
+{
+ const char* mdescname;
+ const char* mdesc;
+ const char* miconfilename;
+ typedef std::vector<ObjectConstructor*> vectype;
+ vectype mctors;
+public:
+ MergeObjectConstructor( const char* descname, const char* desc,
+ const char* iconfilename );
+ ~MergeObjectConstructor();
+
+ void merge( ObjectConstructor* e );
+
+ const QString descriptiveName() const;
+ const QString description() const;
+ const QCString iconFileName( const bool canBeNull = false ) const;
+
+ const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const;
+ const int wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v ) const;
+
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+
+ void handleArgs( const std::vector<ObjectCalcer*>& os, KigPart& d, KigWidget& v ) const;
+
+ void handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v ) const;
+
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+/**
+ * MacroConstructor is a class that represents Kig macro's: these are
+ * constructed by the user, and defined by a set of input and a set of
+ * output objects. The macro-constructor saves the way in which the
+ * output objects have been built from the input objects, and when
+ * given similar input objects, it will produce objects in the given
+ * way. The data is saved in a \ref ObjectHierarchy.
+ */
+class MacroConstructor
+ : public ObjectConstructor
+{
+ ObjectHierarchy mhier;
+ QString mname;
+ QString mdesc;
+ bool mbuiltin;
+ QCString miconfile;
+ ArgsParser mparser;
+public:
+ MacroConstructor( const std::vector<ObjectCalcer*>& input, const std::vector<ObjectCalcer*>& output,
+ const QString& name, const QString& description,
+ const QCString& iconfile = 0 );
+ MacroConstructor( const ObjectHierarchy& hier, const QString& name,
+ const QString& desc,
+ const QCString& iconfile = 0 );
+ ~MacroConstructor();
+
+ const ObjectHierarchy& hierarchy() const;
+
+ const QString descriptiveName() const;
+ const QString description() const;
+ const QCString iconFileName( const bool canBeNull = false ) const;
+
+ const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const;
+ const int wantArgs( const std::vector<ObjectCalcer*>& os, const KigDocument& d,
+ const KigWidget& v ) const;
+
+ void handleArgs( const std::vector<ObjectCalcer*>& os, KigPart& d,
+ KigWidget& v ) const;
+
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const;
+
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+
+ void handlePrelim( KigPainter& p, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const;
+
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ void setBuiltin( bool builtin );
+
+ /**
+ * is this the ctor for a transformation type. We want to know this
+ * cause transform types are shown separately in an object's RMB
+ * menu..
+ */
+ bool isTransform() const;
+
+ void setName( const QString& name );
+ void setDescription( const QString& desc );
+ void setIcon( QCString& icon );
+};
+
+#endif
diff --git a/kig/misc/object_hierarchy.cc b/kig/misc/object_hierarchy.cc
new file mode 100644
index 00000000..9102051a
--- /dev/null
+++ b/kig/misc/object_hierarchy.cc
@@ -0,0 +1,774 @@
+// 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_hierarchy.h"
+
+#include "../objects/object_holder.h"
+#include "../objects/other_type.h"
+#include "../objects/object_imp.h"
+#include "../objects/object_imp_factory.h"
+#include "../objects/object_type_factory.h"
+#include "../objects/bogus_imp.h"
+#include "../objects/transform_types.h"
+#include "../objects/object_type.h"
+
+#include <kglobal.h>
+#include <qdom.h>
+
+class ObjectHierarchy::Node
+{
+public:
+ enum { ID_PushStack, ID_ApplyType, ID_FetchProp };
+ virtual int id() const = 0;
+
+ virtual ~Node();
+ virtual Node* copy() const = 0;
+
+ virtual void apply( std::vector<const ObjectImp*>& stack, int loc,
+ const KigDocument& ) const = 0;
+
+ virtual void apply( std::vector<ObjectCalcer*>& stack, int loc ) const = 0;
+
+ // this function is used to check whether the final objects depend
+ // on the given objects. The dependsstack contains a set of
+ // booleans telling which parts of the hierarchy certainly depend on
+ // the given objects. In this function, the node should check
+ // whether any of its parents have true set, and if so, set its own
+ // value to true.
+ virtual void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const = 0;
+ // this function is used to check whether the given objects are all
+ // used by one or more of the final objects. The usedstack contains
+ // a set of booleans telling which parts of the hierarchy are
+ // certainly ancestors of the final objects. In this function, the
+ // node should set all of its parents' booleans to true.
+ virtual void checkArgumentsUsed( std::vector<bool>& usedstack ) const = 0;
+};
+
+ObjectHierarchy::Node::~Node()
+{
+}
+
+class PushStackNode
+ : public ObjectHierarchy::Node
+{
+ ObjectImp* mimp;
+public:
+ PushStackNode( ObjectImp* imp ) : mimp( imp ) {}
+ ~PushStackNode();
+
+ const ObjectImp* imp() const { return mimp; }
+
+ int id() const;
+ Node* copy() const;
+ void apply( std::vector<const ObjectImp*>& stack,
+ int loc, const KigDocument& ) const;
+ void apply( std::vector<ObjectCalcer*>& stack, int loc ) const;
+
+ void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const;
+ void checkArgumentsUsed( std::vector<bool>& usedstack ) const;
+};
+
+void PushStackNode::checkArgumentsUsed( std::vector<bool>& ) const
+{
+}
+
+void PushStackNode::apply( std::vector<ObjectCalcer*>& stack, int loc ) const
+{
+ stack[loc] = new ObjectConstCalcer( mimp->copy() );
+}
+
+void PushStackNode::checkDependsOnGiven( std::vector<bool>&, int ) const {
+ // pushstacknode depends on nothing..
+ return;
+}
+
+int PushStackNode::id() const { return ID_PushStack; }
+
+PushStackNode::~PushStackNode()
+{
+ delete mimp;
+}
+
+ObjectHierarchy::Node* PushStackNode::copy() const
+{
+ return new PushStackNode( mimp->copy() );
+}
+
+void PushStackNode::apply( std::vector<const ObjectImp*>& stack,
+ int loc, const KigDocument& ) const
+{
+ stack[loc] = mimp->copy();
+}
+
+class ApplyTypeNode
+ : public ObjectHierarchy::Node
+{
+ const ObjectType* mtype;
+ std::vector<int> mparents;
+public:
+ ApplyTypeNode( const ObjectType* type, const std::vector<int>& parents )
+ : mtype( type ), mparents( parents ) {}
+ ~ApplyTypeNode();
+ Node* copy() const;
+
+ const ObjectType* type() const { return mtype; }
+ const std::vector<int>& parents() const { return mparents; }
+
+ int id() const;
+ void apply( std::vector<const ObjectImp*>& stack,
+ int loc, const KigDocument& ) const;
+ void apply( std::vector<ObjectCalcer*>& stack, int loc ) const;
+
+ void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const;
+ void checkArgumentsUsed( std::vector<bool>& usedstack ) const;
+};
+
+int ApplyTypeNode::id() const { return ID_ApplyType; }
+
+void ApplyTypeNode::checkArgumentsUsed( std::vector<bool>& usedstack ) const
+{
+ for ( uint i = 0; i < mparents.size(); ++i )
+ {
+ usedstack[mparents[i]] = true;
+ }
+}
+
+void ApplyTypeNode::checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const
+{
+ bool result = false;
+ for ( uint i = 0; i < mparents.size(); ++i )
+ if ( dependsstack[mparents[i]] == true ) result = true;
+ dependsstack[loc] = result;
+}
+
+ApplyTypeNode::~ApplyTypeNode()
+{
+}
+
+ObjectHierarchy::Node* ApplyTypeNode::copy() const
+{
+ return new ApplyTypeNode( mtype, mparents );
+}
+
+void ApplyTypeNode::apply( std::vector<ObjectCalcer*>& stack, int loc ) const
+{
+ std::vector<ObjectCalcer*> parents;
+ for ( uint i = 0; i < mparents.size(); ++i )
+ parents.push_back( stack[ mparents[i] ] );
+ stack[loc] = new ObjectTypeCalcer( mtype, parents );
+}
+
+void ApplyTypeNode::apply( std::vector<const ObjectImp*>& stack,
+ int loc, const KigDocument& doc ) const
+{
+ Args args;
+ for ( uint i = 0; i < mparents.size(); ++i )
+ args.push_back( stack[mparents[i]] );
+ args = mtype->sortArgs( args );
+ stack[loc] = mtype->calc( args, doc );
+}
+
+class FetchPropertyNode
+ : public ObjectHierarchy::Node
+{
+ mutable int mpropid;
+ int mparent;
+ const QCString mname;
+public:
+ // propid is a cache of the location of name in the parent's
+ // propertiesInternalNames(), just as it is in PropertyObject. We
+ // don't want to ever save this value, since we cannot guarantee it
+ // remains consistent if we add properties some place..
+ FetchPropertyNode( const int parent, const QCString& name, const int propid = -1 )
+ : mpropid( propid ), mparent( parent ), mname( name ) {}
+ ~FetchPropertyNode();
+ Node* copy() const;
+
+ void checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const;
+ void checkArgumentsUsed( std::vector<bool>& usedstack ) const;
+ int parent() const { return mparent; }
+ const QCString& propinternalname() const { return mname; }
+
+ int id() const;
+ void apply( std::vector<const ObjectImp*>& stack,
+ int loc, const KigDocument& ) const;
+ void apply( std::vector<ObjectCalcer*>& stack, int loc ) const;
+};
+
+FetchPropertyNode::~FetchPropertyNode()
+{
+}
+
+void FetchPropertyNode::checkArgumentsUsed( std::vector<bool>& usedstack ) const
+{
+ usedstack[mparent] = true;
+}
+
+void FetchPropertyNode::checkDependsOnGiven( std::vector<bool>& dependsstack, int loc ) const
+{
+ dependsstack[loc] = dependsstack[mparent];
+}
+
+ObjectHierarchy::Node* FetchPropertyNode::copy() const
+{
+ return new FetchPropertyNode( mparent, mname, mpropid );
+}
+
+int FetchPropertyNode::id() const
+{
+ return ID_FetchProp;
+}
+
+void FetchPropertyNode::apply( std::vector<const ObjectImp*>& stack,
+ int loc, const KigDocument& d ) const
+{
+ assert( stack[mparent] );
+ if ( mpropid == -1 ) mpropid = stack[mparent]->propertiesInternalNames().findIndex( mname );
+ if ( mpropid != -1 )
+ stack[loc] = stack[mparent]->property( mpropid, d );
+ else
+ stack[loc] = new InvalidImp();
+}
+
+void FetchPropertyNode::apply( std::vector<ObjectCalcer*>& stack, int loc ) const
+{
+ if ( mpropid == -1 )
+ mpropid = stack[mparent]->imp()->propertiesInternalNames().findIndex( mname );
+ assert( mpropid != -1 );
+ stack[loc] = new ObjectPropertyCalcer( stack[mparent], mpropid );
+}
+
+std::vector<ObjectImp*> ObjectHierarchy::calc( const Args& a, const KigDocument& doc ) const
+{
+ assert( a.size() == mnumberofargs );
+ for ( uint i = 0; i < a.size(); ++i )
+ assert( a[i]->inherits( margrequirements[i] ) );
+
+ std::vector<const ObjectImp*> stack;
+ stack.resize( mnodes.size() + mnumberofargs, 0 );
+ std::copy( a.begin(), a.end(), stack.begin() );
+ for( uint i = 0; i < mnodes.size(); ++i )
+ {
+ mnodes[i]->apply( stack, mnumberofargs + i, doc );
+ };
+ for ( uint i = mnumberofargs; i < stack.size() - mnumberofresults; ++i )
+ delete stack[i];
+ if ( stack.size() < mnumberofargs + mnumberofresults )
+ {
+ std::vector<ObjectImp*> ret;
+ ret.push_back( new InvalidImp );
+ return ret;
+ }
+ else
+ {
+ std::vector<ObjectImp*> ret;
+ for ( uint i = stack.size() - mnumberofresults; i < stack.size(); ++i )
+ ret.push_back( const_cast<ObjectImp*>( stack[i] ) );
+ return ret;
+ };
+}
+
+int ObjectHierarchy::visit( const ObjectCalcer* o, std::map<const ObjectCalcer*, int>& seenmap,
+ bool needed, bool neededatend )
+{
+ using namespace std;
+
+ std::map<const ObjectCalcer*, int>::iterator smi = seenmap.find( o );
+ if ( smi != seenmap.end() )
+ {
+ if ( neededatend )
+ {
+ // neededatend means that this object is one of the resultant
+ // objects. Therefore, its node has to appear at the end,
+ // because that's where we expect it.. We therefore copy it
+ // there using CopyObjectType..
+ int ret = mnumberofargs + mnodes.size();
+ std::vector<int> parents;
+ parents.push_back( smi->second );
+ mnodes.push_back( new ApplyTypeNode( CopyObjectType::instance(), parents ) );
+ return ret;
+ }
+ else return smi->second;
+ }
+
+ std::vector<ObjectCalcer*> p( o->parents() );
+ // we check if o descends from the given objects..
+ bool descendsfromgiven = false;
+ std::vector<int> parents;
+ parents.resize( p.size(), -1 );
+ for ( uint i = 0; i < p.size(); ++i )
+ {
+ int v = visit( p[i], seenmap, false );
+ parents[i] = v;
+ descendsfromgiven |= (v != -1);
+ };
+
+ if ( ! descendsfromgiven && ! ( needed && o->imp()->isCache() ) )
+ {
+ if ( needed )
+ {
+ assert( ! o->imp()->isCache() );
+ // o is an object that does not depend on the given objects, but
+ // is needed by other objects, so we just have to just save its
+ // current value here.
+ Node* node = new PushStackNode( o->imp()->copy() );
+ mnodes.push_back( node );
+ int ret = mnodes.size() + mnumberofargs - 1;
+ seenmap[o] = ret;
+ return ret;
+ }
+ else
+ return -1;
+ };
+
+ return storeObject( o, p, parents, seenmap );
+}
+
+ObjectHierarchy::~ObjectHierarchy()
+{
+ for ( uint i = 0; i < mnodes.size(); ++i ) delete mnodes[i];
+}
+
+ObjectHierarchy::ObjectHierarchy( const ObjectHierarchy& h )
+ : mnumberofargs( h.mnumberofargs ), mnumberofresults( h.mnumberofresults ),
+ margrequirements( h.margrequirements ), musetexts( h.musetexts ),
+ mselectstatements( h.mselectstatements )
+{
+ mnodes.reserve( h.mnodes.size() );
+ for ( uint i = 0; i < h.mnodes.size(); ++i )
+ mnodes.push_back( h.mnodes[i]->copy() );
+}
+
+ObjectHierarchy ObjectHierarchy::withFixedArgs( const Args& a ) const
+{
+ assert( a.size() <= mnumberofargs );
+ ObjectHierarchy ret( *this );
+
+ ret.mnumberofargs -= a.size();
+ ret.margrequirements.resize( ret.mnumberofargs );
+
+ std::vector<Node*> newnodes( mnodes.size() + a.size() );
+ std::vector<Node*>::iterator newnodesiter = newnodes.begin();
+ for ( uint i = 0; i < a.size(); ++i )
+ {
+ assert( ! a[i]->isCache() );
+ *newnodesiter++ = new PushStackNode( a[i]->copy() );
+ };
+ std::copy( ret.mnodes.begin(), ret.mnodes.end(), newnodesiter );
+ ret.mnodes = newnodes;
+
+ return ret;
+}
+
+void ObjectHierarchy::init( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to )
+{
+ mnumberofargs = from.size();
+ mnumberofresults = to.size();
+ margrequirements.resize( from.size(), ObjectImp::stype() );
+ musetexts.resize( margrequirements.size(), "" );
+ std::map<const ObjectCalcer*, int> seenmap;
+ for ( uint i = 0; i < from.size(); ++i )
+ seenmap[from[i]] = i;
+ for ( std::vector<ObjectCalcer*>::const_iterator i = to.begin(); i != to.end(); ++i )
+ {
+ std::vector<ObjectCalcer*> parents = (*i)->parents();
+ for ( std::vector<ObjectCalcer*>::const_iterator j = parents.begin();
+ j != parents.end(); ++j )
+ visit( *j, seenmap, true );
+ }
+ for ( std::vector<ObjectCalcer*>::const_iterator i = to.begin(); i != to.end(); ++i )
+ visit( *i, seenmap, true, true );
+
+ mselectstatements.resize( margrequirements.size(), "" );
+}
+
+ObjectHierarchy::ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to )
+{
+ std::vector<ObjectCalcer*> tov;
+ tov.push_back( const_cast<ObjectCalcer*>( to ) );
+ init( from, tov );
+}
+
+ObjectHierarchy::ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to )
+{
+ init( from, to );
+}
+
+void ObjectHierarchy::serialize( QDomElement& parent, QDomDocument& doc ) const
+{
+ int id = 1;
+ for ( uint i = 0; i < mnumberofargs; ++i )
+ {
+ QDomElement e = doc.createElement( "input" );
+ e.setAttribute( "id", id++ );
+ e.setAttribute( "requirement", margrequirements[i]->internalName() );
+ // we don't save these atm, since the user can't define them.
+ // we only load them from builtin macro's.
+// QDomElement ut = doc.createElement( "UseText" );
+// ut.appendChild( doc.createTextNode( QString::fromLatin1(musetexts[i].c_str() ) ) );
+// e.appendChild( ut );
+// QDomElement ss = doc.createElement( "SelectStatement" );
+// ss.appendChild( doc.createTextNode( QString::fromLatin1(mselectstatements[i].c_str() ) ) );
+// e.appendChild( ss );
+ parent.appendChild( e );
+ }
+
+ for ( uint i = 0; i < mnodes.size(); ++i )
+ {
+ bool result = mnodes.size() - ( id - mnumberofargs - 1 ) <= mnumberofresults;
+ QDomElement e = doc.createElement( result ? "result" : "intermediate" );
+ e.setAttribute( "id", id++ );
+
+ if ( mnodes[i]->id() == Node::ID_ApplyType )
+ {
+ const ApplyTypeNode* node = static_cast<const ApplyTypeNode*>( mnodes[i] );
+ e.setAttribute( "action", "calc" );
+ e.setAttribute( "type", QString::fromLatin1( node->type()->fullName() ) );
+ for ( uint i = 0; i < node->parents().size(); ++i )
+ {
+ int parent = node->parents()[i] + 1;
+ QDomElement arge = doc.createElement( "arg" );
+ arge.appendChild( doc.createTextNode( QString::number( parent ) ) );
+ e.appendChild( arge );
+ };
+ }
+ else if ( mnodes[i]->id() == Node::ID_FetchProp )
+ {
+ const FetchPropertyNode* node = static_cast<const FetchPropertyNode*>( mnodes[i] );
+ e.setAttribute( "action", "fetch-property" );
+ e.setAttribute( "property", node->propinternalname() );
+ QDomElement arge = doc.createElement( "arg" );
+ arge.appendChild( doc.createTextNode( QString::number( node->parent() + 1 ) ) );
+ e.appendChild( arge );
+ }
+ else
+ {
+ assert( mnodes[i]->id() == ObjectHierarchy::Node::ID_PushStack );
+ const PushStackNode* node = static_cast<const PushStackNode*>( mnodes[i] );
+ e.setAttribute( "action", "push" );
+ QString type = ObjectImpFactory::instance()->serialize( *node->imp(), e, doc );
+ e.setAttribute( "type", type );
+ };
+
+ parent.appendChild( e );
+ };
+}
+
+ObjectHierarchy::ObjectHierarchy()
+ : mnumberofargs( 0 ), mnumberofresults( 0 )
+{
+}
+
+ObjectHierarchy* ObjectHierarchy::buildSafeObjectHierarchy( const QDomElement& parent, QString& error )
+{
+#define KIG_GENERIC_PARSE_ERROR \
+ { \
+ error = i18n( "An error was encountered at line %1 in file %2." ) \
+ .arg( __LINE__ ).arg( __FILE__ ); \
+ return 0; \
+ }
+
+ ObjectHierarchy* obhi = new ObjectHierarchy();
+
+ bool ok = true;
+ QString tmp;
+ QDomElement e = parent.firstChild().toElement();
+ for (; !e.isNull(); e = e.nextSibling().toElement() )
+ {
+ if ( e.tagName() != "input" ) break;
+
+ tmp = e.attribute( "id" );
+ uint id = tmp.toInt( &ok );
+ if ( !ok ) KIG_GENERIC_PARSE_ERROR;
+
+ obhi->mnumberofargs = kMax( id, obhi->mnumberofargs );
+
+ tmp = e.attribute( "requirement" );
+ const ObjectImpType* req = ObjectImpType::typeFromInternalName( tmp.latin1() );
+ if ( req == 0 ) req = ObjectImp::stype(); // sucks, i know..
+ obhi->margrequirements.resize( obhi->mnumberofargs, ObjectImp::stype() );
+ obhi->musetexts.resize( obhi->mnumberofargs, "" );
+ obhi->mselectstatements.resize( obhi->mnumberofargs, "" );
+ obhi->margrequirements[id - 1] = req;
+ obhi->musetexts[id - 1] = req->selectStatement();
+ QDomElement esub = e.firstChild().toElement();
+ for ( ; !esub.isNull(); esub = esub.nextSibling().toElement() )
+ {
+ if ( esub.tagName() == "UseText" )
+ {
+ obhi->musetexts[id - 1] = esub.text().latin1();
+ }
+ else if ( esub.tagName() == "SelectStatement" )
+ {
+ obhi->mselectstatements[id - 1] = esub.text().latin1();
+ }
+ else
+ {
+ // broken file ? ignore...
+ }
+ }
+ }
+ for (; !e.isNull(); e = e.nextSibling().toElement() )
+ {
+ bool result = e.tagName() == "result";
+ if ( result ) ++obhi->mnumberofresults;
+
+ tmp = e.attribute( "id" );
+ int id = tmp.toInt( &ok );
+ if ( !ok ) KIG_GENERIC_PARSE_ERROR;
+
+ tmp = e.attribute( "action" );
+ Node* newnode = 0;
+ if ( tmp == "calc" )
+ {
+ // ApplyTypeNode
+ QCString typen = e.attribute( "type" ).latin1();
+ const ObjectType* type = ObjectTypeFactory::instance()->find( typen );
+ if ( ! type )
+ {
+ 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( typen );
+ return 0;
+ }
+
+ std::vector<int> parents;
+ for ( QDomNode p = e.firstChild(); !p.isNull(); p = p.nextSibling() )
+ {
+ QDomElement q = p.toElement();
+ if ( q.isNull() ) KIG_GENERIC_PARSE_ERROR; // see above
+ if ( q.tagName() != "arg" ) KIG_GENERIC_PARSE_ERROR;
+ int pid = q.text().toInt(&ok );
+ if ( !ok ) KIG_GENERIC_PARSE_ERROR;
+ parents.push_back( pid - 1 );
+ };
+ newnode = new ApplyTypeNode( type, parents );
+ }
+ else if ( tmp == "fetch-property" )
+ {
+ // FetchPropertyNode
+ QCString propname = e.attribute( "property" ).latin1();
+ QDomElement arge = e.firstChild().toElement();
+ int parent = arge.text().toInt( &ok );
+ if ( !ok ) KIG_GENERIC_PARSE_ERROR;
+ newnode = new FetchPropertyNode( parent - 1, propname );
+ }
+ else
+ {
+ // PushStackNode
+ if ( e.attribute( "action" ) != "push" ) KIG_GENERIC_PARSE_ERROR;
+ QString typen = e.attribute( "type" );
+ if ( typen.isNull() ) KIG_GENERIC_PARSE_ERROR;
+ ObjectImp* imp = ObjectImpFactory::instance()->deserialize( typen, e, error );
+ if ( ( ! imp ) && !error.isEmpty() ) return 0;
+ newnode = new PushStackNode( imp );
+ };
+ obhi->mnodes.resize( kMax( size_t(id - obhi->mnumberofargs), obhi->mnodes.size() ) );
+ obhi->mnodes[id - obhi->mnumberofargs - 1] = newnode;
+ };
+
+ // if we are here, all went fine
+ return obhi;
+}
+
+ArgsParser ObjectHierarchy::argParser() const
+{
+ std::vector<ArgsParser::spec> specs;
+ for ( uint i = 0; i < margrequirements.size(); ++i )
+ {
+ const ObjectImpType* req = margrequirements[i];
+ ArgsParser::spec spec;
+ spec.type = req;
+ spec.usetext = musetexts[i];
+ spec.selectstat = mselectstatements[i];
+ specs.push_back( spec );
+ };
+ return ArgsParser( specs );
+}
+
+std::vector<ObjectCalcer*> ObjectHierarchy::buildObjects( const std::vector<ObjectCalcer*>& os, const KigDocument& doc ) const
+{
+ assert( os.size() == mnumberofargs );
+ for ( uint i = 0; i < os.size(); ++i )
+ assert( os[i]->imp()->inherits( margrequirements[i] ) );
+
+ std::vector<ObjectCalcer*> stack;
+ stack.resize( mnodes.size() + mnumberofargs, 0 );
+ std::copy( os.begin(), os.end(), stack.begin() );
+
+ for( uint i = 0; i < mnodes.size(); ++i )
+ {
+ mnodes[i]->apply( stack, mnumberofargs + i );
+ stack[mnumberofargs + i]->calc( doc );
+ };
+
+ std::vector<ObjectCalcer*> ret( stack.end() - mnumberofresults, stack.end() );
+
+ return ret;
+}
+
+const ObjectImpType* ObjectHierarchy::idOfLastResult() const
+{
+ const Node* n = mnodes.back();
+ if ( n->id() == Node::ID_PushStack )
+ return static_cast<const PushStackNode*>( n )->imp()->type();
+ else if ( n->id() == Node::ID_FetchProp )
+ return ObjectImp::stype();
+ else
+ return static_cast<const ApplyTypeNode*>( n )->type()->resultId();
+}
+
+ObjectHierarchy ObjectHierarchy::transformFinalObject( const Transformation& t ) const
+{
+ assert( mnumberofresults == 1 );
+ ObjectHierarchy ret( *this );
+ ret.mnodes.push_back( new PushStackNode( new TransformationImp( t ) ) );
+
+ std::vector<int> parents;
+ parents.push_back( ret.mnodes.size() - 1);
+ parents.push_back( ret.mnodes.size() );
+ const ObjectType* type = ApplyTransformationObjectType::instance();
+ ret.mnodes.push_back( new ApplyTypeNode( type, parents ) );
+ return ret;
+}
+
+bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs )
+{
+ if ( ! ( lhs.mnumberofargs == rhs.mnumberofargs &&
+ lhs.mnumberofresults == rhs.mnumberofresults &&
+ lhs.margrequirements == rhs.margrequirements &&
+ lhs.mnodes.size() == rhs.mnodes.size() ) )
+ return false;
+
+ // this isn't entirely correct, but it will do, because we don't
+ // really want to know whether the hierarchies are different, but
+ // whether rhs has changed with regard to lhs..
+ for ( uint i = 0; i < lhs.mnodes.size(); ++i )
+ if ( lhs.mnodes[i] != lhs.mnodes[i] )
+ return false;
+
+ return true;
+}
+
+bool ObjectHierarchy::resultDoesNotDependOnGiven() const
+{
+ std::vector<bool> dependsstack( mnodes.size() + mnumberofargs, false );
+
+ for ( uint i = 0; i < mnumberofargs; ++i )
+ dependsstack[i] = true;
+ for ( uint i = 0; i < mnodes.size(); ++i )
+ mnodes[i]->checkDependsOnGiven( dependsstack, i + mnumberofargs );
+ for ( uint i = dependsstack.size() - mnumberofresults; i < dependsstack.size(); ++i )
+ if ( !dependsstack[i] )
+ return true;
+ return false;
+}
+
+// returns the "minimum" of a and b ( in the partially ordered set of
+// ObjectImpType's, using the inherits member function as comparison,
+// if you for some reason like this sort of non-sense ;) ). This
+// basically means: return the type that inherits the other type,
+// because if another type inherits the lowermost type, then it will
+// also inherit the other..
+const ObjectImpType* lowermost( const ObjectImpType* a, const ObjectImpType* b )
+{
+ if ( a->inherits( b ) ) return a;
+ assert( b->inherits( a ) );
+ return b;
+}
+
+// this function is part of the visit procedure really. It is
+// factored out, because it recurses for cache ObjectImp's. What this
+// does is, it makes sure that object o is calcable, by putting
+// appropriate Node's in mnodes.. po is o->parents() and pl contains
+// the location of objects that are already in mnodes and -1
+// otherwise.. -1 means we have to store their ObjectImp, unless
+// they're cache ObjectImp's etc.
+int ObjectHierarchy::storeObject( const ObjectCalcer* o, const std::vector<ObjectCalcer*>& po, std::vector<int>& pl,
+ std::map<const ObjectCalcer*, int>& seenmap )
+{
+ for ( uint i = 0; i < po.size(); ++i )
+ {
+ if ( pl[i] == -1 )
+ {
+ // we can't store cache ObjectImp's..
+ if ( po[i]->imp()->isCache() )
+ {
+ pl[i] = visit( po[i], seenmap, true, false );
+ }
+ else
+ {
+ Node* argnode = new PushStackNode( po[i]->imp()->copy() );
+ mnodes.push_back( argnode );
+ int argloc = mnumberofargs + mnodes.size() - 1;
+ seenmap[po[i]] = argloc;
+ pl[i] = argloc;
+ };
+ }
+ else if ( (uint) pl[i] < mnumberofargs )
+ {
+ ObjectCalcer* parent = o->parents()[i];
+ std::vector<ObjectCalcer*> opl = o->parents();
+
+ margrequirements[pl[i]] =
+ lowermost( margrequirements[pl[i]],
+ o->impRequirement( parent, opl ) );
+ musetexts[pl[i]] = margrequirements[pl[i]]->selectStatement();
+ };
+ };
+ if ( dynamic_cast<const ObjectTypeCalcer*>( o ) )
+ mnodes.push_back( new ApplyTypeNode( static_cast<const ObjectTypeCalcer*>( o )->type(), pl ) );
+ else if ( dynamic_cast<const ObjectPropertyCalcer*>( o ) )
+ {
+ assert( pl.size() == 1 );
+ int parent = pl.front();
+ ObjectCalcer* op = po.front();
+ assert( op );
+ uint propid = static_cast<const ObjectPropertyCalcer*>( o )->propId();
+ assert( propid < op->imp()->propertiesInternalNames().size() );
+ mnodes.push_back( new FetchPropertyNode( parent, op->imp()->propertiesInternalNames()[propid], propid ) );
+ }
+ else
+ assert( false );
+ seenmap[o] = mnumberofargs + mnodes.size() - 1;
+ return mnumberofargs + mnodes.size() - 1;
+}
+
+ObjectHierarchy::ObjectHierarchy( const ObjectCalcer* from, const ObjectCalcer* to )
+{
+ std::vector<ObjectCalcer*> fromv;
+ fromv.push_back( const_cast<ObjectCalcer*>( from ) );
+ std::vector<ObjectCalcer*> tov;
+ tov.push_back( const_cast<ObjectCalcer*>( to ) );
+ init( fromv, tov );
+}
+
+bool ObjectHierarchy::allGivenObjectsUsed() const
+{
+ std::vector<bool> usedstack( mnodes.size() + mnumberofargs, false );
+ for ( uint i = mnodes.size() - mnumberofresults; i < mnodes.size(); ++i )
+ usedstack[i + mnumberofargs] = true;
+ for ( int i = mnodes.size() - 1; i >= 0; --i )
+ if ( usedstack[i + mnumberofargs] )
+ mnodes[i]->checkArgumentsUsed( usedstack );
+ for ( uint i = 0; i < mnumberofargs; ++i )
+ if ( ! usedstack[i] ) return false;
+ return true;
+}
+
diff --git a/kig/misc/object_hierarchy.h b/kig/misc/object_hierarchy.h
new file mode 100644
index 00000000..3133dc7c
--- /dev/null
+++ b/kig/misc/object_hierarchy.h
@@ -0,0 +1,111 @@
+// 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_MISC_OBJECT_HIERARCHY_H
+#define KIG_MISC_OBJECT_HIERARCHY_H
+
+#include "../objects/common.h"
+
+#include <map>
+#include <vector>
+#include <string>
+
+class ObjectImpType;
+class ArgsParser;
+
+class ObjectHierarchy
+{
+public:
+ class Node;
+private:
+ std::vector<Node*> mnodes;
+ uint mnumberofargs;
+ uint mnumberofresults;
+ std::vector<const ObjectImpType*> margrequirements;
+ std::vector<std::string> musetexts;
+ std::vector<std::string> mselectstatements;
+
+ // these two are really part of the constructor...
+ int visit( const ObjectCalcer* o, std::map<const ObjectCalcer*, int>&,
+ bool needed, bool neededatend = false);
+ int storeObject( const ObjectCalcer*, const std::vector<ObjectCalcer*>& po, std::vector<int>& pl,
+ std::map<const ObjectCalcer*, int>& seenmap );
+
+ friend bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs );
+
+ void init( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to );
+
+ /**
+ * this constructor is private since it should be used only by the static
+ * constructor buildSafeObjectHierarchy
+ *
+ * \see ObjectHierarchy::buildSafeObjectHierarchy
+ */
+ ObjectHierarchy();
+
+public:
+ ObjectHierarchy( const ObjectCalcer* from, const ObjectCalcer* to );
+ ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const ObjectCalcer* to );
+ ObjectHierarchy( const std::vector<ObjectCalcer*>& from, const std::vector<ObjectCalcer*>& to );
+ ObjectHierarchy( const ObjectHierarchy& h );
+ ~ObjectHierarchy();
+
+ /**
+ * this creates a new ObjectHierarchy, that takes a.size() less
+ * arguments, but uses copies of the ObjectImp's in \p a instead..
+ */
+ ObjectHierarchy withFixedArgs( const Args& a ) const;
+
+ std::vector<ObjectImp*> calc( const Args& a, const KigDocument& doc ) const;
+
+ /**
+ * saves the ObjectHierarchy data in children xml tags of \p parent ..
+ */
+ void serialize( QDomElement& parent, QDomDocument& doc ) const;
+ /**
+ * Deserialize the ObjectHierarchy data from the xml element \p parent ..
+ * Since this operation can fail for some reasons, we provide it as a
+ * static to return 0 in case of error.
+ */
+ static ObjectHierarchy* buildSafeObjectHierarchy( const QDomElement& parent, QString& error );
+// ObjectHierarchy( const QDomElement& parent );
+
+ /**
+ * build a set of objects that interdepend according to this
+ * ObjectHierarchy.. Only the result objects are returned. Helper
+ * objects that connect the given objects with the returned objects,
+ * can only be found by following the returned objects' parents()
+ * methods..
+ */
+ std::vector<ObjectCalcer*> buildObjects( const std::vector<ObjectCalcer*>& os, const KigDocument& ) const;
+
+ ArgsParser argParser() const;
+
+ uint numberOfArgs() const { return mnumberofargs; }
+ uint numberOfResults() const { return mnumberofresults; }
+
+ const ObjectImpType* idOfLastResult() const;
+
+ bool resultDoesNotDependOnGiven() const;
+ bool allGivenObjectsUsed() const;
+
+ ObjectHierarchy transformFinalObject( const Transformation& t ) const;
+};
+
+bool operator==( const ObjectHierarchy& lhs, const ObjectHierarchy& rhs );
+
+#endif
diff --git a/kig/misc/rect.cc b/kig/misc/rect.cc
new file mode 100644
index 00000000..dc28de82
--- /dev/null
+++ b/kig/misc/rect.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 "rect.h"
+#include "common.h"
+
+bool operator==( const Rect& r, const Rect& s )
+{
+ return ( r.bottomLeft() == s.bottomLeft()
+ && r.width() == s.width()
+ && r.height() == s.height() );
+}
+
+kdbgstream& operator<<( kdbgstream& s, const Rect& t )
+{
+ s << "left: " << t.left()
+ << "bottom: " << t.bottom()
+ << "right: " << t.right()
+ << "top: " << t.top()
+ << endl;
+ return s;
+}
+
+Rect::Rect( const Coordinate bottomLeft, const Coordinate topRight )
+ : mBottomLeft(bottomLeft)
+{
+ mwidth = topRight.x - bottomLeft.x;
+ mheight = topRight.y - bottomLeft.y;
+ normalize();
+}
+
+Rect::Rect( const Coordinate p, const double width, const double height )
+ : mBottomLeft(p),
+ mwidth(width),
+ mheight(height)
+{
+ normalize();
+}
+
+Rect::Rect( double xa, double ya, double width, double height )
+ : mBottomLeft( xa, ya ),
+ mwidth( width ),
+ mheight( height )
+{
+ normalize();
+}
+
+Rect::Rect( const Rect& r )
+ : mBottomLeft (r.mBottomLeft),
+ mwidth(r.mwidth),
+ mheight(r.mheight)
+{
+ normalize();
+}
+
+Rect::Rect()
+ : mwidth(0),
+ mheight(0)
+{
+}
+
+void Rect::setBottomLeft( const Coordinate p )
+{
+ mBottomLeft = p;
+}
+
+void Rect::setBottomRight( const Coordinate p )
+{
+ mBottomLeft = p - Coordinate(mwidth,0);
+}
+
+void Rect::setTopRight( const Coordinate p )
+{
+ mBottomLeft = p - Coordinate(mwidth, mheight);
+}
+
+void Rect::setCenter( const Coordinate p )
+{
+ mBottomLeft = p - Coordinate(mwidth, mheight)/2;
+}
+
+void Rect::setLeft( const double p )
+{
+ double r = right();
+ mBottomLeft.x = p;
+ setRight( r );
+}
+
+void Rect::setRight( const double p )
+{
+ mwidth = p - left();
+}
+
+void Rect::setBottom( const double p )
+{
+ double t = top();
+ mBottomLeft.y = p;
+ setTop( t );
+}
+
+void Rect::setTop( const double p )
+{
+ mheight = p - bottom();
+}
+
+void Rect::setWidth( const double w )
+{
+ mwidth = w;
+}
+
+void Rect::setHeight( const double h )
+{
+ mheight = h;
+}
+
+void Rect::normalize()
+{
+ if ( mwidth < 0 )
+ {
+ mBottomLeft.x += mwidth;
+ mwidth = -mwidth;
+ };
+ if ( mheight < 0 )
+ {
+ mBottomLeft.y += mheight;
+ mheight = -mheight;
+ };
+}
+
+void Rect::moveBy( const Coordinate p )
+{
+ mBottomLeft += p;
+}
+
+void Rect::scale( const double r )
+{
+ mwidth *= r;
+ mheight *= r;
+}
+
+
+QRect Rect::toQRect() const
+{
+ return QRect(mBottomLeft.toQPoint(), topRight().toQPoint());
+}
+
+Coordinate Rect::bottomLeft() const
+{
+ return mBottomLeft;
+}
+
+Coordinate Rect::bottomRight() const
+{
+ return mBottomLeft + Coordinate(mwidth, 0);
+}
+
+Coordinate Rect::topLeft() const
+{
+ return mBottomLeft + Coordinate(0, mheight);
+}
+
+Coordinate Rect::topRight() const
+{
+ return mBottomLeft + Coordinate(mwidth, mheight);
+}
+
+Coordinate Rect::center() const
+{
+ return mBottomLeft + Coordinate(mwidth, mheight)/2;
+}
+
+double Rect::left() const
+{
+ return mBottomLeft.x;
+}
+double Rect::right() const
+{
+ return left() + mwidth;
+}
+double Rect::bottom() const
+{
+ return mBottomLeft.y;
+}
+
+double Rect::top() const
+{
+ return bottom() + mheight;
+}
+
+double Rect::width() const
+{
+ return mwidth;
+}
+
+double Rect::height() const
+{
+ return mheight;
+}
+
+bool Rect::contains( const Coordinate& p, double allowed_miss ) const
+{
+ return p.x - left() >= - allowed_miss &&
+ p.y - bottom() >= - allowed_miss &&
+ p.x - left() - width() <= allowed_miss &&
+ p.y - bottom() - height() <= allowed_miss;
+}
+
+bool Rect::contains( const Coordinate& p ) const
+{
+ return p.x >= left() &&
+ p.y >= bottom() &&
+ p.x - left() <= width() &&
+ p.y - bottom() <= height();
+}
+
+bool Rect::intersects( const Rect& p ) const
+{
+ // never thought it was this simple :)
+ if( p.left() < left() && p.right() < left()) return false;
+ if( p.left() > right() && p.right() > right()) return false;
+ if( p.bottom() < bottom() && p.top() < bottom()) return false;
+ if( p.bottom() > top() && p.top() > top()) return false;
+ return true;
+}
+
+void Rect::setContains( Coordinate p )
+{
+ normalize();
+ if( p.x < left() ) setLeft( p.x );
+ if( p.x > right() ) setRight(p.x);
+ if( p.y < bottom() ) setBottom( p.y );
+ if( p.y > top() ) setTop( p.y );
+}
+
+Rect Rect::normalized() const
+{
+ Rect t = *this;
+ (void) t.normalize();
+ return t;
+}
+
+Rect Rect::fromQRect( const QRect& r )
+{
+ return Rect( r.left(), r.top(), r.right(), r.bottom() );
+}
+
+void Rect::setTopLeft( const Coordinate p )
+{
+ Coordinate bl = Coordinate( p.x, p.y - mheight );
+ setBottomLeft( bl );
+}
+
+Rect operator|( const Rect& lhs, const Rect& rhs )
+{
+ Rect r( lhs );
+ r |= rhs;
+ return r;
+}
+
+void Rect::eat( const Rect& r )
+{
+ setLeft( kigMin( left(), r.left() ) );
+ setRight( kigMax( right(), r.right() ) );
+ setBottom( kigMin( bottom(), r.bottom() ) );
+ setTop( kigMax( top(), r.top() ) );
+}
+
+Rect Rect::matchShape( const Rect& rhs, bool shrink ) const
+{
+ Rect ret = *this;
+ Coordinate c = center();
+ double v = width()/height(); // current ratio
+ double w = rhs.width()/rhs.height(); // wanted ratio
+
+ // we don't show less than r, if the dimensions don't match, we
+ // extend r into some dimension...
+ if( ( v > w ) ^ shrink )
+ ret.setHeight( ret.width() / w );
+ else
+ ret.setWidth( ret.height() * w );
+
+ ret.setCenter(c);
+ return ret.normalized();
+}
+
+bool Rect::valid()
+{
+ return mBottomLeft.valid() && mwidth != double_inf && mheight != double_inf;
+}
+
+Rect Rect::invalidRect()
+{
+ return Rect( Coordinate::invalidCoord(), double_inf, double_inf );
+}
diff --git a/kig/misc/rect.h b/kig/misc/rect.h
new file mode 100644
index 00000000..a222d1ab
--- /dev/null
+++ b/kig/misc/rect.h
@@ -0,0 +1,140 @@
+/**
+ This file is part of Kig, a KDE program for Interactive Geometry...
+ 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 RECT_H
+#define RECT_H
+
+#include "coordinate.h"
+
+#include <qrect.h>
+#include <kdebug.h>
+
+/**
+ * like Coordinate is a QPoint replacement with doubles, this is a
+ * QRect replacement with doubles...
+ */
+class Rect
+{
+public:
+ /**
+ * constructors...
+ */
+ Rect( const Coordinate bottomLeft, const Coordinate topRight );
+ Rect( const Coordinate bottomLeft, const double width, const double height );
+ Rect( double xa, double ya, double width, double height );
+ Rect( const Rect& r );
+ Rect();
+ static Rect invalidRect();
+
+
+ bool valid();
+
+ void setBottomLeft( const Coordinate p );
+ void setTopLeft( const Coordinate p );
+ void setTopRight( const Coordinate p );
+ void setBottomRight( const Coordinate p );
+ void setCenter( const Coordinate p );
+ void setLeft( const double p);
+ void setRight( const double p);
+ void setTop( const double p );
+ void setBottom( const double p );
+ void setWidth( const double w );
+ void setHeight( const double h );
+ /**
+ * this makes sure width and height are > 0 ...
+ */
+ void normalize();
+ /**
+ * this makes sure p is in the rect, extending it if necessary...
+ */
+ void setContains( Coordinate p );
+ /**
+ * moves the rect while keeping the size constant...
+ */
+ void moveBy( const Coordinate p );
+ /**
+ * synonym for moveBy...
+ */
+ Rect& operator+=( const Coordinate p ) { moveBy(p); return *this; }
+ /**
+ * scale: only the size changes, topLeft is kept where it is...
+ */
+ void scale( const double r );
+ /**
+ * synonym for scale...
+ */
+ Rect& operator*=( const double r ) { scale(r); return *this; }
+ Rect& operator/=( const double r ) { scale(1/r); return *this; }
+
+ /**
+ * This expands the rect so that it contains r. It has friends
+ * '|=' and '|' below...
+ */
+ void eat( const Rect& r );
+
+ /**
+ * synonym for eat..
+ */
+ Rect& operator|=( const Rect& rhs ) { eat( rhs ); return *this; }
+
+ /**
+ * return a rect which is a copy of this rect, but has an aspect
+ * ratio equal to rhs's one.. if \p shrink is true, the rect will be
+ * shrunk, otherwise extended.. The center of the new rect is the
+ * same as this rect's center..
+ */
+ Rect matchShape( const Rect& rhs, bool shrink = false ) const;
+
+ QRect toQRect() const;
+ Coordinate bottomLeft() const;
+ Coordinate bottomRight() const;
+ Coordinate topLeft() const;
+ Coordinate topRight() const;
+ Coordinate center() const;
+ double left() const;
+ double right() const;
+ double bottom() const;
+ double top() const;
+ double width() const;
+ double height() const;
+ bool contains( const Coordinate& p ) const;
+ bool contains( const Coordinate& p, double allowed_miss ) const;
+ bool intersects( const Rect& p ) const;
+ Rect normalized() const;
+ friend kdbgstream& operator<<( kdbgstream& s, const Rect& t );
+
+ static Rect fromQRect( const QRect& );
+protected:
+ Coordinate mBottomLeft;
+ double mwidth;
+ double mheight;
+};
+
+bool operator==( const Rect& r, const Rect& s );
+kdbgstream& operator<<( kdbgstream& s, const Rect& t );
+/**
+ * this operator returns a Rect that contains both the given
+ * rects..
+ */
+Rect operator|( const Rect& lhs, const Rect& rhs );
+
+#endif
+
diff --git a/kig/misc/screeninfo.cc b/kig/misc/screeninfo.cc
new file mode 100644
index 00000000..c1418876
--- /dev/null
+++ b/kig/misc/screeninfo.cc
@@ -0,0 +1,92 @@
+// 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 "screeninfo.h"
+
+#include <cmath>
+
+ScreenInfo::ScreenInfo( const Rect& docRect, const QRect& viewRect )
+ : mkrect( docRect.normalized() ), mqrect( viewRect.normalize() )
+{
+}
+
+Rect ScreenInfo::fromScreen( const QRect& r ) const
+{
+ return Rect(
+ fromScreen( r.topLeft() ),
+ fromScreen( r.bottomRight() )
+ ).normalized();
+}
+
+Coordinate ScreenInfo::fromScreen( const QPoint& p ) const
+{
+ // invert the y-axis: 0 is at the bottom !
+ Coordinate t( p.x(), mqrect.height() - p.y() );
+ t *= mkrect.width();
+ t /= mqrect.width();
+ return t + mkrect.bottomLeft();
+}
+
+QPoint ScreenInfo::toScreen( const Coordinate& p ) const
+{
+ Coordinate t = p - mkrect.bottomLeft();
+ t *= mqrect.width();
+ t /= mkrect.width();
+ // invert the y-axis: 0 is at the bottom !
+ return QPoint( (int) t.x, mqrect.height() - (int) t.y );
+}
+
+QRect ScreenInfo::toScreen( const Rect& r ) const
+{
+ return QRect(
+ toScreen( r.bottomLeft() ),
+ toScreen( r.topRight() )
+ ).normalize();
+}
+
+double ScreenInfo::pixelWidth() const
+{
+ Coordinate a = fromScreen( QPoint( 0, 0 ) );
+ Coordinate b = fromScreen( QPoint( 0, 1000 ) );
+ return std::fabs( b.y - a.y ) / 1000;
+}
+
+const Rect& ScreenInfo::shownRect() const
+{
+ return mkrect;
+}
+
+void ScreenInfo::setShownRect( const Rect& r )
+{
+ mkrect = r;
+}
+
+const QRect ScreenInfo::viewRect() const
+{
+ return mqrect;
+}
+
+void ScreenInfo::setViewRect( const QRect& r )
+{
+ mqrect = r;
+}
+
+double ScreenInfo::normalMiss( int width ) const
+{
+ int twidth = width == -1 ? 1 : width;
+ return (twidth+2)*pixelWidth();
+}
diff --git a/kig/misc/screeninfo.h b/kig/misc/screeninfo.h
new file mode 100644
index 00000000..b7f94c49
--- /dev/null
+++ b/kig/misc/screeninfo.h
@@ -0,0 +1,57 @@
+// 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 SCREENINFO_H
+#define SCREENINFO_H
+
+#include <qrect.h>
+
+#include "rect.h"
+
+/**
+ * ScreenInfo is a simple utility class that maps a region of the
+ * document onto a region of the screen. It is used by both
+ * KigPainter and KigWidget, and the objects use it in their calc()
+ * method...
+ */
+class ScreenInfo
+{
+ Rect mkrect;
+ QRect mqrect;
+public:
+ ScreenInfo( const Rect& docRect, const QRect& viewRect );
+
+ Coordinate fromScreen( const QPoint& p ) const;
+ Rect fromScreen( const QRect& r ) const;
+
+ QPoint toScreen( const Coordinate& p ) const;
+ QRect toScreen( const Rect& r ) const;
+
+ double pixelWidth() const;
+
+ double normalMiss( int width ) const;
+
+ const Rect& shownRect() const;
+
+ void setShownRect( const Rect& r );
+
+ const QRect viewRect() const;
+
+ void setViewRect( const QRect& r );
+};
+
+#endif
diff --git a/kig/misc/special_constructors.cc b/kig/misc/special_constructors.cc
new file mode 100644
index 00000000..04c8a097
--- /dev/null
+++ b/kig/misc/special_constructors.cc
@@ -0,0 +1,1628 @@
+// 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 "special_constructors.h"
+
+#include "calcpaths.h"
+#include "common.h"
+#include "conic-common.h"
+#include "guiaction.h"
+#include "kigpainter.h"
+
+#include "../kig/kig_part.h"
+#include "../modes/construct_mode.h"
+#include "../objects/bogus_imp.h"
+#include "../objects/centerofcurvature_type.h"
+#include "../objects/circle_imp.h"
+#include "../objects/conic_imp.h"
+#include "../objects/conic_types.h"
+#include "../objects/cubic_imp.h"
+#include "../objects/intersection_types.h"
+#include "../objects/inversion_type.h"
+#include "../objects/line_imp.h"
+#include "../objects/line_type.h"
+#include "../objects/locus_imp.h"
+#include "../objects/object_calcer.h"
+#include "../objects/object_drawer.h"
+#include "../objects/object_factory.h"
+#include "../objects/object_holder.h"
+#include "../objects/object_imp.h"
+#include "../objects/object_type.h"
+#include "../objects/other_imp.h"
+#include "../objects/other_type.h"
+#include "../objects/point_imp.h"
+#include "../objects/point_type.h"
+#include "../objects/polygon_imp.h"
+#include "../objects/polygon_type.h"
+#include "../objects/tangent_type.h"
+#include "../objects/text_imp.h"
+#include "../objects/transform_types.h"
+
+#include <qpen.h>
+
+#include <klocale.h>
+
+#include <algorithm>
+#include <functional>
+
+class ConicConicIntersectionConstructor
+ : public StandardConstructorBase
+{
+protected:
+ ArgsParser mparser;
+public:
+ ConicConicIntersectionConstructor();
+ ~ConicConicIntersectionConstructor();
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+class ConicLineIntersectionConstructor
+ : public MultiObjectTypeConstructor
+{
+public:
+ ConicLineIntersectionConstructor();
+ ~ConicLineIntersectionConstructor();
+};
+
+class ArcLineIntersectionConstructor
+ : public MultiObjectTypeConstructor
+{
+public:
+ ArcLineIntersectionConstructor();
+ ~ArcLineIntersectionConstructor();
+};
+
+ConicRadicalConstructor::ConicRadicalConstructor()
+ : StandardConstructorBase(
+ I18N_NOOP( "Radical Lines for Conics" ),
+ I18N_NOOP( "The lines constructed through the intersections "
+ "of two conics. This is also defined for "
+ "non-intersecting conics." ),
+ "conicsradicalline", mparser ),
+ mtype( ConicRadicalType::instance() ),
+ mparser( mtype->argsParser().without( IntImp::stype() ) )
+{
+}
+
+ConicRadicalConstructor::~ConicRadicalConstructor()
+{
+}
+
+void ConicRadicalConstructor::drawprelim(
+ const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& doc ) const
+{
+ if ( parents.size() == 2 && parents[0]->imp()->inherits( ConicImp::stype() ) &&
+ parents[1]->imp()->inherits( ConicImp::stype() ) )
+ {
+ Args args;
+ std::transform( parents.begin(), parents.end(),
+ std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) );
+ for ( int i = -1; i < 2; i += 2 )
+ {
+ IntImp root( i );
+ IntImp zeroindex( 1 );
+ args.push_back( &root );
+ args.push_back( &zeroindex );
+ ObjectImp* data = mtype->calc( args, doc );
+ drawer.draw( *data, p, true );
+ delete data; data = 0;
+ args.pop_back();
+ args.pop_back();
+ };
+ };
+}
+
+std::vector<ObjectHolder*> ConicRadicalConstructor::build( const std::vector<ObjectCalcer*>& os, KigDocument&, KigWidget& ) const
+{
+ using namespace std;
+ std::vector<ObjectHolder*> ret;
+ ObjectCalcer* zeroindexcalcer = new ObjectConstCalcer( new IntImp( 1 ) );
+ for ( int i = -1; i < 2; i += 2 )
+ {
+ std::vector<ObjectCalcer*> args;
+ std::copy( os.begin(), os.end(), back_inserter( args ) );
+ args.push_back( new ObjectConstCalcer( new IntImp( i ) ) );
+ // we use only one zeroindex dataobject, so that if you switch one
+ // radical line around, then the other switches along..
+ args.push_back( zeroindexcalcer );
+ ret.push_back(
+ new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
+ };
+ return ret;
+}
+
+static const struct ArgsParser::spec argsspecpp[] =
+{
+ { PointImp::stype(), I18N_NOOP( "Moving Point" ),
+ I18N_NOOP( "Select the moving point, which will be moved around while drawing the locus..." ), false },
+ { PointImp::stype(), I18N_NOOP( "Following Point" ),
+ I18N_NOOP( "Select the following point, whose locations the locus will be drawn through..." ), true }
+};
+
+LocusConstructor::LocusConstructor()
+ : StandardConstructorBase( I18N_NOOP( "Locus" ), I18N_NOOP( "A locus" ),
+ "locus", margsparser ),
+ margsparser( argsspecpp, 2 )
+{
+}
+
+LocusConstructor::~LocusConstructor()
+{
+}
+
+void LocusConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const
+{
+ // this function is rather ugly, but it is necessary to do it this
+ // way in order to play nice with Kig's design..
+
+ if ( parents.size() != 2 ) return;
+ const ObjectTypeCalcer* constrained = dynamic_cast<ObjectTypeCalcer*>( parents.front() );
+ const ObjectCalcer* moving = parents.back();
+ if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
+ {
+ // moving is in fact the constrained point.. swap them..
+ moving = parents.front();
+ constrained = dynamic_cast<const ObjectTypeCalcer*>( parents.back() );
+ assert( constrained );
+ };
+ assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) );
+
+ const ObjectImp* oimp = constrained->parents().back()->imp();
+ if( !oimp->inherits( CurveImp::stype() ) )
+ oimp = constrained->parents().front()->imp();
+ assert( oimp->inherits( CurveImp::stype() ) );
+ const CurveImp* cimp = static_cast<const CurveImp*>( oimp );
+
+ ObjectHierarchy hier( constrained, moving );
+
+ LocusImp limp( cimp->copy(), hier );
+ drawer.draw( limp, p, true );
+}
+
+const int LocusConstructor::wantArgs(
+ const std::vector<ObjectCalcer*>& os, const KigDocument&, const KigWidget&
+ ) const
+{
+ int ret = margsparser.check( os );
+ if ( ret == ArgsParser::Invalid ) return ret;
+ else if ( os.size() != 2 ) return ret;
+ if ( dynamic_cast<ObjectTypeCalcer*>( os.front() ) &&
+ static_cast<ObjectTypeCalcer*>( os.front() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
+ {
+ std::set<ObjectCalcer*> children = getAllChildren( os.front() );
+ return children.find( os.back() ) != children.end() ? ret : ArgsParser::Invalid;
+ }
+ if ( dynamic_cast<ObjectTypeCalcer*>( os.back() ) &&
+ static_cast<ObjectTypeCalcer*>( os.back() )->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
+ {
+ std::set<ObjectCalcer*> children = getAllChildren( os.back() );
+ return children.find( os.front() ) != children.end() ? ret : ArgsParser::Invalid;
+ }
+ return ArgsParser::Invalid;
+}
+
+std::vector<ObjectHolder*> LocusConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
+{
+ std::vector<ObjectHolder*> ret;
+ assert( parents.size() == 2 );
+
+ ObjectTypeCalcer* constrained = dynamic_cast<ObjectTypeCalcer*>( parents.front() );
+ ObjectCalcer* moving = parents.back();
+ if ( ! constrained || ! constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
+ {
+ // moving is in fact the constrained point.. swap them..
+ moving = parents.front();
+ constrained = dynamic_cast<ObjectTypeCalcer*>( parents.back() );
+ assert( constrained );
+ };
+ assert( constrained->type()->inherits( ObjectType::ID_ConstrainedPointType ) );
+
+ ret.push_back( ObjectFactory::instance()->locus( constrained, moving ) );
+ return ret;
+}
+
+QString LocusConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& os,
+ const KigDocument&, const KigWidget& ) const
+{
+ if ( dynamic_cast<const ObjectTypeCalcer*>( &o ) &&
+ static_cast<const ObjectTypeCalcer&>( o ).type()->inherits( ObjectType::ID_ConstrainedPointType ) &&
+ ( os.empty() || !dynamic_cast<ObjectTypeCalcer*>( os[0] ) ||
+ !static_cast<const ObjectTypeCalcer*>( os[0] )->type()->inherits( ObjectType::ID_ConstrainedPointType ) )
+ ) return i18n( "Moving Point" );
+ else return i18n( "Dependent Point" );
+}
+
+void ConicRadicalConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+void LocusConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool ConicRadicalConstructor::isTransform() const
+{
+ return mtype->isTransform();
+}
+
+bool LocusConstructor::isTransform() const
+{
+ return false;
+}
+
+/*
+ * generic polygon constructor
+ */
+
+PolygonBNPTypeConstructor::PolygonBNPTypeConstructor()
+ : mtype( PolygonBNPType::instance() )
+{
+}
+
+PolygonBNPTypeConstructor::~PolygonBNPTypeConstructor()
+{
+}
+
+const QString PolygonBNPTypeConstructor::descriptiveName() const
+{
+ return i18n("Polygon by Its Vertices");
+}
+
+const QString PolygonBNPTypeConstructor::description() const
+{
+ return i18n("Construct a polygon by giving its vertices");
+}
+
+const QCString PolygonBNPTypeConstructor::iconFileName( const bool ) const
+{
+ return "kig_polygon";
+}
+
+const bool PolygonBNPTypeConstructor::isAlreadySelectedOK(
+ const std::vector<ObjectCalcer*>& os, const int& pos ) const
+{
+ if ( pos == 0 && os.size() >= 3 ) return true;
+ return false;
+}
+
+const int PolygonBNPTypeConstructor::wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument&,
+ const KigWidget& ) const
+{
+ int count=os.size() - 1;
+
+ for ( int i = 0; i <= count; i++ )
+ {
+ if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid;
+ }
+ if ( count < 3 ) return ArgsParser::Valid;
+ if ( os[0] == os[count] ) return ArgsParser::Complete;
+ return ArgsParser::Valid;
+}
+
+void PolygonBNPTypeConstructor::handleArgs(
+ const std::vector<ObjectCalcer*>& os, KigPart& d,
+ KigWidget& v ) const
+{
+ std::vector<ObjectHolder*> bos = build( os, d.document(), v );
+ for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
+ i != bos.end(); ++i )
+ {
+ (*i)->calc( d.document() );
+ }
+
+ d.addObjects( bos );
+}
+
+void PolygonBNPTypeConstructor::handlePrelim(
+ KigPainter& p, const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d, const KigWidget&
+ ) const
+{
+ uint count = os.size();
+ if ( count < 2 ) return;
+
+ for ( uint i = 0; i < count; i++ )
+ {
+ assert ( os[i]->imp()->inherits( PointImp::stype() ) );
+ }
+
+ std::vector<ObjectCalcer*> args = os;
+ p.setBrushStyle( Qt::NoBrush );
+ p.setBrushColor( Qt::red );
+ p.setPen( QPen ( Qt::red, 1) );
+ p.setWidth( -1 ); // -1 means the default width for the object being
+ // drawn..
+
+ ObjectDrawer drawer( Qt::red );
+ drawprelim( drawer, p, args, d );
+}
+
+QString PolygonBNPTypeConstructor::useText( const ObjectCalcer&, const std::vector<ObjectCalcer*>& os,
+ const KigDocument&, const KigWidget& ) const
+{
+ if ( os.size() > 3 )
+ return i18n("... with this vertex (click on the first vertex to terminate construction)");
+ else return i18n("Construct a polygon with this vertex");
+}
+
+QString PolygonBNPTypeConstructor::selectStatement(
+ const std::vector<ObjectCalcer*>&, const KigDocument&,
+ const KigWidget& ) const
+{
+ return i18n("Select a point to be a vertex of the new polygon...");
+}
+
+void PolygonBNPTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const
+{
+ if ( parents.size() < 2 ) return;
+
+ std::vector<Coordinate> points;
+
+ for ( uint i = 0; i < parents.size(); ++i )
+ {
+ const Coordinate vertex =
+ static_cast<const PointImp*>( parents[i]->imp() )->coordinate();
+ points.push_back( vertex );
+ }
+
+ if ( parents.size() == 2 )
+ {
+ SegmentImp segment = SegmentImp( points[0], points[1] );
+ drawer.draw( segment, p, true );
+ } else {
+ PolygonImp polygon = PolygonImp( points );
+ drawer.draw( polygon, p, true );
+ }
+}
+
+std::vector<ObjectHolder*> PolygonBNPTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
+{
+ uint count = parents.size() - 1;
+ assert ( count >= 3 );
+ std::vector<ObjectCalcer*> args;
+ for ( uint i = 0; i < count; ++i ) args.push_back( parents[i] );
+ ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args );
+ ObjectHolder* h = new ObjectHolder( calcer );
+ std::vector<ObjectHolder*> ret;
+ ret.push_back( h );
+ return ret;
+}
+
+void PolygonBNPTypeConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool PolygonBNPTypeConstructor::isTransform() const
+{
+ return false;
+}
+
+/*
+ * construction of polygon vertices
+ */
+
+static const struct ArgsParser::spec argsspecpv[] =
+{
+ { PolygonImp::stype(), I18N_NOOP( "Polygon" ),
+ I18N_NOOP( "Construct the vertices of this polygon..." ), true }
+};
+
+PolygonVertexTypeConstructor::PolygonVertexTypeConstructor()
+ : StandardConstructorBase( I18N_NOOP( "Vertices of a Polygon" ),
+ I18N_NOOP( "The vertices of a polygon." ),
+ "polygonvertices", margsparser ),
+ mtype( PolygonVertexType::instance() ),
+ margsparser( argsspecpv, 1 )
+{
+}
+
+PolygonVertexTypeConstructor::~PolygonVertexTypeConstructor()
+{
+}
+
+void PolygonVertexTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const
+{
+ if ( parents.size() != 1 ) return;
+
+ const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
+ const std::vector<Coordinate> points = polygon->points();
+
+ int sides = points.size();
+ for ( int i = 0; i < sides; ++i )
+ {
+ PointImp point = PointImp( points[i] );
+ drawer.draw( point, p, true );
+ }
+}
+
+std::vector<ObjectHolder*> PolygonVertexTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
+{
+ std::vector<ObjectHolder*> ret;
+ assert( parents.size() == 1 );
+ const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
+ const std::vector<Coordinate> points = polygon->points();
+
+ int sides = points.size();
+
+ for ( int i = 0; i < sides; ++i )
+ {
+ ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) );
+ std::vector<ObjectCalcer*> args( parents );
+ args.push_back( d );
+ ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
+ }
+ return ret;
+}
+
+void PolygonVertexTypeConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool PolygonVertexTypeConstructor::isTransform() const
+{
+ return false;
+}
+
+/*
+ * construction of polygon sides
+ */
+
+static const struct ArgsParser::spec argsspecps[] =
+{
+ { PolygonImp::stype(), I18N_NOOP( "Polygon" ),
+ I18N_NOOP( "Construct the sides of this polygon..." ), false }
+};
+
+PolygonSideTypeConstructor::PolygonSideTypeConstructor()
+ : StandardConstructorBase( I18N_NOOP( "Sides of a Polygon" ),
+ I18N_NOOP( "The sides of a polygon." ),
+ "polygonsides", margsparser ),
+ mtype( PolygonSideType::instance() ),
+ margsparser( argsspecps, 1 )
+{
+}
+
+PolygonSideTypeConstructor::~PolygonSideTypeConstructor()
+{
+}
+
+void PolygonSideTypeConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const
+{
+ if ( parents.size() != 1 ) return;
+
+ const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
+ const std::vector<Coordinate> points = polygon->points();
+
+ uint sides = points.size();
+ for ( uint i = 0; i < sides; ++i )
+ {
+ uint nexti = ( i + 1 < sides )?(i + 1):0;
+ SegmentImp segment = SegmentImp( points[i], points[nexti] );
+ drawer.draw( segment, p, true );
+ }
+}
+
+std::vector<ObjectHolder*> PolygonSideTypeConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
+{
+ std::vector<ObjectHolder*> ret;
+ assert( parents.size() == 1 );
+ const PolygonImp* polygon = dynamic_cast<const PolygonImp*>( parents.front()->imp() );
+ const std::vector<Coordinate> points = polygon->points();
+
+ uint sides = points.size();
+
+ for ( uint i = 0; i < sides; ++i )
+ {
+ ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( i ) );
+ std::vector<ObjectCalcer*> args( parents );
+ args.push_back( d );
+ ret.push_back( new ObjectHolder( new ObjectTypeCalcer( mtype, args ) ) );
+ }
+ return ret;
+}
+
+void PolygonSideTypeConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool PolygonSideTypeConstructor::isTransform() const
+{
+ return false;
+}
+
+/*
+ * polygon by center and vertex
+ */
+
+PolygonBCVConstructor::PolygonBCVConstructor()
+ : mtype( PolygonBCVType::instance() )
+{
+}
+
+PolygonBCVConstructor::~PolygonBCVConstructor()
+{
+}
+
+const QString PolygonBCVConstructor::descriptiveName() const
+{
+ return i18n("Regular Polygon with Given Center");
+}
+
+const QString PolygonBCVConstructor::description() const
+{
+ return i18n("Construct a regular polygon with a given center and vertex");
+}
+
+const QCString PolygonBCVConstructor::iconFileName( const bool ) const
+{
+ return "hexagonbcv";
+}
+
+const bool PolygonBCVConstructor::isAlreadySelectedOK(
+ const std::vector<ObjectCalcer*>&, const int& ) const
+{
+ return false;
+}
+
+const int PolygonBCVConstructor::wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument&,
+ const KigWidget& ) const
+{
+ if ( os.size() > 3 ) return ArgsParser::Invalid;
+
+ uint imax = ( os.size() <= 2) ? os.size() : 2;
+ for ( uint i = 0; i < imax; ++i )
+ if ( ! ( os[i]->imp()->inherits( PointImp::stype() ) ) ) return ArgsParser::Invalid;
+
+ if ( os.size() < 3 ) return ArgsParser::Valid;
+
+ if ( ! ( os[2]->imp()->inherits( BogusPointImp::stype() ) ) )
+ return ArgsParser::Invalid;
+
+ return ArgsParser::Complete;
+}
+
+void PolygonBCVConstructor::handleArgs(
+ const std::vector<ObjectCalcer*>& os, KigPart& d,
+ KigWidget& v ) const
+{
+ std::vector<ObjectHolder*> bos = build( os, d.document(), v );
+ for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
+ i != bos.end(); ++i )
+ {
+ (*i)->calc( d.document() );
+ }
+
+ d.addObjects( bos );
+}
+
+void PolygonBCVConstructor::handlePrelim(
+ KigPainter& p, const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d, const KigWidget&
+ ) const
+{
+ if ( os.size() < 2 ) return;
+
+ for ( uint i = 0; i < 2; i++ )
+ {
+ assert ( os[i]->imp()->inherits( PointImp::stype() ) );
+ }
+
+ Coordinate c = static_cast<const PointImp*>( os[0]->imp() )->coordinate();
+ Coordinate v = static_cast<const PointImp*>( os[1]->imp() )->coordinate();
+
+ int nsides = 6;
+ bool moreinfo = false;
+ int winding = 0; // 0 means allow winding > 1
+ if ( os.size() == 3 )
+ {
+ assert ( os[2]->imp()->inherits( BogusPointImp::stype() ) );
+ Coordinate cntrl = static_cast<const PointImp*>( os[2]->imp() )->coordinate();
+ nsides = computeNsides( c, v, cntrl, winding );
+ moreinfo = true;
+ }
+
+ std::vector<ObjectCalcer*> args;
+ args.push_back( os[0] );
+ args.push_back( os[1] );
+ ObjectConstCalcer* ns = new ObjectConstCalcer( new IntImp( nsides ) );
+ args.push_back( ns );
+ if ( winding > 1 )
+ {
+ ns = new ObjectConstCalcer( new IntImp( winding ) );
+ args.push_back( ns );
+ }
+
+ p.setBrushStyle( Qt::NoBrush );
+ p.setBrushColor( Qt::red );
+ p.setPen( QPen ( Qt::red, 1) );
+ p.setWidth( -1 ); // -1 means the default width for the object being
+ // drawn..
+
+ ObjectDrawer drawer( Qt::red );
+ drawprelim( drawer, p, args, d );
+ if ( moreinfo )
+ {
+ p.setPointStyle( 1 );
+ p.setWidth( 6 );
+ double ro = 1.0/(2.5);
+ Coordinate where = getRotatedCoord( c, (1-ro)*c+ro*v, 4*M_PI/5.0 );
+ PointImp ptn = PointImp( where );
+ TextImp text = TextImp( "(5,2)", where, false );
+ ptn.draw( p );
+ text.draw( p );
+ for ( int i = 3; i < 9; ++i )
+ {
+ where = getRotatedCoord( c, v, 2.0*M_PI/i );
+ ptn = PointImp( where );
+ ptn.draw( p );
+ if ( i > 5 ) continue;
+ text = TextImp( QString( "(%1)" ).arg(i), where, false );
+ text.draw( p );
+ }
+ p.setStyle( Qt::DotLine );
+ p.setWidth( 1 );
+ double radius = ( v - c ).length();
+ CircleImp circle = CircleImp( c, radius );
+ circle.draw( p );
+ for ( int i = 2; i < 5; i++ )
+ {
+ ro = 1.0/(i+0.5);
+ CircleImp circle = CircleImp( c, ro*radius );
+ circle.draw( p );
+ }
+ }
+ delete_all( args.begin() + 2, args.end() );
+}
+
+std::vector<ObjectHolder*> PolygonBCVConstructor::build( const std::vector<ObjectCalcer*>& parents, KigDocument&, KigWidget& ) const
+{
+ assert ( parents.size() == 3 );
+ std::vector<ObjectCalcer*> args;
+
+ Coordinate c = static_cast<const PointImp*>( parents[0]->imp() )->coordinate();
+ Coordinate v = static_cast<const PointImp*>( parents[1]->imp() )->coordinate();
+ Coordinate cntrl = static_cast<const PointImp*>( parents[2]->imp() )->coordinate();
+
+ args.push_back( parents[0] );
+ args.push_back( parents[1] );
+ int winding = 0;
+ int nsides = computeNsides( c, v, cntrl, winding );
+ ObjectConstCalcer* d = new ObjectConstCalcer( new IntImp( nsides ) );
+ args.push_back( d );
+ if ( winding > 1 )
+ {
+ d = new ObjectConstCalcer( new IntImp( winding ) );
+ args.push_back( d );
+ }
+
+ ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, args );
+ ObjectHolder* h = new ObjectHolder( calcer );
+ std::vector<ObjectHolder*> ret;
+ ret.push_back( h );
+ return ret;
+}
+
+QString PolygonBCVConstructor::useText( const ObjectCalcer&, const std::vector<ObjectCalcer*>& os,
+ const KigDocument&, const KigWidget& ) const
+{
+ switch ( os.size() )
+ {
+ case 1:
+ return i18n( "Construct a regular polygon with this center" );
+ break;
+
+ case 2:
+ return i18n( "Construct a regular polygon with this vertex" );
+ break;
+
+ case 3:
+ Coordinate c = static_cast<const PointImp*>( os[0]->imp() )->coordinate();
+ Coordinate v = static_cast<const PointImp*>( os[1]->imp() )->coordinate();
+ Coordinate cntrl = static_cast<const PointImp*>( os[2]->imp() )->coordinate();
+ int winding = 0;
+ int nsides = computeNsides( c, v, cntrl, winding );
+
+ if ( winding > 1 )
+ {
+ QString result = QString(
+ i18n( "Adjust the number of sides (%1/%2)" )
+ ).arg( nsides ).arg( winding );
+ return result;
+ } else
+ {
+ QString result = QString(
+ i18n( "Adjust the number of sides (%1)" )
+ ).arg( nsides );
+ return result;
+ }
+ break;
+ }
+
+ return "";
+}
+
+QString PolygonBCVConstructor::selectStatement(
+ const std::vector<ObjectCalcer*>& os, const KigDocument&,
+ const KigWidget& ) const
+{
+ switch ( os.size() )
+ {
+ case 1:
+ return i18n( "Select the center of the new polygon..." );
+ break;
+
+ case 2:
+ return i18n( "Select a vertex for the new polygon..." );
+ break;
+
+ case 3:
+ return i18n( "Move the cursor to get the desired number of sides..." );
+ break;
+ }
+
+ return "";
+}
+
+void PolygonBCVConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& doc ) const
+{
+ if ( parents.size() < 3 || parents.size() > 4 ) return;
+
+ assert ( parents[0]->imp()->inherits( PointImp::stype() ) &&
+ parents[1]->imp()->inherits( PointImp::stype() ) &&
+ parents[2]->imp()->inherits( IntImp::stype() ) );
+
+ if ( parents.size() == 4 )
+ assert ( parents[3]->imp()->inherits( IntImp::stype() ) );
+
+ Args args;
+ std::transform( parents.begin(), parents.end(),
+ std::back_inserter( args ), std::mem_fun( &ObjectCalcer::imp ) );
+
+ ObjectImp* data = mtype->calc( args, doc );
+ drawer.draw( *data, p, true );
+ delete data;
+ data = 0;
+}
+
+void PolygonBCVConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool PolygonBCVConstructor::isTransform() const
+{
+ return false;
+}
+
+Coordinate PolygonBCVConstructor::getRotatedCoord( const Coordinate& c,
+ const Coordinate& v, double alpha ) const
+{
+ double cosalpha = cos(alpha);
+ double sinalpha = sin(alpha);
+ double dx = v.x - c.x;
+ double dy = v.y - c.y;
+ return c + Coordinate( cosalpha*dx - sinalpha*dy, sinalpha*dx + cosalpha*dy );
+}
+
+int PolygonBCVConstructor::computeNsides ( const Coordinate& c,
+ const Coordinate& v, const Coordinate& cntrl, int& winding ) const
+{
+ Coordinate lvect = v - c;
+ Coordinate rvect = cntrl - c;
+
+ double angle = atan2( rvect.y, rvect.x ) - atan2( lvect.y, lvect.x );
+ angle = fabs( angle/(2*M_PI) );
+ while ( angle > 1 ) angle -= 1;
+ if ( angle > 0.5 ) angle = 1 - angle;
+
+ double realsides = 1.0/angle; // this is bigger that 2
+ if ( angle == 0. ) realsides = 3;
+ if ( winding <= 0 ) // free to compute winding
+ {
+ winding = 1;
+ double ratio = lvect.length()/rvect.length();
+ winding = int ( ratio );
+ if ( winding < 1 ) winding = 1;
+ if ( winding > 50 ) winding = 50;
+ }
+ int nsides = int( winding*realsides + 0.5 ); // nsides/winding should be reduced!
+ if ( nsides > 100 ) nsides = 100; // well, 100 seems large enough!
+ if ( nsides < 3 ) nsides = 3;
+ while ( !relativePrimes ( nsides, winding ) ) ++nsides;
+ return nsides;
+}
+
+/*
+ * ConicConic intersection...
+ */
+
+static const ArgsParser::spec argsspectc[] = {
+ { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true },
+ { ConicImp::stype(), "SHOULD NOT BE SEEN", "SHOULD NOT BE SEEN", true }
+};
+
+ConicConicIntersectionConstructor::ConicConicIntersectionConstructor()
+ : StandardConstructorBase( "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "curvelineintersection", mparser ),
+ mparser( argsspectc, 2 )
+{
+}
+
+ConicConicIntersectionConstructor::~ConicConicIntersectionConstructor()
+{
+}
+
+void ConicConicIntersectionConstructor::drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const
+{
+ if ( parents.size() != 2 ) return;
+ assert ( parents[0]->imp()->inherits( ConicImp::stype() ) &&
+ parents[1]->imp()->inherits( ConicImp::stype() ) );
+ const ConicCartesianData conica =
+ static_cast<const ConicImp*>( parents[0]->imp() )->cartesianData();
+ const ConicCartesianData conicb =
+ static_cast<const ConicImp*>( parents[1]->imp() )->cartesianData();
+ bool ok = true;
+ for ( int wr = -1; wr < 2; wr += 2 )
+ {
+ LineData radical = calcConicRadical( conica, conicb, wr, 1, ok );
+ if ( ok )
+ {
+ for ( int wi = -1; wi < 2; wi += 2 )
+ {
+ Coordinate c = calcConicLineIntersect( conica, radical, 0.0, wi );
+ if ( c.valid() ) {
+ PointImp pi( c );
+ drawer.draw( pi, p, true );
+ }
+ };
+ };
+ };
+}
+
+std::vector<ObjectHolder*> ConicConicIntersectionConstructor::build(
+ const std::vector<ObjectCalcer*>& os, KigDocument& doc, KigWidget& ) const
+{
+ assert( os.size() == 2 );
+ std::vector<ObjectHolder*> ret;
+ ObjectCalcer* conica = os[0];
+ ObjectConstCalcer* zeroindexdo = new ObjectConstCalcer( new IntImp( 1 ) );
+
+ for ( int wr = -1; wr < 2; wr += 2 )
+ {
+ std::vector<ObjectCalcer*> args = os;
+ args.push_back( new ObjectConstCalcer( new IntImp( wr ) ) );
+ args.push_back( zeroindexdo );
+ ObjectTypeCalcer* radical =
+ new ObjectTypeCalcer( ConicRadicalType::instance(), args );
+ radical->calc( doc );
+ for ( int wi = -1; wi < 2; wi += 2 )
+ {
+ args.clear();
+ args.push_back( conica );
+ args.push_back( radical );
+ args.push_back( new ObjectConstCalcer( new IntImp( wi ) ) );
+ ret.push_back(
+ new ObjectHolder(
+ new ObjectTypeCalcer(
+ ConicLineIntersectionType::instance(), args ) ) );
+ };
+ };
+ return ret;
+}
+
+void ConicConicIntersectionConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool ConicConicIntersectionConstructor::isTransform() const
+{
+ return false;
+}
+
+ConicLineIntersectionConstructor::ConicLineIntersectionConstructor()
+ : MultiObjectTypeConstructor(
+ ConicLineIntersectionType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "curvelineintersection", -1, 1 )
+{
+}
+
+ConicLineIntersectionConstructor::~ConicLineIntersectionConstructor()
+{
+}
+
+ArcLineIntersectionConstructor::ArcLineIntersectionConstructor()
+ : MultiObjectTypeConstructor(
+ ArcLineIntersectionType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "curvelineintersection", -1, 1 )
+{
+}
+
+ArcLineIntersectionConstructor::~ArcLineIntersectionConstructor()
+{
+}
+
+QString ConicRadicalConstructor::useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>&,
+ const KigDocument&, const KigWidget& ) const
+{
+ if ( o.imp()->inherits( CircleImp::stype() ) )
+ return i18n( "Construct the Radical Lines of This Circle" );
+ else
+ return i18n( "Construct the Radical Lines of This Conic" );
+}
+
+/*
+ * generic affinity and generic projectivity. A unique affinity can be
+ * obtained by specifying the image of three points (four for projectivity)
+ * in the end we need, besides the object to be transformed, a total of
+ * six point or (alternatively) two triangles; our affinity will map the
+ * first triangle onto the second with corresponding ordering of their
+ * vertices. Since we allow for two different ways of specifying the six
+ * points we shall use a Generic constructor, like that for intersections.
+ */
+
+GenericAffinityConstructor::GenericAffinityConstructor()
+ : MergeObjectConstructor(
+ I18N_NOOP( "Generic Affinity" ),
+ I18N_NOOP( "The unique affinity that maps three points (or a triangle) onto three other points (or a triangle)" ),
+ "genericaffinity" )
+{
+ SimpleObjectTypeConstructor* b2tr =
+ new SimpleObjectTypeConstructor(
+ AffinityB2TrType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "genericaffinity" );
+
+ SimpleObjectTypeConstructor* gi3p =
+ new SimpleObjectTypeConstructor(
+ AffinityGI3PType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "genericaffinity" );
+
+ merge( b2tr );
+ merge( gi3p );
+}
+
+GenericAffinityConstructor::~GenericAffinityConstructor() {}
+
+GenericProjectivityConstructor::GenericProjectivityConstructor()
+ : MergeObjectConstructor(
+ I18N_NOOP( "Generic Projective Transformation" ),
+ I18N_NOOP( "The unique projective transformation that maps four points (or a quadrilateral) onto four other points (or a quadrilateral)" ),
+ "genericprojectivity" )
+{
+ SimpleObjectTypeConstructor* b2qu =
+ new SimpleObjectTypeConstructor(
+ ProjectivityB2QuType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "genericprojectivity" );
+
+ SimpleObjectTypeConstructor* gi4p =
+ new SimpleObjectTypeConstructor(
+ ProjectivityGI4PType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "genericprojectivity" );
+
+ merge( b2qu );
+ merge( gi4p );
+}
+
+GenericProjectivityConstructor::~GenericProjectivityConstructor() {}
+
+/*
+ * inversion of points, lines with respect to a circle
+ */
+
+InversionConstructor::InversionConstructor()
+ : MergeObjectConstructor(
+ I18N_NOOP( "Inversion of Point, Line or Circle" ),
+ I18N_NOOP( "The inversion of a point, line or circle with respect to a circle" ),
+ "inversion" )
+{
+ SimpleObjectTypeConstructor* pointobj =
+ new SimpleObjectTypeConstructor(
+ InvertPointType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "inversion" );
+
+ SimpleObjectTypeConstructor* lineobj =
+ new SimpleObjectTypeConstructor(
+ InvertLineType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "inversion" );
+
+ SimpleObjectTypeConstructor* segmentobj =
+ new SimpleObjectTypeConstructor(
+ InvertSegmentType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "inversion" );
+
+ SimpleObjectTypeConstructor* circleobj =
+ new SimpleObjectTypeConstructor(
+ InvertCircleType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "inversion" );
+
+ SimpleObjectTypeConstructor* arcobj =
+ new SimpleObjectTypeConstructor(
+ InvertArcType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "inversion" );
+
+ merge( arcobj );
+ merge( circleobj );
+ merge( pointobj );
+ merge( segmentobj );
+ merge( lineobj );
+}
+
+InversionConstructor::~InversionConstructor() {}
+
+/*
+ * Transport of Measure
+ */
+
+MeasureTransportConstructor::MeasureTransportConstructor()
+ : mtype( MeasureTransportType::instance() )
+{
+}
+
+MeasureTransportConstructor::~MeasureTransportConstructor()
+{
+}
+
+const QString MeasureTransportConstructor::descriptiveName() const
+{
+ return i18n("Measure Transport");
+}
+
+const QString MeasureTransportConstructor::description() const
+{
+ return i18n("Transport the measure of a segment or arc over a line or circle.");
+}
+
+const QCString MeasureTransportConstructor::iconFileName( const bool ) const
+{
+ return "measuretransport";
+}
+
+const bool MeasureTransportConstructor::isAlreadySelectedOK(
+ const std::vector<ObjectCalcer*>&, const int& ) const
+{
+ return false;
+}
+
+/*
+ * we want the arguments in the exact order, this makes
+ * the code simpler, but I guess it is also less confusing
+ * to the user
+ */
+
+const int MeasureTransportConstructor::wantArgs(
+ const std::vector<ObjectCalcer*>& os,
+ const KigDocument&,
+ const KigWidget& ) const
+{
+ if ( os.size() == 0 ) return ArgsParser::Valid;
+
+ if ( ! os[0]->imp()->inherits( SegmentImp::stype() ) &&
+ ! os[0]->imp()->inherits( ArcImp::stype() ) )
+ return ArgsParser::Invalid;
+
+ if ( os.size() == 1 ) return ArgsParser::Valid;
+
+ if ( ! os[1]->imp()->inherits( LineImp::stype() ) &&
+ ! os[1]->imp()->inherits( CircleImp::stype() ) )
+ return ArgsParser::Invalid;
+
+ if ( os.size() == 2 ) return ArgsParser::Valid;
+
+ if ( ! os[2]->imp()->inherits( PointImp::stype() ) )
+ return ArgsParser::Invalid;
+
+ // we here use the "isPointOnCurve", which relies on
+ // "by construction" incidence, instead of a numerical
+ // check
+ if ( ! isPointOnCurve( os[2], os[1] ) )
+ return ArgsParser::Invalid;
+
+ if ( os.size() == 3 ) return ArgsParser::Complete;
+
+ return ArgsParser::Invalid;
+}
+
+void MeasureTransportConstructor::handleArgs(
+ const std::vector<ObjectCalcer*>& os, KigPart& d,
+ KigWidget& v ) const
+{
+ std::vector<ObjectHolder*> bos = build( os, d.document(), v );
+ for ( std::vector<ObjectHolder*>::iterator i = bos.begin();
+ i != bos.end(); ++i )
+ {
+ (*i)->calc( d.document() );
+ }
+
+ d.addObjects( bos );
+}
+
+void MeasureTransportConstructor::handlePrelim(
+ KigPainter& p, const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d, const KigWidget&
+ ) const
+{
+ p.setBrushStyle( Qt::NoBrush );
+ p.setBrushColor( Qt::red );
+ p.setPen( QPen ( Qt::red, 1) );
+ p.setWidth( -1 ); // -1 means the default width for the object being
+ // drawn..
+
+ ObjectDrawer drawer( Qt::red );
+ drawprelim( drawer, p, os, d );
+}
+
+void MeasureTransportConstructor::drawprelim( const ObjectDrawer& drawer,
+ KigPainter& p,
+ const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& doc ) const
+{
+ Args args;
+ using namespace std;
+ transform( parents.begin(), parents.end(),
+ back_inserter( args ), mem_fun( &ObjectCalcer::imp ) );
+ ObjectImp* data = mtype->calc( args, doc );
+ drawer.draw( *data, p, true );
+ delete data;
+}
+
+QString MeasureTransportConstructor::useText( const ObjectCalcer& o,
+ const std::vector<ObjectCalcer*>& os,
+ const KigDocument&, const KigWidget& ) const
+{
+ if ( o.imp()->inherits( SegmentImp::stype() ) )
+ return i18n("Segment to transport");
+ if ( o.imp()->inherits( ArcImp::stype() ) )
+ return i18n("Arc to transport");
+ if ( o.imp()->inherits( LineImp::stype() ) )
+ return i18n("Transport a measure on this line");
+ if ( o.imp()->inherits( CircleImp::stype() ) )
+ return i18n("Transport a measure on this circle");
+ if ( o.imp()->inherits( PointImp::stype() ) )
+ {
+ if ( os[1]->imp()->inherits( CircleImp::stype() ) )
+ return i18n("Start transport from this point of the circle");
+ if ( os[1]->imp()->inherits( LineImp::stype() ) )
+ return i18n("Start transport from this point of the line");
+ else
+ return i18n("Start transport from this point of the curve");
+ // well, this isn't impemented yet, should never get here
+ }
+ return "";
+}
+
+QString MeasureTransportConstructor::selectStatement(
+ const std::vector<ObjectCalcer*>&, const KigDocument&,
+ const KigWidget& ) const
+{
+//TODO
+ return i18n("Select a point to be a vertex of the new polygon...");
+}
+
+std::vector<ObjectHolder*> MeasureTransportConstructor::build(
+ const std::vector<ObjectCalcer*>& parents,
+ KigDocument&, KigWidget& ) const
+{
+ assert ( parents.size() == 3 );
+// std::vector<ObjectCalcer*> args;
+// for ( uint i = 0; i < count; ++i ) args.push_back( parents[i] );
+ ObjectTypeCalcer* calcer = new ObjectTypeCalcer( mtype, parents );
+ ObjectHolder* h = new ObjectHolder( calcer );
+ std::vector<ObjectHolder*> ret;
+ ret.push_back( h );
+ return ret;
+}
+
+void MeasureTransportConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool MeasureTransportConstructor::isTransform() const
+{
+ return false;
+}
+
+/*
+ * Generic intersection
+ */
+
+GenericIntersectionConstructor::GenericIntersectionConstructor()
+ : MergeObjectConstructor(
+ I18N_NOOP( "Intersect" ),
+ I18N_NOOP( "The intersection of two objects" ),
+ "curvelineintersection" )
+{
+ // intersection type..
+ // There is one "toplevel" object_constructor, that is composed
+ // of multiple subconstructors.. First we build the
+ // subconstructors:
+ SimpleObjectTypeConstructor* lineline =
+ new SimpleObjectTypeConstructor(
+ LineLineIntersectionType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "curvelineintersection" );
+
+ ObjectConstructor* lineconic =
+ new ConicLineIntersectionConstructor();
+
+ ObjectConstructor* arcline =
+ new ArcLineIntersectionConstructor();
+
+ MultiObjectTypeConstructor* linecubic =
+ new MultiObjectTypeConstructor(
+ LineCubicIntersectionType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "curvelineintersection", 1, 2, 3 );
+
+ ObjectConstructor* conicconic =
+ new ConicConicIntersectionConstructor();
+
+ MultiObjectTypeConstructor* circlecircle =
+ new MultiObjectTypeConstructor(
+ CircleCircleIntersectionType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "circlecircleintersection", -1, 1 );
+
+ SimpleObjectTypeConstructor* polygonline =
+ new SimpleObjectTypeConstructor(
+ PolygonLineIntersectionType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "curvelineintersection" );
+
+ merge( lineline );
+ merge( circlecircle );
+ merge( lineconic );
+ merge( linecubic );
+ merge( conicconic );
+ merge( arcline );
+ merge( polygonline );
+}
+
+GenericIntersectionConstructor::~GenericIntersectionConstructor()
+{
+}
+
+bool GenericIntersectionConstructor::isIntersection() const
+{
+ return true;
+}
+
+QString GenericIntersectionConstructor::useText(
+ const ObjectCalcer& o, const std::vector<ObjectCalcer*>& os,
+ const KigDocument&, const KigWidget& ) const
+{
+ QString preamble;
+ switch (os.size())
+ {
+ case 1:
+ if ( o.imp()->inherits( CircleImp::stype() ) )
+ return i18n( "Intersect this Circle" );
+ else if ( o.imp()->inherits( ConicImp::stype() ) )
+ return i18n( "Intersect this Conic" );
+ else if ( o.imp()->inherits( AbstractLineImp::stype() ) )
+ return i18n( "Intersect this Line" );
+ else if ( o.imp()->inherits( CubicImp::stype() ) )
+ return i18n( "Intersect this Cubic Curve" );
+ else if ( o.imp()->inherits( ArcImp::stype() ) )
+ return i18n( "Intersect this Arc" );
+ else if ( o.imp()->inherits( PolygonImp::stype() ) )
+ return i18n( "Intersect this Polygon" );
+ else assert( false );
+ break;
+ case 2:
+ if ( o.imp()->inherits( CircleImp::stype() ) )
+ return i18n( "with this Circle" );
+ else if ( o.imp()->inherits( ConicImp::stype() ) )
+ return i18n( "with this Conic" );
+ else if ( o.imp()->inherits( AbstractLineImp::stype() ) )
+ return i18n( "with this Line" );
+ else if ( o.imp()->inherits( CubicImp::stype() ) )
+ return i18n( "with this Cubic Curve" );
+ else if ( o.imp()->inherits( ArcImp::stype() ) )
+ return i18n( "with this Arc" );
+ else if ( o.imp()->inherits( PolygonImp::stype() ) )
+ return i18n( "with this Polygon" );
+ else assert( false );
+ break;
+ }
+
+ return QString::null;
+}
+
+static const ArgsParser::spec argsspecMidPointOfTwoPoints[] =
+{
+ { PointImp::stype(), I18N_NOOP( "Construct Midpoint of This Point and Another One" ),
+ I18N_NOOP( "Select the first of the points of which you want to construct the midpoint..." ), false },
+ { PointImp::stype(), I18N_NOOP( "Construct the midpoint of this point and another one" ),
+ I18N_NOOP( "Select the other of the points of which to construct the midpoint..." ), false }
+};
+
+MidPointOfTwoPointsConstructor::MidPointOfTwoPointsConstructor()
+ : StandardConstructorBase( "Mid Point",
+ "Construct the midpoint of two points",
+ "bisection", mparser ),
+ mparser( argsspecMidPointOfTwoPoints, 2 )
+{
+}
+
+MidPointOfTwoPointsConstructor::~MidPointOfTwoPointsConstructor()
+{
+}
+
+void MidPointOfTwoPointsConstructor::drawprelim(
+ const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const
+{
+ if ( parents.size() != 2 ) return;
+ assert( parents[0]->imp()->inherits( PointImp::stype() ) );
+ assert( parents[1]->imp()->inherits( PointImp::stype() ) );
+ const Coordinate m =
+ ( static_cast<const PointImp*>( parents[0]->imp() )->coordinate() +
+ static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2;
+ drawer.draw( PointImp( m ), p, true );
+}
+
+std::vector<ObjectHolder*> MidPointOfTwoPointsConstructor::build(
+ const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& ) const
+{
+ ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), os );
+ seg->calc( d );
+ int index = seg->imp()->propertiesInternalNames().findIndex( "mid-point" );
+ assert( index != -1 );
+ ObjectPropertyCalcer* prop = new ObjectPropertyCalcer( seg, index );
+ prop->calc( d );
+ std::vector<ObjectHolder*> ret;
+ ret.push_back( new ObjectHolder( prop ) );
+ return ret;
+}
+
+void MidPointOfTwoPointsConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool MidPointOfTwoPointsConstructor::isTransform() const
+{
+ return false;
+}
+
+TestConstructor::TestConstructor( const ArgsParserObjectType* type, const char* descname,
+ const char* desc, const char* iconfile )
+ : StandardConstructorBase( descname, desc, iconfile, type->argsParser() ),
+ mtype( type )
+{
+}
+
+TestConstructor::~TestConstructor()
+{
+}
+
+void TestConstructor::drawprelim( const ObjectDrawer&, KigPainter&, const std::vector<ObjectCalcer*>&,
+ const KigDocument& ) const
+{
+ // not used, only here because of the wrong
+ // ObjectConstructor-GUIAction design. See the TODO
+}
+
+std::vector<ObjectHolder*> TestConstructor::build( const std::vector<ObjectCalcer*>&, KigDocument&,
+ KigWidget& ) const
+{
+ // not used, only here because of the wrong
+ // ObjectConstructor-GUIAction design. See the TODO
+ std::vector<ObjectHolder*> ret;
+ return ret;
+}
+
+void TestConstructor::plug( KigPart*, KigGUIAction* )
+{
+}
+
+bool TestConstructor::isTransform() const
+{
+ return false;
+}
+
+bool TestConstructor::isTest() const
+{
+ return true;
+}
+
+BaseConstructMode* TestConstructor::constructMode( KigPart& doc )
+{
+ return new TestConstructMode( doc, mtype );
+}
+
+const int TestConstructor::wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d, const KigWidget& v ) const
+{
+ int ret = StandardConstructorBase::wantArgs( os, d, v );
+ if ( ret == ArgsParser::Complete ) ret = ArgsParser::Valid;
+ return ret;
+}
+
+QString GenericIntersectionConstructor::selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument&,
+ const KigWidget& ) const
+{
+ if ( sel.size() == 0 )
+ return i18n( "Select the first object to intersect..." );
+ else
+ return i18n( "Select the second object to intersect..." );
+}
+
+TangentConstructor::TangentConstructor()
+ : MergeObjectConstructor(
+ I18N_NOOP( "Tangent" ),
+ I18N_NOOP( "The line tangent to a curve" ),
+ "tangent" )
+{
+ SimpleObjectTypeConstructor* conic =
+ new SimpleObjectTypeConstructor(
+ TangentConicType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "tangentconic" );
+
+ SimpleObjectTypeConstructor* arc =
+ new SimpleObjectTypeConstructor(
+ TangentArcType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "tangentarc" );
+
+ SimpleObjectTypeConstructor* cubic =
+ new SimpleObjectTypeConstructor(
+ TangentCubicType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "tangentcubic" );
+
+ SimpleObjectTypeConstructor* curve =
+ new SimpleObjectTypeConstructor(
+ TangentCurveType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "tangentcurve" );
+
+ merge( conic );
+ merge( arc );
+ merge( cubic );
+ merge( curve );
+}
+
+TangentConstructor::~TangentConstructor()
+{
+}
+
+QString TangentConstructor::useText(
+ const ObjectCalcer& o, const std::vector<ObjectCalcer*>&,
+ const KigDocument&, const KigWidget& ) const
+{
+ if ( o.imp()->inherits( CircleImp::stype() ) )
+ return i18n( "Tangent to This Circle" );
+ else if ( o.imp()->inherits( ConicImp::stype() ) )
+ return i18n( "Tangent to This Conic" );
+ else if ( o.imp()->inherits( ArcImp::stype() ) )
+ return i18n( "Tangent to This Arc" );
+ else if ( o.imp()->inherits( CubicImp::stype() ) )
+ return i18n( "Tangent to This Cubic Curve" );
+ else if ( o.imp()->inherits( CurveImp::stype() ) )
+ return i18n( "Tangent to This Curve" );
+ else if ( o.imp()->inherits( PointImp::stype() ) )
+ return i18n( "Tangent at This Point" );
+// else assert( false );
+ return QString::null;
+}
+
+//QString TangentConstructor::selectStatement(
+// const std::vector<ObjectCalcer*>& sel, const KigDocument&,
+// const KigWidget& ) const
+//{
+// if ( sel.size() == 0 )
+// return i18n( "Select the object..." );
+// else
+// return i18n( "Select the point for the tangent to go through..." );
+//}
+
+/*
+ * center of curvature of a curve
+ */
+
+CocConstructor::CocConstructor()
+ : MergeObjectConstructor(
+ I18N_NOOP( "Center Of Curvature" ),
+ I18N_NOOP( "The center of the osculating circle to a curve" ),
+ "centerofcurvature" )
+{
+ SimpleObjectTypeConstructor* conic =
+ new SimpleObjectTypeConstructor(
+ CocConicType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "cocconic" );
+
+ SimpleObjectTypeConstructor* cubic =
+ new SimpleObjectTypeConstructor(
+ CocCubicType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "coccubic" );
+
+ SimpleObjectTypeConstructor* curve =
+ new SimpleObjectTypeConstructor(
+ CocCurveType::instance(),
+ "SHOULDNOTBESEEN", "SHOULDNOTBESEEN",
+ "coccurve" );
+
+ merge( conic );
+ merge( cubic );
+ merge( curve );
+}
+
+CocConstructor::~CocConstructor()
+{
+}
+
+QString CocConstructor::useText(
+ const ObjectCalcer& o, const std::vector<ObjectCalcer*>&,
+ const KigDocument&, const KigWidget& ) const
+{
+ if ( o.imp()->inherits( ConicImp::stype() ) )
+ return i18n( "Center of Curvature of This Conic" );
+ else if ( o.imp()->inherits( CubicImp::stype() ) )
+ return i18n( "Center of Curvature of This Cubic Curve" );
+ else if ( o.imp()->inherits( CurveImp::stype() ) )
+ return i18n( "Center of Curvature of This Curve" );
+ else if ( o.imp()->inherits( PointImp::stype() ) )
+ return i18n( "Center of Curvature at This Point" );
+ return QString::null;
+}
+
+bool relativePrimes( int n, int p )
+{
+ if ( p > n ) return relativePrimes( p, n );
+ assert ( p >= 0 );
+ if ( p == 0 ) return false;
+ if ( p == 1 ) return true;
+ int d = int( n/p );
+ return relativePrimes( p, n-d*p );
+}
+
+//QString CocConstructor::selectStatement(
+// const std::vector<ObjectCalcer*>& sel, const KigDocument&,
+// const KigWidget& ) const
+//{
+// if ( sel.size() == 0 )
+// return i18n( "Select the object..." );
+// else
+// return i18n( "Select the point where to compute the center of curvature..." );
+//}
diff --git a/kig/misc/special_constructors.h b/kig/misc/special_constructors.h
new file mode 100644
index 00000000..99760be3
--- /dev/null
+++ b/kig/misc/special_constructors.h
@@ -0,0 +1,320 @@
+// 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_MISC_SPECIAL_CONSTRUCTORS_H
+#define KIG_MISC_SPECIAL_CONSTRUCTORS_H
+
+#include "object_constructor.h"
+
+class PolygonVertexTypeConstructor
+ : public StandardConstructorBase
+{
+ const ArgsParserObjectType* mtype;
+ ArgsParser margsparser;
+public:
+ PolygonVertexTypeConstructor();
+ ~PolygonVertexTypeConstructor();
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+};
+
+class PolygonSideTypeConstructor
+ : public StandardConstructorBase
+{
+ const ArgsParserObjectType* mtype;
+ ArgsParser margsparser;
+public:
+ PolygonSideTypeConstructor();
+ ~PolygonSideTypeConstructor();
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+};
+
+class PolygonBNPTypeConstructor
+ : public ObjectConstructor
+{
+ const ObjectType* mtype;
+public:
+ PolygonBNPTypeConstructor();
+ ~PolygonBNPTypeConstructor();
+
+ const QString descriptiveName() const;
+ const QString description() const;
+ const QCString iconFileName( const bool canBeNull = false ) const;
+ const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const;
+ const int wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+ void handleArgs( const std::vector<ObjectCalcer*>& os,
+ KigPart& d,
+ KigWidget& v
+ ) const;
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const;
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+ void handlePrelim( KigPainter& p,
+ const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+};
+
+class PolygonBCVConstructor
+ : public ObjectConstructor
+{
+ const ObjectType* mtype;
+public:
+ PolygonBCVConstructor();
+ ~PolygonBCVConstructor();
+
+ const QString descriptiveName() const;
+ const QString description() const;
+ const QCString iconFileName( const bool canBeNull = false ) const;
+ const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const;
+ const int wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+ void handleArgs( const std::vector<ObjectCalcer*>& os,
+ KigPart& d,
+ KigWidget& v
+ ) const;
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const;
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+ void handlePrelim( KigPainter& p,
+ const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+ int computeNsides( const Coordinate& c, const Coordinate& v, const Coordinate& cntrl, int& winding ) const;
+ Coordinate getRotatedCoord( const Coordinate& c1,
+ const Coordinate& c2, double alpha ) const;
+};
+
+class MeasureTransportConstructor
+ : public ObjectConstructor
+{
+ const ObjectType* mtype;
+public:
+ MeasureTransportConstructor();
+ ~MeasureTransportConstructor();
+
+ const QString descriptiveName() const;
+ const QString description() const;
+ const QCString iconFileName( const bool canBeNull = false ) const;
+ const bool isAlreadySelectedOK( const std::vector<ObjectCalcer*>& os,
+ const int& ) const;
+ const int wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+ void handleArgs( const std::vector<ObjectCalcer*>& os,
+ KigPart& d,
+ KigWidget& v
+ ) const;
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d, const KigWidget& v
+ ) const;
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+ void handlePrelim( KigPainter& p,
+ const std::vector<ObjectCalcer*>& sel,
+ const KigDocument& d,
+ const KigWidget& v
+ ) const;
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+};
+
+class ConicRadicalConstructor
+ : public StandardConstructorBase
+{
+ const ArgsParserObjectType* mtype;
+ const ArgsParser mparser;
+public:
+ ConicRadicalConstructor();
+ ~ConicRadicalConstructor();
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& v ) const;
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+class LocusConstructor
+ : public StandardConstructorBase
+{
+ ArgsParser margsparser;
+public:
+ LocusConstructor();
+ ~LocusConstructor();
+ /**
+ * we override the wantArgs() function, since we need to see
+ * something about the objects that an ArgsParser can't know about,
+ * namely, whether the first point is a constrained point...
+ */
+ const int wantArgs(
+ const std::vector<ObjectCalcer*>& os, const KigDocument& d,
+ const KigWidget& v
+ ) const;
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& v ) const;
+
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents, const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d, KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+
+ bool isTransform() const;
+};
+
+class GenericAffinityConstructor
+ : public MergeObjectConstructor
+{
+public:
+ GenericAffinityConstructor();
+ ~GenericAffinityConstructor();
+};
+
+class GenericProjectivityConstructor
+ : public MergeObjectConstructor
+{
+public:
+ GenericProjectivityConstructor();
+ ~GenericProjectivityConstructor();
+};
+
+class InversionConstructor
+ : public MergeObjectConstructor
+{
+public:
+ InversionConstructor();
+ ~InversionConstructor();
+};
+
+class GenericIntersectionConstructor
+ : public MergeObjectConstructor
+{
+public:
+ GenericIntersectionConstructor();
+ ~GenericIntersectionConstructor();
+
+ bool isIntersection() const;
+
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& v ) const;
+ QString selectStatement(
+ const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& w ) const;
+};
+
+class MidPointOfTwoPointsConstructor
+ : public StandardConstructorBase
+{
+ ArgsParser mparser;
+public:
+ MidPointOfTwoPointsConstructor();
+ ~MidPointOfTwoPointsConstructor();
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d,
+ KigWidget& w ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+};
+
+class TestConstructor
+ : public StandardConstructorBase
+{
+ const ArgsParserObjectType* mtype;
+public:
+ TestConstructor( const ArgsParserObjectType* type, const char* descname,
+ const char* desc, const char* iconfile );
+ ~TestConstructor();
+ void drawprelim( const ObjectDrawer& drawer, KigPainter& p, const std::vector<ObjectCalcer*>& parents,
+ const KigDocument& ) const;
+ std::vector<ObjectHolder*> build( const std::vector<ObjectCalcer*>& os, KigDocument& d,
+ KigWidget& w ) const;
+ const int wantArgs( const std::vector<ObjectCalcer*>& os,
+ const KigDocument& d, const KigWidget& v ) const;
+ void plug( KigPart* doc, KigGUIAction* kact );
+ bool isTransform() const;
+ bool isTest() const;
+
+ BaseConstructMode* constructMode( KigPart& doc );
+};
+
+class TangentConstructor
+ : public MergeObjectConstructor
+{
+public:
+ TangentConstructor();
+ ~TangentConstructor();
+
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& v ) const;
+// QString selectStatement(
+// const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+// const KigWidget& w ) const;
+};
+
+class CocConstructor
+ : public MergeObjectConstructor
+{
+public:
+ CocConstructor();
+ ~CocConstructor();
+
+ QString useText( const ObjectCalcer& o, const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+ const KigWidget& v ) const;
+// QString selectStatement(
+// const std::vector<ObjectCalcer*>& sel, const KigDocument& d,
+// const KigWidget& w ) const;
+};
+
+bool relativePrimes( int n, int p );
+#endif