Implement basic perspective control lines (WIP)

This commit is contained in:
Lawrence Lee
2020-06-08 18:21:12 -07:00
parent 852b3270e1
commit 6b7c1871b9
12 changed files with 703 additions and 51 deletions

View File

@@ -37,6 +37,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("
EvPerspProjAngle = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_ANGLE");
EvPerspProjRotate = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_ROTATE");
EvPerspProjShift = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_SHIFT");
EvPerspRender = mapper->newEvent(TRANSFORM);
lens_geom_listener = nullptr;
Gtk::Image* ipersHL = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png"));
@@ -107,6 +108,31 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("
-60, 60, 0.1, 0, ipers_cam_yaw_left, ipers_cam_yaw_right));
camera_yaw->setAdjusterListener (this);
// Begin control lines interface.
lines_button_h = Gtk::manage (new Gtk::ToggleButton());
lines_button_h->signal_toggled().connect(sigc::bind(sigc::mem_fun(
*this, &::PerspCorrection::linesButtonPressed), lines_button_h));
lines_button_v = Gtk::manage (new Gtk::ToggleButton());
lines_button_v->signal_toggled().connect(sigc::bind(sigc::mem_fun(
*this, &::PerspCorrection::linesButtonPressed), lines_button_v));
lines_button_edit = Gtk::manage (new Gtk::ToggleButton());
lines_button_edit->signal_toggled().connect(sigc::mem_fun(
*this, &::PerspCorrection::linesEditButtonPressed));
lines = new ControlLineManager();
lines->callbacks = new LinesCallbacks(this, lines);
img_ctrl_lines_apply = NULL;
img_ctrl_lines_edit = NULL;
Gtk::HBox* control_lines_box = Gtk::manage (new Gtk::HBox());
control_lines_box->pack_start(*lines_button_v);
control_lines_box->pack_start(*lines_button_h);
control_lines_box->pack_start(*lines_button_edit);
// End control lines interface.
auto_pitch = Gtk::manage (new Gtk::Button ());
auto_pitch->set_image(*ipers_auto_pitch);
auto_pitch->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_pitch) );
@@ -164,6 +190,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("
camera_vbox->pack_start (*camera_roll);
camera_vbox->pack_start (*camera_pitch);
camera_vbox->pack_start (*camera_yaw);
camera_vbox->pack_start (*control_lines_box);
camera_vbox->pack_start (*auto_hbox);
camera_frame->add(*camera_vbox);
camera_based->pack_start(*camera_frame);
@@ -192,6 +219,18 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("
show_all();
}
PerspCorrection::~PerspCorrection()
{
delete lines->callbacks;
delete lines;
if (img_ctrl_lines_apply) {
delete img_ctrl_lines_apply;
}
if (img_ctrl_lines_edit) {
delete img_ctrl_lines_edit;
}
}
void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited)
{
@@ -242,6 +281,8 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited)
void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited)
{
pp->perspective.render = render;
pp->perspective.horizontal = horiz->getValue ();
pp->perspective.vertical = vert->getValue ();
pp->perspective.camera_crop_factor= camera_crop_factor->getValue ();
@@ -387,6 +428,39 @@ void PerspCorrection::adjusterChanged(Adjuster* a, double newval)
}
}
void PerspCorrection::applyControlLines(void)
{
if (!lens_geom_listener) {
return;
}
auto control_lines = lines->toControlLines();
int h_count = 0, v_count = 0;
double rot = 0;
double pitch = 0;
double yaw = 0;
for (unsigned int i = 0; i < lines->size(); i++) {
if (control_lines[i].type == rtengine::ControlLine::HORIZONTAL) {
h_count++;
} else if (control_lines[i].type == rtengine::ControlLine::VERTICAL) {
v_count++;
}
}
lens_geom_listener->autoPerspRequested(v_count > 1, h_count > 1, rot, pitch,
yaw, control_lines, lines->size());
free(control_lines);
disableListener();
camera_pitch->setValue(pitch);
camera_roll->setValue(rot);
camera_yaw->setValue(yaw);
enableListener();
adjusterChanged(camera_pitch, pitch);
}
void PerspCorrection::autoCorrectionPressed(Gtk::Button* b)
{
if (!lens_geom_listener) {
@@ -548,3 +622,393 @@ void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const Fram
camera_focal_length->setValue(default_focal_length);
}
}
void PerspCorrection::switchOffEditMode(ControlLineManager* lines)
{
lines_button_h->set_active(false);
lines_button_v->set_active(false);
lines_button_edit->set_active(false);
}
void PerspCorrection::setEditProvider(EditDataProvider* provider)
{
lines->setEditProvider(provider);
}
void PerspCorrection::linesButtonPressed(Gtk::ToggleButton* button)
{
lines->setLinesState(lines_button_h->get_active(), lines_button_v->get_active());
if (!button->get_active()) {
return;
}
if (button == lines_button_h) {
lines->draw_line_type = rtengine::ControlLine::HORIZONTAL;
if (lines_button_v->get_active()) {
lines_button_v->set_active(false);
}
} else if (button == lines_button_v) {
lines->draw_line_type = rtengine::ControlLine::VERTICAL;
if (lines_button_h->get_active()) {
lines_button_h->set_active(false);
}
}
if (!lines_button_edit->get_active()) {
lines_button_edit->set_active(true);
}
lines->setDrawMode(true);
}
void PerspCorrection::linesEditButtonPressed(void)
{
if (lines_button_edit->get_active()) { // Enter edit mode.
lines->setActive(true);
if (img_ctrl_lines_apply) {
lines_button_edit->set_image(*img_ctrl_lines_apply);
}
render = false;
lines->setLinesState(lines_button_h->get_active(), lines_button_v->get_active());
if (lens_geom_listener) {
lens_geom_listener->updateTransformPreviewRequested(EvPerspRender, false);
}
} else { // Leave edit mode.
render = true;
lines->setDrawMode(false);
lines->setActive(false);
if (img_ctrl_lines_edit) {
lines_button_edit->set_image(*img_ctrl_lines_edit);
}
lines_button_h->set_active(false);
lines_button_v->set_active(false);
applyControlLines();
}
}
ControlLineManager::ControlLineManager():
EditSubscriber(ET_OBJECTS),
cursor(CSCrosshair),
prev_obj(-1),
selected_object(-1)
{
canvas_area = new Rectangle();
canvas_area->filled = true;
canvas_area->topLeft = Coord(0, 0);
mouseOverGeometry.push_back(canvas_area);
}
ControlLineManager::~ControlLineManager()
{
for (auto i = mouseOverGeometry.begin(); i != mouseOverGeometry.end(); i++) {
delete *i;
}
for (auto i = control_lines.begin(); i != control_lines.end(); i++) {
delete *i;
}
}
Geometry::State ControlLineManager::calcLineState(const ::ControlLine& line) const
{
if (line.type == rtengine::ControlLine::HORIZONTAL && active_h) {
return Geometry::NORMAL;
} else if (line.type == rtengine::ControlLine::VERTICAL && active_v) {
return Geometry::NORMAL;
}
return Geometry::INSENSITIVE;
}
void ControlLineManager::setActive(bool active)
{
EditDataProvider* provider = getEditProvider();
if (!provider || (this == provider->getCurrSubscriber()) == active) {
return;
}
if (active) {
subscribe();
int ih, iw;
provider->getImageSize(iw, ih);
canvas_area->bottomRight = Coord(iw, ih);
} else {
unsubscribe();
}
}
void ControlLineManager::setDrawMode(bool draw)
{
draw_mode = draw;
}
void ControlLineManager::setLinesState(bool horiz_active, bool vert_active)
{
active_h = horiz_active;
active_v = vert_active;
for (auto line = control_lines.begin(); line != control_lines.end(); line++) {
auto state = calcLineState(**line);
(*line)->begin->state = state;
(*line)->end->state = state;
(*line)->line->state = state;
}
}
size_t ControlLineManager::size(void) const
{
return control_lines.size();
}
bool ControlLineManager::button1Pressed(int modifierKey)
{
EditDataProvider* dataProvider = getEditProvider();
if (!dataProvider) {
return false;
}
drag_delta = Coord(0, 0);
const int object = dataProvider->getObject();
if (object > 0) { // A control line.
selected_object = object;
action = Action::DRAGGING;
} else if (draw_mode && (modifierKey & GDK_CONTROL_MASK)) { // Add new line.
addLine(dataProvider->posImage, dataProvider->posImage);
selected_object = mouseOverGeometry.size() - 1; // Select endpoint.
action = Action::DRAGGING;
}
return false;
}
bool ControlLineManager::button1Released(void)
{
action = Action::NONE;
selected_object = -1;
return false;
}
bool ControlLineManager::button3Pressed(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
action = Action::NONE;
if (!provider || provider->getObject() < 1) {
return false;
}
action = Action::PICKING;
return false;
}
bool ControlLineManager::pick3(bool picked)
{
if (!picked) {
return false;
}
EditDataProvider* provider = getEditProvider();
if (!provider) {
return false;
}
removeLine((provider->getObject() - 1) / 3);
return false;
}
bool ControlLineManager::drag1(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
if (!provider || selected_object < 1) {
return false;
}
::ControlLine* control_line = control_lines[(selected_object - 1) / 3];
int component = selected_object % 3; // 0 == end, 1 == line, 2 == begin
Coord mouse = provider->posImage + provider->deltaImage;
Coord delta = provider->deltaImage - drag_delta;
int ih, iw;
provider->getImageSize(iw, ih);
switch (component) {
case (0): // end
control_line->end->center = mouse;
control_line->end->center.clip(iw, ih);
control_line->line->end = control_line->end->center;
control_line->end->state = Geometry::DRAGGED;
break;
case (1): { // line
// Constrain delta so the end stays above the image.
Coord new_delta = control_line->end->center + delta;
new_delta.clip(iw, ih);
new_delta -= control_line->end->center;
// Constrain delta so the beginning stays above the image.
new_delta += control_line->begin->center;
new_delta.clip(iw, ih);
new_delta -= control_line->begin->center;
// Move all objects in the control line.
control_line->end->center += new_delta;
control_line->begin->center += new_delta;
control_line->line->end = control_line->end->center;
control_line->line->begin = control_line->begin->center;
drag_delta += new_delta;
control_line->line->state = Geometry::DRAGGED;
break;
}
case (2): // begin
control_line->begin->center = mouse;
control_line->begin->center.clip(iw, ih);
control_line->line->begin = control_line->begin->center;
control_line->begin->state = Geometry::DRAGGED;
break;
}
return false;
}
CursorShape ControlLineManager::getCursor(int objectID) const
{
return cursor;
}
bool ControlLineManager::mouseOver(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
if (!provider) {
return false;
}
int cur_obj = provider->getObject();
if (cur_obj == 0) { // Canvas
if (draw_mode && modifierKey & GDK_CONTROL_MASK) {
cursor = CSPlus;
} else {
cursor = CSCrosshair;
}
} else if (cur_obj < 0) { // Nothing
cursor = CSArrow;
} else { // Object
visibleGeometry[cur_obj - 1]->state = Geometry::PRELIGHT;
cursor = CSMove2D;
}
if (prev_obj != cur_obj && prev_obj > 0) {
auto state = calcLineState(*control_lines[(prev_obj - 1) / 3]);
visibleGeometry[prev_obj - 1]->state = state;
}
prev_obj = cur_obj;
return false;
}
void ControlLineManager::switchOffEditMode(void)
{
if (callbacks) {
callbacks->switchOffEditMode();
}
}
void ControlLineManager::setEditProvider(EditDataProvider* provider)
{
EditSubscriber::setEditProvider(provider);
}
void ControlLineManager::addLine(Coord begin, Coord end)
{
constexpr int line_width = 2;
constexpr int handle_radius = 6;
Line* line;
Circle *begin_c, *end_c;
line = new Line();
line->datum = Geometry::IMAGE;
line->innerLineWidth = line_width;
line->begin = begin;
line->end = end;
begin_c = new Circle();
begin_c->datum = Geometry::IMAGE;
begin_c->filled = true;
begin_c->radius = handle_radius;
begin_c->center = begin;
end_c = new Circle();
end_c->datum = Geometry::IMAGE;
end_c->filled = true;
end_c->radius = handle_radius;
end_c->center = begin;
EditSubscriber::visibleGeometry.push_back(line);
EditSubscriber::visibleGeometry.push_back(begin_c);
EditSubscriber::visibleGeometry.push_back(end_c);
EditSubscriber::mouseOverGeometry.push_back(line);
EditSubscriber::mouseOverGeometry.push_back(begin_c);
EditSubscriber::mouseOverGeometry.push_back(end_c);
::ControlLine* control_line = new ::ControlLine();
control_line->begin = begin_c;
control_line->end = end_c;
control_line->line = line;
control_line->type = draw_line_type;
control_lines.push_back(control_line);
}
void ControlLineManager::removeLine(size_t line_id)
{
if (line_id >= control_lines.size()) {
return;
}
::ControlLine* line = control_lines[line_id];
delete line->begin;
delete line->end;
delete line->line;
delete line;
control_lines.erase(control_lines.begin() + line_id);
visibleGeometry.erase(visibleGeometry.begin() + 3 * line_id,
visibleGeometry.begin() + 3 * line_id + 3);
mouseOverGeometry.erase(mouseOverGeometry.begin() + 3 * line_id + 1,
mouseOverGeometry.begin() + 3 * line_id + 4);
}
rtengine::ControlLine* ControlLineManager::toControlLines(void) const
{
auto retval = (rtengine::ControlLine*)malloc(control_lines.size() * sizeof(rtengine::ControlLine));
for (unsigned int i = 0; i < control_lines.size(); i++) {
retval[i].x1 = control_lines[i]->begin->center.x;
retval[i].y1 = control_lines[i]->begin->center.y;
retval[i].x2 = control_lines[i]->end->center.x;
retval[i].y2 = control_lines[i]->end->center.y;
retval[i].type = control_lines[i]->type;
}
return retval;
}
LinesCallbacks::LinesCallbacks(PerspCorrection* tool, ControlLineManager* lines):
lines(lines),
tool(tool)
{
}
LinesCallbacks::~LinesCallbacks()
{
}
void LinesCallbacks::switchOffEditMode(void)
{
if (tool) {
tool->switchOffEditMode(lines);
}
}