Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update round off vertex to support modification/removal #735

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/common/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,40 @@ constexpr shallow_copy_t shallow_copy = shallow_copy_t();

enum class CopyMode { DEEP, SHALLOW };


template <typename T> class Segment {
public:
Coord<T> from;
Coord<T> to;

Segment(Coord<T> a, Coord<T> b) : from(a), to(b)
{
}

bool intersect(const Segment<T> &other, Coord<T> &output) const
{
const T &x1 = from.x;
const T &y1 = from.y;
const T &x2 = to.x;
const T &y2 = to.y;
const T &x3 = other.from.x;
const T &y3 = other.from.y;
const T &x4 = other.to.x;
const T &y4 = other.to.y;

// See https://en.wikipedia.org/wiki/Line-line_intersection
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (fabs(d) < 1e-6) {
return false; // Parallel or coincident
}
T a = (x1 * y2 - y1 * x2);
T b = (x3 * y4 - y3 * x4);
double x = (a * (x3 - x4) - (x1 - x2) * b) / d;
double y = (a * (y3 - y4) - (y1 - y2) * b) / d;
output.x = x;
output.y = y;
return true;
}
};

} // namespace horizon
116 changes: 89 additions & 27 deletions src/core/tools/tool_round_off_vertex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace horizon {

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

int ToolRoundOffVertex::wrap_index(int i) const
Expand All @@ -27,50 +28,88 @@ int ToolRoundOffVertex::wrap_index(int i) const
ToolResponse ToolRoundOffVertex::begin(const ToolArgs &args)
{
int vertex_idx = 0;
int v_inserted = 0;
bool selected_center = sel_has_type(selection, ObjectType::POLYGON_ARC_CENTER);
{
auto x = sel_find_one(selection, ObjectType::POLYGON_VERTEX);
ObjectType type = selected_center ? ObjectType::POLYGON_ARC_CENTER : ObjectType::POLYGON_VERTEX;
auto x = sel_find_one(selection, type);
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();
if (poly->vertices.at(vertex_idx).type == Polygon::Vertex::Type::ARC) {
// Selected center of existing arc or start vertex of arc
editing = true;
v_inserted = v_next;
v_next = wrap_index(v_inserted + 1);
}
else if (poly->vertices.at(v_prev).type == Polygon::Vertex::Type::ARC) {
// Selected second vertex of arc
// shift vertex_idx so it matches the case above
editing = true;
v_inserted = vertex_idx;
vertex_idx = v_prev;
v_next = wrap_index(v_inserted + 1);
v_prev = wrap_index(vertex_idx - 1);
}
else {
editing = false;
}

Coordd p1 = poly->vertices.at(v_next).position;
Coordd p2 = poly->vertices.at(v_prev).position;

if (editing) {
// Update tool's p0, vxp, and vxn
if ((poly->vertices.at(vertex_idx).type != Polygon::Vertex::Type::ARC)
|| (poly->vertices.at(v_inserted).type == Polygon::Vertex::Type::ARC)) {
// Should be an arc and non-arc vertex... something is wrong
imp->tool_bar_flash("can't round off arc");
return ToolResponse::end();
}

vxp = &poly->vertices.at(vertex_idx);
vxn = &poly->vertices.at(v_inserted);

if (!revert_arc(p0, vxp->position, vxn->position, vxp->arc_center)) {
imp->tool_bar_flash("can't undo arc");
return ToolResponse::end();
}
}
else {
// Insert arc
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;
p0 = poly->vertices.at(vertex_idx).position;
}
selection.clear();

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

delta_max = std::min((poly->vertices.at(v_next).position - poly->vertices.at(vertex_idx).position).magd(),
(poly->vertices.at(v_prev).position - poly->vertices.at(vertex_idx).position).magd());
if (!editing) {
const bool rev = vn.cross(vp) < 0;
vxp->arc_reverse = rev;
}

delta_max = std::min((p1 - p0).mag(), (p2 - p0).mag());
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();
}
r_max = tan(alpha) * delta_max;

const bool rev = vn.cross(vp) < 0;

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;

imp->set_snap_filter({{ObjectType::POLYGON, poly->uuid}});

update_cursor(args.coords);
Expand Down Expand Up @@ -122,7 +161,7 @@ ToolResponse ToolRoundOffVertex::update(const ToolArgs &args)
else if (args.type == ToolEventType::ACTION) {
switch (args.action) {
case InToolActionID::LMB:
if (plane_finish())
if (commit())
return ToolResponse::commit();
else
return ToolResponse::revert();
Expand Down Expand Up @@ -153,7 +192,7 @@ ToolResponse ToolRoundOffVertex::update(const ToolArgs &args)
}
else if (data->event == ToolDataWindow::Event::OK) {
imp->dialogs.close_nonmodal();
if (plane_finish())
if (commit())
return ToolResponse::commit();
else
return ToolResponse::revert();
Expand All @@ -162,4 +201,27 @@ ToolResponse ToolRoundOffVertex::update(const ToolArgs &args)
}
return ToolResponse();
}

bool ToolRoundOffVertex::commit()
{
if (radius_current < 1) {
// Tiny radius, just ignore it
if (!editing)
return false; // Not a modification so just revert the action

// Undo change to original vertex
vxp->arc_center = Coordi();
vxp->arc_reverse = false;
vxp->position = p0.to_coordi();
vxp->type = Polygon::Vertex::Type::LINE;

// Remove inserted vertex
vxn->remove = true;
poly->vertices.erase(
std::remove_if(poly->vertices.begin(), poly->vertices.end(), [](const auto &x) { return x.remove; }),
poly->vertices.end());
}
return plane_finish();
}

} // namespace horizon
2 changes: 2 additions & 0 deletions src/core/tools/tool_round_off_vertex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ class ToolRoundOffVertex : public virtual ToolBase, public ToolHelperPlane {
double r_max = 0;
double alpha = 0;
double radius_current = 0;
bool editing = false;

void update_poly(double r);
void update_cursor(const Coordi &c);
void update_tip();
bool commit();
};
} // namespace horizon
20 changes: 20 additions & 0 deletions src/util/geom_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,24 @@ Placement transform_text_placement_to_new_reference(Placement pl, Placement old_
return out;
}


/**
* Determine original point that the arc rounded off. If the parameters are
* invalid or it cannot determine the proper value return false.
*/
bool revert_arc(Coordd &output, const Coordd &p0, const Coordd &p1, const Coordd &c)
{
// The original point position is found by computing the intersection of
// lines defined by each arc point and their tangents. Tangents are of an
// arc perpendicular to vector to center.
const Coordd v0 = (p0 - c);
const Coordd v1 = (p1 - c);
double a = v0.cross(v1) > 0 ? M_PI / 2 : -M_PI / 2; // CW / CCW
const Coordd t0 = v0.rotate(a);
const Coordd t1 = v1.rotate(-a);
Segment<double> l0 = Segment<double>(p0, t0 + p0);
Segment<double> l1 = Segment<double>(p1, t1 + p1);
return l0.intersect(l1, output);
}

} // namespace horizon
2 changes: 2 additions & 0 deletions src/util/geom_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ template <typename T> T c2pi(T x);
Placement transform_package_placement_to_new_reference(Placement pl, Placement old_ref, Placement new_ref);
Placement transform_text_placement_to_new_reference(Placement pl, Placement old_ref, Placement new_ref);

bool revert_arc(Coordd &output, const Coordd &p0, const Coordd &p1, const Coordd &c);

} // namespace horizon
Loading