Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / sp-ellipse.cpp
1 /*
2  * SVG <ellipse> and related implementations
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   Mitsuru Oka
7  *   bulia byak <buliabyak@users.sf.net>
8  *   Abhishek Sharma
9  *
10  * Copyright (C) 1999-2002 Lauris Kaplinski
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
21 #include "libnr/nr-matrix-fns.h"
22 #include "svg/svg.h"
23 #include "svg/path-string.h"
24 #include "xml/repr.h"
25 #include "attributes.h"
26 #include "style.h"
27 #include "display/curve.h"
28 #include <glibmm/i18n.h>
29 #include <2geom/transforms.h>
30 #include <2geom/pathvector.h>
31 #include "document.h"
32 #include "sp-ellipse.h"
33 #include "preferences.h"
34 #include "snap-candidate.h"
36 /* Common parent class */
38 #define noELLIPSE_VERBOSE
40 #ifndef M_PI
41 #define M_PI 3.14159265358979323846
42 #endif
44 #define SP_2PI (2 * M_PI)
46 #if 1
47 /* Hmmm... shouldn't this also qualify */
48 /* Whether it is faster or not, well, nobody knows */
49 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
50 #else
51 /* we do not use C99 round(3) function yet */
52 static double sp_round(double x, double y)
53 {
54     double remain;
56     g_assert(y > 0.0);
58     /* return round(x/y) * y; */
60     remain = fmod(x, y);
62     if (remain >= 0.5*y)
63         return x - remain + y;
64     else
65         return x - remain;
66 }
67 #endif
69 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
70 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
72 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
74 static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
76 static void sp_genericellipse_set_shape(SPShape *shape);
77 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
79 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
80                                                     guint flags);
82 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
84 static SPShapeClass *ge_parent_class;
86 GType
87 sp_genericellipse_get_type(void)
88 {
89     static GType type = 0;
90     if (!type) {
91         GTypeInfo info = {
92             sizeof(SPGenericEllipseClass),
93             NULL,   /* base_init */
94             NULL,   /* base_finalize */
95             (GClassInitFunc) sp_genericellipse_class_init,
96             NULL,   /* class_finalize */
97             NULL,   /* class_data */
98             sizeof(SPGenericEllipse),
99             16,   /* n_preallocs */
100             (GInstanceInitFunc) sp_genericellipse_init,
101             NULL,   /* value_table */
102         };
103         type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
104     }
105     return type;
108 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
110     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
111     SPItemClass *item_class = (SPItemClass *) klass;
112     SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass;
113     SPShapeClass *shape_class = (SPShapeClass *) klass;
115     ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
117     sp_object_class->update = sp_genericellipse_update;
118     sp_object_class->write = sp_genericellipse_write;
120     item_class->snappoints = sp_genericellipse_snappoints;
122     shape_class->set_shape = sp_genericellipse_set_shape;
123     lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect;
126 static void
127 sp_genericellipse_init(SPGenericEllipse *ellipse)
129     ellipse->cx.unset();
130     ellipse->cy.unset();
131     ellipse->rx.unset();
132     ellipse->ry.unset();
134     ellipse->start = 0.0;
135     ellipse->end = SP_2PI;
136     ellipse->closed = TRUE;
139 static void
140 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
142     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
143         SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
144         SPStyle const *style = object->style;
145         Geom::OptRect viewbox = ((SPItemCtx const *) ctx)->vp;
146         if (viewbox) {
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             static_cast<SPShape *>(object)->setShape();
157         }
158     }
160     if (((SPObjectClass *) ge_parent_class)->update)
161         ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
164 static void
165 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
167     SPShape *shape = (SPShape *) lpeitem;
168     sp_genericellipse_set_shape(shape);
170     if (write) {
171         Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
172         if ( shape->curve != NULL ) {
173             gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
174             repr->setAttribute("d", str);
175             g_free(str);
176         } else {
177             repr->setAttribute("d", NULL);
178         }
179     }
181     ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
184 /* fixme: Think (Lauris) */
185 /* Can't we use arcto in this method? */
186 static void sp_genericellipse_set_shape(SPShape *shape)
188     if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) {
189         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");
190         if (SP_OBJECT_REPR(shape)->attribute("d")) {
191             // unconditionally read the curve from d, if any, to preserve appearance
192             Geom::PathVector pv = sp_svg_read_pathv(SP_OBJECT_REPR(shape)->attribute("d"));
193             SPCurve *cold = new SPCurve(pv);
194             shape->setCurveInsync( cold, TRUE);
195             cold->unref();
196         }
197         return;
198     }
200     double rx, ry, s, e;
201     double x0, y0, x1, y1, x2, y2, x3, y3;
202     double len;
203     gint slice = FALSE;
204  //   gint i;
206     SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
208     if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
209     if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
211     sp_genericellipse_normalize(ellipse);
213     rx = ellipse->rx.computed;
214     ry = ellipse->ry.computed;
216     // figure out if we have a slice, guarding against rounding errors
217     len = fmod(ellipse->end - ellipse->start, SP_2PI);
218     if (len < 0.0) len += SP_2PI;
219     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
220         slice = FALSE;
221         ellipse->end = ellipse->start + SP_2PI;
222     } else {
223         slice = TRUE;
224     }
226     SPCurve * curve = new SPCurve();
227     curve->moveto(cos(ellipse->start), sin(ellipse->start));
229     for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
230         e = s + M_PI_2;
231         if (e > ellipse->end)
232             e = ellipse->end;
233         len = 4*tan((e - s)/4)/3;
234         x0 = cos(s);
235         y0 = sin(s);
236         x1 = x0 + len * cos(s + M_PI_2);
237         y1 = y0 + len * sin(s + M_PI_2);
238         x3 = cos(e);
239         y3 = sin(e);
240         x2 = x3 + len * cos(e - M_PI_2);
241         y2 = y3 + len * sin(e - M_PI_2);
242 #ifdef ELLIPSE_VERBOSE
243         g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
244                 i, s, e, x1, y1, x2, y2, x3, y3);
245 #endif
246         curve->curveto(x1,y1, x2,y2, x3,y3);
247     }
249     if (slice && ellipse->closed) {  // TODO: is this check for "ellipse->closed" necessary?
250         curve->lineto(0., 0.);
251     }
252     if (ellipse->closed) {
253         curve->closepath();
254     }
256     Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
257     curve->transform(aff);
259     /* Reset the shape'scurve to the "original_curve"
260      * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
261     shape->setCurveInsync( curve, TRUE);
262     if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
263         SPCurve *c_lpe = curve->copy();
264         bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
265         if (success) {
266             shape->setCurveInsync( c_lpe, TRUE);
267         }
268         c_lpe->unref();
269     }
270     curve->unref();
273 static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
275     g_assert(item != NULL);
276     g_assert(SP_IS_GENERICELLIPSE(item));
278     // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
279     if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
280         return;
281     }
283     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
284     sp_genericellipse_normalize(ellipse);
285     Geom::Matrix const i2d = item->i2d_affine();
287     // figure out if we have a slice, while guarding against rounding errors
288     bool slice = false;
289     double len = fmod(ellipse->end - ellipse->start, SP_2PI);
290     if (len < 0.0) len += SP_2PI;
291     if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
292         slice = false;
293         ellipse->end = ellipse->start + SP_2PI;
294     } else {
295         slice = true;
296     }
298     double rx = ellipse->rx.computed;
299     double ry = ellipse->ry.computed;
300     double cx = ellipse->cx.computed;
301     double cy = ellipse->cy.computed;
303     Geom::Point pt;
305     // Snap to the 4 quadrant points of the ellipse, but only if the arc
306     // spans far enough to include them
307     if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this?
308         double angle = 0;
309         for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
310             if (angle >= ellipse->start && angle <= ellipse->end) {
311                 pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
312                 p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT));
313             }
314         }
315     }
317     // Add the centre, if we have a closed slice or when explicitly asked for
318     if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) {
319         pt = Geom::Point(cx, cy) * i2d;
320         p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_CENTER, Inkscape::SNAPTARGET_CENTER));
321     }
323     // And if we have a slice, also snap to the endpoints
324     if (snapprefs->getSnapToItemNode() && slice) {
325         // Add the start point, if it's not coincident with a quadrant point
326         if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
327             pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
328             p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
329         }
330         // Add the end point, if it's not coincident with a quadrant point
331         if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
332             pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
333             p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
334         }
335     }
338 void
339 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
341     ellipse->start = fmod(ellipse->start, SP_2PI);
342     ellipse->end = fmod(ellipse->end, SP_2PI);
344     if (ellipse->start < 0.0)
345         ellipse->start += SP_2PI;
346     double diff = ellipse->start - ellipse->end;
347     if (diff >= 0.0)
348         ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
350     /* Now we keep: 0 <= start < end <= 2*PI */
353 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
355     SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
357     if (flags & SP_OBJECT_WRITE_EXT) {
358         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
359             repr = xml_doc->createElement("svg:path");
360         }
362         sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
363         sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
364         sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
365         sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
367         if (SP_IS_ARC(ellipse))
368             sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
369     }
371     if (((SPObjectClass *) ge_parent_class)->write)
372         ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
374     return repr;
377 /* SVG <ellipse> element */
379 static void sp_ellipse_class_init(SPEllipseClass *klass);
380 static void sp_ellipse_init(SPEllipse *ellipse);
382 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
383 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
384 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
385 static gchar *sp_ellipse_description(SPItem *item);
387 static SPGenericEllipseClass *ellipse_parent_class;
389 GType
390 sp_ellipse_get_type(void)
392     static GType type = 0;
393     if (!type) {
394         GTypeInfo info = {
395             sizeof(SPEllipseClass),
396             NULL,   /* base_init */
397             NULL,   /* base_finalize */
398             (GClassInitFunc) sp_ellipse_class_init,
399             NULL,   /* class_finalize */
400             NULL,   /* class_data */
401             sizeof(SPEllipse),
402             16,   /* n_preallocs */
403             (GInstanceInitFunc) sp_ellipse_init,
404             NULL,   /* value_table */
405         };
406         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
407     }
408     return type;
411 static void sp_ellipse_class_init(SPEllipseClass *klass)
413     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
414     SPItemClass *item_class = (SPItemClass *) klass;
416     ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
418     sp_object_class->build = sp_ellipse_build;
419     sp_object_class->write = sp_ellipse_write;
420     sp_object_class->set = sp_ellipse_set;
422     item_class->description = sp_ellipse_description;
425 static void
426 sp_ellipse_init(SPEllipse */*ellipse*/)
428     /* Nothing special */
431 static void
432 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
434     if (((SPObjectClass *) ellipse_parent_class)->build)
435         (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
437     object->readAttr( "cx" );
438     object->readAttr( "cy" );
439     object->readAttr( "rx" );
440     object->readAttr( "ry" );
443 static Inkscape::XML::Node *
444 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
446     SPGenericEllipse *ellipse;
448     ellipse = SP_GENERICELLIPSE(object);
450     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
451         repr = xml_doc->createElement("svg:ellipse");
452     }
454     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
455     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
456     sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
457     sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
459     if (((SPObjectClass *) ellipse_parent_class)->write)
460         (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
462     return repr;
465 static void
466 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
468     SPGenericEllipse *ellipse;
470     ellipse = SP_GENERICELLIPSE(object);
472     switch (key) {
473         case SP_ATTR_CX:
474             ellipse->cx.readOrUnset(value);
475             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
476             break;
477         case SP_ATTR_CY:
478             ellipse->cy.readOrUnset(value);
479             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
480             break;
481         case SP_ATTR_RX:
482             if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
483                 ellipse->rx.unset();
484             }
485             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
486             break;
487         case SP_ATTR_RY:
488             if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
489                 ellipse->ry.unset();
490             }
491             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
492             break;
493         default:
494             if (((SPObjectClass *) ellipse_parent_class)->set)
495                 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
496             break;
497     }
500 static gchar *sp_ellipse_description(SPItem */*item*/)
502     return g_strdup(_("<b>Ellipse</b>"));
506 void
507 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
509     SPGenericEllipse *ge;
511     g_return_if_fail(ellipse != NULL);
512     g_return_if_fail(SP_IS_ELLIPSE(ellipse));
514     ge = SP_GENERICELLIPSE(ellipse);
516     ge->cx.computed = x;
517     ge->cy.computed = y;
518     ge->rx.computed = rx;
519     ge->ry.computed = ry;
521     ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
524 /* SVG <circle> element */
526 static void sp_circle_class_init(SPCircleClass *klass);
527 static void sp_circle_init(SPCircle *circle);
529 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
530 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
531 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
532 static gchar *sp_circle_description(SPItem *item);
534 static SPGenericEllipseClass *circle_parent_class;
536 GType
537 sp_circle_get_type(void)
539     static GType type = 0;
540     if (!type) {
541         GTypeInfo info = {
542             sizeof(SPCircleClass),
543             NULL,   /* base_init */
544             NULL,   /* base_finalize */
545             (GClassInitFunc) sp_circle_class_init,
546             NULL,   /* class_finalize */
547             NULL,   /* class_data */
548             sizeof(SPCircle),
549             16,   /* n_preallocs */
550             (GInstanceInitFunc) sp_circle_init,
551             NULL,   /* value_table */
552         };
553         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
554     }
555     return type;
558 static void
559 sp_circle_class_init(SPCircleClass *klass)
561     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
562     SPItemClass *item_class = (SPItemClass *) klass;
564     circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
566     sp_object_class->build = sp_circle_build;
567     sp_object_class->write = sp_circle_write;
568     sp_object_class->set = sp_circle_set;
570     item_class->description = sp_circle_description;
573 static void
574 sp_circle_init(SPCircle */*circle*/)
576     /* Nothing special */
579 static void
580 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
582     if (((SPObjectClass *) circle_parent_class)->build)
583         (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
585     object->readAttr( "cx" );
586     object->readAttr( "cy" );
587     object->readAttr( "r" );
590 static Inkscape::XML::Node *
591 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
593     SPGenericEllipse *ellipse;
595     ellipse = SP_GENERICELLIPSE(object);
597     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
598         repr = xml_doc->createElement("svg:circle");
599     }
601     sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
602     sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
603     sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
605     if (((SPObjectClass *) circle_parent_class)->write)
606         ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
608     return repr;
611 static void
612 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
614     SPGenericEllipse *ge;
616     ge = SP_GENERICELLIPSE(object);
618     switch (key) {
619         case SP_ATTR_CX:
620             ge->cx.readOrUnset(value);
621             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
622             break;
623         case SP_ATTR_CY:
624             ge->cy.readOrUnset(value);
625             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
626             break;
627         case SP_ATTR_R:
628             if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
629                 ge->rx.unset();
630             }
631             ge->ry = ge->rx;
632             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
633             break;
634         default:
635             if (((SPObjectClass *) circle_parent_class)->set)
636                 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
637             break;
638     }
641 static gchar *sp_circle_description(SPItem */*item*/)
643     return g_strdup(_("<b>Circle</b>"));
646 /* <path sodipodi:type="arc"> element */
648 static void sp_arc_class_init(SPArcClass *klass);
649 static void sp_arc_init(SPArc *arc);
651 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
652 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
653 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
654 static void sp_arc_modified(SPObject *object, guint flags);
656 static gchar *sp_arc_description(SPItem *item);
658 static SPGenericEllipseClass *arc_parent_class;
660 GType
661 sp_arc_get_type(void)
663     static GType type = 0;
664     if (!type) {
665         GTypeInfo info = {
666             sizeof(SPArcClass),
667             NULL,   /* base_init */
668             NULL,   /* base_finalize */
669             (GClassInitFunc) sp_arc_class_init,
670             NULL,   /* class_finalize */
671             NULL,   /* class_data */
672             sizeof(SPArc),
673             16,   /* n_preallocs */
674             (GInstanceInitFunc) sp_arc_init,
675             NULL,   /* value_table */
676         };
677         type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
678     }
679     return type;
682 static void
683 sp_arc_class_init(SPArcClass *klass)
685     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
686     SPItemClass *item_class = (SPItemClass *) klass;
688     arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
690     sp_object_class->build = sp_arc_build;
691     sp_object_class->write = sp_arc_write;
692     sp_object_class->set = sp_arc_set;
693     sp_object_class->modified = sp_arc_modified;
695     item_class->description = sp_arc_description;
698 static void
699 sp_arc_init(SPArc */*arc*/)
701     /* Nothing special */
704 static void
705 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
707     if (((SPObjectClass *) arc_parent_class)->build)
708         (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
710     object->readAttr( "sodipodi:cx" );
711     object->readAttr( "sodipodi:cy" );
712     object->readAttr( "sodipodi:rx" );
713     object->readAttr( "sodipodi:ry" );
715     object->readAttr( "sodipodi:start" );
716     object->readAttr( "sodipodi:end" );
717     object->readAttr( "sodipodi:open" );
720 /*
721  * sp_arc_set_elliptical_path_attribute:
722  *
723  * Convert center to endpoint parameterization and set it to repr.
724  *
725  * See SVG 1.0 Specification W3C Recommendation
726  * ``F.6 Ellptical arc implementation notes'' for more detail.
727  */
728 static gboolean
729 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
731     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
733     Inkscape::SVG::PathString str;
735     Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
736     Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
737     double rx = ge->rx.computed;
738     double ry = ge->ry.computed;
740     str.moveTo(p1);
742     double dt = fmod(ge->end - ge->start, SP_2PI);
743     if (fabs(dt) < 1e-6) {
744         Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
745         str.arcTo(rx, ry, 0, true, true, ph)
746            .arcTo(rx, ry, 0, true, true, p2)
747            .closePath();
748     } else {
749         bool fa = (fabs(dt) > M_PI);
750         bool fs = (dt > 0);
751         str.arcTo(rx, ry, 0, fa, fs, p2);
752         if (ge->closed) {
753             Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
754             str.lineTo(center).closePath();
755         }
756     }
758     repr->setAttribute("d", str.c_str());
759     return true;
762 static Inkscape::XML::Node *
763 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
765     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
766     SPArc *arc = SP_ARC(object);
768     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
769         repr = xml_doc->createElement("svg:path");
770     }
772     if (flags & SP_OBJECT_WRITE_EXT) {
773         repr->setAttribute("sodipodi:type", "arc");
774         sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
775         sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
776         sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
777         sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
779         // write start and end only if they are non-trivial; otherwise remove
780         gdouble len = fmod(ge->end - ge->start, SP_2PI);
781         if (len < 0.0) len += SP_2PI;
782         if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
783             sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
784             sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
785             repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
786         } else {
787             repr->setAttribute("sodipodi:end", NULL);
788             repr->setAttribute("sodipodi:start", NULL);
789             repr->setAttribute("sodipodi:open", NULL);
790         }
791     }
793     // write d=
794     sp_arc_set_elliptical_path_attribute(arc, repr);
796     if (((SPObjectClass *) arc_parent_class)->write)
797         ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
799     return repr;
802 static void
803 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
805     SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
807     switch (key) {
808         case SP_ATTR_SODIPODI_CX:
809             ge->cx.readOrUnset(value);
810             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
811             break;
812         case SP_ATTR_SODIPODI_CY:
813             ge->cy.readOrUnset(value);
814             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
815             break;
816         case SP_ATTR_SODIPODI_RX:
817             if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
818                 ge->rx.unset();
819             }
820             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
821             break;
822         case SP_ATTR_SODIPODI_RY:
823             if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
824                 ge->ry.unset();
825             }
826             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
827             break;
828         case SP_ATTR_SODIPODI_START:
829             if (value) {
830                 sp_svg_number_read_d(value, &ge->start);
831             } else {
832                 ge->start = 0;
833             }
834             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
835             break;
836         case SP_ATTR_SODIPODI_END:
837             if (value) {
838                 sp_svg_number_read_d(value, &ge->end);
839             } else {
840                 ge->end = 2 * M_PI;
841             }
842             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
843             break;
844         case SP_ATTR_SODIPODI_OPEN:
845             ge->closed = (!value);
846             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
847             break;
848         default:
849             if (((SPObjectClass *) arc_parent_class)->set)
850                 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
851             break;
852     }
855 static void
856 sp_arc_modified(SPObject *object, guint flags)
858     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
859         ((SPShape *) object)->setShape();
860     }
862     if (((SPObjectClass *) arc_parent_class)->modified)
863         ((SPObjectClass *) arc_parent_class)->modified(object, flags);
866 static gchar *sp_arc_description(SPItem *item)
868     SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
870     gdouble len = fmod(ge->end - ge->start, SP_2PI);
871     if (len < 0.0) len += SP_2PI;
872     if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
873         if (ge->closed) {
874             return g_strdup(_("<b>Segment</b>"));
875         } else {
876             return g_strdup(_("<b>Arc</b>"));
877         }
878     } else {
879         return g_strdup(_("<b>Ellipse</b>"));
880     }
883 void
884 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
886     g_return_if_fail(arc != NULL);
887     g_return_if_fail(SP_IS_ARC(arc));
889     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
891     ge->cx.computed = x;
892     ge->cy.computed = y;
893     ge->rx.computed = rx;
894     ge->ry.computed = ry;
895     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
896     // those pref values are in degrees, while we want radians
897     if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
898         ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0) * M_PI / 180;
899     if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
900         ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0) * M_PI / 180;
901     if (!prefs->getBool("/tools/shapes/arc/open"))
902         ge->closed = 1;
903     else
904         ge->closed = 0;
906     ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
909 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
911     SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
913     return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
914                      ge->ry.computed * sin(arg) + ge->cy.computed);
918 /*
919   Local Variables:
920   mode:c++
921   c-file-style:"stroustrup"
922   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
923   indent-tabs-mode:nil
924   fill-column:99
925   End:
926 */
927 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :