Code

Use g_base64_encode_step when importing images via drag and drop.
[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, bool const target, SnapPointsWithType &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         Geom::OptRect viewbox = ((SPItemCtx const *) ctx)->vp;
147         double const dx = viewbox->width();
148         double const dy = viewbox->height();
149         double const dr = sqrt(dx*dx + dy*dy)/sqrt(2);
150         double const em = style->font_size.computed;
151         double const ex = em * 0.5; // fixme: get from pango or libnrtype
152         ellipse->cx.update(em, ex, dx);
153         ellipse->cy.update(em, ex, dy);
154         ellipse->rx.update(em, ex, dr);
155         ellipse->ry.update(em, ex, dr);
156         sp_shape_set_shape((SPShape *) object);
157     }
159     if (((SPObjectClass *) ge_parent_class)->update)
160         ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
163 static void
164 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
166     SPShape *shape = (SPShape *) lpeitem;
167     sp_genericellipse_set_shape(shape);
169     if (write) {
170         Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
171         if ( shape->curve != NULL ) {
172             gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
173             repr->setAttribute("d", str);
174             g_free(str);
175         } else {
176             repr->setAttribute("d", NULL);
177         }
178     }
180     ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
184 #define C1 0.552
186 /* fixme: Think (Lauris) */
187 /* Can't we use arcto in this method? */
188 static void sp_genericellipse_set_shape(SPShape *shape)
190     double rx, ry, s, e;
191     double x0, y0, x1, y1, x2, y2, x3, y3;
192     double len;
193     gint slice = FALSE;
194  //   gint i;
196     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
198     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
199     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
201     sp_genericellipse_normalize(ellipse);
203     rx = ellipse->rx.computed;
204     ry = ellipse->ry.computed;
206     // figure out if we have a slice, guarding against rounding errors
207     len = fmod(ellipse->end - ellipse->start, SP_2PI);
208     if (len < 0.0) len += SP_2PI;
209     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
210         slice = FALSE;
211         ellipse->end = ellipse->start + SP_2PI;
212     } else {
213         slice = TRUE;
214     }
216     SPCurve * curve = new SPCurve();
217     curve->moveto(cos(ellipse->start), sin(ellipse->start));
219     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
220         e = s + M_PI_2;
221         if (e > ellipse->end)
222             e = ellipse->end;
223         len = C1 * (e - s) / M_PI_2;
224         x0 = cos(s);
225         y0 = sin(s);
226         x1 = x0 + len * cos(s + M_PI_2);
227         y1 = y0 + len * sin(s + M_PI_2);
228         x3 = cos(e);
229         y3 = sin(e);
230         x2 = x3 + len * cos(e - M_PI_2);
231         y2 = y3 + len * sin(e - M_PI_2);
232 #ifdef ELLIPSE_VERBOSE
233         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
234                 i, s, e, x1, y1, x2, y2, x3, y3);
235 #endif
236         curve->curveto(x1,y1, x2,y2, x3,y3);
237     }
239     if (slice && ellipse->closed) {  // TODO: is this check for "ellipse->closed" necessary?
240         curve->lineto(0., 0.);
241     }
242     if (ellipse->closed) {
243         curve->closepath();
244     }
246     Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
247     curve->transform(aff);
249     /* Reset the shape'scurve to the "original_curve"
250      * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
251     sp_shape_set_curve_insync (shape, curve, TRUE);
252     if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
253         SPCurve *c_lpe = curve->copy();
254         bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
255         if (success) {
256             sp_shape_set_curve_insync (shape, c_lpe, TRUE);
257         }
258         c_lpe->unref();
259     }
260     curve->unref();
263 static void sp_genericellipse_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs)
265     g_assert(item != NULL);
266     g_assert(SP_IS_GENERICELLIPSE(item));
268     // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
269         if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
270                 return;
271         }
273     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
274     sp_genericellipse_normalize(ellipse);
275     Geom::Matrix const i2d = sp_item_i2d_affine(item);
277     // figure out if we have a slice, while guarding against rounding errors
278     bool slice = false;
279     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
280     if (len < 0.0) len += SP_2PI;
281     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
282         slice = false;
283         ellipse->end = ellipse->start + SP_2PI;
284     } else {
285         slice = true;
286     }
288     double rx = ellipse->rx.computed;
289     double ry = ellipse->ry.computed;
290     double cx = ellipse->cx.computed;
291     double cy = ellipse->cy.computed;
293     Geom::Point pt;
295     // Snap to the 4 quadrant points of the ellipse, but only if the arc
296     // spans far enough to include them
297     if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this?
298                 double angle = 0;
299                 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
300                         if (angle >= ellipse->start && angle <= ellipse->end) {
301                                 pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
302                                 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) : int(Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT)));
303                         }
304                 }
305     }
307     // Add the centre, if we have a closed slice or when explicitly asked for
308     if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) {
309         pt = Geom::Point(cx, cy) * i2d;
310         p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_HANDLE) : int(Inkscape::SNAPSOURCE_HANDLE)));
311     }
313     // And if we have a slice, also snap to the endpoints
314     if (snapprefs->getSnapToItemNode() && slice) {
315         // Add the start point, if it's not coincident with a quadrant point
316         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
317             pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
318             p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP)));
319         }
320         // Add the end point, if it's not coincident with a quadrant point
321         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
322             pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
323             p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP)));
324         }
325     }
328 void
329 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
331     ellipse->start = fmod(ellipse->start, SP_2PI);
332     ellipse->end = fmod(ellipse->end, SP_2PI);
334     if (ellipse->start < 0.0)
335         ellipse->start += SP_2PI;
336     double diff = ellipse->start - ellipse->end;
337     if (diff >= 0.0)
338         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
340     /* Now we keep: 0 <= start < end <= 2*PI */
343 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
345     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
347     if (flags & SP_OBJECT_WRITE_EXT) {
348         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
349             repr = xml_doc->createElement("svg:path");
350         }
352         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
353         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
354         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
355         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
357         if (SP_IS_ARC(ellipse))
358             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
359     }
361     if (((SPObjectClass *) ge_parent_class)->write)
362         ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
364     return repr;
367 /* SVG <ellipse> element */
369 static void sp_ellipse_class_init(SPEllipseClass *klass);
370 static void sp_ellipse_init(SPEllipse *ellipse);
372 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
373 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
374 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
375 static gchar *sp_ellipse_description(SPItem *item);
377 static SPGenericEllipseClass *ellipse_parent_class;
379 GType
380 sp_ellipse_get_type(void)
382     static GType type = 0;
383     if (!type) {
384         GTypeInfo info = {
385             sizeof(SPEllipseClass),
386             NULL,   /* base_init */
387             NULL,   /* base_finalize */
388             (GClassInitFunc) sp_ellipse_class_init,
389             NULL,   /* class_finalize */
390             NULL,   /* class_data */
391             sizeof(SPEllipse),
392             16,   /* n_preallocs */
393             (GInstanceInitFunc) sp_ellipse_init,
394             NULL,   /* value_table */
395         };
396         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
397     }
398     return type;
401 static void sp_ellipse_class_init(SPEllipseClass *klass)
403     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
404     SPItemClass *item_class = (SPItemClass *) klass;
406     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
408     sp_object_class->build = sp_ellipse_build;
409     sp_object_class->write = sp_ellipse_write;
410     sp_object_class->set = sp_ellipse_set;
412     item_class->description = sp_ellipse_description;
415 static void
416 sp_ellipse_init(SPEllipse */*ellipse*/)
418     /* Nothing special */
421 static void
422 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
424     if (((SPObjectClass *) ellipse_parent_class)->build)
425         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
427     sp_object_read_attr(object, "cx");
428     sp_object_read_attr(object, "cy");
429     sp_object_read_attr(object, "rx");
430     sp_object_read_attr(object, "ry");
433 static Inkscape::XML::Node *
434 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
436     SPGenericEllipse *ellipse;
438     ellipse = SP_GENERICELLIPSE(object);
440     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
441         repr = xml_doc->createElement("svg:ellipse");
442     }
444     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
445     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
446     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
447     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
449     if (((SPObjectClass *) ellipse_parent_class)->write)
450         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
452     return repr;
455 static void
456 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
458     SPGenericEllipse *ellipse;
460     ellipse = SP_GENERICELLIPSE(object);
462     switch (key) {
463         case SP_ATTR_CX:
464             ellipse->cx.readOrUnset(value);
465             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
466             break;
467         case SP_ATTR_CY:
468             ellipse->cy.readOrUnset(value);
469             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
470             break;
471         case SP_ATTR_RX:
472             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
473                 ellipse->rx.unset();
474             }
475             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
476             break;
477         case SP_ATTR_RY:
478             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
479                 ellipse->ry.unset();
480             }
481             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
482             break;
483         default:
484             if (((SPObjectClass *) ellipse_parent_class)->set)
485                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
486             break;
487     }
490 static gchar *sp_ellipse_description(SPItem */*item*/)
492     return g_strdup(_("<b>Ellipse</b>"));
496 void
497 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
499     SPGenericEllipse *ge;
501     g_return_if_fail(ellipse != NULL);
502     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
504     ge = SP_GENERICELLIPSE(ellipse);
506     ge->cx.computed = x;
507     ge->cy.computed = y;
508     ge->rx.computed = rx;
509     ge->ry.computed = ry;
511     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
514 /* SVG <circle> element */
516 static void sp_circle_class_init(SPCircleClass *klass);
517 static void sp_circle_init(SPCircle *circle);
519 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
520 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
521 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
522 static gchar *sp_circle_description(SPItem *item);
524 static SPGenericEllipseClass *circle_parent_class;
526 GType
527 sp_circle_get_type(void)
529     static GType type = 0;
530     if (!type) {
531         GTypeInfo info = {
532             sizeof(SPCircleClass),
533             NULL,   /* base_init */
534             NULL,   /* base_finalize */
535             (GClassInitFunc) sp_circle_class_init,
536             NULL,   /* class_finalize */
537             NULL,   /* class_data */
538             sizeof(SPCircle),
539             16,   /* n_preallocs */
540             (GInstanceInitFunc) sp_circle_init,
541             NULL,   /* value_table */
542         };
543         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
544     }
545     return type;
548 static void
549 sp_circle_class_init(SPCircleClass *klass)
551     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
552     SPItemClass *item_class = (SPItemClass *) klass;
554     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
556     sp_object_class->build = sp_circle_build;
557     sp_object_class->write = sp_circle_write;
558     sp_object_class->set = sp_circle_set;
560     item_class->description = sp_circle_description;
563 static void
564 sp_circle_init(SPCircle */*circle*/)
566     /* Nothing special */
569 static void
570 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
572     if (((SPObjectClass *) circle_parent_class)->build)
573         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
575     sp_object_read_attr(object, "cx");
576     sp_object_read_attr(object, "cy");
577     sp_object_read_attr(object, "r");
580 static Inkscape::XML::Node *
581 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
583     SPGenericEllipse *ellipse;
585     ellipse = SP_GENERICELLIPSE(object);
587     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
588         repr = xml_doc->createElement("svg:circle");
589     }
591     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
592     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
593     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
595     if (((SPObjectClass *) circle_parent_class)->write)
596         ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
598     return repr;
601 static void
602 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
604     SPGenericEllipse *ge;
606     ge = SP_GENERICELLIPSE(object);
608     switch (key) {
609         case SP_ATTR_CX:
610             ge->cx.readOrUnset(value);
611             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
612             break;
613         case SP_ATTR_CY:
614             ge->cy.readOrUnset(value);
615             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
616             break;
617         case SP_ATTR_R:
618             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
619                 ge->rx.unset();
620             }
621             ge->ry = ge->rx;
622             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
623             break;
624         default:
625             if (((SPObjectClass *) circle_parent_class)->set)
626                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
627             break;
628     }
631 static gchar *sp_circle_description(SPItem */*item*/)
633     return g_strdup(_("<b>Circle</b>"));
636 /* <path sodipodi:type="arc"> element */
638 static void sp_arc_class_init(SPArcClass *klass);
639 static void sp_arc_init(SPArc *arc);
641 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
642 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
643 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
644 static void sp_arc_modified(SPObject *object, guint flags);
646 static gchar *sp_arc_description(SPItem *item);
648 static SPGenericEllipseClass *arc_parent_class;
650 GType
651 sp_arc_get_type(void)
653     static GType type = 0;
654     if (!type) {
655         GTypeInfo info = {
656             sizeof(SPArcClass),
657             NULL,   /* base_init */
658             NULL,   /* base_finalize */
659             (GClassInitFunc) sp_arc_class_init,
660             NULL,   /* class_finalize */
661             NULL,   /* class_data */
662             sizeof(SPArc),
663             16,   /* n_preallocs */
664             (GInstanceInitFunc) sp_arc_init,
665             NULL,   /* value_table */
666         };
667         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
668     }
669     return type;
672 static void
673 sp_arc_class_init(SPArcClass *klass)
675     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
676     SPItemClass *item_class = (SPItemClass *) klass;
678     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
680     sp_object_class->build = sp_arc_build;
681     sp_object_class->write = sp_arc_write;
682     sp_object_class->set = sp_arc_set;
683     sp_object_class->modified = sp_arc_modified;
685     item_class->description = sp_arc_description;
688 static void
689 sp_arc_init(SPArc */*arc*/)
691     /* Nothing special */
694 static void
695 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
697     if (((SPObjectClass *) arc_parent_class)->build)
698         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
700     Inkscape::Version version = sp_object_get_sodipodi_version(object);
702     sp_object_read_attr(object, "sodipodi:cx");
703     sp_object_read_attr(object, "sodipodi:cy");
704     sp_object_read_attr(object, "sodipodi:rx");
705     sp_object_read_attr(object, "sodipodi:ry");
707     sp_object_read_attr(object, "sodipodi:start");
708     sp_object_read_attr(object, "sodipodi:end");
709     sp_object_read_attr(object, "sodipodi:open");
712 /*
713  * sp_arc_set_elliptical_path_attribute:
714  *
715  * Convert center to endpoint parameterization and set it to repr.
716  *
717  * See SVG 1.0 Specification W3C Recommendation
718  * ``F.6 Ellptical arc implementation notes'' for more detail.
719  */
720 static gboolean
721 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
723     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
725     Inkscape::SVG::PathString str;
727     Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
728     Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
729     double rx = ge->rx.computed;
730     double ry = ge->ry.computed;
732     str.moveTo(p1);
734     double dt = fmod(ge->end - ge->start, SP_2PI);
735     if (fabs(dt) < 1e-6) {
736         Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
737         str.arcTo(rx, ry, 0, true, true, ph)
738            .arcTo(rx, ry, 0, true, true, p2)
739            .closePath();
740     } else {
741         bool fa = (fabs(dt) > M_PI);
742         bool fs = (dt > 0);
743         str.arcTo(rx, ry, 0, fa, fs, p2);
744         if (ge->closed) {
745             Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
746             str.lineTo(center).closePath();
747         }
748     }
750     repr->setAttribute("d", str.c_str());
751     return true;
754 static Inkscape::XML::Node *
755 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
757     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
758     SPArc *arc = SP_ARC(object);
760     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
761         repr = xml_doc->createElement("svg:path");
762     }
764     if (flags & SP_OBJECT_WRITE_EXT) {
765         repr->setAttribute("sodipodi:type", "arc");
766         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
767         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
768         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
769         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
771         // write start and end only if they are non-trivial; otherwise remove
772         gdouble len = fmod(ge->end - ge->start, SP_2PI);
773         if (len < 0.0) len += SP_2PI;
774         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
775             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
776             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
777             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
778         } else {
779             repr->setAttribute("sodipodi:end", NULL);
780             repr->setAttribute("sodipodi:start", NULL);
781             repr->setAttribute("sodipodi:open", NULL);
782         }
783     }
785     // write d=
786     sp_arc_set_elliptical_path_attribute(arc, repr);
788     if (((SPObjectClass *) arc_parent_class)->write)
789         ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
791     return repr;
794 static void
795 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
797     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
799     switch (key) {
800         case SP_ATTR_SODIPODI_CX:
801             ge->cx.readOrUnset(value);
802             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
803             break;
804         case SP_ATTR_SODIPODI_CY:
805             ge->cy.readOrUnset(value);
806             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
807             break;
808         case SP_ATTR_SODIPODI_RX:
809             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
810                 ge->rx.unset();
811             }
812             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
813             break;
814         case SP_ATTR_SODIPODI_RY:
815             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
816                 ge->ry.unset();
817             }
818             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
819             break;
820         case SP_ATTR_SODIPODI_START:
821             if (value) {
822                 sp_svg_number_read_d(value, &ge->start);
823             } else {
824                 ge->start = 0;
825             }
826             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
827             break;
828         case SP_ATTR_SODIPODI_END:
829             if (value) {
830                 sp_svg_number_read_d(value, &ge->end);
831             } else {
832                 ge->end = 2 * M_PI;
833             }
834             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
835             break;
836         case SP_ATTR_SODIPODI_OPEN:
837             ge->closed = (!value);
838             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
839             break;
840         default:
841             if (((SPObjectClass *) arc_parent_class)->set)
842                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
843             break;
844     }
847 static void
848 sp_arc_modified(SPObject *object, guint flags)
850     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
851         sp_shape_set_shape((SPShape *) object);
852     }
854     if (((SPObjectClass *) arc_parent_class)->modified)
855         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
858 static gchar *sp_arc_description(SPItem *item)
860     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
862     gdouble len = fmod(ge->end - ge->start, SP_2PI);
863     if (len < 0.0) len += SP_2PI;
864     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
865         if (ge->closed) {
866             return g_strdup(_("<b>Segment</b>"));
867         } else {
868             return g_strdup(_("<b>Arc</b>"));
869         }
870     } else {
871         return g_strdup(_("<b>Ellipse</b>"));
872     }
875 void
876 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
878     g_return_if_fail(arc != NULL);
879     g_return_if_fail(SP_IS_ARC(arc));
881     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
883     ge->cx.computed = x;
884     ge->cy.computed = y;
885     ge->rx.computed = rx;
886     ge->ry.computed = ry;
887     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
888     if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
889         ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0);
890     if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
891         ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0);
892     if (!prefs->getBool("/tools/shapes/arc/open"))
893         ge->closed = 1;
894     else
895         ge->closed = 0;
897     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
900 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
902     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
904     return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
905                      ge->ry.computed * sin(arg) + ge->cy.computed);
909 /*
910   Local Variables:
911   mode:c++
912   c-file-style:"stroustrup"
913   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
914   indent-tabs-mode:nil
915   fill-column:99
916   End:
917 */
918 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :