00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <math.h>
00022
00023 #include <qwmatrix.h>
00024 #include <qdom.h>
00025
00026 #include "vglobal.h"
00027 #include "vstar.h"
00028 #include "vtransformcmd.h"
00029 #include <klocale.h>
00030 #include <KoUnit.h>
00031 #include <vdocument.h>
00032
00033 VStar::VStar( VObject* parent, VState state )
00034 : VPath( parent, state )
00035 {
00036 }
00037
00038 VStar::VStar( VObject* parent,
00039 const KoPoint& center, double outerRadius, double innerRadius,
00040 uint edges, double angle, uint innerAngle, double roundness, VStarType type )
00041 : 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 )
00042 {
00043 init();
00044 }
00045
00046 void
00047 VStar::init()
00048 {
00049 double angle = m_angle;
00050
00051
00052 if( m_edges < 3 )
00053 m_edges = 3;
00054
00055
00056 if( m_outerRadius < 0.0 )
00057 m_outerRadius = -m_outerRadius;
00058
00059 if( m_innerRadius < 0.0 )
00060 m_innerRadius = -m_innerRadius;
00061
00062
00063 if( m_type == spoke || m_type == wheel && m_roundness == 0.0 )
00064 m_roundness = 0.01;
00065
00066
00067 KoPoint p2, p3;
00068 KoPoint p(
00069 m_outerRadius * cos( angle + VGlobal::pi_2 ),
00070 m_outerRadius * sin( angle + VGlobal::pi_2 ) );
00071 moveTo( p );
00072
00073 double inAngle = VGlobal::twopi / 360 * m_innerAngle;
00074
00075 if( m_type == star )
00076 {
00077 int j = ( m_edges % 2 == 0 ) ? ( m_edges - 2 ) / 2 : ( m_edges - 1 ) / 2;
00078
00079 int jumpto = 0;
00080 bool discontinueous = ( m_edges % 4 == 2 );
00081
00082 double outerRoundness = ( VGlobal::twopi * m_outerRadius * m_roundness ) / m_edges;
00083 double nextOuterAngle;
00084
00085 for ( uint i = 1; i < m_edges + 1; ++i )
00086 {
00087 double nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( jumpto + 0.5 );
00088 p.setX( m_innerRadius * cos( nextInnerAngle ) );
00089 p.setY( m_innerRadius * sin( nextInnerAngle ) );
00090 if( m_roundness == 0.0 )
00091 lineTo( p );
00092 else
00093 {
00094 nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
00095 p2.setX( m_outerRadius * cos( nextOuterAngle ) -
00096 cos( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
00097 p2.setY( m_outerRadius * sin( nextOuterAngle ) -
00098 sin( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
00099
00100 curveTo( p2, p, p );
00101 }
00102
00103 jumpto = ( i * j ) % m_edges;
00104 nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( jumpto - 0.5 );
00105 p.setX( m_innerRadius * cos( nextInnerAngle ) );
00106 p.setY( m_innerRadius * sin( nextInnerAngle ) );
00107 lineTo( p );
00108
00109 nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
00110 p.setX( m_outerRadius * cos( nextOuterAngle ) );
00111 p.setY( m_outerRadius * sin( nextOuterAngle ) );
00112
00113 if( m_roundness == 0.0 )
00114 lineTo( p );
00115 else
00116 {
00117 p2.setX( m_innerRadius * cos( nextInnerAngle ) );
00118 p2.setY( m_innerRadius * sin( nextInnerAngle ) );
00119
00120 p3.setX( m_outerRadius * cos( nextOuterAngle ) +
00121 cos( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
00122 p3.setY( m_outerRadius * sin( nextOuterAngle ) +
00123 sin( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
00124
00125 curveTo( p2, p3, p );
00126 }
00127 if( discontinueous && i == ( m_edges / 2 ) )
00128 {
00129 angle += VGlobal::pi;
00130 nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
00131 p.setX( m_outerRadius * cos( nextOuterAngle ) );
00132 p.setY( m_outerRadius * sin( nextOuterAngle ) );
00133 moveTo( p );
00134 }
00135 }
00136 }
00137 else
00138 {
00139 if( m_type == wheel || m_type == spoke )
00140 m_innerRadius = 0.0;
00141
00142 double innerRoundness = ( VGlobal::twopi * m_innerRadius * m_roundness ) / m_edges;
00143 double outerRoundness = ( VGlobal::twopi * m_outerRadius * m_roundness ) / m_edges;
00144
00145 for ( uint i = 0; i < m_edges; ++i )
00146 {
00147 double nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 1.0 );
00148 double nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 0.5 );
00149 if( m_type != polygon )
00150 {
00151 p.setX( m_innerRadius * cos( nextInnerAngle ) );
00152 p.setY( m_innerRadius * sin( nextInnerAngle ) );
00153
00154 if( m_roundness == 0.0 )
00155 lineTo( p );
00156 else
00157 {
00158 p2.setX( m_outerRadius *
00159 cos( angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i ) ) -
00160 cos( angle + VGlobal::twopi / m_edges * ( i ) ) * outerRoundness );
00161 p2.setY( m_outerRadius *
00162 sin( angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i ) ) -
00163 sin( angle + VGlobal::twopi / m_edges * ( i ) ) * outerRoundness );
00164
00165 p3.setX( m_innerRadius * cos( nextInnerAngle ) +
00166 cos( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
00167 p3.setY( m_innerRadius * sin( nextInnerAngle ) +
00168 sin( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
00169
00170 if( m_type == gear )
00171 {
00172 lineTo( p2 );
00173 lineTo( p3 );
00174 lineTo( p );
00175 }
00176 else
00177 curveTo( p2, p3, p );
00178 }
00179 }
00180
00181 p.setX( m_outerRadius * cos( nextOuterAngle ) );
00182 p.setY( m_outerRadius * sin( nextOuterAngle ) );
00183
00184 if( m_roundness == 0.0 )
00185 lineTo( p );
00186 else
00187 {
00188 p2.setX( m_innerRadius * cos( nextInnerAngle ) -
00189 cos( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
00190 p2.setY( m_innerRadius * sin( nextInnerAngle ) -
00191 sin( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
00192
00193 p3.setX( m_outerRadius * cos( nextOuterAngle ) +
00194 cos( angle + VGlobal::twopi / m_edges * ( i + 1.0 ) ) * outerRoundness );
00195 p3.setY( m_outerRadius * sin( nextOuterAngle ) +
00196 sin( angle + VGlobal::twopi / m_edges * ( i + 1.0 ) ) * outerRoundness );
00197
00198 if( m_type == gear )
00199 {
00200 lineTo( p2 );
00201 lineTo( p3 );
00202 lineTo( p );
00203 }
00204 else
00205 curveTo( p2, p3, p );
00206 }
00207 }
00208 }
00209 if( m_type == wheel || m_type == framed_star )
00210 {
00211 close();
00212 for ( int i = m_edges - 1; i >= 0; --i )
00213 {
00214 double nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 1.0 );
00215 p.setX( m_outerRadius * cos( nextOuterAngle ) );
00216 p.setY( m_outerRadius * sin( nextOuterAngle ) );
00217 lineTo( p );
00218 }
00219 }
00220 close();
00221
00222
00223 QWMatrix m;
00224 m.translate( m_center.x(), m_center.y() );
00225
00226
00227 VTransformCmd cmd( 0L, m );
00228 cmd.VVisitor::visitVPath( *this );
00229
00230 setFillRule( evenOdd );
00231
00232 m_matrix.reset();
00233 }
00234
00235 double
00236 VStar::getOptimalInnerRadius( uint edges, double outerRadius, uint )
00237 {
00238 int j = (edges % 2 == 0 ) ? ( edges - 2 ) / 2 : ( edges - 1 ) / 2;
00239
00240
00241 KoPoint p1( outerRadius * cos( VGlobal::pi_2 ), outerRadius * sin( VGlobal::pi_2 ) );
00242 int jumpto = ( j ) % edges;
00243 double nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges * jumpto;
00244 KoPoint p2( outerRadius * cos( nextOuterAngle ), outerRadius * sin( nextOuterAngle ) );
00245
00246 nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges;
00247 KoPoint p3( outerRadius * cos( nextOuterAngle ),
00248 outerRadius * sin( nextOuterAngle ) );
00249 jumpto = ( edges - j + 1 ) % edges;
00250 nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges * jumpto;
00251 KoPoint p4( outerRadius * cos( nextOuterAngle ), outerRadius * sin( nextOuterAngle ) );
00252
00253
00254 double b1 = ( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
00255 double b2 = ( p4.y() - p3.y() ) / ( p4.x() - p3.x() );
00256 double a1 = p1.y() - b1 * p1.x();
00257 double a2 = p3.y() - b2 * p3.x();
00258 double x = -( a1 - a2 ) / ( b1 - b2 );
00259 double y = a1 + b1 * x;
00260
00261 return sqrt( x * x + y * y );
00262 }
00263
00264 QString
00265 VStar::name() const
00266 {
00267 QString result = VObject::name();
00268 return !result.isEmpty() ? result : i18n( "Star" );
00269 }
00270
00271 void
00272 VStar::save( QDomElement& element ) const
00273 {
00274 VDocument *doc = document();
00275 if( doc && doc->saveAsPath() )
00276 {
00277 VPath::save( element );
00278 return;
00279 }
00280
00281 if( state() != deleted )
00282 {
00283 QDomElement me = element.ownerDocument().createElement( "STAR" );
00284 element.appendChild( me );
00285
00286
00287 VPath path( *this );
00288 VTransformCmd cmd( 0L, m_matrix.invert() );
00289 cmd.visit( path );
00290 path.VObject::save( me );
00291
00292
00293 me.setAttribute( "cx", m_center.x() );
00294 me.setAttribute( "cy", m_center.y() );
00295
00296 me.setAttribute( "outerradius", m_outerRadius );
00297 me.setAttribute( "innerradius", m_innerRadius );
00298 me.setAttribute( "edges", m_edges );
00299
00300 me.setAttribute( "angle", m_angle );
00301 me.setAttribute( "innerangle", m_innerAngle );
00302
00303 me.setAttribute( "roundness", m_roundness );
00304
00305 me.setAttribute( "type", m_type );
00306
00307 QString transform = buildSvgTransform();
00308 if( !transform.isEmpty() )
00309 me.setAttribute( "transform", transform );
00310 }
00311 }
00312
00313 void
00314 VStar::load( const QDomElement& element )
00315 {
00316 setState( normal );
00317
00318 QDomNodeList list = element.childNodes();
00319 for( uint i = 0; i < list.count(); ++i )
00320 if( list.item( i ).isElement() )
00321 VObject::load( list.item( i ).toElement() );
00322
00323 m_center.setX( KoUnit::parseValue( element.attribute( "cx" ) ) );
00324 m_center.setY( KoUnit::parseValue( element.attribute( "cy" ) ) );
00325
00326 m_outerRadius = KoUnit::parseValue( element.attribute( "outerradius" ) );
00327 m_innerRadius = KoUnit::parseValue( element.attribute( "innerradius" ) );
00328 m_edges = element.attribute( "edges" ).toUInt();
00329
00330 m_innerAngle = element.attribute( "innerangle" ).toUInt();
00331 m_angle = element.attribute( "angle" ).toDouble();
00332
00333 m_roundness = element.attribute( "roundness" ).toDouble();
00334
00335 m_type =(VStar::VStarType) element.attribute( "type" ).toInt();
00336
00337 init();
00338
00339 QString trafo = element.attribute( "transform" );
00340 if( !trafo.isEmpty() )
00341 transform( trafo );
00342 }
00343
00344 VPath*
00345 VStar::clone() const
00346 {
00347 return new VStar( *this );
00348 }