summaryrefslogtreecommitdiffstats
path: root/src/sp-ellipse.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp-ellipse.cpp')
-rw-r--r--src/sp-ellipse.cpp1225
1 files changed, 510 insertions, 715 deletions
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp
index d74eaa6fb..25e4c07d7 100644
--- a/src/sp-ellipse.cpp
+++ b/src/sp-ellipse.cpp
@@ -9,6 +9,7 @@
*
* Copyright (C) 1999-2002 Lauris Kaplinski
* Copyright (C) 2000-2001 Ximian, Inc.
+ * Copyright (C) 2013 Tavmjong Bah
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -24,16 +25,45 @@
#include "style.h"
#include "display/curve.h"
#include <glibmm/i18n.h>
+#include <2geom/angle.h>
+#include <2geom/ellipse.h>
#include <2geom/transforms.h>
#include <2geom/pathvector.h>
+#include <2geom/path-sink.h>
#include "document.h"
#include "sp-ellipse.h"
#include "preferences.h"
#include "snap-candidate.h"
-/* Common parent class */
+#include "sp-factory.h"
+
+namespace {
+SPObject *create_ellipse()
+{
+ SPGenericEllipse *ellipse = new SPGenericEllipse();
+ ellipse->type = SP_GENERIC_ELLIPSE_ELLIPSE;
+ return (SPObject*)ellipse;
+}
+
+SPObject *create_circle()
+{
+ SPGenericEllipse *circle = new SPGenericEllipse();
+ circle->type = SP_GENERIC_ELLIPSE_CIRCLE;
+ return (SPObject*)circle;
+}
+
+SPObject *create_arc()
+{
+ SPGenericEllipse *arc = new SPGenericEllipse();
+ arc->type = SP_GENERIC_ELLIPSE_ARC;
+ return (SPObject*)arc;
+}
+
+bool ellipse_registered = SPFactory::instance().registerObject("svg:ellipse", create_ellipse);
+bool circle_registered = SPFactory::instance().registerObject("svg:circle", create_circle);
+bool arc_registered = SPFactory::instance().registerObject("arc", create_arc);
+}
-#define noELLIPSE_VERBOSE
#ifndef M_PI
#define M_PI 3.14159265358979323846
@@ -41,883 +71,648 @@
#define SP_2PI (2 * M_PI)
-#if 1
-/* Hmmm... shouldn't this also qualify */
-/* Whether it is faster or not, well, nobody knows */
-#define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
-#else
-/* we do not use C99 round(3) function yet */
-static double sp_round(double x, double y)
+SPGenericEllipse::SPGenericEllipse()
+ : SPShape()
+ , start(0)
+ , end(SP_2PI)
+ , type(SP_GENERIC_ELLIPSE_UNDEFINED)
+ , _closed(true)
{
- double remain;
-
- g_assert(y > 0.0);
-
- /* return round(x/y) * y; */
-
- remain = fmod(x, y);
-
- if (remain >= 0.5*y)
- return x - remain + y;
- else
- return x - remain;
}
-#endif
-static void sp_genericellipse_class_init(SPGenericEllipseClass *klass);
-static void sp_genericellipse_init(SPGenericEllipse *ellipse);
+SPGenericEllipse::~SPGenericEllipse()
+{
+}
-static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags);
+void SPGenericEllipse::setClosed(bool value) {
+ _closed = value;
+}
-static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
+bool SPGenericEllipse::closed() {
+ return _closed;
+}
-static void sp_genericellipse_set_shape(SPShape *shape);
-static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write);
+void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr)
+{
+ // std::cout << "SPGenericEllipse::build: Entrance: " << this->type
+ // << " (" << g_quark_to_string(repr->code()) << ")" << std::endl;
-static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
- guint flags);
+ switch ( type ) {
+ case SP_GENERIC_ELLIPSE_ARC:
+ this->readAttr("sodipodi:cx");
+ this->readAttr("sodipodi:cy");
+ this->readAttr("sodipodi:rx");
+ this->readAttr("sodipodi:ry");
+ this->readAttr("sodipodi:start");
+ this->readAttr("sodipodi:end");
+ this->readAttr("sodipodi:open");
+ break;
-static gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
+ case SP_GENERIC_ELLIPSE_CIRCLE:
+ this->readAttr("cx");
+ this->readAttr("cy");
+ this->readAttr("r");
+ break;
-static SPShapeClass *ge_parent_class;
+ case SP_GENERIC_ELLIPSE_ELLIPSE:
+ this->readAttr("cx");
+ this->readAttr("cy");
+ this->readAttr("rx");
+ this->readAttr("ry");
+ break;
-GType
-sp_genericellipse_get_type(void)
-{
- static GType type = 0;
- if (!type) {
- GTypeInfo info = {
- sizeof(SPGenericEllipseClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) sp_genericellipse_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof(SPGenericEllipse),
- 16, /* n_preallocs */
- (GInstanceInitFunc) sp_genericellipse_init,
- NULL, /* value_table */
- };
- type = g_type_register_static(SP_TYPE_SHAPE, "SPGenericEllipse", &info, (GTypeFlags)0);
+ default:
+ std::cerr << "SPGenericEllipse::build() unknown defined type." << std::endl;
}
- return type;
+
+ SPShape::build(document, repr);
}
-static void sp_genericellipse_class_init(SPGenericEllipseClass *klass)
+void SPGenericEllipse::set(unsigned int key, gchar const *value)
{
- SPObjectClass *sp_object_class = (SPObjectClass *) klass;
- SPItemClass *item_class = (SPItemClass *) klass;
- SPLPEItemClass *lpe_item_class = (SPLPEItemClass *) klass;
- SPShapeClass *shape_class = (SPShapeClass *) klass;
-
- ge_parent_class = (SPShapeClass*) g_type_class_ref(SP_TYPE_SHAPE);
+ // There are multiple ways to set internal cx, cy, rx, and ry (via SVG attributes or Sodipodi
+ // attributes) thus we don't want to unset them if a read fails (e.g., when we explicitly clear
+ // an attribute by setting it to NULL).
+ SVGLength t;
+ switch (key) {
+ case SP_ATTR_CX:
+ case SP_ATTR_SODIPODI_CX:
+ if( t.read(value) ) cx = t;
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_CY:
+ case SP_ATTR_SODIPODI_CY:
+ if( t.read(value) ) cy = t;
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_RX:
+ case SP_ATTR_SODIPODI_RX:
+ if( t.read(value) && t.value > 0.0 ) this->rx = t;
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_RY:
+ case SP_ATTR_SODIPODI_RY:
+ if( t.read(value) && t.value > 0.0 ) this->ry = t;
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_R:
+ if( t.read(value) && t.value > 0.0 ) {
+ this->ry = this->rx = t;
+ }
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
- sp_object_class->update = sp_genericellipse_update;
- sp_object_class->write = sp_genericellipse_write;
+ case SP_ATTR_SODIPODI_START:
+ if (value) {
+ sp_svg_number_read_d(value, &this->start);
+ } else {
+ this->start = 0;
+ }
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
- item_class->snappoints = sp_genericellipse_snappoints;
+ case SP_ATTR_SODIPODI_END:
+ if (value) {
+ sp_svg_number_read_d(value, &this->end);
+ } else {
+ this->end = 2 * M_PI;
+ }
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
- shape_class->set_shape = sp_genericellipse_set_shape;
- lpe_item_class->update_patheffect = sp_genericellipse_update_patheffect;
-}
+ case SP_ATTR_SODIPODI_OPEN:
+ this->_closed = (!value);
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
-static void
-sp_genericellipse_init(SPGenericEllipse *ellipse)
-{
- ellipse->cx.unset();
- ellipse->cy.unset();
- ellipse->rx.unset();
- ellipse->ry.unset();
-
- ellipse->start = 0.0;
- ellipse->end = SP_2PI;
- ellipse->closed = TRUE;
+ default:
+ SPShape::set(key, value);
+ break;
+ }
}
-static void
-sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags)
+void SPGenericEllipse::update(SPCtx *ctx, guint flags)
{
+ // std::cout << "\nSPGenericEllipse::update: Entrance" << std::endl;
if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
- SPGenericEllipse *ellipse = (SPGenericEllipse *) object;
- SPStyle const *style = object->style;
Geom::Rect const &viewbox = ((SPItemCtx const *) ctx)->viewport;
double const dx = viewbox.width();
double const dy = viewbox.height();
- double const dr = sqrt(dx*dx + dy*dy)/sqrt(2);
- double const em = style->font_size.computed;
+ double const dr = hypot(dx, dy) / sqrt(2);
+ double const em = this->style->font_size.computed;
double const ex = em * 0.5; // fixme: get from pango or libnrtype
- ellipse->cx.update(em, ex, dx);
- ellipse->cy.update(em, ex, dy);
- ellipse->rx.update(em, ex, dr);
- ellipse->ry.update(em, ex, dr);
- static_cast<SPShape *>(object)->setShape();
- }
- if (((SPObjectClass *) ge_parent_class)->update)
- ((SPObjectClass *) ge_parent_class)->update(object, ctx, flags);
-}
+ this->cx.update(em, ex, dx);
+ this->cy.update(em, ex, dy);
+ this->rx.update(em, ex, dr);
+ this->ry.update(em, ex, dr);
-static void
-sp_genericellipse_update_patheffect(SPLPEItem *lpeitem, bool write)
-{
- SPShape *shape = (SPShape *) lpeitem;
- sp_genericellipse_set_shape(shape);
-
- if (write) {
- Inkscape::XML::Node *repr = shape->getRepr();
- if ( shape->_curve != NULL ) {
- gchar *str = sp_svg_write_path(shape->_curve->get_pathvector());
- repr->setAttribute("d", str);
- g_free(str);
- } else {
- repr->setAttribute("d", NULL);
- }
+ this->set_shape();
}
- ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ SPShape::update(ctx, flags);
+ // std::cout << "SPGenericEllipse::update: Exit\n" << std::endl;
}
-/* fixme: Think (Lauris) */
-/* Can't we use arcto in this method? */
-static void sp_genericellipse_set_shape(SPShape *shape)
+Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
{
- if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) {
- 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");
- if (shape->getRepr()->attribute("d")) {
- // unconditionally read the curve from d, if any, to preserve appearance
- Geom::PathVector pv = sp_svg_read_pathv(shape->getRepr()->attribute("d"));
- SPCurve *cold = new SPCurve(pv);
- shape->setCurveInsync( cold, TRUE);
- cold->unref();
- }
- return;
- }
-
- double rx, ry, s, e;
- double x0, y0, x1, y1, x2, y2, x3, y3;
- double len;
- gint slice = FALSE;
- // gint i;
-
- SPGenericEllipse *ellipse = (SPGenericEllipse *) shape;
-
- if ((ellipse->rx.computed < 1e-18) || (ellipse->ry.computed < 1e-18)) return;
- if (fabs(ellipse->end - ellipse->start) < 1e-9) return;
-
- sp_genericellipse_normalize(ellipse);
+ // std::cout << "\nSPGenericEllipse::write: Entrance ("
+ // << (repr == NULL ? " NULL" : g_quark_to_string(repr->code()))
+ // << ")" << std::endl;
- rx = ellipse->rx.computed;
- ry = ellipse->ry.computed;
-
- // figure out if we have a slice, guarding against rounding errors
- len = fmod(ellipse->end - ellipse->start, SP_2PI);
- if (len < 0.0) len += SP_2PI;
- if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
- slice = FALSE;
- ellipse->end = ellipse->start + SP_2PI;
+ GenericEllipseType new_type = SP_GENERIC_ELLIPSE_UNDEFINED;
+ if (this->_isSlice() || hasPathEffect() ) {
+ new_type = SP_GENERIC_ELLIPSE_ARC;
+ } else if ( rx.computed == ry.computed ) {
+ new_type = SP_GENERIC_ELLIPSE_CIRCLE;
} else {
- slice = TRUE;
+ new_type = SP_GENERIC_ELLIPSE_ELLIPSE;
}
+ // std::cout << " new_type: " << new_type << std::endl;
- SPCurve * curve = new SPCurve();
- curve->moveto(cos(ellipse->start), sin(ellipse->start));
-
- for (s = ellipse->start; s < ellipse->end; s += M_PI_2) {
- e = s + M_PI_2;
- if (e > ellipse->end)
- e = ellipse->end;
- len = 4*tan((e - s)/4)/3;
- x0 = cos(s);
- y0 = sin(s);
- x1 = x0 + len * cos(s + M_PI_2);
- y1 = y0 + len * sin(s + M_PI_2);
- x3 = cos(e);
- y3 = sin(e);
- x2 = x3 + len * cos(e - M_PI_2);
- y2 = y3 + len * sin(e - M_PI_2);
-#ifdef ELLIPSE_VERBOSE
- g_print("step %d s %f e %f coords %f %f %f %f %f %f\n",
- i, s, e, x1, y1, x2, y2, x3, y3);
-#endif
- curve->curveto(x1,y1, x2,y2, x3,y3);
- }
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- if (slice && ellipse->closed) { // TODO: is this check for "ellipse->closed" necessary?
- curve->lineto(0., 0.);
- }
- if (ellipse->closed) {
- curve->closepath();
+ switch ( new_type ) {
+
+ case SP_GENERIC_ELLIPSE_ARC:
+ repr = xml_doc->createElement("svg:path");
+ break;
+ case SP_GENERIC_ELLIPSE_CIRCLE:
+ repr = xml_doc->createElement("svg:circle");
+ break;
+ case SP_GENERIC_ELLIPSE_ELLIPSE:
+ repr = xml_doc->createElement("svg:ellipse");
+ break;
+ case SP_GENERIC_ELLIPSE_UNDEFINED:
+ default:
+ std::cerr << "SPGenericEllipse::write(): unknown type." << std::endl;
+ }
}
- Geom::Affine aff = Geom::Scale(rx, ry) * Geom::Translate(ellipse->cx.computed, ellipse->cy.computed);
- curve->transform(aff);
-
- /* Reset the shape's curve to the "original_curve"
- * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
- shape->setCurveInsync( curve, TRUE);
- shape->setCurveBeforeLPE(curve);
-
- if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
- SPCurve *c_lpe = curve->copy();
- bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
- if (success) {
- shape->setCurveInsync( c_lpe, TRUE);
+ if( type != new_type ) {
+ switch( new_type ) {
+ case SP_GENERIC_ELLIPSE_ARC:
+ repr->setCodeUnsafe(g_quark_from_string("svg:path"));
+ break;
+ case SP_GENERIC_ELLIPSE_CIRCLE:
+ repr->setCodeUnsafe(g_quark_from_string("svg:circle"));
+ break;
+ case SP_GENERIC_ELLIPSE_ELLIPSE:
+ repr->setCodeUnsafe(g_quark_from_string("svg:ellipse"));
+ break;
+ default:
+ std::cerr << "SPGenericEllipse::write(): unknown type." << std::endl;
}
- c_lpe->unref();
- }
- curve->unref();
-}
+ type = new_type;
-static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
-{
- g_assert(item != NULL);
- g_assert(SP_IS_GENERICELLIPSE(item));
-
- SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item);
- sp_genericellipse_normalize(ellipse);
- Geom::Affine const i2dt = item->i2dt_affine();
-
- // figure out if we have a slice, while guarding against rounding errors
- bool slice = false;
- double len = fmod(ellipse->end - ellipse->start, SP_2PI);
- if (len < 0.0) len += SP_2PI;
- if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
- slice = false;
- ellipse->end = ellipse->start + SP_2PI;
- } else {
- slice = true;
+ // FIXME: The XML dialog won't update the element name. We need
+ // a notifyElementNameChanged callback added to the XML observers
+ // to trigger a refresh.
}
- double rx = ellipse->rx.computed;
- double ry = ellipse->ry.computed;
- double cx = ellipse->cx.computed;
- double cy = ellipse->cy.computed;
+ // std::cout << " type: " << g_quark_to_string( repr->code() ) << std::endl;
+ // std::cout << " cx: " << cx.computed
+ // << " cy: " << cy.computed
+ // << " rx: " << rx.computed
+ // << " ry: " << ry.computed << std::endl;
+
+ switch ( type ) {
+ case SP_GENERIC_ELLIPSE_UNDEFINED:
+ case SP_GENERIC_ELLIPSE_ARC:
+
+ repr->setAttribute("cx", NULL );
+ repr->setAttribute("cy", NULL );
+ repr->setAttribute("rx", NULL );
+ repr->setAttribute("ry", NULL );
+ repr->setAttribute("r", NULL );
+
+ if (flags & SP_OBJECT_WRITE_EXT) {
+
+ repr->setAttribute("sodipodi:type", "arc");
+ sp_repr_set_svg_double(repr, "sodipodi:cx", this->cx.computed);
+ sp_repr_set_svg_double(repr, "sodipodi:cy", this->cy.computed);
+ sp_repr_set_svg_double(repr, "sodipodi:rx", this->rx.computed);
+ sp_repr_set_svg_double(repr, "sodipodi:ry", this->ry.computed);
+
+ // write start and end only if they are non-trivial; otherwise remove
+ if (this->_isSlice()) {
+ sp_repr_set_svg_double(repr, "sodipodi:start", this->start);
+ sp_repr_set_svg_double(repr, "sodipodi:end", this->end);
+
+ repr->setAttribute("sodipodi:open", (!this->_closed) ? "true" : NULL);
+ } else {
+ repr->setAttribute("sodipodi:end", NULL);
+ repr->setAttribute("sodipodi:start", NULL);
+ repr->setAttribute("sodipodi:open", NULL);
+ }
+ }
- Geom::Point pt;
+ // write d=
+ this->set_elliptical_path_attribute(repr);
+ break;
- // Snap to the 4 quadrant points of the ellipse, but only if the arc
- // spans far enough to include them
- if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)) {
- double angle = 0;
- for (angle = 0; angle < SP_2PI; angle += M_PI_2) {
- if (angle >= ellipse->start && angle <= ellipse->end) {
- pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2dt;
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT));
- }
- }
- }
+ case SP_GENERIC_ELLIPSE_CIRCLE:
+ sp_repr_set_svg_double(repr, "cx", this->cx.computed);
+ sp_repr_set_svg_double(repr, "cy", this->cy.computed);
+ sp_repr_set_svg_double(repr, "r", this->rx.computed);
+ repr->setAttribute("rx", NULL );
+ repr->setAttribute("ry", NULL );
+ repr->setAttribute("sodipodi:cx", NULL );
+ repr->setAttribute("sodipodi:cy", NULL );
+ repr->setAttribute("sodipodi:rx", NULL );
+ repr->setAttribute("sodipodi:ry", NULL );
+ repr->setAttribute("sodipodi:end", NULL );
+ repr->setAttribute("sodipodi:start", NULL );
+ repr->setAttribute("sodipodi:open", NULL );
+ repr->setAttribute("sodipodi:type", NULL );
+ repr->setAttribute("d", NULL );
+ break;
- // Add the centre, if we have a closed slice or when explicitly asked for
- bool c1 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && ellipse->closed;
- bool c2 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT);
- if (c1 || c2) {
- pt = Geom::Point(cx, cy) * i2dt;
- if (c1) {
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
- }
- if (c2) {
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
- }
- }
+ case SP_GENERIC_ELLIPSE_ELLIPSE:
+ sp_repr_set_svg_double(repr, "cx", this->cx.computed);
+ sp_repr_set_svg_double(repr, "cy", this->cy.computed);
+ sp_repr_set_svg_double(repr, "rx", this->rx.computed);
+ sp_repr_set_svg_double(repr, "ry", this->ry.computed);
+ repr->setAttribute("r", NULL );
+ repr->setAttribute("sodipodi:cx", NULL );
+ repr->setAttribute("sodipodi:cy", NULL );
+ repr->setAttribute("sodipodi:rx", NULL );
+ repr->setAttribute("sodipodi:ry", NULL );
+ repr->setAttribute("sodipodi:end", NULL );
+ repr->setAttribute("sodipodi:start", NULL );
+ repr->setAttribute("sodipodi:open", NULL );
+ repr->setAttribute("sodipodi:type", NULL );
+ repr->setAttribute("d", NULL );
+ break;
- // And if we have a slice, also snap to the endpoints
- if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice) {
- // Add the start point, if it's not coincident with a quadrant point
- if (fmod(ellipse->start, M_PI_2) != 0.0 ) {
- pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2dt;
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
- }
- // Add the end point, if it's not coincident with a quadrant point
- if (fmod(ellipse->end, M_PI_2) != 0.0 ) {
- pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2dt;
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
- }
+ default:
+ std::cerr << "SPGenericEllipse::write: unknown type." << std::endl;
}
-}
-void
-sp_genericellipse_normalize(SPGenericEllipse *ellipse)
-{
- ellipse->start = fmod(ellipse->start, SP_2PI);
- ellipse->end = fmod(ellipse->end, SP_2PI);
+ this->set_shape(); // evaluate SPCurve
- if (ellipse->start < 0.0)
- ellipse->start += SP_2PI;
- double diff = ellipse->start - ellipse->end;
- if (diff >= 0.0)
- ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
+ SPShape::write(xml_doc, repr, flags);
- /* Now we keep: 0 <= start < end <= 2*PI */
+ // std::cout << "SPGenericEllipse::write: Exit: " << g_quark_to_string(repr->code()) << "\n" << std::endl;
+ return repr;
}
-static Inkscape::XML::Node *sp_genericellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+const char *SPGenericEllipse::displayName() const
{
- SPGenericEllipse *ellipse = SP_GENERICELLIPSE(object);
- if (flags & SP_OBJECT_WRITE_EXT) {
- if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- repr = xml_doc->createElement("svg:path");
- }
+ switch ( type ) {
+ case SP_GENERIC_ELLIPSE_UNDEFINED:
+ case SP_GENERIC_ELLIPSE_ARC:
- sp_repr_set_svg_double(repr, "sodipodi:cx", ellipse->cx.computed);
- sp_repr_set_svg_double(repr, "sodipodi:cy", ellipse->cy.computed);
- sp_repr_set_svg_double(repr, "sodipodi:rx", ellipse->rx.computed);
- sp_repr_set_svg_double(repr, "sodipodi:ry", ellipse->ry.computed);
+ if (this->_isSlice()) {
+ if (this->_closed) {
+ return _("Segment");
+ } else {
+ return _("Arc");
+ }
+ } else {
+ return _("Ellipse");
+ }
- if (SP_IS_ARC(ellipse)) {
- sp_arc_set_elliptical_path_attribute(SP_ARC(object), object->getRepr());
- }
- }
+ case SP_GENERIC_ELLIPSE_CIRCLE:
+ return _("Circle");
- if (((SPObjectClass *) ge_parent_class)->write) {
- ((SPObjectClass *) ge_parent_class)->write(object, xml_doc, repr, flags);
- }
+ case SP_GENERIC_ELLIPSE_ELLIPSE:
+ return _("Ellipse");
- return repr;
+ default:
+ return "Unknown ellipse: ERROR";
+ }
+ return ("Shouldn't be here");
}
-/* SVG <ellipse> element */
-
-static void sp_ellipse_class_init(SPEllipseClass *klass);
-static void sp_ellipse_init(SPEllipse *ellipse);
-
-static void sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
-static Inkscape::XML::Node *sp_ellipse_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
-static void sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value);
-static gchar *sp_ellipse_description(SPItem *item);
+// Create path for rendering shape on screen
+void SPGenericEllipse::set_shape()
+{
+ // std::cout << "SPGenericEllipse::set_shape: Entrance" << std::endl;
+ if (hasBrokenPathEffect()) {
+ 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");
-static SPGenericEllipseClass *ellipse_parent_class;
+ if (this->getRepr()->attribute("d")) {
+ // unconditionally read the curve from d, if any, to preserve appearance
+ Geom::PathVector pv = sp_svg_read_pathv(this->getRepr()->attribute("d"));
+ SPCurve *cold = new SPCurve(pv);
+ this->setCurveInsync(cold, TRUE);
+ cold->unref();
+ }
-GType
-sp_ellipse_get_type(void)
-{
- static GType type = 0;
- if (!type) {
- GTypeInfo info = {
- sizeof(SPEllipseClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) sp_ellipse_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof(SPEllipse),
- 16, /* n_preallocs */
- (GInstanceInitFunc) sp_ellipse_init,
- NULL, /* value_table */
- };
- type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPEllipse", &info, (GTypeFlags)0);
+ return;
+ }
+ if (Geom::are_near(this->rx.computed, 0) || Geom::are_near(this->ry.computed, 0)) {
+ return;
}
- return type;
-}
-static void sp_ellipse_class_init(SPEllipseClass *klass)
-{
- SPObjectClass *sp_object_class = (SPObjectClass *) klass;
- SPItemClass *item_class = (SPItemClass *) klass;
+ this->normalize();
- ellipse_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
+ SPCurve *curve = NULL;
- sp_object_class->build = sp_ellipse_build;
- sp_object_class->write = sp_ellipse_write;
- sp_object_class->set = sp_ellipse_set;
+ // For simplicity, we use a circle with center (0, 0) and radius 1 for our calculations.
+ Geom::Circle circle(0, 0, 1);
- item_class->description = sp_ellipse_description;
-}
+ if (this->_isSlice()) {
+ Geom::Point center(0, 0);
+ Geom::Point start_point = Geom::Point::polar(start);
+ Geom::Point end_point = Geom::Point::polar(end);
+ Geom::Point middle_point = make_angle_bisector_ray(Geom::Ray(center, start), Geom::Ray(center, end)).versor();
-static void
-sp_ellipse_init(SPEllipse */*ellipse*/)
-{
- /* Nothing special */
-}
+ Geom::EllipticalArc *arc = circle.arc(start_point, middle_point, end_point);
-static void
-sp_ellipse_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
-{
- if (((SPObjectClass *) ellipse_parent_class)->build)
- (* ((SPObjectClass *) ellipse_parent_class)->build) (object, document, repr);
+ Geom::Path path(start_point);
+ path.append(*arc);
- object->readAttr( "cx" );
- object->readAttr( "cy" );
- object->readAttr( "rx" );
- object->readAttr( "ry" );
-}
+ delete arc;
-static Inkscape::XML::Node *
-sp_ellipse_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
-{
- SPGenericEllipse *ellipse;
+ Geom::PathBuilder pb;
+ pb.append(path);
- ellipse = SP_GENERICELLIPSE(object);
+ if (this->_closed) {
+ // "pizza slice"
+ pb.lineTo(center);
+ pb.closePath();
+ } else {
+ // arc only
+ pb.flush();
+ }
- if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- repr = xml_doc->createElement("svg:ellipse");
+ curve = new SPCurve(pb.peek());
+ } else {
+ // Full ellipse
+ // This code converts the circle to four elliptical arcs explicitly.
+ // Circle::getPath currently creates cubic bezier curves, these are not suitable here
+ // as a circle should have four mid markers at 0, 90, 180, 270 degrees.
+ Geom::Path path(Geom::Point::polar(0));
+ Geom::EllipticalArc* arc;
+
+ arc = circle.arc(Geom::Point::polar(0), Geom::Point::polar(M_PI / 4.0), Geom::Point::polar(M_PI / 2.0));
+ path.append(*arc);
+ delete arc;
+
+ arc = circle.arc(Geom::Point::polar(M_PI / 2.0), Geom::Point::polar(3.0 * M_PI / 4.0), Geom::Point::polar(M_PI));
+ path.append(*arc);
+ delete arc;
+
+ arc = circle.arc(Geom::Point::polar(M_PI), Geom::Point::polar(5.0 * M_PI / 4.0), Geom::Point::polar(3.0 * M_PI / 2.0));
+ path.append(*arc);
+ delete arc;
+
+ arc = circle.arc(Geom::Point::polar(3.0 * M_PI / 2.0), Geom::Point::polar(7.0 * M_PI / 4.0), Geom::Point::polar(2.0 * M_PI));
+ path.append(*arc);
+ delete arc;
+
+ Geom::PathBuilder pb;
+ pb.append(path);
+ pb.closePath();
+
+ curve = new SPCurve(pb.peek());
}
- sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
- sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
- sp_repr_set_svg_double(repr, "rx", ellipse->rx.computed);
- sp_repr_set_svg_double(repr, "ry", ellipse->ry.computed);
+ // gchar *str = sp_svg_write_path(curve->get_pathvector());
+ // std::cout << " path: " << str << std::endl;
+ // g_free(str);
- if (((SPObjectClass *) ellipse_parent_class)->write)
- (* ((SPObjectClass *) ellipse_parent_class)->write) (object, xml_doc, repr, flags);
+ // Stretching / moving the calculated shape to fit the actual dimensions.
+ Geom::Affine aff = Geom::Scale(rx.computed, ry.computed) * Geom::Translate(cx.computed, cy.computed);
+ curve->transform(aff);
- return repr;
-}
+ /* Reset the shape's curve to the "original_curve"
+ * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
+ this->setCurveInsync(curve, TRUE);
+ this->setCurveBeforeLPE(curve);
-static void
-sp_ellipse_set(SPObject *object, unsigned int key, gchar const *value)
-{
- SPGenericEllipse *ellipse;
+ if (hasPathEffect() && pathEffectsEnabled()) {
+ SPCurve *c_lpe = curve->copy();
+ bool success = this->performPathEffect(c_lpe);
- ellipse = SP_GENERICELLIPSE(object);
+ if (success) {
+ this->setCurveInsync(c_lpe, TRUE);
+ }
- switch (key) {
- case SP_ATTR_CX:
- ellipse->cx.readOrUnset(value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_CY:
- ellipse->cy.readOrUnset(value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_RX:
- if (!ellipse->rx.read(value) || (ellipse->rx.value <= 0.0)) {
- ellipse->rx.unset();
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_RY:
- if (!ellipse->ry.read(value) || (ellipse->ry.value <= 0.0)) {
- ellipse->ry.unset();
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- default:
- if (((SPObjectClass *) ellipse_parent_class)->set)
- ((SPObjectClass *) ellipse_parent_class)->set(object, key, value);
- break;
+ c_lpe->unref();
}
-}
-static gchar *sp_ellipse_description(SPItem */*item*/)
-{
- return g_strdup(_("<b>Ellipse</b>"));
+ curve->unref();
+ // std::cout << "SPGenericEllipse::set_shape: Exit" << std::endl;
}
-
-void
-sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
+Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform)
{
- SPGenericEllipse *ge;
-
- g_return_if_fail(ellipse != NULL);
- g_return_if_fail(SP_IS_ELLIPSE(ellipse));
-
- ge = SP_GENERICELLIPSE(ellipse);
-
- ge->cx.computed = x;
- ge->cy.computed = y;
- ge->rx.computed = rx;
- ge->ry.computed = ry;
+ // Allow live effects
+ if (hasPathEffect() && pathEffectsEnabled()) {
+ return xform;
+ }
- ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-}
+ /* Calculate ellipse start in parent coords. */
+ Geom::Point pos(Geom::Point(this->cx.computed, this->cy.computed) * xform);
-/* SVG <circle> element */
+ /* This function takes care of translation and scaling, we return whatever parts we can't
+ handle. */
+ Geom::Affine ret(Geom::Affine(xform).withoutTranslation());
+ gdouble const sw = hypot(ret[0], ret[1]);
+ gdouble const sh = hypot(ret[2], ret[3]);
-static void sp_circle_class_init(SPCircleClass *klass);
-static void sp_circle_init(SPCircle *circle);
+ if (sw > 1e-9) {
+ ret[0] /= sw;
+ ret[1] /= sw;
+ } else {
+ ret[0] = 1.0;
+ ret[1] = 0.0;
+ }
-static void sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
-static Inkscape::XML::Node *sp_circle_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
-static void sp_circle_set(SPObject *object, unsigned int key, gchar const *value);
-static gchar *sp_circle_description(SPItem *item);
+ if (sh > 1e-9) {
+ ret[2] /= sh;
+ ret[3] /= sh;
+ } else {
+ ret[2] = 0.0;
+ ret[3] = 1.0;
+ }
-static SPGenericEllipseClass *circle_parent_class;
+ if (this->rx._set) {
+ this->rx = this->rx.computed * sw;
+ }
-GType
-sp_circle_get_type(void)
-{
- static GType type = 0;
- if (!type) {
- GTypeInfo info = {
- sizeof(SPCircleClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) sp_circle_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof(SPCircle),
- 16, /* n_preallocs */
- (GInstanceInitFunc) sp_circle_init,
- NULL, /* value_table */
- };
- type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPCircle", &info, (GTypeFlags)0);
+ if (this->ry._set) {
+ this->ry = this->ry.computed * sh;
}
- return type;
-}
-static void
-sp_circle_class_init(SPCircleClass *klass)
-{
- SPObjectClass *sp_object_class = (SPObjectClass *) klass;
- SPItemClass *item_class = (SPItemClass *) klass;
+ /* Find start in item coords */
+ pos = pos * ret.inverse();
+ this->cx = pos[Geom::X];
+ this->cy = pos[Geom::Y];
- circle_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
+ this->set_shape();
- sp_object_class->build = sp_circle_build;
- sp_object_class->write = sp_circle_write;
- sp_object_class->set = sp_circle_set;
+ // Adjust stroke width
+ this->adjust_stroke(sqrt(fabs(sw * sh)));
- item_class->description = sp_circle_description;
-}
+ // Adjust pattern fill
+ this->adjust_pattern(xform * ret.inverse());
-static void
-sp_circle_init(SPCircle */*circle*/)
-{
- /* Nothing special */
-}
+ // Adjust gradient fill
+ this->adjust_gradient(xform * ret.inverse());
-static void
-sp_circle_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
-{
- if (((SPObjectClass *) circle_parent_class)->build)
- (* ((SPObjectClass *) circle_parent_class)->build)(object, document, repr);
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
- object->readAttr( "cx" );
- object->readAttr( "cy" );
- object->readAttr( "r" );
+ return ret;
}
-static Inkscape::XML::Node *
-sp_circle_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+void SPGenericEllipse::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const
{
- SPGenericEllipse *ellipse;
+ // CPPIFY: is this call necessary?
+ const_cast<SPGenericEllipse*>(this)->normalize();
- ellipse = SP_GENERICELLIPSE(object);
+ Geom::Affine const i2dt = this->i2dt_affine();
- if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- repr = xml_doc->createElement("svg:circle");
+ // Snap to the 4 quadrant points of the ellipse, but only if the arc
+ // spans far enough to include them
+ if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)) {
+ for (double angle = 0; angle < SP_2PI; angle += M_PI_2) {
+ if (Geom::AngleInterval(this->start, this->end, true).contains(angle)) {
+ Geom::Point pt = this->getPointAtAngle(angle) * i2dt;
+ p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT));
+ }
+ }
}
- sp_repr_set_svg_double(repr, "cx", ellipse->cx.computed);
- sp_repr_set_svg_double(repr, "cy", ellipse->cy.computed);
- sp_repr_set_svg_double(repr, "r", ellipse->rx.computed);
+ double cx = this->cx.computed;
+ double cy = this->cy.computed;
+
- if (((SPObjectClass *) circle_parent_class)->write)
- ((SPObjectClass *) circle_parent_class)->write(object, xml_doc, repr, flags);
+ bool slice = this->_isSlice();
- return repr;
-}
+ // Add the centre, if we have a closed slice or when explicitly asked for
+ if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && this->_closed) {
+ Geom::Point pt = Geom::Point(cx, cy) * i2dt;
+ p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
+ }
-static void
-sp_circle_set(SPObject *object, unsigned int key, gchar const *value)
-{
- SPGenericEllipse *ge;
+ if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT)) {
+ Geom::Point pt = Geom::Point(cx, cy) * i2dt;
+ p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
+ }
- ge = SP_GENERICELLIPSE(object);
+ // And if we have a slice, also snap to the endpoints
+ if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice) {
+ // Add the start point, if it's not coincident with a quadrant point
+ if (!Geom::are_near(std::fmod(this->start, M_PI_2), 0)) {
+ Geom::Point pt = this->getPointAtAngle(this->start) * i2dt;
+ p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
+ }
- switch (key) {
- case SP_ATTR_CX:
- ge->cx.readOrUnset(value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_CY:
- ge->cy.readOrUnset(value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_R:
- if (!ge->rx.read(value) || ge->rx.value <= 0.0) {
- ge->rx.unset();
- }
- ge->ry = ge->rx;
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- default:
- if (((SPObjectClass *) circle_parent_class)->set)
- ((SPObjectClass *) circle_parent_class)->set(object, key, value);
- break;
+ // Add the end point, if it's not coincident with a quadrant point
+ if (!Geom::are_near(std::fmod(this->end, M_PI_2), 0)) {
+ Geom::Point pt = this->getPointAtAngle(this->end) * i2dt;
+ p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
+ }
}
}
-static gchar *sp_circle_description(SPItem */*item*/)
+void SPGenericEllipse::modified(guint flags)
{
- return g_strdup(_("<b>Circle</b>"));
-}
-
-/* <path sodipodi:type="arc"> element */
-
-static void sp_arc_class_init(SPArcClass *klass);
-static void sp_arc_init(SPArc *arc);
-
-static void sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
-static Inkscape::XML::Node *sp_arc_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
-static void sp_arc_set(SPObject *object, unsigned int key, gchar const *value);
-static void sp_arc_modified(SPObject *object, guint flags);
-
-static gchar *sp_arc_description(SPItem *item);
-
-static SPGenericEllipseClass *arc_parent_class;
-
-GType
-sp_arc_get_type(void)
-{
- static GType type = 0;
- if (!type) {
- GTypeInfo info = {
- sizeof(SPArcClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) sp_arc_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof(SPArc),
- 16, /* n_preallocs */
- (GInstanceInitFunc) sp_arc_init,
- NULL, /* value_table */
- };
- type = g_type_register_static(SP_TYPE_GENERICELLIPSE, "SPArc", &info, (GTypeFlags)0);
+ if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
+ this->set_shape();
}
- return type;
+
+ SPShape::modified(flags);
}
-static void
-sp_arc_class_init(SPArcClass *klass)
+void SPGenericEllipse::update_patheffect(bool write)
{
- SPObjectClass *sp_object_class = (SPObjectClass *) klass;
- SPItemClass *item_class = (SPItemClass *) klass;
+ this->set_shape();
- arc_parent_class = (SPGenericEllipseClass*) g_type_class_ref(SP_TYPE_GENERICELLIPSE);
+ if (write) {
+ Inkscape::XML::Node *repr = this->getRepr();
- sp_object_class->build = sp_arc_build;
- sp_object_class->write = sp_arc_write;
- sp_object_class->set = sp_arc_set;
- sp_object_class->modified = sp_arc_modified;
+ if (this->_curve != NULL) {
+ gchar *str = sp_svg_write_path(this->_curve->get_pathvector());
+ repr->setAttribute("d", str);
+ g_free(str);
+ } else {
+ repr->setAttribute("d", NULL);
+ }
+ }
- item_class->description = sp_arc_description;
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
-static void
-sp_arc_init(SPArc */*arc*/)
+void SPGenericEllipse::normalize()
{
- /* Nothing special */
+ Geom::AngleInterval a(this->start, this->end, true);
+
+ this->start = a.initialAngle().radians0();
+ this->end = a.finalAngle().radians0();
}
-static void
-sp_arc_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+Geom::Point SPGenericEllipse::getPointAtAngle(double arg) const
{
- if (((SPObjectClass *) arc_parent_class)->build)
- (* ((SPObjectClass *) arc_parent_class)->build) (object, document, repr);
-
- object->readAttr( "sodipodi:cx" );
- object->readAttr( "sodipodi:cy" );
- object->readAttr( "sodipodi:rx" );
- object->readAttr( "sodipodi:ry" );
-
- object->readAttr( "sodipodi:start" );
- object->readAttr( "sodipodi:end" );
- object->readAttr( "sodipodi:open" );
+ return Geom::Point::polar(arg) * Geom::Scale(rx.computed, ry.computed) * Geom::Translate(cx.computed, cy.computed);
}
/*
- * sp_arc_set_elliptical_path_attribute:
+ * set_elliptical_path_attribute:
*
* Convert center to endpoint parameterization and set it to repr.
*
* See SVG 1.0 Specification W3C Recommendation
* ``F.6 Ellptical arc implementation notes'' for more detail.
*/
-static gboolean
-sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
+bool SPGenericEllipse::set_elliptical_path_attribute(Inkscape::XML::Node *repr)
{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
+ // Make sure our pathvector is up to date.
+ this->set_shape();
- Inkscape::SVG::PathString str;
+ if (this->getCurve() != NULL) {
+ gchar* d = sp_svg_write_path(this->getCurve()->get_pathvector());
- Geom::Point p1 = sp_arc_get_xy(arc, ge->start);
- Geom::Point p2 = sp_arc_get_xy(arc, ge->end);
- double rx = ge->rx.computed;
- double ry = ge->ry.computed;
+ repr->setAttribute("d", d);
- str.moveTo(p1);
-
- double dt = fmod(ge->end - ge->start, SP_2PI);
- if (fabs(dt) < 1e-6) {
- Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
- str.arcTo(rx, ry, 0, true, true, ph)
- .arcTo(rx, ry, 0, true, true, p2)
- .closePath();
+ g_free(d);
} else {
- bool fa = (fabs(dt) > M_PI);
- bool fs = (dt > 0);
- str.arcTo(rx, ry, 0, fa, fs, p2);
- if (ge->closed) {
- Geom::Point center = Geom::Point(ge->cx.computed, ge->cy.computed);
- str.lineTo(center).closePath();
- }
+ repr->setAttribute("d", NULL);
}
- repr->setAttribute("d", str.c_str());
return true;
}
-static Inkscape::XML::Node *
-sp_arc_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+void SPGenericEllipse::position_set(gdouble x, gdouble y, gdouble rx, gdouble ry)
{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
- SPArc *arc = SP_ARC(object);
-
- if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- repr = xml_doc->createElement("svg:path");
- }
-
- if (flags & SP_OBJECT_WRITE_EXT) {
- repr->setAttribute("sodipodi:type", "arc");
- sp_repr_set_svg_double(repr, "sodipodi:cx", ge->cx.computed);
- sp_repr_set_svg_double(repr, "sodipodi:cy", ge->cy.computed);
- sp_repr_set_svg_double(repr, "sodipodi:rx", ge->rx.computed);
- sp_repr_set_svg_double(repr, "sodipodi:ry", ge->ry.computed);
-
- // write start and end only if they are non-trivial; otherwise remove
- gdouble len = fmod(ge->end - ge->start, SP_2PI);
- if (len < 0.0) len += SP_2PI;
- if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
- sp_repr_set_svg_double(repr, "sodipodi:start", ge->start);
- sp_repr_set_svg_double(repr, "sodipodi:end", ge->end);
- repr->setAttribute("sodipodi:open", (!ge->closed) ? "true" : NULL);
- } else {
- repr->setAttribute("sodipodi:end", NULL);
- repr->setAttribute("sodipodi:start", NULL);
- repr->setAttribute("sodipodi:open", NULL);
- }
- }
+ this->cx.computed = x;
+ this->cy.computed = y;
+ this->rx.computed = rx;
+ this->ry.computed = ry;
- // write d=
- sp_arc_set_elliptical_path_attribute(arc, repr);
-
- if (((SPObjectClass *) arc_parent_class)->write)
- ((SPObjectClass *) arc_parent_class)->write(object, xml_doc, repr, flags);
-
- return repr;
-}
-
-static void
-sp_arc_set(SPObject *object, unsigned int key, gchar const *value)
-{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(object);
-
- switch (key) {
- case SP_ATTR_SODIPODI_CX:
- ge->cx.readOrUnset(value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_SODIPODI_CY:
- ge->cy.readOrUnset(value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_SODIPODI_RX:
- if (!ge->rx.read(value) || ge->rx.computed <= 0.0) {
- ge->rx.unset();
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_SODIPODI_RY:
- if (!ge->ry.read(value) || ge->ry.computed <= 0.0) {
- ge->ry.unset();
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_SODIPODI_START:
- if (value) {
- sp_svg_number_read_d(value, &ge->start);
- } else {
- ge->start = 0;
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_SODIPODI_END:
- if (value) {
- sp_svg_number_read_d(value, &ge->end);
- } else {
- ge->end = 2 * M_PI;
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- case SP_ATTR_SODIPODI_OPEN:
- ge->closed = (!value);
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
- break;
- default:
- if (((SPObjectClass *) arc_parent_class)->set)
- ((SPObjectClass *) arc_parent_class)->set(object, key, value);
- break;
- }
-}
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-static void
-sp_arc_modified(SPObject *object, guint flags)
-{
- if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
- ((SPShape *) object)->setShape();
+ // those pref values are in degrees, while we want radians
+ if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0) {
+ this->start = Geom::Angle::from_degrees(prefs->getDouble("/tools/shapes/arc/start", 0.0)).radians0();
}
- if (((SPObjectClass *) arc_parent_class)->modified)
- ((SPObjectClass *) arc_parent_class)->modified(object, flags);
-}
-
-static gchar *sp_arc_description(SPItem *item)
-{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
-
- gdouble len = fmod(ge->end - ge->start, SP_2PI);
- if (len < 0.0) len += SP_2PI;
- if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
- if (ge->closed) {
- return g_strdup(_("<b>Segment</b>"));
- } else {
- return g_strdup(_("<b>Arc</b>"));
- }
- } else {
- return g_strdup(_("<b>Ellipse</b>"));
+ if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0) {
+ this->end = Geom::Angle::from_degrees(prefs->getDouble("/tools/shapes/arc/end", 0.0)).radians0();
}
-}
-
-void
-sp_arc_position_set(SPArc *arc, gdouble x, gdouble y, gdouble rx, gdouble ry)
-{
- g_return_if_fail(arc != NULL);
- g_return_if_fail(SP_IS_ARC(arc));
- SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
+ this->_closed = !prefs->getBool("/tools/shapes/arc/open");
- ge->cx.computed = x;
- ge->cy.computed = y;
- ge->rx.computed = rx;
- ge->ry.computed = ry;
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- // those pref values are in degrees, while we want radians
- if (prefs->getDouble("/tools/shapes/arc/start", 0.0) != 0)
- ge->start = prefs->getDouble("/tools/shapes/arc/start", 0.0) * M_PI / 180;
- if (prefs->getDouble("/tools/shapes/arc/end", 0.0) != 0)
- ge->end = prefs->getDouble("/tools/shapes/arc/end", 0.0) * M_PI / 180;
- if (!prefs->getBool("/tools/shapes/arc/open"))
- ge->closed = 1;
- else
- ge->closed = 0;
-
- ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
-Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
+bool SPGenericEllipse::_isSlice() const
{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
+ Geom::AngleInterval a(this->start, this->end, true);
- return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
- ge->ry.computed * sin(arg) + ge->cy.computed);
+ return !(Geom::are_near(a.extent(), 0) || Geom::are_near(a.extent(), SP_2PI));
}
-
/*
Local Variables:
mode:c++