Code

dropper modes: replace undecipherable icons with text labels
[inkscape.git] / src / sp-ellipse.cpp
1 #define __SP_ELLIPSE_C__
3 /*
4  * SVG <ellipse> and related implementations
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Mitsuru Oka
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 1999-2002 Lauris Kaplinski
12  * Copyright (C) 2000-2001 Ximian, Inc.
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
22 #include "libnr/n-art-bpath.h"
23 #include "libnr/nr-path.h"
24 #include "libnr/nr-matrix-fns.h"
25 #include "svg/svg.h"
26 #include "svg/path-string.h"
27 #include "xml/repr.h"
28 #include "attributes.h"
29 #include "style.h"
30 #include "display/curve.h"
31 #include <glibmm/i18n.h>
33 #include "document.h"
34 #include "sp-ellipse.h"
36 #include "prefs-utils.h"
38 /* Common parent class */
40 #define noELLIPSE_VERBOSE
42 #ifndef M_PI
43 #define M_PI 3.14159265358979323846
44 #endif
46 #define SP_2PI (2 * M_PI)
48 #if 1
49 /* Hmmm... shouldn't this also qualify */
50 /* Whether it is faster or not, well, nobody knows */
51 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
52 #else
53 /* we do not use C99 round(3) function yet */
54 static double sp_round(double x, double y)
55 {
56     double remain;
58     g_assert(y > 0.0);
60     /* return round(x/y) * y; */
62     remain = fmod(x, y);
64     if (remain >= 0.5*y)
65         return x - remain + y;
66     else
67         return x - remain;
68 }
69 #endif
71 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
72 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
74 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
76 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p);
78 static void sp_genericellipse_set_shape(SPShape *shape);
79 static void sp_genericellipse_update_patheffect (SPShape *shape, bool write);
81 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr,
82                                                     guint flags);
84 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
86 static SPShapeClass *ge_parent_class;
88 GType
89 sp_genericellipse_get_type(void)
90 {
91     static GType type = 0;
92     if (!type) {
93         GTypeInfo info = {
94             sizeof(SPGenericEllipseClass),
95             NULL,   /* base_init */
96             NULL,   /* base_finalize */
97             (GClassInitFunc) sp_genericellipse_class_init,
98             NULL,   /* class_finalize */
99             NULL,   /* class_data */
100             sizeof(SPGenericEllipse),
101             16,   /* n_preallocs */
102             (GInstanceInitFunc) sp_genericellipse_init,
103             NULL,   /* value_table */
104         };
105         type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
106     }
107     return type;
110 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
112     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
113     SPItemClass *item_class = (SPItemClass *) klass;
114     SPShapeClass *shape_class = (SPShapeClass *) klass;
116     ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
118     sp_object_class->update = sp_genericellipse_update;
119     sp_object_class->write = sp_genericellipse_write;
121     item_class->snappoints = sp_genericellipse_snappoints;
123     shape_class->set_shape = sp_genericellipse_set_shape;
124     shape_class->update_patheffect = sp_genericellipse_update_patheffect;
127 static void
128 sp_genericellipse_init(SPGenericEllipse *ellipse)
130     ellipse->cx.unset();
131     ellipse->cy.unset();
132     ellipse->rx.unset();
133     ellipse->ry.unset();
135     ellipse->start = 0.0;
136     ellipse->end = SP_2PI;
137     ellipse->closed = TRUE;
140 static void
141 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
143     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
144         SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
145         SPStyle const *style = object->style;
146         double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
147         double const em = style->font_size.computed;
148         double const ex = em * 0.5; // fixme: get from pango or libnrtype
149         ellipse->cx.update(em, ex, d);
150         ellipse->cy.update(em, ex, d);
151         ellipse->rx.update(em, ex, d);
152         ellipse->ry.update(em, ex, d);
153         sp_shape_set_shape((SPShape *) object);
154     }
156     if (((SPObjectClass *) ge_parent_class)->update)
157         ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
160 static void
161 sp_genericellipse_update_patheffect(SPShape *shape, bool write)
163     sp_genericellipse_set_shape(shape);
165     if (write) {
166         Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
167         if ( shape->curve != NULL ) {
168             NArtBpath *abp = sp_curve_first_bpath(shape->curve);
169             if (abp) {
170                 gchar *str = sp_svg_write_path(abp);
171                 repr->setAttribute("d", str);
172                 g_free(str);
173             } else {
174                 repr->setAttribute("d", "");
175             }
176         } else {
177             repr->setAttribute("d", NULL);
178         }
179     }
181     ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
185 #define C1 0.552
187 /* fixme: Think (Lauris) */
189 static void sp_genericellipse_set_shape(SPShape *shape)
191     double rx, ry, s, e;
192     double x0, y0, x1, y1, x2, y2, x3, y3;
193     double len;
194     gint slice = FALSE;
195     gint i;
197     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
199     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
200     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
202     sp_genericellipse_normalize(ellipse);
204     rx = ellipse->rx.computed;
205     ry = ellipse->ry.computed;
207     // figure out if we have a slice, guarding against rounding errors
208     len = fmod(ellipse->end - ellipse->start, SP_2PI);
209     if (len < 0.0) len += SP_2PI;
210     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
211         slice = FALSE;
212         ellipse->end = ellipse->start + SP_2PI;
213     } else {
214         slice = TRUE;
215     }
217     NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
218     aff[4] = ellipse->cx.computed;
219     aff[5] = ellipse->cy.computed;
221     NArtBpath bpath[16];
222     i = 0;
223     if (ellipse->closed) {
224         bpath[i].code = NR_MOVETO;
225     } else {
226         bpath[i].code = NR_MOVETO_OPEN;
227     }
228     bpath[i].x3 = cos(ellipse->start);
229     bpath[i].y3 = sin(ellipse->start);
230     i++;
232     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
233         e = s + M_PI_2;
234         if (e > ellipse->end)
235             e = ellipse->end;
236         len = C1 * (e - s) / M_PI_2;
237         x0 = cos(s);
238         y0 = sin(s);
239         x1 = x0 + len * cos(s + M_PI_2);
240         y1 = y0 + len * sin(s + M_PI_2);
241         x3 = cos(e);
242         y3 = sin(e);
243         x2 = x3 + len * cos(e - M_PI_2);
244         y2 = y3 + len * sin(e - M_PI_2);
245 #ifdef ELLIPSE_VERBOSE
246         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
247                 i, s, e, x1, y1, x2, y2, x3, y3);
248 #endif
249         bpath[i].code = NR_CURVETO;
250         bpath[i].x1 = x1;
251         bpath[i].y1 = y1;
252         bpath[i].x2 = x2;
253         bpath[i].y2 = y2;
254         bpath[i].x3 = x3;
255         bpath[i].y3 = y3;
256         i++;
257     }
259     if (slice && ellipse->closed) {
260         bpath[i].code = NR_LINETO;
261         bpath[i].x3 = 0.0;
262         bpath[i].y3 = 0.0;
263         i++;
264         bpath[i].code = NR_LINETO;
265         bpath[i].x3 = bpath[0].x3;
266         bpath[i].y3 = bpath[0].y3;
267         i++;
268     } else if (ellipse->closed) {
269         bpath[i-1].x3 = bpath[0].x3;
270         bpath[i-1].y3 = bpath[0].y3;
271     }
273     bpath[i].code = NR_END;
274     SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff));
275     g_assert(c != NULL);
277     sp_shape_perform_path_effect(c, SP_SHAPE (ellipse));
278     sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
279     sp_curve_unref(c);
282 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
284     g_assert(item != NULL);
285     g_assert(SP_IS_GENERICELLIPSE(item));
286     
287     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
288     sp_genericellipse_normalize(ellipse);
289     NR::Matrix const i2d = sp_item_i2d_affine(item);
291     // figure out if we have a slice, whilst guarding against rounding errors
292     bool slice = false;
293     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
294     if (len < 0.0) len += SP_2PI;
295     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
296         slice = false;
297         ellipse->end = ellipse->start + SP_2PI;
298     } else {
299         slice = true;
300     }
302     double rx = ellipse->rx.computed;
303     double ry = ellipse->ry.computed;    
304     double cx = ellipse->cx.computed;
305     double cy = ellipse->cy.computed;
306     
307     // Snap to the 4 quadrant points of the ellipse, but only if the arc
308     // spans far enough to include them
309     double angle = 0;
310     for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
311         if (angle >= ellipse->start && angle <= ellipse->end) {
312             *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
313         }
314     }
315     
316     // And if we have a slice, also snap to the endpoints and the centre point 
317     if (slice) {
318         // Add the centre, if we have a closed slice
319         if (ellipse->closed) {
320             *p = NR::Point(cx, cy) * i2d;
321         }
322         // Add the start point, if it's not coincident with a quadrant point
323         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {    
324             *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
325         } 
326         // Add the end point, if it's not coincident with a quadrant point
327         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {    
328             *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
329         }
330     }
333 void
334 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
336     ellipse->start = fmod(ellipse->start, SP_2PI);
337     ellipse->end = fmod(ellipse->end, SP_2PI);
339     if (ellipse->start < 0.0)
340         ellipse->start += SP_2PI;
341     double diff = ellipse->start - ellipse->end;
342     if (diff >= 0.0)
343         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
345     /* Now we keep: 0 <= start < end <= 2*PI */
348 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
350     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
352     if (flags & SP_OBJECT_WRITE_EXT) {
353         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
354             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
355             repr = xml_doc->createElement("svg:path");
356         }
358         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
359         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
360         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
361         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
363         if (SP_IS_ARC(ellipse))
364             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
365     }
367     if (((SPObjectClass *) ge_parent_class)->write)
368         ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
370     return repr;
373 /* SVG <ellipse> element */
375 static void sp_ellipse_class_init(SPEllipseClass *klass);
376 static void sp_ellipse_init(SPEllipse *ellipse);
378 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
379 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
380 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
381 static gchar *sp_ellipse_description(SPItem *item);
383 static SPGenericEllipseClass *ellipse_parent_class;
385 GType
386 sp_ellipse_get_type(void)
388     static GType type = 0;
389     if (!type) {
390         GTypeInfo info = {
391             sizeof(SPEllipseClass),
392             NULL,   /* base_init */
393             NULL,   /* base_finalize */
394             (GClassInitFunc) sp_ellipse_class_init,
395             NULL,   /* class_finalize */
396             NULL,   /* class_data */
397             sizeof(SPEllipse),
398             16,   /* n_preallocs */
399             (GInstanceInitFunc) sp_ellipse_init,
400             NULL,   /* value_table */
401         };
402         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
403     }
404     return type;
407 static void sp_ellipse_class_init(SPEllipseClass *klass)
409     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
410     SPItemClass *item_class = (SPItemClass *) klass;
412     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
414     sp_object_class->build = sp_ellipse_build;
415     sp_object_class->write = sp_ellipse_write;
416     sp_object_class->set = sp_ellipse_set;
418     item_class->description = sp_ellipse_description;
421 static void
422 sp_ellipse_init(SPEllipse */*ellipse*/)
424     /* Nothing special */
427 static void
428 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
430     if (((SPObjectClass *) ellipse_parent_class)->build)
431         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
433     sp_object_read_attr(object, "cx");
434     sp_object_read_attr(object, "cy");
435     sp_object_read_attr(object, "rx");
436     sp_object_read_attr(object, "ry");
439 static Inkscape::XML::Node *
440 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
442     SPGenericEllipse *ellipse;
444     ellipse = SP_GENERICELLIPSE(object);
446     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
447         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
448         repr = xml_doc->createElement("svg:ellipse");
449     }
451     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
452     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
453     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
454     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
456     if (((SPObjectClass *) ellipse_parent_class)->write)
457         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
459     return repr;
462 static void
463 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
465     SPGenericEllipse *ellipse;
467     ellipse = SP_GENERICELLIPSE(object);
469     switch (key) {
470         case SP_ATTR_CX:
471             ellipse->cx.readOrUnset(value);
472             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
473             break;
474         case SP_ATTR_CY:
475             ellipse->cy.readOrUnset(value);
476             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
477             break;
478         case SP_ATTR_RX:
479             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
480                 ellipse->rx.unset();
481             }
482             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
483             break;
484         case SP_ATTR_RY:
485             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
486                 ellipse->ry.unset();
487             }
488             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
489             break;
490         default:
491             if (((SPObjectClass *) ellipse_parent_class)->set)
492                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
493             break;
494     }
497 static gchar *sp_ellipse_description(SPItem */*item*/)
499     return g_strdup(_("<b>Ellipse</b>"));
503 void
504 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
506     SPGenericEllipse *ge;
508     g_return_if_fail(ellipse != NULL);
509     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
511     ge = SP_GENERICELLIPSE(ellipse);
513     ge->cx.computed = x;
514     ge->cy.computed = y;
515     ge->rx.computed = rx;
516     ge->ry.computed = ry;
518     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
521 /* SVG <circle> element */
523 static void sp_circle_class_init(SPCircleClass *klass);
524 static void sp_circle_init(SPCircle *circle);
526 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
527 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
528 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
529 static gchar *sp_circle_description(SPItem *item);
531 static SPGenericEllipseClass *circle_parent_class;
533 GType
534 sp_circle_get_type(void)
536     static GType type = 0;
537     if (!type) {
538         GTypeInfo info = {
539             sizeof(SPCircleClass),
540             NULL,   /* base_init */
541             NULL,   /* base_finalize */
542             (GClassInitFunc) sp_circle_class_init,
543             NULL,   /* class_finalize */
544             NULL,   /* class_data */
545             sizeof(SPCircle),
546             16,   /* n_preallocs */
547             (GInstanceInitFunc) sp_circle_init,
548             NULL,   /* value_table */
549         };
550         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
551     }
552     return type;
555 static void
556 sp_circle_class_init(SPCircleClass *klass)
558     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
559     SPItemClass *item_class = (SPItemClass *) klass;
561     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
563     sp_object_class->build = sp_circle_build;
564     sp_object_class->write = sp_circle_write;
565     sp_object_class->set = sp_circle_set;
567     item_class->description = sp_circle_description;
570 static void
571 sp_circle_init(SPCircle */*circle*/)
573     /* Nothing special */
576 static void
577 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
579     if (((SPObjectClass *) circle_parent_class)->build)
580         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
582     sp_object_read_attr(object, "cx");
583     sp_object_read_attr(object, "cy");
584     sp_object_read_attr(object, "r");
587 static Inkscape::XML::Node *
588 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
590     SPGenericEllipse *ellipse;
592     ellipse = SP_GENERICELLIPSE(object);
594     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
595         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
596         repr = xml_doc->createElement("svg:circle");
597     }
599     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
600     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
601     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
603     if (((SPObjectClass *) circle_parent_class)->write)
604         ((SPObjectClass *) circle_parent_class)->write(object, repr, flags);
606     return repr;
609 static void
610 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
612     SPGenericEllipse *ge;
614     ge = SP_GENERICELLIPSE(object);
616     switch (key) {
617         case SP_ATTR_CX:
618             ge->cx.readOrUnset(value);
619             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
620             break;
621         case SP_ATTR_CY:
622             ge->cy.readOrUnset(value);
623             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
624             break;
625         case SP_ATTR_R:
626             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
627                 ge->rx.unset();
628             }
629             ge->ry = ge->rx;
630             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
631             break;
632         default:
633             if (((SPObjectClass *) circle_parent_class)->set)
634                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
635             break;
636     }
639 static gchar *sp_circle_description(SPItem */*item*/)
641     return g_strdup(_("<b>Circle</b>"));
644 /* <path sodipodi:type="arc"> element */
646 static void sp_arc_class_init(SPArcClass *klass);
647 static void sp_arc_init(SPArc *arc);
649 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
650 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
651 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
652 static void sp_arc_modified(SPObject *object, guint flags);
654 static gchar *sp_arc_description(SPItem *item);
656 static SPGenericEllipseClass *arc_parent_class;
658 GType
659 sp_arc_get_type(void)
661     static GType type = 0;
662     if (!type) {
663         GTypeInfo info = {
664             sizeof(SPArcClass),
665             NULL,   /* base_init */
666             NULL,   /* base_finalize */
667             (GClassInitFunc) sp_arc_class_init,
668             NULL,   /* class_finalize */
669             NULL,   /* class_data */
670             sizeof(SPArc),
671             16,   /* n_preallocs */
672             (GInstanceInitFunc) sp_arc_init,
673             NULL,   /* value_table */
674         };
675         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
676     }
677     return type;
680 static void
681 sp_arc_class_init(SPArcClass *klass)
683     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
684     SPItemClass *item_class = (SPItemClass *) klass;
686     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
688     sp_object_class->build = sp_arc_build;
689     sp_object_class->write = sp_arc_write;
690     sp_object_class->set = sp_arc_set;
691     sp_object_class->modified = sp_arc_modified;
693     item_class->description = sp_arc_description;
696 static void
697 sp_arc_init(SPArc */*arc*/)
699     /* Nothing special */
702 static void
703 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
705     if (((SPObjectClass *) arc_parent_class)->build)
706         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
708     Inkscape::Version version = sp_object_get_sodipodi_version(object);
710     sp_object_read_attr(object, "sodipodi:cx");
711     sp_object_read_attr(object, "sodipodi:cy");
712     sp_object_read_attr(object, "sodipodi:rx");
713     sp_object_read_attr(object, "sodipodi:ry");
715     sp_object_read_attr(object, "sodipodi:start");
716     sp_object_read_attr(object, "sodipodi:end");
717     sp_object_read_attr(object, "sodipodi:open");
720 /*
721  * sp_arc_set_elliptical_path_attribute:
722  *
723  * Convert center to endpoint parameterization and set it to repr.
724  *
725  * See SVG 1.0 Specification W3C Recommendation
726  * ``F.6 Ellptical arc implementation notes'' for more detail.
727  */
728 static gboolean
729 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
731     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
733     Inkscape::SVG::PathString str;
735     NR::Point p1 = sp_arc_get_xy(arc, ge->start);
736     NR::Point p2 = sp_arc_get_xy(arc, ge->end);
737     double rx = ge->rx.computed;
738     double ry = ge->ry.computed;
740     str.moveTo(p1);
742     double dt = fmod(ge->end - ge->start, SP_2PI);
743     if (fabs(dt) < 1e-6) {
744         NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
745         str.arcTo(rx, ry, 0, true, true, ph)
746            .arcTo(rx, ry, 0, true, true, p2)
747            .closePath();
748     } else {
749         bool fa = (fabs(dt) > M_PI);
750         bool fs = (dt > 0);
751         str.arcTo(rx, ry, 0, fa, fs, p2);
752         if (ge->closed) {
753             NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
754             str.lineTo(center).closePath();
755         }
756     }
758     repr->setAttribute("d", str.c_str());
759     return true;
762 static Inkscape::XML::Node *
763 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
765     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
766     SPArc *arc = SP_ARC(object);
768     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
769         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
770         repr = xml_doc->createElement("svg:path");
771     }
773     if (flags & SP_OBJECT_WRITE_EXT) {
774         repr->setAttribute("sodipodi:type", "arc");
775         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
776         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
777         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
778         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
780         // write start and end only if they are non-trivial; otherwise remove
781         gdouble len = fmod(ge->end - ge->start, SP_2PI);
782         if (len < 0.0) len += SP_2PI;
783         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
784             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
785             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
786             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
787         } else {
788             repr->setAttribute("sodipodi:end", NULL);
789             repr->setAttribute("sodipodi:start", NULL);
790             repr->setAttribute("sodipodi:open", NULL);
791         }
792     }
794     // write d=
795     sp_arc_set_elliptical_path_attribute(arc, repr);
797     if (((SPObjectClass *) arc_parent_class)->write)
798         ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
800     return repr;
803 static void
804 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
806     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
808     switch (key) {
809         case SP_ATTR_SODIPODI_CX:
810             ge->cx.readOrUnset(value);
811             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
812             break;
813         case SP_ATTR_SODIPODI_CY:
814             ge->cy.readOrUnset(value);
815             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
816             break;
817         case SP_ATTR_SODIPODI_RX:
818             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
819                 ge->rx.unset();
820             }
821             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
822             break;
823         case SP_ATTR_SODIPODI_RY:
824             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
825                 ge->ry.unset();
826             }
827             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
828             break;
829         case SP_ATTR_SODIPODI_START:
830             if (value) {
831                 sp_svg_number_read_d(value, &ge->start);
832             } else {
833                 ge->start = 0;
834             }
835             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
836             break;
837         case SP_ATTR_SODIPODI_END:
838             if (value) {
839                 sp_svg_number_read_d(value, &ge->end);
840             } else {
841                 ge->end = 2 * M_PI;
842             }
843             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
844             break;
845         case SP_ATTR_SODIPODI_OPEN:
846             ge->closed = (!value);
847             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
848             break;
849         default:
850             if (((SPObjectClass *) arc_parent_class)->set)
851                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
852             break;
853     }
856 static void
857 sp_arc_modified(SPObject *object, guint flags)
859     if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
860         sp_shape_set_shape((SPShape *) object);
861     }
863     if (((SPObjectClass *) arc_parent_class)->modified)
864         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
867 static gchar *sp_arc_description(SPItem *item)
869     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
871     gdouble len = fmod(ge->end - ge->start, SP_2PI);
872     if (len < 0.0) len += SP_2PI;
873     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
874         if (ge->closed) {
875             return g_strdup(_("<b>Segment</b>"));
876         } else {
877             return g_strdup(_("<b>Arc</b>"));
878         }
879     } else {
880         return g_strdup(_("<b>Ellipse</b>"));
881     }
884 void
885 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
887     g_return_if_fail(arc != NULL);
888     g_return_if_fail(SP_IS_ARC(arc));
890     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
892     ge->cx.computed = x;
893     ge->cy.computed = y;
894     ge->rx.computed = rx;
895     ge->ry.computed = ry;
896     if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
897         ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
898     if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
899         ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
900     if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
901         ge->closed = 1;
902     else
903         ge->closed = 0;
905     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
908 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
910     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
912     return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
913                      ge->ry.computed * sin(arg) + ge->cy.computed);
917 /*
918   Local Variables:
919   mode:c++
920   c-file-style:"stroustrup"
921   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
922   indent-tabs-mode:nil
923   fill-column:99
924   End:
925 */
926 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :