~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to plugins/pathshapes/star/KoStarShape.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2010-09-21 15:36:35 UTC
  • mfrom: (1.4.1 upstream) (60.2.11 maverick)
  • Revision ID: james.westby@ubuntu.com-20100921153635-6tejqkiro2u21ydi
Tags: 1:2.2.2-0ubuntu3
Add kubuntu_03_fix-crash-on-closing-sqlite-connection-2.2.2.diff and
kubuntu_04_support-large-memo-values-for-msaccess-2.2.2.diff as
recommended by upstream http://kexi-
project.org/wiki/wikiview/index.php@Kexi2.2_Patches.html#sqlite_stab
ility

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* This file is part of the KDE project
2
 
   Copyright (C) 2006-2009 Jan Hambrecht <jaham@gmx.net>
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
 
#include "KoStarShape.h"
21
 
 
22
 
#include <KoPathPoint.h>
23
 
#include <KoShapeLoadingContext.h>
24
 
#include <KoShapeSavingContext.h>
25
 
#include <KoXmlReader.h>
26
 
#include <KoXmlNS.h>
27
 
#include <KoXmlWriter.h>
28
 
 
29
 
#include <math.h>
30
 
 
31
 
KoStarShape::KoStarShape()
32
 
: m_cornerCount( 5 )
33
 
, m_zoomX( 1.0 )
34
 
, m_zoomY( 1.0 )
35
 
, m_convex( false )
36
 
{
37
 
    m_radius[base] = 25.0;
38
 
    m_radius[tip] = 50.0;
39
 
    m_angles[base] = m_angles[tip] = defaultAngleRadian();
40
 
    m_roundness[base] = m_roundness[tip] = 0.0f;
41
 
 
42
 
    m_center = QPointF(50,50);
43
 
    updatePath( QSize(100,100) );
44
 
}
45
 
 
46
 
KoStarShape::~KoStarShape()
47
 
{
48
 
}
49
 
 
50
 
void KoStarShape::setCornerCount( uint cornerCount )
51
 
{
52
 
    if( cornerCount >= 3 )
53
 
    {
54
 
        double oldDefaultAngle = defaultAngleRadian();
55
 
        m_cornerCount = cornerCount;
56
 
        double newDefaultAngle = defaultAngleRadian();
57
 
        m_angles[base] += newDefaultAngle-oldDefaultAngle;
58
 
        m_angles[tip] += newDefaultAngle-oldDefaultAngle;
59
 
        
60
 
        updatePath( QSize() );
61
 
    }
62
 
}
63
 
 
64
 
uint KoStarShape::cornerCount() const
65
 
{
66
 
    return m_cornerCount;
67
 
}
68
 
 
69
 
void KoStarShape::setBaseRadius( qreal baseRadius )
70
 
{
71
 
    m_radius[base] = fabs( baseRadius );
72
 
    updatePath( QSize() );
73
 
}
74
 
 
75
 
qreal KoStarShape::baseRadius() const
76
 
{
77
 
    return m_radius[base];
78
 
}
79
 
 
80
 
void KoStarShape::setTipRadius( qreal tipRadius )
81
 
{
82
 
    m_radius[tip] = fabs( tipRadius );
83
 
    updatePath( QSize() );
84
 
}
85
 
 
86
 
qreal KoStarShape::tipRadius() const
87
 
{
88
 
    return m_radius[tip];
89
 
}
90
 
 
91
 
void KoStarShape::setBaseRoundness( qreal baseRoundness )
92
 
{
93
 
    m_roundness[base] = baseRoundness;
94
 
    updatePath( QSize() );
95
 
}
96
 
 
97
 
void KoStarShape::setTipRoundness( qreal tipRoundness )
98
 
{
99
 
    m_roundness[tip] = tipRoundness;
100
 
    updatePath( QSize() );
101
 
}
102
 
 
103
 
void KoStarShape::setConvex( bool convex )
104
 
{
105
 
    m_convex = convex;
106
 
    updatePath( QSize() );
107
 
}
108
 
 
109
 
