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/objects/other_imp.cc

711 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 "other_imp.h"
#include "bogus_imp.h"
#include "point_imp.h"
#include "line_imp.h"
#include "../misc/screeninfo.h"
#include "../misc/common.h"
#include "../misc/kigtransform.h"
#include "../misc/kigpainter.h"
#include "../misc/goniometry.h"
#include "../kig/kig_view.h"
#include <klocale.h>
#include <cmath>
#include <utility>
using namespace std;
AngleImp::~AngleImp()
{
}
ObjectImp* AngleImp::transform( const Transformation& ) const
{
// TODO ?
return new InvalidImp;
}
void AngleImp::draw( KigPainter& p ) const
{
p.drawAngle( mpoint, mstartangle, mangle );
}
AngleImp::AngleImp( const Coordinate& pt, double start_angle_in_radials,
double angle_in_radials )
: mpoint( pt ), mstartangle( start_angle_in_radials ),
mangle( angle_in_radials )
{
}
bool AngleImp::tqcontains( const Coordinate& p, int width, const KigWidget& w ) const
{
double radius = 50*w.screenInfo().pixelWidth();
if ( fabs( (p-mpoint).length() - radius ) > w.screenInfo().normalMiss( width ) )
return false;
// and next we check if the angle is appropriate...
Coordinate vect = p - mpoint;
double angle = atan2( vect.y, vect.x );
while ( angle < mstartangle ) angle += 2*M_PI;
return angle <= mstartangle + mangle;
}
bool AngleImp::inRect( const Rect& r, int width, const KigWidget& w ) const
{
// TODO ?
return r.tqcontains( mpoint, w.screenInfo().normalMiss( width ) );
}
Coordinate AngleImp::attachPoint() const
{
return mpoint;
}
const uint AngleImp::numberOfProperties() const
{
return Parent::numberOfProperties() + 3;
}
const QCStringList AngleImp::propertiesInternalNames() const
{
QCStringList l = Parent::propertiesInternalNames();
l << "angle-radian";
l << "angle-degrees";
l << "angle-bisector";
assert( l.size() == AngleImp::numberOfProperties() );
return l;
}
const QCStringList AngleImp::properties() const
{
QCStringList l = Parent::properties();
l << I18N_NOOP( "Angle in Radians" );
l << I18N_NOOP( "Angle in Degrees" );
l << I18N_NOOP( "Angle Bisector" );
assert( l.size() == AngleImp::numberOfProperties() );
return l;
}
const ObjectImpType* AngleImp::impRequirementForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::impRequirementForProperty( which );
else return AngleImp::stype();
}
const char* AngleImp::iconForProperty( uint which ) const
{
int numprop = 0;
if ( which < Parent::numberOfProperties() )
return Parent::iconForProperty( which );
if ( which == Parent::numberOfProperties() + numprop++ )
return "angle_size"; // size in radians
else if ( which == Parent::numberOfProperties() + numprop++ )
return "angle_size"; // size in degrees
else if ( which == Parent::numberOfProperties() + numprop++ )
return "angle_bisector"; // angle bisector..
else assert( false );
return "";
}
ObjectImp* AngleImp::property( uint which, const KigDocument& w ) const
{
int numprop = 0;
if ( which < Parent::numberOfProperties() )
return Parent::property( which, w );
if ( which == Parent::numberOfProperties() + numprop++ )
return new DoubleImp( size() );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new DoubleImp( Goniometry::convert( size(), Goniometry::Rad, Goniometry::Deg ) );
else if ( which == Parent::numberOfProperties() + numprop++ )
{
const double angle = mstartangle + mangle / 2;
Coordinate p2 = mpoint + Coordinate( cos( angle ), sin( angle ) ) * 10;
return new RayImp( mpoint, p2 );
}
else assert( false );
return new InvalidImp;
}
const double AngleImp::size() const
{
return mangle;
}
ObjectImp* AngleImp::copy() const
{
return new AngleImp( mpoint, mstartangle, mangle );
}
VectorImp::VectorImp( const Coordinate& a, const Coordinate& b )
: mdata( a, b )
{
}
VectorImp::~VectorImp()
{
}
ObjectImp* VectorImp::transform( const Transformation& t ) const
{
Coordinate ta = t.apply( mdata.a );
Coordinate tb = t.apply( mdata.b );
if ( ta.valid() && tb.valid() ) return new VectorImp( ta, tb );
else return new InvalidImp;
}
void VectorImp::draw( KigPainter& p ) const
{
p.drawVector( mdata.a, mdata.b );
}
bool VectorImp::tqcontains( const Coordinate& o, int width, const KigWidget& w ) const
{
return internalContainsPoint( o, w.screenInfo().normalMiss( width ) );
}
bool VectorImp::inRect( const Rect& r, int width, const KigWidget& w ) const
{
return lineInRect( r, mdata.a, mdata.b, width, this, w );
}
const uint VectorImp::numberOfProperties() const
{
return Parent::numberOfProperties() + 5;
}
const QCStringList VectorImp::propertiesInternalNames() const
{
QCStringList ret = Parent::propertiesInternalNames();
ret << "length";
ret << "vect-mid-point";
ret << "length-x";
ret << "length-y";
ret << "vector-opposite";
assert( ret.size() == VectorImp::numberOfProperties() );
return ret;
}
const QCStringList VectorImp::properties() const
{
QCStringList ret = Parent::properties();
ret << I18N_NOOP( "Length" );
ret << I18N_NOOP( "Midpoint" );
ret << I18N_NOOP( "X length" );
ret << I18N_NOOP( "Y length" );
ret << I18N_NOOP( "Opposite Vector" );
assert( ret.size() == VectorImp::numberOfProperties() );
return ret;
}
const ObjectImpType* VectorImp::impRequirementForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::impRequirementForProperty( which );
else return VectorImp::stype();
}
const char* VectorImp::iconForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::iconForProperty( which );
else if ( which == Parent::numberOfProperties() )
return "distance"; // length
else if ( which == Parent::numberOfProperties() + 1 )
return "bisection"; // mid point
else if ( which == Parent::numberOfProperties() + 2 )
return "distance"; // length-x
else if ( which == Parent::numberOfProperties() + 3 )
return "distance"; // length-y
else if ( which == Parent::numberOfProperties() + 4 )
return "opposite-vector"; // opposite vector
else assert( false );
return "";
}
ObjectImp* VectorImp::property( uint which, const KigDocument& w ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::property( which, w );
else if ( which == Parent::numberOfProperties() )
return new DoubleImp( length() );
else if ( which == Parent::numberOfProperties() + 1 )
return new PointImp( ( mdata.a + mdata.b ) / 2 );
else if ( which == Parent::numberOfProperties() + 2 )
return new DoubleImp( fabs( mdata.a.x - mdata.b.x ) );
else if ( which == Parent::numberOfProperties() + 3 )
return new DoubleImp( fabs( mdata.a.y - mdata.b.y ) );
else if ( which == Parent::numberOfProperties() + 4 ) // opposite
return new VectorImp( mdata.a, 2*mdata.a-mdata.b );
else assert( false );
return new InvalidImp;
}
VectorImp* VectorImp::copy() const
{
return new VectorImp( mdata.a, mdata.b );
}
const Coordinate VectorImp::dir() const
{
return mdata.dir();
}
void AngleImp::visit( ObjectImpVisitor* vtor ) const
{
vtor->visit( this );
}
void VectorImp::visit( ObjectImpVisitor* vtor ) const
{
vtor->visit( this );
}
const double VectorImp::length() const
{
return ( mdata.a - mdata.b ).length();
}
ArcImp::ArcImp( const Coordinate& center, const double radius,
const double startangle, const double angle )
: CurveImp(), mcenter( center ), mradius( radius ),
msa( startangle ), ma( angle )
{
if ( ma < 0 )
{
// we want a positive angle..
msa = msa + ma;
ma = -ma;
};
}
ArcImp::~ArcImp()
{
}
ArcImp* ArcImp::copy() const
{
return new ArcImp( mcenter, mradius, msa, ma );
}
ObjectImp* ArcImp::transform( const Transformation& t ) const
{
//
// we don't have conic arcs! So it is invalid to transform an arc
// with a nonhomothetic transformation
//
if ( ! t.isHomothetic() ) return new InvalidImp();
Coordinate nc = t.apply( mcenter );
double nr = t.apply( mradius );
// transform msa...
double nmsa = msa;
if ( t.getAffineDeterminant() > 0 )
{
nmsa = msa - t.getRotationAngle();
} else
{
Coordinate ar = t.apply2by2only( Coordinate( cos(msa), sin(msa) ) );
nmsa = atan2( ar.y, ar.x );
nmsa -= ma;
}
while ( nmsa < -M_PI ) nmsa += 2*M_PI;
while ( nmsa > M_PI ) nmsa -= 2*M_PI;
if ( nc.valid() ) return new ArcImp( nc, nr, nmsa, ma );
else return new InvalidImp;
}
void ArcImp::draw( KigPainter& p ) const
{
p.drawArc( mcenter, mradius, msa, ma );
}
bool ArcImp::tqcontains( const Coordinate& p, int width, const KigWidget& w ) const
{
return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
}
bool ArcImp::inRect( const Rect&, int, const KigWidget& ) const
{
// TODO
return false;
}
bool ArcImp::valid() const
{
return true;
}
const uint ArcImp::numberOfProperties() const
{
return Parent::numberOfProperties() + 9;
}
const QCStringList ArcImp::properties() const
{
QCStringList ret = Parent::properties();
ret << I18N_NOOP( "Center" );
ret << I18N_NOOP( "Radius" );
ret << I18N_NOOP( "Angle" );
ret << I18N_NOOP( "Angle in Degrees" );
ret << I18N_NOOP( "Angle in Radians" );
ret << I18N_NOOP( "Sector Surface" );
ret << I18N_NOOP( "Arc Length" );
ret << I18N_NOOP( "First End Point" );
ret << I18N_NOOP( "Second End Point" );
assert( ret.size() == ArcImp::numberOfProperties() );
return ret;
}
const QCStringList ArcImp::propertiesInternalNames() const
{
QCStringList ret = Parent::propertiesInternalNames();
ret << "center";
ret << "radius";
ret << "angle";
ret << "angle-degrees";
ret << "angle-radians";
ret << "sector-surface";
ret << "arc-length";
ret << "end-point-A";
ret << "end-point-B";
return ret;
}
const char* ArcImp::iconForProperty( uint which ) const
{
int numprop = 0;
if ( which < Parent::numberOfProperties() )
return Parent::iconForProperty( which );
else if ( which == Parent::numberOfProperties() + numprop++ )
return "arc_center"; // center
else if ( which == Parent::numberOfProperties() + numprop++ )
return "";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "angle";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "angle_size";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "angle_size";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "";
else if ( which == Parent::numberOfProperties() + numprop++ )
return "";
else assert( false );
return "";
}
ObjectImp* ArcImp::property( uint which, const KigDocument& d ) const
{
int numprop = 0;
if ( which < Parent::numberOfProperties() )
return Parent::property( which, d );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new PointImp( mcenter );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new DoubleImp( mradius );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new AngleImp( mcenter, msa, ma );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new IntImp( static_cast<int>( Goniometry::convert( ma, Goniometry::Rad, Goniometry::Deg ) ) );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new DoubleImp( ma );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new DoubleImp( sectorSurface() );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new DoubleImp( mradius * ma );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new PointImp( firstEndPoint() );
else if ( which == Parent::numberOfProperties() + numprop++ )
return new PointImp( secondEndPoint() );
else assert( false );
return new InvalidImp;
}
const double ArcImp::sectorSurface() const
{
return mradius * mradius * ma / 2;
}
const ObjectImpType* ArcImp::impRequirementForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::impRequirementForProperty( which );
else
return ArcImp::stype();
}
void ArcImp::visit( ObjectImpVisitor* vtor ) const
{
vtor->visit( this );
}
double ArcImp::getParam( const Coordinate& c, const KigDocument& ) const
{
Coordinate d = (c - mcenter).normalize();
double angle = atan2( d.y, d.x );
angle -= msa;
// mp: problems with large arcs
while ( angle > ma/2 + M_PI ) angle -= 2*M_PI;
while ( angle < ma/2 - M_PI ) angle += 2*M_PI;
//
angle = max( 0., min( angle, ma ) );
angle /= ma;
return angle;
}
const Coordinate ArcImp::getPoint( double p, const KigDocument& ) const
{
double angle = msa + p * ma;
Coordinate d = Coordinate( cos( angle ), sin( angle ) ) * mradius;
return mcenter + d;
}
const Coordinate ArcImp::center() const
{
return mcenter;
}
double ArcImp::radius() const
{
return mradius;
}
double ArcImp::startAngle() const
{
return msa;
}
double ArcImp::angle() const
{
return ma;
}
Coordinate ArcImp::firstEndPoint() const
{
double angle = msa;
return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius;
}
Coordinate ArcImp::secondEndPoint() const
{
double angle = msa + ma;
return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius;
}
const Coordinate VectorImp::a() const
{
return mdata.a;
}
const Coordinate VectorImp::b() const
{
return mdata.b;
}
bool ArcImp::equals( const ObjectImp& rhs ) const
{
return rhs.inherits( ArcImp::stype() ) &&
static_cast<const ArcImp&>( rhs ).radius() == radius() &&
static_cast<const ArcImp&>( rhs ).startAngle() == startAngle() &&
static_cast<const ArcImp&>( rhs ).angle() == angle();
}
bool AngleImp::equals( const ObjectImp& rhs ) const
{
return rhs.inherits( AngleImp::stype() ) &&
static_cast<const AngleImp&>( rhs ).point() == point() &&
static_cast<const AngleImp&>( rhs ).startAngle() == startAngle() &&
static_cast<const AngleImp&>( rhs ).angle() == angle();
}
bool VectorImp::equals( const ObjectImp& rhs ) const
{
return rhs.inherits( VectorImp::stype() ) &&
static_cast<const VectorImp&>( rhs ).a() == a() &&
static_cast<const VectorImp&>( rhs ).b() == b();
}
const ObjectImpType* AngleImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "angle",
I18N_NOOP( "angle" ),
I18N_NOOP( "Select this angle" ),
I18N_NOOP( "Select angle %1" ),
I18N_NOOP( "Remove an Angle" ),
I18N_NOOP( "Add an Angle" ),
I18N_NOOP( "Move an Angle" ),
I18N_NOOP( "Attach to this angle" ),
I18N_NOOP( "Show an Angle" ),
I18N_NOOP( "Hide an Angle" )
);
return &t;
}
const ObjectImpType* VectorImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "vector",
I18N_NOOP( "vector" ),
I18N_NOOP( "Select this vector" ),
I18N_NOOP( "Select vector %1" ),
I18N_NOOP( "Remove a Vector" ),
I18N_NOOP( "Add a Vector" ),
I18N_NOOP( "Move a Vector" ),
I18N_NOOP( "Attach to this vector" ),
I18N_NOOP( "Show a Vector" ),
I18N_NOOP( "Hide a Vector" )
);
return &t;
}
const ObjectImpType* ArcImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "arc",
I18N_NOOP( "arc" ),
I18N_NOOP( "Select this arc" ),
I18N_NOOP( "Select arc %1" ),
I18N_NOOP( "Remove an Arc" ),
I18N_NOOP( "Add an Arc" ),
I18N_NOOP( "Move an Arc" ),
I18N_NOOP( "Attach to this arc" ),
I18N_NOOP( "Show an Arc" ),
I18N_NOOP( "Hide an Arc" )
);
return &t;
}
const ObjectImpType* AngleImp::type() const
{
return AngleImp::stype();
}
const ObjectImpType* VectorImp::type() const
{
return VectorImp::stype();
}
const ObjectImpType* ArcImp::type() const
{
return ArcImp::stype();
}
bool ArcImp::tqcontainsPoint( const Coordinate& p, const KigDocument& ) const
{
return internalContainsPoint( p, test_threshold );
}
bool ArcImp::internalContainsPoint( const Coordinate& p, double threshold ) const
{
return isOnArc( p, mcenter, mradius, msa, ma, threshold );
}
bool AngleImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::isPropertyDefinedOnOrThroughThisImp( which );
return false;
}
bool VectorImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const
{
return Parent::isPropertyDefinedOnOrThroughThisImp( which );
}
bool ArcImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::isPropertyDefinedOnOrThroughThisImp( which );
else if ( which == Parent::numberOfProperties() )
return true;
else
return false;
}
Rect AngleImp::surroundingRect() const
{
return Rect( mpoint, 0, 0 );
}
Rect VectorImp::surroundingRect() const
{
return Rect( mdata.a, mdata.b );
}
Rect ArcImp::surroundingRect() const
{
// the returned rect should contain the center point(?), the two end
// points, and all extreme x and y positions in between.
//Rect ret( mcenter, 0, 0 );
double a = msa;
//ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) );
Rect ret ( mcenter + mradius*Coordinate( cos( a ), sin( a ) ), 0, 0 );
a = msa + ma;
ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) );
for ( a = -2*M_PI; a <= 2*M_PI; a+=M_PI/2 )
{
Coordinate d = mcenter + mradius*Coordinate( cos( a ), sin( a ) );
if ( msa <= a && a <= msa + ma )
ret.setContains( d );
}
return ret;
}
const Coordinate VectorImp::getPoint( double param, const KigDocument& ) const
{
return mdata.a + mdata.dir() * param;
}
double VectorImp::getParam( const Coordinate& p, const KigDocument& ) const
{
Coordinate pt = calcPointOnPerpend( mdata, p );
pt = calcIntersectionPoint( mdata, LineData( p, pt ) );
// if pt is over the end of the vector we set it to one of the end
// points of the vector...
if ( ( pt - mdata.a ).length() > dir().length() )
pt = mdata.b;
else if ( ( pt - mdata.b ).length() > dir().length() )
pt = mdata.a;
if ( mdata.b == mdata.a ) return 0;
return ( ( pt - mdata.a ).length() ) / ( dir().length() );
}
bool VectorImp::tqcontainsPoint( const Coordinate& p, const KigDocument& ) const
{
return internalContainsPoint( p, test_threshold );
}
bool VectorImp::internalContainsPoint( const Coordinate& p, double threshold ) const
{
return isOnSegment( p, mdata.a, mdata.b, threshold );
}
LineData VectorImp::data() const
{
return mdata;
}