summaryrefslogtreecommitdiffstats
path: root/karbon/shapes/vstar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'karbon/shapes/vstar.cpp')
-rw-r--r--karbon/shapes/vstar.cpp348
1 files changed, 348 insertions, 0 deletions
diff --git a/karbon/shapes/vstar.cpp b/karbon/shapes/vstar.cpp
new file mode 100644
index 00000000..a43413c9
--- /dev/null
+++ b/karbon/shapes/vstar.cpp
@@ -0,0 +1,348 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001, 2002, 2003 The Karbon Developers
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+
+#include <math.h>
+
+#include <tqwmatrix.h>
+#include <tqdom.h>
+
+#include "vglobal.h"
+#include "vstar.h"
+#include "vtransformcmd.h"
+#include <tdelocale.h>
+#include <KoUnit.h>
+#include <vdocument.h>
+
+VStar::VStar( VObject* parent, VState state )
+ : VPath( parent, state )
+{
+}
+
+VStar::VStar( VObject* parent,
+ const KoPoint& center, double outerRadius, double innerRadius,
+ uint edges, double angle, uint innerAngle, double roundness, VStarType type )
+ : VPath( parent ), m_center( center), m_outerRadius( outerRadius ), m_innerRadius( innerRadius), m_edges( edges ), m_angle( angle ), m_innerAngle( innerAngle ), m_roundness( roundness ), m_type( type )
+{
+ init();
+}
+
+void
+VStar::init()
+{
+ double angle = m_angle;
+
+ // A star should have at least 3 edges:
+ if( m_edges < 3 )
+ m_edges = 3;
+
+ // Make sure, radii are positive:
+ if( m_outerRadius < 0.0 )
+ m_outerRadius = -m_outerRadius;
+
+ if( m_innerRadius < 0.0 )
+ m_innerRadius = -m_innerRadius;
+
+ // trick for spoke, wheel (libart bug?)
+ if( m_type == spoke || m_type == wheel && m_roundness == 0.0 )
+ m_roundness = 0.01;
+
+ // We start at angle + VGlobal::pi_2:
+ KoPoint p2, p3;
+ KoPoint p(
+ m_outerRadius * cos( angle + VGlobal::pi_2 ),
+ m_outerRadius * sin( angle + VGlobal::pi_2 ) );
+ moveTo( p );
+
+ double inAngle = VGlobal::twopi / 360 * m_innerAngle;
+
+ if( m_type == star )
+ {
+ int j = ( m_edges % 2 == 0 ) ? ( m_edges - 2 ) / 2 : ( m_edges - 1 ) / 2;
+ //innerRadius = getOptimalInnerRadius( outerRadius, edges, innerAngle );
+ int jumpto = 0;
+ bool discontinueous = ( m_edges % 4 == 2 );
+
+ double outerRoundness = ( VGlobal::twopi * m_outerRadius * m_roundness ) / m_edges;
+ double nextOuterAngle;
+
+ for ( uint i = 1; i < m_edges + 1; ++i )
+ {
+ double nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( jumpto + 0.5 );
+ p.setX( m_innerRadius * cos( nextInnerAngle ) );
+ p.setY( m_innerRadius * sin( nextInnerAngle ) );
+ if( m_roundness == 0.0 )
+ lineTo( p );
+ else
+ {
+ nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
+ p2.setX( m_outerRadius * cos( nextOuterAngle ) -
+ cos( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
+ p2.setY( m_outerRadius * sin( nextOuterAngle ) -
+ sin( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
+
+ curveTo( p2, p, p );
+ }
+
+ jumpto = ( i * j ) % m_edges;
+ nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( jumpto - 0.5 );
+ p.setX( m_innerRadius * cos( nextInnerAngle ) );
+ p.setY( m_innerRadius * sin( nextInnerAngle ) );
+ lineTo( p );
+
+ nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
+ p.setX( m_outerRadius * cos( nextOuterAngle ) );
+ p.setY( m_outerRadius * sin( nextOuterAngle ) );
+
+ if( m_roundness == 0.0 )
+ lineTo( p );
+ else
+ {
+ p2.setX( m_innerRadius * cos( nextInnerAngle ) );
+ p2.setY( m_innerRadius * sin( nextInnerAngle ) );
+
+ p3.setX( m_outerRadius * cos( nextOuterAngle ) +
+ cos( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
+ p3.setY( m_outerRadius * sin( nextOuterAngle ) +
+ sin( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
+
+ curveTo( p2, p3, p );
+ }
+ if( discontinueous && i == ( m_edges / 2 ) )
+ {
+ angle += VGlobal::pi;
+ nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
+ p.setX( m_outerRadius * cos( nextOuterAngle ) );
+ p.setY( m_outerRadius * sin( nextOuterAngle ) );
+ moveTo( p );
+ }
+ }
+ }
+ else
+ {
+ if( m_type == wheel || m_type == spoke )
+ m_innerRadius = 0.0;
+
+ double innerRoundness = ( VGlobal::twopi * m_innerRadius * m_roundness ) / m_edges;
+ double outerRoundness = ( VGlobal::twopi * m_outerRadius * m_roundness ) / m_edges;
+
+ for ( uint i = 0; i < m_edges; ++i )
+ {
+ double nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 1.0 );
+ double nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 0.5 );
+ if( m_type != polygon )
+ {
+ p.setX( m_innerRadius * cos( nextInnerAngle ) );
+ p.setY( m_innerRadius * sin( nextInnerAngle ) );
+
+ if( m_roundness == 0.0 )
+ lineTo( p );
+ else
+ {
+ p2.setX( m_outerRadius *
+ cos( angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i ) ) -
+ cos( angle + VGlobal::twopi / m_edges * ( i ) ) * outerRoundness );
+ p2.setY( m_outerRadius *
+ sin( angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i ) ) -
+ sin( angle + VGlobal::twopi / m_edges * ( i ) ) * outerRoundness );
+
+ p3.setX( m_innerRadius * cos( nextInnerAngle ) +
+ cos( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
+ p3.setY( m_innerRadius * sin( nextInnerAngle ) +
+ sin( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
+
+ if( m_type == gear )
+ {
+ lineTo( p2 );
+ lineTo( p3 );
+ lineTo( p );
+ }
+ else
+ curveTo( p2, p3, p );
+ }
+ }
+
+ p.setX( m_outerRadius * cos( nextOuterAngle ) );
+ p.setY( m_outerRadius * sin( nextOuterAngle ) );
+
+ if( m_roundness == 0.0 )
+ lineTo( p );
+ else
+ {
+ p2.setX( m_innerRadius * cos( nextInnerAngle ) -
+ cos( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
+ p2.setY( m_innerRadius * sin( nextInnerAngle ) -
+ sin( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
+
+ p3.setX( m_outerRadius * cos( nextOuterAngle ) +
+ cos( angle + VGlobal::twopi / m_edges * ( i + 1.0 ) ) * outerRoundness );
+ p3.setY( m_outerRadius * sin( nextOuterAngle ) +
+ sin( angle + VGlobal::twopi / m_edges * ( i + 1.0 ) ) * outerRoundness );
+
+ if( m_type == gear )
+ {
+ lineTo( p2 );
+ lineTo( p3 );
+ lineTo( p );
+ }
+ else
+ curveTo( p2, p3, p );
+ }
+ }
+ }
+ if( m_type == wheel || m_type == framed_star )
+ {
+ close();
+ for ( int i = m_edges - 1; i >= 0; --i )
+ {
+ double nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 1.0 );
+ p.setX( m_outerRadius * cos( nextOuterAngle ) );
+ p.setY( m_outerRadius * sin( nextOuterAngle ) );
+ lineTo( p );
+ }
+ }
+ close();
+
+ // translate path to center:
+ TQWMatrix m;
+ m.translate( m_center.x(), m_center.y() );
+
+ // only tranform the path data
+ VTransformCmd cmd( 0L, m );
+ cmd.VVisitor::visitVPath( *this );
+
+ setFillRule( evenOdd );
+
+ m_matrix.reset();
+}
+
+double
+VStar::getOptimalInnerRadius( uint edges, double outerRadius, uint /*innerAngle*/ )
+{
+ int j = (edges % 2 == 0 ) ? ( edges - 2 ) / 2 : ( edges - 1 ) / 2;
+
+ // get two well chosen lines of the star
+ KoPoint p1( outerRadius * cos( VGlobal::pi_2 ), outerRadius * sin( VGlobal::pi_2 ) );
+ int jumpto = ( j ) % edges;
+ double nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges * jumpto;
+ KoPoint p2( outerRadius * cos( nextOuterAngle ), outerRadius * sin( nextOuterAngle ) );
+
+ nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges;
+ KoPoint p3( outerRadius * cos( nextOuterAngle ),
+ outerRadius * sin( nextOuterAngle ) );
+ jumpto = ( edges - j + 1 ) % edges;
+ nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges * jumpto;
+ KoPoint p4( outerRadius * cos( nextOuterAngle ), outerRadius * sin( nextOuterAngle ) );
+
+ // calc (x, y) -> intersection point
+ double b1 = ( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
+ double b2 = ( p4.y() - p3.y() ) / ( p4.x() - p3.x() );
+ double a1 = p1.y() - b1 * p1.x();
+ double a2 = p3.y() - b2 * p3.x();
+ double x = -( a1 - a2 ) / ( b1 - b2 );
+ double y = a1 + b1 * x;
+ // calc inner radius from intersection point and center
+ return sqrt( x * x + y * y );
+}
+
+TQString
+VStar::name() const
+{
+ TQString result = VObject::name();
+ return !result.isEmpty() ? result : i18n( "Star" );
+}
+
+void
+VStar::save( TQDomElement& element ) const
+{
+ VDocument *doc = document();
+ if( doc && doc->saveAsPath() )
+ {
+ VPath::save( element );
+ return;
+ }
+
+ if( state() != deleted )
+ {
+ TQDomElement me = element.ownerDocument().createElement( "STAR" );
+ element.appendChild( me );
+
+ // save fill/stroke untransformed
+ VPath path( *this );
+ VTransformCmd cmd( 0L, m_matrix.invert() );
+ cmd.visit( path );
+ path.VObject::save( me );
+ //VObject::save( me );
+
+ me.setAttribute( "cx", m_center.x() );
+ me.setAttribute( "cy", m_center.y() );
+
+ me.setAttribute( "outerradius", m_outerRadius );
+ me.setAttribute( "innerradius", m_innerRadius );
+ me.setAttribute( "edges", m_edges );
+
+ me.setAttribute( "angle", m_angle );
+ me.setAttribute( "innerangle", m_innerAngle );
+
+ me.setAttribute( "roundness", m_roundness );
+
+ me.setAttribute( "type", m_type );
+
+ TQString transform = buildSvgTransform();
+ if( !transform.isEmpty() )
+ me.setAttribute( "transform", transform );
+ }
+}
+
+void
+VStar::load( const TQDomElement& element )
+{
+ setState( normal );
+
+ TQDomNodeList list = element.childNodes();
+ for( uint i = 0; i < list.count(); ++i )
+ if( list.item( i ).isElement() )
+ VObject::load( list.item( i ).toElement() );
+
+ m_center.setX( KoUnit::parseValue( element.attribute( "cx" ) ) );
+ m_center.setY( KoUnit::parseValue( element.attribute( "cy" ) ) );
+
+ m_outerRadius = KoUnit::parseValue( element.attribute( "outerradius" ) );
+ m_innerRadius = KoUnit::parseValue( element.attribute( "innerradius" ) );
+ m_edges = element.attribute( "edges" ).toUInt();
+
+ m_innerAngle = element.attribute( "innerangle" ).toUInt();
+ m_angle = element.attribute( "angle" ).toDouble();
+
+ m_roundness = element.attribute( "roundness" ).toDouble();
+
+ m_type =(VStar::VStarType) element.attribute( "type" ).toInt();
+
+ init();
+
+ TQString trafo = element.attribute( "transform" );
+ if( !trafo.isEmpty() )
+ transform( trafo );
+}
+
+VPath*
+VStar::clone() const
+{
+ return new VStar( *this );
+}