12void Skeleton2D::ensureSorted() {
13 if (sk_.bones().empty()) {
14 sk_.topologicalSort();
18void Skeleton2D::ensureStateSize() {
20 const unsigned n =
static_cast<unsigned>(sk_.bones().size());
21 if (state_.size() <
n) {
23 state_.position_.resize(
n);
24 state_.orientation_.resize(
n);
25 state_.scale_.resize(
n);
26 state_.rotation_.resize(
n);
30::ik::bone2d *Skeleton2D::boneAt(
int boneId)
const {
31 if (boneId < 0)
return nullptr;
32 const auto &
bones = sk_.bones();
33 if (
static_cast<size_t>(boneId) >=
bones.size())
return nullptr;
34 return bones[
static_cast<size_t>(boneId)];
37void Skeleton2D::requireBone(
int boneId)
const {
38 if (!boneAt(boneId)) {
39 throw Exception(
"Skeleton2D: invalid bone id %d", boneId);
47 throw Exception(
"Skeleton2D.createBone: length must be > 0");
51 sk_.topologicalSort();
53 return static_cast<int>(child->
id);
57 const_cast<Skeleton2D *
>(
this)->ensureSorted();
58 return static_cast<int>(sk_.bones().size());
63 ::ik::bone2d *
b = boneAt(boneId);
64 if (!
b->parent)
return -1;
65 return static_cast<int>(
b->parent->id);
70 return static_cast<int>(boneAt(boneId)->children.size());
75 ::ik::bone2d *
b = boneAt(boneId);
76 if (childIndex < 0 ||
static_cast<size_t>(childIndex) >=
b->children.size()) {
77 throw Exception(
"Skeleton2D.getChild: childIndex out of range");
79 return static_cast<int>(
b->children[
static_cast<size_t>(childIndex)]->
id);
84 return boneAt(boneId)->length;
90 throw Exception(
"Skeleton2D.setLength: root length is always 0");
93 throw Exception(
"Skeleton2D.setLength: length must be > 0");
95 boneAt(boneId)->setLength(
length);
101 boneAt(boneId)->position(state_) = {
x,
y};
106 const_cast<Skeleton2D *
>(
this)->ensureStateSize();
107 return boneAt(boneId)->position(state_)[0];
112 const_cast<Skeleton2D *
>(
this)->ensureStateSize();
113 return boneAt(boneId)->position(state_)[1];
119 boneAt(boneId)->orientation(state_) = {
x,
y};
124 const_cast<Skeleton2D *
>(
this)->ensureStateSize();
125 return boneAt(boneId)->orientation(state_)[0];
130 const_cast<Skeleton2D *
>(
this)->ensureStateSize();
131 return boneAt(boneId)->orientation(state_)[1];
137 boneAt(boneId)->rotation(state_) = {
angle};
142 const_cast<Skeleton2D *
>(
this)->ensureStateSize();
143 return boneAt(boneId)->rotation(state_)[0];
148 if (minAngle > maxAngle) {
149 throw Exception(
"Skeleton2D.setConstraints: minAngle must be <= maxAngle");
151 boneAt(boneId)->setConstraints(::ik::vec<float, 1>{minAngle},
152 ::ik::vec<float, 1>{maxAngle});
157 boneAt(boneId)->clearConstraints();
162 return boneAt(boneId)->constrained;
168 sk_.init_straight_pose(state_, ::ik::vec2{rootX, rootY});
180 sk_.forward_kinematics(state_);
186 sk_.update_rotations(state_);
191 return sk_.total_length_to(boneAt(boneId));
std::vector< Bone > bones
EVENGINE_API_FOUNDATION public API.
Script-facing 2D skeleton + pose state (ik::skeleton2d + ik::ecs2d). Bone indices are stable after ea...
float getLength(int boneId) const
Rest length of the bone.
Skeleton2D()
Creates an empty 2D skeleton with a root bone.
void setOrientation(int boneId, float x, float y)
Sets the bone forward orientation vector.
void setRotation(int boneId, float angle)
Local hinge angle (radians) relative to parent forward.
float totalLengthTo(int boneId) const
Sum of bone lengths from root to boneId inclusive.
void bind()
Binds the current pose as the rest pose.
void updateRotations()
Derives local rotations from world positions.
float getY(int boneId) const
World-space Y of the bone (or component).
int getChildCount(int boneId) const
Number of direct children of boneId.
bool hasConstraints(int boneId) const
True if the bone has joint limits.
int getBoneCount() const
Number of bones currently in the skeleton.
float getX(int boneId) const
World-space X of the bone (or component).
void forwardKinematics()
Updates world positions from local rotations.
void initStraightPose(float rootX=0.f, float rootY=0.f)
Initializes a straight chain pose from the root.
float getRotation(int boneId) const
Local hinge angle in radians.
int getParent(int boneId) const
Parent bone id, or -1 for the root.
void clearConstraints(int boneId)
Removes joint limits from the bone.
float getOrientationX(int boneId) const
Orientation vector X.
float getOrientationY(int boneId) const
Orientation vector Y.
void setConstraints(int boneId, float minAngle, float maxAngle)
Joint limits for the single 2D hinge DOF.
void setLength(int boneId, float length)
Sets the rest length of the bone.
int createBone(int parentId, float length=1.f)
Create a child bone under parentId; returns new bone id.
void setPosition(int boneId, float x, float y)
Sets the world-space bone position.
int getChild(int boneId, int childIndex) const
Child bone id at childIndex under boneId.