%
% Very simple model of my hand
%
% Gene Ressler
%
% In particular, this completely ignores the carpal joints.
%
% Try this with various options to sketch.
%
% -D fist   \
% -D ok      > none or one of these
% -D spread /
%
% -D topview   \
% -D frontview  > none or one of these
% -D sideview  /
%
% -D repeated
%
% I am not responsible for modifications to draw
% obscene gestures.

% parameterization of model

% for fingers, 0 is thumb, 1 is index,
%   2 is middle, 3 is ring, 4 is little

% lateral angle between fingers
def spread_rot
 <fist> 0
 <ok> 5
 <spread>10
 <> 0
% and between thumb and index finger
def spread_rot_0
 <fist> 40
 <ok> 55
 <spread> 55
 <> 25

% rotations of finger parts
% distal is the finger tip
% middle is below that
% meta is the knuckle
def distal_0_rot
 <fist> 60
 <ok> 45
 <> -10
def middle_0_rot
 <fist> 50
 <ok> 40
 <spread> 0
 <> 10
def meta_0_rot
 <fist> 40
 <ok> 33
 <> 0

def distal_1_rot
 <fist> 90
 <ok> 60
 <> 0
def meta_1_rot
 <fist> 90
 <ok> 55
 <> 0

def distal_2_rot
 <fist> 90
 <ok> 30
 <> 0
def meta_2_rot
 <fist> 90
 <ok> 35
 <> 0

def distal_3_rot
 <fist> 90
 <ok> 30
 <> 0
def meta_3_rot
 <fist> 90
 <ok> 30
 <> 0

def distal_4_rot
 <fist> 90
 <ok> 30
 <> 0
def meta_4_rot
 <fist> 90
 <ok> 25
 <> 0

% end parameters

% useful stuff
def O (0,0,0)
def I [1,0,0]
def J [0,1,0]
def K [0,0,1]

% dependent rotations
% fingers have the last two joints wired together
def middle_1_rot distal_1_rot
def middle_2_rot distal_2_rot
def middle_3_rot distal_3_rot
def middle_4_rot distal_4_rot

% proportions
def proximal_rad           .6
def distal_rad             .5
def distal_len            1.8
def joint_rad              .6
def joint_gap              .7
def middle_ratio          1.8
def proximal_distal_ratio  proximal_rad / distal_rad

% primitive segment of a finger is a truncated cone
def segment {
 def n_faces 8
 sweep { n_faces<>, rotate(360 / n_faces, [J]) }
   line(proximal_rad, 0)(distal_rad, distal_len)
}

% spheres to connect segments at joints
def joint_sphere {
 def n_joint_faces 8
 sweep [fillcolor=red] { n_joint_faces, rotate(360 / n_joint_faces, [J]) }
   sweep { n_joint_faces, rotate(180 / n_joint_faces) }
     (0, -joint_rad)
}

% following is five separate definitions for five fingers
% with parameters, this would be much shorter!

