1 #define __SP_ELLIPSE_C__
3 /*
4 * SVG <ellipse> and related implementations
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Mitsuru Oka
9 * bulia byak <buliabyak@users.sf.net>
10 *
11 * Copyright (C) 1999-2002 Lauris Kaplinski
12 * Copyright (C) 2000-2001 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
22 #include "libnr/nr-matrix-fns.h"
23 #include "svg/svg.h"
24 #include "svg/path-string.h"
25 #include "xml/repr.h"
26 #include "attributes.h"
27 #include "style.h"
28 #include "display/curve.h"
29 #include <glibmm/i18n.h>
30 #include <2geom/transforms.h>
32 #include "document.h"
33 #include "sp-ellipse.h"
35 #include "preferences.h"
37 /* Common parent class */
39 #define noELLIPSE_VERBOSE
41 #ifndef M_PI
42 #define M_PI 3.14159265358979323846
43 #endif
45 #define SP_2PI (2 * M_PI)
47 #if 1
48 /* Hmmm... shouldn't this also qualify */
49 /* Whether it is faster or not, well, nobody knows */
50 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
51 #else
52 /* we do not use C99 round(3) function yet */
53 static double sp_round(double x, double y)
54 {
55 double remain;
57 g_assert(y > 0.0);
59 /* return round(x/y) * y; */
61 remain = fmod(x, y);
63 if (remain >= 0.5*y)
64 return x - remain + y;
65 else
66 return x - remain;
67 }
68 #endif
70 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
71 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
73 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
75 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs);
77 static void sp_genericellipse_set_shape(SPShape *shape);
78 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
80 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
81 guint flags);
83 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
85 static SPShapeClass *ge_parent_class;
87 GType
88 sp_genericellipse_get_type(void)
89 {
90 static GType type = 0;
91 if (!type) {
92 GTypeInfo info = {
93 sizeof(SPGenericEllipseClass),
94 NULL, /* base_init */
95 NULL, /* base_finalize */
96 (GClassInitFunc) sp_genericellipse_class_init,
97 NULL, /* class_finalize */
98 NULL, /* class_data */
99 sizeof(SPGenericEllipse),
100 16, /* n_preallocs */
101 (GInstanceInitFunc) sp_genericellipse_init,
102 NULL, /* value_table */
103 };
104 type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
105 }
106 return type;
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 double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
147 double const em = style->font_size.computed;
148 double const ex = em * 0.5; // fixme: get from pango or libnrtype
149 ellipse->cx.update(em, ex, d);
150 ellipse->cy.update(em, ex, d);
151 ellipse->rx.update(em, ex, d);
152 ellipse->ry.update(em, ex, d);
153 sp_shape_set_shape((SPShape *) object);
154 }
156 if (((SPObjectClass *) ge_parent_class)->update)
157 ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
158 }
160 static void
161 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
162 {
163 SPShape *shape = (SPShape *) lpeitem;
164 sp_genericellipse_set_shape(shape);
166 if (write) {
167 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
168 if ( shape->curve != NULL ) {
169 gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
170 repr->setAttribute("d", str);
171 g_free(str);
172 } else {
173 repr->setAttribute("d", NULL);
174 }
175 }
177 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
178 }
181 #define C1 0.552
183 /* fixme: Think (Lauris) */
184 /* Can't we use arcto in this method? */
185 static void sp_genericellipse_set_shape(SPShape *shape)
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 = C1 * (e - s) / M_PI_2;
221 x0 = cos(s);
222 y0 = sin(s);
223 x1 = x0 + len * cos(s + M_PI_2);
224 y1 = y0 + len * sin(s + M_PI_2);
225 x3 = cos(e);
226 y3 = sin(e);
227 x2 = x3 + len * cos(e - M_PI_2);
228 y2 = y3 + len * sin(e - M_PI_2);
229 #ifdef ELLIPSE_VERBOSE
230 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
231 i, s, e, x1, y1, x2, y2, x3, y3);
232 #endif
233 curve->curveto(x1,y1, x2,y2, x3,y3);
234 }
236 if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary?
237 curve->lineto(0., 0.);
238 }
239 if (ellipse->closed) {
240 curve->closepath();
241 }
243 Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
244 curve->transform(aff);
246 /* Reset the shape'scurve to the "original_curve"
247 * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
248 sp_shape_set_curve_insync (shape, curve, TRUE);
249 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
250 SPCurve *c_lpe = curve->copy();
251 bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
252 if (success) {
253 sp_shape_set_curve_insync (shape, c_lpe, TRUE);
254 }
255 c_lpe->unref();
256 }
257 curve->unref();
258 }
260 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const */*snapprefs*/)
261 {
262 g_assert(item != NULL);
263 g_assert(SP_IS_GENERICELLIPSE(item));
265 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
266 sp_genericellipse_normalize(ellipse);
267 Geom::Matrix const i2d = sp_item_i2d_affine(item);
269 // figure out if we have a slice, whilst guarding against rounding errors
270 bool slice = false;
271 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
272 if (len < 0.0) len += SP_2PI;
273 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
274 slice = false;
275 ellipse->end = ellipse->start + SP_2PI;
276 } else {
277 slice = true;
278 }
280 double rx = ellipse->rx.computed;
281 double ry = ellipse->ry.computed;
282 double cx = ellipse->cx.computed;
283 double cy = ellipse->cy.computed;
285 // Snap to the 4 quadrant points of the ellipse, but only if the arc
286 // spans far enough to include them
287 double angle = 0;
288 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
289 if (angle >= ellipse->start && angle <= ellipse->end) {
290 *p = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
291 }
292 }
294 // And if we have a slice, also snap to the endpoints and the centre point
295 if (slice) {
296 // Add the centre, if we have a closed slice
297 if (ellipse->closed) {
298 *p = Geom::Point(cx, cy) * i2d;
299 }
300 // Add the start point, if it's not coincident with a quadrant point
301 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
302 *p = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
303 }
304 // Add the end point, if it's not coincident with a quadrant point
305 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
306 *p = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
307 }
308 }
309 }
311 void
312 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
313 {
314 ellipse->start = fmod(ellipse->start, SP_2PI);
315 ellipse->end = fmod(ellipse->end, SP_2PI);
317 if (ellipse->start < 0.0)
318 ellipse->start += SP_2PI;
319 double diff = ellipse->start - ellipse->end;
320 if (diff >= 0.0)
321 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
323 /* Now we keep: 0 <= start < end <= 2*PI */
324 }
326 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
327 {
328 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
330 if (flags & SP_OBJECT_WRITE_EXT) {
331 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
332 repr = xml_doc->createElement("svg:path");
333 }
335 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
336 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
337 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
338 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
340 if (SP_IS_ARC(ellipse))
341 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
342 }
344 if (((SPObjectClass *) ge_parent_class)->write)
345 ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
347 return repr;
348 }
350 /* SVG <ellipse> element */
352 static void sp_ellipse_class_init(SPEllipseClass *klass);
353 static void sp_ellipse_init(SPEllipse *ellipse);
355 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
356 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
357 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
358 static gchar *sp_ellipse_description(SPItem *item);
360 static SPGenericEllipseClass *ellipse_parent_class;
362 GType
363 sp_ellipse_get_type(void)
364 {
365 static GType type = 0;
366 if (!type) {
367 GTypeInfo info = {
368 sizeof(SPEllipseClass),
369 NULL, /* base_init */
370 NULL, /* base_finalize */
371 (GClassInitFunc) sp_ellipse_class_init,
372 NULL, /* class_finalize */
373 NULL, /* class_data */
374 sizeof(SPEllipse),
375 16, /* n_preallocs */
376 (GInstanceInitFunc) sp_ellipse_init,
377 NULL, /* value_table */
378 };
379 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
380 }
381 return type;
382 }
384 static void sp_ellipse_class_init(SPEllipseClass *klass)
385 {
386 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
387 SPItemClass *item_class = (SPItemClass *) klass;
389 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
391 sp_object_class->build = sp_ellipse_build;
392 sp_object_class->write = sp_ellipse_write;
393 sp_object_class->set = sp_ellipse_set;
395 item_class->description = sp_ellipse_description;
396 }
398 static void
399 sp_ellipse_init(SPEllipse */*ellipse*/)
400 {
401 /* Nothing special */
402 }
404 static void
405 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
406 {
407 if (((SPObjectClass *) ellipse_parent_class)->build)
408 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
410 sp_object_read_attr(object, "cx");
411 sp_object_read_attr(object, "cy");
412 sp_object_read_attr(object, "rx");
413 sp_object_read_attr(object, "ry");
414 }
416 static Inkscape::XML::Node *
417 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
418 {
419 SPGenericEllipse *ellipse;
421 ellipse = SP_GENERICELLIPSE(object);
423 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
424 repr = xml_doc->createElement("svg:ellipse");
425 }
427 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
428 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
429 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
430 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
432 if (((SPObjectClass *) ellipse_parent_class)->write)
433 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
435 return repr;
436 }
438 static void
439 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
440 {
441 SPGenericEllipse *ellipse;
443 ellipse = SP_GENERICELLIPSE(object);
445 switch (key) {
446 case SP_ATTR_CX:
447 ellipse->cx.readOrUnset(value);
448 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
449 break;
450 case SP_ATTR_CY:
451 ellipse->cy.readOrUnset(value);
452 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
453 break;
454 case SP_ATTR_RX:
455 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
456 ellipse->rx.unset();
457 }
458 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
459 break;
460 case SP_ATTR_RY:
461 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
462 ellipse->ry.unset();
463 }
464 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
465 break;
466 default:
467 if (((SPObjectClass *) ellipse_parent_class)->set)
468 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
469 break;
470 }
471 }
473 static gchar *sp_ellipse_description(SPItem */*item*/)
474 {
475 return g_strdup(_("<b>Ellipse</b>"));
476 }
479 void
480 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
481 {
482 SPGenericEllipse *ge;
484 g_return_if_fail(ellipse != NULL);
485 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
487 ge = SP_GENERICELLIPSE(ellipse);
489 ge->cx.computed = x;
490 ge->cy.computed = y;
491 ge->rx.computed = rx;
492 ge->ry.computed = ry;
494 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
495 }
497 /* SVG <circle> element */
499 static void sp_circle_class_init(SPCircleClass *klass);
500 static void sp_circle_init(SPCircle *circle);
502 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
503 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
504 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
505 static gchar *sp_circle_description(SPItem *item);
507 static SPGenericEllipseClass *circle_parent_class;
509 GType
510 sp_circle_get_type(void)
511 {
512 static GType type = 0;
513 if (!type) {
514 GTypeInfo info = {
515 sizeof(SPCircleClass),
516 NULL, /* base_init */
517 NULL, /* base_finalize */
518 (GClassInitFunc) sp_circle_class_init,
519 NULL, /* class_finalize */
520 NULL, /* class_data */
521 sizeof(SPCircle),
522 16, /* n_preallocs */
523 (GInstanceInitFunc) sp_circle_init,
524 NULL, /* value_table */
525 };
526 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
527 }
528 return type;
529 }
531 static void
532 sp_circle_class_init(SPCircleClass *klass)
533 {
534 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
535 SPItemClass *item_class = (SPItemClass *) klass;
537 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
539 sp_object_class->build = sp_circle_build;
540 sp_object_class->write = sp_circle_write;
541 sp_object_class->set = sp_circle_set;
543 item_class->description = sp_circle_description;
544 }
546 static void
547 sp_circle_init(SPCircle */*circle*/)
548 {
549 /* Nothing special */
550 }
552 static void
553 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
554 {
555 if (((SPObjectClass *) circle_parent_class)->build)
556 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
558 sp_object_read_attr(object, "cx");
559 sp_object_read_attr(object, "cy");
560 sp_object_read_attr(object, "r");
561 }
563 static Inkscape::XML::Node *
564 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
565 {
566 SPGenericEllipse *ellipse;
568 ellipse = SP_GENERICELLIPSE(object);
570 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
571 repr = xml_doc->createElement("svg:circle");
572 }
574 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
575 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
576 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
578 if (((SPObjectClass *) circle_parent_class)->write)
579 ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
581 return repr;
582 }
584 static void
585 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
586 {
587 SPGenericEllipse *ge;
589 ge = SP_GENERICELLIPSE(object);
591 switch (key) {
592 case SP_ATTR_CX:
593 ge->cx.readOrUnset(value);
594 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
595 break;
596 case SP_ATTR_CY:
597 ge->cy.readOrUnset(value);
598 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
599 break;
600 case SP_ATTR_R:
601 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
602 ge->rx.unset();
603 }
604 ge->ry = ge->rx;
605 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
606 break;
607 default:
608 if (((SPObjectClass *) circle_parent_class)->set)
609 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
610 break;
611 }
612 }
614 static gchar *sp_circle_description(SPItem */*item*/)
615 {
616 return g_strdup(_("<b>Circle</b>"));
617 }
619 /* <path sodipodi:type="arc"> element */
621 static void sp_arc_class_init(SPArcClass *klass);
622 static void sp_arc_init(SPArc *arc);
624 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
625 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
626 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
627 static void sp_arc_modified(SPObject *object, guint flags);
629 static gchar *sp_arc_description(SPItem *item);
631 static SPGenericEllipseClass *arc_parent_class;
633 GType
634 sp_arc_get_type(void)
635 {
636 static GType type = 0;
637 if (!type) {
638 GTypeInfo info = {
639 sizeof(SPArcClass),
640 NULL, /* base_init */
641 NULL, /* base_finalize */
642 (GClassInitFunc) sp_arc_class_init,
643 NULL, /* class_finalize */
644 NULL, /* class_data */
645 sizeof(SPArc),
646 16, /* n_preallocs */
647 (GInstanceInitFunc) sp_arc_init,
648 NULL, /* value_table */
649 };
650 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
651 }
652 return type;
653 }
655 static void
656 sp_arc_class_init(SPArcClass *klass)
657 {
658 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
659 SPItemClass *item_class = (SPItemClass *) klass;
661 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
663 sp_object_class->build = sp_arc_build;
664 sp_object_class->write = sp_arc_write;
665 sp_object_class->set = sp_arc_set;
666 sp_object_class->modified = sp_arc_modified;
668 item_class->description = sp_arc_description;
669 }
671 static void
672 sp_arc_init(SPArc */*arc*/)
673 {
674 /* Nothing special */
675 }
677 static void
678 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
679 {
680 if (((SPObjectClass *) arc_parent_class)->build)
681 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
683 Inkscape::Version version = sp_object_get_sodipodi_version(object);
685 sp_object_read_attr(object, "sodipodi:cx");
686 sp_object_read_attr(object, "sodipodi:cy");
687 sp_object_read_attr(object, "sodipodi:rx");
688 sp_object_read_attr(object, "sodipodi:ry");
690 sp_object_read_attr(object, "sodipodi:start");
691 sp_object_read_attr(object, "sodipodi:end");
692 sp_object_read_attr(object, "sodipodi:open");
693 }
695 /*
696 * sp_arc_set_elliptical_path_attribute:
697 *
698 * Convert center to endpoint parameterization and set it to repr.
699 *
700 * See SVG 1.0 Specification W3C Recommendation
701 * ``F.6 Ellptical arc implementation notes'' for more detail.
702 */
703 static gboolean
704 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
705 {
706 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
708 Inkscape::SVG::PathString str;
710 Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
711 Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
712 double rx = ge->rx.computed;
713 double ry = ge->ry.computed;
715 str.moveTo(p1);
717 double dt = fmod(ge->end - ge->start, SP_2PI);
718 if (fabs(dt) < 1e-6) {
719 Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
720 str.arcTo(rx, ry, 0, true, true, ph)
721 .arcTo(rx, ry, 0, true, true, p2)
722 .closePath();
723 } else {
724 bool fa = (fabs(dt) > M_PI);
725 bool fs = (dt > 0);
726 str.arcTo(rx, ry, 0, fa, fs, p2);
727 if (ge->closed) {
728 Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
729 str.lineTo(center).closePath();
730 }
731 }
733 repr->setAttribute("d", str.c_str());
734 return true;
735 }
737 static Inkscape::XML::Node *
738 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
739 {
740 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
741 SPArc *arc = SP_ARC(object);
743 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
744 repr = xml_doc->createElement("svg:path");
745 }
747 if (flags & SP_OBJECT_WRITE_EXT) {
748 repr->setAttribute("sodipodi:type", "arc");
749 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
750 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
751 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
752 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
754 // write start and end only if they are non-trivial; otherwise remove
755 gdouble len = fmod(ge->end - ge->start, SP_2PI);
756 if (len < 0.0) len += SP_2PI;
757 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
758 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
759 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
760 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
761 } else {
762 repr->setAttribute("sodipodi:end", NULL);
763 repr->setAttribute("sodipodi:start", NULL);
764 repr->setAttribute("sodipodi:open", NULL);
765 }
766 }
768 // write d=
769 sp_arc_set_elliptical_path_attribute(arc, repr);
771 if (((SPObjectClass *) arc_parent_class)->write)
772 ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
774 return repr;
775 }
777 static void
778 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
779 {
780 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
782 switch (key) {
783 case SP_ATTR_SODIPODI_CX:
784 ge->cx.readOrUnset(value);
785 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
786 break;
787 case SP_ATTR_SODIPODI_CY:
788 ge->cy.readOrUnset(value);
789 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
790 break;
791 case SP_ATTR_SODIPODI_RX:
792 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
793 ge->rx.unset();
794 }
795 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
796 break;
797 case SP_ATTR_SODIPODI_RY:
798 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
799 ge->ry.unset();
800 }
801 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
802 break;
803 case SP_ATTR_SODIPODI_START:
804 if (value) {
805 sp_svg_number_read_d(value, &ge->start);
806 } else {
807 ge->start = 0;
808 }
809 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
810 break;
811 case SP_ATTR_SODIPODI_END:
812 if (value) {
813 sp_svg_number_read_d(value, &ge->end);
814 } else {
815 ge->end = 2 * M_PI;
816 }
817 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
818 break;
819 case SP_ATTR_SODIPODI_OPEN:
820 ge->closed = (!value);
821 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
822 break;
823 default:
824 if (((SPObjectClass *) arc_parent_class)->set)
825 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
826 break;
827 }
828 }
830 static void
831 sp_arc_modified(SPObject *object, guint flags)
832 {
833 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
834 sp_shape_set_shape((SPShape *) object);
835 }
837 if (((SPObjectClass *) arc_parent_class)->modified)
838 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
839 }
841 static gchar *sp_arc_description(SPItem *item)
842 {
843 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
845 gdouble len = fmod(ge->end - ge->start, SP_2PI);
846 if (len < 0.0) len += SP_2PI;
847 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
848 if (ge->closed) {
849 return g_strdup(_("<b>Segment</b>"));
850 } else {
851 return g_strdup(_("<b>Arc</b>"));
852 }
853 } else {
854 return g_strdup(_("<b>Ellipse</b>"));
855 }
856 }
858 void
859 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
860 {
861 g_return_if_fail(arc != NULL);
862 g_return_if_fail(SP_IS_ARC(arc));
864 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
866 ge->cx.computed = x;
867 ge->cy.computed = y;
868 ge->rx.computed = rx;
869 ge->ry.computed = ry;
870 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
871 if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
872 ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0);
873 if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
874 ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0);
875 if (!prefs->getBool("/tools/shapes/arc/open"))
876 ge->closed = 1;
877 else
878 ge->closed = 0;
880 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
881 }
883 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
884 {
885 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
887 return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
888 ge->ry.computed * sin(arg) + ge->cy.computed);
889 }
892 /*
893 Local Variables:
894 mode:c++
895 c-file-style:"stroustrup"
896 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
897 indent-tabs-mode:nil
898 fill-column:99
899 End:
900 */
901 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :