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;
494 intersections_max_per_segment);
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() :
511 dst_points.
append({src_point,
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});
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;
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) {
583 EraserRing next_sample = eraser_rings[sample_index + 1];
595 const EraserRing &sample_after = eraser_rings[sample_index + 1];
604 sample.squared_radius = radius * radius;
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;
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;
678 intersections_max_per_segment);
680 src, screen_space_positions, eraser_rings, src_point_ring, src_intersections);
685 const auto compute_opacity = [&](
const int 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 &&
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;
773 dst_opacity.finish();
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;
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]);
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()]);
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);
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_) {
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);
988 radius_ =
paint->eraser_brush->size;
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;
1037 if (point_was_inserted.
is_empty()) {
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);
1065 range_to_simplify, epsilon, opacity_distance, remove_points);
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
BPy_StructRNA * depsgraph
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
Span< T > as_span() const
constexpr int64_t one_before_start() 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
friend struct EraseOperationExecutor
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
draw_view in_light_buf[] float
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval, const Object &ob_orig, int layer_index, int frame)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
int64_t ramer_douglas_peucker_simplify(const IndexRange range, const float epsilon, const FunctionRef< float(int64_t, int64_t, int64_t)> dist_function, MutableSpan< bool > points_to_delete)
Array< PointTransferData > compute_topology_change(const bke::CurvesGeometry &src, bke::CurvesGeometry &dst, const Span< Vector< PointTransferData > > src_to_dst_points, const bool keep_caps)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
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_each(Range &&range, const Function &function)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
float distance(float a, float b)
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 ToolSettings * toolsettings
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)