bool KoStarShape::convex() const
110
 
{
111
 
    return m_convex;
112
 
}
113
 
 
114
 
QPointF KoStarShape::starCenter() const
115
 
{
116
 
    return m_center;
117
 
}
118
 
 
119
 
void KoStarShape::moveHandleAction( int handleId, const QPointF & point, Qt::KeyboardModifiers modifiers )
120
 
{
121
 
    if( modifiers & Qt::ShiftModifier )
122
 
    {
123
 
        QPointF tangentVector = point - m_handles[handleId];
124
 
        qreal distance = sqrt( tangentVector.x()*tangentVector.x() + tangentVector.y()*tangentVector.y() );
125
 
        QPointF radialVector = m_handles[handleId] - m_center;
126
 
        // cross product to determine in which direction the user is dragging
127
 
        qreal moveDirection = radialVector.x()*tangentVector.y() - radialVector.y()*tangentVector.x();
128
 
        // make the roundness stick to zero if distance is under a certain value
129
 
        float snapDistance = 3.0;
130
 
        if( distance >= 0.0 )
131
 
            distance = distance < snapDistance ? 0.0 : distance-snapDistance;
132
 
        else
133
 
            distance = distance > -snapDistance ? 0.0 : distance+snapDistance;
134
 
        // control changes roundness on both handles, else only the actual handle roundness is changed
135
 
        if( modifiers & Qt::ControlModifier )
136
 
            m_roundness[handleId] = moveDirection < 0.0f ? distance : -distance;
137
 
        else
138
 
            m_roundness[base] = m_roundness[tip] = moveDirection < 0.0f ? distance : -distance;
139
 
    }
140
 
    else
141
 
    {
142
 
        QPointF distVector = point - m_center;
143
 
        // unapply scaling
144
 
        distVector.rx() /= m_zoomX;
145
 
        distVector.ry() /= m_zoomY;
146
 
        m_radius[handleId] = sqrt( distVector.x()*distVector.x() + distVector.y()*distVector.y() );
147
 
 
148
 
        qreal angle = atan2( distVector.y(), distVector.x() );
149
 
        if( angle < 0.0 )
150
 
            angle += 2.0*M_PI;
151
 
        qreal diffAngle = angle-m_angles[handleId];
152
 
        qreal radianStep = M_PI / static_cast<qreal>(m_cornerCount);
153
 
        if( handleId == tip )
154
 
        {
155
 
            m_angles[tip] += diffAngle-radianStep;
156
 
            m_angles[base] += diffAngle-radianStep;
157
 
        }
158
 
        else
159
 
        {
160
 
            // control make the base point move freely
161
 
            if( modifiers & Qt::ControlModifier )
162
 
                m_angles[base] += diffAngle-2*radianStep;
163
 
            else
164
 
                m_angles[base] = m_angles[tip];
165
 
        }
166
 
    }
167
 
}
168
 
 
169
 
void KoStarShape::updatePath( const QSizeF &size )
170
 
