diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index def39a5cd..0cd47f132 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1904,7 +1904,12 @@ bool PerspectiveParams::operator ==(const PerspectiveParams& other) const && projection_shift_vert == other.projection_shift_vert && projection_rotate == other.projection_rotate && projection_pitch == other.projection_pitch - && projection_yaw == other.projection_yaw; + && projection_yaw == other.projection_yaw + // Lines could still be equivalent if the vectors aren't, but this is + // rare and a small issue. Besides, a proper comparison requires lots + // more code which introduces clutter. + && control_line_values == other.control_line_values + && control_line_types == other.control_line_types; } bool PerspectiveParams::operator !=(const PerspectiveParams& other) const @@ -5210,6 +5215,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->perspective.projection_shift_horiz, "Perspective", "ProjectionShiftHorizontal", perspective.projection_shift_horiz, keyFile); saveToKeyfile(!pedited || pedited->perspective.projection_shift_vert, "Perspective", "ProjectionShiftVertical", perspective.projection_shift_vert, keyFile); saveToKeyfile(!pedited || pedited->perspective.projection_yaw, "Perspective", "ProjectionYaw", perspective.projection_yaw, keyFile); + saveToKeyfile(!pedited || pedited->perspective.control_lines, "Perspective", "ControlLineValues", perspective.control_line_values, keyFile); + saveToKeyfile(!pedited || pedited->perspective.control_lines, "Perspective", "ControlLineTypes", perspective.control_line_types, keyFile); // Gradient saveToKeyfile(!pedited || pedited->gradient.enabled, "Gradient", "Enabled", gradient.enabled, keyFile); @@ -6839,6 +6846,13 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Perspective", "ProjectionShiftHorizontal", pedited, perspective.projection_shift_horiz, pedited->perspective.projection_shift_horiz); assignFromKeyfile(keyFile, "Perspective", "ProjectionShiftVertical", pedited, perspective.projection_shift_vert, pedited->perspective.projection_shift_vert); assignFromKeyfile(keyFile, "Perspective", "ProjectionYaw", pedited, perspective.projection_yaw, pedited->perspective.projection_yaw); + if (keyFile.has_key("Perspective", "ControlLineValues") && keyFile.has_key("Perspective", "ControlLineTypes")) { + perspective.control_line_values = keyFile.get_integer_list("Perspective", "ControlLineValues"); + perspective.control_line_types = keyFile.get_integer_list("Perspective", "ControlLineTypes"); + if (pedited) { + pedited->perspective.control_lines = true; + } + } } if (keyFile.has_group("Gradient")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index ae46174a2..c7139236c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -945,6 +945,10 @@ struct PerspectiveParams { double projection_shift_horiz; double projection_shift_vert; double projection_yaw; + /** A line is stored as 4 integers in this order: x1, y1, x2, y2 */ + std::vector control_line_values; + /** 0 is vertical, 1 is horizontal, undefined otherwise. */ + std::vector control_line_types; PerspectiveParams(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index bbfb7cc4e..53c514916 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -353,6 +353,7 @@ void ParamsEdited::set(bool v) perspective.projection_shift_horiz = v; perspective.projection_shift_vert = v; perspective.projection_yaw = v; + perspective.control_lines = v; gradient.enabled = v; gradient.degree = v; gradient.feather = v; @@ -1006,6 +1007,7 @@ void ParamsEdited::initFrom(const std::vector& perspective.projection_shift_horiz = perspective.projection_shift_horiz && p.perspective.projection_shift_horiz == other.perspective.projection_shift_horiz; perspective.projection_shift_vert = perspective.projection_shift_vert && p.perspective.projection_shift_vert == other.perspective.projection_shift_vert; perspective.projection_yaw = perspective.projection_yaw && p.perspective.projection_yaw == other.perspective.projection_yaw; + perspective.control_lines = perspective.control_lines && p.perspective.control_line_values == other.perspective.control_line_values && p.perspective.control_line_types == other.perspective.control_line_types; gradient.enabled = gradient.enabled && p.gradient.enabled == other.gradient.enabled; gradient.degree = gradient.degree && p.gradient.degree == other.gradient.degree; gradient.feather = gradient.feather && p.gradient.feather == other.gradient.feather; @@ -2986,6 +2988,11 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.perspective.projection_yaw = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_ANGLE] ? toEdit.perspective.projection_yaw + mods.perspective.projection_yaw : mods.perspective.projection_yaw; } + if (perspective.control_lines) { + toEdit.perspective.control_line_values = mods.perspective.control_line_values; + toEdit.perspective.control_line_types = mods.perspective.control_line_types; + } + if (gradient.enabled) { toEdit.gradient.enabled = mods.gradient.enabled; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 1bd2e045e..1c309e994 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -879,6 +879,7 @@ struct PerspectiveParamsEdited { bool projection_shift_horiz; bool projection_shift_vert; bool projection_yaw; + bool control_lines; }; struct GradientParamsEdited { diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 68e43278f..e652c21f2 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -27,6 +27,63 @@ using namespace rtengine; using namespace rtengine::procparams; +namespace +{ + +void controlLinesToValues(const std::vector& lines, + std::vector& values, std::vector& types) +{ + values.clear(); + types.clear(); + + for (auto&& line : lines) { + values.push_back(line.x1); + values.push_back(line.y1); + values.push_back(line.x2); + values.push_back(line.y2); + + int type = -1; + switch (line.type) { + case rtengine::ControlLine::VERTICAL: + type = 0; + break; + case rtengine::ControlLine::HORIZONTAL: + type = 1; + break; + } + types.push_back(type); + } +} + +std::vector valuesToControlLines( + const std::vector& values, const std::vector& types) +{ + int line_count = min(values.size() / 4, types.size()); + std::vector lines(line_count); + + auto values_iter = values.begin(); + auto types_iter = types.begin(); + for (auto&& line : lines) { + line.x1 = *(values_iter++); + line.y1 = *(values_iter++); + line.x2 = *(values_iter++); + line.y2 = *(values_iter++); + + switch (*(types_iter++)) { + case 0: + line.type = rtengine::ControlLine::VERTICAL; + break; + case 1: + line.type = rtengine::ControlLine::HORIZONTAL; + break; + } + } + + return lines; +} + +} + PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("TP_PERSPECTIVE_LABEL")) { @@ -48,6 +105,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" EvPerspProjRotateVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_ROTATE"); EvPerspProjShiftVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_SHIFT"); setCamBasedEventsActive(); + EvPerspControlLines = mapper->newEvent(M_VOID); lens_geom_listener = nullptr; metadata = nullptr; @@ -262,6 +320,7 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) projection_shift_horiz->setEditedState (pedited->perspective.projection_shift_horiz ? Edited : UnEdited); projection_shift_vert->setEditedState (pedited->perspective.projection_shift_vert ? Edited : UnEdited); projection_yaw->setEditedState (pedited->perspective.projection_yaw ? Edited : UnEdited); + lines->setEdited (pedited->perspective.control_lines); } horiz->setValue (pp->perspective.horizontal); @@ -277,6 +336,8 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) projection_shift_horiz->setValue (pp->perspective.projection_shift_horiz); projection_shift_vert->setValue (pp->perspective.projection_shift_vert); projection_yaw->setValue (pp->perspective.projection_yaw); + lines->setLines(valuesToControlLines(pp->perspective.control_line_values, + pp->perspective.control_line_types)); if (pedited && !pedited->perspective.method) { method->set_active (2); @@ -309,6 +370,11 @@ void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited) pp->perspective.projection_shift_vert = projection_shift_vert->getValue (); pp->perspective.projection_yaw = projection_yaw->getValue (); + std::vector control_lines; + lines->toControlLines(control_lines); + controlLinesToValues(control_lines, pp->perspective.control_line_values, + pp->perspective.control_line_types); + if (method->get_active_row_number() == 0) { pp->perspective.method = "simple"; } else if (method->get_active_row_number() == 1) { @@ -331,6 +397,7 @@ void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited) pedited->perspective.projection_shift_horiz = projection_shift_horiz->getEditedState(); pedited->perspective.projection_shift_vert = projection_shift_vert->getEditedState(); pedited->perspective.projection_yaw = projection_yaw->getEditedState(); + pedited->perspective.control_lines = lines->getEdited(); } } @@ -650,6 +717,13 @@ void PerspCorrection::setEditProvider(EditDataProvider* provider) lines->setEditProvider(provider); } +void PerspCorrection::lineChanged(void) +{ + if (listener) { + listener->panelChanged(EvPerspControlLines, ""); + } +} + void PerspCorrection::linesApplyButtonPressed(void) { if (method->get_active_row_number() == 1) { @@ -714,6 +788,7 @@ ControlLineManager::ControlLineManager(): cursor(CSCrosshair), draw_mode(false), drawing_line(false), + edited(false), prev_obj(-1), selected_object(-1) { @@ -790,6 +865,8 @@ bool ControlLineManager::button1Released(void) if (selected_object > 0) { mouseOverGeometry[selected_object]->state = Geometry::NORMAL; } + edited = true; + callbacks->lineChanged(); drawing_line = false; selected_object = -1; return false; @@ -837,6 +914,9 @@ bool ControlLineManager::pick1(bool picked) visibleGeometry[object_id - 1] = line.icon.get(); + edited = true; + callbacks->lineChanged(); + return true; } @@ -920,6 +1000,11 @@ bool ControlLineManager::drag1(int modifierKey) return false; } +bool ControlLineManager::getEdited(void) const +{ + return edited; +} + CursorShape ControlLineManager::getCursor(int objectID) const { return cursor; @@ -966,12 +1051,27 @@ void ControlLineManager::switchOffEditMode(void) } } +void ControlLineManager::setEdited(bool edited) +{ + this->edited = edited; +} + void ControlLineManager::setEditProvider(EditDataProvider* provider) { EditSubscriber::setEditProvider(provider); } -void ControlLineManager::addLine(Coord begin, Coord end) +void ControlLineManager::setLines(const std::vector& lines) +{ + removeAll(); + for (auto&& line : lines) { + Coord start(line.x1, line.y1); + Coord end(line.x2, line.y2); + addLine(start, end, line.type); + } +} + +void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine::Type type) { constexpr int line_width = 2; constexpr int handle_radius = 6; @@ -1005,16 +1105,20 @@ void ControlLineManager::addLine(Coord begin, Coord end) end_c->datum = Geometry::IMAGE; end_c->filled = true; end_c->radius = handle_radius; - end_c->center = begin; + end_c->center = end; std::unique_ptr<::ControlLine> control_line(new ::ControlLine()); control_line->begin = std::move(begin_c); control_line->end = std::move(end_c); control_line->icon_h = icon_h; control_line->icon_v = icon_v; - control_line->icon = icon_v; + if (type == rtengine::ControlLine::HORIZONTAL) { + control_line->icon = icon_h; + } else { + control_line->icon = icon_v; + } control_line->line = std::move(line); - control_line->type = rtengine::ControlLine::VERTICAL; + control_line->type = type; EditSubscriber::visibleGeometry.push_back(control_line->line.get()); EditSubscriber::visibleGeometry.push_back(control_line->icon.get()); @@ -1069,6 +1173,8 @@ void ControlLineManager::removeAll(void) control_lines.clear(); prev_obj = -1; selected_object = -1; + edited = true; + callbacks->lineChanged(); } void ControlLineManager::removeLine(size_t line_id) @@ -1082,6 +1188,9 @@ void ControlLineManager::removeLine(size_t line_id) mouseOverGeometry.erase(mouseOverGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + 1, mouseOverGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + ::ControlLine::OBJ_COUNT + 1); control_lines.erase(control_lines.begin() + line_id); + + edited = true; + callbacks->lineChanged(); } void ControlLineManager::toControlLines(std::vector& converted) const @@ -1103,6 +1212,13 @@ LinesCallbacks::LinesCallbacks(PerspCorrection* tool): { } +void LinesCallbacks::lineChanged(void) +{ + if (tool) { + tool->lineChanged(); + } +} + void LinesCallbacks::switchOffEditMode(void) { if (tool) { diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 3538af279..c58a9420e 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -48,12 +48,14 @@ protected: CursorShape cursor; bool draw_mode; bool drawing_line; + bool edited; Cairo::RefPtr line_icon_h, line_icon_v; Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; int prev_obj; int selected_object; - void addLine (rtengine::Coord begin, rtengine::Coord end); + void addLine (rtengine::Coord begin, rtengine::Coord end, + rtengine::ControlLine::Type type = rtengine::ControlLine::VERTICAL); /** * Set the line type of the line containing the object according to the * line's angle. @@ -69,6 +71,8 @@ public: { public: virtual ~Callbacks() {}; + /** Called when a line changed (added, removed, moved, etc.). */ + virtual void lineChanged (void) {}; /** Called when the EditSubscriber's switchOffEditMode is called. */ virtual void switchOffEditMode (void) {}; }; @@ -78,12 +82,15 @@ public: ControlLineManager(); + bool getEdited (void) const; void removeAll (void); /** Sets whether or not the lines are visible and interact-able. */ void setActive (bool active); /** Set whether or not lines can be drawn and deleted. */ void setDrawMode (bool draw); + void setEdited (bool edited); void setEditProvider (EditDataProvider* provider); + void setLines (const std::vector& lines); /** Returns the number of lines. */ size_t size (void) const; /** @@ -138,6 +145,7 @@ protected: rtengine::ProcEvent EvPerspCamFocalLength; rtengine::ProcEvent EvPerspCamShift; rtengine::ProcEvent EvPerspCamAngle; + rtengine::ProcEvent EvPerspControlLines; rtengine::ProcEvent EvPerspMethod; rtengine::ProcEvent EvPerspProjShift; rtengine::ProcEvent EvPerspProjRotate; @@ -173,6 +181,7 @@ public: void adjusterChanged (Adjuster* a, double newval) override; void autoCorrectionPressed (Gtk::Button* b); + void lineChanged (void); void linesApplyButtonPressed (void); void linesEditButtonPressed (void); void linesEraseButtonPressed (void); @@ -195,5 +204,6 @@ protected: public: explicit LinesCallbacks(PerspCorrection* tool); + void lineChanged (void) override; void switchOffEditMode (void) override; };