def distal_0 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(distal_0_rot, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {segment}
 put { rotate(distal_0_rot / 2, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( [J] + proximal_distal_ratio * ([I]+[K]) ) }
   {segment}
}

def finger_0 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(middle_0_rot, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {distal_0}
 put { scale(proximal_distal_ratio)
       then rotate(middle_0_rot / 2, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( middle_ratio * [J] + proximal_distal_ratio^2 * ([I]+[K]) ) }
   {segment}
}

def distal_1 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(distal_1_rot, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {segment}
 put { rotate(distal_1_rot / 2, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( [J] + proximal_distal_ratio * ([I]+[K]) ) }
   {segment}
}

def finger_1 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(middle_1_rot, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {distal_1}
 put { scale(proximal_distal_ratio)
       then rotate(middle_1_rot / 2, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( middle_ratio * [J] + proximal_distal_ratio^2 * ([I]+[K]) ) }
   {segment}
}

def distal_2 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(distal_2_rot, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {segment}
 put { rotate(distal_2_rot / 2, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( [J] + proximal_distal_ratio * ([I]+[K]) ) }
   {segment}
}

def finger_2 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(middle_2_rot, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {distal_2}
 put { scale(proximal_distal_ratio)
       then rotate(middle_2_rot / 2, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( middle_ratio * [J] + proximal_distal_ratio^2 * ([I]+[K]) ) }
   {segment}
}

def distal_3 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(distal_3_rot, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {segment}
 put { rotate(distal_3_rot / 2, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( [J] + proximal_distal_ratio * ([I]+[K]) ) }
   {segment}
}

def finger_3 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(middle_3_rot, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {distal_3}
 put { scale(proximal_distal_ratio)
       then rotate(middle_3_rot / 2, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( middle_ratio * [J] + proximal_distal_ratio^2 * ([I]+[K]) ) }
   {segment}
}

def distal_4 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(distal_4_rot, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {segment}
 put { rotate(distal_4_rot / 2, [I])
       then translate((distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( [J] + proximal_distal_ratio * ([I]+[K]) ) }
   {segment}
}

def finger_4 {
 put { translate(joint_gap * joint_rad * [J])
       then rotate(middle_4_rot, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {distal_4}
 put { scale(proximal_distal_ratio)
       then rotate(middle_4_rot / 2, [I])
       then translate((middle_ratio * distal_len + joint_gap * joint_rad) * [J]) }
   {joint_sphere}
 put { scale( middle_ratio * [J] + proximal_distal_ratio^2 * ([I]+[K]) ) }
   {segment}
}

% points on the palm of the hand
def proximal_0_loc (1.8,-5.5,0)
def proximal_1_loc (1.8,.1,0)
def proximal_2_loc (O)
def proximal_3_loc (-1.8,-.2,0)
def proximal_4_loc (-3.6,-.5,0)
def h5 (proximal_4_loc) + [-.6,-.2]
def h6 (h5) + [1,-5]
def h8 (proximal_0_loc) + [.75,-.5]
def h7 (h8) + [-.6,-.8]
def h6a (h6) + .6 * ((h7) - (h6))
def h9 (h8) + [-1.9,1]
def h10 (proximal_1_loc) + [.85,-.3]

def hand {

 % thumb has an extra rotation for opposable-ness!
 def opposition_rot rotate(-50, [J])
 def thk_scale_0 1.2
 put { scale([thk_scale_0,.9,thk_scale_0]) % this distorts a little; oh well
       then translate((joint_gap * joint_rad) * [J])
       then [[opposition_rot]]
       then rotate(meta_0_rot, [I])
       then rotate(-spread_rot_0, [K])
       then translate((proximal_0_loc) - (O)) }
   {finger_0}

 put { scale(thk_scale_0 * proximal_distal_ratio^2)
       then [[opposition_rot]]
       then rotate(meta_0_rot / 2, [I])
       then rotate(-spread_rot_0, [K])
       then translate((proximal_0_loc) - (O)) }
   {joint_sphere}

 % index finger
 def scale_1 .85
 put { scale(scale_1)
       then translate((joint_gap * joint_rad) * [J])
       then rotate(meta_1_rot, [I])
       then rotate(-spread_rot, [K])
       then translate((proximal_1_loc) - (O)) }
   {finger_1}

 put { scale(scale_1 * proximal_distal_ratio^2)
       then rotate(meta_1_rot / 2, [I])
       then rotate(-spread_rot, [K])
       then translate((proximal_1_loc) - (O)) }
   {joint_sphere}

 % middle finger
 put { % no scale then
       translate((joint_gap * joint_rad) * [J])
       then rotate(meta_2_rot, [I])
       % no spread rotation
       then translate((proximal_2_loc) - (O)) }
   {finger_2}

 put { scale(proximal_distal_ratio^2)
       then rotate(meta_2_rot / 2, [I])
       then translate((proximal_2_loc) - (O)) }
   {joint_sphere}

 % ring finger
 def scale_3 .85
 put { scale(scale_3)
       then translate((joint_gap * joint_rad) * [J])
       then rotate(meta_3_rot, [I])
       then rotate(spread_rot, [K])
       then translate((proximal_3_loc) - (O)) }
   {finger_3}

 put { scale(scale_3 * proximal_distal_ratio^2)
       then rotate(meta_3_rot / 2, [I])
       then rotate(spread_rot, [K])
       then translate((proximal_3_loc) - (O)) }
   {joint_sphere}

 % little finger
 def scale_4 .7
 put { scale(scale_4)
       then translate((joint_gap * joint_rad) * [J])
       then rotate(meta_4_rot, [I])
       then rotate(2 * spread_rot, [K])
       then translate((proximal_4_loc) - (O)) }
   {finger_4}

 put { scale(scale_4 * proximal_distal_ratio^2)
       then rotate(meta_4_rot / 2, [I])
       then rotate(2 * spread_rot, [K])
       then translate((proximal_4_loc) - (O)) }
   {joint_sphere}

 % palm is built by sweeping a polygon through a small
 % angle in order to make it thicker at the wrist
 put { translate(joint_gap * joint_rad * -[J]) } % drop polytope to expose knuckles
   sweep { 1, rotate(6, (0,15,0), [I]) }
     put { rotate(-3, (0,15,0), [I]) } {
       % need two polygons for convexity; the desired shape is concave at the thumb
       polygon(proximal_1_loc)(proximal_2_loc)(proximal_3_loc)(proximal_4_loc)
              (h5)(h6)(h6a)(h9)(h10)
       polygon(h6a)(h7)(h8)(h9)
     }
}

% a few views
def viewxf
<frontview> view((0,0,10))
<sideview>  view((10,0,0))
<topview>   view((0,10,0), (O), -[K])
<>          view((7,3,10))

% either a single copy or a repeat to show different angles
def scene
 <repeated>
   put { [[viewxf]] then scale(.25) }  {
     def N 4
     repeat { N, rotate(270/N, [3,2,1]), translate(14*[I]) } {hand}
   }
 <> put { [[viewxf]] then scale(.3) } {hand}

{scene}