Keyboard-Controlled Sign Language Hand Model

By Minwen Chen
🏆 Highlight Submission

Description

This project extends the skeletal animation and inverse kinematics system from Assignment 7 to support American Sign Language (ASL) fingerspelling. The 3D human hand model (developed based on the champanzee hand model) can now display all 26 letters of the ASL alphabet through keyboard input, with smooth transitions between poses.

Features Added

1. Sign Language Pose Data Structure

A new data structure to hold pose information for each ASL letter, mapping bone IDs to XZX Euler angles.

Code Location: main.cpp, Lines 22-26

struct SLPose {
  std::string description;
  std::map<int, Eigen::Vector3d> angles;  // holds bone_id & xzx angles
};

2. ASL Alphabet Pose Definitions (26 Letters)

Complete definitions for all 26 ASL alphabet letters (A-Z), each specifying the exact joint angles for the hand skeleton to form the correct hand shape.

Code Location: main.cpp, Lines 28-513

// Initialize all sign language poses
std::map<char, SLPose> init_sl_poses() {
  std::map<char, SLPose> poses;

  // Lambda to create a pose
  auto make_pose = [](const std::string& desc,
                      std::initializer_list<std::pair<int, Eigen::Vector3d>> angle_list) {
    SLPose pose;
    pose.description = desc;
    for (const auto& p : angle_list) {
      pose.angles[p.first] = p.second;
    }
    return pose;
  };

  // 26 letter definitions (A-Z) start here

  return poses;
}

Each pose uses XZX Euler angles where:

Example pose definition (Letter A):

poses['A'] = make_pose("Fist with thumb alongside", {
  {3, Eigen::Vector3d(0, 20, 0)},    // Thumb proximal
  {4, Eigen::Vector3d(0, 10, 0)},    // Thumb distal
  {6, Eigen::Vector3d(0, 70, 0)},    // Index proximal - curled
  {7, Eigen::Vector3d(0, 70, 0)},    // Index middle - curled
  // Remaining bones
});

3. Pose Application Function

A function that applies ASL poses to the skeleton by setting joint angles for each bone.

Code Location: main.cpp, Lines 515-528

void apply_sl_pose(Skeleton& skeleton, const SLPose& pose) {
  // Reset all bones to rest pose
  for (auto& bone : skeleton) {
    bone.xzx.setZero();
  }

  // Apply the pose angles
  for (const auto& [bone_id, angles] : pose.angles) {
    if (bone_id >= 0 && bone_id < static_cast<int>(skeleton.size())) {
      skeleton[bone_id].xzx = angles;
    }
  }
}

4. Smooth Pose Transition with Interpolation

When switching between ASL letters, the hand linearly interpolates from the current pose to the target pose over 0.5 seconds.

Code Location: main.cpp, Lines 530-546

void interpolate_to_pose(Skeleton& skeleton, const SLPose& target_pose,
                         double t, const std::vector<Eigen::Vector3d>& start_angles) {
  t = std::min(1.0, std::max(0.0, t));  // Clamp t to [0, 1]

  for (size_t i = 0; i < skeleton.size(); i++) {
    Eigen::Vector3d target_ang = Eigen::Vector3d::Zero();

    auto it = target_pose.angles.find(static_cast<int>(i));
    if (it != target_pose.angles.end()) {
      target_ang = it->second;
    }

    // Linear interpolation
    skeleton[i].xzx = (1.0 - t) * start_angles[i] + t * target_ang;
  }
}

The smooth ease-in-out is applied in the pre-draw callback:

Code Location: main.cpp, Lines 740-741

t = t * t * (3.0 - 2.0 * t);
interpolate_to_pose(skeleton, target_pose, t, start_angles);

5. Multi-Mode System

The application supports three distinct modes of operation, allowing users to switch between the new Sign Language mode and the original A7 functionality.

Code Location: main.cpp, Lines 635-636

// Mode: 0 = IK, 1 = FK animation, 2 = sign language pose mode
int mode = 2;  // Default to sign language mode

Mode handling in pre-draw callback:

Code Location: main.cpp, Lines 704-746

v.callback_pre_draw = [&](igl::opengl::glfw::Viewer &)->bool
{
  if(mode == 0)  // IK mode
  {
    ik();
  }
  else if(mode == 1)  // FK animation mode
  {
    // Original FK animation code
  }
  else if(mode == 2)  // sign language pose mode
  {
    if(transitioning_to_pose && v.core().is_animating)
    {
      // Smooth transition handling
    }
  }
  update();
  return false;
};

6. Keyboard Input Handling for Letter Keys

The system captures A-Z key presses and triggers the corresponding ASL pose transition with visual feedback in the console.

Code Location: main.cpp, Lines 807-838

v.callback_key_pressed = [&](
  igl::opengl::glfw::Viewer & viewer,
  unsigned char key,
  int
  )->bool
{
  // Check if it's a letter key
  char upper_key = std::toupper(key);
  if(upper_key >= 'A' && upper_key <= 'Z' && mode == 2)
  {
    auto it = sl_poses.find(upper_key);
    if(it != sl_poses.end())
    {
      // Start transition to new pose - save current angles
      start_angles.clear();
      for(size_t i = 0; i < skeleton.size(); i++) {
        start_angles.push_back(skeleton[i].xzx);
      }
      target_pose = it->second;
      transition_start_time = igl::get_seconds();
      transitioning_to_pose = true;
      current_letter = upper_key;

      std::cout << "sign language Letter: " << upper_key;
      if(!target_pose.description.empty())
      {
        std::cout << " - " << target_pose.description;
      }
      std::cout << std::endl;
      return true;
    }
  }
  // Mode switching code starts here
};

