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>
31 #include <2geom/pathvector.h>
32 #include "document.h"
33 #include "sp-ellipse.h"
34 #include "preferences.h"
35 #include "snap-candidate.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, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
77 static void sp_genericellipse_set_shape(SPShape *shape);
78 static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
80 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
81 guint flags);
83 static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
85 static SPShapeClass *ge_parent_class;
87 GType
88 sp_genericellipse_get_type(void)
89 {
90 static GType type = 0;
91 if (!type) {
92 GTypeInfo info = {
93 sizeof(SPGenericEllipseClass),
94 NULL, /* base_init */
95 NULL, /* base_finalize */
96 (GClassInitFunc) sp_genericellipse_class_init,
97 NULL, /* class_finalize */
98 NULL, /* class_data */
99 sizeof(SPGenericEllipse),
100 16, /* n_preallocs */
101 (GInstanceInitFunc) sp_genericellipse_init,
102 NULL, /* value_table */
103 };
104 type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
105 }
106 return type;
107 }
109 static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
110 {
111 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
112 SPItemClass *item_class = (SPItemClass *) klass;
113 SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass;
114 SPShapeClass *shape_class = (SPShapeClass *) klass;
116 ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
118 sp_object_class->update = sp_genericellipse_update;
119 sp_object_class->write = sp_genericellipse_write;
121 item_class->snappoints = sp_genericellipse_snappoints;
123 shape_class->set_shape = sp_genericellipse_set_shape;
124 lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect;
125 }
127 static void
128 sp_genericellipse_init(SPGenericEllipse *ellipse)
129 {
130 ellipse->cx.unset();
131 ellipse->cy.unset();
132 ellipse->rx.unset();
133 ellipse->ry.unset();
135 ellipse->start = 0.0;
136 ellipse->end = SP_2PI;
137 ellipse->closed = TRUE;
138 }
140 static void
141 sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
142 {
143 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
144 SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
145 SPStyle const *style = object->style;
146 Geom::OptRect viewbox = ((SPItemCtx const *) ctx)->vp;
147 if (viewbox) {
148 double const dx = viewbox->width();
149 double const dy = viewbox->height();
150 double const dr = sqrt(dx*dx + dy*dy)/sqrt(2);
151 double const em = style->font_size.computed;
152 double const ex = em * 0.5; // fixme: get from pango or libnrtype
153 ellipse->cx.update(em, ex, dx);
154 ellipse->cy.update(em, ex, dy);
155 ellipse->rx.update(em, ex, dr);
156 ellipse->ry.update(em, ex, dr);
157 sp_shape_set_shape((SPShape *) object);
158 }
159 }
161 if (((SPObjectClass *) ge_parent_class)->update)
162 ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
163 }
165 static void
166 sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
167 {
168 SPShape *shape = (SPShape *) lpeitem;
169 sp_genericellipse_set_shape(shape);
171 if (write) {
172 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
173 if ( shape->curve != NULL ) {
174 gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
175 repr->setAttribute("d", str);
176 g_free(str);
177 } else {
178 repr->setAttribute("d", NULL);
179 }
180 }
182 ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
183 }
185 /* fixme: Think (Lauris) */
186 /* Can't we use arcto in this method? */
187 static void sp_genericellipse_set_shape(SPShape *shape)
188 {
189 if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) {
190 g_warning ("The ellipse shape has unknown LPE on it! Convert to path to make it editable preserving the appearance; editing it as ellipse will remove the bad LPE");
191 if (SP_OBJECT_REPR(shape)->attribute("d")) {
192 // unconditionally read the curve from d, if any, to preserve appearance
193 Geom::PathVector pv = sp_svg_read_pathv(SP_OBJECT_REPR(shape)->attribute("d"));
194 SPCurve *cold = new SPCurve(pv);
195 sp_shape_set_curve_insync (shape, cold, TRUE);
196 cold->unref();
197 }
198 return;
199 }
201 double rx, ry, s, e;
202 double x0, y0, x1, y1, x2, y2, x3, y3;
203 double len;
204 gint slice = FALSE;
205 // gint i;
207 SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
209 if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
210 if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
212 sp_genericellipse_normalize(ellipse);
214 rx = ellipse->rx.computed;
215 ry = ellipse->ry.computed;
217 // figure out if we have a slice, guarding against rounding errors
218 len = fmod(ellipse->end - ellipse->start, SP_2PI);
219 if (len < 0.0) len += SP_2PI;
220 if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
221 slice = FALSE;
222 ellipse->end = ellipse->start + SP_2PI;
223 } else {
224 slice = TRUE;
225 }
227 SPCurve * curve = new SPCurve();
228 curve->moveto(cos(ellipse->start), sin(ellipse->start));
230 for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
231 e = s + M_PI_2;
232 if (e > ellipse->end)
233 e = ellipse->end;
234 len = 4*tan((e - s)/4)/3;
235 x0 = cos(s);
236 y0 = sin(s);
237 x1 = x0 + len * cos(s + M_PI_2);
238 y1 = y0 + len * sin(s + M_PI_2);
239 x3 = cos(e);
240 y3 = sin(e);
241 x2 = x3 + len * cos(e - M_PI_2);
242 y2 = y3 + len * sin(e - M_PI_2);
243 #ifdef ELLIPSE_VERBOSE
244 g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
245 i, s, e, x1, y1, x2, y2, x3, y3);
246 #endif
247 curve->curveto(x1,y1, x2,y2, x3,y3);
248 }
250 if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary?
251 curve->lineto(0., 0.);
252 }
253 if (ellipse->closed) {
254 curve->closepath();
255 }
257 Geom::Matrix aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
258 curve->transform(aff);
260 /* Reset the shape'scurve to the "original_curve"
261 * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
262 sp_shape_set_curve_insync (shape, curve, TRUE);
263 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
264 SPCurve *c_lpe = curve->copy();
265 bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
266 if (success) {
267 sp_shape_set_curve_insync (shape, c_lpe, TRUE);
268 }
269 c_lpe->unref();
270 }
271 curve->unref();
272 }
274 static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
275 {
276 g_assert(item != NULL);
277 g_assert(SP_IS_GENERICELLIPSE(item));
279 // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
280 if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
281 return;
282 }
284 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
285 sp_genericellipse_normalize(ellipse);
286 Geom::Matrix const i2d = sp_item_i2d_affine(item);
288 // figure out if we have a slice, while 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 Geom::Point pt;
306 // Snap to the 4 quadrant points of the ellipse, but only if the arc
307 // spans far enough to include them
308 if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this?
309 double angle = 0;
310 for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
311 if (angle >= ellipse->start && angle <= ellipse->end) {
312 pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d;
313 p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT));
314 }
315 }
316 }
318 // Add the centre, if we have a closed slice or when explicitly asked for
319 if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) {
320 pt = Geom::Point(cx, cy) * i2d;
321 p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_CENTER, Inkscape::SNAPTARGET_CENTER));
322 }
324 // And if we have a slice, also snap to the endpoints
325 if (snapprefs->getSnapToItemNode() && slice) {
326 // Add the start point, if it's not coincident with a quadrant point
327 if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
328 pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d;
329 p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
330 }
331 // Add the end point, if it's not coincident with a quadrant point
332 if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
333 pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d;
334 p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
335 }
336 }
337 }
339 void
340 sp_genericellipse_normalize(SPGenericEllipse *ellipse)
341 {
342 ellipse->start = fmod(ellipse->start, SP_2PI);
343 ellipse->end = fmod(ellipse->end, SP_2PI);
345 if (ellipse->start < 0.0)
346 ellipse->start += SP_2PI;
347 double diff = ellipse->start - ellipse->end;
348 if (diff >= 0.0)
349 ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
351 /* Now we keep: 0 <= start < end <= 2*PI */
352 }
354 static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
355 {
356 SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
358 if (flags & SP_OBJECT_WRITE_EXT) {
359 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
360 repr = xml_doc->createElement("svg:path");
361 }
363 sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
364 sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
365 sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
366 sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
368 if (SP_IS_ARC(ellipse))
369 sp_arc_set_elliptical_path_attribute(SP_ARC(object), SP_OBJECT_REPR(object));
370 }
372 if (((SPObjectClass *) ge_parent_class)->write)
373 ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
375 return repr;
376 }
378 /* SVG <ellipse> element */
380 static void sp_ellipse_class_init(SPEllipseClass *klass);
381 static void sp_ellipse_init(SPEllipse *ellipse);
383 static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
384 static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
385 static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
386 static gchar *sp_ellipse_description(SPItem *item);
388 static SPGenericEllipseClass *ellipse_parent_class;
390 GType
391 sp_ellipse_get_type(void)
392 {
393 static GType type = 0;
394 if (!type) {
395 GTypeInfo info = {
396 sizeof(SPEllipseClass),
397 NULL, /* base_init */
398 NULL, /* base_finalize */
399 (GClassInitFunc) sp_ellipse_class_init,
400 NULL, /* class_finalize */
401 NULL, /* class_data */
402 sizeof(SPEllipse),
403 16, /* n_preallocs */
404 (GInstanceInitFunc) sp_ellipse_init,
405 NULL, /* value_table */
406 };
407 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
408 }
409 return type;
410 }
412 static void sp_ellipse_class_init(SPEllipseClass *klass)
413 {
414 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
415 SPItemClass *item_class = (SPItemClass *) klass;
417 ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
419 sp_object_class->build = sp_ellipse_build;
420 sp_object_class->write = sp_ellipse_write;
421 sp_object_class->set = sp_ellipse_set;
423 item_class->description = sp_ellipse_description;
424 }
426 static void
427 sp_ellipse_init(SPEllipse */*ellipse*/)
428 {
429 /* Nothing special */
430 }
432 static void
433 sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
434 {
435 if (((SPObjectClass *) ellipse_parent_class)->build)
436 (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
438 sp_object_read_attr(object, "cx");
439 sp_object_read_attr(object, "cy");
440 sp_object_read_attr(object, "rx");
441 sp_object_read_attr(object, "ry");
442 }
444 static Inkscape::XML::Node *
445 sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
446 {
447 SPGenericEllipse *ellipse;
449 ellipse = SP_GENERICELLIPSE(object);
451 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
452 repr = xml_doc->createElement("svg:ellipse");
453 }
455 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
456 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
457 sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
458 sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
460 if (((SPObjectClass *) ellipse_parent_class)->write)
461 (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
463 return repr;
464 }
466 static void
467 sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
468 {
469 SPGenericEllipse *ellipse;
471 ellipse = SP_GENERICELLIPSE(object);
473 switch (key) {
474 case SP_ATTR_CX:
475 ellipse->cx.readOrUnset(value);
476 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
477 break;
478 case SP_ATTR_CY:
479 ellipse->cy.readOrUnset(value);
480 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
481 break;
482 case SP_ATTR_RX:
483 if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
484 ellipse->rx.unset();
485 }
486 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
487 break;
488 case SP_ATTR_RY:
489 if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
490 ellipse->ry.unset();
491 }
492 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
493 break;
494 default:
495 if (((SPObjectClass *) ellipse_parent_class)->set)
496 ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
497 break;
498 }
499 }
501 static gchar *sp_ellipse_description(SPItem */*item*/)
502 {
503 return g_strdup(_("<b>Ellipse</b>"));
504 }
507 void
508 sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
509 {
510 SPGenericEllipse *ge;
512 g_return_if_fail(ellipse != NULL);
513 g_return_if_fail(SP_IS_ELLIPSE(ellipse));
515 ge = SP_GENERICELLIPSE(ellipse);
517 ge->cx.computed = x;
518 ge->cy.computed = y;
519 ge->rx.computed = rx;
520 ge->ry.computed = ry;
522 ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
523 }
525 /* SVG <circle> element */
527 static void sp_circle_class_init(SPCircleClass *klass);
528 static void sp_circle_init(SPCircle *circle);
530 static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
531 static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
532 static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
533 static gchar *sp_circle_description(SPItem *item);
535 static SPGenericEllipseClass *circle_parent_class;
537 GType
538 sp_circle_get_type(void)
539 {
540 static GType type = 0;
541 if (!type) {
542 GTypeInfo info = {
543 sizeof(SPCircleClass),
544 NULL, /* base_init */
545 NULL, /* base_finalize */
546 (GClassInitFunc) sp_circle_class_init,
547 NULL, /* class_finalize */
548 NULL, /* class_data */
549 sizeof(SPCircle),
550 16, /* n_preallocs */
551 (GInstanceInitFunc) sp_circle_init,
552 NULL, /* value_table */
553 };
554 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
555 }
556 return type;
557 }
559 static void
560 sp_circle_class_init(SPCircleClass *klass)
561 {
562 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
563 SPItemClass *item_class = (SPItemClass *) klass;
565 circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
567 sp_object_class->build = sp_circle_build;
568 sp_object_class->write = sp_circle_write;
569 sp_object_class->set = sp_circle_set;
571 item_class->description = sp_circle_description;
572 }
574 static void
575 sp_circle_init(SPCircle */*circle*/)
576 {
577 /* Nothing special */
578 }
580 static void
581 sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
582 {
583 if (((SPObjectClass *) circle_parent_class)->build)
584 (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
586 sp_object_read_attr(object, "cx");
587 sp_object_read_attr(object, "cy");
588 sp_object_read_attr(object, "r");
589 }
591 static Inkscape::XML::Node *
592 sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
593 {
594 SPGenericEllipse *ellipse;
596 ellipse = SP_GENERICELLIPSE(object);
598 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
599 repr = xml_doc->createElement("svg:circle");
600 }
602 sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
603 sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
604 sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
606 if (((SPObjectClass *) circle_parent_class)->write)
607 ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
609 return repr;
610 }
612 static void
613 sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
614 {
615 SPGenericEllipse *ge;
617 ge = SP_GENERICELLIPSE(object);
619 switch (key) {
620 case SP_ATTR_CX:
621 ge->cx.readOrUnset(value);
622 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
623 break;
624 case SP_ATTR_CY:
625 ge->cy.readOrUnset(value);
626 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
627 break;
628 case SP_ATTR_R:
629 if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
630 ge->rx.unset();
631 }
632 ge->ry = ge->rx;
633 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
634 break;
635 default:
636 if (((SPObjectClass *) circle_parent_class)->set)
637 ((SPObjectClass *) circle_parent_class)->set(object, key, value);
638 break;
639 }
640 }
642 static gchar *sp_circle_description(SPItem */*item*/)
643 {
644 return g_strdup(_("<b>Circle</b>"));
645 }
647 /* <path sodipodi:type="arc"> element */
649 static void sp_arc_class_init(SPArcClass *klass);
650 static void sp_arc_init(SPArc *arc);
652 static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
653 static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
654 static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
655 static void sp_arc_modified(SPObject *object, guint flags);
657 static gchar *sp_arc_description(SPItem *item);
659 static SPGenericEllipseClass *arc_parent_class;
661 GType
662 sp_arc_get_type(void)
663 {
664 static GType type = 0;
665 if (!type) {
666 GTypeInfo info = {
667 sizeof(SPArcClass),
668 NULL, /* base_init */
669 NULL, /* base_finalize */
670 (GClassInitFunc) sp_arc_class_init,
671 NULL, /* class_finalize */
672 NULL, /* class_data */
673 sizeof(SPArc),
674 16, /* n_preallocs */
675 (GInstanceInitFunc) sp_arc_init,
676 NULL, /* value_table */
677 };
678 type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
679 }
680 return type;
681 }
683 static void
684 sp_arc_class_init(SPArcClass *klass)
685 {
686 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
687 SPItemClass *item_class = (SPItemClass *) klass;
689 arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
691 sp_object_class->build = sp_arc_build;
692 sp_object_class->write = sp_arc_write;
693 sp_object_class->set = sp_arc_set;
694 sp_object_class->modified = sp_arc_modified;
696 item_class->description = sp_arc_description;
697 }
699 static void
700 sp_arc_init(SPArc */*arc*/)
701 {
702 /* Nothing special */
703 }
705 static void
706 sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
707 {
708 if (((SPObjectClass *) arc_parent_class)->build)
709 (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
711 sp_object_read_attr(object, "sodipodi:cx");
712 sp_object_read_attr(object, "sodipodi:cy");
713 sp_object_read_attr(object, "sodipodi:rx");
714 sp_object_read_attr(object, "sodipodi:ry");
716 sp_object_read_attr(object, "sodipodi:start");
717 sp_object_read_attr(object, "sodipodi:end");
718 sp_object_read_attr(object, "sodipodi:open");
719 }
721 /*
722 * sp_arc_set_elliptical_path_attribute:
723 *
724 * Convert center to endpoint parameterization and set it to repr.
725 *
726 * See SVG 1.0 Specification W3C Recommendation
727 * ``F.6 Ellptical arc implementation notes'' for more detail.
728 */
729 static gboolean
730 sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
731 {
732 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
734 Inkscape::SVG::PathString str;
736 Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
737 Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
738 double rx = ge->rx.computed;
739 double ry = ge->ry.computed;
741 str.moveTo(p1);
743 double dt = fmod(ge->end - ge->start, SP_2PI);
744 if (fabs(dt) < 1e-6) {
745 Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
746 str.arcTo(rx, ry, 0, true, true, ph)
747 .arcTo(rx, ry, 0, true, true, p2)
748 .closePath();
749 } else {
750 bool fa = (fabs(dt) > M_PI);
751 bool fs = (dt > 0);
752 str.arcTo(rx, ry, 0, fa, fs, p2);
753 if (ge->closed) {
754 Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
755 str.lineTo(center).closePath();
756 }
757 }
759 repr->setAttribute("d", str.c_str());
760 return true;
761 }
763 static Inkscape::XML::Node *
764 sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
765 {
766 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
767 SPArc *arc = SP_ARC(object);
769 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
770 repr = xml_doc->createElement("svg:path");
771 }
773 if (flags & SP_OBJECT_WRITE_EXT) {
774 repr->setAttribute("sodipodi:type", "arc");
775 sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
776 sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
777 sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
778 sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
780 // write start and end only if they are non-trivial; otherwise remove
781 gdouble len = fmod(ge->end - ge->start, SP_2PI);
782 if (len < 0.0) len += SP_2PI;
783 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
784 sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
785 sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
786 repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
787 } else {
788 repr->setAttribute("sodipodi:end", NULL);
789 repr->setAttribute("sodipodi:start", NULL);
790 repr->setAttribute("sodipodi:open", NULL);
791 }
792 }
794 // write d=
795 sp_arc_set_elliptical_path_attribute(arc, repr);
797 if (((SPObjectClass *) arc_parent_class)->write)
798 ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
800 return repr;
801 }
803 static void
804 sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
805 {
806 SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
808 switch (key) {
809 case SP_ATTR_SODIPODI_CX:
810 ge->cx.readOrUnset(value);
811 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
812 break;
813 case SP_ATTR_SODIPODI_CY:
814 ge->cy.readOrUnset(value);
815 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
816 break;
817 case SP_ATTR_SODIPODI_RX:
818 if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
819 ge->rx.unset();
820 }
821 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
822 break;
823 case SP_ATTR_SODIPODI_RY:
824 if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
825 ge->ry.unset();
826 }
827 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
828 break;
829 case SP_ATTR_SODIPODI_START:
830 if (value) {
831 sp_svg_number_read_d(value, &ge->start);
832 } else {
833 ge->start = 0;
834 }
835 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
836 break;
837 case SP_ATTR_SODIPODI_END:
838 if (value) {
839 sp_svg_number_read_d(value, &ge->end);
840 } else {
841 ge->end = 2 * M_PI;
842 }
843 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
844 break;
845 case SP_ATTR_SODIPODI_OPEN:
846 ge->closed = (!value);
847 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
848 break;
849 default:
850 if (((SPObjectClass *) arc_parent_class)->set)
851 ((SPObjectClass *) arc_parent_class)->set(object, key, value);
852 break;
853 }
854 }
856 static void
857 sp_arc_modified(SPObject *object, guint flags)
858 {
859 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
860 sp_shape_set_shape((SPShape *) object);
861 }
863 if (((SPObjectClass *) arc_parent_class)->modified)
864 ((SPObjectClass *) arc_parent_class)->modified(object, flags);
865 }
867 static gchar *sp_arc_description(SPItem *item)
868 {
869 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
871 gdouble len = fmod(ge->end - ge->start, SP_2PI);
872 if (len < 0.0) len += SP_2PI;
873 if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
874 if (ge->closed) {
875 return g_strdup(_("<b>Segment</b>"));
876 } else {
877 return g_strdup(_("<b>Arc</b>"));
878 }
879 } else {
880 return g_strdup(_("<b>Ellipse</b>"));
881 }
882 }
884 void
885 sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
886 {
887 g_return_if_fail(arc != NULL);
888 g_return_if_fail(SP_IS_ARC(arc));
890 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
892 ge->cx.computed = x;
893 ge->cy.computed = y;
894 ge->rx.computed = rx;
895 ge->ry.computed = ry;
896 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
897 // those pref values are in degrees, while we want radians
898 if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
899 ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0) * M_PI / 180;
900 if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
901 ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0) * M_PI / 180;
902 if (!prefs->getBool("/tools/shapes/arc/open"))
903 ge->closed = 1;
904 else
905 ge->closed = 0;
907 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
908 }
910 Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
911 {
912 SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
914 return Geom::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:fileencoding=utf-8:textwidth=99 :