{
171
 
    Q_UNUSED(size);
172
 
    qreal radianStep = M_PI / static_cast<qreal>(m_cornerCount);
173
 
 
174
 
    createPoints( m_convex ? m_cornerCount : 2*m_cornerCount );
175
 
 
176
 
    KoSubpath &points = *m_subpaths[0];
177
 
 
178
 
    uint index = 0;
179
 
    for( uint i = 0; i < 2*m_cornerCount; ++i )
180
 
    {
181
 
        uint cornerType = i % 2;
182
 
        if( cornerType == base && m_convex )
183
 
            continue;
184
 
        qreal radian = static_cast<qreal>( (i+1)*radianStep ) + m_angles[cornerType];
185
 
        QPointF cornerPoint = QPointF( m_zoomX * m_radius[cornerType] * cos( radian ), m_zoomY * m_radius[cornerType] * sin( radian ) );
186
 
 
187
 
        points[index]->setPoint( m_center + cornerPoint );
188
 
        points[index]->unsetProperty( KoPathPoint::StopSubpath );
189
 
        points[index]->unsetProperty( KoPathPoint::CloseSubpath );
190
 
        if( m_roundness[cornerType] > 1e-10 || m_roundness[cornerType] < -1e-10 )
191
 
        {
192
 
            // normalized cross product to compute tangential vector for handle point
193
 
            QPointF tangentVector( cornerPoint.y()/m_radius[cornerType], -cornerPoint.x()/m_radius[cornerType] );
194
 
            points[index]->setControlPoint2( points[index]->point() - m_roundness[cornerType] * tangentVector );
195
 
            points[index]->setControlPoint1( points[index]->point() + m_roundness[cornerType] * tangentVector );
196
 
        }
197
 
        else
198
 
        {
199
 
            points[index]->removeControlPoint1();
200
 
            points[index]->removeControlPoint2();
201
 
        }
202
 
        index++;
203
 
    }
204
 
 
205
 
    // first path starts and closes path
206
 
    points[0]->setProperty( KoPathPoint::StartSubpath );
207
 
    points[0]->setProperty( KoPathPoint::CloseSubpath );
208
 
    // last point stops and closes path
209
 
    points.last()->setProperty( KoPathPoint::StopSubpath );
210
 
    points.last()->setProperty( KoPathPoint::CloseSubpath );
211
 
 
212
 
    normalize();
213
 
 
214
 
    m_handles.clear();
215
 
    m_handles.push_back( points.at(tip)->point() );
216
 
    if( ! m_convex )
217
 
        m_handles.push_back( points.at(base)->point() );
218
 
 
219
 
    m_center = computeCenter();
220
 
}
221
 
 
222
 
void KoStarShape::createPoints( int requiredPointCount )
223
 
{
224
 
    if ( m_subpaths.count() != 1 ) {
225
 
        clear();
226
 
        m_subpaths.append( new KoSubpath() );
227
 
    }
228
 
    int currentPointCount = m_subpaths[0]->count();
229
 
    if (currentPointCount > requiredPointCount) {
230
 
        for( int i = 0; i < currentPointCount-requiredPointCount; ++i ) {
231
 
            delete m_subpaths[0]->front();
232
 
            m_subpaths[0]->pop_front();
233
 
        }
234
 
    }
235
 
    else if (requiredPointCount > currentPointCount) {
236
 
        for( int i = 0; i < requiredPointCount-currentPointCount; ++i ) {
237
 
            m_subpaths[0]->append( new KoPathPoint( this, QPointF() ) );
238
 
        }
239
 
    }
240
 
}
241
 
 
242
 
void KoStarShape::setSize( const QSizeF &newSize )
243
 
{
244
 
    QMatrix matrix(resizeMatrix(newSize));
245
 
    m_zoomX *= matrix.m11();
246
 
    m_zoomY *= matrix.m22();
247
 
 
248
 
    // this transforms the handles
249
 
    KoParameterShape::setSize( newSize );
250
 
 
251
 
    m_center = computeCenter();
252
 
}
253
 
 
254
 
QPointF KoStarShape::computeCenter() const
255
 
{
256
 
    KoSubpath &points = *m_subpaths[0];
257
 
 
258
 
    QPointF center( 0, 0 );
259
 
    for( uint i = 0; i < m_cornerCount; ++i )
260
 
    {
261
 
        if( m_convex )
262
 
            center += points[i]->point();
263
 
        else
264
 
            center += points[2*i]->point();
265
 
    }
266
 
    return center / static_cast<qreal>( m_cornerCount );
267
 
}
268
 
 
269
 
bool KoStarShape::loadOdf( const KoXmlElement & element, KoShapeLoadingContext & context )
270
 
