33void placeOnLine(::ik::vec<float, D>& point, const ::ik::vec<float, D>& anchor,
35 ::ik::vec<float, D>
dir = point - anchor;
36 float len = ::ik::length(
dir);
38 dir = ::ik::detail::default_forward<D>();
41 point = anchor +
dir * (boneLength / len);
47std::vector<::ik::bone<D>*> buildChain(::ik::skeleton<D>& sk,
unsigned root_id,
49 if (sk.bones().empty()) {
52 const auto& bones = sk.bones();
53 if (root_id >= bones.size() || tip_id >= bones.size()) {
57 std::vector<::ik::bone<D>*> chain;
58 for (::ik::bone<D>*
b = bones[tip_id];
b !=
nullptr;
b =
b->parent) {
60 if (
b->id == root_id) {
64 if (chain.empty() || chain.back()->id != root_id || chain.size() < 2) {
67 std::reverse(chain.begin(), chain.end());
74void reachTarget(::ik::ecs<D>&
state,
const std::vector<::ik::bone<D>*>& chain,
75 unsigned tipIndex, const ::ik::vec<float, D>& desired) {
76 const ::ik::vec<float, D> root_pos = chain[0]->position(
state);
78 float chain_length = 0.f;
79 for (
unsigned i = 1; i <= tipIndex; ++i) {
80 chain_length += chain[i]->length;
83 if (::ik::distance(root_pos, desired) >= chain_length) {
84 const ::ik::vec<float, D>
dir = ::ik::detail::safe_normalize(desired - root_pos);
85 for (
unsigned i = 1; i <= tipIndex; ++i) {
86 chain[i]->position(
state) = chain[i - 1]->position(
state) +
dir * chain[i]->length;
92 chain[tipIndex]->position(
state) = desired;
93 for (
int i =
static_cast<int>(tipIndex); i >= 1; --i) {
94 placeOnLine(chain[
static_cast<size_t>(i) - 1]->position(
state),
95 chain[
static_cast<size_t>(i)]->position(
state),
96 chain[
static_cast<size_t>(i)]->length);
100 chain[0]->position(
state) = root_pos;
101 for (
unsigned i = 1; i <= tipIndex; ++i) {
102 placeOnLine(chain[i]->position(
state), chain[i - 1]->position(
state),
110void applyPole(::ik::ecs<D>&
state,
const std::vector<::ik::bone<D>*>& chain,
111 const ::ik::vec<float, D>& goal, const ::ik::vec3& pole,
float weight) {
112 if constexpr (D == 3) {
113 if (weight <= 0.f || chain.size() < 2) {
116 const ::ik::vec3 root_pos = chain[0]->position(
state);
117 const ::ik::vec3 to_goal = goal - root_pos;
118 const float goal_len = ::ik::length(to_goal);
119 if (goal_len <= 1e-8f) {
122 const ::ik::vec3 axis = to_goal / goal_len;
124 const ::ik::vec3 to_pole = pole - root_pos;
125 const ::ik::vec3
proj = to_pole - axis * ::ik::dot(to_pole, axis);
126 if (::ik::length_squared(
proj) <= 1e-8f) {
129 const ::ik::vec3 pole_dir = ::ik::detail::safe_normalize(
proj);
131 for (
size_t i = 1; i + 1 < chain.size(); ++i) {
132 const ::ik::vec3
v = chain[i]->position(
state) - root_pos;
133 const float along = ::ik::dot(
v, axis);
134 ::ik::vec3 radial =
v - axis * along;
135 const float radial_len = ::ik::length(radial);
136 if (radial_len <= 1e-8f) {
139 const ::ik::vec3
dir = radial / radial_len;
140 const ::ik::vec3 new_dir =
141 ::ik::detail::safe_normalize(
dir + (pole_dir -
dir) * weight);
142 chain[i]->position(
state) = root_pos + axis * along + new_dir * radial_len;
149void applyChainConstraints(::ik::ecs<D>&
state,
150 const std::vector<::ik::bone<D>*>& chain) {
151 if (chain.size() < 2) {
154 std::vector<::ik::bone<D>*> rest(chain.begin() + 1, chain.end());
155 ::ik::detail::apply_angle_constraints<D>(rest,
state);
171 unsigned tip_id,
const std::vector<typename ::ik::solver<D>::target>& targets,
173 std::vector<::ik::bone<D>*> chain = buildChain(sk, root_id, tip_id);
174 if (chain.size() < 2) {
180 const typename ::ik::solver<D>::target *target =
nullptr;
182 std::vector<ChainTarget> chain_targets;
183 for (
const auto& t : targets) {
184 for (
size_t i = 0; i < chain.size(); ++i) {
185 if (chain[i]->
id == t.bone_id) {
186 chain_targets.push_back({
static_cast<unsigned>(i), &t});
191 if (chain_targets.empty()) {
196 const ChainTarget* goal = &chain_targets.front();
197 for (
const auto& ct : chain_targets) {
198 if (ct.index > goal->index) {
203 std::vector<::ik::vec<float, D>> original_pos(chain.size());
204 for (
size_t i = 0; i < chain.size(); ++i) {
205 original_pos[i] = chain[i]->position(
state);
208 const float pole_weight = std::min(1.f, std::max(0.f, opt.
pole_weight));
210 bool reached =
false;
211 for (
unsigned iter = 0; iter <
iterations; ++iter) {
212 for (
const auto& ct : chain_targets) {
213 ::ik::vec<float, D> desired = ct.target->position;
215 const ::ik::vec<float, D> cur = chain[ct.index]->position(
state);
216 desired = cur + (desired - cur) * (opt.
force * ct.target->weight);
218 reachTarget(
state, chain, ct.index, desired);
221 if (opt.
use_pole && pole_weight > 0.f) {
222 ::ik::vec<float, D> goal_pos = goal->target->position;
224 const ::ik::vec<float, D> cur = chain[goal->index]->position(
state);
225 goal_pos = cur + (goal_pos - cur) * (opt.
force * goal->target->weight);
227 applyPole(
state, chain, goal_pos, opt.
pole, pole_weight);
230 applyChainConstraints(
state, chain);
231 sk.update_rotations(
state);
234 for (
const auto& ct : chain_targets) {
235 if (::ik::distance(chain[ct.index]->position(
state), ct.target->position) >
247 const float t = std::min(1.f, std::max(0.f, opt.
influence));
248 for (
size_t i = 0; i < chain.size(); ++i) {
249 const auto & orig = original_pos[i];
250 ::ik::vec<float, D>&
pos = chain[i]->position(
state);
251 pos = orig + (
pos - orig) * t;
253 sk.update_rotations(
state);