7. Transition State Variables

Variables to manage the smooth animated transitions between poses.

Code Location: main.cpp, Lines 627-633

// sign language pose transition variables
bool transitioning_to_pose = false;
double transition_start_time = 0;
double transition_duration = 0.5;  // seconds
SLPose target_pose;
std::vector<Eigen::Vector3d> start_angles;  // Store angles instead of Skeleton
char current_letter = ' ';

8. Console User Interface

Console output showing available controls and current state.

Code Location: main.cpp, Lines 890-902

std::cout << std::endl;
std::cout << "=== Keyboard-controlled sign language hand model ===" << std::endl;
std::cout << std::endl;
std::cout << "Controls:" << std::endl;
std::cout << "  A-Z      Show sign language letter pose (in sign language mode)" << std::endl;
std::cout << "  0        Reset to rest pose" << std::endl;
std::cout << "  1        Switch to IK mode (drag fingertips)" << std::endl;
std::cout << "  2        Switch to FK animation mode - content from assignment#7" << std::endl;
std::cout << "  3        Switch to sign language alphabet mode (need to set to this mode before using letters)" << std::endl;
std::cout << std::endl;
std::cout << "Current mode (default): sign language Alphabet" << std::endl;
std::cout << "Press any letter A-Z to see the sign language sign!" << std::endl;
std::cout << std::endl;

ASL Pose Summary Table

LetterDescriptionKey Bone Configurations
AFist with thumb alongsideAll fingers curled (70°), thumb slight curl (20°)
BFlat hand, thumb across palmAll fingers straight (0°), thumb curled across (50°)
CCurved hand (holding ball)All fingers ~35° curl
DIndex pointing, others curledIndex straight, others 65° curl
EAll fingers curled, thumb acrossProgressive curl (60°, 50°, 40°)
FOK sign (index-thumb circle)Index curled to thumb, others straight
GIndex pointing sidewaysIndex ~10°, others curled
HIndex and middle sidewaysIndex & middle ~10°, others curled
IPinky up, fistPinky straight, others curled (70°)
JSame as I (movement letter)Same as I
KIndex up, middle spread, thumb betweenIndex straight, middle spread (-5°), thumb toward middle
LL-shape (thumb and index)Thumb extended (-10°), index straight
MThumb under three fingersIndex/middle/ring curled over thumb
NThumb under two fingersIndex/middle curled over thumb
OAll fingertips touch thumbAll fingers ~45° curl forming O
PLike K (index up, middle angled)Index straight, middle angled (15°), thumb out
QThumb and index pinchingThumb (10°, 15°), index slight curl, tips close
RIndex and middle crossedIndex (-10° lateral), middle (+5° lateral) crossing
SFist with thumb over fingersAll curled (70°), thumb over (50°)
TThumb between index and middleIndex curled (65°), thumb tucked (40°)
UIndex and middle up togetherIndex & middle straight (0°), close together
VPeace sign (V shape)Index & middle spread (±10° lateral)
WThree fingers upIndex/middle/ring straight and spread
XIndex bent like hookIndex hooked (35°, 55°, 45°)
YHang loose (thumb and pinky)Thumb out (-15°), pinky straight, others curled
ZIndex pointing (draws Z)Index straight, others curled

Bone Structure Reference

The skeleton consists of 21 bones organized hierarchically:

Bone IDNameParentFunction
0Root-1Wrist anchor point
1Palm0Palm/hand base
2-4Thumb1→2→3Metacarpal → Proximal → Distal
5-8Index1→5→6→7Metacarpal → Proximal → Middle → Distal
9-12Middle1→9→10→11Metacarpal → Proximal → Middle → Distal
13-16Ring1→13→14→15Metacarpal → Proximal → Middle → Distal
17-20Pinky1→17→18→19Metacarpal → Proximal → Middle → Distal

JSON File Changes: Expanded Joint Limits

The chimpanzee-hand.json file was modified to expand joint angle limits (xzx_min and xzx_max) to allow more expressive finger movements required for ASL poses.

Key Changes

Bone TypeOriginal Lateral SpreadModified Lateral SpreadOriginal Curl MaxModified Curl Max
Metacarpals (5,9,13,17)±5°±10°±10°
Finger Joints±10°70°90°
Thumb BonesLimitedExpanded60-70°70°

Why These Changes Were Necessary

  1. Finger Spreading (V, W, K): Required expanded lateral rotation (first X component) on metacarpals and proximal joints to spread fingers apart
  2. Finger Crossing (R): Required lateral rotation on finger joints (originally locked at 0°) to allow index and middle fingers to cross
  3. Tighter Curling: Some poses needed curl beyond the original 70° limit for proper fist formation
  4. Thumb Mobility: Expanded range needed for various pinching and gripping poses (Q, F, O)

Acknowledgements

Libraries Used

  1. libigl (Geometry Processing Library)

  2. Eigen (C++ Linear Algebra Library)

  3. GLFW (Graphics Library Framework)

  4. nlohmann/json (JSON for Modern C++)

References

  1. American Sign Language (ASL) Alphabet