Skip to content

Commit

Permalink
new tool: roundoff polygon vertex
Browse files Browse the repository at this point in the history
closes #322
  • Loading branch information
carrotIndustries committed Apr 11, 2020
1 parent 7a28712 commit b028dbe
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ SRC_IMP = \
src/core/tools/tool_smash_panel_outline.cpp\
src/core/tools/tool_smash_package_outline.cpp\
src/core/tools/tool_resize_symbol.cpp\
src/core/tools/tool_round_off_vertex.cpp\
src/document/documents.cpp\
src/core/clipboard.cpp\
src/core/buffer.cpp\
Expand Down
4 changes: 4 additions & 0 deletions src/core/create_tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#include "tools/tool_smash_panel_outline.hpp"
#include "tools/tool_smash_package_outline.hpp"
#include "tools/tool_resize_symbol.hpp"
#include "tools/tool_round_off_vertex.hpp"

namespace horizon {

Expand Down Expand Up @@ -364,6 +365,9 @@ std::unique_ptr<ToolBase> Core::create_tool(ToolID tool_id)
case ToolID::RESIZE_SYMBOL:
return std::make_unique<ToolResizeSymbol>(this, tool_id);

case ToolID::ROUND_OFF_VERTEX:
return std::make_unique<ToolRoundOffVertex>(this, tool_id);

default:
return nullptr;
}
Expand Down
1 change: 1 addition & 0 deletions src/core/tool_id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,6 @@ enum class ToolID {
SMASH_PANEL_OUTLINE,
SMASH_PACKAGE_OUTLINE,
RESIZE_SYMBOL,
ROUND_OFF_VERTEX
};
} // namespace horizon
163 changes: 163 additions & 0 deletions src/core/tools/tool_round_off_vertex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include "tool_round_off_vertex.hpp"
#include "common/polygon.hpp"
#include "imp/imp_interface.hpp"
#include "util/util.hpp"
#include "document/idocument.hpp"
#include <gdk/gdkkeysyms.h>
#include "util/selection_util.hpp"
#include "clipper/clipper.hpp"

