Code

Mark suspicious ignoring of parameters
[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 (SPLPEItem *lpeitem, bool write);
81 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, 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     SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass;
115     SPShapeClass *shape_class = (SPShapeClass *) klass;
117     ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
119     sp_object_class->update = sp_genericellipse_update;
120     sp_object_class->write = sp_genericellipse_write;
122     item_class->snappoints = sp_genericellipse_snappoints;
124     shape_class->set_shape = sp_genericellipse_set_shape;
125     lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect;
128 static void
129 sp_genericellipse_init(SPGenericEllipse *ellipse)
131     ellipse->cx.unset();
132     ellipse->cy.unset();
133     ellipse->rx.unset();
134     ellipse->ry.unset();
136     ellipse->start = 0.0;
137     ellipse->end = SP_2PI;
138     ellipse->closed = TRUE;
141 static void
142 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
144     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
145         SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
146         SPStyle const *style = object->style;
147         double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
148         double const em = style->font_size.computed;
149         double const ex = em * 0.5; // fixme: get from pango or libnrtype
150         ellipse->cx.update(em, ex, d);
151         ellipse->cy.update(em, ex, d);
152         ellipse->rx.update(em, ex, d);
153         ellipse->ry.update(em, ex, d);
154         sp_shape_set_shape((SPShape *) object);
155     }
157     if (((SPObjectClass *) ge_parent_class)->update)
158         ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
161 static void
162 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
164     SPShape *shape = (SPShape *) lpeitem;
165     sp_genericellipse_set_shape(shape);
167     if (write) {
168         Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
169         if ( shape->curve != NULL ) {
170             gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
171             repr->setAttribute("d", str);
172             g_free(str);
173         } else {
174             repr->setAttribute("d", NULL);
175         }
176     }
178     ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
182 #define C1 0.552
184 /* fixme: Think (Lauris) */
186 static void sp_genericellipse_set_shape(SPShape *shape)
188     double rx, ry, s, e;
189     double x0, y0, x1, y1, x2, y2, x3, y3;
190     double len;
191     gint slice = FALSE;
192     gint i;
194     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
196     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
197     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
199     sp_genericellipse_normalize(ellipse);
201     rx = ellipse->rx.computed;
202     ry = ellipse->ry.computed;
204     // figure out if we have a slice, guarding against rounding errors
205     len = fmod(ellipse->end - ellipse->start, SP_2PI);
206     if (len < 0.0) len += SP_2PI;
207     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
208         slice = FALSE;
209         ellipse->end = ellipse->start + SP_2PI;
210     } else {
211         slice = TRUE;
212     }
214     NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
215     aff[4] = ellipse->cx.computed;
216     aff[5] = ellipse->cy.computed;
218     NArtBpath bpath[16];
219     i = 0;
220     if (ellipse->closed) {
221         bpath[i].code = NR_MOVETO;
222     } else {
223         bpath[i].code = NR_MOVETO_OPEN;
224     }
225     bpath[i].x3 = cos(ellipse->start);
226     bpath[i].y3 = sin(ellipse->start);
227     i++;
229     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
230         e = s + M_PI_2;
231         if (e > ellipse->end)
232             e = ellipse->end;
233         len = C1 * (e - s) / M_PI_2;
234         x0 = cos(s);
235         y0 = sin(s);
236         x1 = x0 + len * cos(s + M_PI_2);
237         y1 = y0 + len * sin(s + M_PI_2);
238         x3 = cos(e);
239         y3 = sin(e);
240         x2 = x3 + len * cos(e - M_PI_2);
241         y2 = y3 + len * sin(e - M_PI_2);
242 #ifdef ELLIPSE_VERBOSE
243         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
244                 i, s, e, x1, y1, x2, y2, x3, y3);
245 #endif
246         bpath[i].code = NR_CURVETO;
247         bpath[i].x1 = x1;
248         bpath[i].y1 = y1;
249         bpath[i].x2 = x2;
250         bpath[i].y2 = y2;
251         bpath[i].x3 = x3;
252         bpath[i].y3 = y3;
253         i++;
254     }
256     if (slice && ellipse->closed) {
257         bpath[i].code = NR_LINETO;
258         bpath[i].x3 = 0.0;
259         bpath[i].y3 = 0.0;
260         i++;
261         bpath[i].code = NR_LINETO;
262         bpath[i].x3 = bpath[0].x3;
263         bpath[i].y3 = bpath[0].y3;
264         i++;
265     } else if (ellipse->closed) {
266         bpath[i-1].x3 = bpath[0].x3;
267         bpath[i-1].y3 = bpath[0].y3;
268     }
270     bpath[i].code = NR_END;
271     SPCurve *c = SPCurve::new_from_bpath(nr_artpath_affine(bpath, aff));
272     g_assert(c != NULL);
274     sp_lpe_item_perform_path_effect(SP_LPE_ITEM (ellipse), c);
275     sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
276     c->unref();
279 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
281     g_assert(item != NULL);
282     g_assert(SP_IS_GENERICELLIPSE(item));
283     
284     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
285     sp_genericellipse_normalize(ellipse);
286     NR::Matrix const i2d = from_2geom(sp_item_i2d_affine(item));
288     // figure out if we have a slice, whilst guarding against rounding errors
289     bool slice = false;
290     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
291     if (len < 0.0) len += SP_2PI;
292     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
293         slice = false;
294         ellipse->end = ellipse->start + SP_2PI;
295     } else {
296         slice = true;
297     }
299     double rx = ellipse->rx.computed;
300     double ry = ellipse->ry.computed;    
301     double cx = ellipse->cx.computed;
302     double cy = ellipse->cy.computed;
303     
304     // Snap to the 4 quadrant points of the ellipse, but only if the arc
305     // spans far enough to include them
306     double angle = 0;
307     for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
308         if (angle >= ellipse->start && angle <= ellipse->end) {
309             *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
310         }
311     }
312     
313     // And if we have a slice, also snap to the endpoints and the centre point 
314     if (slice) {
315         // Add the centre, if we have a closed slice
316         if (ellipse->closed) {
317             *p = NR::Point(cx, cy) * i2d;
318         }
319         // Add the start point, if it's not coincident with a quadrant point
320         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {    
321             *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
322         } 
323         // Add the end point, if it's not coincident with a quadrant point
324         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {    
325             *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
326         }
327     }
330 void
331 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
333     ellipse->start = fmod(ellipse->start, SP_2PI);
334     ellipse->end = fmod(ellipse->end, SP_2PI);
336     if (ellipse->start < 0.0)
337         ellipse->start += SP_2PI;
338     double diff = ellipse->start - ellipse->end;
339     if (diff >= 0.0)
340         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
342     /* Now we keep: 0 <= start < end <= 2*PI */
345 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
347     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
349     if (flags & SP_OBJECT_WRITE_EXT) {
350         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
351             repr = xml_doc->createElement("svg:path");
352         }
354         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
355         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
356         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
357         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
359         if (SP_IS_ARC(ellipse))
360             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
361     }
363     if (((SPObjectClass *) ge_parent_class)->write)
364         ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
366     return repr;
369 /* SVG <ellipse> element */
371 static void sp_ellipse_class_init(SPEllipseClass *klass);
372 static void sp_ellipse_init(SPEllipse *ellipse);
374 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
375 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
376 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
377 static gchar *sp_ellipse_description(SPItem *item);
379 static SPGenericEllipseClass *ellipse_parent_class;
381 GType
382 sp_ellipse_get_type(void)
384     static GType type = 0;
385     if (!type) {
386         GTypeInfo info = {
387             sizeof(SPEllipseClass),
388             NULL,   /* base_init */
389             NULL,   /* base_finalize */
390             (GClassInitFunc) sp_ellipse_class_init,
391             NULL,   /* class_finalize */
392             NULL,   /* class_data */
393             sizeof(SPEllipse),
394             16,   /* n_preallocs */
395             (GInstanceInitFunc) sp_ellipse_init,
396             NULL,   /* value_table */
397         };
398         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
399     }
400     return type;
403 static void sp_ellipse_class_init(SPEllipseClass *klass)
405     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
406     SPItemClass *item_class = (SPItemClass *) klass;
408     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
410     sp_object_class->build = sp_ellipse_build;
411     sp_object_class->write = sp_ellipse_write;
412     sp_object_class->set = sp_ellipse_set;
414     item_class->description = sp_ellipse_description;
417 static void
418 sp_ellipse_init(SPEllipse */*ellipse*/)
420     /* Nothing special */
423 static void
424 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
426     if (((SPObjectClass *) ellipse_parent_class)->build)
427         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
429     sp_object_read_attr(object, "cx");
430     sp_object_read_attr(object, "cy");
431     sp_object_read_attr(object, "rx");
432     sp_object_read_attr(object, "ry");
435 static Inkscape::XML::Node *
436 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
438     SPGenericEllipse *ellipse;
440     ellipse = SP_GENERICELLIPSE(object);
442     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
443         repr = xml_doc->createElement("svg:ellipse");
444     }
446     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
447     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
448     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
449     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
451     if (((SPObjectClass *) ellipse_parent_class)->write)
452         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
454     return repr;
457 static void
458 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
460     SPGenericEllipse *ellipse;
462     ellipse = SP_GENERICELLIPSE(object);
464     switch (key) {
465         case SP_ATTR_CX:
466             ellipse->cx.readOrUnset(value);
467             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
468             break;
469         case SP_ATTR_CY:
470             ellipse->cy.readOrUnset(value);
471             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
472             break;
473         case SP_ATTR_RX:
474             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
475                 ellipse->rx.unset();
476             }
477             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
478             break;
479         case SP_ATTR_RY:
480             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
481                 ellipse->ry.unset();
482             }
483             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
484             break;
485         default:
486             if (((SPObjectClass *) ellipse_parent_class)->set)
487                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
488             break;
489     }
492 static gchar *sp_ellipse_description(SPItem */*item*/)
494     return g_strdup(_("<b>Ellipse</b>"));
498 void
499 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
501     SPGenericEllipse *ge;
503     g_return_if_fail(ellipse != NULL);
504     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
506     ge = SP_GENERICELLIPSE(ellipse);
508     ge->cx.computed = x;
509     ge->cy.computed = y;
510     ge->rx.computed = rx;
511     ge->ry.computed = ry;
513     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
516 /* SVG <circle> element */
518 static void sp_circle_class_init(SPCircleClass *klass);
519 static void sp_circle_init(SPCircle *circle);
521 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
522 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
523 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
524 static gchar *sp_circle_description(SPItem *item);
526 static SPGenericEllipseClass *circle_parent_class;
528 GType
529 sp_circle_get_type(void)
531     static GType type = 0;
532     if (!type) {
533         GTypeInfo info = {
534             sizeof(SPCircleClass),
535             NULL,   /* base_init */
536             NULL,   /* base_finalize */
537             (GClassInitFunc) sp_circle_class_init,
538             NULL,   /* class_finalize */
539             NULL,   /* class_data */
540             sizeof(SPCircle),
541             16,   /* n_preallocs */
542             (GInstanceInitFunc) sp_circle_init,
543             NULL,   /* value_table */
544         };
545         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
546     }
547     return type;
550 static void
551 sp_circle_class_init(SPCircleClass *klass)
553     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
554     SPItemClass *item_class = (SPItemClass *) klass;
556     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
558     sp_object_class->build = sp_circle_build;
559     sp_object_class->write = sp_circle_write;
560     sp_object_class->set = sp_circle_set;
562     item_class->description = sp_circle_description;
565 static void
566 sp_circle_init(SPCircle */*circle*/)
568     /* Nothing special */
571 static void
572 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
574     if (((SPObjectClass *) circle_parent_class)->build)
575         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
577     sp_object_read_attr(object, "cx");
578     sp_object_read_attr(object, "cy");
579     sp_object_read_attr(object, "r");
582 static Inkscape::XML::Node *
583 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
585     SPGenericEllipse *ellipse;
587     ellipse = SP_GENERICELLIPSE(object);
589     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
590         repr = xml_doc->createElement("svg:circle");
591     }
593     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
594     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
595     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
597     if (((SPObjectClass *) circle_parent_class)->write)
598         ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
600     return repr;
603 static void
604 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
606     SPGenericEllipse *ge;
608     ge = SP_GENERICELLIPSE(object);
610     switch (key) {
611         case SP_ATTR_CX:
612             ge->cx.readOrUnset(value);
613             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
614             break;
615         case SP_ATTR_CY:
616             ge->cy.readOrUnset(value);
617             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
618             break;
619         case SP_ATTR_R:
620             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
621                 ge->rx.unset();
622             }
623             ge->ry = ge->rx;
624             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
625             break;
626         default:
627             if (((SPObjectClass *) circle_parent_class)->set)
628                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
629             break;
630     }
633 static gchar *sp_circle_description(SPItem */*item*/)
635     return g_strdup(_("<b>Circle</b>"));
638 /* <path sodipodi:type="arc"> element */
640 static void sp_arc_class_init(SPArcClass *klass);
641 static void sp_arc_init(SPArc *arc);
643 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
644 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
645 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
646 static void sp_arc_modified(SPObject *object, guint flags);
648 static gchar *sp_arc_description(SPItem *item);
650 static SPGenericEllipseClass *arc_parent_class;
652 GType
653 sp_arc_get_type(void)
655     static GType type = 0;
656     if (!type) {
657         GTypeInfo info = {
658             sizeof(SPArcClass),
659             NULL,   /* base_init */
660             NULL,   /* base_finalize */
661             (GClassInitFunc) sp_arc_class_init,
662             NULL,   /* class_finalize */
663             NULL,   /* class_data */
664             sizeof(SPArc),
665             16,   /* n_preallocs */
666             (GInstanceInitFunc) sp_arc_init,
667             NULL,   /* value_table */
668         };
669         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
670     }
671     return type;
674 static void
675 sp_arc_class_init(SPArcClass *klass)
677     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
678     SPItemClass *item_class = (SPItemClass *) klass;
680     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
682     sp_object_class->build = sp_arc_build;
683     sp_object_class->write = sp_arc_write;
684     sp_object_class->set = sp_arc_set;
685     sp_object_class->modified = sp_arc_modified;
687     item_class->description = sp_arc_description;
690 static void
691 sp_arc_init(SPArc */*arc*/)
693     /* Nothing special */
696 static void
697 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
699     if (((SPObjectClass *) arc_parent_class)->build)
700         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
702     Inkscape::Version version = sp_object_get_sodipodi_version(object);
704     sp_object_read_attr(object, "sodipodi:cx");
705     sp_object_read_attr(object, "sodipodi:cy");
706     sp_object_read_attr(object, "sodipodi:rx");
707     sp_object_read_attr(object, "sodipodi:ry");
709     sp_object_read_attr(object, "sodipodi:start");
710     sp_object_read_attr(object, "sodipodi:end");
711     sp_object_read_attr(object, "sodipodi:open");
714 /*
715  * sp_arc_set_elliptical_path_attribute:
716  *
717  * Convert center to endpoint parameterization and set it to repr.
718  *
719  * See SVG 1.0 Specification W3C Recommendation
720  * ``F.6 Ellptical arc implementation notes'' for more detail.
721  */
722 static gboolean
723 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
725     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
727     Inkscape::SVG::PathString str;
729     NR::Point p1 = sp_arc_get_xy(arc, ge->start);
730     NR::Point p2 = sp_arc_get_xy(arc, ge->end);
731     double rx = ge->rx.computed;
732     double ry = ge->ry.computed;
734     str.moveTo(p1);
736     double dt = fmod(ge->end - ge->start, SP_2PI);
737     if (fabs(dt) < 1e-6) {
738         NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
739         str.arcTo(rx, ry, 0, true, true, ph)
740            .arcTo(rx, ry, 0, true, true, p2)
741            .closePath();
742     } else {
743         bool fa = (fabs(dt) > M_PI);
744         bool fs = (dt > 0);
745         str.arcTo(rx, ry, 0, fa, fs, p2);
746         if (ge->closed) {
747             NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
748             str.lineTo(center).closePath();
749         }
750     }
752     repr->setAttribute("d", str.c_str());
753     return true;
756 static Inkscape::XML::Node *
757 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
759     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
760     SPArc *arc = SP_ARC(object);
762     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
763         repr = xml_doc->createElement("svg:path");
764     }
766     if (flags & SP_OBJECT_WRITE_EXT) {
767         repr->setAttribute("sodipodi:type", "arc");
768         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
769         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
770         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
771         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
773         // write start and end only if they are non-trivial; otherwise remove
774         gdouble len = fmod(ge->end - ge->start, SP_2PI);
775         if (len < 0.0) len += SP_2PI;
776         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
777             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
778             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
779             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
780         } else {
781             repr->setAttribute("sodipodi:end", NULL);
782             repr->setAttribute("sodipodi:start", NULL);
783             repr->setAttribute("sodipodi:open", NULL);
784         }
785     }
787     // write d=
788     sp_arc_set_elliptical_path_attribute(arc, repr);
790     if (((SPObjectClass *) arc_parent_class)->write)
791         ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
793     return repr;
796 static void
797 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
799     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
801     switch (key) {
802         case SP_ATTR_SODIPODI_CX:
803             ge->cx.readOrUnset(value);
804             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
805             break;
806         case SP_ATTR_SODIPODI_CY:
807             ge->cy.readOrUnset(value);
808             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
809             break;
810         case SP_ATTR_SODIPODI_RX:
811             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
812                 ge->rx.unset();
813             }
814             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
815             break;
816         case SP_ATTR_SODIPODI_RY:
817             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
818                 ge->ry.unset();
819             }
820             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
821             break;
822         case SP_ATTR_SODIPODI_START:
823             if (value) {
824                 sp_svg_number_read_d(value, &ge->start);
825             } else {
826                 ge->start = 0;
827             }
828             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
829             break;
830         case SP_ATTR_SODIPODI_END:
831             if (value) {
832                 sp_svg_number_read_d(value, &ge->end);
833             } else {
834                 ge->end = 2 * M_PI;
835             }
836             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
837             break;
838         case SP_ATTR_SODIPODI_OPEN:
839             ge->closed = (!value);
840             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
841             break;
842         default:
843             if (((SPObjectClass *) arc_parent_class)->set)
844                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
845             break;
846     }
849 static void
850 sp_arc_modified(SPObject *object, guint flags)
852     if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
853         sp_shape_set_shape((SPShape *) object);
854     }
856     if (((SPObjectClass *) arc_parent_class)->modified)
857         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
860 static gchar *sp_arc_description(SPItem *item)
862     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
864     gdouble len = fmod(ge->end - ge->start, SP_2PI);
865     if (len < 0.0) len += SP_2PI;
866     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
867         if (ge->closed) {
868             return g_strdup(_("<b>Segment</b>"));
869         } else {
870             return g_strdup(_("<b>Arc</b>"));
871         }
872     } else {
873         return g_strdup(_("<b>Ellipse</b>"));
874     }
877 void
878 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
880     g_return_if_fail(arc != NULL);
881     g_return_if_fail(SP_IS_ARC(arc));
883     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
885     ge->cx.computed = x;
886     ge->cy.computed = y;
887     ge->rx.computed = rx;
888     ge->ry.computed = ry;
889     if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
890         ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
891     if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
892         ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
893     if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
894         ge->closed = 1;
895     else
896         ge->closed = 0;
898     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
901 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
903     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
905     return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
906                      ge->ry.computed * sin(arg) + ge->cy.computed);
910 /*
911   Local Variables:
912   mode:c++
913   c-file-style:"stroustrup"
914   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
915   indent-tabs-mode:nil
916   fill-column:99
917   End:
918 */
919 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :