34 b.add_input<
decl::Float>(
"Scale",
"Scale").default_value(1.0f).min(0.0f).field_on_all();
39 "Origin of the scaling for each element. If multiple elements are connected, their "
40 "center is averaged");
42 .default_value({1.0f, 0.0f, 0.0f})
44 .
description(
"Direction in which to scale the element")
49 const bNode *node =
b.node_or_null();
50 if (node !=
nullptr) {
64 node->custom1 =
int16_t(AttrDomain::Face);
77 const int value = values[indices.first()];
78 const int &first_other = *std::find_if(
79 indices.begin(), indices.end(), [&](
const int index) { return values[index] != value; });
80 return indices.take_front(&first_other - indices.begin());
87 constexpr const int segment_size = 1024;
88 constexpr const IndexRange segment(segment_size);
89 const bool last_small_segmet = bool(group_indices.
size() % segment_size);
90 const int total_segments = group_indices.
size() / segment_size +
int(last_small_segmet);
96 std::iota(segment_indices.
begin(), segment_indices.
end(), segment_size * segment_index);
98 return group_indices[a] < group_indices[b];
102 const int group = group_indices[indices.first()];
105 indices = indices.drop_front(step_size);
118 const int step_size = indices_of_current_group.
size();
119 const int group = group_indices[indices.first()];
123 indices = indices.drop_front(step_size);
141 int *counts = MEM_cnew_array<int>(
size_t(offsets.
size()), __func__);
145 for (const int64_t i : range) {
146 const int group_index = group_indices[i];
147 const int index_in_group = atomic_fetch_and_add_int32(&counts[group_index], 1);
148 results[offsets[group_index][index_in_group]] = int(i);
155 const int groups_num,
159 if (group_indices.
size() / groups_num > 1000) {
175 if (
const std::optional<T> value = values.get_if_single()) {
179 using MeanAccumulator = std::pair<T, int>;
180 const auto join_accumulators = [](
const MeanAccumulator a,
181 const MeanAccumulator
b) -> MeanAccumulator {
182 return {(a.first +
b.first) / (a.second +
b.second), 1};
187 const auto accumulator = threading::parallel_reduce<MeanAccumulator>(
188 indices.index_range(),
190 MeanAccumulator(
T(), 0),
191 [&](
const IndexRange range, MeanAccumulator other) -> MeanAccumulator {
193 for (
const int i : indices.slice(range)) {
196 return join_accumulators({value,
int(range.size())}, other);
199 value = accumulator.first / accumulator.second;
210 const float3 new_position = center + scaled_diff;
221 threading::parallel_for(
225 for (const int island_index : range) {
226 const Span<int> vert_island = vert_islands[island_index];
227 const Span<int> elem_island = elem_islands[island_index];
229 const float scale = gather_mean<float>(scale_varray, elem_island);
230 const float3 center = gather_mean<float3>(center_varray, elem_island);
232 threading::parallel_for(vert_island.index_range(), 2048, [&](const IndexRange range) {
233 for (const int vert_i : vert_island.slice(range)) {
234 positions[vert_i] = transform_with_uniform_scale(positions[vert_i], center, scale);
239 threading::accumulated_task_sizes([&](
const IndexRange range) {
240 return elem_islands.offsets[
range].size() + vert_islands.offsets[range].size();
258 float4x4 transform = float4x4::identity();
261 transform.location() -= center;
265 float4x4 base_change = float4x4::identity();
266 base_change.
x_axis() = x_axis;
267 base_change.
y_axis() = y_axis;
268 base_change.
z_axis() = z_axis;
273 float4x4 scale_transform = float4x4::identity();
274 scale_transform[0][0] = scale;
276 transform = base_change * scale_transform * base_change_inv *
transform;
279 transform.location() += center;
292 threading::parallel_for(
296 for (const int island_index : range) {
297 const Span<int> vert_island = vert_islands[island_index];
298 const Span<int> elem_island = elem_islands[island_index];
300 const float scale = gather_mean<float>(scale_varray, elem_island);
301 const float3 center = gather_mean<float3>(center_varray, elem_island);
302 const float3 axis = gather_mean<float3>(axis_varray, elem_island);
303 const float3 fixed_axis = math::is_zero(axis) ? float3(1.0f, 0.0f, 0.0f) : axis;
305 const float4x4 transform = create_single_axis_transform(center, fixed_axis, scale);
306 threading::parallel_for(vert_island.index_range(), 2048, [&](const IndexRange range) {
307 for (const int vert_i : vert_island.slice(range)) {
308 positions[vert_i] = math::transform_point(transform, positions[vert_i]);
313 threading::accumulated_task_sizes([&](
const IndexRange range) {
314 return vert_islands.offsets[
range].size() + elem_islands.offsets[range].size();
325 index_mask::build_reverse_map<int>(vert_mask, verts_pos);
332 const int v1 = verts_pos[
verts.first()];
333 for (
const int vert_i :
verts.drop_front(1)) {
334 const int v2 = verts_pos[vert_i];
335 disjoint_set.
join(v1,
v2);
342 const int face_vert_i = face_verts[face_i].first();
343 const int vert_pos = verts_pos[face_vert_i];
344 const int vert_island = vert_island_indices[vert_pos];
345 face_island_indices[face_pos] = vert_island;
359 const IndexMask vert_mask = geometry::vert_selection_from_face(
360 mesh.face_offsets(), face_mask, mesh.corner_verts(), mesh.verts_num, memory);
365 mesh, face_mask, vert_mask, face_island_indices, vert_island_indices);
368 gather_groups(vert_island_indices, total_islands, r_vert_offsets, r_vert_indices);
369 gather_groups(face_island_indices, total_islands, r_item_offsets, r_item_indices);
372 if (face_mask.
size() != mesh.faces_num) {
375 array_utils::gather<int>(
378 if (vert_mask.
size() != mesh.verts_num) {
381 array_utils::gather<int>(
393 index_mask::build_reverse_map<int>(vert_mask, verts_pos);
399 const int2 edge = edges[edge_i];
400 const int v1 = verts_pos[edge[0]];
401 const int v2 = verts_pos[edge[1]];
402 disjoint_set.
join(v1,
v2);
408 const int2 edge = edges[edge_i];
409 const int edge_vert_i = edge[0];
410 const int vert_pos = verts_pos[edge_vert_i];
411 const int vert_island = vert_island_indices[vert_pos];
412 edge_island_indices[edge_pos] = vert_island;
426 const IndexMask vert_mask = geometry::vert_selection_from_edge(
427 mesh.edges(), edge_mask, mesh.verts_num, memory);
432 mesh, edge_mask, vert_mask, edge_island_indices, vert_island_indices);
435 gather_groups(vert_island_indices, total_islands, r_vert_offsets, r_vert_indices);
436 gather_groups(edge_island_indices, total_islands, r_item_offsets, r_item_indices);
439 if (edge_mask.
size() != mesh.edges_num) {
442 array_utils::gather<int>(
445 if (vert_mask.
size() != mesh.verts_num) {
448 array_utils::gather<int>(
456 const AttrDomain domain = AttrDomain(node.custom1);
465 geometry.modify_geometry_sets([&](
GeometrySet &geometry) {
466 if (
Mesh *mesh = geometry.get_mesh_for_write()) {
468 FieldEvaluator evaluator{context, mesh->attributes().domain_size(domain)};
470 evaluator.add(scale_field);
471 evaluator.add(center_field);
475 evaluator.evaluate();
476 const IndexMask &mask = evaluator.get_evaluated_selection_as_mask();
477 if (mask.is_empty()) {
488 case AttrDomain::Face:
491 case AttrDomain::Edge:
501 const VArray<float> scale_varray = evaluator.get_evaluated<
float>(0);
504 switch (scale_mode) {
506 scale_uniformly(item_islands, vert_islands, scale_varray, center_varray, *mesh);
511 item_islands, vert_islands, scale_varray, center_varray, axis_varray, *mesh);
515 mesh->tag_positions_changed();
519 params.set_output(
"Geometry", std::move(geometry));
525 {
int(AttrDomain::Face),
529 "Scale individual faces or neighboring face islands"},
530 {
int(AttrDomain::Edge),
534 "Scale individual edges or neighboring edge islands"},
535 {0,
nullptr, 0,
nullptr,
nullptr},
543 "Scale elements by the same factor in every direction"},
548 "Scale elements in a single direction"},
549 {0,
nullptr, 0,
nullptr,
nullptr},
555 "Element type to transform",
558 int(AttrDomain::Face));
#define NODE_CLASS_GEOMETRY
#define BLI_assert_unreachable()
#define BLI_SCOPED_DEFER(function_to_defer)
GeometryNodeScaleElementsMode
@ GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS
@ GEO_NODE_SCALE_ELEMENTS_UNIFORM
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
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
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 IndexRange shift(int64_t n) const
constexpr IndexRange slice(int64_t start, int64_t size) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr bool is_empty() 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
void set_selection(Field< bool > selection)
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
local_group_size(16, 16) .push_constant(Type b
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
void MEM_freeN(void *vmemh)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void node_register_type(bNodeType *ntype)
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 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)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeDeclareFunction declare
IndexRange index_range() const