Welcome to mirror list, hosted at ThFree Co, Russian Federation.

node_geo_offset_point_in_curve.cc « nodes « geometry « nodes « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d71e27e03858571fd745dc17238e839738a493d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include "BLI_task.hh"

#include "BKE_curves.hh"

#include "node_geometry_util.hh"

namespace blender::nodes {

int apply_offset_in_cyclic_range(const IndexRange range, const int start_index, const int offset)
{
  BLI_assert(range.contains(start_index));
  const int start_in_range = start_index - range.first();
  const int offset_in_range = start_in_range + offset;
  const int mod_offset = offset_in_range % range.size();
  if (mod_offset >= 0) {
    return range[mod_offset];
  }
  return range.last(-(mod_offset + 1));
}

}  // namespace blender::nodes

namespace blender::nodes::node_geo_offset_point_in_curve_cc {

static void node_declare(NodeDeclarationBuilder &b)
{
  b.add_input<decl::Int>(N_("Point Index"))
      .implicit_field(implicit_field_inputs::index)
      .description(
          N_("The index of the control point to evaluate. Defaults to the current index"));
  b.add_input<decl::Int>(N_("Offset"))
      .supports_field()
      .description(N_("The number of control points along the curve to traverse"));
  b.add_output<decl::Bool>(N_("Is Valid Offset"))
      .dependent_field()
      .description(N_("Outputs true if the evaluated control point plus the offset "
                      "is a valid index of the original curve"));
  b.add_output<decl::Int>(N_("Point Index"))
      .dependent_field()
      .description(N_("The index of the control point plus the offset within the entire "
                      "curves data-block"));
}

class ControlPointNeighborFieldInput final : public bke::CurvesFieldInput {
 private:
  const Field<int> index_;
  const Field<int> offset_;

 public:
  ControlPointNeighborFieldInput(Field<int> index, Field<int> offset)
      : CurvesFieldInput(CPPType::get<int>(), "Offset Point in Curve"),
        index_(std::move(index)),
        offset_(std::move(offset))
  {
    category_ = Category::Generated;
  }

  GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
                                 const eAttrDomain domain,
                                 const IndexMask mask) const final
  {
    const VArray<bool> cyclic = curves.cyclic();
    const Array<int> parent_curves = curves.point_to_curve_map();

    const bke::CurvesFieldContext context{curves, domain};
    fn::FieldEvaluator evaluator{context, &mask};
    evaluator.add(index_);
    evaluator.add(offset_);
    evaluator.evaluate();
    const VArray<int> indices = evaluator.get_evaluated<int>(0);
    const VArray<int> offsets = evaluator.get_evaluated<int>(1);

    Array<int> output(mask.min_array_size());
    for (const int i_selection : mask) {
      const int i_point = std::clamp(indices[i_selection], 0, curves.points_num() - 1);
      const int i_curve = parent_curves[i_point];
      const IndexRange curve_points = curves.points_for_curve(i_curve);
      const int offset_point = i_point + offsets[i_point];

      if (cyclic[i_curve]) {
        output[i_selection] = apply_offset_in_cyclic_range(
            curve_points, i_point, offsets[i_selection]);
        continue;
      }
      output[i_selection] = std::clamp(offset_point, 0, curves.points_num() - 1);
    }

    return VArray<int>::ForContainer(std::move(output));
  }
};

class OffsetValidFieldInput final : public bke::CurvesFieldInput {
 private:
  const Field<int> index_;
  const Field<int> offset_;

 public:
  OffsetValidFieldInput(Field<int> index, Field<int> offset)
      : CurvesFieldInput(CPPType::get<bool>(), "Offset Valid"),
        index_(std::move(index)),
        offset_(std::move(offset))
  {
    category_ = Category::Generated;
  }

  GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
                                 const eAttrDomain domain,
                                 const IndexMask mask) const final
  {
    const VArray<bool> cyclic = curves.cyclic();
    const Array<int> parent_curves = curves.point_to_curve_map();

    const bke::CurvesFieldContext context{curves, domain};
    fn::FieldEvaluator evaluator{context, &mask};
    evaluator.add(index_);
    evaluator.add(offset_);
    evaluator.evaluate();
    const VArray<int> indices = evaluator.get_evaluated<int>(0);
    const VArray<int> offsets = evaluator.get_evaluated<int>(1);

    Array<bool> output(mask.min_array_size());
    for (const int i_selection : mask) {
      const int i_point = indices[i_selection];
      if (!curves.points_range().contains(i_point)) {
        output[i_selection] = false;
        continue;
      }

      const int i_curve = parent_curves[i_point];
      const IndexRange curve_points = curves.points_for_curve(i_curve);
      if (cyclic[i_curve]) {
        output[i_selection] = true;
        continue;
      }
      output[i_selection] = curve_points.contains(i_point + offsets[i_selection]);
    };
    return VArray<bool>::ForContainer(std::move(output));
  }
};

static void node_geo_exec(GeoNodeExecParams params)
{
  Field<int> index = params.extract_input<Field<int>>("Point Index");
  Field<int> offset = params.extract_input<Field<int>>("Offset");

  if (params.output_is_required("Point Index")) {
    Field<int> curve_point_field{std::make_shared<ControlPointNeighborFieldInput>(index, offset)};
    params.set_output("Point Index", std::move(curve_point_field));
  }
  if (params.output_is_required("Is Valid Offset")) {
    Field<bool> valid_field{std::make_shared<OffsetValidFieldInput>(index, offset)};
    params.set_output("Is Valid Offset", std::move(valid_field));
  }
}

}  // namespace blender::nodes::node_geo_offset_point_in_curve_cc

void register_node_type_geo_offset_point_in_curve()
{
  namespace file_ns = blender::nodes::node_geo_offset_point_in_curve_cc;
  static bNodeType ntype;
  geo_node_type_base(
      &ntype, GEO_NODE_OFFSET_POINT_IN_CURVE, "Offset Point in Curve", NODE_CLASS_INPUT);
  ntype.geometry_node_execute = file_ns::node_geo_exec;
  ntype.declare = file_ns::node_declare;
  nodeRegisterType(&ntype);
}