namespace horizon {

ToolRoundOffVertex::ToolRoundOffVertex(IDocument *c, ToolID tid) : ToolBase(c, tid)
{
}

bool ToolRoundOffVertex::can_begin()
{
return sel_count_type(selection, ObjectType::POLYGON_VERTEX) == 1;
}

int ToolRoundOffVertex::wrap_index(int i) const
{
while (i < 0) {
i += poly->vertices.size();
}
while (i >= (int)poly->vertices.size())
i -= poly->vertices.size();
return i;
}

using Coordd = Coord<double>;

static Coordd normalize(const Coordd &c)
{
return c / (sqrt(c.mag_sq()));
}

static Coordi to_coordi(const Coordd &c)
{
return Coordi(c.x, c.y);
}

ToolResponse ToolRoundOffVertex::begin(const ToolArgs &args)
{
int vertex_idx = 0;
{
auto x = sel_find_one(selection, ObjectType::POLYGON_VERTEX);
poly = doc.r->get_polygon(x.uuid);
vertex_idx = x.vertex;
}

auto v_next = wrap_index(vertex_idx + 1);
auto v_prev = wrap_index(vertex_idx - 1);

if ((poly->vertices.at(vertex_idx).type == Polygon::Vertex::Type::ARC)
|| (poly->vertices.at(v_prev).type == Polygon::Vertex::Type::ARC)) {
imp->tool_bar_flash("can't round off arc");
return ToolResponse::end();
}

selection.clear();

p0 = poly->vertices.at(vertex_idx).position;
vn = normalize(Coordd(poly->vertices.at(v_next).position) - p0);
vp = normalize(Coordd(poly->vertices.at(v_prev).position) - p0);
vh = normalize(vn + vp);

delta_max = sqrt(std::min((poly->vertices.at(v_next).position - poly->vertices.at(vertex_idx).position).mag_sq(),
(poly->vertices.at(v_prev).position - poly->vertices.at(vertex_idx).position).mag_sq()));
alpha = acos(vh.dot(vp));
if (isnan(alpha) || (alpha > .99 * (M_PI / 2))) {
imp->tool_bar_flash("can't round off collinear edges");
return ToolResponse::end();
}

bool rev = false;
{
ClipperLib::Path path;
path.reserve(poly->vertices.size());
std::transform(poly->vertices.begin(), poly->vertices.end(), std::back_inserter(path),
[](const Polygon::Vertex &v) { return ClipperLib::IntPoint(v.position.x, v.position.y); });
rev = (!ClipperLib::Orientation(path));
}


if (v_next == 0) {
poly->vertices.emplace_back(Coordi());
vxn = &poly->vertices.back();
}
else {
vxn = &*poly->vertices.emplace(poly->vertices.begin() + v_next, Coordi());
}
vxp = &poly->vertices.at(vertex_idx);
vxp->type = Polygon::Vertex::Type::ARC;
vxp->arc_reverse = rev;

poly->temp = true;

update_cursor(args.coords);

return ToolResponse();
}

void ToolRoundOffVertex::update_poly(double r)
{
auto r_max = tan(alpha) * delta_max;
r = std::min(r_max, r);
auto delta = r / tan(alpha);
auto u = r / sin(alpha);
vxp->position = to_coordi(p0 + vp * delta);
vxp->arc_center = to_coordi(p0 + vh * u);
vxn->position = to_coordi(p0 + vn * delta);
}

void ToolRoundOffVertex::update_cursor(const Coordi &c)
{
auto vm = Coordd(c) - p0;
auto u = std::max(sqrt(vm.mag_sq()) * vh.dot(normalize(vm)), 0.);
auto r = u * sin(alpha);
auto r_max = tan(alpha) * delta_max;
r = std::min(r_max, r);
radius_current = r;
imp->tool_bar_set_tip(
"<b>LMB:</b>set radius <b>RMB:</b>cancel <b>Return:</b>enter radius <b>e:</b>flip arc <i>Current radius:"
+ dim_to_string(r, false) + "</i>");
update_poly(r);
}

ToolResponse ToolRoundOffVertex::update(const ToolArgs &args)
{
if (args.type == ToolEventType::MOVE) {
update_cursor(args.coords);
}
else if (args.type == ToolEventType::CLICK) {
if (args.button == 1) {
poly->temp = false;
return ToolResponse::commit();
}
else if (args.button == 3) {
selection.clear();
return ToolResponse::revert();
}
}
else if (args.type == ToolEventType::KEY) {
if (args.key == GDK_KEY_Escape) {
selection.clear();
return ToolResponse::revert();
}
else if (args.key == GDK_KEY_e) {
vxp->arc_reverse = !vxp->arc_reverse;
}
else if (args.key == GDK_KEY_Return) {
auto r = imp->dialogs.ask_datum("Enter arc radius", radius_current);
if (r.first && r.second > 0) {
update_poly(r.second);
poly->temp = false;
return ToolResponse::commit();
}
}
}
return ToolResponse();
}
} // namespace horizon
39 changes: 39 additions & 0 deletions src/core/tools/tool_round_off_vertex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once
#include "common/polygon.hpp"
#include "core/tool.hpp"
#include <forward_list>

namespace horizon {

class ToolRoundOffVertex : public ToolBase {
public:
ToolRoundOffVertex(IDocument *c, ToolID tid);
ToolResponse begin(const ToolArgs &args) override;
ToolResponse update(const ToolArgs &args) override;
bool can_begin() override;
bool is_specific() override
{
return true;
}

private:
Polygon *poly = nullptr;
int wrap_index(int i) const;

Polygon::Vertex *vxn = nullptr;
Polygon::Vertex *vxp = nullptr;

Coord<double> p0;
Coord<double> vp;
Coord<double> vn;
Coord<double> vh;
double delta_max = 0;
double alpha = 0;
double radius_current = 0;

void update_poly(double r);
void update_cursor(const Coordi &c);

bool orientation = false;
};
} // namespace horizon
5 changes: 5 additions & 0 deletions src/imp/action_catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,10 @@ const std::map<std::pair<ActionID, ToolID>, ActionCatalogItem> action_catalog =
{{ActionID::TOOL, ToolID::RESIZE_SYMBOL},
{"Resize symbol", ActionGroup::SYMBOL, ActionCatalogItem::AVAILABLE_IN_SYMBOL,
ActionCatalogItem::FLAGS_DEFAULT}},

