50 bool temp_eraser_ =
false;
52 bool keep_caps_ =
false;
53 float radius_ = 50.0f;
54 float strength_ = 0.1f;
56 bool active_layer_only_ =
false;
132 const int64_t c = d_s0_center - radius_2;
166 const float mu0_f = -
b / (2.0f * a);
173 const float mu0_f = (-
b + i_sqrt) / (2.0f * a);
174 const float mu1_f = (-
b - i_sqrt) / (2.0f * a);
202 const int2 &point_after,
215 point, point_after, this->mouse_position_pixels, squared_radius, mu0, mu1);
217 if (nb_intersections != 2) {
222 r_mu0 = r_mu1 = -1.0f;
237 const int8_t side_mu0 = (mu0 <= 0) ? (-1) : ((mu0 >= segment_length) ? 1 : 0);
238 const int8_t side_mu1 = (mu1 <= 0) ? (-1) : ((mu1 >= segment_length) ? 1 : 0);
245 r_point_after_side = (mu0 == segment_length) ?
251 r_mu0 = mu0 /
float(segment_length);
252 r_mu1 = mu1 /
float(segment_length);
254 const bool is_mu0_inside = (side_mu0 == 0);
255 const bool is_mu1_inside = (side_mu1 == 0);
256 if (!is_mu0_inside && !is_mu1_inside) {
259 if (side_mu0 == side_mu1) {
272 if (is_mu0_inside && is_mu1_inside) {
281 const int8_t side_outside_intersection = is_mu0_inside ? side_mu1 : side_mu0;
285 r_point_after_side = (side_outside_intersection == 1) ? r_point_after_side :
289 std::swap(r_mu0, r_mu1);
317 MutableSpan<std::pair<int, PointCircleSide>> r_point_ring,
321 const int intersections_max_per_segment = rings.
size() * 2;
327 for (const int src_point : src_points) {
328 const float2 pos = screen_space_positions[src_point];
329 screen_space_positions_pixel[src_point] = int2(round_fl_to_int(pos[0]),
330 round_fl_to_int(pos[1]));
335 for (const int src_curve : src_curves) {
336 const IndexRange src_curve_points = src_points_by_curve[src_curve];
338 if (src_curve_points.size() == 1) {
341 for (const EraserRing &eraser_point : rings) {
342 const int src_point = src_curve_points.first();
343 const int64_t squared_distance = math::distance_squared(
344 this->mouse_position_pixels, screen_space_positions_pixel[src_point]);
348 if ((r_point_ring[src_point].first == -1) &&
349 (squared_distance <= eraser_point.squared_radius))
351 r_point_ring[src_point] = {ring_index, PointCircleSide::Inside};
358 for (const int src_point : src_curve_points.drop_back(1)) {
360 int intersection_offset = src_point * intersections_max_per_segment - 1;
362 for (const EraserRing &eraser_point : rings) {
363 SegmentCircleIntersection inter0;
364 SegmentCircleIntersection inter1;
366 inter0.ring_index = ring_index;
367 inter1.ring_index = ring_index;
369 PointCircleSide point_side;
370 PointCircleSide point_after_side;
372 const int8_t nb_inter = segment_intersections_and_points_sides(
373 screen_space_positions_pixel[src_point],
374 screen_space_positions_pixel[src_point + 1],
375 eraser_point.squared_radius,
386 if ((r_point_ring[src_point].first == -1) &&
387 ELEM(point_side, PointCircleSide::Inside, PointCircleSide::InsideOutsideBoundary))
389 r_point_ring[src_point] = {ring_index, point_side};
392 if (src_point + 1 == src_curve_points.last()) {
393 if ((r_point_ring[src_point + 1].first == -1) &&
394 ELEM(point_after_side,
395 PointCircleSide::Inside,
396 PointCircleSide::InsideOutsideBoundary))
398 r_point_ring[src_point + 1] = {ring_index, point_after_side};
403 inter0.inside_outside_intersection = (inter0.factor > inter1.factor);
404 r_intersections[++intersection_offset] = inter0;
407 inter1.inside_outside_intersection = true;
408 r_intersections[++intersection_offset] = inter1;
416 if (src_cyclic[src_curve]) {
418 const int src_last_point = src_curve_points.last();
419 const int src_first_point = src_curve_points.first();
421 int intersection_offset = src_last_point * intersections_max_per_segment - 1;
423 for (const EraserRing &eraser_point : rings) {
424 SegmentCircleIntersection inter0;
425 SegmentCircleIntersection inter1;
427 inter0.ring_index = ring_index;
428 inter1.ring_index = ring_index;
430 PointCircleSide point_side;
431 PointCircleSide point_after_side;
433 const int8_t nb_inter = segment_intersections_and_points_sides(
434 screen_space_positions_pixel[src_last_point],
435 screen_space_positions_pixel[src_first_point],
436 eraser_point.squared_radius,
446 inter0.inside_outside_intersection = (inter0.factor > inter1.factor);
447 r_intersections[++intersection_offset] = inter0;
450 inter1.inside_outside_intersection = true;
451 r_intersections[++intersection_offset] = inter1;
462 int total_intersections = 0;
463 for (
const SegmentCircleIntersection &intersection : r_intersections) {
464 if (intersection.is_valid()) {
465 total_intersections++;
469 return total_intersections;
479 const bool keep_caps)
const
487 1, {this->eraser_radius, this->eraser_squared_radius_pixels, 0.0f,
true});
488 const int intersections_max_per_segment = eraser_rings.
size() * 2;
492 {-1, PointCircleSide::Outside});
494 intersections_max_per_segment);
495 curves_intersections_and_points_sides(
496 src, screen_space_positions, eraser_rings, src_point_ring, src_intersections);
501 const IndexRange src_points = src_points_by_curve[src_curve];
503 for (
const int src_point : src_points) {
505 const int src_next_point = (src_point == src_points.
last()) ? src_points.
first() :
510 if (point_side != PointCircleSide::Inside) {
511 dst_points.
append({src_point,
515 (point_side == PointCircleSide::InsideOutsideBoundary)});
519 const IndexRange src_point_intersections(src_point * intersections_max_per_segment,
520 intersections_max_per_segment);
522 src_intersections.
as_span().slice(src_point_intersections))
524 if (!intersection.is_valid()) {
528 dst_points.
append({src_point,
532 intersection.inside_outside_intersection});
537 ed::greasepencil::compute_topology_change(src, dst, src_to_dst_points, keep_caps);
557 const int step_pixels = 2;
558 int nb_samples =
math::round(this->eraser_radius / step_pixels);
560 for (
const int sample_index : eraser_rings.
index_range()) {
561 const int64_t sampled_distance = (sample_index + 1) * step_pixels;
563 EraserRing &ring = eraser_rings[sample_index];
564 ring.radius = sampled_distance;
565 ring.squared_radius = sampled_distance * sampled_distance;
566 ring.opacity = 1.0 - this->eraser_strength *
568 this->brush_,
float(sampled_distance), this->eraser_radius);
573 for (
const int sample_index : eraser_rings.
index_range()) {
576 if (sample_index == nb_samples - 1) {
579 sample.hard_erase = (
sample.opacity < opacity_threshold);
583 EraserRing next_sample = eraser_rings[sample_index + 1];
588 if ((
sample.opacity < opacity_threshold) == (next_sample.
opacity < opacity_threshold)) {
589 prune_sample[sample_index] = (
sample.opacity < opacity_threshold);
595 const EraserRing &sample_after = eraser_rings[sample_index + 1];
597 const float t = (opacity_threshold -
sample.opacity) /
604 sample.squared_radius = radius * radius;
605 sample.opacity = opacity_threshold;
606 sample.hard_erase = !(next_sample.
opacity < opacity_threshold);
609 for (
const int rev_sample_index : eraser_rings.
index_range()) {
610 const int sample_index = nb_samples - rev_sample_index - 1;
611 if (prune_sample[sample_index]) {
612 eraser_rings.
remove(sample_index);
617 nb_samples = eraser_rings.
size();
623 const EraserRing &sample_first = eraser_rings[first_index];
624 const EraserRing &sample_last = eraser_rings[last_index];
638 const float distance_threshold = 0.1f;
639 ed::greasepencil::ramer_douglas_peucker_simplify(
640 eraser_rings.
index_range(), distance_threshold, opacity_distance, simplify_sample);
642 for (
const int rev_sample_index : eraser_rings.
index_range()) {
643 const int sample_index = nb_samples - rev_sample_index - 1;
644 if (simplify_sample[sample_index]) {
645 eraser_rings.
remove(sample_index);
662 const bool keep_caps)
665 const std::string opacity_attr =
"opacity";
670 const int intersections_max_per_segment = eraser_rings.
size() * 2;
676 {-1, PointCircleSide::Outside});
678 intersections_max_per_segment);
679 curves_intersections_and_points_sides(
680 src, screen_space_positions, eraser_rings, src_point_ring, src_intersections);
684 opacity_attr, bke::AttrDomain::Point, 1.0f);
685 const auto compute_opacity = [&](
const int src_point) {
686 const float distance =
math::distance(screen_space_positions[src_point],
687 this->mouse_position);
690 this->brush_, distance, this->eraser_radius);
702 const IndexRange src_points = src_points_by_curve[src_curve];
704 for (
const int src_point : src_points) {
706 const int src_next_point = (src_point == src_points.
last()) ? src_points.
first() :
711 const int point_ring = src_point_ring[src_point].first;
712 const bool ring_is_cut = (point_ring != -1) && eraser_rings[point_ring].hard_erase;
715 const bool point_is_cut = ring_is_cut &&
716 (point_side == PointCircleSide::InsideOutsideBoundary);
717 const bool remove_point = ring_is_cut && (point_side == PointCircleSide::Inside);
720 {src_point, src_next_point, 0.0f,
true, point_is_cut, compute_opacity(src_point)});
723 const IndexRange src_point_intersections(src_point * intersections_max_per_segment,
724 intersections_max_per_segment);
726 std::sort(src_intersections.
begin() + src_point_intersections.
first(),
727 src_intersections.
begin() + src_point_intersections.
last() + 1,
729 return a.factor < b.factor;
734 src_intersections.
as_span().slice(src_point_intersections))
736 if (!intersection.is_valid()) {
741 const EraserRing &ring = eraser_rings[intersection.ring_index];
742 const bool is_cut = intersection.inside_outside_intersection && ring.hard_erase;
744 src_opacity[src_point], src_opacity[src_next_point], intersection.factor);
749 if (is_cut && !dst_points.
is_empty() && dst_points.
last().is_cut) {
754 {src_point, src_next_point, intersection.factor,
false, is_cut, opacity});
760 src, dst, src_to_dst_points, keep_caps);
768 for (const int dst_point_index : dst_points_range) {
769 const ed::greasepencil::PointTransferData &dst_point = dst_points[dst_point_index];
770 dst_opacity.span[dst_point_index] = dst_point.opacity;
776 "_eraser_inserted", bke::AttrDomain::Point);
778 threading::parallel_for(dst.curves_range(), 4096, [&](
const IndexRange dst_curves_range) {
779 for (const int dst_curve : dst_curves_range) {
780 IndexRange dst_points_range = dst_points_by_curve[dst_curve];
782 dst_inserted.span[dst_points_range.first()] = false;
783 dst_inserted.span[dst_points_range.last()] = false;
785 if (dst_points_range.size() < 3) {
789 for (const int dst_point_index : dst_points_range.drop_back(1).drop_front(1)) {
790 const ed::greasepencil::PointTransferData &dst_point = dst_points[dst_point_index];
791 dst_inserted.span[dst_point_index] |= !dst_point.is_src_point;
808 const IndexMask strokes_to_keep = IndexMask::from_predicate(
810 const IndexRange src_curve_points = src_points_by_curve[src_curve];
813 if (src_curve_points.
size() == 1) {
814 const float2 &point_pos = screen_space_positions[src_curve_points.first()];
815 const float dist_to_eraser = math::distance(point_pos, this->mouse_position);
816 return !(dist_to_eraser < this->eraser_radius);
821 for (
const int src_point : src_curve_points.
drop_back(1)) {
823 this->mouse_position,
824 screen_space_positions[src_point],
825 screen_space_positions[src_point + 1]);
826 if (dist_to_eraser < this->eraser_radius) {
831 if (src_cyclic[src_curve]) {
833 this->mouse_position,
834 screen_space_positions[src_curve_points.
first()],
835 screen_space_positions[src_curve_points.
last()]);
836 if (dist_to_eraser < this->eraser_radius) {
848 dst = bke::curves_copy_curve_selection(src, strokes_to_keep, {});
861 Paint *paint = &scene->toolsettings->gp_paint->paint;
870 this->eraser_radius =
self.radius_;
871 this->eraser_strength =
self.strength_;
881 this->brush_ = brush;
886 this->eraser_squared_radius_pixels = eraser_radius_pixels * eraser_radius_pixels;
891 bool changed =
false;
892 const auto execute_eraser_on_drawing =
893 [&](
const int layer_index,
const int frame_number,
Drawing &drawing) {
894 const Layer &layer = grease_pencil.layer(layer_index);
899 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
900 ob_eval, *obact, layer_index, frame_number);
905 for (const int src_point : src_points) {
906 const int result = ED_view3d_project_float_global(
908 math::transform_point(layer.to_world_space(*ob_eval),
909 deformation.positions[src_point]),
910 screen_space_positions[src_point],
911 V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_FAR);
912 if (result != V3D_PROJ_RET_OK) {
916 screen_space_positions[src_point] = float2(1e20);
924 switch (
self.eraser_mode_) {
926 erased = stroke_eraser(src, screen_space_positions, dst);
929 erased = hard_eraser(src, screen_space_positions, dst,
self.keep_caps_);
932 erased = soft_eraser(src, screen_space_positions, dst,
self.keep_caps_);
938 drawing.geometry.wrap() = std::move(dst);
939 drawing.tag_topology_changed();
941 self.affected_drawings_.add(&drawing);
945 if (
self.active_layer_only_) {
947 if (!grease_pencil.has_active_layer()) {
950 const Layer &active_layer = *grease_pencil.get_active_layer();
951 Drawing *drawing = grease_pencil.get_editable_drawing_at(active_layer, scene->r.cfra);
953 if (drawing ==
nullptr) {
957 execute_eraser_on_drawing(
958 *grease_pencil.get_layer_index(active_layer), scene->r.cfra, *drawing);
963 ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
964 threading::parallel_for_each(
989 grease_pencil->
runtime->temp_eraser_size = radius_;
990 grease_pencil->
runtime->temp_use_eraser =
true;
995 radius_ = brush->
size;
1009 strength_ = brush->
alpha;
1015 executor.
execute(*
this, C, extension_sample);
1025 grease_pencil.
runtime->temp_use_eraser =
false;
1026 grease_pencil.
runtime->temp_eraser_size = 0.0f;
1030 const float epsilon = 0.01f;
1035 const VArray<bool> &point_was_inserted = *curves.attributes().lookup<
bool>(
1036 "_eraser_inserted", bke::AttrDomain::Point);
1037 if (point_was_inserted.
is_empty()) {
1041 IndexMask inserted_points = IndexMask::from_bools(point_was_inserted, mem_inserted);
1049 const float3 &s0 = positions[first_index];
1050 const float3 &s1 = positions[last_index];
1052 if (segment_length < 1
e-6) {
1055 const float t =
math::distance(s0, positions[index]) / segment_length;
1057 opacities[first_index], opacities[last_index], t);
1058 return math::abs(opacities[index] - linear_opacity);
1061 Array<bool> remove_points(curves.points_num(),
false);
1063 IndexRange range_to_simplify(range.one_before_start(), range.size() + 2);
1064 ed::greasepencil::ramer_douglas_peucker_simplify(
1065 range_to_simplify, epsilon, opacity_distance, remove_points);
1070 IndexMask points_to_remove = IndexMask::from_bools(remove_points, mem_remove);
1072 curves.remove_points(points_to_remove, {});
1073 drawing_->wrap().tag_topology_changed();
1075 curves.attributes_for_write().remove(
"_eraser_inserted");
1078 affected_drawings_.clear();
1083 return std::make_unique<EraseOperation>(temp_eraser);
bool BKE_brush_use_alpha_pressure(const Brush *brush)
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
bool BKE_brush_use_size_pressure(const Brush *brush)
void BKE_brush_init_gpencil_settings(Brush *brush)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
Brush * BKE_paint_eraser_brush(Paint *paint)
Paint * BKE_paint_get_active_from_context(const bContext *C)
Brush * BKE_paint_brush(Paint *paint)
MINLINE int round_fl_to_int(float a)
float dist_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
#define IN_RANGE(a, b, c)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ GP_BRUSH_ERASER_KEEP_CAPS
@ GP_BRUSH_ACTIVE_LAYER_ONLY
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
Span< T > as_span() const
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
void remove(const int64_t index)
IndexRange index_range() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
IndexRange points_range() const
AttributeAccessor attributes() const
VArray< bool > cyclic() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
void on_stroke_done(const bContext &C) override
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
EraseOperation(bool temp_use_eraser)
~EraseOperation() override
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
void foreach_range(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
draw_view in_light_buf[] float
std::unique_ptr< GreasePencilStrokeOperation > new_erase_operation(const bool temp_eraser)
T clamp(const T &a, const T &min, const T &max)
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T min(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T max(const T &a, const T &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
static float brush_strength(const Sculpt &sd, const blender::ed::sculpt_paint::StrokeCache &cache, const float feather, const UnifiedPaintSettings &ups, const PaintModeSettings &)
struct CurveMapping * curve_strength
struct CurveMapping * curve
struct BrushGpencilSettings * gpencil_settings
GreasePencilRuntimeHandle * runtime
struct Brush * eraser_brush
bke::greasepencil::Drawing & drawing
bool stroke_eraser(const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, bke::CurvesGeometry &dst) const
static int8_t intersections_segment_circle_integers(const int2 &s0, const int2 &s1, const int2 ¢er, const int64_t radius_2, int64_t &r_mu0, int64_t &r_mu1)
static constexpr float opacity_threshold
Vector< EraserRing > compute_piecewise_linear_falloff() const
int64_t eraser_squared_radius_pixels
bool hard_eraser(const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, bke::CurvesGeometry &dst, const bool keep_caps) const
void execute(EraseOperation &self, const bContext &C, const InputSample &extension_sample)
int2 mouse_position_pixels
int8_t segment_intersections_and_points_sides(const int2 &point, const int2 &point_after, const int64_t squared_radius, float &r_mu0, float &r_mu1, PointCircleSide &r_point_side, PointCircleSide &r_point_after_side) const
int curves_intersections_and_points_sides(const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, const Span< EraserRing > rings, MutableSpan< std::pair< int, PointCircleSide > > r_point_ring, MutableSpan< SegmentCircleIntersection > r_intersections) const
EraseOperationExecutor(const bContext &)
bool soft_eraser(const blender::bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, blender::bke::CurvesGeometry &dst, const bool keep_caps)
bool inside_outside_intersection
void WM_event_add_notifier(const bContext *C, uint type, void *reference)