34 N_(
"Scale elements by the same factor in every direction")},
39 N_(
"Scale elements in a single direction")},
40 {0,
nullptr, 0,
nullptr,
nullptr},
45 b.use_custom_socket_order();
46 b.allow_any_socket_order();
47 b.add_default_layout();
49 .supported_type(GeometryComponent::Type::Mesh)
51 b.add_output<
decl::Geometry>(
"Geometry").propagate_all().align_with_previous();
54 b.add_input<
decl::Float>(
"Scale",
"Scale").default_value(1.0f).min(0.0f).field_on_all();
59 "Origin of the scaling for each element. If multiple elements are connected, their "
60 "center is averaged");
66 .default_value({1.0f, 0.0f, 0.0f})
68 .
description(
"Direction in which to scale the element")
79 node->
custom1 = int16_t(AttrDomain::Face);
91 const int value = values[
indices.first()];
92 const int &first_other = *std::find_if(
93 indices.begin(),
indices.end(), [&](
const int index) { return values[index] != value; });
101 constexpr const int segment_size = 1024;
102 constexpr const IndexRange segment(segment_size);
103 const bool last_small_segmet = bool(group_indices.
size() % segment_size);
104 const int total_segments = group_indices.
size() / segment_size + int(last_small_segmet);
108 const IndexRange range = segment.shift(segment_size * segment_index);
110 std::iota(segment_indices.
begin(), segment_indices.
end(), segment_size * segment_index);
112 return group_indices[a] < group_indices[b];
116 const int group = group_indices[
indices.first()];
127 const IndexRange range = segment.shift(segment_size * segment_index);
132 const int step_size = indices_of_current_group.
size();
133 const int group = group_indices[
indices.first()];
159 for (const int64_t i : range) {
160 const int group_index = group_indices[i];
161 const int index_in_group = atomic_fetch_and_add_int32(&counts[group_index], 1);
162 results[offsets[group_index][index_in_group]] = int(i);
169 const int groups_num,
173 if (group_indices.
size() / groups_num > 1000) {
193 using MeanAccumulator = std::pair<T, int>;
194 const auto join_accumulators = [](
const MeanAccumulator a,
195 const MeanAccumulator
b) -> MeanAccumulator {
196 return {(a.first +
b.first) / (a.second +
b.second), 1};
204 MeanAccumulator(
T(), 0),
205 [&](
const IndexRange range, MeanAccumulator other) -> MeanAccumulator {
207 for (
const int i :
indices.slice(range)) {
210 return join_accumulators({value, int(range.
size())}, other);
213 value = accumulator.first / accumulator.second;
224 const float3 new_position = center + scaled_diff;
239 for (const int island_index : range) {
240 const Span<int> vert_island = vert_islands[island_index];
241 const Span<int> elem_island = elem_islands[island_index];
243 const float scale = gather_mean<float>(scale_varray, elem_island);
244 const float3 center = gather_mean<float3>(center_varray, elem_island);
246 threading::parallel_for(vert_island.index_range(), 2048, [&](const IndexRange range) {
247 for (const int vert_i : vert_island.slice(range)) {
248 positions[vert_i] = transform_with_uniform_scale(positions[vert_i], center, scale);
254 return elem_islands.offsets[range].size() + vert_islands.offsets[range].size();
280 base_change.
x_axis() = x_axis;
281 base_change.
y_axis() = y_axis;
282 base_change.
z_axis() = z_axis;
288 scale_transform[0][0] = scale;
310 for (const int island_index : range) {
311 const Span<int> vert_island = vert_islands[island_index];
312 const Span<int> elem_island = elem_islands[island_index];
314 const float scale = gather_mean<float>(scale_varray, elem_island);
315 const float3 center = gather_mean<float3>(center_varray, elem_island);
316 const float3 axis = gather_mean<float3>(axis_varray, elem_island);
317 const float3 fixed_axis = math::is_zero(axis) ? float3(1.0f, 0.0f, 0.0f) : axis;
319 const float4x4 transform = create_single_axis_transform(center, fixed_axis, scale);
320 threading::parallel_for(vert_island.index_range(), 2048, [&](const IndexRange range) {
321 for (const int vert_i : vert_island.slice(range)) {
322 positions[vert_i] = math::transform_point(transform, positions[vert_i]);
328 return vert_islands.offsets[range].size() + elem_islands.offsets[range].size();
346 const int v1 = verts_pos[
verts.first()];
347 for (
const int vert_i :
verts.drop_front(1)) {
348 const int v2 = verts_pos[vert_i];
349 disjoint_set.
join(v1,
v2);
356 const int face_vert_i = face_verts[face_i].first();
357 const int vert_pos = verts_pos[face_vert_i];
358 const int vert_island = vert_island_indices[vert_pos];
359 face_island_indices[face_pos] = vert_island;
374 mesh.face_offsets(), face_mask, mesh.corner_verts(), mesh.
verts_num, memory);
379 mesh, face_mask, vert_mask, face_island_indices, vert_island_indices);
382 gather_groups(vert_island_indices, total_islands, r_vert_offsets, r_vert_indices);
383 gather_groups(face_island_indices, total_islands, r_item_offsets, r_item_indices);
413 const int2 edge = edges[edge_i];
414 const int v1 = verts_pos[edge[0]];
415 const int v2 = verts_pos[edge[1]];
416 disjoint_set.
join(v1,
v2);
422 const int2 edge = edges[edge_i];
423 const int edge_vert_i = edge[0];
424 const int vert_pos = verts_pos[edge_vert_i];
425 const int vert_island = vert_island_indices[vert_pos];
426 edge_island_indices[edge_pos] = vert_island;
441 mesh.edges(), edge_mask, mesh.
verts_num, memory);
446 mesh, edge_mask, vert_mask, edge_island_indices, vert_island_indices);
449 gather_groups(vert_island_indices, total_islands, r_vert_offsets, r_vert_indices);
450 gather_groups(edge_island_indices, total_islands, r_item_offsets, r_item_indices);
482 FieldEvaluator evaluator{context, mesh->attributes().domain_size(domain)};
484 evaluator.
add(scale_field);
485 evaluator.
add(center_field);
491 if (
mask.is_empty()) {
502 case AttrDomain::Face:
505 case AttrDomain::Edge:
518 switch (scale_mode) {
520 scale_uniformly(item_islands, vert_islands, scale_varray, center_varray, *mesh);
525 item_islands, vert_islands, scale_varray, center_varray, axis_varray, *mesh);
529 mesh->tag_positions_changed();
539 {int(AttrDomain::Face),
543 "Scale individual faces or neighboring face islands"},
544 {int(AttrDomain::Edge),
548 "Scale individual edges or neighboring edge islands"},
549 {0,
nullptr, 0,
nullptr,
nullptr},
555 "Element type to transform",
558 int(AttrDomain::Face));
566 ntype.
ui_name =
"Scale Elements";
567 ntype.
ui_description =
"Scale groups of connected edges and faces";
#define NODE_CLASS_GEOMETRY
#define GEO_NODE_SCALE_ELEMENTS
#define BLI_assert_unreachable()
#define BLI_SCOPED_DEFER(function_to_defer)
@ NODE_DEFAULT_INPUT_POSITION_FIELD
GeometryNodeScaleElementsMode
@ GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS
@ GEO_NODE_SCALE_ELEMENTS_UNIFORM
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
constexpr int64_t size() const
Span< T > as_span() const
MutableSpan< T > as_mutable_span()
void reinitialize(const int64_t new_size)
int calc_reduced_ids(MutableSpan< int > result) const
constexpr int64_t size() const
constexpr IndexRange slice(int64_t start, int64_t size) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr T * end() const
constexpr T * begin() const
constexpr int64_t size() const
constexpr const T * end() const
constexpr IndexRange index_range() const
constexpr const T * begin() const
constexpr bool is_empty() const
std::optional< T > get_if_single() const
void set_selection(Field< bool > selection)
int add(GField field, GVArray *varray_ptr)
IndexMask get_evaluated_selection_as_mask() const
const GVArray & get_evaluated(const int field_index) const
void to_indices(MutableSpan< T > r_indices) const
void foreach_index_optimized(Fn &&fn) const
int64_t min_array_size() const
void foreach_index(Fn &&fn) const
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
VecBase< float, 3 > float3
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
void MEM_freeN(void *vmemh)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void node_register_type(bNodeType &ntype)
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
IndexMask vert_selection_from_face(OffsetIndices< int > faces, const IndexMask &face_mask, Span< int > corner_verts, int verts_num, IndexMaskMemory &memory)
IndexMask vert_selection_from_edge(Span< int2 > edges, const IndexMask &edge_mask, int verts_num, IndexMaskMemory &memory)
void build_reverse_map(const IndexMask &mask, MutableSpan< T > r_map)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
static Array< int > reverse_indices_in_groups(const Span< int > group_indices, const OffsetIndices< int > offsets)
static int face_to_vert_islands(const Mesh &mesh, const IndexMask &face_mask, const IndexMask &vert_mask, MutableSpan< int > face_island_indices, MutableSpan< int > vert_island_indices)
static void node_rna(StructRNA *srna)
static float3 transform_with_uniform_scale(const float3 &position, const float3 ¢er, const float scale)
static T gather_mean(const VArray< T > &values, const Span< int > indices)
static void node_register()
static int edge_to_vert_islands(const Mesh &mesh, const IndexMask &edge_mask, const IndexMask &vert_mask, MutableSpan< int > edge_island_indices, MutableSpan< int > vert_island_indices)
static const EnumPropertyItem scale_mode_items[]
static Span< int > front_indices_to_same_value(const Span< int > indices, const Span< int > values)
static void node_declare(NodeDeclarationBuilder &b)
static void node_init(bNodeTree *, bNode *node)
static void scale_on_axis(const GroupedSpan< int > elem_islands, const GroupedSpan< int > vert_islands, const VArray< float > &scale_varray, const VArray< float3 > ¢er_varray, const VArray< float3 > &axis_varray, Mesh &mesh)
static Array< int > create_reverse_offsets(const Span< int > indices, const int items_num)
static GroupedSpan< int > gather_groups(const Span< int > group_indices, const int groups_num, Array< int > &r_offsets, Array< int > &r_indices)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void gather_edge_islands(const Mesh &mesh, const IndexMask &edge_mask, Array< int > &r_item_offsets, Array< int > &r_item_indices, Array< int > &r_vert_offsets, Array< int > &r_vert_indices)
static void gather_face_islands(const Mesh &mesh, const IndexMask &face_mask, Array< int > &r_item_offsets, Array< int > &r_item_indices, Array< int > &r_vert_offsets, Array< int > &r_vert_indices)
static float4x4 create_single_axis_transform(const float3 ¢er, const float3 &axis, const float scale)
static void from_indices_large_groups(const Span< int > group_indices, MutableSpan< int > r_counts_to_offset, MutableSpan< int > r_indices)
static void node_geo_exec(GeoNodeExecParams params)
static void scale_uniformly(const GroupedSpan< int > elem_islands, const GroupedSpan< int > vert_islands, const VArray< float > &scale_varray, const VArray< float3 > ¢er_varray, Mesh &mesh)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void build_reverse_offsets(Span< int > indices, MutableSpan< int > offsets)
void parallel_for_each(Range &&range, const Function &function)
Value parallel_deterministic_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
auto accumulated_task_sizes(Fn &&fn)
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
MatBase< float, 4, 4 > float4x4
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
VecBase< int32_t, 2 > int2
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
static MatBase identity()
std::string ui_description
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeDeclareFunction declare
IndexRange index_range() const
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)