Code

Merge from fe-moved
[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 "preferences.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, Inkscape::SnapPreferences const *snapprefs);
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     /* Reset the shape'scurve to the "original_curve"
247      * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
248     sp_shape_set_curve_insync (shape, curve, TRUE);
249     if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
250         SPCurve *c_lpe = curve->copy();
251         bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
252         if (success) {
253             sp_shape_set_curve_insync (shape, c_lpe, TRUE);
254         }
255         c_lpe->unref();
256     }
257     curve->unref();
260 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
262     g_assert(item != NULL);
263     g_assert(SP_IS_GENERICELLIPSE(item));
264     
265     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
266     sp_genericellipse_normalize(ellipse);
267     NR::Matrix const i2d = sp_item_i2d_affine(item);
269     // figure out if we have a slice, whilst guarding against rounding errors
270     bool slice = false;
271     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
272     if (len < 0.0) len += SP_2PI;
273     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
274         slice = false;
275         ellipse->end = ellipse->start + SP_2PI;
276     } else {
277         slice = true;
278     }
280     double rx = ellipse->rx.computed;
281     double ry = ellipse->ry.computed;    
282     double cx = ellipse->cx.computed;
283     double cy = ellipse->cy.computed;
284     
285     // Snap to the 4 quadrant points of the ellipse, but only if the arc
286     // spans far enough to include them
287     double angle = 0;
288     for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
289         if (angle >= ellipse->start && angle <= ellipse->end) {
290             *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
291         }
292     }
293     
294     // And if we have a slice, also snap to the endpoints and the centre point 
295     if (slice) {
296         // Add the centre, if we have a closed slice
297         if (ellipse->closed) {
298             *p = NR::Point(cx, cy) * i2d;
299         }
300         // Add the start point, if it's not coincident with a quadrant point
301         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {    
302             *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
303         } 
304         // Add the end point, if it's not coincident with a quadrant point
305         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {    
306             *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
307         }
308     }
311 void
312 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
314     ellipse->start = fmod(ellipse->start, SP_2PI);
315     ellipse->end = fmod(ellipse->end, SP_2PI);
317     if (ellipse->start < 0.0)
318         ellipse->start += SP_2PI;
319     double diff = ellipse->start - ellipse->end;
320     if (diff >= 0.0)
321         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
323     /* Now we keep: 0 <= start < end <= 2*PI */
326 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
328     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
330     if (flags & SP_OBJECT_WRITE_EXT) {
331         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
332             repr = xml_doc->createElement("svg:path");
333         }
335         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
336         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
337         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
338         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
340         if (SP_IS_ARC(ellipse))
341             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
342     }
344     if (((SPObjectClass *) ge_parent_class)->write)
345         ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
347     return repr;
350 /* SVG <ellipse> element */
352 static void sp_ellipse_class_init(SPEllipseClass *klass);
353 static void sp_ellipse_init(SPEllipse *ellipse);
355 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
356 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
357 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
358 static gchar *sp_ellipse_description(SPItem *item);
360 static SPGenericEllipseClass *ellipse_parent_class;
362 GType
363 sp_ellipse_get_type(void)
365     static GType type = 0;
366     if (!type) {
367         GTypeInfo info = {
368             sizeof(SPEllipseClass),
369             NULL,   /* base_init */
370             NULL,   /* base_finalize */
371             (GClassInitFunc) sp_ellipse_class_init,
372             NULL,   /* class_finalize */
373             NULL,   /* class_data */
374             sizeof(SPEllipse),
375             16,   /* n_preallocs */
376             (GInstanceInitFunc) sp_ellipse_init,
377             NULL,   /* value_table */
378         };
379         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
380     }
381     return type;
384 static void sp_ellipse_class_init(SPEllipseClass *klass)
386     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
387     SPItemClass *item_class = (SPItemClass *) klass;
389     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
391     sp_object_class->build = sp_ellipse_build;
392     sp_object_class->write = sp_ellipse_write;
393     sp_object_class->set = sp_ellipse_set;
395     item_class->description = sp_ellipse_description;
398 static void
399 sp_ellipse_init(SPEllipse */*ellipse*/)
401     /* Nothing special */
404 static void
405 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
407     if (((SPObjectClass *) ellipse_parent_class)->build)
408         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
410     sp_object_read_attr(object, "cx");
411     sp_object_read_attr(object, "cy");
412     sp_object_read_attr(object, "rx");
413     sp_object_read_attr(object, "ry");
416 static Inkscape::XML::Node *
417 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
419     SPGenericEllipse *ellipse;
421     ellipse = SP_GENERICELLIPSE(object);
423     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
424         repr = xml_doc->createElement("svg:ellipse");
425     }
427     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
428     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
429     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
430     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
432     if (((SPObjectClass *) ellipse_parent_class)->write)
433         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
435     return repr;
438 static void
439 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
441     SPGenericEllipse *ellipse;
443     ellipse = SP_GENERICELLIPSE(object);
445     switch (key) {
446         case SP_ATTR_CX:
447             ellipse->cx.readOrUnset(value);
448             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
449             break;
450         case SP_ATTR_CY:
451             ellipse->cy.readOrUnset(value);
452             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
453             break;
454         case SP_ATTR_RX:
455             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
456                 ellipse->rx.unset();
457             }
458             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
459             break;
460         case SP_ATTR_RY:
461             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
462                 ellipse->ry.unset();
463             }
464             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
465             break;
466         default:
467             if (((SPObjectClass *) ellipse_parent_class)->set)
468                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
469             break;
470     }
473 static gchar *sp_ellipse_description(SPItem */*item*/)
475     return g_strdup(_("<b>Ellipse</b>"));
479 void
480 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
482     SPGenericEllipse *ge;
484     g_return_if_fail(ellipse != NULL);
485     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
487     ge = SP_GENERICELLIPSE(ellipse);
489     ge->cx.computed = x;
490     ge->cy.computed = y;
491     ge->rx.computed = rx;
492     ge->ry.computed = ry;
494     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
497 /* SVG <circle> element */
499 static void sp_circle_class_init(SPCircleClass *klass);
500 static void sp_circle_init(SPCircle *circle);
502 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
503 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
504 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
505 static gchar *sp_circle_description(SPItem *item);
507 static SPGenericEllipseClass *circle_parent_class;
509 GType
510 sp_circle_get_type(void)
512     static GType type = 0;
513     if (!type) {
514         GTypeInfo info = {
515             sizeof(SPCircleClass),
516             NULL,   /* base_init */
517             NULL,   /* base_finalize */
518             (GClassInitFunc) sp_circle_class_init,
519             NULL,   /* class_finalize */
520             NULL,   /* class_data */
521             sizeof(SPCircle),
522             16,   /* n_preallocs */
523             (GInstanceInitFunc) sp_circle_init,
524             NULL,   /* value_table */
525         };
526         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
527     }
528     return type;
531 static void
532 sp_circle_class_init(SPCircleClass *klass)
534     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
535     SPItemClass *item_class = (SPItemClass *) klass;
537     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
539     sp_object_class->build = sp_circle_build;
540     sp_object_class->write = sp_circle_write;
541     sp_object_class->set = sp_circle_set;
543     item_class->description = sp_circle_description;
546 static void
547 sp_circle_init(SPCircle */*circle*/)
549     /* Nothing special */
552 static void
553 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
555     if (((SPObjectClass *) circle_parent_class)->build)
556         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
558     sp_object_read_attr(object, "cx");
559     sp_object_read_attr(object, "cy");
560     sp_object_read_attr(object, "r");
563 static Inkscape::XML::Node *
564 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
566     SPGenericEllipse *ellipse;
568     ellipse = SP_GENERICELLIPSE(object);
570     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
571         repr = xml_doc->createElement("svg:circle");
572     }
574     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
575     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
576     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
578     if (((SPObjectClass *) circle_parent_class)->write)
579         ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
581     return repr;
584 static void
585 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
587     SPGenericEllipse *ge;
589     ge = SP_GENERICELLIPSE(object);
591     switch (key) {
592         case SP_ATTR_CX:
593             ge->cx.readOrUnset(value);
594             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
595             break;
596         case SP_ATTR_CY:
597             ge->cy.readOrUnset(value);
598             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
599             break;
600         case SP_ATTR_R:
601             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
602                 ge->rx.unset();
603             }
604             ge->ry = ge->rx;
605             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
606             break;
607         default:
608             if (((SPObjectClass *) circle_parent_class)->set)
609                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
610             break;
611     }
614 static gchar *sp_circle_description(SPItem */*item*/)
616     return g_strdup(_("<b>Circle</b>"));
619 /* <path sodipodi:type="arc"> element */
621 static void sp_arc_class_init(SPArcClass *klass);
622 static void sp_arc_init(SPArc *arc);
624 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
625 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
626 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
627 static void sp_arc_modified(SPObject *object, guint flags);
629 static gchar *sp_arc_description(SPItem *item);
631 static SPGenericEllipseClass *arc_parent_class;
633 GType
634 sp_arc_get_type(void)
636     static GType type = 0;
637     if (!type) {
638         GTypeInfo info = {
639             sizeof(SPArcClass),
640             NULL,   /* base_init */
641             NULL,   /* base_finalize */
642             (GClassInitFunc) sp_arc_class_init,
643             NULL,   /* class_finalize */
644             NULL,   /* class_data */
645             sizeof(SPArc),
646             16,   /* n_preallocs */
647             (GInstanceInitFunc) sp_arc_init,
648             NULL,   /* value_table */
649         };
650         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
651     }
652     return type;
655 static void
656 sp_arc_class_init(SPArcClass *klass)
658     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
659     SPItemClass *item_class = (SPItemClass *) klass;
661     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
663     sp_object_class->build = sp_arc_build;
664     sp_object_class->write = sp_arc_write;
665     sp_object_class->set = sp_arc_set;
666     sp_object_class->modified = sp_arc_modified;
668     item_class->description = sp_arc_description;
671 static void
672 sp_arc_init(SPArc */*arc*/)
674     /* Nothing special */
677 static void
678 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
680     if (((SPObjectClass *) arc_parent_class)->build)
681         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
683     Inkscape::Version version = sp_object_get_sodipodi_version(object);
685     sp_object_read_attr(object, "sodipodi:cx");
686     sp_object_read_attr(object, "sodipodi:cy");
687     sp_object_read_attr(object, "sodipodi:rx");
688     sp_object_read_attr(object, "sodipodi:ry");
690     sp_object_read_attr(object, "sodipodi:start");
691     sp_object_read_attr(object, "sodipodi:end");
692     sp_object_read_attr(object, "sodipodi:open");
695 /*
696  * sp_arc_set_elliptical_path_attribute:
697  *
698  * Convert center to endpoint parameterization and set it to repr.
699  *
700  * See SVG 1.0 Specification W3C Recommendation
701  * ``F.6 Ellptical arc implementation notes'' for more detail.
702  */
703 static gboolean
704 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
706     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
708     Inkscape::SVG::PathString str;
710     NR::Point p1 = sp_arc_get_xy(arc, ge->start);
711     NR::Point p2 = sp_arc_get_xy(arc, ge->end);
712     double rx = ge->rx.computed;
713     double ry = ge->ry.computed;
715     str.moveTo(p1);
717     double dt = fmod(ge->end - ge->start, SP_2PI);
718     if (fabs(dt) < 1e-6) {
719         NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
720         str.arcTo(rx, ry, 0, true, true, ph)
721            .arcTo(rx, ry, 0, true, true, p2)
722            .closePath();
723     } else {
724         bool fa = (fabs(dt) > M_PI);
725         bool fs = (dt > 0);
726         str.arcTo(rx, ry, 0, fa, fs, p2);
727         if (ge->closed) {
728             NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
729             str.lineTo(center).closePath();
730         }
731     }
733     repr->setAttribute("d", str.c_str());
734     return true;
737 static Inkscape::XML::Node *
738 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
740     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
741     SPArc *arc = SP_ARC(object);
743     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
744         repr = xml_doc->createElement("svg:path");
745     }
747     if (flags & SP_OBJECT_WRITE_EXT) {
748         repr->setAttribute("sodipodi:type", "arc");
749         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
750         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
751         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
752         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
754         // write start and end only if they are non-trivial; otherwise remove
755         gdouble len = fmod(ge->end - ge->start, SP_2PI);
756         if (len < 0.0) len += SP_2PI;
757         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
758             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
759             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
760             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
761         } else {
762             repr->setAttribute("sodipodi:end", NULL);
763             repr->setAttribute("sodipodi:start", NULL);
764             repr->setAttribute("sodipodi:open", NULL);
765         }
766     }
768     // write d=
769     sp_arc_set_elliptical_path_attribute(arc, repr);
771     if (((SPObjectClass *) arc_parent_class)->write)
772         ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
774     return repr;
777 static void
778 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
780     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
782     switch (key) {
783         case SP_ATTR_SODIPODI_CX:
784             ge->cx.readOrUnset(value);
785             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
786             break;
787         case SP_ATTR_SODIPODI_CY:
788             ge->cy.readOrUnset(value);
789             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
790             break;
791         case SP_ATTR_SODIPODI_RX:
792             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
793                 ge->rx.unset();
794             }
795             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
796             break;
797         case SP_ATTR_SODIPODI_RY:
798             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
799                 ge->ry.unset();
800             }
801             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
802             break;
803         case SP_ATTR_SODIPODI_START:
804             if (value) {
805                 sp_svg_number_read_d(value, &ge->start);
806             } else {
807                 ge->start = 0;
808             }
809             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
810             break;
811         case SP_ATTR_SODIPODI_END:
812             if (value) {
813                 sp_svg_number_read_d(value, &ge->end);
814             } else {
815                 ge->end = 2 * M_PI;
816             }
817             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
818             break;
819         case SP_ATTR_SODIPODI_OPEN:
820             ge->closed = (!value);
821             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
822             break;
823         default:
824             if (((SPObjectClass *) arc_parent_class)->set)
825                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
826             break;
827     }
830 static void
831 sp_arc_modified(SPObject *object, guint flags)
833     if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
834         sp_shape_set_shape((SPShape *) object);
835     }
837     if (((SPObjectClass *) arc_parent_class)->modified)
838         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
841 static gchar *sp_arc_description(SPItem *item)
843     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
845     gdouble len = fmod(ge->end - ge->start, SP_2PI);
846     if (len < 0.0) len += SP_2PI;
847     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
848         if (ge->closed) {
849             return g_strdup(_("<b>Segment</b>"));
850         } else {
851             return g_strdup(_("<b>Arc</b>"));
852         }
853     } else {
854         return g_strdup(_("<b>Ellipse</b>"));
855     }
858 void
859 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
861     g_return_if_fail(arc != NULL);
862     g_return_if_fail(SP_IS_ARC(arc));
864     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
866     ge->cx.computed = x;
867     ge->cy.computed = y;
868     ge->rx.computed = rx;
869     ge->ry.computed = ry;
870     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
871     if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
872         ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0);
873     if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
874         ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0);
875     if (!prefs->getBool("/tools/shapes/arc/open"))
876         ge->closed = 1;
877     else
878         ge->closed = 0;
880     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
883 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
885     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
887     return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
888                      ge->ry.computed * sin(arg) + ge->cy.computed);
892 /*
893   Local Variables:
894   mode:c++
895   c-file-style:"stroustrup"
896   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
897   indent-tabs-mode:nil
898   fill-column:99
899   End:
900 */
901 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :