You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
11 KiB
375 lines
11 KiB
/**
|
|
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 "kgeo-filter.h"
|
|
|
|
#include "kgeo-resource.h"
|
|
#include "filters-common.h"
|
|
|
|
#include "../kig/kig_part.h"
|
|
#include "../kig/kig_document.h"
|
|
#include "../objects/angle_type.h"
|
|
#include "../objects/bogus_imp.h"
|
|
#include "../objects/circle_imp.h"
|
|
#include "../objects/circle_type.h"
|
|
#include "../objects/intersection_types.h"
|
|
#include "../objects/line_type.h"
|
|
#include "../objects/object_calcer.h"
|
|
#include "../objects/object_drawer.h"
|
|
#include "../objects/object_factory.h"
|
|
#include "../objects/object_holder.h"
|
|
#include "../objects/other_type.h"
|
|
#include "../objects/point_imp.h"
|
|
#include "../objects/point_type.h"
|
|
#include "../objects/text_type.h"
|
|
#include "../objects/transform_types.h"
|
|
#include "../objects/vector_type.h"
|
|
|
|
#include <ksimpleconfig.h>
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
|
|
bool KigFilterKGeo::supportMime( const TQString& mime )
|
|
{
|
|
return mime == "application/x-kgeo";
|
|
}
|
|
|
|
KigDocument* KigFilterKGeo::load( const TQString& sFrom )
|
|
{
|
|
// kgeo uses a KSimpleConfig to save its contents...
|
|
KSimpleConfig config ( sFrom );
|
|
|
|
loadMetrics ( &config );
|
|
return loadObjects ( sFrom, &config );
|
|
}
|
|
|
|
void KigFilterKGeo::loadMetrics(KSimpleConfig* c )
|
|
{
|
|
c->setGroup("Main");
|
|
xMax = c->readNumEntry("XMax", 16);
|
|
yMax = c->readNumEntry("YMax", 11);
|
|
grid = c->readBoolEntry( "Grid", true );
|
|
axes = c->readBoolEntry( "Axes", true );
|
|
// the rest is not relevant to us (yet ?)...
|
|
}
|
|
|
|
struct KGeoHierarchyElement
|
|
{
|
|
int id;
|
|
std::vector<int> parents;
|
|
};
|
|
|
|
static void visitElem( std::vector<KGeoHierarchyElement>& ret,
|
|
const std::vector<KGeoHierarchyElement>& elems,
|
|
std::vector<bool>& seen,
|
|
int i )
|
|
{
|
|
if ( !seen[i] )
|
|
{
|
|
for ( uint j = 0; j < elems[i].parents.size(); ++j )
|
|
visitElem( ret, elems, seen, elems[i].parents[j] );
|
|
ret.push_back( elems[i] );
|
|
seen[i] = true;
|
|
};
|
|
}
|
|
|
|
static std::vector<KGeoHierarchyElement> sortElems( const std::vector<KGeoHierarchyElement> elems )
|
|
{
|
|
std::vector<KGeoHierarchyElement> ret;
|
|
std::vector<bool> seenElems( elems.size(), false );
|
|
for ( uint i = 0; i < elems.size(); ++i )
|
|
visitElem( ret, elems, seenElems, i );
|
|
return ret;
|
|
}
|
|
|
|
KigDocument* KigFilterKGeo::loadObjects( const TQString& file, KSimpleConfig* c )
|
|
{
|
|
KigDocument* ret = new KigDocument();
|
|
|
|
using namespace std;
|
|
|
|
TQString group;
|
|
bool ok = true;
|
|
c->setGroup("Main");
|
|
int number = c->readNumEntry ("Number");
|
|
|
|
// first we determine the parent relationships, and sort the
|
|
// elements in an order that we can be sure all of an object's
|
|
// parents will have been handled before it is handled itself..
|
|
// ( aka topological sort of the parent relations graph..
|
|
std::vector<KGeoHierarchyElement> elems;
|
|
elems.reserve( number );
|
|
|
|
for ( int i = 0; i < number; ++i )
|
|
{
|
|
KGeoHierarchyElement elem;
|
|
elem.id = i;
|
|
group.setNum( i + 1 );
|
|
group.prepend( "Object " );
|
|
c->setGroup( group );
|
|
TQStrList parents;
|
|
c->readListEntry( "Parents", parents );
|
|
elems.push_back( elem );
|
|
for ( const char* parent = parents.first(); parent; parent = parents.next() )
|
|
{
|
|
int parentIndex = TQString::fromLatin1( parent ).toInt( &ok );
|
|
if ( ! ok ) KIG_FILTER_PARSE_ERROR;
|
|
if ( parentIndex != 0 )
|
|
elems[i].parents.push_back( parentIndex - 1 );
|
|
};
|
|
};
|
|
|
|
std::vector<KGeoHierarchyElement> sortedElems = sortElems( elems );
|
|
std::vector<ObjectHolder*> os;
|
|
os.resize( number, 0 );
|
|
const ObjectFactory* factory = ObjectFactory::instance();
|
|
|
|
// now we iterate over the elems again in the newly determined
|
|
// order..
|
|
for ( uint i = 0; i < sortedElems.size(); ++i )
|
|
{
|
|
const KGeoHierarchyElement& e = sortedElems[i];
|
|
int id = e.id;
|
|
group.setNum( id + 1 );
|
|
group.prepend( "Object " );
|
|
c->setGroup( group );
|
|
int objID = c->readNumEntry( "Geo" );
|
|
|
|
std::vector<ObjectCalcer*> parents;
|
|
for ( uint j = 0; j < e.parents.size(); ++j )
|
|
{
|
|
int parentid = e.parents[j];
|
|
parents.push_back( os[parentid]->calcer() );
|
|
};
|
|
|
|
ObjectCalcer* o = 0;
|
|
switch (objID)
|
|
{
|
|
case ID_point:
|
|
{
|
|
// fetch the coordinates...
|
|
TQString strX = c->readEntry("TQPointX");
|
|
TQString strY = c->readEntry("TQPointY");
|
|
double x = strX.toDouble(&ok);
|
|
if (!ok) KIG_FILTER_PARSE_ERROR;
|
|
double y = strY.toDouble(&ok);
|
|
if (!ok) KIG_FILTER_PARSE_ERROR;
|
|
Coordinate m( x, y );
|
|
uint nparents = parents.size();
|
|
if ( nparents == 0 )
|
|
o = factory->fixedPointCalcer( m );
|
|
else if ( nparents == 1 )
|
|
o = factory->constrainedPointCalcer( parents[0], m, *ret );
|
|
else
|
|
KIG_FILTER_PARSE_ERROR;
|
|
break;
|
|
}
|
|
case ID_segment:
|
|
{
|
|
o = new ObjectTypeCalcer( SegmentABType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_circle:
|
|
{
|
|
o = new ObjectTypeCalcer( CircleBCPType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_line:
|
|
{
|
|
o = new ObjectTypeCalcer( LineABType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_bisection:
|
|
{
|
|
// if this is the bisection of two points, first build a segment
|
|
// between them..
|
|
if ( parents.size() == 2 )
|
|
{
|
|
ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), parents );
|
|
parents.clear();
|
|
parents.push_back( seg );
|
|
}
|
|
if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
|
|
o = factory->propertyObjectCalcer( parents[0], "mid-point" );
|
|
break;
|
|
};
|
|
case ID_perpendicular:
|
|
{
|
|
o = new ObjectTypeCalcer( LinePerpendLPType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_parallel:
|
|
{
|
|
o = new ObjectTypeCalcer( LineParallelLPType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_vector:
|
|
{
|
|
o = new ObjectTypeCalcer( VectorType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_ray:
|
|
{
|
|
o = new ObjectTypeCalcer( RayABType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_move:
|
|
{
|
|
o = new ObjectTypeCalcer( TranslatedType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_mirrorPoint:
|
|
{
|
|
o = new ObjectTypeCalcer( PointReflectionType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_pointOfConc:
|
|
{
|
|
o = new ObjectTypeCalcer( LineLineIntersectionType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_text:
|
|
{
|
|
bool frame = c->readBoolEntry( "Frame" );
|
|
double x = c->readDoubleNumEntry( "TextRectCenterX" );
|
|
double y = c->readDoubleNumEntry( "TextRectCenterY" );
|
|
TQString text = c->readEntry( "TextRectEntry" );
|
|
double height = c->readNumEntry( "TextRectHeight" );
|
|
double width = c->readNumEntry( "TextRectWidth" );
|
|
// we don't want the center, but the top left..
|
|
x -= width / 80;
|
|
y -= height / 80;
|
|
o = factory->labelCalcer(
|
|
text, Coordinate( x, y ), frame, std::vector<ObjectCalcer*>(), *ret );
|
|
break;
|
|
}
|
|
case ID_fixedCircle:
|
|
{
|
|
double r = c->readDoubleNumEntry( "Radius" );
|
|
parents.push_back( new ObjectConstCalcer( new DoubleImp( r ) ) );
|
|
o = new ObjectTypeCalcer( CircleBPRType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_angle:
|
|
{
|
|
if ( parents.size() == 3 )
|
|
{
|
|
ObjectTypeCalcer* ao = new ObjectTypeCalcer( AngleType::instance(), parents );
|
|
ao->calc( *ret );
|
|
parents.clear();
|
|
parents.push_back( ao );
|
|
};
|
|
if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
|
|
ObjectCalcer* angle = parents[0];
|
|
parents.clear();
|
|
const Coordinate c =
|
|
static_cast<const PointImp*>( angle->parents()[1]->imp() )->coordinate();
|
|
o = filtersConstructTextObject( c, angle, "angle-degrees", *ret, true );
|
|
break;
|
|
}
|
|
case ID_distance:
|
|
{
|
|
if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR;
|
|
ObjectTypeCalcer* segment = new ObjectTypeCalcer( SegmentABType::instance(), parents );
|
|
segment->calc( *ret );
|
|
Coordinate m = ( static_cast<const PointImp*>( parents[0]->imp() )->coordinate() +
|
|
static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2;
|
|
o = filtersConstructTextObject( m, segment, "length", *ret, true );
|
|
break;
|
|
}
|
|
case ID_arc:
|
|
{
|
|
o = new ObjectTypeCalcer( AngleType::instance(), parents );
|
|
break;
|
|
}
|
|
case ID_area:
|
|
{
|
|
if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
|
|
const CircleImp* circle = static_cast<const CircleImp*>( parents[0]->imp() );
|
|
const Coordinate c = circle->center() + Coordinate( circle->radius(), 0 );
|
|
o = filtersConstructTextObject( c, parents[0], "surface", *ret, true );
|
|
break;
|
|
}
|
|
case ID_slope:
|
|
{
|
|
// if parents contains a segment, line, vector or whatever, we
|
|
// take its parents cause we want points..
|
|
if ( parents.size() == 1 ) parents = parents[0]->parents();
|
|
if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR;
|
|
const Coordinate c = (
|
|
static_cast<const PointImp*>( parents[0]->imp() )->coordinate() +
|
|
static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2;
|
|
ObjectTypeCalcer* line = new ObjectTypeCalcer( LineABType::instance(), parents );
|
|
line->calc( *ret );
|
|
o = filtersConstructTextObject( c, line, "slope", *ret, true );
|
|
break;
|
|
}
|
|
case ID_circumference:
|
|
{
|
|
if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
|
|
const CircleImp* c = static_cast<const CircleImp*>( parents[0]->imp() );
|
|
const Coordinate m = c->center() + Coordinate( c->radius(), 0 );
|
|
o = filtersConstructTextObject( m, parents[0], "circumference", *ret, true );
|
|
break;
|
|
}
|
|
case ID_rotation:
|
|
{
|
|
// in kig, the rotated object should be last..
|
|
ObjectCalcer* t = parents[2];
|
|
parents[2] = parents[0];
|
|
parents[0] = t;
|
|
o = new ObjectTypeCalcer( RotationType::instance(), parents );
|
|
break;
|
|
}
|
|
default:
|
|
KIG_FILTER_PARSE_ERROR;
|
|
};
|
|
|
|
// set the color...
|
|
TQColor co = c->readColorEntry( "Color" );
|
|
if( !co.isValid() )
|
|
co = TQt::blue;
|
|
ObjectDrawer* d = new ObjectDrawer( co );
|
|
|
|
os[i] = new ObjectHolder( o, d );
|
|
os[i]->calc( *ret );
|
|
}; // for loop (creating KGeoHierarchyElements..
|
|
|
|
ret->addObjects( os );
|
|
ret->setGrid( grid );
|
|
ret->setAxes( axes );
|
|
return ret;
|
|
}
|
|
|
|
KigFilterKGeo::KigFilterKGeo()
|
|
{
|
|
}
|
|
|
|
KigFilterKGeo::~KigFilterKGeo()
|
|
{
|
|
}
|
|
|
|
KigFilterKGeo* KigFilterKGeo::instance()
|
|
{
|
|
static KigFilterKGeo f;
|
|
return &f;
|
|
}
|