{{ActionID::TOOL, ToolID::ROUND_OFF_VERTEX},
{"Round off vertex", ActionGroup::GRAPHICS, ActionCatalogItem::AVAILABLE_EVERYWHERE,
ActionCatalogItem::FLAGS_DEFAULT}},
};

const std::vector<std::pair<ActionGroup, std::string>> action_group_catalog = {
Expand Down Expand Up @@ -1026,5 +1030,6 @@ const LutEnumStr<ToolID> tool_lut = {
TOOL_LUT_ITEM(SMASH_PANEL_OUTLINE),
TOOL_LUT_ITEM(SMASH_PACKAGE_OUTLINE),
TOOL_LUT_ITEM(RESIZE_SYMBOL),
TOOL_LUT_ITEM(ROUND_OFF_VERTEX),
};
} // namespace horizon

6 comments on commit b028dbe

@atoav
Copy link
Contributor

@atoav atoav commented on b028dbe Apr 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great and welcome addition.

Two comments – right now we can set the radius via:

  • Enter: set absolute radius
  • LMB: Radius point

I'd really love it if additionally we could:

  • Scroll: change radius in realtime

Bonus points for seting the radius in 0.1 mm increments per default and making it finer with Alt modifier.

What do you think? This could be a model for trace rounding as well (but there we have a routing issue, I know...)

@fruchti
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really love it if additionally we could:

* Scroll: change radius in realtime

I don't think that would be too intuitive. I'd expect the scroll wheel to zoom the board view as it does with other tools active. Especially if the tool is later extended to work with multiple vertices at once, you'd lose the ability to quickly zoom in on different corners of a shape while adjusting the corner radius.

@carrotIndustries
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have an idea that also solves the scrolling issue: Instead of making the dialog for setting the radius a modal window, make it a non-modal window so that the user can change the radius with the scroll wheel and observe the change in real time.
Clicking cancel will return to setting the radius by mouse, clicking Okay will end the tool with the current radius.

@atoav
Copy link
Contributor

@atoav atoav commented on b028dbe Apr 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so that the user can change the radius with the scroll wheel and observe the change in real time

If you scroll and the screen doesn't change you will see the radius of the selected vertecies change. So hitting Escto abort the action should be really the intuitive way to get out of this.

I didn't even think this would need a window to be honest, although I'd have nothing against it either.

@carrotIndustries
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dunno, but scrolling anywhere on the screen to change a parameter strikes me as a bit odd. I guess this is a blender thing, right? Zooming and panning should be possible regardless of any mode the user is in. With the window, users get something they're already somewhat familiar with.

@atoav
Copy link
Contributor

@atoav atoav commented on b028dbe Apr 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is a blender thing, right?

You are right, their Bevel tool (more or less a round tool for polygon edges) uses this – but for another parameter: by scrolling you dial in the number of steps the polygon arc uses (which is more important in 3D I guess). Navigating the space is even more important in a 3D application, but it never remotely bothered me that the scroll wheel is captured there, I always found it more intuitive and quick than setting a number in a window especially if the helper bar on the bottom also tells you what scrolling does. If you really need to zoom in or out you hit Esc scroll out and fire up that tool again.

However I understand if that is not your preference, as I said I'd have nothing against a window, the most important thing to me is beeing able to judge the result in realtime with having actual numbers displayed.

Please sign in to comment.