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/stringstream.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 "sp-ellipse.h"
35 #include "prefs-utils.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);
77 static void sp_genericellipse_set_shape(SPShape *shape);
78 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr,
79 guint flags);
81 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
83 static SPShapeClass *ge_parent_class;
85 GType
86 sp_genericellipse_get_type(void)
87 {
88 static GType type = 0;
89 if (!type) {
90 GTypeInfo info = {
91 sizeof(SPGenericEllipseClass),
92 NULL, /* base_init */
93 NULL, /* base_finalize */
94 (GClassInitFunc) sp_genericellipse_class_init,
95 NULL, /* class_finalize */
96 NULL, /* class_data */
97 sizeof(SPGenericEllipse),
98 16, /* n_preallocs */
99 (GInstanceInitFunc) sp_genericellipse_init,
100 NULL, /* value_table */
101 };
102 type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
103 }
104 return type;
105 }
107 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
108 {
109 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
110 SPItemClass *item_class = (SPItemClass *) klass;
111 SPShapeClass *shape_class = (SPShapeClass *) klass;
113 ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
115 sp_object_class->update = sp_genericellipse_update;
116 sp_object_class->write = sp_genericellipse_write;
118 item_class->snappoints = sp_genericellipse_snappoints;
120 shape_class->set_shape = sp_genericellipse_set_shape;
121 }
123 static void
124 sp_genericellipse_init(SPGenericEllipse *ellipse)
125 {
126 ellipse->cx.unset();
127 ellipse->cy.unset();
128 ellipse->rx.unset();
129 ellipse->ry.unset();
131 ellipse->start = 0.0;
132 ellipse->end = SP_2PI;
133 ellipse->closed = TRUE;
134 }
136 static void
137 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
138 {
139 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
140 SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
141 SPStyle const *style = object->style;
142 double const d = 1.0 / NR::expansion(((SPItemCtx const *) ctx)->i2vp);
143 double const em = style->font_size.computed;
144 double const ex = em * 0.5; // fixme: get from pango or libnrtype
145 ellipse->cx.update(em, ex, d);
146 ellipse->cy.update(em, ex, d);
147 ellipse->rx.update(em, ex, d);
148 ellipse->ry.update(em, ex, d);
149 sp_shape_set_shape((SPShape *) object);
150 }
152 if (((SPObjectClass *) ge_parent_class)->update)
153 ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
154 }
156 #define C1 0.552
158 /* fixme: Think (Lauris) */
160 static void sp_genericellipse_set_shape(SPShape *shape)
161 {
162 double cx, cy, rx, ry, s, e;
163 double x0, y0, x1, y1, x2, y2, x3, y3;
164 double len;
165 gint slice = FALSE;
166 gint i;
168 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
170 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
171 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
173 sp_genericellipse_normalize(ellipse);
175 cx = 0.0;
176 cy = 0.0;
177 rx = ellipse->rx.computed;
178 ry = ellipse->ry.computed;
180 // figure out if we have a slice, guarding against rounding errors
181 len = fmod(ellipse->end - ellipse->start, SP_2PI);
182 if (len < 0.0) len += SP_2PI;
183 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
184 slice = FALSE;
185 ellipse->end = ellipse->start + SP_2PI;
186 } else {
187 slice = TRUE;
188 }
190 NR::Matrix aff = NR::Matrix(NR::scale(rx, ry));
191 aff[4] = ellipse->cx.computed;
192 aff[5] = ellipse->cy.computed;
194 NArtBpath bpath[16];
195 i = 0;
196 if (ellipse->closed) {
197 bpath[i].code = NR_MOVETO;
198 } else {
199 bpath[i].code = NR_MOVETO_OPEN;
200 }
201 bpath[i].x3 = cos(ellipse->start);
202 bpath[i].y3 = sin(ellipse->start);
203 i++;
205 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
206 e = s + M_PI_2;
207 if (e > ellipse->end)
208 e = ellipse->end;
209 len = C1 * (e - s) / M_PI_2;
210 x0 = cos(s);
211 y0 = sin(s);
212 x1 = x0 + len * cos(s + M_PI_2);
213 y1 = y0 + len * sin(s + M_PI_2);
214 x3 = cos(e);
215 y3 = sin(e);
216 x2 = x3 + len * cos(e - M_PI_2);
217 y2 = y3 + len * sin(e - M_PI_2);
218 #ifdef ELLIPSE_VERBOSE
219 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
220 i, s, e, x1, y1, x2, y2, x3, y3);
221 #endif
222 bpath[i].code = NR_CURVETO;
223 bpath[i].x1 = x1;
224 bpath[i].y1 = y1;
225 bpath[i].x2 = x2;
226 bpath[i].y2 = y2;
227 bpath[i].x3 = x3;
228 bpath[i].y3 = y3;
229 i++;
230 }
232 if (slice && ellipse->closed) {
233 bpath[i].code = NR_LINETO;
234 bpath[i].x3 = 0.0;
235 bpath[i].y3 = 0.0;
236 i++;
237 bpath[i].code = NR_LINETO;
238 bpath[i].x3 = bpath[0].x3;
239 bpath[i].y3 = bpath[0].y3;
240 i++;
241 } else if (ellipse->closed) {
242 bpath[i-1].x3 = bpath[0].x3;
243 bpath[i-1].y3 = bpath[0].y3;
244 }
246 bpath[i].code = NR_END;
247 SPCurve *c = sp_curve_new_from_bpath(nr_artpath_affine(bpath, aff));
248 g_assert(c != NULL);
250 sp_shape_set_curve_insync((SPShape *) ellipse, c, TRUE);
251 sp_curve_unref(c);
252 }
254 static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p)
255 {
256 SPGenericEllipse const *ge = SP_GENERICELLIPSE(item);
258 NR::Matrix const i2d = sp_item_i2d_affine(item);
260 /* Add the centre */
261 *p = NR::Point(ge->cx.computed, ge->cy.computed) * i2d;
263 // TODO: add the ends of radii
264 }
266 void
267 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
268 {
269 ellipse->start = fmod(ellipse->start, SP_2PI);
270 ellipse->end = fmod(ellipse->end, SP_2PI);
272 if (ellipse->start < 0.0)
273 ellipse->start += SP_2PI;
274 double diff = ellipse->start - ellipse->end;
275 if (diff >= 0.0)
276 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
278 /* Now we keep: 0 <= start < end <= 2*PI */
279 }
281 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
282 {
283 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
285 if (flags & SP_OBJECT_WRITE_EXT) {
286 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
287 repr = sp_repr_new("svg:path");
288 }
290 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
291 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
292 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
293 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
295 if (SP_IS_ARC(ellipse))
296 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
297 }
299 if (((SPObjectClass *) ge_parent_class)->write)
300 ((SPObjectClass *) ge_parent_class)->write(object, repr, flags);
302 return repr;
303 }
305 /* SVG <ellipse> element */
307 static void sp_ellipse_class_init(SPEllipseClass *klass);
308 static void sp_ellipse_init(SPEllipse *ellipse);
310 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
311 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
312 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
313 static gchar *sp_ellipse_description(SPItem *item);
315 static SPGenericEllipseClass *ellipse_parent_class;
317 GType
318 sp_ellipse_get_type(void)
319 {
320 static GType type = 0;
321 if (!type) {
322 GTypeInfo info = {
323 sizeof(SPEllipseClass),
324 NULL, /* base_init */
325 NULL, /* base_finalize */
326 (GClassInitFunc) sp_ellipse_class_init,
327 NULL, /* class_finalize */
328 NULL, /* class_data */
329 sizeof(SPEllipse),
330 16, /* n_preallocs */
331 (GInstanceInitFunc) sp_ellipse_init,
332 NULL, /* value_table */
333 };
334 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
335 }
336 return type;
337 }
339 static void sp_ellipse_class_init(SPEllipseClass *klass)
340 {
341 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
342 SPItemClass *item_class = (SPItemClass *) klass;
344 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
346 sp_object_class->build = sp_ellipse_build;
347 sp_object_class->write = sp_ellipse_write;
348 sp_object_class->set = sp_ellipse_set;
350 item_class->description = sp_ellipse_description;
351 }
353 static void
354 sp_ellipse_init(SPEllipse *ellipse)
355 {
356 /* Nothing special */
357 }
359 static void
360 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
361 {
362 if (((SPObjectClass *) ellipse_parent_class)->build)
363 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
365 sp_object_read_attr(object, "cx");
366 sp_object_read_attr(object, "cy");
367 sp_object_read_attr(object, "rx");
368 sp_object_read_attr(object, "ry");
369 }
371 static Inkscape::XML::Node *
372 sp_ellipse_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
373 {
374 SPGenericEllipse *ellipse;
376 ellipse = SP_GENERICELLIPSE(object);
378 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
379 repr = sp_repr_new("svg:ellipse");
380 }
382 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
383 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
384 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
385 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
387 if (((SPObjectClass *) ellipse_parent_class)->write)
388 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, repr, flags);
390 return repr;
391 }
393 static void
394 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
395 {
396 SPGenericEllipse *ellipse;
398 ellipse = SP_GENERICELLIPSE(object);
400 switch (key) {
401 case SP_ATTR_CX:
402 ellipse->cx.readOrUnset(value);
403 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
404 break;
405 case SP_ATTR_CY:
406 ellipse->cy.readOrUnset(value);
407 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
408 break;
409 case SP_ATTR_RX:
410 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
411 ellipse->rx.unset();
412 }
413 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
414 break;
415 case SP_ATTR_RY:
416 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
417 ellipse->ry.unset();
418 }
419 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
420 break;
421 default:
422 if (((SPObjectClass *) ellipse_parent_class)->set)
423 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
424 break;
425 }
426 }
428 static gchar *sp_ellipse_description(SPItem *item)
429 {
430 return g_strdup(_("<b>Ellipse</b>"));
431 }
434 void
435 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
436 {
437 SPGenericEllipse *ge;
439 g_return_if_fail(ellipse != NULL);
440 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
442 ge = SP_GENERICELLIPSE(ellipse);
444 ge->cx.computed = x;
445 ge->cy.computed = y;
446 ge->rx.computed = rx;
447 ge->ry.computed = ry;
449 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
450 }
452 /* SVG <circle> element */
454 static void sp_circle_class_init(SPCircleClass *klass);
455 static void sp_circle_init(SPCircle *circle);
457 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
458 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
459 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
460 static gchar *sp_circle_description(SPItem *item);
462 static SPGenericEllipseClass *circle_parent_class;
464 GType
465 sp_circle_get_type(void)
466 {
467 static GType type = 0;
468 if (!type) {
469 GTypeInfo info = {
470 sizeof(SPCircleClass),
471 NULL, /* base_init */
472 NULL, /* base_finalize */
473 (GClassInitFunc) sp_circle_class_init,
474 NULL, /* class_finalize */
475 NULL, /* class_data */
476 sizeof(SPCircle),
477 16, /* n_preallocs */
478 (GInstanceInitFunc) sp_circle_init,
479 NULL, /* value_table */
480 };
481 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
482 }
483 return type;
484 }
486 static void
487 sp_circle_class_init(SPCircleClass *klass)
488 {
489 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
490 SPItemClass *item_class = (SPItemClass *) klass;
492 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
494 sp_object_class->build = sp_circle_build;
495 sp_object_class->write = sp_circle_write;
496 sp_object_class->set = sp_circle_set;
498 item_class->description = sp_circle_description;
499 }
501 static void
502 sp_circle_init(SPCircle *circle)
503 {
504 /* Nothing special */
505 }
507 static void
508 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
509 {
510 if (((SPObjectClass *) circle_parent_class)->build)
511 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
513 sp_object_read_attr(object, "cx");
514 sp_object_read_attr(object, "cy");
515 sp_object_read_attr(object, "r");
516 }
518 static Inkscape::XML::Node *
519 sp_circle_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
520 {
521 SPGenericEllipse *ellipse;
523 ellipse = SP_GENERICELLIPSE(object);
525 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
526 repr = sp_repr_new("svg:circle");
527 }
529 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
530 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
531 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
533 if (((SPObjectClass *) circle_parent_class)->write)
534 ((SPObjectClass *) circle_parent_class)->write(object, repr, flags);
536 return repr;
537 }
539 static void
540 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
541 {
542 SPGenericEllipse *ge;
544 ge = SP_GENERICELLIPSE(object);
546 switch (key) {
547 case SP_ATTR_CX:
548 ge->cx.readOrUnset(value);
549 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
550 break;
551 case SP_ATTR_CY:
552 ge->cy.readOrUnset(value);
553 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
554 break;
555 case SP_ATTR_R:
556 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
557 ge->rx.unset();
558 }
559 ge->ry = ge->rx;
560 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
561 break;
562 default:
563 if (((SPObjectClass *) circle_parent_class)->set)
564 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
565 break;
566 }
567 }
569 static gchar *sp_circle_description(SPItem *item)
570 {
571 return g_strdup(_("<b>Circle</b>"));
572 }
574 /* <path sodipodi:type="arc"> element */
576 static void sp_arc_class_init(SPArcClass *klass);
577 static void sp_arc_init(SPArc *arc);
579 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
580 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
581 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
582 static void sp_arc_modified(SPObject *object, guint flags);
584 static gchar *sp_arc_description(SPItem *item);
586 static SPGenericEllipseClass *arc_parent_class;
588 GType
589 sp_arc_get_type(void)
590 {
591 static GType type = 0;
592 if (!type) {
593 GTypeInfo info = {
594 sizeof(SPArcClass),
595 NULL, /* base_init */
596 NULL, /* base_finalize */
597 (GClassInitFunc) sp_arc_class_init,
598 NULL, /* class_finalize */
599 NULL, /* class_data */
600 sizeof(SPArc),
601 16, /* n_preallocs */
602 (GInstanceInitFunc) sp_arc_init,
603 NULL, /* value_table */
604 };
605 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
606 }
607 return type;
608 }
610 static void
611 sp_arc_class_init(SPArcClass *klass)
612 {
613 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
614 SPItemClass *item_class = (SPItemClass *) klass;
616 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
618 sp_object_class->build = sp_arc_build;
619 sp_object_class->write = sp_arc_write;
620 sp_object_class->set = sp_arc_set;
621 sp_object_class->modified = sp_arc_modified;
623 item_class->description = sp_arc_description;
624 }
626 static void
627 sp_arc_init(SPArc *arc)
628 {
629 /* Nothing special */
630 }
632 static void
633 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
634 {
635 if (((SPObjectClass *) arc_parent_class)->build)
636 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
638 Inkscape::Version version = sp_object_get_sodipodi_version(object);
640 sp_object_read_attr(object, "sodipodi:cx");
641 sp_object_read_attr(object, "sodipodi:cy");
642 sp_object_read_attr(object, "sodipodi:rx");
643 sp_object_read_attr(object, "sodipodi:ry");
645 sp_object_read_attr(object, "sodipodi:start");
646 sp_object_read_attr(object, "sodipodi:end");
647 sp_object_read_attr(object, "sodipodi:open");
648 }
650 /*
651 * sp_arc_set_elliptical_path_attribute:
652 *
653 * Convert center to endpoint parameterization and set it to repr.
654 *
655 * See SVG 1.0 Specification W3C Recommendation
656 * ``F.6 Ellptical arc implementation notes'' for more detail.
657 */
658 static gboolean
659 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
660 {
661 gint fa, fs;
662 gdouble dt;
663 Inkscape::SVGOStringStream os;
665 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
667 NR::Point p1 = sp_arc_get_xy(arc, ge->start);
668 NR::Point p2 = sp_arc_get_xy(arc, ge->end);
670 dt = fmod(ge->end - ge->start, SP_2PI);
671 if (fabs(dt) < 1e-6) {
672 NR::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
673 os << "M " << p1[NR::X] << " " << p1[NR::Y]
674 << " A " << ge->rx.computed << " " << ge->ry.computed
675 << " 0 1 1 " << " " << ph[NR::X] << "," << ph[NR::Y]
676 << " A " << ge->rx.computed << " " << ge->ry.computed
677 << " 0 1 1 " << " " << p2[NR::X] << " " << p2[NR::Y] << " z";
678 } else {
679 fa = (fabs(dt) > M_PI) ? 1 : 0;
680 fs = (dt > 0) ? 1 : 0;
681 #ifdef ARC_VERBOSE
682 g_print("start:%g end:%g fa=%d fs=%d\n", ge->start, ge->end, fa, fs);
683 #endif
684 if (ge->closed) {
685 os << "M " << p1[NR::X] << "," << p1[NR::Y]
686 << " A " << ge->rx.computed << "," << ge->ry.computed
687 << " 0 " << fa << " " << fs << " " << p2[NR::X] << "," << p2[NR::Y]
688 << " L " << ge->cx.computed << "," << ge->cy.computed << " z";
689 } else {
690 os << "M " << p1[NR::X] << "," << p1[NR::Y]
691 << " A " << ge->rx.computed << "," << ge->ry.computed
692 << " 0 " << fa << " " << fs << " " << p2[NR::X] << "," << p2[NR::Y];
694 }
695 }
696 repr->setAttribute("d", os.str().c_str());
697 return true;
698 }
700 static Inkscape::XML::Node *
701 sp_arc_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
702 {
703 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
704 SPArc *arc = SP_ARC(object);
706 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
707 repr = sp_repr_new("svg:path");
708 }
710 if (flags & SP_OBJECT_WRITE_EXT) {
711 repr->setAttribute("sodipodi:type", "arc");
712 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
713 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
714 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
715 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
717 // write start and end only if they are non-trivial; otherwise remove
718 gdouble len = fmod(ge->end - ge->start, SP_2PI);
719 if (len < 0.0) len += SP_2PI;
720 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
721 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
722 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
723 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
724 } else {
725 repr->setAttribute("sodipodi:end", NULL);
726 repr->setAttribute("sodipodi:start", NULL);
727 repr->setAttribute("sodipodi:open", NULL);
728 }
729 }
731 // write d=
732 sp_arc_set_elliptical_path_attribute(arc, repr);
734 if (((SPObjectClass *) arc_parent_class)->write)
735 ((SPObjectClass *) arc_parent_class)->write(object, repr, flags);
737 return repr;
738 }
740 static void
741 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
742 {
743 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
745 switch (key) {
746 case SP_ATTR_SODIPODI_CX:
747 ge->cx.readOrUnset(value);
748 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
749 break;
750 case SP_ATTR_SODIPODI_CY:
751 ge->cy.readOrUnset(value);
752 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
753 break;
754 case SP_ATTR_SODIPODI_RX:
755 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
756 ge->rx.unset();
757 }
758 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
759 break;
760 case SP_ATTR_SODIPODI_RY:
761 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
762 ge->ry.unset();
763 }
764 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
765 break;
766 case SP_ATTR_SODIPODI_START:
767 if (value) {
768 sp_svg_number_read_d(value, &ge->start);
769 } else {
770 ge->start = 0;
771 }
772 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
773 break;
774 case SP_ATTR_SODIPODI_END:
775 if (value) {
776 sp_svg_number_read_d(value, &ge->end);
777 } else {
778 ge->end = 2 * M_PI;
779 }
780 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
781 break;
782 case SP_ATTR_SODIPODI_OPEN:
783 ge->closed = (!value);
784 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
785 break;
786 default:
787 if (((SPObjectClass *) arc_parent_class)->set)
788 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
789 break;
790 }
791 }
793 static void
794 sp_arc_modified(SPObject *object, guint flags)
795 {
796 if (flags & SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG) {
797 sp_shape_set_shape((SPShape *) object);
798 }
800 if (((SPObjectClass *) arc_parent_class)->modified)
801 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
802 }
804 static gchar *sp_arc_description(SPItem *item)
805 {
806 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
808 gdouble len = fmod(ge->end - ge->start, SP_2PI);
809 if (len < 0.0) len += SP_2PI;
810 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
811 if (ge->closed) {
812 return g_strdup(_("<b>Segment</b>"));
813 } else {
814 return g_strdup(_("<b>Arc</b>"));
815 }
816 } else {
817 return g_strdup(_("<b>Ellipse</b>"));
818 }
819 }
821 void
822 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
823 {
824 g_return_if_fail(arc != NULL);
825 g_return_if_fail(SP_IS_ARC(arc));
827 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
829 ge->cx.computed = x;
830 ge->cy.computed = y;
831 ge->rx.computed = rx;
832 ge->ry.computed = ry;
833 if (prefs_get_double_attribute("tools.shapes.arc", "start", 0.0) != 0)
834 ge->start = prefs_get_double_attribute("tools.shapes.arc", "start", 0.0);
835 if (prefs_get_double_attribute("tools.shapes.arc", "end", 0.0) != 0)
836 ge->end = prefs_get_double_attribute("tools.shapes.arc", "end", 0.0);
837 if (!prefs_get_string_attribute("tools.shapes.arc", "open"))
838 ge->closed = 1;
839 else
840 ge->closed = 0;
842 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
843 }
845 NR::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
846 {
847 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
849 return NR::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
850 ge->ry.computed * sin(arg) + ge->cy.computed);
851 }
854 /*
855 Local Variables:
856 mode:c++
857 c-file-style:"stroustrup"
858 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
859 indent-tabs-mode:nil
860 fill-column:99
861 End:
862 */
863 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :