summaryrefslogtreecommitdiffstats
path: root/src/sp-ellipse.cpp
diff options
context:
space:
mode:
authorMarkus Engel <markus.engel@tum.de>2013-09-26 23:45:12 +0000
committerMarkus Engel <markus.engel@tum.de>2013-09-26 23:45:12 +0000
commit2ed673e7554d0fd8b775978a0ef80c9aa529f7de (patch)
tree30aea5c40e84a51fb5f9916c875eb1d31a51d629 /src/sp-ellipse.cpp
parentUse Cairo 1.10 blend operators to render feBlend (diff)
downloadinkscape-2ed673e7554d0fd8b775978a0ef80c9aa529f7de.tar.gz
inkscape-2ed673e7554d0fd8b775978a0ef80c9aa529f7de.zip
Further refactored SPEllipse.
(bzr r12601)
Diffstat (limited to 'src/sp-ellipse.cpp')
-rw-r--r--src/sp-ellipse.cpp265
1 files changed, 86 insertions, 179 deletions
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp
index fc78b9777..e556f0dc5 100644
--- a/src/sp-ellipse.cpp
+++ b/src/sp-ellipse.cpp
@@ -24,6 +24,8 @@
#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/svg-path.h>
@@ -32,8 +34,6 @@
#include "preferences.h"
#include "snap-candidate.h"
-/* Common parent class */
-
#define noELLIPSE_VERBOSE
@@ -64,40 +64,12 @@ namespace {
#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()
+ , closed(true)
+ , start(0)
+ , end(SP_2PI)
{
- 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 gboolean sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr);
-
-SPGenericEllipse::SPGenericEllipse() : SPShape() {
- this->cx.unset();
- this->cy.unset();
- this->rx.unset();
- this->ry.unset();
-
- this->start = 0.0;
- this->end = SP_2PI;
- this->closed = TRUE;
}
SPGenericEllipse::~SPGenericEllipse() {
@@ -109,7 +81,7 @@ void SPGenericEllipse::update(SPCtx *ctx, guint flags) {
double const dx = viewbox.width();
double const dy = viewbox.height();
- double const dr = sqrt(dx*dx + dy*dy)/sqrt(2);
+ 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
@@ -142,13 +114,22 @@ void SPGenericEllipse::update_patheffect(bool write) {
this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
-#include <2geom/ellipse.h>
+bool SPGenericEllipse::isSlice() const {
+ // figure out if we have a slice, guarding against rounding errors
+ double diff = fmod(this->end - this->start, SP_2PI);
+
+ if (diff < 0.0) {
+ diff += SP_2PI;
+ }
+
+ return (fabs(diff) >= 1e-8 && fabs(diff - SP_2PI) >= 1e-8);
+}
/* fixme: Think (Lauris) */
/* Can't we use arcto in this method? */
void SPGenericEllipse::set_shape() {
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");
+ 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 (this->getRepr()->attribute("d")) {
// unconditionally read the curve from d, if any, to preserve appearance
@@ -169,32 +150,17 @@ void SPGenericEllipse::set_shape() {
return;
}
- sp_genericellipse_normalize(this);
-
- // figure out if we have a slice, guarding against rounding errors
- double len = fmod(this->end - this->start, SP_2PI);
-
- if (len < 0.0) {
- len += SP_2PI;
- }
-
- bool slice = false;
-
- if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
- slice = false;
- this->end = this->start + SP_2PI;
- } else {
- slice = true;
- }
+ this->normalize();
SPCurve *curve = NULL;
// For simplicity, we use a circle with center (0, 0) and radius 1 for our calculations.
- if (slice) {
- Geom::Point startPoint(cos(start), sin(start));
- Geom::Point endPoint(cos(end), sin(end));
- Geom::Point middlePoint = make_angle_bisector_ray(Geom::Ray(Geom::Point(), start), Geom::Ray(Geom::Point(), end)).versor();
+ if (this->isSlice()) {
+ Geom::Point center(0, 0);
+ Geom::Point startPoint = Geom::Point::polar(start);
+ Geom::Point endPoint = Geom::Point::polar(end);
+ Geom::Point middlePoint = make_angle_bisector_ray(Geom::Ray(center, start), Geom::Ray(center, end)).versor();
Geom::Ellipse ellipse(0, 0, 1, 1, 0);
Geom::EllipticalArc *arc = ellipse.arc(startPoint, middlePoint, endPoint);
@@ -209,7 +175,7 @@ void SPGenericEllipse::set_shape() {
if (this->closed) {
// "pizza slice"
- pb.lineTo(Geom::Point(0, 0));
+ pb.lineTo(center);
pb.closePath();
} else {
// arc only
@@ -252,69 +218,49 @@ void SPGenericEllipse::set_shape() {
}
void SPGenericEllipse::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) {
- sp_genericellipse_normalize(this);
+ this->normalize();
Geom::Affine const i2dt = this->i2dt_affine();
- // figure out if we have a slice, while guarding against rounding errors
- bool slice = false;
- double len = fmod(this->end - this->start, SP_2PI);
-
- if (len < 0.0) {
- len += SP_2PI;
- }
-
- if (fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8) {
- slice = false;
- this->end = this->start + SP_2PI;
- } else {
- slice = true;
- }
-
- double rx = this->rx.computed;
- double ry = this->ry.computed;
- double cx = this->cx.computed;
- double cy = this->cy.computed;
-
- // Snap to the 4 quadrant points of the this, but only if the arc
+ // 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 >= this->start && angle <= this->end) {
- Geom::Point pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2dt;
+ Geom::Point pt = this->getPointAtAngle(angle) * i2dt;
p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT));
}
}
}
- // Add the centre, if we have a closed slice or when explicitly asked for
- bool c1 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && this->closed;
- bool c2 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT);
+ double cx = this->cx.computed;
+ double cy = this->cy.computed;
- if (c1 || c2) {
- Geom::Point pt = Geom::Point(cx, cy) * i2dt;
+ bool slice = this->isSlice();
- if (c1) {
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
- }
+ // 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));
+ }
- if (c2) {
- p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
- }
+ 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));
}
// 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(this->start, M_PI_2) != 0.0 ) {
- Geom::Point pt = Geom::Point(cx + cos(this->start)*rx, cy + sin(this->start)*ry) * i2dt;
+ Geom::Point pt = this->getPointAtAngle(this->start) * 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(this->end, M_PI_2) != 0.0 ) {
- Geom::Point pt = Geom::Point(cx + cos(this->end)*rx, cy + sin(this->end)*ry) * i2dt;
+ Geom::Point pt = this->getPointAtAngle(this->end) * i2dt;
p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
}
}
@@ -330,6 +276,7 @@ Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform)
Geom::Affine ret(Geom::Affine(xform).withoutTranslation());
gdouble const sw = hypot(ret[0], ret[1]);
gdouble const sh = hypot(ret[2], ret[3]);
+
if (sw > 1e-9) {
ret[0] /= sw;
ret[1] /= sw;
@@ -337,6 +284,7 @@ Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform)
ret[0] = 1.0;
ret[1] = 0.0;
}
+
if (sh > 1e-9) {
ret[2] /= sh;
ret[3] /= sh;
@@ -348,6 +296,7 @@ Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform)
if (this->rx._set) {
this->rx = this->rx.computed * sw;
}
+
if (this->ry._set) {
this->ry = this->ry.computed * sh;
}
@@ -373,20 +322,18 @@ Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform)
return ret;
}
-void
-sp_genericellipse_normalize(SPGenericEllipse *ellipse)
-{
- ellipse->start = fmod(ellipse->start, SP_2PI);
- ellipse->end = fmod(ellipse->end, SP_2PI);
+void SPGenericEllipse::normalize() {
+ this->start = fmod(this->start, SP_2PI);
+ this->end = fmod(this->end, SP_2PI);
- if (ellipse->start < 0.0) {
- ellipse->start += SP_2PI;
+ if (this->start < 0.0) {
+ this->start += SP_2PI;
}
- double diff = ellipse->start - ellipse->end;
+ double diff = this->start - this->end;
if (diff >= 0.0) {
- ellipse->end += diff - fmod(diff, SP_2PI) + SP_2PI;
+ this->end += diff - fmod(diff, SP_2PI) + SP_2PI;
}
/* Now we keep: 0 <= start < end <= 2*PI */
@@ -404,7 +351,7 @@ Inkscape::XML::Node* SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I
sp_repr_set_svg_double(repr, "sodipodi:ry", this->ry.computed);
if (SP_IS_ARC(this)) {
- sp_arc_set_elliptical_path_attribute(SP_ARC(this), this->getRepr());
+ SP_ARC(this)->sp_arc_set_elliptical_path_attribute(this->getRepr());
}
}
@@ -487,24 +434,6 @@ const char* SPEllipse::displayName() {
}
-void
-sp_ellipse_position_set(SPEllipse *ellipse, gdouble x, gdouble y, gdouble rx, gdouble ry)
-{
- 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;
-
- ((SPObject *)ge)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-}
-
/* SVG <circle> element */
SPCircle::SPCircle() : SPGenericEllipse() {
}
@@ -594,32 +523,32 @@ void SPArc::build(SPDocument *document, Inkscape::XML::Node *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)
-{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
-
+bool SPArc::sp_arc_set_elliptical_path_attribute(Inkscape::XML::Node *repr) {
Inkscape::SVG::PathString str;
- 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;
+ Geom::Point p1 = this->getPointAtAngle(this->start);
+ Geom::Point p2 = this->getPointAtAngle(this->end);
+ double rx = this->rx.computed;
+ double ry = this->ry.computed;
str.moveTo(p1);
- double dt = fmod(ge->end - ge->start, SP_2PI);
+ double dt = fmod(this->end - this->start, SP_2PI);
+
if (fabs(dt) < 1e-6) {
- Geom::Point ph = sp_arc_get_xy(arc, (ge->start + ge->end) / 2.0);
+ Geom::Point ph = getPointAtAngle((this->start + this->end) / 2.0);
+
str.arcTo(rx, ry, 0, true, true, ph)
.arcTo(rx, ry, 0, true, true, p2)
.closePath();
} 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);
+
+ if (this->closed) {
+ Geom::Point center = Geom::Point(this->cx.computed, this->cy.computed);
str.lineTo(center).closePath();
}
}
@@ -642,13 +571,7 @@ Inkscape::XML::Node* SPArc::write(Inkscape::XML::Document *xml_doc, Inkscape::XM
sp_repr_set_svg_double(repr, "sodipodi:ry", this->ry.computed);
// write start and end only if they are non-trivial; otherwise remove
- gdouble len = fmod(this->end - this->start, SP_2PI);
-
- if (len < 0.0) {
- len += SP_2PI;
- }
-
- if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
+ if (this->isSlice()) {
sp_repr_set_svg_double(repr, "sodipodi:start", this->start);
sp_repr_set_svg_double(repr, "sodipodi:end", this->end);
@@ -661,7 +584,7 @@ Inkscape::XML::Node* SPArc::write(Inkscape::XML::Document *xml_doc, Inkscape::XM
}
// write d=
- sp_arc_set_elliptical_path_attribute(this, repr);
+ this->sp_arc_set_elliptical_path_attribute(repr);
SPGenericEllipse::write(xml_doc, repr, flags);
@@ -735,15 +658,8 @@ void SPArc::modified(guint flags) {
SPGenericEllipse::modified(flags);
}
-
const char* SPArc::displayName() {
- gdouble len = fmod(this->end - this->start, SP_2PI);
-
- if (len < 0.0) {
- len += SP_2PI;
- }
-
- if (!(fabs(len) < 1e-8 || fabs(len - SP_2PI) < 1e-8)) {
+ if (this->isSlice()) {
if (this->closed) {
return _("Segment");
} else {
@@ -754,40 +670,31 @@ const char* SPArc::displayName() {
}
}
-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);
+void SPArc::sp_arc_position_set(gdouble x, gdouble y, gdouble rx, gdouble ry) {
+ this->cx.computed = x;
+ this->cy.computed = y;
+ this->rx.computed = rx;
+ this->ry.computed = ry;
- 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;
+ 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();
+ }
- ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-}
+ 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();
+ }
-Geom::Point sp_arc_get_xy(SPArc *arc, gdouble arg)
-{
- SPGenericEllipse *ge = SP_GENERICELLIPSE(arc);
+ this->closed = !prefs->getBool("/tools/shapes/arc/open");
- return Geom::Point(ge->rx.computed * cos(arg) + ge->cx.computed,
- ge->ry.computed * sin(arg) + ge->cy.computed);
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
+Geom::Point SPGenericEllipse::getPointAtAngle(double arg) const {
+ return Geom::Point::polar(arg) * Geom::Scale(rx.computed, ry.computed) * Geom::Translate(cx.computed, cy.computed);
+}
/*
Local Variables: