karbon

vstar.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001, 2002, 2003 The Karbon Developers
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20
21#include <math.h>
22
23#include <tqwmatrix.h>
24#include <tqdom.h>
25
26#include "vglobal.h"
27#include "vstar.h"
28#include "vtransformcmd.h"
29#include <tdelocale.h>
30#include <KoUnit.h>
31#include <vdocument.h>
32
33VStar::VStar( VObject* parent, VState state )
34 : VPath( parent, state )
35{
36}
37
38VStar::VStar( VObject* parent,
39 const KoPoint& center, double outerRadius, double innerRadius,
40 uint edges, double angle, uint innerAngle, double roundness, VStarType type )
41 : 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 )
42{
43 init();
44}
45
46void
47VStar::init()
48{
49 double angle = m_angle;
50
51 // A star should have at least 3 edges:
52 if( m_edges < 3 )
53 m_edges = 3;
54
55 // Make sure, radii are positive:
56 if( m_outerRadius < 0.0 )
57 m_outerRadius = -m_outerRadius;
58
59 if( m_innerRadius < 0.0 )
60 m_innerRadius = -m_innerRadius;
61
62 // trick for spoke, wheel (libart bug?)
63 if( m_type == spoke || m_type == wheel && m_roundness == 0.0 )
64 m_roundness = 0.01;
65
66 // We start at angle + VGlobal::pi_2:
67 KoPoint p2, p3;
68 KoPoint p(
69 m_outerRadius * cos( angle + VGlobal::pi_2 ),
70 m_outerRadius * sin( angle + VGlobal::pi_2 ) );
71 moveTo( p );
72
73 double inAngle = VGlobal::twopi / 360 * m_innerAngle;
74
75 if( m_type == star )
76 {
77 int j = ( m_edges % 2 == 0 ) ? ( m_edges - 2 ) / 2 : ( m_edges - 1 ) / 2;
78 //innerRadius = getOptimalInnerRadius( outerRadius, edges, innerAngle );
79 int jumpto = 0;
80 bool discontinueous = ( m_edges % 4 == 2 );
81
82 double outerRoundness = ( VGlobal::twopi * m_outerRadius * m_roundness ) / m_edges;
83 double nextOuterAngle;
84
85 for ( uint i = 1; i < m_edges + 1; ++i )
86 {
87 double nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( jumpto + 0.5 );
88 p.setX( m_innerRadius * cos( nextInnerAngle ) );
89 p.setY( m_innerRadius * sin( nextInnerAngle ) );
90 if( m_roundness == 0.0 )
91 lineTo( p );
92 else
93 {
94 nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
95 p2.setX( m_outerRadius * cos( nextOuterAngle ) -
96 cos( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
97 p2.setY( m_outerRadius * sin( nextOuterAngle ) -
98 sin( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
99
100 curveTo( p2, p, p );
101 }
102
103 jumpto = ( i * j ) % m_edges;
104 nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( jumpto - 0.5 );
105 p.setX( m_innerRadius * cos( nextInnerAngle ) );
106 p.setY( m_innerRadius * sin( nextInnerAngle ) );
107 lineTo( p );
108
109 nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
110 p.setX( m_outerRadius * cos( nextOuterAngle ) );
111 p.setY( m_outerRadius * sin( nextOuterAngle ) );
112
113 if( m_roundness == 0.0 )
114 lineTo( p );
115 else
116 {
117 p2.setX( m_innerRadius * cos( nextInnerAngle ) );
118 p2.setY( m_innerRadius * sin( nextInnerAngle ) );
119
120 p3.setX( m_outerRadius * cos( nextOuterAngle ) +
121 cos( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
122 p3.setY( m_outerRadius * sin( nextOuterAngle ) +
123 sin( angle + VGlobal::twopi / m_edges * jumpto ) * outerRoundness );
124
125 curveTo( p2, p3, p );
126 }
127 if( discontinueous && i == ( m_edges / 2 ) )
128 {
129 angle += VGlobal::pi;
130 nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * jumpto;
131 p.setX( m_outerRadius * cos( nextOuterAngle ) );
132 p.setY( m_outerRadius * sin( nextOuterAngle ) );
133 moveTo( p );
134 }
135 }
136 }
137 else
138 {
139 if( m_type == wheel || m_type == spoke )
140 m_innerRadius = 0.0;
141
142 double innerRoundness = ( VGlobal::twopi * m_innerRadius * m_roundness ) / m_edges;
143 double outerRoundness = ( VGlobal::twopi * m_outerRadius * m_roundness ) / m_edges;
144
145 for ( uint i = 0; i < m_edges; ++i )
146 {
147 double nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 1.0 );
148 double nextInnerAngle = angle + inAngle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 0.5 );
149 if( m_type != polygon )
150 {
151 p.setX( m_innerRadius * cos( nextInnerAngle ) );
152 p.setY( m_innerRadius * sin( nextInnerAngle ) );
153
154 if( m_roundness == 0.0 )
155 lineTo( p );
156 else
157 {
158 p2.setX( m_outerRadius *
159 cos( angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i ) ) -
160 cos( angle + VGlobal::twopi / m_edges * ( i ) ) * outerRoundness );
161 p2.setY( m_outerRadius *
162 sin( angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i ) ) -
163 sin( angle + VGlobal::twopi / m_edges * ( i ) ) * outerRoundness );
164
165 p3.setX( m_innerRadius * cos( nextInnerAngle ) +
166 cos( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
167 p3.setY( m_innerRadius * sin( nextInnerAngle ) +
168 sin( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
169
170 if( m_type == gear )
171 {
172 lineTo( p2 );
173 lineTo( p3 );
174 lineTo( p );
175 }
176 else
177 curveTo( p2, p3, p );
178 }
179 }
180
181 p.setX( m_outerRadius * cos( nextOuterAngle ) );
182 p.setY( m_outerRadius * sin( nextOuterAngle ) );
183
184 if( m_roundness == 0.0 )
185 lineTo( p );
186 else
187 {
188 p2.setX( m_innerRadius * cos( nextInnerAngle ) -
189 cos( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
190 p2.setY( m_innerRadius * sin( nextInnerAngle ) -
191 sin( angle + inAngle + VGlobal::twopi / m_edges * ( i + 0.5 ) ) * innerRoundness );
192
193 p3.setX( m_outerRadius * cos( nextOuterAngle ) +
194 cos( angle + VGlobal::twopi / m_edges * ( i + 1.0 ) ) * outerRoundness );
195 p3.setY( m_outerRadius * sin( nextOuterAngle ) +
196 sin( angle + VGlobal::twopi / m_edges * ( i + 1.0 ) ) * outerRoundness );
197
198 if( m_type == gear )
199 {
200 lineTo( p2 );
201 lineTo( p3 );
202 lineTo( p );
203 }
204 else
205 curveTo( p2, p3, p );
206 }
207 }
208 }
209 if( m_type == wheel || m_type == framed_star )
210 {
211 close();
212 for ( int i = m_edges - 1; i >= 0; --i )
213 {
214 double nextOuterAngle = angle + VGlobal::pi_2 + VGlobal::twopi / m_edges * ( i + 1.0 );
215 p.setX( m_outerRadius * cos( nextOuterAngle ) );
216 p.setY( m_outerRadius * sin( nextOuterAngle ) );
217 lineTo( p );
218 }
219 }
220 close();
221
222 // translate path to center:
223 TQWMatrix m;
224 m.translate( m_center.x(), m_center.y() );
225
226 // only tranform the path data
227 VTransformCmd cmd( 0L, m );
228 cmd.VVisitor::visitVPath( *this );
229
230 setFillRule( evenOdd );
231
232 m_matrix.reset();
233}
234
235double
236VStar::getOptimalInnerRadius( uint edges, double outerRadius, uint /*innerAngle*/ )
237{
238 int j = (edges % 2 == 0 ) ? ( edges - 2 ) / 2 : ( edges - 1 ) / 2;
239
240 // get two well chosen lines of the star
241 KoPoint p1( outerRadius * cos( VGlobal::pi_2 ), outerRadius * sin( VGlobal::pi_2 ) );
242 int jumpto = ( j ) % edges;
243 double nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges * jumpto;
244 KoPoint p2( outerRadius * cos( nextOuterAngle ), outerRadius * sin( nextOuterAngle ) );
245
246 nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges;
247 KoPoint p3( outerRadius * cos( nextOuterAngle ),
248 outerRadius * sin( nextOuterAngle ) );
249 jumpto = ( edges - j + 1 ) % edges;
250 nextOuterAngle = VGlobal::pi_2 + VGlobal::twopi / edges * jumpto;
251 KoPoint p4( outerRadius * cos( nextOuterAngle ), outerRadius * sin( nextOuterAngle ) );
252
253 // calc (x, y) -> intersection point
254 double b1 = ( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
255 double b2 = ( p4.y() - p3.y() ) / ( p4.x() - p3.x() );
256 double a1 = p1.y() - b1 * p1.x();
257 double a2 = p3.y() - b2 * p3.x();
258 double x = -( a1 - a2 ) / ( b1 - b2 );
259 double y = a1 + b1 * x;
260 // calc inner radius from intersection point and center
261 return sqrt( x * x + y * y );
262}
263
264TQString
266{
267 TQString result = VObject::name();
268 return !result.isEmpty() ? result : i18n( "Star" );
269}
270
271void
272VStar::save( TQDomElement& element ) const
273{
274 VDocument *doc = document();
275 if( doc && doc->saveAsPath() )
276 {
277 VPath::save( element );
278 return;
279 }
280
281 if( state() != deleted )
282 {
283 TQDomElement me = element.ownerDocument().createElement( "STAR" );
284 element.appendChild( me );
285
286 // save fill/stroke untransformed
287 VPath path( *this );
288 VTransformCmd cmd( 0L, m_matrix.invert() );
289 cmd.visit( path );
290 path.VObject::save( me );
291 //VObject::save( me );
292
293 me.setAttribute( "cx", m_center.x() );
294 me.setAttribute( "cy", m_center.y() );
295
296 me.setAttribute( "outerradius", m_outerRadius );
297 me.setAttribute( "innerradius", m_innerRadius );
298 me.setAttribute( "edges", m_edges );
299
300 me.setAttribute( "angle", m_angle );
301 me.setAttribute( "innerangle", m_innerAngle );
302
303 me.setAttribute( "roundness", m_roundness );
304
305 me.setAttribute( "type", m_type );
306
307 TQString transform = buildSvgTransform();
308 if( !transform.isEmpty() )
309 me.setAttribute( "transform", transform );
310 }
311}
312
313void
314VStar::load( const TQDomElement& element )
315{
316 setState( normal );
317
318 TQDomNodeList list = element.childNodes();
319 for( uint i = 0; i < list.count(); ++i )
320 if( list.item( i ).isElement() )
321 VObject::load( list.item( i ).toElement() );
322
323 m_center.setX( KoUnit::parseValue( element.attribute( "cx" ) ) );
324 m_center.setY( KoUnit::parseValue( element.attribute( "cy" ) ) );
325
326 m_outerRadius = KoUnit::parseValue( element.attribute( "outerradius" ) );
327 m_innerRadius = KoUnit::parseValue( element.attribute( "innerradius" ) );
328 m_edges = element.attribute( "edges" ).toUInt();
329
330 m_innerAngle = element.attribute( "innerangle" ).toUInt();
331 m_angle = element.attribute( "angle" ).toDouble();
332
333 m_roundness = element.attribute( "roundness" ).toDouble();
334
335 m_type =(VStar::VStarType) element.attribute( "type" ).toInt();
336
337 init();
338
339 TQString trafo = element.attribute( "transform" );
340 if( !trafo.isEmpty() )
341 transform( trafo );
342}
343
344VPath*
346{
347 return new VStar( *this );
348}
All non-visual, static doc info is in here.
Definition vdocument.h:53
The base class for all karbon objects.
Definition vobject.h:50
virtual void setState(const VState state)
Sets the state to a specified new state.
Definition vobject.h:163
@ deleted
deleted, nearly dead
Definition vobject.h:58
@ normal
visible, not active
Definition vobject.h:54
virtual TQString name() const
Returns the name of the object.
Definition vobject.cpp:225
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
Definition vobject.cpp:157
VDocument * document() const
Return document the object belongs to.
Definition vobject.cpp:216
VState state() const
Get the state the object is in.
Definition vobject.h:154
A composite path consists of one or many subpaths.
Definition vcomposite.h:48
void close()
Closes the current subpath.
virtual void save(TQDomElement &element) const
Save this object's state to xml.
This shape offers star-like shapes with a lot of parameters :
Definition vstar.h:48
virtual VPath * clone() const
Create an exact copy of this object.
Definition vstar.cpp:345
virtual void save(TQDomElement &element) const
Save this object's state to xml.
Definition vstar.cpp:272
virtual TQString name() const
Returns the name of the object.
Definition vstar.cpp:265
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
Definition vstar.cpp:314