{
271
 
    bool loadAsCustomShape = false;
272
 
    
273
 
    if( element.localName() == "custom-shape" )
274
 
    {
275
 
        QString drawEngine = element.attributeNS( KoXmlNS::draw, "engine", "" );
276
 
        if( drawEngine != "koffice:star" )
277
 
            return false;
278
 
        loadAsCustomShape = true;
279
 
    }
280
 
    else if( element.localName() != "regular-polygon" )
281
 
    {
282
 
        return false;
283
 
    }
284
 
 
285
 
    QPointF loadedPosition = position();
286
 
 
287
 
    m_radius[tip] = 50;
288
 
    m_center = QPointF(50,50);
289
 
 
290
 
    if( ! loadAsCustomShape )
291
 
    {
292
 
        QString corners = element.attributeNS( KoXmlNS::draw, "corners", "" );
293
 
        if( ! corners.isEmpty() ) {
294
 
            m_cornerCount = corners.toUInt();
295
 
            // initialize default angles of tip and base
296
 
            m_angles[base] = m_angles[tip] = defaultAngleRadian();
297
 
        }
298
 
 
299
 
        m_convex = (element.attributeNS( KoXmlNS::draw, "concave", "false" ) == "false" );
300
 
 
301
 
        if( m_convex )
302
 
        {
303
 
            m_radius[base] = m_radius[tip];
304
 
        }
305
 
        else
306
 
        {
307
 
            // sharpness is radius of ellipse on which inner polygon points are located
308
 
            // 0% means all polygon points are on a single ellipse
309
 
            // 100% means inner points are located at polygon center point
310
 
            QString sharpness = element.attributeNS( KoXmlNS::draw, "sharpness", "" );
311
 
            if( ! sharpness.isEmpty() && sharpness.right( 1 ) == "%" )
312
 
            {
313
 
                float percent = sharpness.left( sharpness.length()-1 ).toFloat();
314
 
                m_radius[base] = m_radius[tip] * (100-percent)/100;
315
 
            }
316
 
        }
317
 
    }
318
 
    else
319
 
    {
320
 
        QString drawData = element.attributeNS( KoXmlNS::draw, "data" );
321
 
        if( drawData.isEmpty() )
322
 
            return false;
323
 
 
324
 
        QStringList properties = drawData.split( ';' );
325
 
        if( properties.count() == 0 )
326
 
            return false;
327
 
 
328
 
        foreach( const QString &property, properties )
329
 
        {
330
 
            QStringList pair = property.split( ':' );
331
 
            if( pair.count() != 2 )
332
 
                continue;
333
 
            if( pair[0] == "corners" )
334
 
            {
335
 
                m_cornerCount = pair[1].toInt();
336
 
            }
337
 
            else if( pair[0] == "concave" )
338
 
            {
339
 
                m_convex = (pair[1] == "false");
340
 
            }
341
 
            else if( pair[0] == "baseRoundness" )
342
 
            {
343
 
                m_roundness[base] = pair[1].toDouble();
344
 
            }
345
 
            else if( pair[0] == "tipRoundness" )
346
 
            {
347
 
                m_roundness[tip] = pair[1].toDouble();
348
 
            }
349
 
            else if( pair[0] == "baseAngle" )
350
 
            {
351
 
                m_angles[base] = pair[1].toDouble();
352
 
            }
353
 
            else if( pair[0] == "tipAngle" )
354
 
            {
355
 
                m_angles[tip] = pair[1].toDouble();
356
 
            }
357
 
            else if( pair[0] == "sharpness" )
358
 
            {
359
 
                float percent = pair[1].left( pair[1].length()-1 ).toFloat();
360
 
                m_radius[base] = m_radius[tip] * (100-percent)/100;
361
 
            }
362
 
        }
363
 
 
364
 
        if( m_convex )
365
 
        {
366
 
            m_radius[base] = m_radius[tip];
367
 
        }
368
 
    }
369
 
 
370
 
    updatePath( QSizeF() );
371
 
 
372
 
    // reset transformation
373
 
    setTransformation( QMatrix() );
374
 
 
375
 
    loadOdfAttributes( element, context, OdfAllAttributes );
376
 
 
377
 
    return true;
378
 
}
379
 
 
380
 
void KoStarShape::saveOdf( KoShapeSavingContext & context ) const
381
 
