Blender V5.0
IK_Solver.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
8
10
11#include "IK_QJacobianSolver.h"
12#include "IK_QSegment.h"
13#include "IK_QTask.h"
14
15#include <algorithm>
16#include <list>
17using namespace std;
18
20 public:
21 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
22 IK_QSolver() = default;
23
25 IK_QSegment *root = nullptr;
26 std::list<IK_QTask *> tasks;
27};
28
29// FIXME: locks still result in small "residual" changes to the locked axes...
30static IK_QSegment *CreateSegment(int flag, bool translate)
31{
32 int ndof = 0;
33 ndof += (flag & IK_XDOF) ? 1 : 0;
34 ndof += (flag & IK_YDOF) ? 1 : 0;
35 ndof += (flag & IK_ZDOF) ? 1 : 0;
36
37 IK_QSegment *seg;
38
39 if (ndof == 0) {
40 return nullptr;
41 }
42 if (ndof == 1) {
43 int axis;
44
45 if (flag & IK_XDOF) {
46 axis = 0;
47 }
48 else if (flag & IK_YDOF) {
49 axis = 1;
50 }
51 else {
52 axis = 2;
53 }
54
55 if (translate) {
56 seg = new IK_QTranslateSegment(axis);
57 }
58 else {
59 seg = new IK_QRevoluteSegment(axis);
60 }
61 }
62 else if (ndof == 2) {
63 int axis1, axis2;
64
65 if (flag & IK_XDOF) {
66 axis1 = 0;
67 axis2 = (flag & IK_YDOF) ? 1 : 2;
68 }
69 else {
70 axis1 = 1;
71 axis2 = 2;
72 }
73
74 if (translate) {
75 seg = new IK_QTranslateSegment(axis1, axis2);
76 }
77 else {
78 if (axis1 + axis2 == 2) {
79 seg = new IK_QSwingSegment();
80 }
81 else {
82 seg = new IK_QElbowSegment((axis1 == 0) ? 0 : 2);
83 }
84 }
85 }
86 else {
87 if (translate) {
88 seg = new IK_QTranslateSegment();
89 }
90 else {
91 seg = new IK_QSphericalSegment();
92 }
93 }
94
95 return seg;
96}
97
99{
101 IK_QSegment *trans = CreateSegment(flag >> 3, true);
102
103 IK_QSegment *seg;
104
105 if (rot == nullptr && trans == nullptr) {
106 seg = new IK_QNullSegment();
107 }
108 else if (rot == nullptr) {
109 seg = trans;
110 }
111 else {
112 seg = rot;
113
114 // make it seem from the interface as if the rotation and translation
115 // segment are one
116 if (trans) {
117 seg->SetComposite(trans);
118 trans->SetParent(seg);
119 }
120 }
121
122 return seg;
123}
124
126{
127 IK_QSegment *qseg = (IK_QSegment *)seg;
128
129 if (qseg->Composite()) {
130 delete qseg->Composite();
131 }
132 delete qseg;
133}
134
136{
137 IK_QSegment *qseg = (IK_QSegment *)seg;
138 IK_QSegment *qparent = (IK_QSegment *)parent;
139
140 if (qparent && qparent->Composite()) {
141 qseg->SetParent(qparent->Composite());
142 }
143 else {
144 qseg->SetParent(qparent);
145 }
146}
147
149 IK_Segment *seg, float start[3], float rest[][3], float basis[][3], float length)
150{
151 IK_QSegment *qseg = (IK_QSegment *)seg;
152
153 Vector3d mstart(start[0], start[1], start[2]);
154 // convert from blender column major
155 Matrix3d mbasis = CreateMatrix(basis[0][0],
156 basis[1][0],
157 basis[2][0],
158 basis[0][1],
159 basis[1][1],
160 basis[2][1],
161 basis[0][2],
162 basis[1][2],
163 basis[2][2]);
164 Matrix3d mrest = CreateMatrix(rest[0][0],
165 rest[1][0],
166 rest[2][0],
167 rest[0][1],
168 rest[1][1],
169 rest[2][1],
170 rest[0][2],
171 rest[1][2],
172 rest[2][2]);
173 double mlength(length);
174
175 if (qseg->Composite()) {
176 Vector3d cstart(0, 0, 0);
177 Matrix3d cbasis;
178 cbasis.setIdentity();
179
180 qseg->SetTransform(mstart, mrest, mbasis, 0.0);
181 qseg->Composite()->SetTransform(cstart, cbasis, cbasis, mlength);
182 }
183 else {
184 qseg->SetTransform(mstart, mrest, mbasis, mlength);
185 }
186}
187
188void IK_SetLimit(IK_Segment *seg, IK_SegmentAxis axis, float lmin, float lmax)
189{
190 IK_QSegment *qseg = (IK_QSegment *)seg;
191
192 if (axis >= IK_TRANS_X) {
193 if (!qseg->Translational()) {
194 if (qseg->Composite() && qseg->Composite()->Translational()) {
195 qseg = qseg->Composite();
196 }
197 else {
198 return;
199 }
200 }
201
202 if (axis == IK_TRANS_X) {
203 axis = IK_X;
204 }
205 else if (axis == IK_TRANS_Y) {
206 axis = IK_Y;
207 }
208 else {
209 axis = IK_Z;
210 }
211 }
212
213 qseg->SetLimit(axis, lmin, lmax);
214}
215
216void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness)
217{
218 if (stiffness < 0.0f) {
219 return;
220 }
221
222 stiffness = std::min<double>(stiffness, 1.0 - IK_STRETCH_STIFF_EPS);
223
224 IK_QSegment *qseg = (IK_QSegment *)seg;
225 double weight = 1.0f - stiffness;
226
227 if (axis >= IK_TRANS_X) {
228 if (!qseg->Translational()) {
229 if (qseg->Composite() && qseg->Composite()->Translational()) {
230 qseg = qseg->Composite();
231 }
232 else {
233 return;
234 }
235 }
236
237 if (axis == IK_TRANS_X) {
238 axis = IK_X;
239 }
240 else if (axis == IK_TRANS_Y) {
241 axis = IK_Y;
242 }
243 else {
244 axis = IK_Z;
245 }
246 }
247
248 qseg->SetWeight(axis, weight);
249}
250
251void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
252{
253 IK_QSegment *qseg = (IK_QSegment *)seg;
254
255 if (qseg->Translational() && qseg->Composite()) {
256 qseg = qseg->Composite();
257 }
258
259 const Matrix3d &change = qseg->BasisChange();
260
261 // convert to blender column major
262 basis_change[0][0] = (float)change(0, 0);
263 basis_change[1][0] = (float)change(0, 1);
264 basis_change[2][0] = (float)change(0, 2);
265 basis_change[0][1] = (float)change(1, 0);
266 basis_change[1][1] = (float)change(1, 1);
267 basis_change[2][1] = (float)change(1, 2);
268 basis_change[0][2] = (float)change(2, 0);
269 basis_change[1][2] = (float)change(2, 1);
270 basis_change[2][2] = (float)change(2, 2);
271}
272
273void IK_GetTranslationChange(IK_Segment *seg, float *translation_change)
274{
275 IK_QSegment *qseg = (IK_QSegment *)seg;
276
277 if (!qseg->Translational() && qseg->Composite()) {
278 qseg = qseg->Composite();
279 }
280
281 const Vector3d &change = qseg->TranslationChange();
282
283 translation_change[0] = (float)change[0];
284 translation_change[1] = (float)change[1];
285 translation_change[2] = (float)change[2];
286}
287
289{
290 if (root == nullptr) {
291 return nullptr;
292 }
293
294 IK_QSolver *solver = new IK_QSolver();
295 solver->root = (IK_QSegment *)root;
296
297 return (IK_Solver *)solver;
298}
299
301{
302 if (solver == nullptr) {
303 return;
304 }
305
306 IK_QSolver *qsolver = (IK_QSolver *)solver;
307 std::list<IK_QTask *> &tasks = qsolver->tasks;
308 std::list<IK_QTask *>::iterator task;
309
310 for (task = tasks.begin(); task != tasks.end(); task++) {
311 delete (*task);
312 }
313
314 delete qsolver;
315}
316
317void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight)
318{
319 if (solver == nullptr || tip == nullptr) {
320 return;
321 }
322
323 IK_QSolver *qsolver = (IK_QSolver *)solver;
324 IK_QSegment *qtip = (IK_QSegment *)tip;
325
326 // in case of composite segment the second segment is the tip
327 if (qtip->Composite()) {
328 qtip = qtip->Composite();
329 }
330
331 Vector3d pos(goal[0], goal[1], goal[2]);
332
333 IK_QTask *ee = new IK_QPositionTask(true, qtip, pos);
334 ee->SetWeight(weight);
335 qsolver->tasks.push_back(ee);
336}
337
338void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight)
339{
340 if (solver == nullptr || tip == nullptr) {
341 return;
342 }
343
344 IK_QSolver *qsolver = (IK_QSolver *)solver;
345 IK_QSegment *qtip = (IK_QSegment *)tip;
346
347 // in case of composite segment the second segment is the tip
348 if (qtip->Composite()) {
349 qtip = qtip->Composite();
350 }
351
352 // convert from blender column major
353 Matrix3d rot = CreateMatrix(goal[0][0],
354 goal[1][0],
355 goal[2][0],
356 goal[0][1],
357 goal[1][1],
358 goal[2][1],
359 goal[0][2],
360 goal[1][2],
361 goal[2][2]);
362
363 IK_QTask *orient = new IK_QOrientationTask(true, qtip, rot);
364 orient->SetWeight(weight);
365 qsolver->tasks.push_back(orient);
366}
367
369 IK_Segment *tip,
370 float goal[3],
371 float polegoal[3],
372 float poleangle,
373 int getangle)
374{
375 if (solver == nullptr || tip == nullptr) {
376 return;
377 }
378
379 IK_QSolver *qsolver = (IK_QSolver *)solver;
380 IK_QSegment *qtip = (IK_QSegment *)tip;
381
382 // in case of composite segment the second segment is the tip
383 if (qtip->Composite()) {
384 qtip = qtip->Composite();
385 }
386
387 Vector3d qgoal(goal[0], goal[1], goal[2]);
388 Vector3d qpolegoal(polegoal[0], polegoal[1], polegoal[2]);
389
390 qsolver->solver.SetPoleVectorConstraint(qtip, qgoal, qpolegoal, poleangle, getangle);
391}
392
394{
395 if (solver == nullptr) {
396 return 0.0f;
397 }
398
399 IK_QSolver *qsolver = (IK_QSolver *)solver;
400
401 return qsolver->solver.GetPoleAngle();
402}
403
404#if 0
405static void IK_SolverAddCenterOfMass(IK_Solver *solver,
406 IK_Segment *root,
407 float goal[3],
408 float weight)
409{
410 if (solver == nullptr || root == nullptr) {
411 return;
412 }
413
414 IK_QSolver *qsolver = (IK_QSolver *)solver;
415 IK_QSegment *qroot = (IK_QSegment *)root;
416
417 // convert from blender column major
418 Vector3d center(goal);
419
420 IK_QTask *com = new IK_QCenterOfMassTask(true, qroot, center);
421 com->SetWeight(weight);
422 qsolver->tasks.push_back(com);
423}
424#endif
425
426int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations)
427{
428 if (solver == nullptr) {
429 return 0;
430 }
431
432 IK_QSolver *qsolver = (IK_QSolver *)solver;
433
434 IK_QSegment *root = qsolver->root;
435 IK_QJacobianSolver &jacobian = qsolver->solver;
436 std::list<IK_QTask *> &tasks = qsolver->tasks;
437 double tol = tolerance;
438
439 if (!jacobian.Setup(root, tasks)) {
440 return 0;
441 }
442
443 bool result = jacobian.Solve(root, tasks, tol, max_iterations);
444
445 return ((result) ? 1 : 0);
446}
static Eigen::Matrix3d CreateMatrix(double xx, double xy, double xz, double yx, double yy, double yz, double zx, double zy, double zz)
Definition IK_Math.h:35
IK_Solver * IK_CreateSolver(IK_Segment *root)
float IK_SolverGetPoleAngle(IK_Solver *solver)
void IK_SolverSetPoleVectorConstraint(IK_Solver *solver, IK_Segment *tip, float goal[3], float polegoal[3], float poleangle, int getangle)
void IK_SetParent(IK_Segment *seg, IK_Segment *parent)
void IK_FreeSolver(IK_Solver *solver)
IK_Segment * IK_CreateSegment(int flag)
Definition IK_Solver.cpp:98
void IK_FreeSegment(IK_Segment *seg)
static IK_QSegment * CreateSegment(int flag, bool translate)
Definition IK_Solver.cpp:30
void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight)
void IK_SetLimit(IK_Segment *seg, IK_SegmentAxis axis, float lmin, float lmax)
void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight)
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations)
void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3])
void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness)
void IK_GetTranslationChange(IK_Segment *seg, float *translation_change)
void IK_SetTransform(IK_Segment *seg, float start[3], float rest[][3], float basis[][3], float length)
void IK_Segment
Definition IK_solver.h:80
void IK_Solver
Definition IK_solver.h:125
#define IK_STRETCH_STIFF_EPS
Definition IK_solver.h:145
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)
bool Translational() const
virtual void SetWeight(int, double)
void SetComposite(IK_QSegment *seg)
Matrix3d BasisChange() const
IK_QSegment * Composite() const
Definition IK_QSegment.h:69
void SetParent(IK_QSegment *parent)
virtual void SetLimit(int, double, double)
Vector3d TranslationChange() const
void SetTransform(const Vector3d &start, const Matrix3d &rest_basis, const Matrix3d &basis, const double length)
IK_QJacobianSolver solver
Definition IK_Solver.cpp:24
IK_QSegment * root
Definition IK_Solver.cpp:25
std::list< IK_QTask * > tasks
Definition IK_Solver.cpp:26
EIGEN_MAKE_ALIGNED_OPERATOR_NEW IK_QSolver()=default
void SetWeight(double weight)
Definition IK_QTask.h:50
nullptr float
#define rot(x, k)
uint pos
float length(VecOp< float, D >) RET
@ IK_ZDOF
@ IK_XDOF
@ IK_YDOF
IK_SegmentAxis
@ IK_X
@ IK_TRANS_X
@ IK_Y
@ IK_TRANS_Y
@ IK_Z
uint8_t flag
Definition wm_window.cc:145