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;
107 }
109 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
110 {
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;
125 }
127 static void
128 sp_genericellipse_init(SPGenericEllipse *ellipse)
129 {
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;
138 }
140 static void
141 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
142 {
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);
161 }
163 static void
164 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
165 {
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);
181 }
183 /* fixme: Think (Lauris) */
184 /* Can't we use arcto in this method? */
185 static void sp_genericellipse_set_shape(SPShape *shape)
186 {
187 double rx, ry, s, e;
188 double x0, y0, x1, y1, x2, y2, x3, y3;
189 double len;
190 gint slice = FALSE;
191 // gint i;
193 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
195 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
196 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
198 sp_genericellipse_normalize(ellipse);
200 rx = ellipse->rx.computed;
201 ry = ellipse->ry.computed;
203 // figure out if we have a slice, guarding against rounding errors
204 len = fmod(ellipse->end - ellipse->start, SP_2PI);
205 if (len < 0.0) len += SP_2PI;
206 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
207 slice = FALSE;
208 ellipse->end = ellipse->start + SP_2PI;
209 } else {
210 slice = TRUE;
211 }
213 SPCurve * curve = new SPCurve();
214 curve->moveto(cos(ellipse->start), sin(ellipse->start));
216 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
217 e = s + M_PI_2;
218 if (e > ellipse->end)
219 e = ellipse->end;
220 len = 4*tan((e - s)/4)/3;
221 x0 = cos(s);
222 y0 = sin(s);
223 x1 = x0 + len * cos(s + M_PI_2);
224 y1 = y0 + len * sin(s + M_PI_2);
225 x3 = cos(e);
226 y3 = sin(e);
227 x2 = x3 + len * cos(e - M_PI_2);
228 y2 = y3 + len * sin(e - M_PI_2);
229 #ifdef ELLIPSE_VERBOSE
230 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
231 i, s, e, x1, y1, x2, y2, x3, y3);
232 #endif
233 curve->curveto(x1,y1, x2,y2, x3,y3);
234 }
236 if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary?
237 curve->lineto(0., 0.);
238 }
239 if (ellipse->closed) {
240 curve->closepath();
241 }
243 Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
244 curve->transform(aff);
246 /* Reset the shape'scurve to the "original_curve"
247 * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
248 sp_shape_set_curve_insync (shape, curve, TRUE);
249 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
250 SPCurve *c_lpe = curve->copy();
251 bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
252 if (success) {
253 sp_shape_set_curve_insync (shape, c_lpe, TRUE);
254 }
255 c_lpe->unref();
256 }
257 curve->unref();
258 }
260 static void sp_genericellipse_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs)
261 {
262 g_assert(item != NULL);
263 g_assert(SP_IS_GENERICELLIPSE(item));
265 // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
266 if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
267 return;
268 }
270 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
271 sp_genericellipse_normalize(ellipse);
272 Geom::Matrix const i2d = sp_item_i2d_affine(item);
274 // figure out if we have a slice, while guarding against rounding errors
275 bool slice = false;
276 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
277 if (len < 0.0) len += SP_2PI;
278 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
279 slice = false;
280 ellipse->end = ellipse->start + SP_2PI;
281 } else {
282 slice = true;
283 }
285 double rx = ellipse->rx.computed;
286 double ry = ellipse->ry.computed;
287 double cx = ellipse->cx.computed;
288 double cy = ellipse->cy.computed;
290 Geom::Point pt;
292 // Snap to the 4 quadrant points of the ellipse, but only if the arc
293 // spans far enough to include them
294 if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this?
295 double angle = 0;
296 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
297 if (angle >= ellipse->start && angle <= ellipse->end) {
298 pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
299 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) : int(Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT)));
300 }
301 }
302 }
304 // Add the centre, if we have a closed slice or when explicitly asked for
305 if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) {
306 pt = Geom::Point(cx, cy) * i2d;
307 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_CENTER) : int(Inkscape::SNAPSOURCE_CENTER)));
308 }
310 // And if we have a slice, also snap to the endpoints
311 if (snapprefs->getSnapToItemNode() && slice) {
312 // Add the start point, if it's not coincident with a quadrant point
313 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
314 pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
315 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP)));
316 }
317 // Add the end point, if it's not coincident with a quadrant point
318 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
319 pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
320 p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP)));
321 }
322 }
323 }
325 void
326 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
327 {
328 ellipse->start = fmod(ellipse->start, SP_2PI);
329 ellipse->end = fmod(ellipse->end, SP_2PI);
331 if (ellipse->start < 0.0)
332 ellipse->start += SP_2PI;
333 double diff = ellipse->start - ellipse->end;
334 if (diff >= 0.0)
335 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
337 /* Now we keep: 0 <= start < end <= 2*PI */
338 }
340 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
341 {
342 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
344 if (flags & SP_OBJECT_WRITE_EXT) {
345 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
346 repr = xml_doc->createElement("svg:path");
347 }
349 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
350 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
351 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
352 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
354 if (SP_IS_ARC(ellipse))
355 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
356 }
358 if (((SPObjectClass *) ge_parent_class)->write)
359 ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
361 return repr;
362 }
364 /* SVG <ellipse> element */
366 static void sp_ellipse_class_init(SPEllipseClass *klass);
367 static void sp_ellipse_init(SPEllipse *ellipse);
369 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
370 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
371 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
372 static gchar *sp_ellipse_description(SPItem *item);
374 static SPGenericEllipseClass *ellipse_parent_class;
376 GType
377 sp_ellipse_get_type(void)
378 {
379 static GType type = 0;
380 if (!type) {
381 GTypeInfo info = {
382 sizeof(SPEllipseClass),
383 NULL, /* base_init */
384 NULL, /* base_finalize */
385 (GClassInitFunc) sp_ellipse_class_init,
386 NULL, /* class_finalize */
387 NULL, /* class_data */
388 sizeof(SPEllipse),
389 16, /* n_preallocs */
390 (GInstanceInitFunc) sp_ellipse_init,
391 NULL, /* value_table */
392 };
393 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
394 }
395 return type;
396 }
398 static void sp_ellipse_class_init(SPEllipseClass *klass)
399 {
400 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
401 SPItemClass *item_class = (SPItemClass *) klass;
403 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
405 sp_object_class->build = sp_ellipse_build;
406 sp_object_class->write = sp_ellipse_write;
407 sp_object_class->set = sp_ellipse_set;
409 item_class->description = sp_ellipse_description;
410 }
412 static void
413 sp_ellipse_init(SPEllipse */*ellipse*/)
414 {
415 /* Nothing special */
416 }
418 static void
419 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
420 {
421 if (((SPObjectClass *) ellipse_parent_class)->build)
422 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
424 sp_object_read_attr(object, "cx");
425 sp_object_read_attr(object, "cy");
426 sp_object_read_attr(object, "rx");
427 sp_object_read_attr(object, "ry");
428 }
430 static Inkscape::XML::Node *
431 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
432 {
433 SPGenericEllipse *ellipse;
435 ellipse = SP_GENERICELLIPSE(object);
437 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
438 repr = xml_doc->createElement("svg:ellipse");
439 }
441 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
442 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
443 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
444 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
446 if (((SPObjectClass *) ellipse_parent_class)->write)
447 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
449 return repr;
450 }
452 static void
453 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
454 {
455 SPGenericEllipse *ellipse;
457 ellipse = SP_GENERICELLIPSE(object);
459 switch (key) {
460 case SP_ATTR_CX:
461 ellipse->cx.readOrUnset(value);
462 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
463 break;
464 case SP_ATTR_CY:
465 ellipse->cy.readOrUnset(value);
466 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
467 break;
468 case SP_ATTR_RX:
469 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
470 ellipse->rx.unset();
471 }
472 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
473 break;
474 case SP_ATTR_RY:
475 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
476 ellipse->ry.unset();
477 }
478 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
479 break;
480 default:
481 if (((SPObjectClass *) ellipse_parent_class)->set)
482 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
483 break;
484 }
485 }
487 static gchar *sp_ellipse_description(SPItem */*item*/)
488 {
489 return g_strdup(_("<b>Ellipse</b>"));
490 }
493 void
494 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
495 {
496 SPGenericEllipse *ge;
498 g_return_if_fail(ellipse != NULL);
499 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
501 ge = SP_GENERICELLIPSE(ellipse);
503 ge->cx.computed = x;
504 ge->cy.computed = y;
505 ge->rx.computed = rx;
506 ge->ry.computed = ry;
508 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
509 }
511 /* SVG <circle> element */
513 static void sp_circle_class_init(SPCircleClass *klass);
514 static void sp_circle_init(SPCircle *circle);
516 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
517 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
518 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
519 static gchar *sp_circle_description(SPItem *item);
521 static SPGenericEllipseClass *circle_parent_class;
523 GType
524 sp_circle_get_type(void)
525 {
526 static GType type = 0;
527 if (!type) {
528 GTypeInfo info = {
529 sizeof(SPCircleClass),
530 NULL, /* base_init */
531 NULL, /* base_finalize */
532 (GClassInitFunc) sp_circle_class_init,
533 NULL, /* class_finalize */
534 NULL, /* class_data */
535 sizeof(SPCircle),
536 16, /* n_preallocs */
537 (GInstanceInitFunc) sp_circle_init,
538 NULL, /* value_table */
539 };
540 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
541 }
542 return type;
543 }
545 static void
546 sp_circle_class_init(SPCircleClass *klass)
547 {
548 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
549 SPItemClass *item_class = (SPItemClass *) klass;
551 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
553 sp_object_class->build = sp_circle_build;
554 sp_object_class->write = sp_circle_write;
555 sp_object_class->set = sp_circle_set;
557 item_class->description = sp_circle_description;
558 }
560 static void
561 sp_circle_init(SPCircle */*circle*/)
562 {
563 /* Nothing special */
564 }
566 static void
567 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
568 {
569 if (((SPObjectClass *) circle_parent_class)->build)
570 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
572 sp_object_read_attr(object, "cx");
573 sp_object_read_attr(object, "cy");
574 sp_object_read_attr(object, "r");
575 }
577 static Inkscape::XML::Node *
578 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
579 {
580 SPGenericEllipse *ellipse;
582 ellipse = SP_GENERICELLIPSE(object);
584 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
585 repr = xml_doc->createElement("svg:circle");
586 }
588 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
589 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
590 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
592 if (((SPObjectClass *) circle_parent_class)->write)
593 ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
595 return repr;
596 }
598 static void
599 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
600 {
601 SPGenericEllipse *ge;
603 ge = SP_GENERICELLIPSE(object);
605 switch (key) {
606 case SP_ATTR_CX:
607 ge->cx.readOrUnset(value);
608 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
609 break;
610 case SP_ATTR_CY:
611 ge->cy.readOrUnset(value);
612 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
613 break;
614 case SP_ATTR_R:
615 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
616 ge->rx.unset();
617 }
618 ge->ry = ge->rx;
619 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
620 break;
621 default:
622 if (((SPObjectClass *) circle_parent_class)->set)
623 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
624 break;
625 }
626 }
628 static gchar *sp_circle_description(SPItem */*item*/)
629 {
630 return g_strdup(_("<b>Circle</b>"));
631 }
633 /* <path sodipodi:type="arc"> element */
635 static void sp_arc_class_init(SPArcClass *klass);
636 static void sp_arc_init(SPArc *arc);
638 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
639 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
640 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
641 static void sp_arc_modified(SPObject *object, guint flags);
643 static gchar *sp_arc_description(SPItem *item);
645 static SPGenericEllipseClass *arc_parent_class;
647 GType
648 sp_arc_get_type(void)
649 {
650 static GType type = 0;
651 if (!type) {
652 GTypeInfo info = {
653 sizeof(SPArcClass),
654 NULL, /* base_init */
655 NULL, /* base_finalize */
656 (GClassInitFunc) sp_arc_class_init,
657 NULL, /* class_finalize */
658 NULL, /* class_data */
659 sizeof(SPArc),
660 16, /* n_preallocs */
661 (GInstanceInitFunc) sp_arc_init,
662 NULL, /* value_table */
663 };
664 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
665 }
666 return type;
667 }
669 static void
670 sp_arc_class_init(SPArcClass *klass)
671 {
672 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
673 SPItemClass *item_class = (SPItemClass *) klass;
675 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
677 sp_object_class->build = sp_arc_build;
678 sp_object_class->write = sp_arc_write;
679 sp_object_class->set = sp_arc_set;
680 sp_object_class->modified = sp_arc_modified;
682 item_class->description = sp_arc_description;
683 }
685 static void
686 sp_arc_init(SPArc */*arc*/)
687 {
688 /* Nothing special */
689 }
691 static void
692 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
693 {
694 if (((SPObjectClass *) arc_parent_class)->build)
695 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
697 Inkscape::Version version = sp_object_get_sodipodi_version(object);
699 sp_object_read_attr(object, "sodipodi:cx");
700 sp_object_read_attr(object, "sodipodi:cy");
701 sp_object_read_attr(object, "sodipodi:rx");
702 sp_object_read_attr(object, "sodipodi:ry");
704 sp_object_read_attr(object, "sodipodi:start");
705 sp_object_read_attr(object, "sodipodi:end");
706 sp_object_read_attr(object, "sodipodi:open");
707 }
709 /*
710 * sp_arc_set_elliptical_path_attribute:
711 *
712 * Convert center to endpoint parameterization and set it to repr.
713 *
714 * See SVG 1.0 Specification W3C Recommendation
715 * ``F.6 Ellptical arc implementation notes'' for more detail.
716 */
717 static gboolean
718 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
719 {
720 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
722 Inkscape::SVG::PathString str;
724 Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
725 Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
726 double rx = ge->rx.computed;
727 double ry = ge->ry.computed;
729 str.moveTo(p1);
731 double dt = fmod(ge->end - ge->start, SP_2PI);
732 if (fabs(dt) < 1e-6) {
733 Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
734 str.arcTo(rx, ry, 0, true, true, ph)
735 .arcTo(rx, ry, 0, true, true, p2)
736 .closePath();
737 } else {
738 bool fa = (fabs(dt) > M_PI);
739 bool fs = (dt > 0);
740 str.arcTo(rx, ry, 0, fa, fs, p2);
741 if (ge->closed) {
742 Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
743 str.lineTo(center).closePath();
744 }
745 }
747 repr->setAttribute("d", str.c_str());
748 return true;
749 }
751 static Inkscape::XML::Node *
752 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
753 {
754 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
755 SPArc *arc = SP_ARC(object);
757 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
758 repr = xml_doc->createElement("svg:path");
759 }
761 if (flags & SP_OBJECT_WRITE_EXT) {
762 repr->setAttribute("sodipodi:type", "arc");
763 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
764 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
765 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
766 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
768 // write start and end only if they are non-trivial; otherwise remove
769 gdouble len = fmod(ge->end - ge->start, SP_2PI);
770 if (len < 0.0) len += SP_2PI;
771 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
772 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
773 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
774 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
775 } else {
776 repr->setAttribute("sodipodi:end", NULL);
777 repr->setAttribute("sodipodi:start", NULL);
778 repr->setAttribute("sodipodi:open", NULL);
779 }
780 }
782 // write d=
783 sp_arc_set_elliptical_path_attribute(arc, repr);
785 if (((SPObjectClass *) arc_parent_class)->write)
786 ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
788 return repr;
789 }
791 static void
792 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
793 {
794 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
796 switch (key) {
797 case SP_ATTR_SODIPODI_CX:
798 ge->cx.readOrUnset(value);
799 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
800 break;
801 case SP_ATTR_SODIPODI_CY:
802 ge->cy.readOrUnset(value);
803 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
804 break;
805 case SP_ATTR_SODIPODI_RX:
806 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
807 ge->rx.unset();
808 }
809 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
810 break;
811 case SP_ATTR_SODIPODI_RY:
812 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
813 ge->ry.unset();
814 }
815 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
816 break;
817 case SP_ATTR_SODIPODI_START:
818 if (value) {
819 sp_svg_number_read_d(value, &ge->start);
820 } else {
821 ge->start = 0;
822 }
823 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
824 break;
825 case SP_ATTR_SODIPODI_END:
826 if (value) {
827 sp_svg_number_read_d(value, &ge->end);
828 } else {
829 ge->end = 2 * M_PI;
830 }
831 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
832 break;
833 case SP_ATTR_SODIPODI_OPEN:
834 ge->closed = (!value);
835 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
836 break;
837 default:
838 if (((SPObjectClass *) arc_parent_class)->set)
839 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
840 break;
841 }
842 }
844 static void
845 sp_arc_modified(SPObject *object, guint flags)
846 {
847 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
848 sp_shape_set_shape((SPShape *) object);
849 }
851 if (((SPObjectClass *) arc_parent_class)->modified)
852 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
853 }
855 static gchar *sp_arc_description(SPItem *item)
856 {
857 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
859 gdouble len = fmod(ge->end - ge->start, SP_2PI);
860 if (len < 0.0) len += SP_2PI;
861 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
862 if (ge->closed) {
863 return g_strdup(_("<b>Segment</b>"));
864 } else {
865 return g_strdup(_("<b>Arc</b>"));
866 }
867 } else {
868 return g_strdup(_("<b>Ellipse</b>"));
869 }
870 }
872 void
873 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
874 {
875 g_return_if_fail(arc != NULL);
876 g_return_if_fail(SP_IS_ARC(arc));
878 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
880 ge->cx.computed = x;
881 ge->cy.computed = y;
882 ge->rx.computed = rx;
883 ge->ry.computed = ry;
884 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
885 if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
886 ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0);
887 if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
888 ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0);
889 if (!prefs->getBool("/tools/shapes/arc/open"))
890 ge->closed = 1;
891 else
892 ge->closed = 0;
894 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
895 }
897 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
898 {
899 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
901 return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
902 ge->ry.computed * sin(arg) + ge->cy.computed);
903 }
906 /*
907 Local Variables:
908 mode:c++
909 c-file-style:"stroustrup"
910 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
911 indent-tabs-mode:nil
912 fill-column:99
913 End:
914 */
915 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :