4b68a2ca6b14094e9cbf4294dcb1c1af40377e41
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/n-art-bpath.h"
23 #include "libnr/nr-path.h"
24 #include "libnr/nr-matrix-fns.h"
25 #include "svg/svg.h"
26 #include "svg/path-string.h"
27 #include "xml/repr.h"
28 #include "attributes.h"
29 #include "style.h"
30 #include "display/curve.h"
31 #include <glibmm/i18n.h>
33 #include "document.h"
34 #include "sp-ellipse.h"
36 #include "prefs-utils.h"
38 /* Common parent class */
40 #define noELLIPSE_VERBOSE
42 #ifndef M_PI
43 #define M_PI 3.14159265358979323846
44 #endif
46 #define SP_2PI (2 * M_PI)
48 #if 1
49 /* Hmmm... shouldn't this also qualify */
50 /* Whether it is faster or not, well, nobody knows */
51 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
52 #else
53 /* we do not use C99 round(3) function yet */
54 static double sp_round(double x, double y)
55 {
56 double remain;
58 g_assert(y > 0.0);
60 /* return round(x/y) * y; */
62 remain = fmod(x, y);
64 if (remain >= 0.5*y)
65 return x - remain + y;
66 else
67 return x - remain;
68 }
69 #endif
71 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
72 static void sp_genericellipse_init(SPGenericEllipse *ellipse);
74 static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
76 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p);
78 static void sp_genericellipse_set_shape(SPShape *shape);
79 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
81 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr,
82 guint flags);
84 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
86 static SPShapeClass *ge_parent_class;
88 GType
89 sp_genericellipse_get_type(void)
90 {
91 static GType type = 0;
92 if (!type) {
93 GTypeInfo info = {
94 sizeof(SPGenericEllipseClass),
95 NULL, /* base_init */
96 NULL, /* base_finalize */
97 (GClassInitFunc) sp_genericellipse_class_init,
98 NULL, /* class_finalize */
99 NULL, /* class_data */
100 sizeof(SPGenericEllipse),
101 16, /* n_preallocs */
102 (GInstanceInitFunc) sp_genericellipse_init,
103 NULL, /* value_table */
104 };
105 type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
106 }
107 return type;
108 }
110 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
111 {
112 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
113 SPItemClass *item_class = (SPItemClass *) klass;
114 SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass;
115 SPShapeClass *shape_class = (SPShapeClass *) klass;
117 ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
119 sp_object_class->update = sp_genericellipse_update;
120 sp_object_class->write = sp_genericellipse_write;
122 item_class->snappoints = sp_genericellipse_snappoints;
124 shape_class->set_shape = sp_genericellipse_set_shape;
125 lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect;
126 }
128 static void
129 sp_genericellipse_init(SPGenericEllipse *ellipse)
130 {
131 ellipse->cx.unset();
132 ellipse->cy.unset();
133 ellipse->rx.unset();
134 ellipse->ry.unset();
136 ellipse->start = 0.0;
137 ellipse->end = SP_2PI;
138 ellipse->closed = TRUE;
139 }
141 static void
142 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
143 {
144 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
145 SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
146 SPStyle const *style = object->style;
147 double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
148 double const em = style->font_size.computed;
149 double const ex = em * 0.5; // fixme: get from pango or libnrtype
150 ellipse->cx.update(em, ex, d);
151 ellipse->cy.update(em, ex, d);
152 ellipse->rx.update(em, ex, d);
153 ellipse->ry.update(em, ex, d);
154 sp_shape_set_shape((SPShape *) object);
155 }
157 if (((SPObjectClass *) ge_parent_class)->update)
158 ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
159 }
161 static void
162 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
163 {
164 SPShape *shape = (SPShape *) lpeitem;
165 sp_genericellipse_set_shape(shape);
167 if (write) {
168 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
169 if ( shape->curve != NULL ) {
170 gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
171 repr->setAttribute("d", str);
172 g_free(str);
173 } else {
174 repr->setAttribute("d", NULL);
175 }
176 }
178 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
179 }
182 #define C1 0.552
184 /* fixme: Think (Lauris) */
186 static void sp_genericellipse_set_shape(SPShape *shape)
187 {
188 double rx, ry, s, e;
189 double x0, y0, x1, y1, x2, y2, x3, y3;
190 double len;
191 gint slice = FALSE;
192 gint i;
194 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
196 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
197 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
199 sp_genericellipse_normalize(ellipse);
201 rx = ellipse->rx.computed;
202 ry = ellipse->ry.computed;
204 // figure out if we have a slice, guarding against rounding errors
205 len = fmod(ellipse->end - ellipse->start, SP_2PI);
206 if (len < 0.0) len += SP_2PI;
207 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
208 slice = FALSE;
209 ellipse->end = ellipse->start + SP_2PI;
210 } else {
211 slice = TRUE;
212 }
214 NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
215 aff[4] = ellipse->cx.computed;
216 aff[5] = ellipse->cy.computed;
218 NArtBpath bpath[16];
219 i = 0;
220 if (ellipse->closed) {
221 bpath[i].code = NR_MOVETO;
222 } else {
223 bpath[i].code = NR_MOVETO_OPEN;
224 }
225 bpath[i].x3 = cos(ellipse->start);
226 bpath[i].y3 = sin(ellipse->start);
227 i++;
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 = C1 * (e - s) / M_PI_2;
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 bpath[i].code = NR_CURVETO;
247 bpath[i].x1 = x1;
248 bpath[i].y1 = y1;
249 bpath[i].x2 = x2;
250 bpath[i].y2 = y2;
251 bpath[i].x3 = x3;
252 bpath[i].y3 = y3;
253 i++;
254 }
256 if (slice && ellipse->closed) {
257 bpath[i].code = NR_LINETO;
258 bpath[i].x3 = 0.0;
259 bpath[i].y3 = 0.0;
260 i++;
261 bpath[i].code = NR_LINETO;
262 bpath[i].x3 = bpath[0].x3;
263 bpath[i].y3 = bpath[0].y3;
264 i++;
265 } else if (ellipse->closed) {
266 bpath[i-1].x3 = bpath[0].x3;
267 bpath[i-1].y3 = bpath[0].y3;
268 }
270 bpath[i].code = NR_END;
271 SPCurve *c = SPCurve::new_from_bpath(nr_artpath_affine(bpath, aff));
272 g_assert(c != NULL);
274 sp_lpe_item_perform_path_effect(SP_LPE_ITEM (ellipse), c);
275 sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
276 c->unref();
277 }
279 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
280 {
281 g_assert(item != NULL);
282 g_assert(SP_IS_GENERICELLIPSE(item));
284 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
285 sp_genericellipse_normalize(ellipse);
286 NR::Matrix const i2d = sp_item_i2d_affine(item);
288 // figure out if we have a slice, whilst guarding against rounding errors
289 bool slice = false;
290 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
291 if (len < 0.0) len += SP_2PI;
292 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
293 slice = false;
294 ellipse->end = ellipse->start + SP_2PI;
295 } else {
296 slice = true;
297 }
299 double rx = ellipse->rx.computed;
300 double ry = ellipse->ry.computed;
301 double cx = ellipse->cx.computed;
302 double cy = ellipse->cy.computed;
304 // Snap to the 4 quadrant points of the ellipse, but only if the arc
305 // spans far enough to include them
306 double angle = 0;
307 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
308 if (angle >= ellipse->start && angle <= ellipse->end) {
309 *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
310 }
311 }
313 // And if we have a slice, also snap to the endpoints and the centre point
314 if (slice) {
315 // Add the centre, if we have a closed slice
316 if (ellipse->closed) {
317 *p = NR::Point(cx, cy) * i2d;
318 }
319 // Add the start point, if it's not coincident with a quadrant point
320 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
321 *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
322 }
323 // Add the end point, if it's not coincident with a quadrant point
324 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
325 *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
326 }
327 }
328 }
330 void
331 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
332 {
333 ellipse->start = fmod(ellipse->start, SP_2PI);
334 ellipse->end = fmod(ellipse->end, SP_2PI);
336 if (ellipse->start < 0.0)
337 ellipse->start += SP_2PI;
338 double diff = ellipse->start - ellipse->end;
339 if (diff >= 0.0)
340 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
342 /* Now we keep: 0 <= start < end <= 2*PI */
343 }
345 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
346 {
347 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
349 if (flags & SP_OBJECT_WRITE_EXT) {
350 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
351 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
352 repr = xml_doc->createElement("svg:path");
353 }
355 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
356 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
357 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
358 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
360 if (SP_IS_ARC(ellipse))
361 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
362 }
364 if (((SPObjectClass *) ge_parent_class)->write)
365 ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
367 return repr;
368 }
370 /* SVG <ellipse> element */
372 static void sp_ellipse_class_init(SPEllipseClass *klass);
373 static void sp_ellipse_init(SPEllipse *ellipse);
375 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
376 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
377 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
378 static gchar *sp_ellipse_description(SPItem *item);
380 static SPGenericEllipseClass *ellipse_parent_class;
382 GType
383 sp_ellipse_get_type(void)
384 {
385 static GType type = 0;
386 if (!type) {
387 GTypeInfo info = {
388 sizeof(SPEllipseClass),
389 NULL, /* base_init */
390 NULL, /* base_finalize */
391 (GClassInitFunc) sp_ellipse_class_init,
392 NULL, /* class_finalize */
393 NULL, /* class_data */
394 sizeof(SPEllipse),
395 16, /* n_preallocs */
396 (GInstanceInitFunc) sp_ellipse_init,
397 NULL, /* value_table */
398 };
399 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
400 }
401 return type;
402 }
404 static void sp_ellipse_class_init(SPEllipseClass *klass)
405 {
406 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
407 SPItemClass *item_class = (SPItemClass *) klass;
409 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
411 sp_object_class->build = sp_ellipse_build;
412 sp_object_class->write = sp_ellipse_write;
413 sp_object_class->set = sp_ellipse_set;
415 item_class->description = sp_ellipse_description;
416 }
418 static void
419 sp_ellipse_init(SPEllipse */*ellipse*/)
420 {
421 /* Nothing special */
422 }
424 static void
425 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
426 {
427 if (((SPObjectClass *) ellipse_parent_class)->build)
428 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
430 sp_object_read_attr(object, "cx");
431 sp_object_read_attr(object, "cy");
432 sp_object_read_attr(object, "rx");
433 sp_object_read_attr(object, "ry");
434 }
436 static Inkscape::XML::Node *
437 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
438 {
439 SPGenericEllipse *ellipse;
441 ellipse = SP_GENERICELLIPSE(object);
443 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
444 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
445 repr = xml_doc->createElement("svg:ellipse");
446 }
448 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
449 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
450 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
451 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
453 if (((SPObjectClass *) ellipse_parent_class)->write)
454 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
456 return repr;
457 }
459 static void
460 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
461 {
462 SPGenericEllipse *ellipse;
464 ellipse = SP_GENERICELLIPSE(object);
466 switch (key) {
467 case SP_ATTR_CX:
468 ellipse->cx.readOrUnset(value);
469 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
470 break;
471 case SP_ATTR_CY:
472 ellipse->cy.readOrUnset(value);
473 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
474 break;
475 case SP_ATTR_RX:
476 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
477 ellipse->rx.unset();
478 }
479 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
480 break;
481 case SP_ATTR_RY:
482 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
483 ellipse->ry.unset();
484 }
485 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
486 break;
487 default:
488 if (((SPObjectClass *) ellipse_parent_class)->set)
489 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
490 break;
491 }
492 }
494 static gchar *sp_ellipse_description(SPItem */*item*/)
495 {
496 return g_strdup(_("<b>Ellipse</b>"));
497 }
500 void
501 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
502 {
503 SPGenericEllipse *ge;
505 g_return_if_fail(ellipse != NULL);
506 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
508 ge = SP_GENERICELLIPSE(ellipse);
510 ge->cx.computed = x;
511 ge->cy.computed = y;
512 ge->rx.computed = rx;
513 ge->ry.computed = ry;
515 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
516 }
518 /* SVG <circle> element */
520 static void sp_circle_class_init(SPCircleClass *klass);
521 static void sp_circle_init(SPCircle *circle);
523 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
524 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
525 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
526 static gchar *sp_circle_description(SPItem *item);
528 static SPGenericEllipseClass *circle_parent_class;
530 GType
531 sp_circle_get_type(void)
532 {
533 static GType type = 0;
534 if (!type) {
535 GTypeInfo info = {
536 sizeof(SPCircleClass),
537 NULL, /* base_init */
538 NULL, /* base_finalize */
539 (GClassInitFunc) sp_circle_class_init,
540 NULL, /* class_finalize */
541 NULL, /* class_data */
542 sizeof(SPCircle),
543 16, /* n_preallocs */
544 (GInstanceInitFunc) sp_circle_init,
545 NULL, /* value_table */
546 };
547 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
548 }
549 return type;
550 }
552 static void
553 sp_circle_class_init(SPCircleClass *klass)
554 {
555 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
556 SPItemClass *item_class = (SPItemClass *) klass;
558 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
560 sp_object_class->build = sp_circle_build;
561 sp_object_class->write = sp_circle_write;
562 sp_object_class->set = sp_circle_set;
564 item_class->description = sp_circle_description;
565 }
567 static void
568 sp_circle_init(SPCircle */*circle*/)
569 {
570 /* Nothing special */
571 }
573 static void
574 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
575 {
576 if (((SPObjectClass *) circle_parent_class)->build)
577 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
579 sp_object_read_attr(object, "cx");
580 sp_object_read_attr(object, "cy");
581 sp_object_read_attr(object, "r");
582 }
584 static Inkscape::XML::Node *
585 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
586 {
587 SPGenericEllipse *ellipse;
589 ellipse = SP_GENERICELLIPSE(object);
591 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
592 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
593 repr = xml_doc->createElement("svg:circle");
594 }
596 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
597 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
598 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
600 if (((SPObjectClass *) circle_parent_class)->write)
601 ((SPObjectClass *) circle_parent_class)->write(object, repr, flags);
603 return repr;
604 }
606 static void
607 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
608 {
609 SPGenericEllipse *ge;
611 ge = SP_GENERICELLIPSE(object);
613 switch (key) {
614 case SP_ATTR_CX:
615 ge->cx.readOrUnset(value);
616 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
617 break;
618 case SP_ATTR_CY:
619 ge->cy.readOrUnset(value);
620 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
621 break;
622 case SP_ATTR_R:
623 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
624 ge->rx.unset();
625 }
626 ge->ry = ge->rx;
627 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
628 break;
629 default:
630 if (((SPObjectClass *) circle_parent_class)->set)
631 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
632 break;
633 }
634 }
636 static gchar *sp_circle_description(SPItem */*item*/)
637 {
638 return g_strdup(_("<b>Circle</b>"));
639 }
641 /* <path sodipodi:type="arc"> element */
643 static void sp_arc_class_init(SPArcClass *klass);
644 static void sp_arc_init(SPArc *arc);
646 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
647 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
648 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
649 static void sp_arc_modified(SPObject *object, guint flags);
651 static gchar *sp_arc_description(SPItem *item);
653 static SPGenericEllipseClass *arc_parent_class;
655 GType
656 sp_arc_get_type(void)
657 {
658 static GType type = 0;
659 if (!type) {
660 GTypeInfo info = {
661 sizeof(SPArcClass),
662 NULL, /* base_init */
663 NULL, /* base_finalize */
664 (GClassInitFunc) sp_arc_class_init,
665 NULL, /* class_finalize */
666 NULL, /* class_data */
667 sizeof(SPArc),
668 16, /* n_preallocs */
669 (GInstanceInitFunc) sp_arc_init,
670 NULL, /* value_table */
671 };
672 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
673 }
674 return type;
675 }
677 static void
678 sp_arc_class_init(SPArcClass *klass)
679 {
680 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
681 SPItemClass *item_class = (SPItemClass *) klass;
683 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
685 sp_object_class->build = sp_arc_build;
686 sp_object_class->write = sp_arc_write;
687 sp_object_class->set = sp_arc_set;
688 sp_object_class->modified = sp_arc_modified;
690 item_class->description = sp_arc_description;
691 }
693 static void
694 sp_arc_init(SPArc */*arc*/)
695 {
696 /* Nothing special */
697 }
699 static void
700 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
701 {
702 if (((SPObjectClass *) arc_parent_class)->build)
703 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
705 Inkscape::Version version = sp_object_get_sodipodi_version(object);
707 sp_object_read_attr(object, "sodipodi:cx");
708 sp_object_read_attr(object, "sodipodi:cy");
709 sp_object_read_attr(object, "sodipodi:rx");
710 sp_object_read_attr(object, "sodipodi:ry");
712 sp_object_read_attr(object, "sodipodi:start");
713 sp_object_read_attr(object, "sodipodi:end");
714 sp_object_read_attr(object, "sodipodi:open");
715 }
717 /*
718 * sp_arc_set_elliptical_path_attribute:
719 *
720 * Convert center to endpoint parameterization and set it to repr.
721 *
722 * See SVG 1.0 Specification W3C Recommendation
723 * ``F.6 Ellptical arc implementation notes'' for more detail.
724 */
725 static gboolean
726 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
727 {
728 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
730 Inkscape::SVG::PathString str;
732 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
733 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
734 double rx = ge->rx.computed;
735 double ry = ge->ry.computed;
737 str.moveTo(p1);
739 double dt = fmod(ge->end - ge->start, SP_2PI);
740 if (fabs(dt) < 1e-6) {
741 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
742 str.arcTo(rx, ry, 0, true, true, ph)
743 .arcTo(rx, ry, 0, true, true, p2)
744 .closePath();
745 } else {
746 bool fa = (fabs(dt) > M_PI);
747 bool fs = (dt > 0);
748 str.arcTo(rx, ry, 0, fa, fs, p2);
749 if (ge->closed) {
750 NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
751 str.lineTo(center).closePath();
752 }
753 }
755 repr->setAttribute("d", str.c_str());
756 return true;
757 }
759 static Inkscape::XML::Node *
760 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
761 {
762 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
763 SPArc *arc = SP_ARC(object);
765 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
766 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
767 repr = xml_doc->createElement("svg:path");
768 }
770 if (flags & SP_OBJECT_WRITE_EXT) {
771 repr->setAttribute("sodipodi:type", "arc");
772 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
773 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
774 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
775 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
777 // write start and end only if they are non-trivial; otherwise remove
778 gdouble len = fmod(ge->end - ge->start, SP_2PI);
779 if (len < 0.0) len += SP_2PI;
780 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
781 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
782 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
783 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
784 } else {
785 repr->setAttribute("sodipodi:end", NULL);
786 repr->setAttribute("sodipodi:start", NULL);
787 repr->setAttribute("sodipodi:open", NULL);
788 }
789 }
791 // write d=
792 sp_arc_set_elliptical_path_attribute(arc, repr);
794 if (((SPObjectClass *) arc_parent_class)->write)
795 ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
797 return repr;
798 }
800 static void
801 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
802 {
803 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
805 switch (key) {
806 case SP_ATTR_SODIPODI_CX:
807 ge->cx.readOrUnset(value);
808 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
809 break;
810 case SP_ATTR_SODIPODI_CY:
811 ge->cy.readOrUnset(value);
812 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
813 break;
814 case SP_ATTR_SODIPODI_RX:
815 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
816 ge->rx.unset();
817 }
818 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
819 break;
820 case SP_ATTR_SODIPODI_RY:
821 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
822 ge->ry.unset();
823 }
824 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
825 break;
826 case SP_ATTR_SODIPODI_START:
827 if (value) {
828 sp_svg_number_read_d(value, &ge->start);
829 } else {
830 ge->start = 0;
831 }
832 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
833 break;
834 case SP_ATTR_SODIPODI_END:
835 if (value) {
836 sp_svg_number_read_d(value, &ge->end);
837 } else {
838 ge->end = 2 * M_PI;
839 }
840 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
841 break;
842 case SP_ATTR_SODIPODI_OPEN:
843 ge->closed = (!value);
844 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
845 break;
846 default:
847 if (((SPObjectClass *) arc_parent_class)->set)
848 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
849 break;
850 }
851 }
853 static void
854 sp_arc_modified(SPObject *object, guint flags)
855 {
856 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
857 sp_shape_set_shape((SPShape *) object);
858 }
860 if (((SPObjectClass *) arc_parent_class)->modified)
861 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
862 }
864 static gchar *sp_arc_description(SPItem *item)
865 {
866 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
868 gdouble len = fmod(ge->end - ge->start, SP_2PI);
869 if (len < 0.0) len += SP_2PI;
870 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
871 if (ge->closed) {
872 return g_strdup(_("<b>Segment</b>"));
873 } else {
874 return g_strdup(_("<b>Arc</b>"));
875 }
876 } else {
877 return g_strdup(_("<b>Ellipse</b>"));
878 }
879 }
881 void
882 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
883 {
884 g_return_if_fail(arc != NULL);
885 g_return_if_fail(SP_IS_ARC(arc));
887 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
889 ge->cx.computed = x;
890 ge->cy.computed = y;
891 ge->rx.computed = rx;
892 ge->ry.computed = ry;
893 if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
894 ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
895 if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
896 ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
897 if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
898 ge->closed = 1;
899 else
900 ge->closed = 0;
902 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
903 }
905 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
906 {
907 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
909 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
910 ge->ry.computed * sin(arg) + ge->cy.computed);
911 }
914 /*
915 Local Variables:
916 mode:c++
917 c-file-style:"stroustrup"
918 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
919 indent-tabs-mode:nil
920 fill-column:99
921 End:
922 */
923 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :