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 NArtBpath *abp = shape->curve->first_bpath();
171 if (abp) {
172 gchar *str = sp_svg_write_path(abp);
173 repr->setAttribute("d", str);
174 g_free(str);
175 } else {
176 repr->setAttribute("d", "");
177 }
178 } else {
179 repr->setAttribute("d", NULL);
180 }
181 }
183 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
184 }
187 #define C1 0.552
189 /* fixme: Think (Lauris) */
191 static void sp_genericellipse_set_shape(SPShape *shape)
192 {
193 double rx, ry, s, e;
194 double x0, y0, x1, y1, x2, y2, x3, y3;
195 double len;
196 gint slice = FALSE;
197 gint i;
199 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
201 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
202 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
204 sp_genericellipse_normalize(ellipse);
206 rx = ellipse->rx.computed;
207 ry = ellipse->ry.computed;
209 // figure out if we have a slice, guarding against rounding errors
210 len = fmod(ellipse->end - ellipse->start, SP_2PI);
211 if (len < 0.0) len += SP_2PI;
212 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
213 slice = FALSE;
214 ellipse->end = ellipse->start + SP_2PI;
215 } else {
216 slice = TRUE;
217 }
219 NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
220 aff[4] = ellipse->cx.computed;
221 aff[5] = ellipse->cy.computed;
223 NArtBpath bpath[16];
224 i = 0;
225 if (ellipse->closed) {
226 bpath[i].code = NR_MOVETO;
227 } else {
228 bpath[i].code = NR_MOVETO_OPEN;
229 }
230 bpath[i].x3 = cos(ellipse->start);
231 bpath[i].y3 = sin(ellipse->start);
232 i++;
234 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
235 e = s + M_PI_2;
236 if (e > ellipse->end)
237 e = ellipse->end;
238 len = C1 * (e - s) / M_PI_2;
239 x0 = cos(s);
240 y0 = sin(s);
241 x1 = x0 + len * cos(s + M_PI_2);
242 y1 = y0 + len * sin(s + M_PI_2);
243 x3 = cos(e);
244 y3 = sin(e);
245 x2 = x3 + len * cos(e - M_PI_2);
246 y2 = y3 + len * sin(e - M_PI_2);
247 #ifdef ELLIPSE_VERBOSE
248 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
249 i, s, e, x1, y1, x2, y2, x3, y3);
250 #endif
251 bpath[i].code = NR_CURVETO;
252 bpath[i].x1 = x1;
253 bpath[i].y1 = y1;
254 bpath[i].x2 = x2;
255 bpath[i].y2 = y2;
256 bpath[i].x3 = x3;
257 bpath[i].y3 = y3;
258 i++;
259 }
261 if (slice && ellipse->closed) {
262 bpath[i].code = NR_LINETO;
263 bpath[i].x3 = 0.0;
264 bpath[i].y3 = 0.0;
265 i++;
266 bpath[i].code = NR_LINETO;
267 bpath[i].x3 = bpath[0].x3;
268 bpath[i].y3 = bpath[0].y3;
269 i++;
270 } else if (ellipse->closed) {
271 bpath[i-1].x3 = bpath[0].x3;
272 bpath[i-1].y3 = bpath[0].y3;
273 }
275 bpath[i].code = NR_END;
276 SPCurve *c = SPCurve::new_from_bpath(nr_artpath_affine(bpath, aff));
277 g_assert(c != NULL);
279 sp_lpe_item_perform_path_effect(SP_LPE_ITEM (ellipse), c);
280 sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
281 c->unref();
282 }
284 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
285 {
286 g_assert(item != NULL);
287 g_assert(SP_IS_GENERICELLIPSE(item));
289 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
290 sp_genericellipse_normalize(ellipse);
291 NR::Matrix const i2d = sp_item_i2d_affine(item);
293 // figure out if we have a slice, whilst guarding against rounding errors
294 bool slice = false;
295 double len = fmod(ellipse->end - ellipse->start, SP_2PI);
296 if (len < 0.0) len += SP_2PI;
297 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
298 slice = false;
299 ellipse->end = ellipse->start + SP_2PI;
300 } else {
301 slice = true;
302 }
304 double rx = ellipse->rx.computed;
305 double ry = ellipse->ry.computed;
306 double cx = ellipse->cx.computed;
307 double cy = ellipse->cy.computed;
309 // Snap to the 4 quadrant points of the ellipse, but only if the arc
310 // spans far enough to include them
311 double angle = 0;
312 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
313 if (angle >= ellipse->start && angle <= ellipse->end) {
314 *p = NR::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
315 }
316 }
318 // And if we have a slice, also snap to the endpoints and the centre point
319 if (slice) {
320 // Add the centre, if we have a closed slice
321 if (ellipse->closed) {
322 *p = NR::Point(cx, cy) * i2d;
323 }
324 // Add the start point, if it's not coincident with a quadrant point
325 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
326 *p = NR::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
327 }
328 // Add the end point, if it's not coincident with a quadrant point
329 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
330 *p = NR::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
331 }
332 }
333 }
335 void
336 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
337 {
338 ellipse->start = fmod(ellipse->start, SP_2PI);
339 ellipse->end = fmod(ellipse->end, SP_2PI);
341 if (ellipse->start < 0.0)
342 ellipse->start += SP_2PI;
343 double diff = ellipse->start - ellipse->end;
344 if (diff >= 0.0)
345 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
347 /* Now we keep: 0 <= start < end <= 2*PI */
348 }
350 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
351 {
352 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
354 if (flags & SP_OBJECT_WRITE_EXT) {
355 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
356 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
357 repr = xml_doc->createElement("svg:path");
358 }
360 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
361 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
362 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
363 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
365 if (SP_IS_ARC(ellipse))
366 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
367 }
369 if (((SPObjectClass *) ge_parent_class)->write)
370 ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
372 return repr;
373 }
375 /* SVG <ellipse> element */
377 static void sp_ellipse_class_init(SPEllipseClass *klass);
378 static void sp_ellipse_init(SPEllipse *ellipse);
380 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
381 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
382 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
383 static gchar *sp_ellipse_description(SPItem *item);
385 static SPGenericEllipseClass *ellipse_parent_class;
387 GType
388 sp_ellipse_get_type(void)
389 {
390 static GType type = 0;
391 if (!type) {
392 GTypeInfo info = {
393 sizeof(SPEllipseClass),
394 NULL, /* base_init */
395 NULL, /* base_finalize */
396 (GClassInitFunc) sp_ellipse_class_init,
397 NULL, /* class_finalize */
398 NULL, /* class_data */
399 sizeof(SPEllipse),
400 16, /* n_preallocs */
401 (GInstanceInitFunc) sp_ellipse_init,
402 NULL, /* value_table */
403 };
404 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
405 }
406 return type;
407 }
409 static void sp_ellipse_class_init(SPEllipseClass *klass)
410 {
411 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
412 SPItemClass *item_class = (SPItemClass *) klass;
414 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
416 sp_object_class->build = sp_ellipse_build;
417 sp_object_class->write = sp_ellipse_write;
418 sp_object_class->set = sp_ellipse_set;
420 item_class->description = sp_ellipse_description;
421 }
423 static void
424 sp_ellipse_init(SPEllipse */*ellipse*/)
425 {
426 /* Nothing special */
427 }
429 static void
430 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
431 {
432 if (((SPObjectClass *) ellipse_parent_class)->build)
433 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
435 sp_object_read_attr(object, "cx");
436 sp_object_read_attr(object, "cy");
437 sp_object_read_attr(object, "rx");
438 sp_object_read_attr(object, "ry");
439 }
441 static Inkscape::XML::Node *
442 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
443 {
444 SPGenericEllipse *ellipse;
446 ellipse = SP_GENERICELLIPSE(object);
448 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
449 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
450 repr = xml_doc->createElement("svg:ellipse");
451 }
453 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
454 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
455 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
456 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
458 if (((SPObjectClass *) ellipse_parent_class)->write)
459 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
461 return repr;
462 }
464 static void
465 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
466 {
467 SPGenericEllipse *ellipse;
469 ellipse = SP_GENERICELLIPSE(object);
471 switch (key) {
472 case SP_ATTR_CX:
473 ellipse->cx.readOrUnset(value);
474 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
475 break;
476 case SP_ATTR_CY:
477 ellipse->cy.readOrUnset(value);
478 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
479 break;
480 case SP_ATTR_RX:
481 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
482 ellipse->rx.unset();
483 }
484 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
485 break;
486 case SP_ATTR_RY:
487 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
488 ellipse->ry.unset();
489 }
490 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
491 break;
492 default:
493 if (((SPObjectClass *) ellipse_parent_class)->set)
494 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
495 break;
496 }
497 }
499 static gchar *sp_ellipse_description(SPItem */*item*/)
500 {
501 return g_strdup(_("<b>Ellipse</b>"));
502 }
505 void
506 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
507 {
508 SPGenericEllipse *ge;
510 g_return_if_fail(ellipse != NULL);
511 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
513 ge = SP_GENERICELLIPSE(ellipse);
515 ge->cx.computed = x;
516 ge->cy.computed = y;
517 ge->rx.computed = rx;
518 ge->ry.computed = ry;
520 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
521 }
523 /* SVG <circle> element */
525 static void sp_circle_class_init(SPCircleClass *klass);
526 static void sp_circle_init(SPCircle *circle);
528 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
529 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
530 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
531 static gchar *sp_circle_description(SPItem *item);
533 static SPGenericEllipseClass *circle_parent_class;
535 GType
536 sp_circle_get_type(void)
537 {
538 static GType type = 0;
539 if (!type) {
540 GTypeInfo info = {
541 sizeof(SPCircleClass),
542 NULL, /* base_init */
543 NULL, /* base_finalize */
544 (GClassInitFunc) sp_circle_class_init,
545 NULL, /* class_finalize */
546 NULL, /* class_data */
547 sizeof(SPCircle),
548 16, /* n_preallocs */
549 (GInstanceInitFunc) sp_circle_init,
550 NULL, /* value_table */
551 };
552 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
553 }
554 return type;
555 }
557 static void
558 sp_circle_class_init(SPCircleClass *klass)
559 {
560 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
561 SPItemClass *item_class = (SPItemClass *) klass;
563 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
565 sp_object_class->build = sp_circle_build;
566 sp_object_class->write = sp_circle_write;
567 sp_object_class->set = sp_circle_set;
569 item_class->description = sp_circle_description;
570 }
572 static void
573 sp_circle_init(SPCircle */*circle*/)
574 {
575 /* Nothing special */
576 }
578 static void
579 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
580 {
581 if (((SPObjectClass *) circle_parent_class)->build)
582 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
584 sp_object_read_attr(object, "cx");
585 sp_object_read_attr(object, "cy");
586 sp_object_read_attr(object, "r");
587 }
589 static Inkscape::XML::Node *
590 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
591 {
592 SPGenericEllipse *ellipse;
594 ellipse = SP_GENERICELLIPSE(object);
596 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
597 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
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, repr, flags);
608 return repr;
609 }
611 static void
612 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
613 {
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 }
639 }
641 static gchar *sp_circle_description(SPItem */*item*/)
642 {
643 return g_strdup(_("<b>Circle</b>"));
644 }
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::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)
662 {
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;
680 }
682 static void
683 sp_arc_class_init(SPArcClass *klass)
684 {
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;
696 }
698 static void
699 sp_arc_init(SPArc */*arc*/)
700 {
701 /* Nothing special */
702 }
704 static void
705 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
706 {
707 if (((SPObjectClass *) arc_parent_class)->build)
708 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
710 Inkscape::Version version = sp_object_get_sodipodi_version(object);
712 sp_object_read_attr(object, "sodipodi:cx");
713 sp_object_read_attr(object, "sodipodi:cy");
714 sp_object_read_attr(object, "sodipodi:rx");
715 sp_object_read_attr(object, "sodipodi:ry");
717 sp_object_read_attr(object, "sodipodi:start");
718 sp_object_read_attr(object, "sodipodi:end");
719 sp_object_read_attr(object, "sodipodi:open");
720 }
722 /*
723 * sp_arc_set_elliptical_path_attribute:
724 *
725 * Convert center to endpoint parameterization and set it to repr.
726 *
727 * See SVG 1.0 Specification W3C Recommendation
728 * ``F.6 Ellptical arc implementation notes'' for more detail.
729 */
730 static gboolean
731 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
732 {
733 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
735 Inkscape::SVG::PathString str;
737 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
738 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
739 double rx = ge->rx.computed;
740 double ry = ge->ry.computed;
742 str.moveTo(p1);
744 double dt = fmod(ge->end - ge->start, SP_2PI);
745 if (fabs(dt) < 1e-6) {
746 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
747 str.arcTo(rx, ry, 0, true, true, ph)
748 .arcTo(rx, ry, 0, true, true, p2)
749 .closePath();
750 } else {
751 bool fa = (fabs(dt) > M_PI);
752 bool fs = (dt > 0);
753 str.arcTo(rx, ry, 0, fa, fs, p2);
754 if (ge->closed) {
755 NR::Point center = NR::Point(ge->cx.computed, ge->cy.computed);
756 str.lineTo(center).closePath();
757 }
758 }
760 repr->setAttribute("d", str.c_str());
761 return true;
762 }
764 static Inkscape::XML::Node *
765 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
766 {
767 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
768 SPArc *arc = SP_ARC(object);
770 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
771 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
772 repr = xml_doc->createElement("svg:path");
773 }
775 if (flags & SP_OBJECT_WRITE_EXT) {
776 repr->setAttribute("sodipodi:type", "arc");
777 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
778 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
779 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
780 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
782 // write start and end only if they are non-trivial; otherwise remove
783 gdouble len = fmod(ge->end - ge->start, SP_2PI);
784 if (len < 0.0) len += SP_2PI;
785 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
786 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
787 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
788 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
789 } else {
790 repr->setAttribute("sodipodi:end", NULL);
791 repr->setAttribute("sodipodi:start", NULL);
792 repr->setAttribute("sodipodi:open", NULL);
793 }
794 }
796 // write d=
797 sp_arc_set_elliptical_path_attribute(arc, repr);
799 if (((SPObjectClass *) arc_parent_class)->write)
800 ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
802 return repr;
803 }
805 static void
806 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
807 {
808 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
810 switch (key) {
811 case SP_ATTR_SODIPODI_CX:
812 ge->cx.readOrUnset(value);
813 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
814 break;
815 case SP_ATTR_SODIPODI_CY:
816 ge->cy.readOrUnset(value);
817 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
818 break;
819 case SP_ATTR_SODIPODI_RX:
820 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
821 ge->rx.unset();
822 }
823 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
824 break;
825 case SP_ATTR_SODIPODI_RY:
826 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
827 ge->ry.unset();
828 }
829 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
830 break;
831 case SP_ATTR_SODIPODI_START:
832 if (value) {
833 sp_svg_number_read_d(value, &ge->start);
834 } else {
835 ge->start = 0;
836 }
837 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
838 break;
839 case SP_ATTR_SODIPODI_END:
840 if (value) {
841 sp_svg_number_read_d(value, &ge->end);
842 } else {
843 ge->end = 2 * M_PI;
844 }
845 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
846 break;
847 case SP_ATTR_SODIPODI_OPEN:
848 ge->closed = (!value);
849 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
850 break;
851 default:
852 if (((SPObjectClass *) arc_parent_class)->set)
853 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
854 break;
855 }
856 }
858 static void
859 sp_arc_modified(SPObject *object, guint flags)
860 {
861 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
862 sp_shape_set_shape((SPShape *) object);
863 }
865 if (((SPObjectClass *) arc_parent_class)->modified)
866 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
867 }
869 static gchar *sp_arc_description(SPItem *item)
870 {
871 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
873 gdouble len = fmod(ge->end - ge->start, SP_2PI);
874 if (len < 0.0) len += SP_2PI;
875 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
876 if (ge->closed) {
877 return g_strdup(_("<b>Segment</b>"));
878 } else {
879 return g_strdup(_("<b>Arc</b>"));
880 }
881 } else {
882 return g_strdup(_("<b>Ellipse</b>"));
883 }
884 }
886 void
887 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
888 {
889 g_return_if_fail(arc != NULL);
890 g_return_if_fail(SP_IS_ARC(arc));
892 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
894 ge->cx.computed = x;
895 ge->cy.computed = y;
896 ge->rx.computed = rx;
897 ge->ry.computed = ry;
898 if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
899 ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
900 if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
901 ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
902 if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
903 ge->closed = 1;
904 else
905 ge->closed = 0;
907 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
908 }
910 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
911 {
912 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
914 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
915 ge->ry.computed * sin(arg) + ge->cy.computed);
916 }
919 /*
920 Local Variables:
921 mode:c++
922 c-file-style:"stroustrup"
923 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
924 indent-tabs-mode:nil
925 fill-column:99
926 End:
927 */
928 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :