summaryrefslogtreecommitdiffstats
path: root/src/live_effects/parameter/satellitesarray.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects/parameter/satellitesarray.cpp')
-rw-r--r--src/live_effects/parameter/satellitesarray.cpp575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/live_effects/parameter/satellitesarray.cpp b/src/live_effects/parameter/satellitesarray.cpp
new file mode 100644
index 0000000..7f15298
--- /dev/null
+++ b/src/live_effects/parameter/satellitesarray.cpp
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author(s):
+ * Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
+ *
+ * Copyright (C) 2014 Author(s)
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/satellitesarray.h"
+#include "helper/geom.h"
+#include "knotholder.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpe-fillet-chamfer.h"
+#include "ui/dialog/lpe-fillet-chamfer-properties.h"
+#include "ui/shape-editor.h"
+#include "ui/tools-switch.h"
+#include "ui/tools/node-tool.h"
+
+#include "inkscape.h"
+#include <preferences.h>
+// TODO due to internal breakage in glibmm headers,
+// this has to be included last.
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+SatellitesArrayParam::SatellitesArrayParam(const Glib::ustring &label,
+ const Glib::ustring &tip,
+ const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : ArrayParam<std::vector<Satellite> >(label, tip, key, wr, effect, 0), _knoth(nullptr)
+{
+ _knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ _knot_mode = SP_KNOT_MODE_XOR;
+ _knot_color = 0xAAFF8800;
+ _use_distance = false;
+ _global_knot_hide = false;
+ _current_zoom = 0;
+ _effectType = FILLET_CHAMFER;
+ _last_pathvector_satellites = nullptr;
+ param_widget_is_visible(false);
+}
+
+
+void SatellitesArrayParam::set_oncanvas_looks(SPKnotShapeType shape,
+ SPKnotModeType mode,
+ guint32 color)
+{
+ _knot_shape = shape;
+ _knot_mode = mode;
+ _knot_color = color;
+}
+
+void SatellitesArrayParam::setPathVectorSatellites(PathVectorSatellites *pathVectorSatellites, bool write)
+{
+ _last_pathvector_satellites = pathVectorSatellites;
+ if (write) {
+ param_set_and_write_new_value(_last_pathvector_satellites->getSatellites());
+ } else {
+ param_setValue(_last_pathvector_satellites->getSatellites());
+ }
+}
+
+void SatellitesArrayParam::reloadKnots()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop && tools_isactive(desktop, TOOLS_NODES)) {
+ Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool *>(desktop->event_context);
+ if (nt) {
+ for (auto i = nt->_shape_editors.begin(); i != nt->_shape_editors.end(); ++i) {
+ Inkscape::UI::ShapeEditor *shape_editor = i->second;
+ if (shape_editor && shape_editor->lpeknotholder) {
+ SPItem *item = shape_editor->knotholder->item;
+ shape_editor->unset_item(true);
+ shape_editor->set_item(item);
+ }
+ }
+ }
+ }
+}
+void SatellitesArrayParam::setUseDistance(bool use_knot_distance)
+{
+ _use_distance = use_knot_distance;
+}
+
+void SatellitesArrayParam::setCurrentZoom(double current_zoom)
+{
+ _current_zoom = current_zoom;
+}
+
+void SatellitesArrayParam::setGlobalKnotHide(bool global_knot_hide)
+{
+ _global_knot_hide = global_knot_hide;
+}
+
+void SatellitesArrayParam::setEffectType(EffectType et)
+{
+ _effectType = et;
+}
+
+void SatellitesArrayParam::updateCanvasIndicators(bool mirror)
+{
+ if (!_last_pathvector_satellites) {
+ return;
+ }
+
+ if (!_hp.empty()) {
+ _hp.clear();
+ }
+ Geom::PathVector pathv = _last_pathvector_satellites->getPathVector();
+ if (pathv.empty()) {
+ return;
+ }
+ if (mirror == true) {
+ _hp.clear();
+ }
+ if (_effectType == FILLET_CHAMFER) {
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (_vector[i][j].hidden || //Ignore if hidden
+ (!_vector[i][j].has_mirror && mirror == true) || //Ignore if not have mirror and we are in mirror loop
+ _vector[i][j].amount == 0 || //no helper in 0 value
+ j >= count_path_nodes(pathv[i]) || //ignore last satellite in open paths with fillet chamfer effect
+ (!pathv[i].closed() && j == 0) || //ignore first satellites on open paths
+ count_path_nodes(pathv[i]) == 2)
+ {
+ continue;
+ }
+ Geom::Curve *curve_in = pathv[i][j].duplicate();
+ double pos = 0;
+ bool overflow = false;
+ double size_out = _vector[i][j].arcDistance(*curve_in);
+ double length_out = curve_in->length();
+ gint previous_index = j - 1; //Always are previous index because we skip first satellite on open paths
+ if (j == 0 && pathv[i].closed()) {
+ previous_index = count_path_nodes(pathv[i]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ double length_in = pathv.curveAt(previous_index).length();
+ if (mirror) {
+ curve_in = const_cast<Geom::Curve *>(&pathv.curveAt(previous_index));
+ pos = _vector[i][j].time(size_out, true, *curve_in);
+ if (length_out < size_out) {
+ overflow = true;
+ }
+ } else {
+ pos = _vector[i][j].time(*curve_in);
+ if (length_in < size_out) {
+ overflow = true;
+ }
+ }
+ if (pos <= 0 || pos >= 1) {
+ continue;
+ }
+ }
+ }
+ }
+ if (mirror) {
+ updateCanvasIndicators(false);
+ }
+}
+void SatellitesArrayParam::updateCanvasIndicators()
+{
+ updateCanvasIndicators(true);
+}
+
+void SatellitesArrayParam::addCanvasIndicators(
+ SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ hp_vec.push_back(_hp);
+}
+
+void SatellitesArrayParam::param_transform_multiply(Geom::Affine const &postmul, bool /*set*/)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ if (prefs->getBool("/options/transform/rectcorners", true)) {
+ for (auto & i : _vector) {
+ for (auto & j : i) {
+ if (!j.is_time && j.amount > 0) {
+ j.amount = j.amount * ((postmul.expansionX() + postmul.expansionY()) / 2);
+ }
+ }
+ }
+ param_set_and_write_new_value(_vector);
+ }
+}
+
+void SatellitesArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
+ SPItem *item,
+ bool mirror)
+{
+ if (!_last_pathvector_satellites) {
+ return;
+ }
+ size_t index = 0;
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (!_vector[i][j].has_mirror && mirror) {
+ continue;
+ }
+ SatelliteType type = _vector[i][j].satellite_type;
+ if (mirror && i == 0 && j == 0) {
+ index += _last_pathvector_satellites->getTotalSatellites();
+ }
+ using namespace Geom;
+ //If is for filletChamfer effect...
+ if (_effectType == FILLET_CHAMFER) {
+ const gchar *tip;
+ if (type == CHAMFER) {
+ tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else if (type == INVERSE_CHAMFER) {
+ tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else if (type == INVERSE_FILLET) {
+ tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else {
+ tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ }
+ FilletChamferKnotHolderEntity *e = new FilletChamferKnotHolderEntity(this, index);
+ e->create(nullptr, item, knotholder, Inkscape::CTRL_TYPE_LPE, _(tip), _knot_shape, _knot_mode,
+ _knot_color);
+ knotholder->add(e);
+ }
+ index++;
+ }
+ }
+ if (mirror) {
+ addKnotHolderEntities(knotholder, item, false);
+ }
+}
+
+void SatellitesArrayParam::updateAmmount(double amount)
+{
+ Geom::PathVector const pathv = _last_pathvector_satellites->getPathVector();
+ Satellites satellites = _last_pathvector_satellites->getSatellites();
+ for (size_t i = 0; i < satellites.size(); ++i) {
+ for (size_t j = 0; j < satellites[i].size(); ++j) {
+ Geom::Curve const &curve_in = pathv[i][j];
+ if (param_effect->isNodePointSelected(curve_in.initialPoint()) ){
+ _vector[i][j].amount = amount;
+ _vector[i][j].setSelected(true);
+ } else {
+ _vector[i][j].setSelected(false);
+ }
+ }
+ }
+}
+
+void SatellitesArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
+ SPItem *item)
+{
+ _knoth = knotholder;
+ addKnotHolderEntities(knotholder, item, true);
+}
+
+FilletChamferKnotHolderEntity::FilletChamferKnotHolderEntity(
+ SatellitesArrayParam *p, size_t index)
+ : _pparam(p), _index(index) {}
+
+
+void FilletChamferKnotHolderEntity::knot_set(Geom::Point const &p,
+ Geom::Point const &/*origin*/,
+ guint state)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+
+ Geom::Point s = snap_knot_position(p, state);
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return;
+ }
+ Satellite satellite = _pparam->_vector[satelite_index][subsatelite_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0||//ignore first satellites on open paths
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index))) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ Geom::Curve const &curve_in = pathv[satelite_index][previous_index];
+ double mirror_time = Geom::nearest_time(s, curve_in);
+ Geom::Point mirror = curve_in.pointAt(mirror_time);
+ double normal_time = Geom::nearest_time(s, pathv[satelite_index][subsatelite_index]);
+ Geom::Point normal = pathv[satelite_index][subsatelite_index].pointAt(normal_time);
+ double distance_mirror = Geom::distance(mirror,s);
+ double distance_normal = Geom::distance(normal,s);
+ if (Geom::are_near(s, pathv[satelite_index][subsatelite_index].initialPoint(), 1.5 / _pparam->_current_zoom)) {
+ satellite.amount = 0;
+ } else if (distance_mirror < distance_normal) {
+ double time_start = 0;
+ Satellites satellites = _pparam->_last_pathvector_satellites->getSatellites();
+ time_start = satellites[satelite_index][previous_index].time(curve_in);
+ if (time_start > mirror_time) {
+ mirror_time = time_start;
+ }
+ double size = arcLengthAt(mirror_time, curve_in);
+ double amount = curve_in.length() - size;
+ if (satellite.is_time) {
+ amount = timeAtArcLength(amount, pathv[satelite_index][subsatelite_index]);
+ }
+ satellite.amount = amount;
+ } else {
+ satellite.setPosition(s, pathv[satelite_index][subsatelite_index]);
+ }
+ Inkscape::LivePathEffect::LPEFilletChamfer *filletchamfer = dynamic_cast<Inkscape::LivePathEffect::LPEFilletChamfer *>(_pparam->param_effect);
+ filletchamfer->helperpath = true;
+ _pparam->updateAmmount(satellite.amount);
+ _pparam->_vector[satelite_index][subsatelite_index] = satellite;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+}
+
+void
+FilletChamferKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state)
+{
+ Inkscape::LivePathEffect::LPEFilletChamfer *filletchamfer = dynamic_cast<Inkscape::LivePathEffect::LPEFilletChamfer *>(_pparam->param_effect);
+ if (filletchamfer) {
+ filletchamfer->refresh_widgets = true;
+ filletchamfer->helperpath = false;
+ filletchamfer->writeParamsToSVG();
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ }
+}
+
+Geom::Point FilletChamferKnotHolderEntity::knot_get() const
+{
+ if (!_pparam->_last_pathvector_satellites || _pparam->_global_knot_hide) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Point tmp_point;
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Satellite satellite = _pparam->_vector[satelite_index][subsatelite_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[satelite_index].closed() && (subsatelite_index == 0 ||
+ subsatelite_index == count_path_nodes(pathv[satelite_index]) - 1))) //ignore first and last satellites on open paths
+ {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ this->knot->show();
+ if (is_mirror) {
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Curve const &curve_in = pathv[satelite_index][previous_index];
+ double s = satellite.arcDistance(pathv[satelite_index][subsatelite_index]);
+ double t = satellite.time(s, true, curve_in);
+ if (t > 1) {
+ t = 1;
+ }
+ if (t < 0) {
+ t = 0;
+ }
+ double time_start = 0;
+ time_start = _pparam->_last_pathvector_satellites->getSatellites()[satelite_index][previous_index].time(curve_in);
+ if (time_start > t) {
+ t = time_start;
+ }
+ tmp_point = (curve_in).pointAt(t);
+ } else {
+ tmp_point = satellite.getPosition(pathv[satelite_index][subsatelite_index]);
+ }
+ Geom::Point const canvas_point = tmp_point;
+ return canvas_point;
+}
+
+void FilletChamferKnotHolderEntity::knot_click(guint state)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return;
+ }
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 ||
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index)) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ if (state & GDK_CONTROL_MASK) {
+ if (state & GDK_MOD1_MASK) {
+ _pparam->_vector[satelite_index][subsatelite_index].amount = 0.0;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ } else {
+ using namespace Geom;
+ SatelliteType type = _pparam->_vector[satelite_index][subsatelite_index].satellite_type;
+ switch (type) {
+ case FILLET:
+ type = INVERSE_FILLET;
+ break;
+ case INVERSE_FILLET:
+ type = CHAMFER;
+ break;
+ case CHAMFER:
+ type = INVERSE_CHAMFER;
+ break;
+ default:
+ type = FILLET;
+ break;
+ }
+ _pparam->_vector[satelite_index][subsatelite_index].satellite_type = type;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ const gchar *tip;
+ if (type == CHAMFER) {
+ tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else if (type == INVERSE_CHAMFER) {
+ tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else if (type == INVERSE_FILLET) {
+ tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else {
+ tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ }
+ this->knot->tip = g_strdup(tip);
+ this->knot->show();
+ }
+ } else if (state & GDK_SHIFT_MASK) {
+ double amount = _pparam->_vector[satelite_index][subsatelite_index].amount;
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ if (!_pparam->_use_distance && !_pparam->_vector[satelite_index][subsatelite_index].is_time) {
+ amount = _pparam->_vector[satelite_index][subsatelite_index].lenToRad(
+ amount, pathv[satelite_index][previous_index], pathv[satelite_index][subsatelite_index],
+ _pparam->_vector[satelite_index][previous_index]);
+ }
+ bool aprox = false;
+ Geom::D2<Geom::SBasis> d2_out = pathv[satelite_index][subsatelite_index].toSBasis();
+ Geom::D2<Geom::SBasis> d2_in = pathv[satelite_index][previous_index].toSBasis();
+ aprox = ((d2_in)[0].degreesOfFreedom() != 2 ||
+ d2_out[0].degreesOfFreedom() != 2) &&
+ !_pparam->_use_distance
+ ? true
+ : false;
+ Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog(
+ this->desktop, amount, this, _pparam->_use_distance, aprox,
+ _pparam->_vector[satelite_index][subsatelite_index]);
+ }
+}
+
+void FilletChamferKnotHolderEntity::knot_set_offset(Satellite satellite)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return;
+ }
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 || count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index))) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ double amount = satellite.amount;
+ double max_amount = amount;
+ if (!_pparam->_use_distance && !satellite.is_time) {
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ amount = _pparam->_vector[satelite_index][subsatelite_index].radToLen(
+ amount, pathv[satelite_index][previous_index], pathv[satelite_index][subsatelite_index]);
+ if (max_amount > 0 && amount == 0) {
+ amount = _pparam->_vector[satelite_index][subsatelite_index].amount;
+ }
+ }
+ satellite.amount = amount;
+ _pparam->_vector[satelite_index][subsatelite_index] = satellite;
+ this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(item);
+ if (splpeitem) {
+ sp_lpe_item_update_patheffect(splpeitem, false, false);
+ }
+}
+
+} /* 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 :