From aa8035bc2ce875797c8ba1ba94c93c6700bfa88b Mon Sep 17 00:00:00 2001 From: schwieni Date: Mon, 26 Feb 2018 20:41:30 +0100 Subject: [PATCH 1/7] Added live effect points 2 ellipse (or Ellipse from points) --- src/live_effects/lpe-pts2ellipse.cpp | 675 +++++++++++++++++++++++++++ src/live_effects/lpe-pts2ellipse.h | 85 ++++ 2 files changed, 760 insertions(+) create mode 100644 src/live_effects/lpe-pts2ellipse.cpp create mode 100644 src/live_effects/lpe-pts2ellipse.h diff --git a/src/live_effects/lpe-pts2ellipse.cpp b/src/live_effects/lpe-pts2ellipse.cpp new file mode 100644 index 0000000000..692973d416 --- /dev/null +++ b/src/live_effects/lpe-pts2ellipse.cpp @@ -0,0 +1,675 @@ +/** \file + * LPE "Points to Ellipse" implementation + */ + +/* + * Authors: + * Markus Schwienbacher + * + * Copyright (C) Markus Schwienbacher 2013 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-pts2ellipse.h" + +#include +#include +#include +#include +#include +// #include "ui/widget/scalar.h" +#include + +#include <2geom/path.h> +#include <2geom/circle.h> +#include <2geom/ellipse.h> +#include <2geom/pathvector.h> +#include <2geom/elliptical-arc.h> +// #include <2geom/path-intersection.h> // for winding() + +#include + +using namespace Geom; + +namespace Inkscape { +namespace LivePathEffect { + +static const Util::EnumData EllipseMethodData[] = { + { EM_AUTO, N_("Auto ellipse"), "auto" }, //!< (2..4 points: circle, from 5 points: ellipse) + { EM_CIRCLE, N_("Force circle"), "circle" }, + { EM_ISONOMETRIC_CIRCLE, N_("Isometric circle"), "iso_circle" } +}; +static const Util::EnumDataConverter EMConverter(EllipseMethodData, EM_END); + +LPEPts2Ellipse::LPEPts2Ellipse(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + method(_("Method:"), _("Methods to generate the ellipse"), + "method", EMConverter, &wr, this, EM_AUTO), + //only_circle(_("_Only circle"), _("Fits points to circle instead of ellipse"), "only_circle", &wr, this, false), + // gen_isometric(_("_Isometric from parallelogram"), _("Takes the first three points, and fits it into the parallelogram.\nIdeal for Isometric views"), "gen_isometric", &wr, this, false), + gen_isometric_frame(_("_Frame (isometric rectangle)"), _("Draw Parallelogram around the ellipse"), + "gen_isometric_frame", &wr, this, false), + gen_arc(_("_Arc"), _("Generate open arc (open ellipse)"), "gen_arc", &wr, this, false), + other_arc(_("_Other Arc side"), _("switch sides of the arc"), "arc_other", &wr, this, false), + slice_arc(_("_Slice Arc"), _("slice the arc"), "slice_arc", &wr, this, false), + draw_axes(_("A_xes"), _("Draw both semi-major and semi-minor axes"), "draw_axes", &wr, this, false), + rot_axes(_("Axes Rotation"), _("Axes rotation angle [deg]"), "rot_axes", &wr, this, 0), + draw_ori_path(_("Source _Path"), _("Show the original source path"), "draw_ori_path", &wr, this, false) +{ + //registerParameter(&only_circle); + //registerParameter(&gen_isometric); + registerParameter(&method); + registerParameter(&gen_arc); + registerParameter(&other_arc); + registerParameter(&slice_arc); + registerParameter(&gen_isometric_frame); + registerParameter(&draw_axes); + registerParameter(&rot_axes); + registerParameter(&draw_ori_path); + + rot_axes.param_set_range(-360,360); + rot_axes.param_set_increments(1,10); + + show_orig_path=true; +} + +LPEPts2Ellipse::~LPEPts2Ellipse() +{ +} + +// helper function, transforms a given value into range [0, 2pi] +inline double +range2pi(double a) +{ + a = fmod(a, 2*M_PI); + if(a<0) a+=2*M_PI; + return a; +} + +inline double +deg2rad(double a) +{ + return a*M_PI/180.0; +} + +inline double +rad2deg(double a) +{ + return a*180.0/M_PI; +} + +// helper function, calculates the angle between a0 and a1 in ccw sense +// examples: 0..1->1, -1..1->2, pi/4..-pi/4->1.5pi +// full rotations: 0..2pi->2pi, -pi..pi->2pi, pi..-pi->0, 2pi..0->0 +inline double +calc_delta_angle(const double a0, const double a1) +{ + double da=range2pi(a1-a0); + if((fabs(da)<1e-9) && (a0moveto(cos(start), sin(start)); + double s = start; + for (int i=0; i < nda; s = (++i)*da+start) { + double e = s + da; + if (e > end) + e = end; + const double len = 4*tan((e - s)/4)/3; + const double x0 = cos(s); + const double y0 = sin(s); + const double x1 = x0 + len * cos(s + M_PI_2); + const double y1 = y0 + len * sin(s + M_PI_2); + const double x3 = cos(e); + const double y3 = sin(e); + const double x2 = x3 + len * cos(e - M_PI_2); + const double 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 (slice && !closed) { + curve->lineto(0., 0.); + } + curve->transform(affine); + + path.append(*curve->first_path()); + if ((slice && !closed) || closed) { + path.close(true); + } + // give to GC + curve->unref(); + return 0; +} + +void +gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine) +{ + Geom::Path rect; + SPCurve curve; + // unit rectangle + curve.moveto(-1, -1); + curve.lineto(1, -1); + curve.lineto(1, 1); + curve.lineto(-1, 1); + //curve.transform(Rotate(-rot_angle)*affine); + curve.transform(affine); + rect.append(*curve.first_path()); + rect.close(true); + path_out.push_back(rect); +} + +Gtk::Widget * +LPEPts2Ellipse::newWidget() +{ + Gtk::Widget *widget=Effect::newWidget(); + Gtk::VBox * vbox=dynamic_cast(widget); + // now add our button to the vbox + Gtk::Button *genEllipseButton = Gtk::manage(new Gtk::Button(Glib::ustring(_("put ellipse")))); + genEllipseButton->signal_clicked() + //.connect(sigc::bind(sigc::mem_fun(*this, &LPEPts2Ellipse::putEllipseInDocument),FILLET)); + .connect(sigc::mem_fun(*this, &LPEPts2Ellipse::putEllipseInDocument)); + vbox->pack_start(*genEllipseButton, true, true, 2); + return vbox; +} + +/* +int +LPEPts2Ellipse::putEllipseInDocument() +{ + if(pts.size()<2) { + return -1; + } + + + Geom::PathVector const & path_in; + + + // Create object + SPDesktop *desktop=SP_ACTIVE_DESKTOP; + if(NULL==desktop) return -1; + + Inkscape::Selection *selection = SPDesktop::getSelection(); + if(NULL==selection || selection->isEmpty()) return -1; + SPItem *item = selection->singleItem(); + if(!item) return -1; + SPLPEItem *lpeitem = dynamic_cast(item); + if ( lpeitem ) { + + } + else { + SPUse *use = dynamic_cast(item); + if ( use ) { + printf("%s/%d:have use!\n",__FUNCTION__,__LINE__); + } + } + + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); + repr->setAttribute("sodipodi:type", "arc"); + + // Set style + sp_desktop_apply_style_tool(desktop, repr, "/tools/shapes/arc", false); + + SPGenericEllipse *arc = SP_GENERICELLIPSE(desktop->currentLayer()->appendChildRepr(repr)); + + if(slice_arc.get_value()) + arc->setArcType(SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE); + else + arc->setArcType(SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); + + const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation + Affine affine; + affine*=Rotate(rot_angle); + Coord a0=0; + Coord a1=2*M_PI; + if(pts.size()==2) { + // simple line: circle in the middle of the line to the vertices + Point line=pts.front()-pts.back(); + double radius=line.length()*0.5; + if(radius<1e-9) + return -1; + Point center=middle_point(pts.front(),pts.back()); + affine*=Translate(circle.center()); + } else if(pts.size()>=5 && EM_AUTO == method) { + // do ellipse + try { + Ellipse ellipse; + ellipse.fit(pts); + affine*=Rotate(ellipse.rotationAngle()); + affine*=Translate(ellipse.center()); + if(gen_arc.get_value()) { + Affine inv_affine=affine.inverse(); + Point p0=pts.front()*inv_affine; + Point p1=pts.back()*inv_affine; + endpoints2angles(p0,p1,a0,a1); + arc->start=a0; + arc->end=a1; + } + arc->rx=ellipse.ray(X); + arc->ry=ellipse.ray(Y); + //arc->set_transform(affine); + } catch(...) { + return -1; + } + } else { + // do a circle (3,4 points, or only_circle set) + try { + Circle circle; + circle.fit(pts); + affine*=Translate(circle.center()); + + if(gen_arc.get_value()) + { + Point p0=pts.front()-circle.center(); + Point p1=pts.back()-circle.center(); + endpoints2angles(p0,p1,a0,a1); + arc->start=a0; + arc->end=a1; + } + arc->rx=circle.radius; + arc->ry=circle.radius; + } catch(...) { + return -1; + } + } + + arc->set_transform(affine); + + Inkscape::GC::release(repr); + //arc->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse(); + arc->updateRepr(); + + // finishItem + arc->doWriteTransform(arc->transform, NULL, true); + //desktop->canvas->endForcedFullRedraws(); + + desktop->canvas->endForcedFullRedraws(); + +} +*/ + +void +gen_axes_paths(Geom::PathVector &path_out, const Geom::Affine &affine)//, const double &rot_axes) +{ + // Coord sa,ca; + // sincos(deg2rad(rot_axes),sa,ca); + // LineSegment clx(Point(-ca,-sa),Point(ca,sa)); + // LineSegment cly(Point(sa,-ca),Point(-sa,ca)); + + LineSegment clx(Point(-1,0),Point(1,0)); + LineSegment cly(Point(0,-1),Point(0,1)); + + Geom::Path plx, ply; + plx.append(clx); + ply.append(cly); + plx*=affine; + ply*=affine; + + path_out.push_back(plx); + path_out.push_back(ply); +} + +bool +is_ccw(const std::vector & pts) +{ + // method: sum up the angles between edges + size_t n=pts.size(); + // edges about vertex 0 + Point e0=pts.front()-pts.back(); + Point e1=pts[1]-pts[0]; + Coord sum=cross(e0,e1); + // the rest + for(size_t i=1;i pts; + for(PathVector::const_iterator pit = path_in.begin(); pit!= path_in.end(); ++pit) { + // extract first point of this path + pts.push_back(pit->initialPoint()); + // iterate over all curves + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end(); ++cit) { + pts.push_back(cit->finalPoint()); + } + } + + // avoid identical start-point and end-point + if(pts.front() == pts.back()) { + pts.pop_back(); + } + + // special mode: Use first two edges, interpret them as two sides of a parallelogram and + // generate an ellipse residing inside the parallelogram. This effect is quite useful when + // generating isometric views. Hence, the name. + //if(gen_isometric.get_value()) + if(method == EM_ISONOMETRIC_CIRCLE) + { + if(0!=genIsometricEllipse (pts, path_out)) + return path_in; + } + else + { + if(0!=genFitEllipse(pts, path_out)) + return path_in; + } + return path_out; +} + +/** + * Generates an ellipse (or circle) from the vertices of a given path. Thereby, using fitting + * algorithms from 2geom. Depending on the settings made by the user regarding things like arc, + * slice, circle etc. the final result will be different. We need at least 5 points to fit an + * ellipse. With 5 points each point is on the ellipse. For less points we get a circle. + */ +int +LPEPts2Ellipse::genFitEllipse (std::vector const & pts, + Geom::PathVector & path_out) +{ + // rotation angle based on user provided rot_axes to position the vertices + const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation + Affine affine; + affine*=Rotate(rot_angle); + Coord a0=0; + Coord a1=2*M_PI; + + if(pts.size()<2) { + return -1; + } else if(pts.size()==2) { + // simple line: circle in the middle of the line to the vertices + Point line=pts.front()-pts.back(); + double radius=line.length()*0.5; + if(radius<1e-9) + return -1; + Point center=middle_point(pts.front(),pts.back()); + Circle circle(center[0],center[1],radius); + //Affine affine; + //affine*=Rotate(rot_angle); + //affine*=Scale(circle.ray()); + affine*=Scale(circle.radius()); + affine*=Translate(circle.center()); + Geom::Path path; + unit_arc_path(path,affine); + path_out.push_back(path); + } else if(pts.size()>=5 && EM_AUTO == method) { //!only_circle.get_value()) { + // do ellipse + try { + Ellipse ellipse; + ellipse.fit(pts); + //Affine affine; + //affine*=Rotate(rot_angle); + affine*=Scale(ellipse.ray(X),ellipse.ray(Y)); + affine*=Rotate(ellipse.rotationAngle()); + affine*=Translate(ellipse.center()); + + // Coord a0=0; + // Coord a1=2*M_PI; + + if(gen_arc.get_value()) { + Affine inv_affine=affine.inverse(); + + Point p0=pts.front()*inv_affine; + Point p1=pts.back()*inv_affine; + + endpoints2angles(p0,p1,a0,a1); + + } + + Geom::Path path; + unit_arc_path(path,affine,a0,a1,slice_arc.get_value()); + path_out.push_back(path); + + if(draw_axes.get_value()) { + gen_axes_paths(path_out,affine); + } + } catch(...) { + return -1; + } + } else { + // do a circle (3,4 points, or only_circle set) + try { + Circle circle; + circle.fit(pts); + //Affine affine; + //affine*=Rotate(rot_angle); + //affine*=Scale(circle.ray()); + affine*=Scale(circle.radius()); + affine*=Translate(circle.center()); + + // Coord a0=0.0; + // Coord a1=2*M_PI; + + if(gen_arc.get_value()) + { + Point p0=pts.front()-circle.center(); + Point p1=pts.back()-circle.center(); + endpoints2angles(p0,p1,a0,a1); + + // if(!p0.isZero() && !p1.isZero()) { + // a0=atan2(p0); + // a1=atan2(p1); + + // const bool ccw_wind=is_ccw(pts); + // //printf("p0=[%e, %e], p1=[%e, %e]\n",p0[0],p0[1],p1[0],p1[1]); + // //printf("a0=%f, a1=%f, have ccw winding: %d\n",a0,a1,ccw_wind); + + // if(!ccw_wind) { + // //swapCoords(a0,a1); + // std::swap(a0,a1); + // } + // if(!other_arc.get_value()) { + // //swapCoords(a0,a1); + // std::swap(a0,a1); + // } + // } + } + Geom::Path path; + unit_arc_path(path,affine,a0,a1,slice_arc.get_value()); + path_out.push_back(path); + } catch(...) { + return -1; + } + } + + // draw frame? + if(gen_isometric_frame.get_value()) { + gen_iso_frame_paths(path_out,affine); + } + + // draw axes? + if(draw_axes.get_value()) { + gen_axes_paths(path_out,affine); + } + + return 0; +} + +int +LPEPts2Ellipse::genIsometricEllipse (std::vector const & pts, + Geom::PathVector & path_out) + +{ + // take the first 3 vertices for the edges + if(pts.size() < 3) return -1; + // calc edges + Point e0=pts[0]-pts[1]; + Point e1=pts[2]-pts[1]; + + Coord ce=cross(e0,e1); + // parallel or one is zero? + if(fabs(ce)<1e-9) return -1; + // unit vectors along edges + Point u0=unit_vector(e0); + Point u1=unit_vector(e1); + // calc angles + Coord a0=atan2(e0); + // Coord a1=M_PI_2-atan2(e1)-a0; + Coord a1=acos(dot(u0,u1))-M_PI_2; + // if(fabs(a1)<1e-9) return -1; + if(ce<0) a1=-a1; + // lengths: l0= length of edge 0; l1= height of parallelogram + Coord l0=e0.length()*0.5; + Point e0n=e1-dot(u0,e1)*u0; + Coord l1=e0n.length()*0.5; + // Coord l1=e1.length()*0.5; + // printf("angles [%f %f] pi, diameters [%f, %f]\n",a0/M_PI,a1/M_PI,l0,l1); + + // center of the ellipse + Point pos=pts[1]+0.5*(e0+e1); + + // rotation angle based on user provided rot_axes to position the vertices + const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation + + // build up the affine transformation + Affine affine; + affine*=Rotate(rot_angle); + affine*=Scale(l0,l1); + affine*=HShear(-tan(a1)); + affine*=Rotate(a0); + affine*=Translate(pos); + + Geom::Path path; + unit_arc_path(path,affine); + path_out.push_back(path); + + // draw frame? + if(gen_isometric_frame.get_value()) { + gen_iso_frame_paths(path_out,affine); + } + + // draw axes? + if(draw_axes.get_value()) { + gen_axes_paths(path_out,affine); + } + + return 0; +} + +// void +// LPEPts2Ellipse::addCanvasIndicators(const SPLPEItem */*lpeitem*/, std::vector &hp_vec) +// { +// printf("Hallo LPEPts2Ellipse::addCanvasIndicators!\n"); +// SPCurve curve; +// curve.moveto(points.front()); +// const size_t n=points.size(); +// for(size_t i;i + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/effect.h" +#include "live_effects/parameter/bool.h" +#include "live_effects/parameter/enum.h" +// #include "live_effects/parameter/parameter.h" +// #include "live_effects/parameter/point.h" + +namespace Inkscape { +namespace LivePathEffect { + +enum EllipseMethod { + EM_AUTO, + EM_CIRCLE, + EM_ISONOMETRIC_CIRCLE, + EM_END +}; + +class LPEPts2Ellipse : public Effect { +public: + LPEPts2Ellipse(LivePathEffectObject *lpeobject); + virtual ~LPEPts2Ellipse(); + + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); + virtual Gtk::Widget * newWidget(); + // protected: + // virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector< Geom::PathVector > &hp_vec); + + int putEllipseInDocument(); + +private: + LPEPts2Ellipse(const LPEPts2Ellipse&); + LPEPts2Ellipse& operator=(const LPEPts2Ellipse&); + + + int genIsometricEllipse (std::vector const & points_in, + Geom::PathVector & path_out); + + int genFitEllipse (std::vector const & points_in, + Geom::PathVector & path_out); + + EnumParam method; +// BoolParam only_circle; +// BoolParam gen_isometric; + BoolParam gen_isometric_frame; + BoolParam gen_arc; + BoolParam other_arc; + BoolParam slice_arc; + BoolParam draw_axes; + ScalarParam rot_axes; + BoolParam draw_ori_path; + + // Geom::Path helper_path; + std::vector points; +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : -- GitLab From 8dae5cfc687b61bab0bc6bb69539b8e0543562dd Mon Sep 17 00:00:00 2001 From: schwieni Date: Mon, 26 Feb 2018 22:16:15 +0100 Subject: [PATCH 2/7] Live effect Points to Ellipse (or: Ellipse from Points) added. --- src/live_effects/CMakeLists.txt | 2 + src/live_effects/effect-enum.h | 1 + src/live_effects/effect.cpp | 5 + src/live_effects/lpe-pts2ellipse.cpp | 234 ++------------------------- src/live_effects/lpe-pts2ellipse.h | 8 - 5 files changed, 17 insertions(+), 233 deletions(-) diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 9d18626bc1..aa482a6cf5 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -61,6 +61,7 @@ set(live_effects_SRC lpeobject.cpp spiro-converters.cpp spiro.cpp + lpe-pts2ellipse.cpp parameter/array.cpp parameter/bool.cpp @@ -154,6 +155,7 @@ set(live_effects_SRC lpeobject.h spiro-converters.h spiro.h + lpe-pts2ellipse.h parameter/array.h parameter/bool.h diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index 20d941e693..dc55b52809 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -63,6 +63,7 @@ enum EffectType { LINE_SEGMENT, OFFSET, PARALLEL, + PTS2ELLIPSE, PATH_LENGTH, PERP_BISECTOR, PERSPECTIVE_PATH, diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index b827cff4bc..9a96560133 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -66,6 +66,7 @@ #include "live_effects/lpe-vonkoch.h" #include "live_effects/lpe-embrodery-stitch.h" #include "live_effects/lpe-bool.h" +#include "live_effects/lpe-pts2ellipse.h" #include "live_effects/lpeobject.h" @@ -156,6 +157,7 @@ const Util::EnumData LPETypeData[] = { {RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"}, {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"}, {TEXT_LABEL, N_("Text label"), "text_label"}, + {PTS2ELLIPSE, N_("Ellipse from points"), "pts2ellipse"}, #endif }; @@ -341,6 +343,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case MEASURE_SEGMENTS: neweffect = static_cast ( new LPEMeasureSegments(lpeobj) ); break; + case PTS2ELLIPSE: + neweffect = static_cast ( new LPEPts2Ellipse(lpeobj) ); + break; default: g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); neweffect = NULL; diff --git a/src/live_effects/lpe-pts2ellipse.cpp b/src/live_effects/lpe-pts2ellipse.cpp index 692973d416..a6ea568996 100644 --- a/src/live_effects/lpe-pts2ellipse.cpp +++ b/src/live_effects/lpe-pts2ellipse.cpp @@ -18,7 +18,6 @@ #include #include #include -// #include "ui/widget/scalar.h" #include #include <2geom/path.h> @@ -26,7 +25,6 @@ #include <2geom/ellipse.h> #include <2geom/pathvector.h> #include <2geom/elliptical-arc.h> -// #include <2geom/path-intersection.h> // for winding() #include @@ -46,8 +44,6 @@ LPEPts2Ellipse::LPEPts2Ellipse(LivePathEffectObject *lpeobject) : Effect(lpeobject), method(_("Method:"), _("Methods to generate the ellipse"), "method", EMConverter, &wr, this, EM_AUTO), - //only_circle(_("_Only circle"), _("Fits points to circle instead of ellipse"), "only_circle", &wr, this, false), - // gen_isometric(_("_Isometric from parallelogram"), _("Takes the first three points, and fits it into the parallelogram.\nIdeal for Isometric views"), "gen_isometric", &wr, this, false), gen_isometric_frame(_("_Frame (isometric rectangle)"), _("Draw Parallelogram around the ellipse"), "gen_isometric_frame", &wr, this, false), gen_arc(_("_Arc"), _("Generate open arc (open ellipse)"), "gen_arc", &wr, this, false), @@ -57,8 +53,6 @@ LPEPts2Ellipse::LPEPts2Ellipse(LivePathEffectObject *lpeobject) : rot_axes(_("Axes Rotation"), _("Axes rotation angle [deg]"), "rot_axes", &wr, this, 0), draw_ori_path(_("Source _Path"), _("Show the original source path"), "draw_ori_path", &wr, this, false) { - //registerParameter(&only_circle); - //registerParameter(&gen_isometric); registerParameter(&method); registerParameter(&gen_arc); registerParameter(&other_arc); @@ -116,7 +110,6 @@ unit_arc_path(Geom::Path &path, Geom::Affine &affine, double start=0.0, double end=2*M_PI, // angles bool slice=false) { - // double Da = calc_delta_angle(start,end); if (fabs(Da) < 1e-9) { g_warning("angle was 0"); @@ -158,10 +151,6 @@ unit_arc_path(Geom::Path &path, Geom::Affine &affine, const double y3 = sin(e); const double x2 = x3 + len * cos(e - M_PI_2); const double 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); } @@ -196,144 +185,9 @@ gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine) path_out.push_back(rect); } -Gtk::Widget * -LPEPts2Ellipse::newWidget() -{ - Gtk::Widget *widget=Effect::newWidget(); - Gtk::VBox * vbox=dynamic_cast(widget); - // now add our button to the vbox - Gtk::Button *genEllipseButton = Gtk::manage(new Gtk::Button(Glib::ustring(_("put ellipse")))); - genEllipseButton->signal_clicked() - //.connect(sigc::bind(sigc::mem_fun(*this, &LPEPts2Ellipse::putEllipseInDocument),FILLET)); - .connect(sigc::mem_fun(*this, &LPEPts2Ellipse::putEllipseInDocument)); - vbox->pack_start(*genEllipseButton, true, true, 2); - return vbox; -} - -/* -int -LPEPts2Ellipse::putEllipseInDocument() -{ - if(pts.size()<2) { - return -1; - } - - - Geom::PathVector const & path_in; - - - // Create object - SPDesktop *desktop=SP_ACTIVE_DESKTOP; - if(NULL==desktop) return -1; - - Inkscape::Selection *selection = SPDesktop::getSelection(); - if(NULL==selection || selection->isEmpty()) return -1; - SPItem *item = selection->singleItem(); - if(!item) return -1; - SPLPEItem *lpeitem = dynamic_cast(item); - if ( lpeitem ) { - - } - else { - SPUse *use = dynamic_cast(item); - if ( use ) { - printf("%s/%d:have use!\n",__FUNCTION__,__LINE__); - } - } - - Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); - Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); - repr->setAttribute("sodipodi:type", "arc"); - - // Set style - sp_desktop_apply_style_tool(desktop, repr, "/tools/shapes/arc", false); - - SPGenericEllipse *arc = SP_GENERICELLIPSE(desktop->currentLayer()->appendChildRepr(repr)); - - if(slice_arc.get_value()) - arc->setArcType(SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE); - else - arc->setArcType(SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); - - const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation - Affine affine; - affine*=Rotate(rot_angle); - Coord a0=0; - Coord a1=2*M_PI; - if(pts.size()==2) { - // simple line: circle in the middle of the line to the vertices - Point line=pts.front()-pts.back(); - double radius=line.length()*0.5; - if(radius<1e-9) - return -1; - Point center=middle_point(pts.front(),pts.back()); - affine*=Translate(circle.center()); - } else if(pts.size()>=5 && EM_AUTO == method) { - // do ellipse - try { - Ellipse ellipse; - ellipse.fit(pts); - affine*=Rotate(ellipse.rotationAngle()); - affine*=Translate(ellipse.center()); - if(gen_arc.get_value()) { - Affine inv_affine=affine.inverse(); - Point p0=pts.front()*inv_affine; - Point p1=pts.back()*inv_affine; - endpoints2angles(p0,p1,a0,a1); - arc->start=a0; - arc->end=a1; - } - arc->rx=ellipse.ray(X); - arc->ry=ellipse.ray(Y); - //arc->set_transform(affine); - } catch(...) { - return -1; - } - } else { - // do a circle (3,4 points, or only_circle set) - try { - Circle circle; - circle.fit(pts); - affine*=Translate(circle.center()); - - if(gen_arc.get_value()) - { - Point p0=pts.front()-circle.center(); - Point p1=pts.back()-circle.center(); - endpoints2angles(p0,p1,a0,a1); - arc->start=a0; - arc->end=a1; - } - arc->rx=circle.radius; - arc->ry=circle.radius; - } catch(...) { - return -1; - } - } - - arc->set_transform(affine); - - Inkscape::GC::release(repr); - //arc->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse(); - arc->updateRepr(); - - // finishItem - arc->doWriteTransform(arc->transform, NULL, true); - //desktop->canvas->endForcedFullRedraws(); - - desktop->canvas->endForcedFullRedraws(); - -} -*/ - void -gen_axes_paths(Geom::PathVector &path_out, const Geom::Affine &affine)//, const double &rot_axes) +gen_axes_paths(Geom::PathVector &path_out, const Geom::Affine &affine) { - // Coord sa,ca; - // sincos(deg2rad(rot_axes),sa,ca); - // LineSegment clx(Point(-ca,-sa),Point(ca,sa)); - // LineSegment cly(Point(sa,-ca),Point(-sa,ca)); - LineSegment clx(Point(-1,0),Point(1,0)); LineSegment cly(Point(0,-1),Point(0,1)); @@ -374,30 +228,16 @@ is_ccw(const std::vector & pts) return false; } -// void swapCoords(Geom::Coord &a0, Geom::Coord &a1) -// { -// Geom::Coord tmp=a0; -// a0=a1; -// a1=tmp; -// } - void -endpoints2angles(const Point &p0, const Point &p1, Coord &a0, Coord &a1) +endpoints2angles(const bool ccw_wind, const bool use_other_arc, const Point &p0, const Point &p1, Coord &a0, Coord &a1) { if(!p0.isZero() && !p1.isZero()) { a0=atan2(p0); a1=atan2(p1); - - const bool ccw_wind=is_ccw(pts); - - //printf("ccw=%d\n",ccw_wind); - if(!ccw_wind) { - //swapCoords(a0,a1); std::swap(a0,a1); } - if(!other_arc.get_value()) { - //swapCoords(a0,a1); + if(!use_other_arc) { std::swap(a0,a1); } } @@ -439,13 +279,10 @@ LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) // generate an ellipse residing inside the parallelogram. This effect is quite useful when // generating isometric views. Hence, the name. //if(gen_isometric.get_value()) - if(method == EM_ISONOMETRIC_CIRCLE) - { + if(method == EM_ISONOMETRIC_CIRCLE) { if(0!=genIsometricEllipse (pts, path_out)) return path_in; - } - else - { + } else { if(0!=genFitEllipse(pts, path_out)) return path_in; } @@ -479,9 +316,6 @@ LPEPts2Ellipse::genFitEllipse (std::vector const & pts, return -1; Point center=middle_point(pts.front(),pts.back()); Circle circle(center[0],center[1],radius); - //Affine affine; - //affine*=Rotate(rot_angle); - //affine*=Scale(circle.ray()); affine*=Scale(circle.radius()); affine*=Translate(circle.center()); Geom::Path path; @@ -492,23 +326,15 @@ LPEPts2Ellipse::genFitEllipse (std::vector const & pts, try { Ellipse ellipse; ellipse.fit(pts); - //Affine affine; - //affine*=Rotate(rot_angle); affine*=Scale(ellipse.ray(X),ellipse.ray(Y)); affine*=Rotate(ellipse.rotationAngle()); affine*=Translate(ellipse.center()); - - // Coord a0=0; - // Coord a1=2*M_PI; - if(gen_arc.get_value()) { Affine inv_affine=affine.inverse(); - Point p0=pts.front()*inv_affine; Point p1=pts.back()*inv_affine; - - endpoints2angles(p0,p1,a0,a1); - + const bool ccw_wind=is_ccw(pts); + endpoints2angles(ccw_wind,other_arc.get_value(),p0,p1,a0,a1); } Geom::Path path; @@ -526,38 +352,15 @@ LPEPts2Ellipse::genFitEllipse (std::vector const & pts, try { Circle circle; circle.fit(pts); - //Affine affine; - //affine*=Rotate(rot_angle); - //affine*=Scale(circle.ray()); affine*=Scale(circle.radius()); affine*=Translate(circle.center()); - // Coord a0=0.0; - // Coord a1=2*M_PI; - if(gen_arc.get_value()) { Point p0=pts.front()-circle.center(); Point p1=pts.back()-circle.center(); - endpoints2angles(p0,p1,a0,a1); - - // if(!p0.isZero() && !p1.isZero()) { - // a0=atan2(p0); - // a1=atan2(p1); - - // const bool ccw_wind=is_ccw(pts); - // //printf("p0=[%e, %e], p1=[%e, %e]\n",p0[0],p0[1],p1[0],p1[1]); - // //printf("a0=%f, a1=%f, have ccw winding: %d\n",a0,a1,ccw_wind); - - // if(!ccw_wind) { - // //swapCoords(a0,a1); - // std::swap(a0,a1); - // } - // if(!other_arc.get_value()) { - // //swapCoords(a0,a1); - // std::swap(a0,a1); - // } - // } + const bool ccw_wind=is_ccw(pts); + endpoints2angles(ccw_wind,other_arc.get_value(),p0,p1,a0,a1); } Geom::Path path; unit_arc_path(path,affine,a0,a1,slice_arc.get_value()); @@ -607,8 +410,6 @@ LPEPts2Ellipse::genIsometricEllipse (std::vector const & pts, Coord l0=e0.length()*0.5; Point e0n=e1-dot(u0,e1)*u0; Coord l1=e0n.length()*0.5; - // Coord l1=e1.length()*0.5; - // printf("angles [%f %f] pi, diameters [%f, %f]\n",a0/M_PI,a1/M_PI,l0,l1); // center of the ellipse Point pos=pts[1]+0.5*(e0+e1); @@ -641,23 +442,6 @@ LPEPts2Ellipse::genIsometricEllipse (std::vector const & pts, return 0; } -// void -// LPEPts2Ellipse::addCanvasIndicators(const SPLPEItem */*lpeitem*/, std::vector &hp_vec) -// { -// printf("Hallo LPEPts2Ellipse::addCanvasIndicators!\n"); -// SPCurve curve; -// curve.moveto(points.front()); -// const size_t n=points.size(); -// for(size_t i;i &hp_vec); - - int putEllipseInDocument(); private: LPEPts2Ellipse(const LPEPts2Ellipse&); @@ -54,8 +49,6 @@ private: Geom::PathVector & path_out); EnumParam method; -// BoolParam only_circle; -// BoolParam gen_isometric; BoolParam gen_isometric_frame; BoolParam gen_arc; BoolParam other_arc; @@ -64,7 +57,6 @@ private: ScalarParam rot_axes; BoolParam draw_ori_path; - // Geom::Path helper_path; std::vector points; }; -- GitLab From c8d88f118064c5c8806277dd960d576cc15bc613 Mon Sep 17 00:00:00 2001 From: schwieni Date: Wed, 28 Feb 2018 00:25:34 +0100 Subject: [PATCH 3/7] Revised version after comments from Jabier Arraiza. --- po/POTFILES.in | 1 + src/live_effects/effect.cpp | 4 ++-- src/live_effects/lpe-pts2ellipse.cpp | 12 ++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index ce52299384..638a8e7c00 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -144,6 +144,7 @@ src/live_effects/lpe-perspective-envelope.cpp src/live_effects/lpe-powerclip.cpp src/live_effects/lpe-powermask.cpp src/live_effects/lpe-powerstroke.cpp +src/live_effects/lpe-pts2ellipse.cpp src/live_effects/lpe-rough-hatches.cpp src/live_effects/lpe-roughen.cpp src/live_effects/lpe-ruler.cpp diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 9a96560133..1255d74218 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -137,9 +137,10 @@ const Util::EnumData LPETypeData[] = { {MEASURE_SEGMENTS, N_("Measure Segments"), "measure_segments"}, {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet_chamfer"}, {BOOL_OP, N_("Boolean operation"), "bool_op"}, - {EMBRODERY_STITCH, N_("Embroidery stitch"), "embrodery_stitch"}, + {EMBRODERY_STITCH, N_("Embroidery stitch"), "embrodery_stitch"}, {POWERCLIP, N_("Power clip"), "powerclip"}, {POWERMASK, N_("Power mask"), "powermask"}, + {PTS2ELLIPSE, N_("Ellipse from points"), "pts2ellipse"}, #ifdef LPE_ENABLE_TEST_EFFECTS {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"}, {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"}, @@ -157,7 +158,6 @@ const Util::EnumData LPETypeData[] = { {RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"}, {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"}, {TEXT_LABEL, N_("Text label"), "text_label"}, - {PTS2ELLIPSE, N_("Ellipse from points"), "pts2ellipse"}, #endif }; diff --git a/src/live_effects/lpe-pts2ellipse.cpp b/src/live_effects/lpe-pts2ellipse.cpp index a6ea568996..4fd1456469 100644 --- a/src/live_effects/lpe-pts2ellipse.cpp +++ b/src/live_effects/lpe-pts2ellipse.cpp @@ -110,8 +110,8 @@ unit_arc_path(Geom::Path &path, Geom::Affine &affine, double start=0.0, double end=2*M_PI, // angles bool slice=false) { - double Da = calc_delta_angle(start,end); - if (fabs(Da) < 1e-9) { + double arc_angle = calc_delta_angle(start,end); + if (fabs(arc_angle) < 1e-9) { g_warning("angle was 0"); return -1; } @@ -119,19 +119,19 @@ unit_arc_path(Geom::Path &path, Geom::Affine &affine, // the delta angle double da=M_PI_2; // number of segments with da length - int nda=(int)ceil(Da/M_PI_2); + int nda=(int)ceil(arc_angle/M_PI_2); // recalculate da - da=Da/(double)nda; + da=arc_angle/(double)nda; bool closed=false; - if (fabs(Da - 2*M_PI) < 1e-8) { + if (fabs(arc_angle - 2*M_PI) < 1e-8) { closed = true; da=M_PI_2; nda=4; } start = range2pi(start); - end=start+Da; + end=start+arc_angle; // adopted from: sp-ellipse.cpp SPCurve * curve=new SPCurve(); -- GitLab From 1ce5926e7367b59fe60bf8dd6012d25a6c746865 Mon Sep 17 00:00:00 2001 From: schwieni Date: Sun, 1 Apr 2018 20:32:37 +0200 Subject: [PATCH 4/7] code typo fixed removed SPCurve dependency, since this belongs to the display part of inkscape --- src/live_effects/lpe-pts2ellipse.cpp | 156 +++++++++++++-------------- src/live_effects/lpe-pts2ellipse.h | 2 +- 2 files changed, 75 insertions(+), 83 deletions(-) diff --git a/src/live_effects/lpe-pts2ellipse.cpp b/src/live_effects/lpe-pts2ellipse.cpp index 4fd1456469..4921e08ca2 100644 --- a/src/live_effects/lpe-pts2ellipse.cpp +++ b/src/live_effects/lpe-pts2ellipse.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include <2geom/path.h> #include <2geom/circle.h> @@ -28,15 +27,13 @@ #include -using namespace Geom; - namespace Inkscape { namespace LivePathEffect { static const Util::EnumData EllipseMethodData[] = { { EM_AUTO, N_("Auto ellipse"), "auto" }, //!< (2..4 points: circle, from 5 points: ellipse) { EM_CIRCLE, N_("Force circle"), "circle" }, - { EM_ISONOMETRIC_CIRCLE, N_("Isometric circle"), "iso_circle" } + { EM_ISOMETRIC_CIRCLE, N_("Isometric circle"), "iso_circle" } }; static const Util::EnumDataConverter EMConverter(EllipseMethodData, EM_END); @@ -106,7 +103,7 @@ calc_delta_angle(const double a0, const double a1) } int -unit_arc_path(Geom::Path &path, Geom::Affine &affine, +unit_arc_path(Geom::Path &path_in, Geom::Affine &affine, double start=0.0, double end=2*M_PI, // angles bool slice=false) { @@ -130,57 +127,52 @@ unit_arc_path(Geom::Path &path, Geom::Affine &affine, nda=4; } - start = range2pi(start); - end=start+arc_angle; + double s = range2pi(start); + end = s+arc_angle; - // adopted from: sp-ellipse.cpp - SPCurve * curve=new SPCurve(); - // start point - curve->moveto(cos(start), sin(start)); - double s = start; - for (int i=0; i < nda; s = (++i)*da+start) { + double x0 = cos(s); + double y0 = sin(s); + // construct the path + Geom::Path path(Geom::Point(x0, y0)); + path.setStitching(true); + for (int i=0; i < nda; ) { double e = s + da; if (e > end) e = end; const double len = 4*tan((e - s)/4)/3; - const double x0 = cos(s); - const double y0 = sin(s); const double x1 = x0 + len * cos(s + M_PI_2); const double y1 = y0 + len * sin(s + M_PI_2); const double x3 = cos(e); const double y3 = sin(e); const double x2 = x3 + len * cos(e - M_PI_2); const double y2 = y3 + len * sin(e - M_PI_2); - curve->curveto(x1,y1, x2,y2, x3,y3); + path.appendNew( Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3) ); + s = (++i)*da+start; + x0 = cos(s); + y0 = sin(s); } if (slice && !closed) { - curve->lineto(0., 0.); + path.appendNew( Geom::Point(0.0, 0.0) ); } - curve->transform(affine); + path *= affine; - path.append(*curve->first_path()); + path_in.append(path); if ((slice && !closed) || closed) { - path.close(true); + path_in.close(true); } - // give to GC - curve->unref(); return 0; } void gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine) { - Geom::Path rect; - SPCurve curve; - // unit rectangle - curve.moveto(-1, -1); - curve.lineto(1, -1); - curve.lineto(1, 1); - curve.lineto(-1, 1); - //curve.transform(Rotate(-rot_angle)*affine); - curve.transform(affine); - rect.append(*curve.first_path()); + Geom::Path rect(Geom::Point(-1, -1)); + rect.setStitching(true); + rect.appendNew( Geom::Point(+1, -1) ); + rect.appendNew( Geom::Point(+1, +1) ); + rect.appendNew( Geom::Point(-1, +1) ); + rect *= affine; rect.close(true); path_out.push_back(rect); } @@ -188,8 +180,8 @@ gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine) void gen_axes_paths(Geom::PathVector &path_out, const Geom::Affine &affine) { - LineSegment clx(Point(-1,0),Point(1,0)); - LineSegment cly(Point(0,-1),Point(0,1)); + Geom::LineSegment clx(Geom::Point(-1,0),Geom::Point(1,0)); + Geom::LineSegment cly(Geom::Point(0,-1),Geom::Point(0,1)); Geom::Path plx, ply; plx.append(clx); @@ -207,9 +199,9 @@ is_ccw(const std::vector & pts) // method: sum up the angles between edges size_t n=pts.size(); // edges about vertex 0 - Point e0=pts.front()-pts.back(); - Point e1=pts[1]-pts[0]; - Coord sum=cross(e0,e1); + Geom::Point e0=pts.front()-pts.back(); + Geom::Point e1=pts[1]-pts[0]; + Geom::Coord sum=cross(e0,e1); // the rest for(size_t i=1;i & pts) } void -endpoints2angles(const bool ccw_wind, const bool use_other_arc, const Point &p0, const Point &p1, Coord &a0, Coord &a1) +endpoints2angles(const bool ccw_wind, const bool use_other_arc, const Geom::Point &p0, const Geom::Point &p1, Geom::Coord &a0, Geom::Coord &a1) { if(!p0.isZero() && !p1.isZero()) { a0=atan2(p0); @@ -251,7 +243,7 @@ endpoints2angles(const bool ccw_wind, const bool use_other_arc, const Point &p0, Geom::PathVector LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) { - PathVector path_out; + Geom::PathVector path_out; if(draw_ori_path.get_value()){ path_out.insert(path_out.end(),path_in.begin(),path_in.end()); @@ -260,8 +252,8 @@ LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) // from: extension/internal/odf.cpp // get all points - std::vector pts; - for(PathVector::const_iterator pit = path_in.begin(); pit!= path_in.end(); ++pit) { + std::vector pts; + for(Geom::PathVector::const_iterator pit = path_in.begin(); pit!= path_in.end(); ++pit) { // extract first point of this path pts.push_back(pit->initialPoint()); // iterate over all curves @@ -279,11 +271,11 @@ LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) // generate an ellipse residing inside the parallelogram. This effect is quite useful when // generating isometric views. Hence, the name. //if(gen_isometric.get_value()) - if(method == EM_ISONOMETRIC_CIRCLE) { - if(0!=genIsometricEllipse (pts, path_out)) + if (EM_ISOMETRIC_CIRCLE == method) { + if (0 != genIsometricEllipse(pts, path_out)) return path_in; } else { - if(0!=genFitEllipse(pts, path_out)) + if (0 != genFitEllipse(pts, path_out)) return path_in; } return path_out; @@ -301,38 +293,38 @@ LPEPts2Ellipse::genFitEllipse (std::vector const & pts, { // rotation angle based on user provided rot_axes to position the vertices const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation - Affine affine; - affine*=Rotate(rot_angle); - Coord a0=0; - Coord a1=2*M_PI; + Geom::Affine affine; + affine*=Geom::Rotate(rot_angle); + Geom::Coord a0=0; + Geom::Coord a1=2*M_PI; if(pts.size()<2) { return -1; } else if(pts.size()==2) { // simple line: circle in the middle of the line to the vertices - Point line=pts.front()-pts.back(); + Geom::Point line=pts.front()-pts.back(); double radius=line.length()*0.5; if(radius<1e-9) return -1; - Point center=middle_point(pts.front(),pts.back()); - Circle circle(center[0],center[1],radius); - affine*=Scale(circle.radius()); - affine*=Translate(circle.center()); + Geom::Point center=middle_point(pts.front(),pts.back()); + Geom::Circle circle(center[0],center[1],radius); + affine*=Geom::Scale(circle.radius()); + affine*=Geom::Translate(circle.center()); Geom::Path path; unit_arc_path(path,affine); path_out.push_back(path); } else if(pts.size()>=5 && EM_AUTO == method) { //!only_circle.get_value()) { // do ellipse try { - Ellipse ellipse; + Geom::Ellipse ellipse; ellipse.fit(pts); - affine*=Scale(ellipse.ray(X),ellipse.ray(Y)); - affine*=Rotate(ellipse.rotationAngle()); - affine*=Translate(ellipse.center()); + affine*=Geom::Scale(ellipse.ray(Geom::X),ellipse.ray(Geom::Y)); + affine*=Geom::Rotate(ellipse.rotationAngle()); + affine*=Geom::Translate(ellipse.center()); if(gen_arc.get_value()) { - Affine inv_affine=affine.inverse(); - Point p0=pts.front()*inv_affine; - Point p1=pts.back()*inv_affine; + Geom::Affine inv_affine=affine.inverse(); + Geom::Point p0=pts.front()*inv_affine; + Geom::Point p1=pts.back()*inv_affine; const bool ccw_wind=is_ccw(pts); endpoints2angles(ccw_wind,other_arc.get_value(),p0,p1,a0,a1); } @@ -350,15 +342,15 @@ LPEPts2Ellipse::genFitEllipse (std::vector const & pts, } else { // do a circle (3,4 points, or only_circle set) try { - Circle circle; + Geom::Circle circle; circle.fit(pts); - affine*=Scale(circle.radius()); - affine*=Translate(circle.center()); + affine*=Geom::Scale(circle.radius()); + affine*=Geom::Translate(circle.center()); if(gen_arc.get_value()) { - Point p0=pts.front()-circle.center(); - Point p1=pts.back()-circle.center(); + Geom::Point p0=pts.front()-circle.center(); + Geom::Point p1=pts.back()-circle.center(); const bool ccw_wind=is_ccw(pts); endpoints2angles(ccw_wind,other_arc.get_value(),p0,p1,a0,a1); } @@ -391,39 +383,39 @@ LPEPts2Ellipse::genIsometricEllipse (std::vector const & pts, // take the first 3 vertices for the edges if(pts.size() < 3) return -1; // calc edges - Point e0=pts[0]-pts[1]; - Point e1=pts[2]-pts[1]; + Geom::Point e0=pts[0]-pts[1]; + Geom::Point e1=pts[2]-pts[1]; - Coord ce=cross(e0,e1); + Geom::Coord ce=cross(e0,e1); // parallel or one is zero? if(fabs(ce)<1e-9) return -1; // unit vectors along edges - Point u0=unit_vector(e0); - Point u1=unit_vector(e1); + Geom::Point u0=unit_vector(e0); + Geom::Point u1=unit_vector(e1); // calc angles - Coord a0=atan2(e0); + Geom::Coord a0=atan2(e0); // Coord a1=M_PI_2-atan2(e1)-a0; - Coord a1=acos(dot(u0,u1))-M_PI_2; + Geom::Coord a1=acos(dot(u0,u1))-M_PI_2; // if(fabs(a1)<1e-9) return -1; if(ce<0) a1=-a1; // lengths: l0= length of edge 0; l1= height of parallelogram - Coord l0=e0.length()*0.5; - Point e0n=e1-dot(u0,e1)*u0; - Coord l1=e0n.length()*0.5; + Geom::Coord l0=e0.length()*0.5; + Geom::Point e0n=e1-dot(u0,e1)*u0; + Geom::Coord l1=e0n.length()*0.5; // center of the ellipse - Point pos=pts[1]+0.5*(e0+e1); + Geom::Point pos=pts[1]+0.5*(e0+e1); // rotation angle based on user provided rot_axes to position the vertices const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation // build up the affine transformation - Affine affine; - affine*=Rotate(rot_angle); - affine*=Scale(l0,l1); - affine*=HShear(-tan(a1)); - affine*=Rotate(a0); - affine*=Translate(pos); + Geom::Affine affine; + affine*=Geom::Rotate(rot_angle); + affine*=Geom::Scale(l0,l1); + affine*=Geom::HShear(-tan(a1)); + affine*=Geom::Rotate(a0); + affine*=Geom::Translate(pos); Geom::Path path; unit_arc_path(path,affine); diff --git a/src/live_effects/lpe-pts2ellipse.h b/src/live_effects/lpe-pts2ellipse.h index 1e04159d62..ebf40dec50 100644 --- a/src/live_effects/lpe-pts2ellipse.h +++ b/src/live_effects/lpe-pts2ellipse.h @@ -26,7 +26,7 @@ namespace LivePathEffect { enum EllipseMethod { EM_AUTO, EM_CIRCLE, - EM_ISONOMETRIC_CIRCLE, + EM_ISOMETRIC_CIRCLE, EM_END }; -- GitLab From 20fd80318772abdff5c31ff5295bcde29ddc4074 Mon Sep 17 00:00:00 2001 From: schwieni Date: Sun, 1 Apr 2018 21:01:26 +0200 Subject: [PATCH 5/7] added me to the AUTHORS file --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 2aab0faf05..bb3fd93958 100644 --- a/AUTHORS +++ b/AUTHORS @@ -142,6 +142,7 @@ Felipe Corrêa da Silva Sanches Christian Schaller Marco Scholten Tom von Schwerdtner +Markus Schwienbacher Danilo Šegan Abhishek Sharma Tim Sheridan -- GitLab From fc50eeffcbe033bbba46fed030fbcf5de141ab27 Mon Sep 17 00:00:00 2001 From: schwieni Date: Mon, 2 Apr 2018 09:19:12 +0200 Subject: [PATCH 6/7] run astyle for pts2ellipse.h and cpp --- src/live_effects/lpe-pts2ellipse.cpp | 299 ++++++++++++++------------- src/live_effects/lpe-pts2ellipse.h | 18 +- 2 files changed, 165 insertions(+), 152 deletions(-) diff --git a/src/live_effects/lpe-pts2ellipse.cpp b/src/live_effects/lpe-pts2ellipse.cpp index 4921e08ca2..66e823772a 100644 --- a/src/live_effects/lpe-pts2ellipse.cpp +++ b/src/live_effects/lpe-pts2ellipse.cpp @@ -59,10 +59,10 @@ LPEPts2Ellipse::LPEPts2Ellipse(LivePathEffectObject *lpeobject) : registerParameter(&rot_axes); registerParameter(&draw_ori_path); - rot_axes.param_set_range(-360,360); - rot_axes.param_set_increments(1,10); + rot_axes.param_set_range(-360, 360); + rot_axes.param_set_increments(1, 10); - show_orig_path=true; + show_orig_path = true; } LPEPts2Ellipse::~LPEPts2Ellipse() @@ -73,21 +73,23 @@ LPEPts2Ellipse::~LPEPts2Ellipse() inline double range2pi(double a) { - a = fmod(a, 2*M_PI); - if(a<0) a+=2*M_PI; + a = fmod(a, 2 * M_PI); + if (a < 0) { + a += 2 * M_PI; + } return a; } inline double deg2rad(double a) { - return a*M_PI/180.0; + return a * M_PI / 180.0; } inline double rad2deg(double a) { - return a*180.0/M_PI; + return a * 180.0 / M_PI; } // helper function, calculates the angle between a0 and a1 in ccw sense @@ -96,64 +98,66 @@ rad2deg(double a) inline double calc_delta_angle(const double a0, const double a1) { - double da=range2pi(a1-a0); - if((fabs(da)<1e-9) && (a0 end) + if (e > end) { e = end; - const double len = 4*tan((e - s)/4)/3; + } + const double len = 4 * tan((e - s) / 4) / 3; const double x1 = x0 + len * cos(s + M_PI_2); const double y1 = y0 + len * sin(s + M_PI_2); const double x3 = cos(e); const double y3 = sin(e); const double x2 = x3 + len * cos(e - M_PI_2); const double y2 = y3 + len * sin(e - M_PI_2); - path.appendNew( Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3) ); - s = (++i)*da+start; + path.appendNew(Geom::Point(x1, y1), Geom::Point(x2, y2), Geom::Point(x3, y3)); + s = (++i) * da + start; x0 = cos(s); y0 = sin(s); } if (slice && !closed) { - path.appendNew( Geom::Point(0.0, 0.0) ); + path.appendNew(Geom::Point(0.0, 0.0)); } path *= affine; @@ -169,9 +173,9 @@ gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine) { Geom::Path rect(Geom::Point(-1, -1)); rect.setStitching(true); - rect.appendNew( Geom::Point(+1, -1) ); - rect.appendNew( Geom::Point(+1, +1) ); - rect.appendNew( Geom::Point(-1, +1) ); + rect.appendNew(Geom::Point(+1, -1)); + rect.appendNew(Geom::Point(+1, +1)); + rect.appendNew(Geom::Point(-1, +1)); rect *= affine; rect.close(true); path_out.push_back(rect); @@ -180,57 +184,58 @@ gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine) void gen_axes_paths(Geom::PathVector &path_out, const Geom::Affine &affine) { - Geom::LineSegment clx(Geom::Point(-1,0),Geom::Point(1,0)); - Geom::LineSegment cly(Geom::Point(0,-1),Geom::Point(0,1)); + Geom::LineSegment clx(Geom::Point(-1, 0), Geom::Point(1, 0)); + Geom::LineSegment cly(Geom::Point(0, -1), Geom::Point(0, 1)); Geom::Path plx, ply; plx.append(clx); ply.append(cly); - plx*=affine; - ply*=affine; + plx *= affine; + ply *= affine; path_out.push_back(plx); path_out.push_back(ply); } bool -is_ccw(const std::vector & pts) +is_ccw(const std::vector &pts) { // method: sum up the angles between edges - size_t n=pts.size(); + size_t n = pts.size(); // edges about vertex 0 - Geom::Point e0=pts.front()-pts.back(); - Geom::Point e1=pts[1]-pts[0]; - Geom::Coord sum=cross(e0,e1); + Geom::Point e0(pts.front() - pts.back()); + Geom::Point e1(pts[1] - pts[0]); + Geom::Coord sum = cross(e0, e1); // the rest - for(size_t i=1;i pts; - for(Geom::PathVector::const_iterator pit = path_in.begin(); pit!= path_in.end(); ++pit) { + for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) { // extract first point of this path pts.push_back(pit->initialPoint()); // iterate over all curves @@ -263,7 +268,7 @@ LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) } // avoid identical start-point and end-point - if(pts.front() == pts.back()) { + if (pts.front() == pts.back()) { pts.pop_back(); } @@ -272,11 +277,13 @@ LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) // generating isometric views. Hence, the name. //if(gen_isometric.get_value()) if (EM_ISOMETRIC_CIRCLE == method) { - if (0 != genIsometricEllipse(pts, path_out)) + if (0 != genIsometricEllipse(pts, path_out)) { return path_in; + } } else { - if (0 != genFitEllipse(pts, path_out)) + if (0 != genFitEllipse(pts, path_out)) { return path_in; + } } return path_out; } @@ -288,55 +295,56 @@ LPEPts2Ellipse::doEffect_path (Geom::PathVector const & path_in) * ellipse. With 5 points each point is on the ellipse. For less points we get a circle. */ int -LPEPts2Ellipse::genFitEllipse (std::vector const & pts, - Geom::PathVector & path_out) +LPEPts2Ellipse::genFitEllipse(std::vector const &pts, + Geom::PathVector &path_out) { // rotation angle based on user provided rot_axes to position the vertices const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation Geom::Affine affine; - affine*=Geom::Rotate(rot_angle); - Geom::Coord a0=0; - Geom::Coord a1=2*M_PI; - - if(pts.size()<2) { + affine *= Geom::Rotate(rot_angle); + Geom::Coord a0 = 0; + Geom::Coord a1 = 2 * M_PI; + + if (pts.size() < 2) { return -1; - } else if(pts.size()==2) { + } else if (pts.size() == 2) { // simple line: circle in the middle of the line to the vertices - Geom::Point line=pts.front()-pts.back(); - double radius=line.length()*0.5; - if(radius<1e-9) + Geom::Point line = pts.front() - pts.back(); + double radius = line.length() * 0.5; + if (radius < 1e-9) { return -1; - Geom::Point center=middle_point(pts.front(),pts.back()); - Geom::Circle circle(center[0],center[1],radius); - affine*=Geom::Scale(circle.radius()); - affine*=Geom::Translate(circle.center()); + } + Geom::Point center = middle_point(pts.front(), pts.back()); + Geom::Circle circle(center[0], center[1], radius); + affine *= Geom::Scale(circle.radius()); + affine *= Geom::Translate(circle.center()); Geom::Path path; - unit_arc_path(path,affine); + unit_arc_path(path, affine); path_out.push_back(path); - } else if(pts.size()>=5 && EM_AUTO == method) { //!only_circle.get_value()) { + } else if (pts.size() >= 5 && EM_AUTO == method) { //!only_circle.get_value()) { // do ellipse try { Geom::Ellipse ellipse; ellipse.fit(pts); - affine*=Geom::Scale(ellipse.ray(Geom::X),ellipse.ray(Geom::Y)); - affine*=Geom::Rotate(ellipse.rotationAngle()); - affine*=Geom::Translate(ellipse.center()); - if(gen_arc.get_value()) { - Geom::Affine inv_affine=affine.inverse(); - Geom::Point p0=pts.front()*inv_affine; - Geom::Point p1=pts.back()*inv_affine; - const bool ccw_wind=is_ccw(pts); - endpoints2angles(ccw_wind,other_arc.get_value(),p0,p1,a0,a1); + affine *= Geom::Scale(ellipse.ray(Geom::X), ellipse.ray(Geom::Y)); + affine *= Geom::Rotate(ellipse.rotationAngle()); + affine *= Geom::Translate(ellipse.center()); + if (gen_arc.get_value()) { + Geom::Affine inv_affine = affine.inverse(); + Geom::Point p0 = pts.front() * inv_affine; + Geom::Point p1 = pts.back() * inv_affine; + const bool ccw_wind = is_ccw(pts); + endpoints2angles(ccw_wind, other_arc.get_value(), p0, p1, a0, a1); } Geom::Path path; - unit_arc_path(path,affine,a0,a1,slice_arc.get_value()); + unit_arc_path(path, affine, a0, a1, slice_arc.get_value()); path_out.push_back(path); - if(draw_axes.get_value()) { - gen_axes_paths(path_out,affine); + if (draw_axes.get_value()) { + gen_axes_paths(path_out, affine); } - } catch(...) { + } catch (...) { return -1; } } else { @@ -344,91 +352,96 @@ LPEPts2Ellipse::genFitEllipse (std::vector const & pts, try { Geom::Circle circle; circle.fit(pts); - affine*=Geom::Scale(circle.radius()); - affine*=Geom::Translate(circle.center()); - - if(gen_arc.get_value()) - { - Geom::Point p0=pts.front()-circle.center(); - Geom::Point p1=pts.back()-circle.center(); - const bool ccw_wind=is_ccw(pts); - endpoints2angles(ccw_wind,other_arc.get_value(),p0,p1,a0,a1); + affine *= Geom::Scale(circle.radius()); + affine *= Geom::Translate(circle.center()); + + if (gen_arc.get_value()) { + Geom::Point p0 = pts.front() - circle.center(); + Geom::Point p1 = pts.back() - circle.center(); + const bool ccw_wind = is_ccw(pts); + endpoints2angles(ccw_wind, other_arc.get_value(), p0, p1, a0, a1); } Geom::Path path; - unit_arc_path(path,affine,a0,a1,slice_arc.get_value()); + unit_arc_path(path, affine, a0, a1, slice_arc.get_value()); path_out.push_back(path); - } catch(...) { + } catch (...) { return -1; } } // draw frame? - if(gen_isometric_frame.get_value()) { - gen_iso_frame_paths(path_out,affine); + if (gen_isometric_frame.get_value()) { + gen_iso_frame_paths(path_out, affine); } // draw axes? - if(draw_axes.get_value()) { - gen_axes_paths(path_out,affine); + if (draw_axes.get_value()) { + gen_axes_paths(path_out, affine); } return 0; } int -LPEPts2Ellipse::genIsometricEllipse (std::vector const & pts, - Geom::PathVector & path_out) +LPEPts2Ellipse::genIsometricEllipse(std::vector const &pts, + Geom::PathVector &path_out) { // take the first 3 vertices for the edges - if(pts.size() < 3) return -1; + if (pts.size() < 3) { + return -1; + } // calc edges - Geom::Point e0=pts[0]-pts[1]; - Geom::Point e1=pts[2]-pts[1]; + Geom::Point e0 = pts[0] - pts[1]; + Geom::Point e1 = pts[2] - pts[1]; - Geom::Coord ce=cross(e0,e1); + Geom::Coord ce = cross(e0, e1); // parallel or one is zero? - if(fabs(ce)<1e-9) return -1; + if (fabs(ce) < 1e-9) { + return -1; + } // unit vectors along edges - Geom::Point u0=unit_vector(e0); - Geom::Point u1=unit_vector(e1); + Geom::Point u0 = unit_vector(e0); + Geom::Point u1 = unit_vector(e1); // calc angles - Geom::Coord a0=atan2(e0); + Geom::Coord a0 = atan2(e0); // Coord a1=M_PI_2-atan2(e1)-a0; - Geom::Coord a1=acos(dot(u0,u1))-M_PI_2; + Geom::Coord a1 = acos(dot(u0, u1)) - M_PI_2; // if(fabs(a1)<1e-9) return -1; - if(ce<0) a1=-a1; + if (ce < 0) { + a1 = -a1; + } // lengths: l0= length of edge 0; l1= height of parallelogram - Geom::Coord l0=e0.length()*0.5; - Geom::Point e0n=e1-dot(u0,e1)*u0; - Geom::Coord l1=e0n.length()*0.5; + Geom::Coord l0 = e0.length() * 0.5; + Geom::Point e0n = e1 - dot(u0, e1) * u0; + Geom::Coord l1 = e0n.length() * 0.5; // center of the ellipse - Geom::Point pos=pts[1]+0.5*(e0+e1); + Geom::Point pos = pts[1] + 0.5 * (e0 + e1); // rotation angle based on user provided rot_axes to position the vertices const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation // build up the affine transformation Geom::Affine affine; - affine*=Geom::Rotate(rot_angle); - affine*=Geom::Scale(l0,l1); - affine*=Geom::HShear(-tan(a1)); - affine*=Geom::Rotate(a0); - affine*=Geom::Translate(pos); + affine *= Geom::Rotate(rot_angle); + affine *= Geom::Scale(l0, l1); + affine *= Geom::HShear(-tan(a1)); + affine *= Geom::Rotate(a0); + affine *= Geom::Translate(pos); Geom::Path path; - unit_arc_path(path,affine); + unit_arc_path(path, affine); path_out.push_back(path); // draw frame? - if(gen_isometric_frame.get_value()) { - gen_iso_frame_paths(path_out,affine); + if (gen_isometric_frame.get_value()) { + gen_iso_frame_paths(path_out, affine); } // draw axes? - if(draw_axes.get_value()) { - gen_axes_paths(path_out,affine); + if (draw_axes.get_value()) { + gen_axes_paths(path_out, affine); } return 0; @@ -439,13 +452,13 @@ LPEPts2Ellipse::genIsometricEllipse (std::vector const & pts, } //namespace LivePathEffect } /* namespace Inkscape */ - /* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: - */ - // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-pts2ellipse.h b/src/live_effects/lpe-pts2ellipse.h index ebf40dec50..b4a6c55dd1 100644 --- a/src/live_effects/lpe-pts2ellipse.h +++ b/src/live_effects/lpe-pts2ellipse.h @@ -32,21 +32,21 @@ enum EllipseMethod { class LPEPts2Ellipse : public Effect { public: - LPEPts2Ellipse(LivePathEffectObject *lpeobject); - virtual ~LPEPts2Ellipse(); + explicit LPEPts2Ellipse(LivePathEffectObject *lpeobject); + ~LPEPts2Ellipse() override; - virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); + Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override; private: - LPEPts2Ellipse(const LPEPts2Ellipse&); - LPEPts2Ellipse& operator=(const LPEPts2Ellipse&); + LPEPts2Ellipse(const LPEPts2Ellipse &); + LPEPts2Ellipse &operator=(const LPEPts2Ellipse &); - int genIsometricEllipse (std::vector const & points_in, - Geom::PathVector & path_out); + int genIsometricEllipse(std::vector const &points_in, + Geom::PathVector &path_out); - int genFitEllipse (std::vector const & points_in, - Geom::PathVector & path_out); + int genFitEllipse(std::vector const &points_in, + Geom::PathVector &path_out); EnumParam method; BoolParam gen_isometric_frame; -- GitLab From 8711f4bbd8e92ca066cabd359f0698727023fc32 Mon Sep 17 00:00:00 2001 From: schwieni Date: Sun, 10 Mar 2019 17:33:36 +0100 Subject: [PATCH 7/7] Added Steiner ellipse and inellipse generation from 3 points Added Steiner ellipse and inellipse generation from three points. Did some minor cleanup too. --- src/live_effects/lpe-pts2ellipse.cpp | 126 ++++++++++++++++++++++++--- src/live_effects/lpe-pts2ellipse.h | 10 ++- 2 files changed, 120 insertions(+), 16 deletions(-) diff --git a/src/live_effects/lpe-pts2ellipse.cpp b/src/live_effects/lpe-pts2ellipse.cpp index 66e823772a..f9e23d68a3 100644 --- a/src/live_effects/lpe-pts2ellipse.cpp +++ b/src/live_effects/lpe-pts2ellipse.cpp @@ -32,8 +32,10 @@ namespace LivePathEffect { static const Util::EnumData EllipseMethodData[] = { { EM_AUTO, N_("Auto ellipse"), "auto" }, //!< (2..4 points: circle, from 5 points: ellipse) - { EM_CIRCLE, N_("Force circle"), "circle" }, - { EM_ISOMETRIC_CIRCLE, N_("Isometric circle"), "iso_circle" } + { EM_CIRCLE, N_("Force circle"), "circle" }, //!< always fit a circle + { EM_ISOMETRIC_CIRCLE, N_("Isometric circle"), "iso_circle" }, //!< use first two edges to generate a sheared ellipse + { EM_STEINER_ELLIPSE, N_("Steiner ellipse"), "steiner_ellipse" }, //!< generate a steiner ellipse from the first three points + { EM_STEINER_INELLIPSE, N_("Steiner inellipse"), "steiner_inellipse" } //!< generate a steiner inellipse from the first three points }; static const Util::EnumDataConverter EMConverter(EllipseMethodData, EM_END); @@ -41,11 +43,11 @@ LPEPts2Ellipse::LPEPts2Ellipse(LivePathEffectObject *lpeobject) : Effect(lpeobject), method(_("Method:"), _("Methods to generate the ellipse"), "method", EMConverter, &wr, this, EM_AUTO), - gen_isometric_frame(_("_Frame (isometric rectangle)"), _("Draw Parallelogram around the ellipse"), + gen_isometric_frame(_("_Frame (isometric rectangle)"), _("Draw parallelogram around the ellipse"), "gen_isometric_frame", &wr, this, false), gen_arc(_("_Arc"), _("Generate open arc (open ellipse)"), "gen_arc", &wr, this, false), - other_arc(_("_Other Arc side"), _("switch sides of the arc"), "arc_other", &wr, this, false), - slice_arc(_("_Slice Arc"), _("slice the arc"), "slice_arc", &wr, this, false), + other_arc(_("_Other Arc side"), _("Switch sides of the arc"), "arc_other", &wr, this, false), + slice_arc(_("_Slice Arc"), _("Slice the arc"), "slice_arc", &wr, this, false), draw_axes(_("A_xes"), _("Draw both semi-major and semi-minor axes"), "draw_axes", &wr, this, false), rot_axes(_("Axes Rotation"), _("Axes rotation angle [deg]"), "rot_axes", &wr, this, 0), draw_ori_path(_("Source _Path"), _("Show the original source path"), "draw_ori_path", &wr, this, false) @@ -275,15 +277,23 @@ LPEPts2Ellipse::doEffect_path(Geom::PathVector const &path_in) // special mode: Use first two edges, interpret them as two sides of a parallelogram and // generate an ellipse residing inside the parallelogram. This effect is quite useful when // generating isometric views. Hence, the name. - //if(gen_isometric.get_value()) - if (EM_ISOMETRIC_CIRCLE == method) { - if (0 != genIsometricEllipse(pts, path_out)) { - return path_in; - } - } else { - if (0 != genFitEllipse(pts, path_out)) { - return path_in; - } + switch(method) { + case EM_ISOMETRIC_CIRCLE: + if (0 != genIsometricEllipse(pts, path_out)) { + return path_in; + } break; + case EM_STEINER_ELLIPSE: + if (0 != genSteinerEllipse(pts, false, path_out)) { + return path_in; + } break; + case EM_STEINER_INELLIPSE: + if (0 != genSteinerEllipse(pts, true, path_out)) { + return path_in; + } break; + default: + if (0 != genFitEllipse(pts, path_out)) { + return path_in; + } } return path_out; } @@ -447,6 +457,94 @@ LPEPts2Ellipse::genIsometricEllipse(std::vector const &pts, return 0; } +void +evalSteinerEllipse(Geom::Point const &pCenter, + Geom::Point const &pCenter_Pt2, + Geom::Point const &pPt0_Pt1, + const double &angle, + Geom::Point &pRes) +{ + // formula for the evaluation of points on the steiner ellipse using parameter angle + pRes = pCenter + + pCenter_Pt2*cos(angle) + + pPt0_Pt1*sin(angle)/sqrt(3); +} + +int +LPEPts2Ellipse::genSteinerEllipse(std::vector const &pts, + bool gen_inellipse, + Geom::PathVector &path_out) +{ + // take the first 3 vertices for the edges + if (pts.size() < 3) { + return -1; + } + // calc center + Geom::Point pCenter = (pts[0]+pts[1]+pts[2])/3; + // calc main directions of affine triangle + Geom::Point f1 = pts[2]-pCenter; + Geom::Point f2 = (pts[1]-pts[0])/sqrt(3); + + // calc zero angle t0 + const double denominator = dot(f1, f1) - dot(f2, f2); + double t0=0; + if(fabs(denominator) > 1e-12) { + const double cot2t0 = 2.0 * dot(f1, f2) / denominator; + t0 = atan(cot2t0)/2.0; + } + + // calc relative points of main axes (for axis directions) + Geom::Point p0(0,0), pRel0, pRel1; + evalSteinerEllipse(p0, pts[2]-pCenter, pts[1]-pts[0], t0, pRel0); + evalSteinerEllipse(p0, pts[2]-pCenter, pts[1]-pts[0], t0+M_PI_2, pRel1); + Geom::Coord l0 = pRel0.length(); + Geom::Coord l1 = pRel1.length(); + + // basic rotation + double a0 = atan2(pRel0); + + bool swapped=false; + + if (l1 > l0) { + std::swap(l0,l1); + a0 += M_PI_2; + swapped = true; + } + + // the steiner inellipse is just scaled down by 2 + if(gen_inellipse) { + l0/=2; + l1/=2; + } + + // rotation angle based on user provided rot_axes to position the vertices + const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation + + // build up the affine transformation + Geom::Affine affine; + affine *= Geom::Rotate(rot_angle); + affine *= Geom::Scale(l0, l1); + affine *= Geom::Rotate(a0); + affine *= Geom::Translate(pCenter); + + Geom::Path path; + unit_arc_path(path, affine); + path_out.push_back(path); + + // draw frame? + if (gen_isometric_frame.get_value()) { + gen_iso_frame_paths(path_out, affine); + } + + // draw axes? + if (draw_axes.get_value()) { + gen_axes_paths(path_out, affine); + } + + return 0; +} + + /* ######################## */ } //namespace LivePathEffect diff --git a/src/live_effects/lpe-pts2ellipse.h b/src/live_effects/lpe-pts2ellipse.h index b4a6c55dd1..1a48448934 100644 --- a/src/live_effects/lpe-pts2ellipse.h +++ b/src/live_effects/lpe-pts2ellipse.h @@ -27,6 +27,8 @@ enum EllipseMethod { EM_AUTO, EM_CIRCLE, EM_ISOMETRIC_CIRCLE, + EM_STEINER_ELLIPSE, + EM_STEINER_INELLIPSE, EM_END }; @@ -45,8 +47,12 @@ private: int genIsometricEllipse(std::vector const &points_in, Geom::PathVector &path_out); - int genFitEllipse(std::vector const &points_in, - Geom::PathVector &path_out); + int genFitEllipse(std::vector const &points_in, + Geom::PathVector &path_out); + + int genSteinerEllipse(std::vector const &points_in, + bool gen_inellipse, + Geom::PathVector &path_out); EnumParam method; BoolParam gen_isometric_frame; -- GitLab