{
382
 
    if( isParametricShape() )
383
 
    {
384
 
        double defaultAngle = defaultAngleRadian();
385
 
        bool hasRoundness = m_roundness[tip] != 0.0f || m_roundness[base] != 0.0f;
386
 
        bool hasAngleOffset = m_angles[base] != defaultAngle || m_angles[tip] != defaultAngle;
387
 
        if( hasRoundness || hasAngleOffset )
388
 
        {
389
 
            // draw:regular-polygon has no means of saving roundness
390
 
            // so we save as a custom shape with a specific draw:engine
391
 
            context.xmlWriter().startElement("draw:custom-shape");
392
 
            saveOdfAttributes( context, OdfAllAttributes );
393
 
 
394
 
            // now write the special shape data
395
 
            context.xmlWriter().addAttribute( "draw:engine", "koffice:star" );
396
 
            // create the data attribute
397
 
            QString drawData = QString("corners:%1;").arg( m_cornerCount );
398
 
            drawData += m_convex ? "concave:false;" : "concave:true;";
399
 
            if( ! m_convex )
400
 
            {
401
 
                // sharpness is radius of ellipse on which inner polygon points are located
402
 
                // 0% means all polygon points are on a single ellipse
403
 
                // 100% means inner points are located at polygon center point
404
 
                qreal percent = (m_radius[tip]-m_radius[base]) / m_radius[tip] * 100.0;
405
 
                drawData += QString("sharpness:%1%;").arg( percent );
406
 
            }
407
 
            if( m_roundness[base] != 0.0f )
408
 
            {
409
 
                drawData += QString("baseRoundness:%1;").arg( m_roundness[base] );
410
 
            }
411
 
            if( m_roundness[tip] != 0.0f )
412
 
            {
413
 
                drawData += QString("tipRoundness:%1;").arg( m_roundness[tip] );
414
 
            }
415
 
            drawData += QString("baseAngle:%1;").arg( m_angles[base] );
416
 
            drawData += QString("tipAngle:%1;").arg( m_angles[tip] );
417
 
 
418
 
            context.xmlWriter().addAttribute( "draw:data", drawData );
419
 
 
420
 
            // write a enhanced geometry element for compatibility with other applications
421
 
            context.xmlWriter().startElement("draw:enhanced-geometry");
422
 
            context.xmlWriter().addAttribute("draw:enhanced-path", toString( transformation() ) );
423
 
            context.xmlWriter().endElement(); // draw:enhanced-geometry
424
 
            
425
 
            saveOdfCommonChildElements( context );
426
 
            context.xmlWriter().endElement(); // draw:custom-shape
427
 
        }
428
 
        else
429
 
        {
430
 
            context.xmlWriter().startElement("draw:regular-polygon");
431
 
            saveOdfAttributes( context, OdfAllAttributes );
432
 
            context.xmlWriter().addAttribute( "draw:corners", m_cornerCount );
433
 
            context.xmlWriter().addAttribute( "draw:concave", m_convex ? "false" : "true" );
434
 
            if( ! m_convex )
435
 
            {
436
 
                // sharpness is radius of ellipse on which inner polygon points are located
437
 
                // 0% means all polygon points are on a single ellipse
438
 
                // 100% means inner points are located at polygon center point
439
 
                qreal percent = (m_radius[tip]-m_radius[base]) / m_radius[tip] * 100.0;
440
 
                context.xmlWriter().addAttribute( "draw:sharpness", QString("%1%" ).arg( percent ) );
441
 
            }
442
 
            saveOdfCommonChildElements( context );
443
 
            context.xmlWriter().endElement();
444
 
        }
445
 
    }
446
 
    else
447
 
    {
448
 
        KoPathShape::saveOdf( context );
449
 
    }
450
 
}
451
 
 
452
 
QString KoStarShape::pathShapeId() const
453
 
{
454
 
    return KoStarShapeId;
455
 
}
456
 
 
457
 
double KoStarShape::defaultAngleRadian() const
458
 
{
459
 
    qreal radianStep = M_PI / static_cast<qreal>(m_cornerCount);
460
 
 
461
 
    return M_PI_2-2*radianStep;
462
 
}