Fix x position of last solution segment in Serway and Jewett v8's 25.36.
[course.git] / asymptote / stickfigure.asy
1 /* sticfigure drawings in asymptote.
2  *
3  * Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19                         
20 struct StickFigure {
21   pair center;
22   real height;
23   real headHeightRatio;
24   real neckHeightRatio;
25   real torsoHeightRatio;
26   real shoulderHeightRatio;
27   real legHeightRatio;
28   real centerTorsoRatio;
29   real armTorsoRatio;
30   real rightArmAngle;
31   real leftArmAngle;
32   real rightLegAngle;
33   real leftLegAngle;
34   pen outline;
35   pen fill;
36
37   void operator init(pair standsOn=(0,0), real height=10mm) {
38     this.height = height;
39     this.headHeightRatio = 0.2;
40     this.neckHeightRatio = 0.1;
41     this.torsoHeightRatio = 0.28;
42     this.shoulderHeightRatio = 0.1;
43     /* at shoulder width, a leg takes this much height:
44      *   1 - head - neck - torso  (opposite legAngle)
45      * and this much width:
46      *   shoulder/2               (adjavent legAngle)
47      */
48     this.rightLegAngle = -aTan(
49         2*(1-this.headHeightRatio-this.neckHeightRatio-this.torsoHeightRatio)
50         /this.shoulderHeightRatio);
51     this.leftLegAngle = rightLegAngle;
52     this.legHeightRatio = this.shoulderHeightRatio/2/Cos(this.rightLegAngle);
53     this.centerTorsoRatio = 0.2;
54     this.armTorsoRatio = 1.2;
55     this.rightArmAngle = -70;
56     this.leftArmAngle = -70;
57     this.outline = currentpen;
58     this.fill = currentpen;
59
60     this.center = standsOn +
61       (0, this.height*(this.legHeightRatio*Sin(-this.rightLegAngle)
62                        +this.torsoHeightRatio*this.centerTorsoRatio));
63   }
64
65   /* center a fixed torso fraction (centerTorsoRatio) above tail.
66    * Everything else located from that point.
67    */
68
69   pair tailPos() {
70     return this.center
71       - (0,this.height*this.torsoHeightRatio*this.centerTorsoRatio);
72   }
73
74   pair neckBasePos() {
75     return this.tailPos() + (0,this.height*this.torsoHeightRatio);
76   }
77
78   pair chinPos() {
79     return this.neckBasePos() + (0,this.height*this.neckHeightRatio);
80   }
81
82   pair crownPos() {
83     return this.chinPos() + (0,this.height*this.headHeightRatio);
84   }
85
86   pair headPos() {
87     return (this.crownPos() + this.chinPos())/2;
88   }
89
90   pair shoulderPos(bool leftSide=false) {
91     real sign = 1;
92     if (leftSide == true)
93       sign = -1;
94     return this.neckBasePos() + (sign*this.height*this.shoulderHeightRatio/2,0);
95   }
96
97   pair handPos(bool leftSide=false) {
98     real len = this.height*this.torsoHeightRatio*this.armTorsoRatio;
99     real ang;
100     if (leftSide == true) {
101       ang = 180 - this.leftArmAngle;
102     } else{ 
103       ang = this.rightArmAngle;
104     }
105     return this.shoulderPos(leftSide=leftSide) + len * dir(ang);
106   }
107
108
109   pair footPos(bool leftSide=false) {
110     real len = this.height*this.legHeightRatio;
111     real ang;
112     if (leftSide == true) {
113       ang = 180 - this.leftLegAngle;
114     } else{ 
115       ang = this.rightLegAngle;
116     }
117     return this.tailPos() + len * dir(ang);
118   }
119
120   pair standsOnPos() {
121     return (this.footPos() + this.footPos(leftSide=true))/2;
122   }
123
124   void standsOn(pair pos) {
125     this.center += pos - this.standsOnPos();
126   }
127
128   void draw(picture pic=currentpicture) {
129     path head =
130       shift(this.headPos())
131       *scale(this.height*this.headHeightRatio/2)
132       *unitcircle;
133     path neckAndTorso =
134       this.chinPos()
135       --this.neckBasePos()
136       --this.tailPos();
137     path armsAndShoulders =
138       this.handPos(leftSide=false)
139       --this.shoulderPos(leftSide=false)
140       --this.shoulderPos(leftSide=true)
141       --this.handPos(leftSide=true);
142     path legs =
143       this.footPos(leftSide=false)
144       --this.tailPos()
145       --this.footPos(leftSide=true);
146     draw(pic, armsAndShoulders, this.outline);
147     draw(pic, legs, this.outline);
148     draw(pic, neckAndTorso, this.outline);
149     dot(this.handPos(leftSide=false), this.fill);
150     dot(this.handPos(leftSide=true), this.fill);
151     dot(this.footPos(leftSide=false), this.fill);
152     dot(this.footPos(leftSide=true), this.fill);
153     filldraw(pic, head, this.fill, this.outline);
154   }
155 }