GPv3: Draw Tool: Refactor simplify_stroke

Previously the `simplify_stroke` stroke step used the screen
space coordinates to do the simplification.
Instead, this is now refactored to use the more general
`geometry::simplify_curve_attribute`. This means we can
simplify the stroke based on the positions and radii.

The simplify step is also now hooked up to the simplify option
in the tool settings (under the `Stroke` panel).
This commit is contained in:
Falk David 2024-05-27 17:23:49 +02:00
parent 2c44789bc3
commit 7b66e2274b

@ -26,6 +26,7 @@
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "GEO_simplify_curves.hh"
#include "GEO_smooth_curves.hh"
#include "WM_api.hh"
@ -705,55 +706,41 @@ void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &ex
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
}
static float dist_to_interpolated_2d(
float2 pos, float2 posA, float2 posB, float val, float valA, float valB)
{
const float dist1 = math::distance_squared(posA, pos);
const float dist2 = math::distance_squared(posB, pos);
if (dist1 + dist2 > 1e-5f) {
const float interpolated_val = math::interpolate(valB, valA, dist1 / (dist1 + dist2));
return math::distance(interpolated_val, val);
}
return 0.0f;
}
void PaintOperation::simplify_stroke(const bContext &C,
bke::greasepencil::Drawing &drawing,
const float epsilon_px)
const float epsilon)
{
const Scene *scene = CTX_data_scene(&C);
const bke::CurvesGeometry &curves = drawing.strokes();
const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
const int active_curve = on_back ? drawing.strokes().curves_range().first() :
drawing.strokes().curves_range().last();
const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const int active_curve = on_back ? curves.curves_range().first() : curves.curves_range().last();
IndexMaskMemory memory;
IndexMask points_to_delete = geometry::simplify_curve_attribute(
curves.positions(),
IndexRange::from_single(active_curve),
curves.points_by_curve(),
curves.cyclic(),
epsilon,
curves.positions(),
memory);
const VArray<float> radii = drawing.radii();
if (!radii.is_empty() && radii.is_span()) {
const IndexMask radii_mask = geometry::simplify_curve_attribute(
curves.positions(),
IndexRange::from_single(active_curve),
curves.points_by_curve(),
curves.cyclic(),
epsilon,
radii.get_internal_span(),
memory);
points_to_delete = IndexMask::from_intersection(points_to_delete, radii_mask, memory);
}
/* Distance function for `ramer_douglas_peucker_simplify`. */
const Span<float2> positions_2d = this->screen_space_smoothed_coords_.as_span();
const auto dist_function = [&](int64_t first_index, int64_t last_index, int64_t index) {
/* 2D coordinates are only stored for the current stroke, so offset the indices. */
const float dist_position_px = dist_to_line_segment_v2(
positions_2d[index - points.first()],
positions_2d[first_index - points.first()],
positions_2d[last_index - points.first()]);
const float dist_radii_px = dist_to_interpolated_2d(positions_2d[index - points.first()],
positions_2d[first_index - points.first()],
positions_2d[last_index - points.first()],
radii[index],
radii[first_index],
radii[last_index]);
return math::max(dist_position_px, dist_radii_px);
};
Array<bool> points_to_delete(curves.points_num(), false);
int64_t total_points_to_delete = ed::greasepencil::ramer_douglas_peucker_simplify(
points, epsilon_px, dist_function, points_to_delete.as_mutable_span());
if (total_points_to_delete > 0) {
IndexMaskMemory memory;
curves.remove_points(IndexMask::from_bools(points_to_delete, memory), {});
if (!points_to_delete.is_empty()) {
drawing.strokes_for_write().remove_points(points_to_delete, {});
}
}
@ -867,8 +854,7 @@ void PaintOperation::on_stroke_done(const bContext &C)
bke::greasepencil::Drawing &drawing = *grease_pencil.get_editable_drawing_at(active_layer,
scene->r.cfra);
if (do_post_processing) {
const float simplifiy_threshold_px = 0.5f;
this->simplify_stroke(C, drawing, simplifiy_threshold_px);
this->simplify_stroke(C, drawing, settings->simplify_f);
}
this->process_stroke_end(C, drawing);
drawing.tag_topology_changed();