Code

A simple layout document as to what, why and how is cppification.
[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>
31 #include <2geom/pathvector.h>
32 #include "document.h"
33 #include "sp-ellipse.h"
34 #include "preferences.h"
35 #include "snap-candidate.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, std::vector<Inkscape::SnapCandidatePoint> &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         ((SPShape *) object)->setShape();
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);
183 /* fixme: Think (Lauris) */
184 /* Can't we use arcto in this method? */
185 static void sp_genericellipse_set_shape(SPShape *shape)
187     if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) {
188         g_warning ("The ellipse shape has unknown LPE on it! Convert to path to make it editable preserving the appearance; editing it as ellipse will remove the bad LPE");
189         if (SP_OBJECT_REPR(shape)->attribute("d")) {
190             // unconditionally read the curve from d, if any, to preserve appearance
191             Geom::PathVector pv = sp_svg_read_pathv(SP_OBJECT_REPR(shape)->attribute("d"));
192             SPCurve *cold = new SPCurve(pv);
193             shape->setCurveInsync( cold, TRUE);
194             cold->unref();
195         }
196         return;
197     }
199     double rx, ry, s, e;
200     double x0, y0, x1, y1, x2, y2, x3, y3;
201     double len;
202     gint slice = FALSE;
203  //   gint i;
205     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
207     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
208     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
210     sp_genericellipse_normalize(ellipse);
212     rx = ellipse->rx.computed;
213     ry = ellipse->ry.computed;
215     // figure out if we have a slice, guarding against rounding errors
216     len = fmod(ellipse->end - ellipse->start, SP_2PI);
217     if (len < 0.0) len += SP_2PI;
218     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
219         slice = FALSE;
220         ellipse->end = ellipse->start + SP_2PI;
221     } else {
222         slice = TRUE;
223     }
225     SPCurve * curve = new SPCurve();
226     curve->moveto(cos(ellipse->start), sin(ellipse->start));
228     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
229         e = s + M_PI_2;
230         if (e > ellipse->end)
231             e = ellipse->end;
232         len = 4*tan((e - s)/4)/3;
233         x0 = cos(s);
234         y0 = sin(s);
235         x1 = x0 + len * cos(s + M_PI_2);
236         y1 = y0 + len * sin(s + M_PI_2);
237         x3 = cos(e);
238         y3 = sin(e);
239         x2 = x3 + len * cos(e - M_PI_2);
240         y2 = y3 + len * sin(e - M_PI_2);
241 #ifdef ELLIPSE_VERBOSE
242         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
243                 i, s, e, x1, y1, x2, y2, x3, y3);
244 #endif
245         curve->curveto(x1,y1, x2,y2, x3,y3);
246     }
248     if (slice && ellipse->closed) {  // TODO: is this check for "ellipse->closed" necessary?
249         curve->lineto(0., 0.);
250     }
251     if (ellipse->closed) {
252         curve->closepath();
253     }
255     Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
256     curve->transform(aff);
258     /* Reset the shape'scurve to the "original_curve"
259      * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
260     shape->setCurveInsync( curve, TRUE);
261     if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
262         SPCurve *c_lpe = curve->copy();
263         bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
264         if (success) {
265             shape->setCurveInsync( c_lpe, TRUE);
266         }
267         c_lpe->unref();
268     }
269     curve->unref();
272 static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
274     g_assert(item != NULL);
275     g_assert(SP_IS_GENERICELLIPSE(item));
277     // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
278     if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
279         return;
280     }
282     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
283     sp_genericellipse_normalize(ellipse);
284     Geom::Matrix const i2d = item->i2d_affine();
286     // figure out if we have a slice, while guarding against rounding errors
287     bool slice = false;
288     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
289     if (len < 0.0) len += SP_2PI;
290     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
291         slice = false;
292         ellipse->end = ellipse->start + SP_2PI;
293     } else {
294         slice = true;
295     }
297     double rx = ellipse->rx.computed;
298     double ry = ellipse->ry.computed;
299     double cx = ellipse->cx.computed;
300     double cy = ellipse->cy.computed;
302     Geom::Point pt;
304     // Snap to the 4 quadrant points of the ellipse, but only if the arc
305     // spans far enough to include them
306     if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this?
307         double angle = 0;
308         for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
309             if (angle >= ellipse->start && angle <= ellipse->end) {
310                 pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
311                 p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT));
312             }
313         }
314     }
316     // Add the centre, if we have a closed slice or when explicitly asked for
317     if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) {
318         pt = Geom::Point(cx, cy) * i2d;
319         p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_CENTER, Inkscape::SNAPTARGET_CENTER));
320     }
322     // And if we have a slice, also snap to the endpoints
323     if (snapprefs->getSnapToItemNode() && slice) {
324         // Add the start point, if it's not coincident with a quadrant point
325         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
326             pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
327             p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
328         }
329         // Add the end point, if it's not coincident with a quadrant point
330         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
331             pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
332             p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
333         }
334     }
337 void
338 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
340     ellipse->start = fmod(ellipse->start, SP_2PI);
341     ellipse->end = fmod(ellipse->end, SP_2PI);
343     if (ellipse->start < 0.0)
344         ellipse->start += SP_2PI;
345     double diff = ellipse->start - ellipse->end;
346     if (diff >= 0.0)
347         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
349     /* Now we keep: 0 <= start < end <= 2*PI */
352 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
354     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
356     if (flags & SP_OBJECT_WRITE_EXT) {
357         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
358             repr = xml_doc->createElement("svg:path");
359         }
361         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
362         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
363         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
364         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
366         if (SP_IS_ARC(ellipse))
367             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
368     }
370     if (((SPObjectClass *) ge_parent_class)->write)
371         ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
373     return repr;
376 /* SVG <ellipse> element */
378 static void sp_ellipse_class_init(SPEllipseClass *klass);
379 static void sp_ellipse_init(SPEllipse *ellipse);
381 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
382 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
383 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
384 static gchar *sp_ellipse_description(SPItem *item);
386 static SPGenericEllipseClass *ellipse_parent_class;
388 GType
389 sp_ellipse_get_type(void)
391     static GType type = 0;
392     if (!type) {
393         GTypeInfo info = {
394             sizeof(SPEllipseClass),
395             NULL,   /* base_init */
396             NULL,   /* base_finalize */
397             (GClassInitFunc) sp_ellipse_class_init,
398             NULL,   /* class_finalize */
399             NULL,   /* class_data */
400             sizeof(SPEllipse),
401             16,   /* n_preallocs */
402             (GInstanceInitFunc) sp_ellipse_init,
403             NULL,   /* value_table */
404         };
405         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
406     }
407     return type;
410 static void sp_ellipse_class_init(SPEllipseClass *klass)
412     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
413     SPItemClass *item_class = (SPItemClass *) klass;
415     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
417     sp_object_class->build = sp_ellipse_build;
418     sp_object_class->write = sp_ellipse_write;
419     sp_object_class->set = sp_ellipse_set;
421     item_class->description = sp_ellipse_description;
424 static void
425 sp_ellipse_init(SPEllipse */*ellipse*/)
427     /* Nothing special */
430 static void
431 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
433     if (((SPObjectClass *) ellipse_parent_class)->build)
434         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
436     object->readAttr( "cx");
437     object->readAttr( "cy");
438     object->readAttr( "rx");
439     object->readAttr( "ry");
442 static Inkscape::XML::Node *
443 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
445     SPGenericEllipse *ellipse;
447     ellipse = SP_GENERICELLIPSE(object);
449     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
450         repr = xml_doc->createElement("svg:ellipse");
451     }
453     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
454     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
455     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
456     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
458     if (((SPObjectClass *) ellipse_parent_class)->write)
459         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
461     return repr;
464 static void
465 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
467     SPGenericEllipse *ellipse;
469     ellipse = SP_GENERICELLIPSE(object);
471     switch (key) {
472         case SP_ATTR_CX:
473             ellipse->cx.readOrUnset(value);
474             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
475             break;
476         case SP_ATTR_CY:
477             ellipse->cy.readOrUnset(value);
478             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
479             break;
480         case SP_ATTR_RX:
481             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
482                 ellipse->rx.unset();
483             }
484             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
485             break;
486         case SP_ATTR_RY:
487             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
488                 ellipse->ry.unset();
489             }
490             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
491             break;
492         default:
493             if (((SPObjectClass *) ellipse_parent_class)->set)
494                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
495             break;
496     }
499 static gchar *sp_ellipse_description(SPItem */*item*/)
501     return g_strdup(_("<b>Ellipse</b>"));
505 void
506 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
508     SPGenericEllipse *ge;
510     g_return_if_fail(ellipse != NULL);
511     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
513     ge = SP_GENERICELLIPSE(ellipse);
515     ge->cx.computed = x;
516     ge->cy.computed = y;
517     ge->rx.computed = rx;
518     ge->ry.computed = ry;
520     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
523 /* SVG <circle> element */
525 static void sp_circle_class_init(SPCircleClass *klass);
526 static void sp_circle_init(SPCircle *circle);
528 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
529 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
530 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
531 static gchar *sp_circle_description(SPItem *item);
533 static SPGenericEllipseClass *circle_parent_class;
535 GType
536 sp_circle_get_type(void)
538     static GType type = 0;
539     if (!type) {
540         GTypeInfo info = {
541             sizeof(SPCircleClass),
542             NULL,   /* base_init */
543             NULL,   /* base_finalize */
544             (GClassInitFunc) sp_circle_class_init,
545             NULL,   /* class_finalize */
546             NULL,   /* class_data */
547             sizeof(SPCircle),
548             16,   /* n_preallocs */
549             (GInstanceInitFunc) sp_circle_init,
550             NULL,   /* value_table */
551         };
552         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
553     }
554     return type;
557 static void
558 sp_circle_class_init(SPCircleClass *klass)
560     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
561     SPItemClass *item_class = (SPItemClass *) klass;
563     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
565     sp_object_class->build = sp_circle_build;
566     sp_object_class->write = sp_circle_write;
567     sp_object_class->set = sp_circle_set;
569     item_class->description = sp_circle_description;
572 static void
573 sp_circle_init(SPCircle */*circle*/)
575     /* Nothing special */
578 static void
579 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
581     if (((SPObjectClass *) circle_parent_class)->build)
582         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
584     object->readAttr( "cx");
585     object->readAttr( "cy");
586     object->readAttr( "r");
589 static Inkscape::XML::Node *
590 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
592     SPGenericEllipse *ellipse;
594     ellipse = SP_GENERICELLIPSE(object);
596     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
597         repr = xml_doc->createElement("svg:circle");
598     }
600     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
601     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
602     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
604     if (((SPObjectClass *) circle_parent_class)->write)
605         ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
607     return repr;
610 static void
611 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
613     SPGenericEllipse *ge;
615     ge = SP_GENERICELLIPSE(object);
617     switch (key) {
618         case SP_ATTR_CX:
619             ge->cx.readOrUnset(value);
620             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
621             break;
622         case SP_ATTR_CY:
623             ge->cy.readOrUnset(value);
624             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
625             break;
626         case SP_ATTR_R:
627             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
628                 ge->rx.unset();
629             }
630             ge->ry = ge->rx;
631             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
632             break;
633         default:
634             if (((SPObjectClass *) circle_parent_class)->set)
635                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
636             break;
637     }
640 static gchar *sp_circle_description(SPItem */*item*/)
642     return g_strdup(_("<b>Circle</b>"));
645 /* <path sodipodi:type="arc"> element */
647 static void sp_arc_class_init(SPArcClass *klass);
648 static void sp_arc_init(SPArc *arc);
650 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
651 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
652 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
653 static void sp_arc_modified(SPObject *object, guint flags);
655 static gchar *sp_arc_description(SPItem *item);
657 static SPGenericEllipseClass *arc_parent_class;
659 GType
660 sp_arc_get_type(void)
662     static GType type = 0;
663     if (!type) {
664         GTypeInfo info = {
665             sizeof(SPArcClass),
666             NULL,   /* base_init */
667             NULL,   /* base_finalize */
668             (GClassInitFunc) sp_arc_class_init,
669             NULL,   /* class_finalize */
670             NULL,   /* class_data */
671             sizeof(SPArc),
672             16,   /* n_preallocs */
673             (GInstanceInitFunc) sp_arc_init,
674             NULL,   /* value_table */
675         };
676         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
677     }
678     return type;
681 static void
682 sp_arc_class_init(SPArcClass *klass)
684     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
685     SPItemClass *item_class = (SPItemClass *) klass;
687     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
689     sp_object_class->build = sp_arc_build;
690     sp_object_class->write = sp_arc_write;
691     sp_object_class->set = sp_arc_set;
692     sp_object_class->modified = sp_arc_modified;
694     item_class->description = sp_arc_description;
697 static void
698 sp_arc_init(SPArc */*arc*/)
700     /* Nothing special */
703 static void
704 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
706     if (((SPObjectClass *) arc_parent_class)->build)
707         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
709     object->readAttr( "sodipodi:cx");
710     object->readAttr( "sodipodi:cy");
711     object->readAttr( "sodipodi:rx");
712     object->readAttr( "sodipodi:ry");
714     object->readAttr( "sodipodi:start");
715     object->readAttr( "sodipodi:end");
716     object->readAttr( "sodipodi:open");
719 /*
720  * sp_arc_set_elliptical_path_attribute:
721  *
722  * Convert center to endpoint parameterization and set it to repr.
723  *
724  * See SVG 1.0 Specification W3C Recommendation
725  * ``F.6 Ellptical arc implementation notes'' for more detail.
726  */
727 static gboolean
728 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
730     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
732     Inkscape::SVG::PathString str;
734     Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
735     Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
736     double rx = ge->rx.computed;
737     double ry = ge->ry.computed;
739     str.moveTo(p1);
741     double dt = fmod(ge->end - ge->start, SP_2PI);
742     if (fabs(dt) < 1e-6) {
743         Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
744         str.arcTo(rx, ry, 0, true, true, ph)
745            .arcTo(rx, ry, 0, true, true, p2)
746            .closePath();
747     } else {
748         bool fa = (fabs(dt) > M_PI);
749         bool fs = (dt > 0);
750         str.arcTo(rx, ry, 0, fa, fs, p2);
751         if (ge->closed) {
752             Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
753             str.lineTo(center).closePath();
754         }
755     }
757     repr->setAttribute("d", str.c_str());
758     return true;
761 static Inkscape::XML::Node *
762 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
764     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
765     SPArc *arc = SP_ARC(object);
767     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
768         repr = xml_doc->createElement("svg:path");
769     }
771     if (flags & SP_OBJECT_WRITE_EXT) {
772         repr->setAttribute("sodipodi:type", "arc");
773         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
774         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
775         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
776         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
778         // write start and end only if they are non-trivial; otherwise remove
779         gdouble len = fmod(ge->end - ge->start, SP_2PI);
780         if (len < 0.0) len += SP_2PI;
781         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
782             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
783             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
784             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
785         } else {
786             repr->setAttribute("sodipodi:end", NULL);
787             repr->setAttribute("sodipodi:start", NULL);
788             repr->setAttribute("sodipodi:open", NULL);
789         }
790     }
792     // write d=
793     sp_arc_set_elliptical_path_attribute(arc, repr);
795     if (((SPObjectClass *) arc_parent_class)->write)
796         ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
798     return repr;
801 static void
802 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
804     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
806     switch (key) {
807         case SP_ATTR_SODIPODI_CX:
808             ge->cx.readOrUnset(value);
809             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
810             break;
811         case SP_ATTR_SODIPODI_CY:
812             ge->cy.readOrUnset(value);
813             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
814             break;
815         case SP_ATTR_SODIPODI_RX:
816             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
817                 ge->rx.unset();
818             }
819             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
820             break;
821         case SP_ATTR_SODIPODI_RY:
822             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
823                 ge->ry.unset();
824             }
825             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
826             break;
827         case SP_ATTR_SODIPODI_START:
828             if (value) {
829                 sp_svg_number_read_d(value, &ge->start);
830             } else {
831                 ge->start = 0;
832             }
833             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
834             break;
835         case SP_ATTR_SODIPODI_END:
836             if (value) {
837                 sp_svg_number_read_d(value, &ge->end);
838             } else {
839                 ge->end = 2 * M_PI;
840             }
841             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
842             break;
843         case SP_ATTR_SODIPODI_OPEN:
844             ge->closed = (!value);
845             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
846             break;
847         default:
848             if (((SPObjectClass *) arc_parent_class)->set)
849                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
850             break;
851     }
854 static void
855 sp_arc_modified(SPObject *object, guint flags)
857     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
858         ((SPShape *) object)->setShape();
859     }
861     if (((SPObjectClass *) arc_parent_class)->modified)
862         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
865 static gchar *sp_arc_description(SPItem *item)
867     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
869     gdouble len = fmod(ge->end - ge->start, SP_2PI);
870     if (len < 0.0) len += SP_2PI;
871     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
872         if (ge->closed) {
873             return g_strdup(_("<b>Segment</b>"));
874         } else {
875             return g_strdup(_("<b>Arc</b>"));
876         }
877     } else {
878         return g_strdup(_("<b>Ellipse</b>"));
879     }
882 void
883 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
885     g_return_if_fail(arc != NULL);
886     g_return_if_fail(SP_IS_ARC(arc));
888     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
890     ge->cx.computed = x;
891     ge->cy.computed = y;
892     ge->rx.computed = rx;
893     ge->ry.computed = ry;
894     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
895     // those pref values are in degrees, while we want radians
896     if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
897         ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0) * M_PI / 180;
898     if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
899         ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0) * M_PI / 180;
900     if (!prefs->getBool("/tools/shapes/arc/open"))
901         ge->closed = 1;
902     else
903         ge->closed = 0;
905     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
908 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
910     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
912     return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
913                      ge->ry.computed * sin(arg) + ge->cy.computed);
917 /*
918   Local Variables:
919   mode:c++
920   c-file-style:"stroustrup"
921   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
922   indent-tabs-mode:nil
923   fill-column:99
924   End:
925 */
926 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :