Code

remove many unnecessary to_2geom and from_2geom calls
[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/nr-matrix-fns.h"
23 #include "svg/svg.h"
24 #include "svg/path-string.h"
25 #include "xml/repr.h"
26 #include "attributes.h"
27 #include "style.h"
28 #include "display/curve.h"
29 #include <glibmm/i18n.h>
30 #include <2geom/transforms.h>
32 #include "document.h"
33 #include "sp-ellipse.h"
35 #include "prefs-utils.h"
37 /* Common parent class */
39 #define noELLIPSE_VERBOSE
41 #ifndef M_PI
42 #define M_PI 3.14159265358979323846
43 #endif
45 #define SP_2PI (2 * M_PI)
47 #if 1
48 /* Hmmm... shouldn't this also qualify */
49 /* Whether it is faster or not, well, nobody knows */
50 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
51 #else
52 /* we do not use C99 round(3) function yet */
53 static double sp_round(double x, double y)
54 {
55     double remain;
57     g_assert(y > 0.0);
59     /* return round(x/y) * y; */
61     remain = fmod(x, y);
63     if (remain >= 0.5*y)
64         return x - remain + y;
65     else
66         return x - remain;
67 }
68 #endif
70 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
71 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
73 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
75 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p);
77 static void sp_genericellipse_set_shape(SPShape *shape);
78 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
80 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
81                                                     guint flags);
83 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
85 static SPShapeClass *ge_parent_class;
87 GType
88 sp_genericellipse_get_type(void)
89 {
90     static GType type = 0;
91     if (!type) {
92         GTypeInfo info = {
93             sizeof(SPGenericEllipseClass),
94             NULL,   /* base_init */
95             NULL,   /* base_finalize */
96             (GClassInitFunc) sp_genericellipse_class_init,
97             NULL,   /* class_finalize */
98             NULL,   /* class_data */
99             sizeof(SPGenericEllipse),
100             16,   /* n_preallocs */
101             (GInstanceInitFunc) sp_genericellipse_init,
102             NULL,   /* value_table */
103         };
104         type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
105     }
106     return type;
109 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
111     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
112     SPItemClass *item_class = (SPItemClass *) klass;
113     SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) 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     lpe_item_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(SPLPEItem *lpeitem, bool write)
163     SPShape *shape = (SPShape *) lpeitem;
164     sp_genericellipse_set_shape(shape);
166     if (write) {
167         Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
168         if ( shape->curve != NULL ) {
169             gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
170             repr->setAttribute("d", str);
171             g_free(str);
172         } else {
173             repr->setAttribute("d", NULL);
174         }
175     }
177     ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
181 #define C1 0.552
183 /* fixme: Think (Lauris) */
184 /* Can't we use arcto in this method? */
185 static void sp_genericellipse_set_shape(SPShape *shape)
187     double rx, ry, s, e;
188     double x0, y0, x1, y1, x2, y2, x3, y3;
189     double len;
190     gint slice = FALSE;
191  //   gint i;
193     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
195     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
196     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
198     sp_genericellipse_normalize(ellipse);
200     rx = ellipse->rx.computed;
201     ry = ellipse->ry.computed;
203     // figure out if we have a slice, guarding against rounding errors
204     len = fmod(ellipse->end - ellipse->start, SP_2PI);
205     if (len < 0.0) len += SP_2PI;
206     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
207         slice = FALSE;
208         ellipse->end = ellipse->start + SP_2PI;
209     } else {
210         slice = TRUE;
211     }
213     SPCurve * curve = new SPCurve();
214     curve->moveto(cos(ellipse->start), sin(ellipse->start));
216     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
217         e = s + M_PI_2;
218         if (e > ellipse->end)
219             e = ellipse->end;
220         len = C1 * (e - s) / M_PI_2;
221         x0 = cos(s);
222         y0 = sin(s);
223         x1 = x0 + len * cos(s + M_PI_2);
224         y1 = y0 + len * sin(s + M_PI_2);
225         x3 = cos(e);
226         y3 = sin(e);
227         x2 = x3 + len * cos(e - M_PI_2);
228         y2 = y3 + len * sin(e - M_PI_2);
229 #ifdef ELLIPSE_VERBOSE
230         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
231                 i, s, e, x1, y1, x2, y2, x3, y3);
232 #endif
233         curve->curveto(x1,y1, x2,y2, x3,y3);
234     }
236     if (slice && ellipse->closed) {  // TODO: is this check for "ellipse->closed" necessary?
237         curve->lineto(0., 0.);
238     }
239     if (ellipse->closed) {
240         curve->closepath();
241     }
243     Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
244     curve->transform(aff);
246     sp_lpe_item_perform_path_effect(SP_LPE_ITEM (ellipse), curve);
247     sp_shape_set_curve_insync((SPShape *) ellipse, curve, TRUE);
248     curve->unref();
251 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
253     g_assert(item != NULL);
254     g_assert(SP_IS_GENERICELLIPSE(item));
255     
256     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
257     sp_genericellipse_normalize(ellipse);
258     NR::Matrix const i2d = sp_item_i2d_affine(item);
260     // figure out if we have a slice, whilst guarding against rounding errors
261     bool slice = false;
262     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
263     if (len < 0.0) len += SP_2PI;
264     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
265         slice = false;
266         ellipse->end = ellipse->start + SP_2PI;
267     } else {
268         slice = true;
269     }
271     double rx = ellipse->rx.computed;
272     double ry = ellipse->ry.computed;    
273     double cx = ellipse->cx.computed;
274     double cy = ellipse->cy.computed;
275     
276     // Snap to the 4 quadrant points of the ellipse, but only if the arc
277     // spans far enough to include them
278     double angle = 0;
279     for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
280         if (angle >= ellipse->start && angle <= ellipse->end) {
281             *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
282         }
283     }
284     
285     // And if we have a slice, also snap to the endpoints and the centre point 
286     if (slice) {
287         // Add the centre, if we have a closed slice
288         if (ellipse->closed) {
289             *p = NR::Point(cx, cy) * i2d;
290         }
291         // Add the start point, if it's not coincident with a quadrant point
292         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {    
293             *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
294         } 
295         // Add the end point, if it's not coincident with a quadrant point
296         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {    
297             *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
298         }
299     }
302 void
303 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
305     ellipse->start = fmod(ellipse->start, SP_2PI);
306     ellipse->end = fmod(ellipse->end, SP_2PI);
308     if (ellipse->start < 0.0)
309         ellipse->start += SP_2PI;
310     double diff = ellipse->start - ellipse->end;
311     if (diff >= 0.0)
312         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
314     /* Now we keep: 0 <= start < end <= 2*PI */
317 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
319     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
321     if (flags & SP_OBJECT_WRITE_EXT) {
322         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
323             repr = xml_doc->createElement("svg:path");
324         }
326         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
327         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
328         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
329         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
331         if (SP_IS_ARC(ellipse))
332             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
333     }
335     if (((SPObjectClass *) ge_parent_class)->write)
336         ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
338     return repr;
341 /* SVG <ellipse> element */
343 static void sp_ellipse_class_init(SPEllipseClass *klass);
344 static void sp_ellipse_init(SPEllipse *ellipse);
346 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
347 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
348 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
349 static gchar *sp_ellipse_description(SPItem *item);
351 static SPGenericEllipseClass *ellipse_parent_class;
353 GType
354 sp_ellipse_get_type(void)
356     static GType type = 0;
357     if (!type) {
358         GTypeInfo info = {
359             sizeof(SPEllipseClass),
360             NULL,   /* base_init */
361             NULL,   /* base_finalize */
362             (GClassInitFunc) sp_ellipse_class_init,
363             NULL,   /* class_finalize */
364             NULL,   /* class_data */
365             sizeof(SPEllipse),
366             16,   /* n_preallocs */
367             (GInstanceInitFunc) sp_ellipse_init,
368             NULL,   /* value_table */
369         };
370         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
371     }
372     return type;
375 static void sp_ellipse_class_init(SPEllipseClass *klass)
377     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
378     SPItemClass *item_class = (SPItemClass *) klass;
380     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
382     sp_object_class->build = sp_ellipse_build;
383     sp_object_class->write = sp_ellipse_write;
384     sp_object_class->set = sp_ellipse_set;
386     item_class->description = sp_ellipse_description;
389 static void
390 sp_ellipse_init(SPEllipse */*ellipse*/)
392     /* Nothing special */
395 static void
396 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
398     if (((SPObjectClass *) ellipse_parent_class)->build)
399         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
401     sp_object_read_attr(object, "cx");
402     sp_object_read_attr(object, "cy");
403     sp_object_read_attr(object, "rx");
404     sp_object_read_attr(object, "ry");
407 static Inkscape::XML::Node *
408 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
410     SPGenericEllipse *ellipse;
412     ellipse = SP_GENERICELLIPSE(object);
414     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
415         repr = xml_doc->createElement("svg:ellipse");
416     }
418     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
419     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
420     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
421     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
423     if (((SPObjectClass *) ellipse_parent_class)->write)
424         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
426     return repr;
429 static void
430 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
432     SPGenericEllipse *ellipse;
434     ellipse = SP_GENERICELLIPSE(object);
436     switch (key) {
437         case SP_ATTR_CX:
438             ellipse->cx.readOrUnset(value);
439             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
440             break;
441         case SP_ATTR_CY:
442             ellipse->cy.readOrUnset(value);
443             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
444             break;
445         case SP_ATTR_RX:
446             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
447                 ellipse->rx.unset();
448             }
449             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
450             break;
451         case SP_ATTR_RY:
452             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
453                 ellipse->ry.unset();
454             }
455             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
456             break;
457         default:
458             if (((SPObjectClass *) ellipse_parent_class)->set)
459                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
460             break;
461     }
464 static gchar *sp_ellipse_description(SPItem */*item*/)
466     return g_strdup(_("<b>Ellipse</b>"));
470 void
471 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
473     SPGenericEllipse *ge;
475     g_return_if_fail(ellipse != NULL);
476     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
478     ge = SP_GENERICELLIPSE(ellipse);
480     ge->cx.computed = x;
481     ge->cy.computed = y;
482     ge->rx.computed = rx;
483     ge->ry.computed = ry;
485     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
488 /* SVG <circle> element */
490 static void sp_circle_class_init(SPCircleClass *klass);
491 static void sp_circle_init(SPCircle *circle);
493 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
494 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
495 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
496 static gchar *sp_circle_description(SPItem *item);
498 static SPGenericEllipseClass *circle_parent_class;
500 GType
501 sp_circle_get_type(void)
503     static GType type = 0;
504     if (!type) {
505         GTypeInfo info = {
506             sizeof(SPCircleClass),
507             NULL,   /* base_init */
508             NULL,   /* base_finalize */
509             (GClassInitFunc) sp_circle_class_init,
510             NULL,   /* class_finalize */
511             NULL,   /* class_data */
512             sizeof(SPCircle),
513             16,   /* n_preallocs */
514             (GInstanceInitFunc) sp_circle_init,
515             NULL,   /* value_table */
516         };
517         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
518     }
519     return type;
522 static void
523 sp_circle_class_init(SPCircleClass *klass)
525     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
526     SPItemClass *item_class = (SPItemClass *) klass;
528     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
530     sp_object_class->build = sp_circle_build;
531     sp_object_class->write = sp_circle_write;
532     sp_object_class->set = sp_circle_set;
534     item_class->description = sp_circle_description;
537 static void
538 sp_circle_init(SPCircle */*circle*/)
540     /* Nothing special */
543 static void
544 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
546     if (((SPObjectClass *) circle_parent_class)->build)
547         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
549     sp_object_read_attr(object, "cx");
550     sp_object_read_attr(object, "cy");
551     sp_object_read_attr(object, "r");
554 static Inkscape::XML::Node *
555 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
557     SPGenericEllipse *ellipse;
559     ellipse = SP_GENERICELLIPSE(object);
561     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
562         repr = xml_doc->createElement("svg:circle");
563     }
565     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
566     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
567     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
569     if (((SPObjectClass *) circle_parent_class)->write)
570         ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
572     return repr;
575 static void
576 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
578     SPGenericEllipse *ge;
580     ge = SP_GENERICELLIPSE(object);
582     switch (key) {
583         case SP_ATTR_CX:
584             ge->cx.readOrUnset(value);
585             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
586             break;
587         case SP_ATTR_CY:
588             ge->cy.readOrUnset(value);
589             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
590             break;
591         case SP_ATTR_R:
592             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
593                 ge->rx.unset();
594             }
595             ge->ry = ge->rx;
596             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
597             break;
598         default:
599             if (((SPObjectClass *) circle_parent_class)->set)
600                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
601             break;
602     }
605 static gchar *sp_circle_description(SPItem */*item*/)
607     return g_strdup(_("<b>Circle</b>"));
610 /* <path sodipodi:type="arc"> element */
612 static void sp_arc_class_init(SPArcClass *klass);
613 static void sp_arc_init(SPArc *arc);
615 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
616 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
617 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
618 static void sp_arc_modified(SPObject *object, guint flags);
620 static gchar *sp_arc_description(SPItem *item);
622 static SPGenericEllipseClass *arc_parent_class;
624 GType
625 sp_arc_get_type(void)
627     static GType type = 0;
628     if (!type) {
629         GTypeInfo info = {
630             sizeof(SPArcClass),
631             NULL,   /* base_init */
632             NULL,   /* base_finalize */
633             (GClassInitFunc) sp_arc_class_init,
634             NULL,   /* class_finalize */
635             NULL,   /* class_data */
636             sizeof(SPArc),
637             16,   /* n_preallocs */
638             (GInstanceInitFunc) sp_arc_init,
639             NULL,   /* value_table */
640         };
641         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
642     }
643     return type;
646 static void
647 sp_arc_class_init(SPArcClass *klass)
649     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
650     SPItemClass *item_class = (SPItemClass *) klass;
652     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
654     sp_object_class->build = sp_arc_build;
655     sp_object_class->write = sp_arc_write;
656     sp_object_class->set = sp_arc_set;
657     sp_object_class->modified = sp_arc_modified;
659     item_class->description = sp_arc_description;
662 static void
663 sp_arc_init(SPArc */*arc*/)
665     /* Nothing special */
668 static void
669 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
671     if (((SPObjectClass *) arc_parent_class)->build)
672         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
674     Inkscape::Version version = sp_object_get_sodipodi_version(object);
676     sp_object_read_attr(object, "sodipodi:cx");
677     sp_object_read_attr(object, "sodipodi:cy");
678     sp_object_read_attr(object, "sodipodi:rx");
679     sp_object_read_attr(object, "sodipodi:ry");
681     sp_object_read_attr(object, "sodipodi:start");
682     sp_object_read_attr(object, "sodipodi:end");
683     sp_object_read_attr(object, "sodipodi:open");
686 /*
687  * sp_arc_set_elliptical_path_attribute:
688  *
689  * Convert center to endpoint parameterization and set it to repr.
690  *
691  * See SVG 1.0 Specification W3C Recommendation
692  * ``F.6 Ellptical arc implementation notes'' for more detail.
693  */
694 static gboolean
695 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
697     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
699     Inkscape::SVG::PathString str;
701     NR::Point p1 = sp_arc_get_xy(arc, ge->start);
702     NR::Point p2 = sp_arc_get_xy(arc, ge->end);
703     double rx = ge->rx.computed;
704     double ry = ge->ry.computed;
706     str.moveTo(p1);
708     double dt = fmod(ge->end - ge->start, SP_2PI);
709     if (fabs(dt) < 1e-6) {
710         NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
711         str.arcTo(rx, ry, 0, true, true, ph)
712            .arcTo(rx, ry, 0, true, true, p2)
713            .closePath();
714     } else {
715         bool fa = (fabs(dt) > M_PI);
716         bool fs = (dt > 0);
717         str.arcTo(rx, ry, 0, fa, fs, p2);
718         if (ge->closed) {
719             NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
720             str.lineTo(center).closePath();
721         }
722     }
724     repr->setAttribute("d", str.c_str());
725     return true;
728 static Inkscape::XML::Node *
729 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
731     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
732     SPArc *arc = SP_ARC(object);
734     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
735         repr = xml_doc->createElement("svg:path");
736     }
738     if (flags & SP_OBJECT_WRITE_EXT) {
739         repr->setAttribute("sodipodi:type", "arc");
740         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
741         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
742         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
743         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
745         // write start and end only if they are non-trivial; otherwise remove
746         gdouble len = fmod(ge->end - ge->start, SP_2PI);
747         if (len < 0.0) len += SP_2PI;
748         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
749             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
750             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
751             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
752         } else {
753             repr->setAttribute("sodipodi:end", NULL);
754             repr->setAttribute("sodipodi:start", NULL);
755             repr->setAttribute("sodipodi:open", NULL);
756         }
757     }
759     // write d=
760     sp_arc_set_elliptical_path_attribute(arc, repr);
762     if (((SPObjectClass *) arc_parent_class)->write)
763         ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
765     return repr;
768 static void
769 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
771     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
773     switch (key) {
774         case SP_ATTR_SODIPODI_CX:
775             ge->cx.readOrUnset(value);
776             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
777             break;
778         case SP_ATTR_SODIPODI_CY:
779             ge->cy.readOrUnset(value);
780             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
781             break;
782         case SP_ATTR_SODIPODI_RX:
783             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
784                 ge->rx.unset();
785             }
786             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
787             break;
788         case SP_ATTR_SODIPODI_RY:
789             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
790                 ge->ry.unset();
791             }
792             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
793             break;
794         case SP_ATTR_SODIPODI_START:
795             if (value) {
796                 sp_svg_number_read_d(value, &ge->start);
797             } else {
798                 ge->start = 0;
799             }
800             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
801             break;
802         case SP_ATTR_SODIPODI_END:
803             if (value) {
804                 sp_svg_number_read_d(value, &ge->end);
805             } else {
806                 ge->end = 2 * M_PI;
807             }
808             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
809             break;
810         case SP_ATTR_SODIPODI_OPEN:
811             ge->closed = (!value);
812             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
813             break;
814         default:
815             if (((SPObjectClass *) arc_parent_class)->set)
816                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
817             break;
818     }
821 static void
822 sp_arc_modified(SPObject *object, guint flags)
824     if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
825         sp_shape_set_shape((SPShape *) object);
826     }
828     if (((SPObjectClass *) arc_parent_class)->modified)
829         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
832 static gchar *sp_arc_description(SPItem *item)
834     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
836     gdouble len = fmod(ge->end - ge->start, SP_2PI);
837     if (len < 0.0) len += SP_2PI;
838     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
839         if (ge->closed) {
840             return g_strdup(_("<b>Segment</b>"));
841         } else {
842             return g_strdup(_("<b>Arc</b>"));
843         }
844     } else {
845         return g_strdup(_("<b>Ellipse</b>"));
846     }
849 void
850 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
852     g_return_if_fail(arc != NULL);
853     g_return_if_fail(SP_IS_ARC(arc));
855     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
857     ge->cx.computed = x;
858     ge->cy.computed = y;
859     ge->rx.computed = rx;
860     ge->ry.computed = ry;
861     if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
862         ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
863     if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
864         ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
865     if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
866         ge->closed = 1;
867     else
868         ge->closed = 0;
870     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
873 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
875     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
877     return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
878                      ge->ry.computed * sin(arg) + ge->cy.computed);
882 /*
883   Local Variables:
884   mode:c++
885   c-file-style:"stroustrup"
886   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
887   indent-tabs-mode:nil
888   fill-column:99
889   End:
890 */
891 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :