jsantell / THREE.IK

inverse kinematics for three.js
https://jsantell.github.io/THREE.IK
MIT License
455 stars 54 forks source link

General API Design #7

Open jsantell opened 6 years ago

jsantell commented 6 years ago

Currently, THREE.IK is comprised of a root IK system, IK, which can contain several root IKChains, each that may contain a tree of IKChains, which store multiple IKJoints that wrap THREE.Bone and an optional constraint.

Some general open questions:

IK/IKChain structure

Constraints

General API

In general, the API is difficult to use and easy to get wrong. Issues:

pauldps commented 6 years ago

I'm new to IK in general (learning about it, and planning to use it on a project), but I have a keen interest in API design, so let me see if I can help.

I think the current API could be simpler. Building a chain seems to always involve the same steps (adding bones, creating joints, adding chain to ik system) and they don't seem to change. I'd suggest consolidating them inside a ik.createChain() method. As an advantage, it would not prevent the developer from going full-advanced mode if they so desire.

// Going from the README example, the code below assumes 
//   we already have a `scene` with `pivot`, `movingTarget`, and 
//   a `bones` array with 10 bones.
const ik = new THREE.IK()

// Create chain from the `ik` instance
// Internally creates joints for each bone
// Automatically adds chain to IK system so `ik.add(chain)` isn't necessary
var chain = ik.createChain(...bones)

// Add a target to the chain
// If jointIndex is undefined, automatically uses last bone
chain.setTarget(movingTarget, jointIndex)

// Set a constraint for a given joint index
// If jointIndex is undefined, set constraint for the whole chain
chain.setConstraint(new THREE.IKBallConstraint(90), jointIndex)

chain.setTarget() would also be called internally when connecting chains, as if I'm understanding correctly, the target of the parent chain is the other chain's first joint. I think chain1.connect(chain2) looks good. If a different parent joint index is necessary, I don't think chain1.connect(chain2, jointIndex) would be too difficult to understand.

I'd also replace ik.getRootBone() with ik.root, where root is a getter. But that's purely semantic: scene.add(ik.root). Short and sweet. 😎

Let me know what you think.