/* * Copyright (C) Jabiertxo Arraiza Cenoz * Special thanks to Johan Engelen for the base of the effect -powerstroke- * Also to ScislaC for point me to the idea * Also su_v for his construvtive feedback and time * and finaly to Liam P. White for his big help on coding, that save me a lot of * hours * Released under GNU GPL, read the file 'COPYING' for more information */ #include <2geom/piecewise.h> #include <2geom/sbasis-to-bezier.h> #include <2geom/sbasis-geometric.h> #include <2geom/svg-elliptical-arc.h> #include <2geom/line.h> #include <2geom/path-intersection.h> #include "ui/dialog/lpe-fillet-chamfer-properties.h" #include "live_effects/parameter/filletchamferpointarray.h" #include "live_effects/effect.h" #include "svg/svg.h" #include "svg/stringstream.h" #include "knotholder.h" #include "sp-lpe-item.h" #include "selection.h" // needed for on-canvas editting: #include "desktop.h" #include "live_effects/lpeobject.h" #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" #include "ui/tools/node-tool.h" // TODO due to internal breakage in glibmm headers, // this has to be included last. #include using namespace Geom; namespace Inkscape { namespace LivePathEffect { FilletChamferPointArrayParam::FilletChamferPointArrayParam( const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key, Inkscape::UI::Widget::Registry *wr, Effect *effect) : ArrayParam(label, tip, key, wr, effect, 0) { knot_shape = SP_KNOT_SHAPE_DIAMOND; knot_mode = SP_KNOT_MODE_XOR; knot_color = 0x00ff0000; } FilletChamferPointArrayParam::~FilletChamferPointArrayParam() {} Gtk::Widget *FilletChamferPointArrayParam::param_newWidget() { return NULL; /* Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg = Gtk::manage( new Inkscape::UI::Widget::RegisteredTransformedPoint( param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc() ) ); // TODO: fix to get correct desktop (don't use SP_ACTIVE_DESKTOP) SPDesktop *desktop = SP_ACTIVE_DESKTOP; Affine transf = desktop->doc2dt(); pointwdg->setTransform(transf); pointwdg->setValue( *this ); pointwdg->clearProgrammatically(); pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter")); Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() ); static_cast(hbox)->pack_start(*pointwdg, true, true); static_cast(hbox)->show_all_children(); return dynamic_cast (hbox); */ } void FilletChamferPointArrayParam::param_transform_multiply(Affine const &postmul, bool /*set*/) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (prefs->getBool("/options/transform/rectcorners", true) && _vector[1][X] <= 0) { std::vector result; for (std::vector::const_iterator point_it = _vector.begin(); point_it != _vector.end(); ++point_it) { Coord A = (*point_it)[X] * ((postmul.expansionX() + postmul.expansionY()) / 2); result.push_back(Point(A, (*point_it)[Y])); } param_set_and_write_new_value(result); } // param_set_and_write_new_value( (*this) * postmul ); } /** call this method to recalculate the controlpoints such that they stay at the * same location relative to the new path. Useful after adding/deleting nodes to * the path.*/ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2( Piecewise > const &pwd2_in) { if (!last_pwd2.empty()) { PathVector const pathv = path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001); PathVector last_pathv = path_from_piecewise(remove_short_cuts(last_pwd2, 0.1), 0.001); std::vector result; unsigned long counter = 0; unsigned long counterPaths = 0; unsigned long counterCurves = 0; long offset = 0; long offsetPaths = 0; Geom::NodeType nodetype; for (PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { if (path_it->empty()) { counterPaths++; counter++; continue; } Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); Geom::Path::const_iterator curve_endit = path_it->end_default(); if (path_it->closed() && path_it->back_closed().isDegenerate()) { const Curve &closingline = path_it->back_closed(); if (are_near(closingline.initialPoint(), closingline.finalPoint())) { curve_endit = path_it->end_open(); } } counterCurves = 0; while (curve_it1 != curve_endit) { //if start a path get node type if (counterCurves == 0) { if (path_it->closed()) { if (path_it->back_closed().isDegenerate()) { nodetype = get_nodetype(path_it->back_open(), *curve_it1); } else { nodetype = get_nodetype(path_it->back_closed(), *curve_it1); } } else { nodetype = NODE_NONE; } } else { //check node type also whith straight lines because get_nodetype //return non cusp node in a node inserted inside a straight line //todo: if the path remove some nodes whith the result of a straight //line but with handles, the node inserted into dont fire the knot // because is not handle as cusp node by get_nodetype function bool this_is_line = true; bool next_is_line = is_straight_curve(*curve_it1); this_is_line = is_straight_curve((*path_it)[counterCurves - 1]); nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1); if (this_is_line || next_is_line) { nodetype = NODE_CUSP; } } if (last_pathv.size() > pathv.size() || (last_pathv.size() > counterPaths && last_pathv[counterPaths].size() > counter - offset && !are_near(curve_it1->initialPoint(), last_pathv[counterPaths][counter - offset].initialPoint(), 0.1))) { if ( curve_it2 == curve_endit) { if (last_pathv[counterPaths].size() != pathv[counterPaths].size()) { offset = (last_pathv[counterPaths].size() - pathv[counterPaths].size()) * -1; } else { offset = 0; } offsetPaths += offset; offset = offsetPaths; } else if (counterCurves == 0 && last_pathv.size() <= pathv.size() && counter - offset <= last_pathv[counterPaths].size() && are_near(curve_it1->initialPoint(), last_pathv[counterPaths].finalPoint(), 0.1) && !last_pathv[counterPaths].closed()) { long e = counter - offset + 1; std::vector tmp = _vector; for (unsigned long i = last_pathv[counterPaths].size() + counter - offset; i > counterCurves - offset + 1; i--) { if (tmp[i - 1][X] > 0) { double fractpart, intpart; fractpart = modf(tmp[i - 1][X], &intpart); _vector[e] = Point(e + fractpart, tmp[i - 1][Y]); } else { _vector[e] = Point(tmp[i - 1][X], tmp[i - 1][Y]); } e++; } //delete temp vector std::vector().swap(tmp); if (last_pathv.size() > counterPaths) { last_pathv[counterPaths] = last_pathv[counterPaths].reverse(); } } else { if (last_pathv.size() > counterPaths) { if (last_pathv[counterPaths].size() < pathv[counterPaths].size()) { offset++; } else if (last_pathv[counterPaths].size() > pathv[counterPaths].size()) { offset--; continue; } } else { offset++; } } double xPos = 0; if (_vector[1][X] > 0) { xPos = nearest_point(curve_it1->initialPoint(), pwd2_in); } if (nodetype == NODE_CUSP) { result.push_back(Point(xPos, 1)); } else { result.push_back(Point(xPos, 0)); } } else { double xPos = _vector[counter - offset][X]; if (_vector.size() <= (unsigned)(counter - offset)) { if (_vector[1][X] > 0) { xPos = nearest_point(curve_it1->initialPoint(), pwd2_in); } else { xPos = 0; } } if (nodetype == NODE_CUSP) { double vectorY = _vector[counter - offset][Y]; if (_vector.size() <= (unsigned)(counter - offset) || vectorY == 0) { vectorY = 1; } result.push_back(Point(xPos, vectorY)); } else { if (_vector[1][X] < 0) { xPos = 0; } result.push_back(Point(floor(xPos), 0)); } } ++curve_it1; if (curve_it2 != curve_endit) { ++curve_it2; } counter++; counterCurves++; } counterPaths++; } _vector = result; write_to_SVG(); } } void FilletChamferPointArrayParam::recalculate_knots( Piecewise > const &pwd2_in) { bool change = false; PathVector pathv = path_from_piecewise(pwd2_in, 0.001); if (!pathv.empty()) { std::vector result; int counter = 0; int counterCurves = 0; Geom::NodeType nodetype; for (PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { if (path_it->empty()) { counter++; continue; } Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); Geom::Path::const_iterator curve_endit = path_it->end_default(); if (path_it->closed() && path_it->back_closed().isDegenerate()) { const Curve &closingline = path_it->back_closed(); if (are_near(closingline.initialPoint(), closingline.finalPoint())) { curve_endit = path_it->end_open(); } } counterCurves = 0; while (curve_it1 != curve_endit) { //if start a path get node type if (counterCurves == 0) { if (path_it->closed()) { if (path_it->back_closed().isDegenerate()) { nodetype = get_nodetype(path_it->back_open(), *curve_it1); } else { nodetype = get_nodetype(path_it->back_closed(), *curve_it1); } } else { nodetype = NODE_NONE; } } else { bool this_is_line = true; bool next_is_line = is_straight_curve(*curve_it1); this_is_line = is_straight_curve((*path_it)[counterCurves - 1]); nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1); if (this_is_line || next_is_line) { nodetype = NODE_CUSP; } } if (nodetype == NODE_CUSP) { double vectorY = _vector[counter][Y]; if (vectorY == 0) { vectorY = 1; change = true; } result.push_back(Point(_vector[counter][X], vectorY)); } else { double xPos = floor(_vector[counter][X]); if (_vector[1][X] < 0) { xPos = 0; } double vectorY = _vector[counter][Y]; if (vectorY != 0) { change = true; } result.push_back(Point(xPos, 0)); } ++curve_it1; counter++; if (curve_it2 != curve_endit) { ++curve_it2; } counterCurves++; } } if (change) { _vector = result; write_to_SVG(); } } } void FilletChamferPointArrayParam::set_pwd2( Piecewise > const &pwd2_in, Piecewise > const &pwd2_normal_in) { last_pwd2 = pwd2_in; last_pwd2_normal = pwd2_normal_in; } void FilletChamferPointArrayParam::set_document_unit(Glib::ustring const * value_document_unit) { documentUnit = value_document_unit; } void FilletChamferPointArrayParam::set_helper_size(int hs) { helper_size = hs; } void FilletChamferPointArrayParam::set_chamfer_steps(int value_chamfer_steps) { chamfer_steps = value_chamfer_steps; } void FilletChamferPointArrayParam::set_use_distance(bool use_knot_distance ) { use_distance = use_knot_distance; } void FilletChamferPointArrayParam::set_unit(const gchar *abbr) { unit = abbr; } void FilletChamferPointArrayParam::updateCanvasIndicators() { std::vector ts = data(); hp.clear(); unsigned int i = 0; for (std::vector::const_iterator point_it = ts.begin(); point_it != ts.end(); ++point_it) { double Xvalue = to_time(i, (*point_it)[X]) -i; if (Xvalue == 0) { i++; continue; } Geom::Point ptA = last_pwd2[i].valueAt(Xvalue); Geom::Point derivA = unit_vector(derivative(last_pwd2[i]).valueAt(Xvalue)); Geom::Rotate rot(Geom::Rotate::from_degrees(-90)); derivA = derivA * rot; Geom::Point C = ptA - derivA * helper_size; Geom::Point D = ptA + derivA * helper_size; Geom::Ray ray1(C, D); char const * svgd = "M 1,0.25 0.5,0 1,-0.25 M 1,0.5 0,0 1,-0.5"; Geom::PathVector pathv = sp_svg_read_pathv(svgd); Geom::Affine aff = Geom::Affine(); aff *= Geom::Scale(helper_size); aff *= Geom::Rotate(ray1.angle() - deg_to_rad(270)); pathv *= aff; pathv += last_pwd2[i].valueAt(Xvalue); hp.push_back(pathv[0]); hp.push_back(pathv[1]); i++; } } void FilletChamferPointArrayParam::addCanvasIndicators( SPLPEItem const */*lpeitem*/, std::vector &hp_vec) { hp_vec.push_back(hp); } double FilletChamferPointArrayParam::rad_to_len(int index, double rad) { double len = 0; std::vector subpaths = path_from_piecewise(last_pwd2, 0.1); std::pair positions = get_positions(index, subpaths); D2 A = last_pwd2[last_index(index, subpaths)]; if(positions.second != 0){ A = last_pwd2[index-1]; }else{ if(!subpaths[positions.first].closed()){ return len; } } D2 B = last_pwd2[index]; Piecewise > offset_curve0 = Piecewise >(A)+rot90(unitVector(derivative(A)))*(rad); Piecewise > offset_curve1 = Piecewise >(B)+rot90(unitVector(derivative(B)))*(rad); Geom::Path p0 = path_from_piecewise(offset_curve0, 0.1)[0]; Geom::Path p1 = path_from_piecewise(offset_curve1, 0.1)[0]; Geom::Crossings cs = Geom::crossings(p0, p1); if(cs.size() > 0){ Point cp =p0(cs[0].ta); double p0pt = nearest_point(cp, B); len = time_to_len(index,p0pt); } else { if(rad < 0){ len = rad_to_len(index, rad * -1); } } return len; } double FilletChamferPointArrayParam::len_to_rad(int index, double len) { double rad = 0; double tmp_len = _vector[index][X]; _vector[index] = Geom::Point(len,_vector[index][Y]); std::vector subpaths = path_from_piecewise(last_pwd2, 0.1); std::pair positions = get_positions(index, subpaths); Piecewise > u; u.push_cut(0); u.push(last_pwd2[last_index(index, subpaths)], 1); Geom::Curve * A = path_from_piecewise(u, 0.1)[0][0].duplicate(); Geom::Curve * B = subpaths[positions.first][positions.second].duplicate(); std::vector times; if(positions.second != 0){ A = subpaths[positions.first][positions.second-1].duplicate(); times = get_times(index-1, subpaths, false); }else{ if(!subpaths[positions.first].closed()){ return rad; } times = get_times(last_index(index, subpaths), subpaths, true); } _vector[index] = Geom::Point(tmp_len,_vector[index][Y]); Geom::Point startArcPoint = A->toSBasis().valueAt(times[1]); Geom::Point endArcPoint = B->toSBasis().valueAt(times[2]); Curve *knotCurve1 = A->portion(times[0], times[1]); Curve *knotCurve2 = B->portion(times[2], 1); Geom::CubicBezier const *cubic1 = dynamic_cast(&*knotCurve1); Ray ray1(startArcPoint, A->finalPoint()); if (cubic1) { ray1.setPoints((*cubic1)[2], startArcPoint); } Geom::CubicBezier const *cubic2 = dynamic_cast(&*knotCurve2); Ray ray2(B->initialPoint(), endArcPoint); if (cubic2) { ray2.setPoints(endArcPoint, (*cubic2)[1]); } bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0; double distanceArc = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint)); double angleBetween = angle_between(ray1, ray2, ccwToggle); rad = distanceArc/sin(angleBetween/2.0); return rad * -1; } std::vector FilletChamferPointArrayParam::get_times(int index, std::vector subpaths, bool last) { const double tolerance = 0.001; const double gapHelper = 0.00001; std::pair positions = get_positions(index, subpaths); Curve *curve_it1; curve_it1 = subpaths[positions.first][positions.second].duplicate(); Coord it1_length = (*curve_it1).length(tolerance); double time_it1, time_it2, time_it1_B, intpart; time_it1 = modf(to_time(index, _vector[index][X]), &intpart); if (_vector[index][Y] == 0) { time_it1 = 0; } double resultLenght = 0; time_it1_B = 1; if (subpaths[positions.first].closed() && last) { time_it2 = modf(to_time(index - positions.second , _vector[index - positions.second ][X]), &intpart); resultLenght = it1_length + to_len(index - positions.second, _vector[index - positions.second ][X]); } else if (!subpaths[positions.first].closed() && last){ time_it2 = 0; resultLenght = 0; } else { time_it2 = modf(to_time(index + 1, _vector[index + 1][X]), &intpart); resultLenght = it1_length + to_len( index + 1, _vector[index + 1][X]); } if (resultLenght > 0 && time_it2 != 0) { time_it1_B = modf(to_time(index, -resultLenght), &intpart); } else { if (time_it2 == 0) { time_it1_B = 1; } else { time_it1_B = gapHelper; } } if ((subpaths[positions.first].closed() && last && _vector[index - positions.second][Y] == 0) || (subpaths[positions.first].size() > positions.second + 1 && _vector[index + 1][Y] == 0)) { time_it1_B = 1; time_it2 = 0; } if (time_it1_B < time_it1) { time_it1_B = time_it1 + gapHelper; } std::vector out; out.push_back(time_it1); out.push_back(time_it1_B); out.push_back(time_it2); return out; } std::pair FilletChamferPointArrayParam::get_positions(int index, std::vector subpaths) { int counter = -1; std::size_t first = 0; std::size_t second = 0; for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) { if (path_it->empty()) continue; Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_endit = path_it->end_default(); if (path_it->closed()) { const Geom::Curve &closingline = path_it->back_closed(); // the closing line segment is always of type // Geom::LineSegment. if (are_near(closingline.initialPoint(), closingline.finalPoint())) { // closingline.isDegenerate() did not work, because it only checks for // *exact* zero length, which goes wrong for relative coordinates and // rounding errors... // the closing line segment has zero-length. So stop before that one! curve_endit = path_it->end_open(); } } first++; second = 0; while (curve_it1 != curve_endit) { counter++; second++; if(counter == index){ break; } ++curve_it1; } if(counter == index){ break; } } first--; second--; std::pair out(first, second); return out; } int FilletChamferPointArrayParam::last_index(int index, std::vector subpaths) { int counter = -1; bool inSubpath = false; for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) { if (path_it->empty()) continue; Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_endit = path_it->end_default(); if (path_it->closed()) { const Geom::Curve &closingline = path_it->back_closed(); if (are_near(closingline.initialPoint(), closingline.finalPoint())) { curve_endit = path_it->end_open(); } } while (curve_it1 != curve_endit) { counter++; if(counter == index){ inSubpath = true; } ++curve_it1; } if(inSubpath){ break; } } if(!inSubpath){ counter = -1; } return counter; } double FilletChamferPointArrayParam::len_to_time(int index, double len) { double t = 0; if (last_pwd2.size() > (unsigned) index) { if (len != 0) { if (last_pwd2[index][0].degreesOfFreedom() != 2) { Piecewise > u; u.push_cut(0); u.push(last_pwd2[index], 1); std::vector t_roots = roots(arcLengthSb(u) - std::abs(len)); if (t_roots.size() > 0) { t = t_roots[0]; } } else { double lenghtPart = 0; if (last_pwd2.size() > (unsigned) index) { lenghtPart = length(last_pwd2[index], EPSILON); } if (std::abs(len) < lenghtPart && lenghtPart != 0) { t = std::abs(len) / lenghtPart; } } } t = double(index) + t; } else { t = double(last_pwd2.size() - 1); } return t; } double FilletChamferPointArrayParam::time_to_len(int index, double time) { double intpart; double len = 0; time = modf(time, &intpart); double lenghtPart = 0; if (last_pwd2.size() <= (unsigned) index || time == 0) { return len; } if (last_pwd2[index][0].degreesOfFreedom() != 2) { Piecewise > u; u.push_cut(0); u.push(last_pwd2[index], 1); u = portion(u, 0, time); return length(u, 0.001) * -1; } lenghtPart = length(last_pwd2[index], EPSILON); return (time * lenghtPart) * -1; } double FilletChamferPointArrayParam::to_time(int index, double A) { if (A > 0) { return A; } else { return len_to_time(index, A); } } double FilletChamferPointArrayParam::to_len(int index, double A) { if (A > 0) { return time_to_len(index, A); } else { return A; } } void FilletChamferPointArrayParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color) { knot_shape = shape; knot_mode = mode; knot_color = color; } FilletChamferPointArrayParamKnotHolderEntity:: FilletChamferPointArrayParamKnotHolderEntity( FilletChamferPointArrayParam *p, unsigned int index) : _pparam(p), _index(index) {} void FilletChamferPointArrayParamKnotHolderEntity::knot_set(Point const &p, Point const &/*origin*/, guint state) { using namespace Geom; if (!valid_index(_index)) { return; } Piecewise > const &pwd2 = _pparam->get_pwd2(); double t = nearest_point(p, pwd2[_index]); Geom::Point const s = snap_knot_position(pwd2[_index].valueAt(t), state); t = nearest_point(s, pwd2[_index]); if (t == 1) { t = 0.9999; } t += _index; if (_pparam->_vector.at(_index)[X] <= 0) { _pparam->_vector.at(_index) = Point(_pparam->time_to_len(_index, t), _pparam->_vector.at(_index)[Y]); } else { _pparam->_vector.at(_index) = Point(t, _pparam->_vector.at(_index)[Y]); } sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); } Point FilletChamferPointArrayParamKnotHolderEntity::knot_get() const { using namespace Geom; if (!valid_index(_index)) { return Point(infinity(), infinity()); } Piecewise > const &pwd2 = _pparam->get_pwd2(); double time_it = _pparam->to_time(_index, _pparam->_vector.at(_index)[X]); Point canvas_point = pwd2.valueAt(time_it); _pparam->updateCanvasIndicators(); return canvas_point; } void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state) { if (state & GDK_CONTROL_MASK) { if (state & GDK_MOD1_MASK) { _pparam->_vector.at(_index) = Point(_index, _pparam->_vector.at(_index)[Y]); _pparam->param_set_and_write_new_value(_pparam->_vector); sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); }else{ using namespace Geom; int type = (int)_pparam->_vector.at(_index)[Y]; if (type >=3000 && type < 4000){ type = 3; } if (type >=4000 && type < 5000){ type = 4; } switch(type){ case 1: type = 2; break; case 2: type = _pparam->chamfer_steps + 3000; break; case 3: type = _pparam->chamfer_steps + 4000; break; default: type = 1; break; } _pparam->_vector.at(_index) = Point(_pparam->_vector.at(_index)[X], (double)type); _pparam->param_set_and_write_new_value(_pparam->_vector); sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); const gchar *tip; if (type >=3000 && type < 4000){ tip = _("Chamfer: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } else if (type >=4000 && type < 5000) { tip = _("Inverse Chamfer: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } else if (type == 2) { tip = _("Inverse Fillet: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } else { tip = _("Fillet: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } this->knot->tip = g_strdup(tip); this->knot->show(); } } else if (state & GDK_SHIFT_MASK) { double xModified = _pparam->_vector.at(_index).x(); if(xModified < 0 && !_pparam->use_distance){ xModified = _pparam->len_to_rad(_index, _pparam->_vector.at(_index).x()); } std::vector subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1); std::pair positions = _pparam->get_positions(_index, subpaths); D2 A = _pparam->last_pwd2[_pparam->last_index(_index, subpaths)]; if(positions.second != 0){ A = _pparam->last_pwd2[_index-1]; } D2 B = _pparam->last_pwd2[_index]; bool aprox = (A[0].degreesOfFreedom() != 2 || B[0].degreesOfFreedom() != 2) && !_pparam->use_distance?true:false; Geom::Point offset = Geom::Point(xModified, _pparam->_vector.at(_index).y()); Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog( this->desktop, offset, this, _pparam->unit, _pparam->use_distance, aprox, _pparam->documentUnit); } } void FilletChamferPointArrayParamKnotHolderEntity::knot_set_offset( Geom::Point offset) { double xModified = offset.x(); if(xModified < 0 && !_pparam->use_distance){ xModified = _pparam->rad_to_len(_index, offset.x()); } _pparam->_vector.at(_index) = Geom::Point(xModified, offset.y()); this->parent_holder->knot_ungrabbed_handler(this->knot, 0); } void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { recalculate_knots(get_pwd2()); for (unsigned int i = 0; i < _vector.size(); ++i) { if (_vector[i][Y] <= 0) { continue; } const gchar *tip; if (_vector[i][Y] >=3000 && _vector[i][Y] < 4000){ tip = _("Chamfer: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } else if (_vector[i][Y] >=4000 && _vector[i][Y] < 5000) { tip = _("Inverse Chamfer: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } else if (_vector[i][Y] == 2) { tip = _("Inverse Fillet: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } else { tip = _("Fillet: Ctrl+Click toggle type, " "Shift+Click open dialog, " "Ctrl+Alt+Click reset"); } FilletChamferPointArrayParamKnotHolderEntity *e = new FilletChamferPointArrayParamKnotHolderEntity(this, i); e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip), knot_shape, knot_mode, knot_color); knotholder->add(e); } updateCanvasIndicators(); } } /* 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 :