Blender V4.3
IK_QJacobianSolver.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <stdio.h>
10
11#include "IK_QJacobianSolver.h"
12
13// #include "analyze.h"
15{
16 m_poleconstraint = false;
17 m_getpoleangle = false;
18 m_rootmatrix.setIdentity();
19}
20
21double IK_QJacobianSolver::ComputeScale()
22{
23 std::vector<IK_QSegment *>::iterator seg;
24 double length = 0.0f;
25
26 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
27 length += (*seg)->MaxExtension();
28 }
29
30 if (length == 0.0) {
31 return 1.0;
32 }
33 else {
34 return 1.0 / length;
35 }
36}
37
38void IK_QJacobianSolver::Scale(double scale, std::list<IK_QTask *> &tasks)
39{
40 std::list<IK_QTask *>::iterator task;
41 std::vector<IK_QSegment *>::iterator seg;
42
43 for (task = tasks.begin(); task != tasks.end(); task++) {
44 (*task)->Scale(scale);
45 }
46
47 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
48 (*seg)->Scale(scale);
49 }
50
51 m_rootmatrix.translation() *= scale;
52 m_goal *= scale;
53 m_polegoal *= scale;
54}
55
56void IK_QJacobianSolver::AddSegmentList(IK_QSegment *seg)
57{
58 m_segments.push_back(seg);
59
60 IK_QSegment *child;
61 for (child = seg->Child(); child; child = child->Sibling()) {
62 AddSegmentList(child);
63 }
64}
65
66bool IK_QJacobianSolver::Setup(IK_QSegment *root, std::list<IK_QTask *> &tasks)
67{
68 m_segments.clear();
69 AddSegmentList(root);
70
71 // assign each segment a unique id for the jacobian
72 std::vector<IK_QSegment *>::iterator seg;
73 int num_dof = 0;
74
75 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
76 (*seg)->SetDoFId(num_dof);
77 num_dof += (*seg)->NumberOfDoF();
78 }
79
80 if (num_dof == 0) {
81 return false;
82 }
83
84 // compute task ids and assign weights to task
85 int primary_size = 0;
86 int secondary_size = 0, secondary = 0;
87 double primary_weight = 0.0, secondary_weight = 0.0;
88 std::list<IK_QTask *>::iterator task;
89
90 for (task = tasks.begin(); task != tasks.end(); task++) {
91 IK_QTask *qtask = *task;
92
93 if (qtask->Primary()) {
94 qtask->SetId(primary_size);
95 primary_size += qtask->Size();
96 primary_weight += qtask->Weight();
97 }
98 else {
99 qtask->SetId(secondary_size);
100 secondary_size += qtask->Size();
101 secondary_weight += qtask->Weight();
102 secondary++;
103 }
104 }
105
106 if (primary_size == 0 || FuzzyZero(primary_weight)) {
107 return false;
108 }
109
110 m_secondary_enabled = (secondary > 0);
111
112 // rescale weights of tasks to sum up to 1
113 double primary_rescale = 1.0 / primary_weight;
114 double secondary_rescale;
115 if (FuzzyZero(secondary_weight)) {
116 secondary_rescale = 0.0;
117 }
118 else {
119 secondary_rescale = 1.0 / secondary_weight;
120 }
121
122 for (task = tasks.begin(); task != tasks.end(); task++) {
123 IK_QTask *qtask = *task;
124
125 if (qtask->Primary()) {
126 qtask->SetWeight(qtask->Weight() * primary_rescale);
127 }
128 else {
129 qtask->SetWeight(qtask->Weight() * secondary_rescale);
130 }
131 }
132
133 // set matrix sizes
134 m_jacobian.ArmMatrices(num_dof, primary_size);
135 if (secondary > 0) {
136 m_jacobian_sub.ArmMatrices(num_dof, secondary_size);
137 }
138
139 // set dof weights
140 int i;
141
142 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
143 for (i = 0; i < (*seg)->NumberOfDoF(); i++) {
144 m_jacobian.SetDoFWeight((*seg)->DoFId() + i, (*seg)->Weight(i));
145 }
146 }
147
148 return true;
149}
150
152 IK_QSegment *tip, Vector3d &goal, Vector3d &polegoal, float poleangle, bool getangle)
153{
154 m_poleconstraint = true;
155 m_poletip = tip;
156 m_goal = goal;
157 m_polegoal = polegoal;
158 m_poleangle = (getangle) ? 0.0f : poleangle;
159 m_getpoleangle = getangle;
160}
161
162void IK_QJacobianSolver::ConstrainPoleVector(IK_QSegment *root, std::list<IK_QTask *> &tasks)
163{
164 // this function will be called before and after solving. calling it before
165 // solving gives predictable solutions by rotating towards the solution,
166 // and calling it afterwards ensures the solution is exact.
167
168 if (!m_poleconstraint) {
169 return;
170 }
171
172 // disable pole vector constraint in case of multiple position tasks
173 std::list<IK_QTask *>::iterator task;
174 int positiontasks = 0;
175
176 for (task = tasks.begin(); task != tasks.end(); task++) {
177 if ((*task)->PositionTask()) {
178 positiontasks++;
179 }
180 }
181
182 if (positiontasks >= 2) {
183 m_poleconstraint = false;
184 return;
185 }
186
187 // get positions and rotations
188 root->UpdateTransform(m_rootmatrix);
189
190 const Vector3d rootpos = root->GlobalStart();
191 const Vector3d endpos = m_poletip->GlobalEnd();
192 const Matrix3d &rootbasis = root->GlobalTransform().linear();
193
194 // construct "lookat" matrices (like gluLookAt), based on a direction and
195 // an up vector, with the direction going from the root to the end effector
196 // and the up vector going from the root to the pole constraint position.
197 Vector3d dir = normalize(endpos - rootpos);
198 Vector3d rootx = rootbasis.col(0);
199 Vector3d rootz = rootbasis.col(2);
200 Vector3d up = rootx * cos(m_poleangle) + rootz * sin(m_poleangle);
201
202 // in post, don't rotate towards the goal but only correct the pole up
203 Vector3d poledir = (m_getpoleangle) ? dir : normalize(m_goal - rootpos);
204 Vector3d poleup = normalize(m_polegoal - rootpos);
205
206 Matrix3d mat, polemat;
207
208 mat.row(0) = normalize(dir.cross(up));
209 mat.row(1) = mat.row(0).cross(dir);
210 mat.row(2) = -dir;
211
212 polemat.row(0) = normalize(poledir.cross(poleup));
213 polemat.row(1) = polemat.row(0).cross(poledir);
214 polemat.row(2) = -poledir;
215
216 if (m_getpoleangle) {
217 // we compute the pole angle that to rotate towards the target
218 m_poleangle = angle(mat.row(1), polemat.row(1));
219
220 double dt = rootz.dot(mat.row(1) * cos(m_poleangle) + mat.row(0) * sin(m_poleangle));
221 if (dt > 0.0) {
222 m_poleangle = -m_poleangle;
223 }
224
225 // solve again, with the pole angle we just computed
226 m_getpoleangle = false;
227 ConstrainPoleVector(root, tasks);
228 }
229 else {
230 // now we set as root matrix the difference between the current and
231 // desired rotation based on the pole vector constraint. we use
232 // transpose instead of inverse because we have orthogonal matrices
233 // anyway, and in case of a singular matrix we don't get NaN's.
234 Affine3d trans;
235 trans.linear() = polemat.transpose() * mat;
236 trans.translation() = Vector3d(0, 0, 0);
237 m_rootmatrix = trans * m_rootmatrix;
238 }
239}
240
241bool IK_QJacobianSolver::UpdateAngles(double &norm)
242{
243 // assign each segment a unique id for the jacobian
244 std::vector<IK_QSegment *>::iterator seg;
245 IK_QSegment *qseg, *minseg = NULL;
246 double minabsdelta = 1e10, absdelta;
247 Vector3d delta, mindelta;
248 bool locked = false, clamp[3];
249 int i, mindof = 0;
250
251 // here we check if any angle limits were violated. angles whose clamped
252 // position is the same as it was before, are locked immediate. of the
253 // other violation angles the most violating angle is rememberd
254 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
255 qseg = *seg;
256 if (qseg->UpdateAngle(m_jacobian, delta, clamp)) {
257 for (i = 0; i < qseg->NumberOfDoF(); i++) {
258 if (clamp[i] && !qseg->Locked(i)) {
259 absdelta = fabs(delta[i]);
260
261 if (absdelta < IK_EPSILON) {
262 qseg->Lock(i, m_jacobian, delta);
263 locked = true;
264 }
265 else if (absdelta < minabsdelta) {
266 minabsdelta = absdelta;
267 mindelta = delta;
268 minseg = qseg;
269 mindof = i;
270 }
271 }
272 }
273 }
274 }
275
276 // lock most violating angle
277 if (minseg) {
278 minseg->Lock(mindof, m_jacobian, mindelta);
279 locked = true;
280
281 if (minabsdelta > norm) {
282 norm = minabsdelta;
283 }
284 }
285
286 if (locked == false) {
287 // no locking done, last inner iteration, apply the angles
288 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
289 (*seg)->UnLock();
290 (*seg)->UpdateAngleApply();
291 }
292 }
293
294 // signal if another inner iteration is needed
295 return locked;
296}
297
299 std::list<IK_QTask *> tasks,
300 const double,
301 const int max_iterations)
302{
303 float scale = ComputeScale();
304 bool solved = false;
305 // double dt = analyze_time();
306
307 Scale(scale, tasks);
308
309 ConstrainPoleVector(root, tasks);
310
311 root->UpdateTransform(m_rootmatrix);
312
313 // iterate
314 for (int iterations = 0; iterations < max_iterations; iterations++) {
315 // update transform
316 root->UpdateTransform(m_rootmatrix);
317
318 std::list<IK_QTask *>::iterator task;
319
320 // compute jacobian
321 for (task = tasks.begin(); task != tasks.end(); task++) {
322 if ((*task)->Primary()) {
323 (*task)->ComputeJacobian(m_jacobian);
324 }
325 else {
326 (*task)->ComputeJacobian(m_jacobian_sub);
327 }
328 }
329
330 double norm = 0.0;
331
332 do {
333 // invert jacobian
334 try {
335 m_jacobian.Invert();
336 if (m_secondary_enabled) {
337 m_jacobian.SubTask(m_jacobian_sub);
338 }
339 }
340 catch (...) {
341 fprintf(stderr, "IK Exception\n");
342 return false;
343 }
344
345 // update angles and check limits
346 } while (UpdateAngles(norm));
347
348 // unlock segments again after locking in clamping loop
349 std::vector<IK_QSegment *>::iterator seg;
350 for (seg = m_segments.begin(); seg != m_segments.end(); seg++) {
351 (*seg)->UnLock();
352 }
353
354 // compute angle update norm
355 double maxnorm = m_jacobian.AngleUpdateNorm();
356 if (maxnorm > norm) {
357 norm = maxnorm;
358 }
359
360 // check for convergence
362 solved = true;
363 break;
364 }
365 }
366
367 if (m_poleconstraint) {
368 root->PrependBasis(m_rootmatrix.linear());
369 }
370
371 Scale(1.0f / scale, tasks);
372
373 // analyze_add_run(max_iterations, analyze_time()-dt);
374
375 return solved;
376}
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
static const double IK_EPSILON
Definition IK_Math.h:22
static bool FuzzyZero(double x)
Definition IK_Math.h:24
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
void SetPoleVectorConstraint(IK_QSegment *tip, Vector3d &goal, Vector3d &polegoal, float poleangle, bool getangle)
bool Solve(IK_QSegment *root, std::list< IK_QTask * > tasks, const double tolerance, const int max_iterations)
bool Setup(IK_QSegment *root, std::list< IK_QTask * > &tasks)
void SetDoFWeight(int dof, double weight)
void SubTask(IK_QJacobian &jacobian)
double AngleUpdateNorm() const
void ArmMatrices(int dof, int task_size)
int NumberOfDoF() const
Definition IK_QSegment.h:77
const Vector3d GlobalStart() const
const Affine3d & GlobalTransform() const
const Vector3d GlobalEnd() const
virtual void Lock(int, IK_QJacobian &, Vector3d &)
virtual bool UpdateAngle(const IK_QJacobian &, Vector3d &, bool *)=0
bool Locked(int dof) const
void UpdateTransform(const Affine3d &global)
void PrependBasis(const Matrix3d &mat)
void SetDoFId(int dof_id)
Definition IK_QSegment.h:88
IK_QSegment * Child() const
Definition IK_QSegment.h:53
IK_QSegment * Sibling() const
Definition IK_QSegment.h:58
void SetId(int id)
Definition IK_QTask.h:25
void SetWeight(double weight)
Definition IK_QTask.h:50
double Weight() const
Definition IK_QTask.h:45
bool Primary() const
Definition IK_QTask.h:35
int Size() const
Definition IK_QTask.h:30
#define NULL
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 cos(float3 v)
struct blender::compositor::@172::@174 task
T sin(const AngleRadianBase< T > &a)