|
|
|
// 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 );
|
|
|
|
}
|