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.
tdeedu/kig/filters/kseg-filter.cc

680 lines
19 KiB

// 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 "kseg-filter.h"
#include "kseg-defs.h"
#include "../kig/kig_document.h"
#include "../kig/kig_part.h"
#include "../misc/coordinate.h"
#include "../objects/angle_type.h"
#include "../objects/arc_type.h"
#include "../objects/bogus_imp.h"
#include "../objects/circle_type.h"
#include "../objects/conic_imp.h"
#include "../objects/conic_types.h"
#include "../objects/intersection_types.h"
#include "../objects/line_imp.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_imp.h"
#include "../objects/other_type.h"
#include "../objects/point_imp.h"
#include "../objects/point_type.h"
#include "../objects/polygon_type.h"
#include "../objects/transform_types.h"
#include "../objects/vector_type.h"
#include <tqfont.h>
#include <tqpen.h>
#include <tqbrush.h>
#include <tqfile.h>
#include <tqdatastream.h>
#include <tqbuffer.h>
#include <tdelocale.h>
KigFilterKSeg::KigFilterKSeg()
{
}
KigFilterKSeg::~KigFilterKSeg()
{
}
bool KigFilterKSeg::supportMime( const TQString& mime )
{
return mime == "application/x-kseg";
}
struct drawstyle
{
TQ_INT8 pointstyle;
TQFont font;
TQPen pen;
TQBrush brush;
};
static Coordinate readKSegCoordinate( TQDataStream& stream )
{
// read the coord..
float inx, iny;
stream >> inx >> iny;
// KSeg uses a coordinate system, where the topleft is (0,0), and
// the bottom right is the widget coordinate in the window: if the
// KSeg window had a width of 600 pixels and a height of 600, then
// the bottom right will be at (600,600). We assume a window of
// such a height here, and transform it into Kig Coordinates. This
// function is quite similar to ScreenInfo::fromScreen, and it's
// basically a simple modification of that code to floats..
// invert the y-axis: 0 is at the bottom !
Coordinate t( inx, 600 - iny );
t *= 14;
t /= 600;
return t + Coordinate( -7, -7 );
}
static ObjectTypeCalcer* intersectionPoint( const std::vector<ObjectCalcer*>& parents, int which )
{
if ( parents.size() != 2 ) return 0;
int nlines = 0;
int nconics = 0;
int narcs = 0;
for ( int i = 0; i < 2; ++i )
{
if ( parents[i]->imp()->inherits( AbstractLineImp::stype() ) ) ++nlines;
else if ( parents[i]->imp()->inherits( ConicImp::stype() ) ) ++nconics;
else if ( parents[i]->imp()->inherits( ArcImp::stype() ) ) ++narcs;
else return 0;
};
if ( nlines == 2 )
return which == -1 ? new ObjectTypeCalcer( LineLineIntersectionType::instance(), parents ) : 0;
else if ( nlines == 1 && nconics == 1 )
{
std::vector<ObjectCalcer*> intparents( parents );
intparents.push_back( new ObjectConstCalcer( new IntImp( which ) ) );
return new ObjectTypeCalcer( ConicLineIntersectionType::instance(), intparents );
}
else if ( nlines == 0 && nconics == 2 )
{
std::vector<ObjectCalcer*> rparents( parents );
rparents.push_back( new ObjectConstCalcer( new IntImp( 1 ) ) );
rparents.push_back( new ObjectConstCalcer( new IntImp( 1 ) ) );
rparents.push_back( new ObjectTypeCalcer( ConicRadicalType::instance(), rparents ) );
std::vector<ObjectCalcer*> iparents;
iparents.push_back( parents[0] );
iparents.push_back( rparents.back() );
iparents.push_back( new ObjectConstCalcer( new IntImp( which ) ) );
return new ObjectTypeCalcer( ConicLineIntersectionType::instance(), iparents );
}
else if ( nlines == 1 && narcs == 1 )
{
std::vector<ObjectCalcer*> intparents( parents );
intparents.push_back( new ObjectConstCalcer( new IntImp( which ) ) );
return new ObjectTypeCalcer( ArcLineIntersectionType::instance(), intparents );
}
else return 0;
}
ObjectCalcer* KigFilterKSeg::transformObject( const TQString& file, KigDocument& kigdoc,
std::vector<ObjectCalcer*>& parents,
int subtype, bool& ok )
{
ok = true;
ObjectCalcer* retobj = 0;
switch( subtype )
{
case G_TRANSLATED:
{
std::vector<ObjectCalcer*> vectorparents( parents.begin() + 1, parents.end() );
ObjectTypeCalcer* vector = new ObjectTypeCalcer( VectorType::instance(), vectorparents );
vector->calc( kigdoc );
std::vector<ObjectCalcer*> transparents;
transparents.push_back( parents[0] );
transparents.push_back( vector );
retobj = new ObjectTypeCalcer( TranslatedType::instance(), transparents );
break;
}
case G_ROTATED:
{
std::vector<ObjectCalcer*> angleparents( parents.begin() + 2, parents.end() );
ObjectTypeCalcer* angle = new ObjectTypeCalcer( AngleType::instance(), angleparents );
angle->calc( kigdoc );
std::vector<ObjectCalcer*> rotparents;
rotparents.push_back( parents[0] );
rotparents.push_back( parents[1] );
rotparents.push_back( angle );
retobj = new ObjectTypeCalcer( RotationType::instance(), rotparents );
break;
}
case G_SCALED:
{
if ( parents.size() == 4 )
{
retobj = new ObjectTypeCalcer( ScalingOverCenter2Type::instance(), parents );
}
else
{
// TODO
notSupported( file, i18n( "This KSeg document uses a scaling "
"transformation, which Kig currently "
"cannot import." ) );
ok = false;
return 0;
}
break;
}
case G_REFLECTED:
{
std::vector<ObjectCalcer*> mirparents( parents.begin(), parents.end() );
retobj = new ObjectTypeCalcer( LineReflectionType::instance(), mirparents );
break;
}
}
return retobj;
}
KigDocument* KigFilterKSeg::load( const TQString& file )
{
TQFile ffile( file );
if ( ! ffile.open( IO_ReadOnly ) )
{
fileNotFound( file );
return 0;
};
KigDocument* retdoc = new KigDocument();
TQDataStream fstream( &ffile );
TQString versionstring;
fstream >> versionstring;
if ( !versionstring.startsWith( "KSeg Document Version " ) )
KIG_FILTER_PARSE_ERROR;
TQByteArray array;
fstream >> array;
TQBuffer buf( array );
buf.open( IO_ReadOnly );
TQDataStream stream( &buf );
stream.setVersion( 3 );
// G_drawstyles:
short numstyles;
stream >> numstyles;
std::vector<drawstyle> drawstyles( numstyles );
for ( short i = 0; i < numstyles; ++i )
{
stream >> drawstyles[i].pointstyle;
stream >> drawstyles[i].font;
stream >> drawstyles[i].pen;
stream >> drawstyles[i].brush;
};
std::vector<ObjectHolder*> ret;
std::vector<ObjectHolder*> ret2;
// G_refs
unsigned int count;
stream >> count;
ret.resize( count, 0 );
const ObjectFactory* fact = ObjectFactory::instance();
// KSeg topologically sorts the objects before saving, that means we
// can read the entire file in one iteration..
for ( uint i = 0; i < count; ++i )
{
short styleid;
stream >> styleid;
short nparents;
stream >> nparents;
std::vector<ObjectCalcer*> parents( nparents, 0 );
for ( short j = 0; j < nparents; ++j )
{
int parent;
stream >> parent;
parents[j] = ret[parent]->calcer();
};
// read the object..
short info;
stream >> info;
int type = 1 << (info & 31);
info >>= 5;
int descendtype = (info & 15);
info >>= 4;
bool visible = info & 1;
bool labelVisible = info & 2;
bool given = info & 4;
bool final = info & 8;
// avoid g++ warnings about unused vars..
// this doesn't really do anything..
(void) given;
(void) final;
drawstyle style = drawstyles[styleid];
if ( type == G_LOOP ) continue;
// read the label..
TQString labeltext;
stream >> labeltext;
Coordinate relcoord = readKSegCoordinate( stream );
// shut up gcc
(void) relcoord;
if ( type & G_CURVE )
{
Coordinate relcurvecoord = readKSegCoordinate( stream );
// shut up gcc
(void) relcurvecoord;
};
// now load the object data..
ObjectHolder* object = 0;
ObjectCalcer* o = 0;
bool ok = true;
TQColor color = style.pen.color();
int width = style.pen.width();
/*
kdDebug() << "type: " << type << endl
<< "descendtype: " << descendtype << endl
<< "label: " << labeltext << endl;
//*/
switch ( type )
{
case G_POINT:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_FREE_POINT:
{
// fixed point
if ( nparents != 0 ) KIG_FILTER_PARSE_ERROR;
Coordinate c = readKSegCoordinate( stream );
o = fact->fixedPointCalcer( c );
break;
}
case G_CONSTRAINED_POINT:
{
// constrained point
double p;
stream >> p;
if ( nparents != 1 ) KIG_FILTER_PARSE_ERROR;
ObjectCalcer* parent = parents[0];
assert( parent );
o = fact->constrainedPointCalcer( parent, p );
break;
}
case G_INTERSECTION_POINT:
{
// KSeg has somewhat weird intersection objects..
// for all objects G_INTERSECTION_POINT gets the
// first intersection of its parents, G_INTERSECTION2_POINT
// represents the second, if present.
o = intersectionPoint( parents, -1 );
if ( ! o ) KIG_FILTER_PARSE_ERROR;
break;
}
case G_INTERSECTION2_POINT:
{
o = intersectionPoint( parents, 1 );
if ( ! o ) KIG_FILTER_PARSE_ERROR;
break;
}
case G_MID_POINT:
{
// midpoint of a segment..
if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
if ( !parents[0]->imp()->inherits( SegmentImp::stype() ) )
KIG_FILTER_PARSE_ERROR;
int index = parents[0]->imp()->propertiesInternalNames().findIndex( "mid-point" );
assert( index != -1 );
o = new ObjectPropertyCalcer( parents[0], index );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
};
width = style.pointstyle == SMALL_CIRCLE ? 2 : style.pointstyle == MEDIUM_CIRCLE ? 3 : 5;
color = style.brush.color();
break;
};
case G_SEGMENT:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_ENDPOINTS_SEGMENT:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( SegmentABType::instance(), parents );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
}
break;
};
case G_RAY:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_TWOPOINTS_RAY:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( RayABType::instance(), parents );
break;
}
case G_BISECTOR_RAY:
{
ObjectTypeCalcer* angle = new ObjectTypeCalcer( HalfAngleType::instance(), parents );
angle->calc( *retdoc );
o = fact->propertyObjectCalcer( angle, "angle-bisector" );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
};
break;
};
case G_LINE:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_TWOPOINTS_LINE:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( LineABType::instance(), parents );
break;
}
case G_PARALLEL_LINE:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( LineParallelLPType::instance(), parents );
break;
}
case G_PERPENDICULAR_LINE:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( LinePerpendLPType::instance(), parents );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
};
break;
};
case G_CIRCLE:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_CENTERPOINT_CIRCLE:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( CircleBCPType::instance(), parents );
break;
}
case G_CENTERRADIUS_CIRCLE:
{
ObjectCalcer* point;
ObjectCalcer* segment;
if ( parents[0]->imp()->inherits( PointImp::stype() ) )
{
point = parents[0];
segment = parents[1];
}
else
{
point = parents[1];
segment = parents[0];
};
int index = segment->imp()->propertiesInternalNames().findIndex( "length" );
if ( index == -1 ) KIG_FILTER_PARSE_ERROR;
ObjectPropertyCalcer* length = new ObjectPropertyCalcer( segment, index );
length->calc( *retdoc );
std::vector<ObjectCalcer*> cparents;
cparents.push_back( point );
cparents.push_back( length );
o = new ObjectTypeCalcer( CircleBPRType::instance(), cparents );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
};
break;
};
case G_ARC:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_THREEPOINTS_ARC:
{
if ( nparents != 3 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( ArcBTPType::instance(), parents );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
}
break;
};
case G_POLYGON:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
default:
{
if ( nparents < 3 ) KIG_FILTER_PARSE_ERROR;
o = new ObjectTypeCalcer( PolygonBNPType::instance(), parents );
}
}
// default:
// KIG_FILTER_PARSE_ERROR;
break;
};
case G_CIRCLEINTERIOR:
{
notSupported( file, i18n( "This KSeg file contains a filled circle, "
"which Kig does not currently support." ) );
return 0;
};
case G_ARCSECTOR:
{
notSupported( file, i18n( "This KSeg file contains an arc sector, "
"which Kig does not currently support." ) );
return 0;
};
case G_ARCSEGMENT:
{
notSupported( file, i18n( "This KSeg file contains an arc segment, "
"which Kig does not currently support." ) );
return 0;
};
case G_LOCUS:
{
switch( descendtype )
{
case G_TRANSLATED:
case G_ROTATED:
case G_SCALED:
case G_REFLECTED:
{
o = transformObject( file, *retdoc, parents, descendtype, ok );
break;
}
case G_OBJECT_LOCUS:
{
if ( nparents != 2 ) KIG_FILTER_PARSE_ERROR;
o = fact->locusCalcer( parents[0], parents[1] );
break;
}
default:
KIG_FILTER_PARSE_ERROR;
}
break;
};
case G_MEASURE:
KIG_FILTER_PARSE_ERROR;
case G_CALCULATE:
KIG_FILTER_PARSE_ERROR;
case G_ANNOTATION:
KIG_FILTER_PARSE_ERROR;
case G_LOOP:
KIG_FILTER_PARSE_ERROR;
default:
KIG_FILTER_PARSE_ERROR;
}
// checking if the object was correctly created
if ( ! o )
{
if ( ok )
KIG_FILTER_PARSE_ERROR
else
return 0;
}
ObjectDrawer* d = new ObjectDrawer( color, width, visible, style.pen.style() );
if ( !labeltext.isEmpty() )
{
ObjectConstCalcer* name = new ObjectConstCalcer( new StringImp( labeltext ) );
object = new ObjectHolder( o, d, name );
}
else
{
object = new ObjectHolder( o, d );
}
assert( object );
ret[i] = object;
object->calc( *retdoc );
if ( !labeltext.isEmpty() && labelVisible )
{
std::vector<ObjectCalcer*> args2;
args2.push_back( object->nameCalcer() );
ObjectCalcer* oc2 = fact->attachedLabelCalcer(
TQString::fromLatin1( "%1" ), object->calcer(),
static_cast<const PointImp*>( object->imp() )->coordinate(),
false, args2, *retdoc );
oc2->calc( *retdoc );
ObjectDrawer* d2 = new ObjectDrawer( style.pen.color() );
ObjectHolder* o2 = new ObjectHolder( oc2, d2 );
ret2.push_back( o2 );
}
};
// selection groups ( we ignore them, but we pretend to read them
// out anyway, so we can find what comes after them.. )
int selgroupcount;
stream >> selgroupcount;
for ( int i = 0; i < selgroupcount; ++i )
{
TQString name;
stream >> name;
int size;
stream >> size;
for ( int i = 0; i < size; ++i )
{
short object;
stream >> object;
(void) object;
};
};
// no more data in the file..
retdoc->addObjects( ret );
retdoc->addObjects( ret2 );
retdoc->setAxes( false );
retdoc->setGrid( false );
return retdoc;
}
KigFilterKSeg* KigFilterKSeg::instance()
{
static KigFilterKSeg f;
return &f;
}