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