._pysketcher005.html 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  1. <!--
  2. Automatically generated HTML file from DocOnce source
  3. (https://github.com/hplgit/doconce/)
  4. -->
  5. <html>
  6. <head>
  7. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  8. <meta name="generator" content="DocOnce: https://github.com/hplgit/doconce/" />
  9. <meta name="description" content="Using Pysketcher to Create Principal Sketches of Physics Problems">
  10. <meta name="keywords" content="tree data structure,recursive function calls">
  11. <title>Using Pysketcher to Create Principal Sketches of Physics Problems</title>
  12. <!-- Bootstrap style: bootswatch_readable -->
  13. <link href="http://netdna.bootstrapcdn.com/bootswatch/3.1.1/readable/bootstrap.min.css" rel="stylesheet">
  14. <!-- not necessary
  15. <link href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
  16. -->
  17. <style type="text/css">
  18. /* Add scrollbar to dropdown menus in bootstrap navigation bar */
  19. .dropdown-menu {
  20. height: auto;
  21. max-height: 400px;
  22. overflow-x: hidden;
  23. }
  24. </style>
  25. </head>
  26. <!-- tocinfo
  27. {'highest level': 1,
  28. 'sections': [('A first glimpse of Pysketcher', 1, None, '___sec0'),
  29. ('Basic construction of sketches', 2, None, '___sec1'),
  30. ('Basic drawing', 3, None, '___sec2'),
  31. ('Groups of objects', 3, None, '___sec3'),
  32. ('Changing line styles and colors', 3, None, '___sec4'),
  33. ('The figure composition as an object hierarchy',
  34. 3,
  35. None,
  36. '___sec5'),
  37. ('Animation: translating the vehicle', 3, None, '___sec6'),
  38. ('Animation: rolling the wheels',
  39. 3,
  40. 'sketcher:vehicle1:anim',
  41. 'sketcher:vehicle1:anim'),
  42. ('A simple pendulum',
  43. 1,
  44. 'sketcher:ex:pendulum',
  45. 'sketcher:ex:pendulum'),
  46. ('The basic physics sketch',
  47. 2,
  48. 'sketcher:ex:pendulum:basic',
  49. 'sketcher:ex:pendulum:basic'),
  50. ('The body diagram', 2, None, '___sec10'),
  51. ('Animated body diagram',
  52. 2,
  53. 'sketcher:ex:pendulum:anim',
  54. 'sketcher:ex:pendulum:anim'),
  55. ('Function for drawing the body diagram', 3, None, '___sec12'),
  56. ('Equations for the motion and forces', 3, None, '___sec13'),
  57. ('Numerical solution', 3, None, '___sec14'),
  58. ('Animation', 3, None, '___sec15'),
  59. ('Basic shapes', 1, None, '___sec16'),
  60. ('Axis', 2, None, '___sec17'),
  61. ('Distance with text', 2, None, '___sec18'),
  62. ('Rectangle', 2, None, '___sec19'),
  63. ('Triangle', 2, None, '___sec20'),
  64. ('Arc', 2, None, '___sec21'),
  65. ('Spring', 2, None, '___sec22'),
  66. ('Dashpot', 2, None, '___sec23'),
  67. ('Wavy', 2, None, '___sec24'),
  68. ('Stochastic curves', 2, None, '___sec25'),
  69. ('Inner workings of the Pysketcher tool', 1, None, '___sec26'),
  70. ('Example of classes for geometric objects',
  71. 2,
  72. None,
  73. '___sec27'),
  74. ('Simple geometric objects', 3, None, '___sec28'),
  75. ('Class curve', 3, None, '___sec29'),
  76. ('Compound geometric objects', 3, None, '___sec30'),
  77. ('Adding functionality via recursion', 2, None, '___sec31'),
  78. ('Basic principles of recursion', 3, None, '___sec32'),
  79. ('Explaining recursion', 3, None, '___sec33'),
  80. ('Scaling, translating, and rotating a figure',
  81. 2,
  82. 'sketcher:scaling',
  83. 'sketcher:scaling'),
  84. ('Scaling', 3, None, '___sec35'),
  85. ('Translation', 3, None, '___sec36'),
  86. ('Rotation', 3, None, '___sec37')]}
  87. end of tocinfo -->
  88. <body>
  89. <script type="text/x-mathjax-config">
  90. MathJax.Hub.Config({
  91. TeX: {
  92. equationNumbers: { autoNumber: "none" },
  93. extensions: ["AMSmath.js", "AMSsymbols.js", "autobold.js", "color.js"]
  94. }
  95. });
  96. </script>
  97. <script type="text/javascript"
  98. src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
  99. </script>
  100. <!-- newcommands.tex -->
  101. $$
  102. \newcommand{\half}{\frac{1}{2}}
  103. \newcommand{\tp}{\thinspace .}
  104. \newcommand{\rpos}{\boldsymbol{r}}
  105. \newcommand{\ii}{\boldsymbol{i}}
  106. \newcommand{\jj}{\boldsymbol{j}}
  107. \newcommand{\ir}{\boldsymbol{i}_r}
  108. \newcommand{\ith}{\boldsymbol{i}_{\theta}}
  109. $$
  110. <!-- Bootstrap navigation bar -->
  111. <div class="navbar navbar-default navbar-fixed-top">
  112. <div class="navbar-header">
  113. <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-responsive-collapse">
  114. <span class="icon-bar"></span>
  115. <span class="icon-bar"></span>
  116. <span class="icon-bar"></span>
  117. </button>
  118. <a class="navbar-brand" href="pysketcher.html">Using Pysketcher to Create Principal Sketches of Physics Problems</a>
  119. </div>
  120. <div class="navbar-collapse collapse navbar-responsive-collapse">
  121. <ul class="nav navbar-nav navbar-right">
  122. <li class="dropdown">
  123. <a href="#" class="dropdown-toggle" data-toggle="dropdown">Contents <b class="caret"></b></a>
  124. <ul class="dropdown-menu">
  125. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec0" style="font-size: 80%;"><b>A first glimpse of Pysketcher</b></a></li>
  126. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec1" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Basic construction of sketches</a></li>
  127. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec2" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Basic drawing</a></li>
  128. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec3" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Groups of objects</a></li>
  129. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec4" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Changing line styles and colors</a></li>
  130. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec5" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The figure composition as an object hierarchy</a></li>
  131. <!-- navigation toc: --> <li><a href="._pysketcher002.html#___sec6" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Animation: translating the vehicle</a></li>
  132. <!-- navigation toc: --> <li><a href="._pysketcher002.html#sketcher:vehicle1:anim" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Animation: rolling the wheels</a></li>
  133. <!-- navigation toc: --> <li><a href="._pysketcher003.html#sketcher:ex:pendulum" style="font-size: 80%;"><b>A simple pendulum</b></a></li>
  134. <!-- navigation toc: --> <li><a href="._pysketcher003.html#sketcher:ex:pendulum:basic" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;The basic physics sketch</a></li>
  135. <!-- navigation toc: --> <li><a href="._pysketcher003.html#___sec10" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;The body diagram</a></li>
  136. <!-- navigation toc: --> <li><a href="._pysketcher003.html#sketcher:ex:pendulum:anim" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Animated body diagram</a></li>
  137. <!-- navigation toc: --> <li><a href="._pysketcher003.html#___sec12" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Function for drawing the body diagram</a></li>
  138. <!-- navigation toc: --> <li><a href="._pysketcher003.html#___sec13" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Equations for the motion and forces</a></li>
  139. <!-- navigation toc: --> <li><a href="._pysketcher003.html#___sec14" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Numerical solution</a></li>
  140. <!-- navigation toc: --> <li><a href="._pysketcher003.html#___sec15" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Animation</a></li>
  141. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec16" style="font-size: 80%;"><b>Basic shapes</b></a></li>
  142. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec17" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Axis</a></li>
  143. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec18" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Distance with text</a></li>
  144. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec19" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Rectangle</a></li>
  145. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec20" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Triangle</a></li>
  146. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec21" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Arc</a></li>
  147. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec22" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Spring</a></li>
  148. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec23" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Dashpot</a></li>
  149. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec24" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Wavy</a></li>
  150. <!-- navigation toc: --> <li><a href="._pysketcher004.html#___sec25" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Stochastic curves</a></li>
  151. <!-- navigation toc: --> <li><a href="#___sec26" style="font-size: 80%;"><b>Inner workings of the Pysketcher tool</b></a></li>
  152. <!-- navigation toc: --> <li><a href="#___sec27" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Example of classes for geometric objects</a></li>
  153. <!-- navigation toc: --> <li><a href="#___sec28" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Simple geometric objects</a></li>
  154. <!-- navigation toc: --> <li><a href="#___sec29" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class curve</a></li>
  155. <!-- navigation toc: --> <li><a href="#___sec30" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Compound geometric objects</a></li>
  156. <!-- navigation toc: --> <li><a href="#___sec31" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Adding functionality via recursion</a></li>
  157. <!-- navigation toc: --> <li><a href="#___sec32" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Basic principles of recursion</a></li>
  158. <!-- navigation toc: --> <li><a href="#___sec33" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Explaining recursion</a></li>
  159. <!-- navigation toc: --> <li><a href="#sketcher:scaling" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;Scaling, translating, and rotating a figure</a></li>
  160. <!-- navigation toc: --> <li><a href="#___sec35" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scaling</a></li>
  161. <!-- navigation toc: --> <li><a href="#___sec36" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Translation</a></li>
  162. <!-- navigation toc: --> <li><a href="#___sec37" style="font-size: 80%;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rotation</a></li>
  163. </ul>
  164. </li>
  165. </ul>
  166. </div>
  167. </div>
  168. </div> <!-- end of navigation bar -->
  169. <div class="container">
  170. <p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p> <!-- add vertical space -->
  171. <a name="part0005"></a>
  172. <!-- !split -->
  173. <h1 id="___sec26">Inner workings of the Pysketcher tool </h1>
  174. <p>
  175. We shall now explain how we can, quite easily, realize software with
  176. the capabilities demonstrated in the previous examples. Each object in
  177. the figure is represented as a class in a class hierarchy. Using
  178. inheritance, classes can inherit properties from parent classes and
  179. add new geometric features.
  180. <p>
  181. Class programming is a key technology for realizing Pysketcher.
  182. As soon as some classes are established, more are easily
  183. added. Enhanced functionality for all the classes is also easy to
  184. implement in common, generic code that can immediately be shared by
  185. all present and future classes. The fundamental data structure
  186. involved in the <code>pysketcher</code> package is a hierarchical tree, and much
  187. of the material on implementation issues targets how to traverse tree
  188. structures with recursive function calls in object hierarchies. This
  189. topic is of key relevance in a wide range of other applications as
  190. well. In total, the inner workings of Pysketcher constitute an
  191. excellent example on the power of class programming.
  192. <h2 id="___sec27">Example of classes for geometric objects </h2>
  193. <p>
  194. We introduce class <code>Shape</code> as superclass for all specialized objects
  195. in a figure. This class does not store any data, but provides a
  196. series of functions that add functionality to all the subclasses.
  197. This will be shown later.
  198. <h3 id="___sec28">Simple geometric objects </h3>
  199. <p>
  200. One simple subclass is <code>Rectangle</code>, specified by the coordinates of
  201. the lower left corner and its width and height:
  202. <p>
  203. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  204. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Rectangle</span>(Shape):
  205. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, lower_left_corner, width, height):
  206. p <span style="color: #666666">=</span> lower_left_corner <span style="color: #408080; font-style: italic"># short form</span>
  207. x <span style="color: #666666">=</span> [p[<span style="color: #666666">0</span>], p[<span style="color: #666666">0</span>] <span style="color: #666666">+</span> width,
  208. p[<span style="color: #666666">0</span>] <span style="color: #666666">+</span> width, p[<span style="color: #666666">0</span>], p[<span style="color: #666666">0</span>]]
  209. y <span style="color: #666666">=</span> [p[<span style="color: #666666">1</span>], p[<span style="color: #666666">1</span>], p[<span style="color: #666666">1</span>] <span style="color: #666666">+</span> height,
  210. p[<span style="color: #666666">1</span>] <span style="color: #666666">+</span> height, p[<span style="color: #666666">1</span>]]
  211. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;rectangle&#39;</span>: Curve(x,y)}
  212. </pre></div>
  213. <p>
  214. Any subclass of <code>Shape</code> will have a constructor that takes geometric
  215. information about the shape of the object and creates a dictionary
  216. <code>self.shapes</code> with the shape built of simpler shapes. The most
  217. fundamental shape is <code>Curve</code>, which is just a collection of \( (x,y) \)
  218. coordinates in two arrays <code>x</code> and <code>y</code>. Drawing the <code>Curve</code> object is
  219. a matter of plotting <code>y</code> versus <code>x</code>. For class <code>Rectangle</code> the <code>x</code>
  220. and <code>y</code> arrays contain the corner points of the rectangle in
  221. counterclockwise direction, starting and ending with in the lower left
  222. corner.
  223. <p>
  224. Class <code>Line</code> is also a simple class:
  225. <p>
  226. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  227. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Line</span>(Shape):
  228. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, start, end):
  229. x <span style="color: #666666">=</span> [start[<span style="color: #666666">0</span>], end[<span style="color: #666666">0</span>]]
  230. y <span style="color: #666666">=</span> [start[<span style="color: #666666">1</span>], end[<span style="color: #666666">1</span>]]
  231. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;line&#39;</span>: Curve(x, y)}
  232. </pre></div>
  233. <p>
  234. Here we only need two points, the start and end point on the line.
  235. However, we may want to add some useful functionality, e.g., the ability
  236. to give an \( x \) coordinate and have the class calculate the
  237. corresponding \( y \) coordinate:
  238. <p>
  239. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  240. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__call__</span>(<span style="color: #008000">self</span>, x):
  241. <span style="color: #BA2121; font-style: italic">&quot;&quot;&quot;Given x, return y on the line.&quot;&quot;&quot;</span>
  242. x, y <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;line&#39;</span>]<span style="color: #666666">.</span>x, <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;line&#39;</span>]<span style="color: #666666">.</span>y
  243. <span style="color: #008000">self</span><span style="color: #666666">.</span>a <span style="color: #666666">=</span> (y[<span style="color: #666666">1</span>] <span style="color: #666666">-</span> y[<span style="color: #666666">0</span>])<span style="color: #666666">/</span>(x[<span style="color: #666666">1</span>] <span style="color: #666666">-</span> x[<span style="color: #666666">0</span>])
  244. <span style="color: #008000">self</span><span style="color: #666666">.</span>b <span style="color: #666666">=</span> y[<span style="color: #666666">0</span>] <span style="color: #666666">-</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>a<span style="color: #666666">*</span>x[<span style="color: #666666">0</span>]
  245. <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>a<span style="color: #666666">*</span>x <span style="color: #666666">+</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>b
  246. </pre></div>
  247. <p>
  248. Unfortunately, this is too simplistic because vertical lines cannot be
  249. handled (infinite <code>self.a</code>). The true source code of <code>Line</code> therefore
  250. provides a more general solution at the cost of significantly longer
  251. code with more tests.
  252. <p>
  253. A circle implies a somewhat increased complexity. Again we represent
  254. the geometric object by a <code>Curve</code> object, but this time the <code>Curve</code>
  255. object needs to store a large number of points on the curve such that
  256. a plotting program produces a visually smooth curve. The points on
  257. the circle must be calculated manually in the constructor of class
  258. <code>Circle</code>. The formulas for points \( (x,y) \) on a curve with radius \( R \)
  259. and center at \( (x_0, y_0) \) are given by
  260. $$
  261. \begin{align*}
  262. x &= x_0 + R\cos (t),\\
  263. y &= y_0 + R\sin (t),
  264. \end{align*}
  265. $$
  266. where \( t\in [0, 2\pi] \). A discrete set of \( t \) values in this
  267. interval gives the corresponding set of \( (x,y) \) coordinates on
  268. the circle. The user must specify the resolution as the number
  269. of \( t \) values. The circle's radius and center must of course
  270. also be specified.
  271. <p>
  272. We can write the <code>Circle</code> class as
  273. <p>
  274. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  275. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Circle</span>(Shape):
  276. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, center, radius, resolution<span style="color: #666666">=180</span>):
  277. <span style="color: #008000">self</span><span style="color: #666666">.</span>center, <span style="color: #008000">self</span><span style="color: #666666">.</span>radius <span style="color: #666666">=</span> center, radius
  278. <span style="color: #008000">self</span><span style="color: #666666">.</span>resolution <span style="color: #666666">=</span> resolution
  279. t <span style="color: #666666">=</span> linspace(<span style="color: #666666">0</span>, <span style="color: #666666">2*</span>pi, resolution<span style="color: #666666">+1</span>)
  280. x0 <span style="color: #666666">=</span> center[<span style="color: #666666">0</span>]; y0 <span style="color: #666666">=</span> center[<span style="color: #666666">1</span>]
  281. R <span style="color: #666666">=</span> radius
  282. x <span style="color: #666666">=</span> x0 <span style="color: #666666">+</span> R<span style="color: #666666">*</span>cos(t)
  283. y <span style="color: #666666">=</span> y0 <span style="color: #666666">+</span> R<span style="color: #666666">*</span>sin(t)
  284. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;circle&#39;</span>: Curve(x, y)}
  285. </pre></div>
  286. <p>
  287. As in class <code>Line</code> we can offer the possibility to give an angle
  288. \( \theta \) (equivalent to \( t \) in the formulas above)
  289. and then get the corresponding \( x \) and \( y \) coordinates:
  290. <p>
  291. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  292. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__call__</span>(<span style="color: #008000">self</span>, theta):
  293. <span style="color: #BA2121; font-style: italic">&quot;&quot;&quot;Return (x, y) point corresponding to angle theta.&quot;&quot;&quot;</span>
  294. <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>center[<span style="color: #666666">0</span>] <span style="color: #666666">+</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>radius<span style="color: #666666">*</span>cos(theta), \
  295. <span style="color: #008000">self</span><span style="color: #666666">.</span>center[<span style="color: #666666">1</span>] <span style="color: #666666">+</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>radius<span style="color: #666666">*</span>sin(theta)
  296. </pre></div>
  297. <p>
  298. There is one flaw with this method: it yields illegal values after
  299. a translation, scaling, or rotation of the circle.
  300. <p>
  301. A part of a circle, an arc, is a frequent geometric object when
  302. drawing mechanical systems. The arc is constructed much like
  303. a circle, but \( t \) runs in \( [\theta_s, \theta_s + \theta_a] \). Giving
  304. \( \theta_s \) and \( \theta_a \) the slightly more descriptive names
  305. <code>start_angle</code> and <code>arc_angle</code>, the code looks like this:
  306. <p>
  307. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  308. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Arc</span>(Shape):
  309. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, center, radius,
  310. start_angle, arc_angle,
  311. resolution<span style="color: #666666">=180</span>):
  312. <span style="color: #008000">self</span><span style="color: #666666">.</span>start_angle <span style="color: #666666">=</span> radians(start_angle)
  313. <span style="color: #008000">self</span><span style="color: #666666">.</span>arc_angle <span style="color: #666666">=</span> radians(arc_angle)
  314. t <span style="color: #666666">=</span> linspace(<span style="color: #008000">self</span><span style="color: #666666">.</span>start_angle,
  315. <span style="color: #008000">self</span><span style="color: #666666">.</span>start_angle <span style="color: #666666">+</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>arc_angle,
  316. resolution<span style="color: #666666">+1</span>)
  317. x0 <span style="color: #666666">=</span> center[<span style="color: #666666">0</span>]; y0 <span style="color: #666666">=</span> center[<span style="color: #666666">1</span>]
  318. R <span style="color: #666666">=</span> radius
  319. x <span style="color: #666666">=</span> x0 <span style="color: #666666">+</span> R<span style="color: #666666">*</span>cos(t)
  320. y <span style="color: #666666">=</span> y0 <span style="color: #666666">+</span> R<span style="color: #666666">*</span>sin(t)
  321. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;arc&#39;</span>: Curve(x, y)}
  322. </pre></div>
  323. <p>
  324. Having the <code>Arc</code> class, a <code>Circle</code> can alternatively be defined as
  325. a subclass specializing the arc to a circle:
  326. <p>
  327. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  328. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Circle</span>(Arc):
  329. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, center, radius, resolution<span style="color: #666666">=180</span>):
  330. Arc<span style="color: #666666">.</span>__init__(<span style="color: #008000">self</span>, center, radius, <span style="color: #666666">0</span>, <span style="color: #666666">360</span>, resolution)
  331. </pre></div>
  332. <h3 id="___sec29">Class curve </h3>
  333. <p>
  334. Class <code>Curve</code> sits on the coordinates to be drawn, but how is that
  335. done? The constructor of class <code>Curve</code> just stores the coordinates,
  336. while a method <code>draw</code> sends the coordinates to the plotting program to
  337. make a graph. Or more precisely, to avoid a lot of (e.g.)
  338. Matplotlib-specific plotting commands in class <code>Curve</code> we have created
  339. a small layer with a simple programming interface to plotting
  340. programs. This makes it straightforward to change from Matplotlib to
  341. another plotting program. The programming interface is represented by
  342. the <code>drawing_tool</code> object and has a few functions:
  343. <ul>
  344. <li> <code>plot_curve</code> for sending a curve in terms of \( x \) and \( y \) coordinates
  345. to the plotting program,</li>
  346. <li> <code>set_coordinate_system</code> for specifying the graphics area,</li>
  347. <li> <code>erase</code> for deleting all elements of the graph,</li>
  348. <li> <code>set_grid</code> for turning on a grid (convenient while constructing the figure),</li>
  349. <li> <code>set_instruction_file</code> for creating a separate file with all
  350. plotting commands (Matplotlib commands in our case),</li>
  351. <li> a series of <code>set_X</code> functions where <code>X</code> is some property like
  352. <code>linecolor</code>, <code>linestyle</code>, <code>linewidth</code>, <code>filled_curves</code>.</li>
  353. </ul>
  354. This is basically all we need to communicate to a plotting program.
  355. <p>
  356. Any class in the <code>Shape</code> hierarchy inherits <code>set_X</code> functions for
  357. setting properties of curves. This information is propagated to
  358. all other shape objects in the <code>self.shapes</code> dictionary. Class
  359. <code>Curve</code> stores the line properties together with the coordinates
  360. of its curve and propagates this information to the plotting program.
  361. When saying <code>vehicle.set_linewidth(10)</code>, all objects that make
  362. up the <code>vehicle</code> object will get a <code>set_linewidth(10)</code> call,
  363. but only the <code>Curve</code> object at the end of the chain will actually
  364. store the information and send it to the plotting program.
  365. <p>
  366. A rough sketch of class <code>Curve</code> reads
  367. <p>
  368. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  369. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Curve</span>(Shape):
  370. <span style="color: #BA2121; font-style: italic">&quot;&quot;&quot;General curve as a sequence of (x,y) coordintes.&quot;&quot;&quot;</span>
  371. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, x, y):
  372. <span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">=</span> asarray(x, dtype<span style="color: #666666">=</span><span style="color: #008000">float</span>)
  373. <span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">=</span> asarray(y, dtype<span style="color: #666666">=</span><span style="color: #008000">float</span>)
  374. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">draw</span>(<span style="color: #008000">self</span>):
  375. drawing_tool<span style="color: #666666">.</span>plot_curve(
  376. <span style="color: #008000">self</span><span style="color: #666666">.</span>x, <span style="color: #008000">self</span><span style="color: #666666">.</span>y,
  377. <span style="color: #008000">self</span><span style="color: #666666">.</span>linestyle, <span style="color: #008000">self</span><span style="color: #666666">.</span>linewidth, <span style="color: #008000">self</span><span style="color: #666666">.</span>linecolor, <span style="color: #666666">...</span>)
  378. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">set_linewidth</span>(<span style="color: #008000">self</span>, width):
  379. <span style="color: #008000">self</span><span style="color: #666666">.</span>linewidth <span style="color: #666666">=</span> width
  380. det set_linestyle(<span style="color: #008000">self</span>, style):
  381. <span style="color: #008000">self</span><span style="color: #666666">.</span>linestyle <span style="color: #666666">=</span> style
  382. <span style="color: #666666">...</span>
  383. </pre></div>
  384. <h3 id="___sec30">Compound geometric objects </h3>
  385. <p>
  386. The simple classes <code>Line</code>, <code>Arc</code>, and <code>Circle</code> could can the geometric
  387. shape through just one <code>Curve</code> object. More complicated shapes are
  388. built from instances of various subclasses of <code>Shape</code>. Classes used
  389. for professional drawings soon get quite complex in composition and
  390. have a lot of geometric details, so here we prefer to make a very
  391. simple composition: the already drawn vehicle from Figure
  392. <a href="._pysketcher002.html#sketcher:fig:vehicle0">2</a>. That is, instead of composing the drawing
  393. in a Python program as shown above, we make a subclass <code>Vehicle0</code> in
  394. the <code>Shape</code> hierarchy for doing the same thing.
  395. <p>
  396. The <code>Shape</code> hierarchy is found in the <code>pysketcher</code> package, so to use these
  397. classes or derive a new one, we need to import <code>pysketcher</code>. The constructor
  398. of class <code>Vehicle0</code> performs approximately the same statements as
  399. in the example program we developed for making the drawing in
  400. Figure <a href="._pysketcher002.html#sketcher:fig:vehicle0">2</a>.
  401. <p>
  402. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  403. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pysketcher</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #666666">*</span>
  404. <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Vehicle0</span>(Shape):
  405. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, w_1, R, L, H):
  406. wheel1 <span style="color: #666666">=</span> Circle(center<span style="color: #666666">=</span>(w_1, R), radius<span style="color: #666666">=</span>R)
  407. wheel2 <span style="color: #666666">=</span> wheel1<span style="color: #666666">.</span>copy()
  408. wheel2<span style="color: #666666">.</span>translate((L,<span style="color: #666666">0</span>))
  409. under <span style="color: #666666">=</span> Rectangle(lower_left_corner<span style="color: #666666">=</span>(w_1<span style="color: #666666">-2*</span>R, <span style="color: #666666">2*</span>R),
  410. width<span style="color: #666666">=2*</span>R <span style="color: #666666">+</span> L <span style="color: #666666">+</span> <span style="color: #666666">2*</span>R, height<span style="color: #666666">=</span>H)
  411. over <span style="color: #666666">=</span> Rectangle(lower_left_corner<span style="color: #666666">=</span>(w_1, <span style="color: #666666">2*</span>R <span style="color: #666666">+</span> H),
  412. width<span style="color: #666666">=2.5*</span>R, height<span style="color: #666666">=1.25*</span>H)
  413. wheels <span style="color: #666666">=</span> Composition(
  414. {<span style="color: #BA2121">&#39;wheel1&#39;</span>: wheel1, <span style="color: #BA2121">&#39;wheel2&#39;</span>: wheel2})
  415. body <span style="color: #666666">=</span> Composition(
  416. {<span style="color: #BA2121">&#39;under&#39;</span>: under, <span style="color: #BA2121">&#39;over&#39;</span>: over})
  417. vehicle <span style="color: #666666">=</span> Composition({<span style="color: #BA2121">&#39;wheels&#39;</span>: wheels, <span style="color: #BA2121">&#39;body&#39;</span>: body})
  418. xmax <span style="color: #666666">=</span> w_1 <span style="color: #666666">+</span> <span style="color: #666666">2*</span>L <span style="color: #666666">+</span> <span style="color: #666666">3*</span>R
  419. ground <span style="color: #666666">=</span> Wall(x<span style="color: #666666">=</span>[R, xmax], y<span style="color: #666666">=</span>[<span style="color: #666666">0</span>, <span style="color: #666666">0</span>], thickness<span style="color: #666666">=-0.3*</span>R)
  420. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;vehicle&#39;</span>: vehicle, <span style="color: #BA2121">&#39;ground&#39;</span>: ground}
  421. </pre></div>
  422. <p>
  423. Any subclass of <code>Shape</code> <em>must</em> define the <code>shapes</code> attribute, otherwise
  424. the inherited <code>draw</code> method (and a lot of other methods too) will
  425. not work.
  426. <p>
  427. The painting of the vehicle, as shown in the right part of
  428. Figure <a href="._pysketcher002.html#sketcher:fig:vehicle0:v2">6</a>, could in class <code>Vehicle0</code>
  429. be offered by a method:
  430. <p>
  431. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  432. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">colorful</span>(<span style="color: #008000">self</span>):
  433. wheels <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;vehicle&#39;</span>][<span style="color: #BA2121">&#39;wheels&#39;</span>]
  434. wheels<span style="color: #666666">.</span>set_filled_curves(<span style="color: #BA2121">&#39;blue&#39;</span>)
  435. wheels<span style="color: #666666">.</span>set_linewidth(<span style="color: #666666">6</span>)
  436. wheels<span style="color: #666666">.</span>set_linecolor(<span style="color: #BA2121">&#39;black&#39;</span>)
  437. under <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;vehicle&#39;</span>][<span style="color: #BA2121">&#39;body&#39;</span>][<span style="color: #BA2121">&#39;under&#39;</span>]
  438. under<span style="color: #666666">.</span>set_filled_curves(<span style="color: #BA2121">&#39;red&#39;</span>)
  439. over <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;vehicle&#39;</span>][<span style="color: #BA2121">&#39;body&#39;</span>][<span style="color: #BA2121">&#39;over&#39;</span>]
  440. over<span style="color: #666666">.</span>set_filled_curves(pattern<span style="color: #666666">=</span><span style="color: #BA2121">&#39;/&#39;</span>)
  441. over<span style="color: #666666">.</span>set_linewidth(<span style="color: #666666">14</span>)
  442. </pre></div>
  443. <p>
  444. The usage of the class is simple: after having set up an appropriate
  445. coordinate system as previously shown, we can do
  446. <p>
  447. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  448. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%">vehicle <span style="color: #666666">=</span> Vehicle0(w_1, R, L, H)
  449. vehicle<span style="color: #666666">.</span>draw()
  450. drawing_tool<span style="color: #666666">.</span>display()
  451. </pre></div>
  452. <p>
  453. and go on the make a painted version by
  454. <p>
  455. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  456. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%">drawing_tool<span style="color: #666666">.</span>erase()
  457. vehicle<span style="color: #666666">.</span>colorful()
  458. vehicle<span style="color: #666666">.</span>draw()
  459. drawing_tool<span style="color: #666666">.</span>display()
  460. </pre></div>
  461. <p>
  462. A complete code defining and using class <code>Vehicle0</code> is found in the file
  463. <a href="http://tinyurl.com/ot733jn/vehicle2.py" target="_self"><tt>vehicle2.py</tt></a>.
  464. <p>
  465. The <code>pysketcher</code> package contains a wide range of classes for various
  466. geometrical objects, particularly those that are frequently used in
  467. drawings of mechanical systems.
  468. <h2 id="___sec31">Adding functionality via recursion </h2>
  469. <p>
  470. The really powerful feature of our class hierarchy is that we can add
  471. much functionality to the superclass <code>Shape</code> and to the &quot;bottom&quot; class
  472. <code>Curve</code>, and then all other classes for various types of geometrical shapes
  473. immediately get the new functionality. To explain the idea we may
  474. look at the <code>draw</code> method, which all classes in the <code>Shape</code>
  475. hierarchy must have. The inner workings of the <code>draw</code> method explain
  476. the secrets of how a series of other useful operations on figures
  477. can be implemented.
  478. <h3 id="___sec32">Basic principles of recursion </h3>
  479. <p>
  480. Note that we work with two types of hierarchies in the
  481. present documentation: one Python <em>class hierarchy</em>,
  482. with <code>Shape</code> as superclass, and one <em>object hierarchy</em> of figure elements
  483. in a specific figure. A subclass of <code>Shape</code> stores its figure in the
  484. <code>self.shapes</code> dictionary. This dictionary represents the object hierarchy
  485. of figure elements for that class. We want to make one <code>draw</code> call
  486. for an instance, say our class <code>Vehicle0</code>, and then we want this call
  487. to be propagated to <em>all</em> objects that are contained in
  488. <code>self.shapes</code> and all is nested subdictionaries. How is this done?
  489. <p>
  490. The natural starting point is to call <code>draw</code> for each <code>Shape</code> object
  491. in the <code>self.shapes</code> dictionary:
  492. <p>
  493. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  494. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">draw</span>(<span style="color: #008000">self</span>):
  495. <span style="color: #008000; font-weight: bold">for</span> shape <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes:
  496. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[shape]<span style="color: #666666">.</span>draw()
  497. </pre></div>
  498. <p>
  499. This general method can be provided by class <code>Shape</code> and inherited in
  500. subclasses like <code>Vehicle0</code>. Let <code>v</code> be a <code>Vehicle0</code> instance.
  501. Seemingly, a call <code>v.draw()</code> just calls
  502. <p>
  503. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  504. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%">v<span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;vehicle&#39;</span>]<span style="color: #666666">.</span>draw()
  505. v<span style="color: #666666">.</span>shapes[<span style="color: #BA2121">&#39;ground&#39;</span>]<span style="color: #666666">.</span>draw()
  506. </pre></div>
  507. <p>
  508. However, in the former call we call the <code>draw</code> method of a <code>Composition</code> object
  509. whose <code>self.shapes</code> attributed has two elements: <code>wheels</code> and <code>body</code>.
  510. Since class <code>Composition</code> inherits the same <code>draw</code> method, this method will
  511. run through <code>self.shapes</code> and call <code>wheels.draw()</code> and <code>body.draw()</code>.
  512. Now, the <code>wheels</code> object is also a <code>Composition</code> with the same <code>draw</code>
  513. method, which will run through <code>self.shapes</code>, now containing
  514. the <code>wheel1</code> and <code>wheel2</code> objects. The <code>wheel1</code> object is a <code>Circle</code>,
  515. so calling <code>wheel1.draw()</code> calls the <code>draw</code> method in class <code>Circle</code>,
  516. but this is the same <code>draw</code> method as shown above. This method will
  517. therefore traverse the circle's <code>shapes</code> dictionary, which we have seen
  518. consists of one <code>Curve</code> element.
  519. <p>
  520. The <code>Curve</code> object holds the coordinates to be plotted so here <code>draw</code>
  521. really needs to do something &quot;physical&quot;, namely send the coordinates to
  522. the plotting program. The <code>draw</code> method is outlined in the short listing
  523. of class <code>Curve</code> shown previously.
  524. <p>
  525. We can go to any of the other shape objects that appear in the figure
  526. hierarchy and follow their <code>draw</code> calls in the similar way. Every time,
  527. a <code>draw</code> call will invoke a new <code>draw</code> call, until we eventually hit
  528. a <code>Curve</code> object at the &quot;bottom&quot; of the figure hierarchy, and then that part
  529. of the figure is really plotted (or more precisely, the coordinates
  530. are sent to a plotting program).
  531. <p>
  532. When a method calls itself, such as <code>draw</code> does, the calls are known as
  533. <em>recursive</em> and the programming principle is referred to as
  534. <em>recursion</em>. This technique is very often used to traverse hierarchical
  535. structures like the figure structures we work with here. Even though the
  536. hierarchy of objects building up a figure are of different types, they
  537. all inherit the same <code>draw</code> method and therefore exhibit the same
  538. behavior with respect to drawing. Only the <code>Curve</code> object has a different
  539. <code>draw</code> method, which does not lead to more recursion.
  540. <h3 id="___sec33">Explaining recursion </h3>
  541. <p>
  542. Understanding recursion is usually a challenge. To get a better idea of
  543. how recursion works, we have equipped class <code>Shape</code> with a method <code>recurse</code>
  544. that just visits all the objects in the <code>shapes</code> dictionary and prints
  545. out a message for each object.
  546. This feature allows us to trace the execution and see exactly where
  547. we are in the hierarchy and which objects that are visited.
  548. <p>
  549. The <code>recurse</code> method is very similar to <code>draw</code>:
  550. <p>
  551. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  552. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">recurse</span>(<span style="color: #008000">self</span>, name, indent<span style="color: #666666">=0</span>):
  553. <span style="color: #408080; font-style: italic"># print message where we are (name is where we come from)</span>
  554. <span style="color: #008000; font-weight: bold">for</span> shape <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes:
  555. <span style="color: #408080; font-style: italic"># print message about which object to visit</span>
  556. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[shape]<span style="color: #666666">.</span>recurse(indent<span style="color: #666666">+2</span>, shape)
  557. </pre></div>
  558. <p>
  559. The <code>indent</code> parameter governs how much the message from this
  560. <code>recurse</code> method is intended. We increase <code>indent</code> by 2 for every
  561. level in the hierarchy, i.e., every row of objects in Figure
  562. <a href="#sketcher:fig:Vehicle0:hier2">11</a>. This indentation makes it easy to
  563. see on the printout how far down in the hierarchy we are.
  564. <p>
  565. A typical message written by <code>recurse</code> when <code>name</code> is <code>'body'</code> and
  566. the <code>shapes</code> dictionary has the keys <code>'over'</code> and <code>'under'</code>,
  567. will be
  568. <p>
  569. <!-- code=text (!bc dat) typeset with pygments style "default" -->
  570. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> Composition: body.shapes has entries &#39;over&#39;, &#39;under&#39;
  571. call body.shapes[&quot;over&quot;].recurse(&quot;over&quot;, 6)
  572. </pre></div>
  573. <p>
  574. The number of leading blanks on each line corresponds to the value of
  575. <code>indent</code>. The code printing out such messages looks like
  576. <p>
  577. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  578. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">recurse</span>(<span style="color: #008000">self</span>, name, indent<span style="color: #666666">=0</span>):
  579. space <span style="color: #666666">=</span> <span style="color: #BA2121">&#39; &#39;</span><span style="color: #666666">*</span>indent
  580. <span style="color: #008000; font-weight: bold">print</span> space, <span style="color: #BA2121">&#39;</span><span style="color: #BB6688; font-weight: bold">%s</span><span style="color: #BA2121">: </span><span style="color: #BB6688; font-weight: bold">%s</span><span style="color: #BA2121">.shapes has entries&#39;</span> <span style="color: #666666">%</span> \
  581. (<span style="color: #008000">self</span><span style="color: #666666">.</span>__class__<span style="color: #666666">.</span>__name__, name), \
  582. <span style="color: #008000">str</span>(<span style="color: #008000">list</span>(<span style="color: #008000">self</span><span style="color: #666666">.</span>shapes<span style="color: #666666">.</span>keys()))[<span style="color: #666666">1</span>:<span style="color: #666666">-1</span>]
  583. <span style="color: #008000; font-weight: bold">for</span> shape <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes:
  584. <span style="color: #008000; font-weight: bold">print</span> space,
  585. <span style="color: #008000; font-weight: bold">print</span> <span style="color: #BA2121">&#39;call </span><span style="color: #BB6688; font-weight: bold">%s</span><span style="color: #BA2121">.shapes[&quot;</span><span style="color: #BB6688; font-weight: bold">%s</span><span style="color: #BA2121">&quot;].recurse(&quot;</span><span style="color: #BB6688; font-weight: bold">%s</span><span style="color: #BA2121">&quot;, </span><span style="color: #BB6688; font-weight: bold">%d</span><span style="color: #BA2121">)&#39;</span> <span style="color: #666666">%</span> \
  586. (name, shape, shape, indent<span style="color: #666666">+2</span>)
  587. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[shape]<span style="color: #666666">.</span>recurse(shape, indent<span style="color: #666666">+2</span>)
  588. </pre></div>
  589. <p>
  590. Let us follow a <code>v.recurse('vehicle')</code> call in detail, <code>v</code> being
  591. a <code>Vehicle0</code> instance. Before looking into the output from <code>recurse</code>,
  592. let us get an overview of the figure hierarchy in the <code>v</code> object
  593. (as produced by <code>print v</code>)
  594. <p>
  595. <!-- code=text (!bc dat) typeset with pygments style "default" -->
  596. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%">ground
  597. wall
  598. vehicle
  599. body
  600. over
  601. rectangle
  602. under
  603. rectangle
  604. wheels
  605. wheel1
  606. arc
  607. wheel2
  608. arc
  609. </pre></div>
  610. <p>
  611. The <code>recurse</code> method performs the same kind of traversal of the
  612. hierarchy, but writes out and explains a lot more.
  613. <p>
  614. The data structure represented by <code>v.shapes</code> is known as a <em>tree</em>.
  615. As in physical trees, there is a <em>root</em>, here the <code>v.shapes</code>
  616. dictionary. A graphical illustration of the tree (upside down) is
  617. shown in Figure <a href="#sketcher:fig:Vehicle0:hier2">11</a>.
  618. From the root there are one or more branches, here two:
  619. <code>ground</code> and <code>vehicle</code>. Following the <code>vehicle</code> branch, it has two new
  620. branches, <code>body</code> and <code>wheels</code>. Relationships as in family trees
  621. are often used to describe the relations in object trees too: we say
  622. that <code>vehicle</code> is the parent of <code>body</code> and that <code>body</code> is a child of
  623. <code>vehicle</code>. The term <em>node</em> is also often used to describe an element
  624. in a tree. A node may have several other nodes as <em>descendants</em>.
  625. <p>
  626. <center> <!-- figure -->
  627. <hr class="figure">
  628. <center><p class="caption">Figure 11: Hierarchy of figure elements in an instance of class <code>Vehicle0</code>. <div id="sketcher:fig:Vehicle0:hier2"></div> </p></center>
  629. <p><img src="fig-tut/Vehicle0_hier2.png" align="bottom" width=600></p>
  630. </center>
  631. <p>
  632. Recursion is the principal programming technique to traverse tree structures.
  633. Any object in the tree can be viewed as a root of a subtree. For
  634. example, <code>wheels</code> is the root of a subtree that branches into
  635. <code>wheel1</code> and <code>wheel2</code>. So when processing an object in the tree,
  636. we imagine we process the root and then recurse into a subtree, but the
  637. first object we recurse into can be viewed as the root of the subtree, so the
  638. processing procedure of the parent object can be repeated.
  639. <p>
  640. A recommended next step is to simulate the <code>recurse</code> method by hand and
  641. carefully check that what happens in the visits to <code>recurse</code> is
  642. consistent with the output listed below. Although tedious, this is
  643. a major exercise that guaranteed will help to demystify recursion.
  644. <p>
  645. A part of the printout of <code>v.recurse('vehicle')</code> looks like
  646. <p>
  647. <!-- code=text (!bc dat) typeset with pygments style "default" -->
  648. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> Vehicle0: vehicle.shapes has entries &#39;ground&#39;, &#39;vehicle&#39;
  649. call vehicle.shapes[&quot;ground&quot;].recurse(&quot;ground&quot;, 2)
  650. Wall: ground.shapes has entries &#39;wall&#39;
  651. call ground.shapes[&quot;wall&quot;].recurse(&quot;wall&quot;, 4)
  652. reached &quot;bottom&quot; object Curve
  653. call vehicle.shapes[&quot;vehicle&quot;].recurse(&quot;vehicle&quot;, 2)
  654. Composition: vehicle.shapes has entries &#39;body&#39;, &#39;wheels&#39;
  655. call vehicle.shapes[&quot;body&quot;].recurse(&quot;body&quot;, 4)
  656. Composition: body.shapes has entries &#39;over&#39;, &#39;under&#39;
  657. call body.shapes[&quot;over&quot;].recurse(&quot;over&quot;, 6)
  658. Rectangle: over.shapes has entries &#39;rectangle&#39;
  659. call over.shapes[&quot;rectangle&quot;].recurse(&quot;rectangle&quot;, 8)
  660. reached &quot;bottom&quot; object Curve
  661. call body.shapes[&quot;under&quot;].recurse(&quot;under&quot;, 6)
  662. Rectangle: under.shapes has entries &#39;rectangle&#39;
  663. call under.shapes[&quot;rectangle&quot;].recurse(&quot;rectangle&quot;, 8)
  664. reached &quot;bottom&quot; object Curve
  665. ...
  666. </pre></div>
  667. <p>
  668. This example should clearly demonstrate the principle that we
  669. can start at any object in the tree and do a recursive set
  670. of calls with that object as root.
  671. <h2 id="sketcher:scaling">Scaling, translating, and rotating a figure</h2>
  672. <p>
  673. With recursion, as explained in the previous section, we can within
  674. minutes equip <em>all</em> classes in the <code>Shape</code> hierarchy, both present and
  675. future ones, with the ability to scale the figure, translate it,
  676. or rotate it. This added functionality requires only a few lines
  677. of code.
  678. <h3 id="___sec35">Scaling </h3>
  679. <p>
  680. We start with the simplest of the three geometric transformations,
  681. namely scaling. For a <code>Curve</code> instance containing a set of \( n \)
  682. coordinates \( (x_i,y_i) \) that make up a curve, scaling by a factor \( a \)
  683. means that we multiply all the \( x \) and \( y \) coordinates by \( a \):
  684. $$
  685. x_i \leftarrow ax_i,\quad y_i\leftarrow ay_i,
  686. \quad i=0,\ldots,n-1\thinspace .
  687. $$
  688. Here we apply the arrow as an assignment operator.
  689. The corresponding Python implementation in
  690. class <code>Curve</code> reads
  691. <p>
  692. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  693. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Curve</span>:
  694. <span style="color: #666666">...</span>
  695. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">scale</span>(<span style="color: #008000">self</span>, factor):
  696. <span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">=</span> factor<span style="color: #666666">*</span><span style="color: #008000">self</span><span style="color: #666666">.</span>x
  697. <span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">=</span> factor<span style="color: #666666">*</span><span style="color: #008000">self</span><span style="color: #666666">.</span>y
  698. </pre></div>
  699. <p>
  700. Note here that <code>self.x</code> and <code>self.y</code> are Numerical Python arrays,
  701. so that multiplication by a scalar number <code>factor</code> is
  702. a vectorized operation.
  703. <p>
  704. An even more efficient implementation is to make use of in-place
  705. multiplication in the arrays,
  706. <p>
  707. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  708. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Curve</span>:
  709. <span style="color: #666666">...</span>
  710. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">scale</span>(<span style="color: #008000">self</span>, factor):
  711. <span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">*=</span> factor
  712. <span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">*=</span> factor
  713. </pre></div>
  714. <p>
  715. as this saves the creation of temporary arrays like <code>factor*self.x</code>.
  716. <p>
  717. In an instance of a subclass of <code>Shape</code>, the meaning of a method
  718. <code>scale</code> is to run through all objects in the dictionary <code>shapes</code> and
  719. ask each object to scale itself. This is the same delegation of
  720. actions to subclass instances as we do in the <code>draw</code> (or <code>recurse</code>)
  721. method. All objects, except <code>Curve</code> instances, can share the same
  722. implementation of the <code>scale</code> method. Therefore, we place the <code>scale</code>
  723. method in the superclass <code>Shape</code> such that all subclasses inherit the
  724. method. Since <code>scale</code> and <code>draw</code> are so similar, we can easily
  725. implement the <code>scale</code> method in class <code>Shape</code> by copying and editing
  726. the <code>draw</code> method:
  727. <p>
  728. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  729. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Shape</span>:
  730. <span style="color: #666666">...</span>
  731. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">scale</span>(<span style="color: #008000">self</span>, factor):
  732. <span style="color: #008000; font-weight: bold">for</span> shape <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes:
  733. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[shape]<span style="color: #666666">.</span>scale(factor)
  734. </pre></div>
  735. <p>
  736. This is all we have to do in order to equip all subclasses of
  737. <code>Shape</code> with scaling functionality!
  738. Any piece of the figure will scale itself, in the same manner
  739. as it can draw itself.
  740. <h3 id="___sec36">Translation </h3>
  741. <p>
  742. A set of coordinates \( (x_i, y_i) \) can be translated \( v_0 \) units in
  743. the \( x \) direction and \( v_1 \) units in the \( y \) direction using the formulas
  744. $$
  745. \begin{equation*}
  746. x_i\leftarrow x_i+v_0,\quad y_i\leftarrow y_i+v_1,
  747. \quad i=0,\ldots,n-1\thinspace .
  748. \end{equation*}
  749. $$
  750. The natural specification of the translation is in terms of the
  751. vector \( v=(v_0,v_1) \).
  752. The corresponding Python implementation in class <code>Curve</code> becomes
  753. <p>
  754. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  755. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Curve</span>:
  756. <span style="color: #666666">...</span>
  757. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">translate</span>(<span style="color: #008000">self</span>, v):
  758. <span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">+=</span> v[<span style="color: #666666">0</span>]
  759. <span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">+=</span> v[<span style="color: #666666">1</span>]
  760. </pre></div>
  761. <p>
  762. The translation operation for a shape object is very similar to the
  763. scaling and drawing operations. This means that we can implement a
  764. common method <code>translate</code> in the superclass <code>Shape</code>. The code
  765. is parallel to the <code>scale</code> method:
  766. <p>
  767. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  768. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"><span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">Shape</span>:
  769. <span style="color: #666666">....</span>
  770. <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">translate</span>(<span style="color: #008000">self</span>, v):
  771. <span style="color: #008000; font-weight: bold">for</span> shape <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes:
  772. <span style="color: #008000">self</span><span style="color: #666666">.</span>shapes[shape]<span style="color: #666666">.</span>translate(v)
  773. </pre></div>
  774. <h3 id="___sec37">Rotation </h3>
  775. <p>
  776. Rotating a figure is more complicated than scaling and translating.
  777. A counter clockwise rotation of \( \theta \) degrees for a set of
  778. coordinates \( (x_i,y_i) \) is given by
  779. $$
  780. \begin{align*}
  781. \bar x_i &\leftarrow x_i\cos\theta - y_i\sin\theta,\\
  782. \bar y_i &\leftarrow x_i\sin\theta + y_i\cos\theta\thinspace .
  783. \end{align*}
  784. $$
  785. This rotation is performed around the origin. If we want the figure
  786. to be rotated with respect to a general point \( (x,y) \), we need to
  787. extend the formulas above:
  788. $$
  789. \begin{align*}
  790. \bar x_i &\leftarrow x + (x_i -x)\cos\theta - (y_i -y)\sin\theta,\\
  791. \bar y_i &\leftarrow y + (x_i -x)\sin\theta + (y_i -y)\cos\theta\thinspace .
  792. \end{align*}
  793. $$
  794. The Python implementation in class <code>Curve</code>, assuming that \( \theta \)
  795. is given in degrees and not in radians, becomes
  796. <p>
  797. <!-- code=python (!bc pycod) typeset with pygments style "default" -->
  798. <div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%"> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">rotate</span>(<span style="color: #008000">self</span>, angle, center):
  799. angle <span style="color: #666666">=</span> radians(angle)
  800. x, y <span style="color: #666666">=</span> center
  801. c <span style="color: #666666">=</span> cos(angle); s <span style="color: #666666">=</span> sin(angle)
  802. xnew <span style="color: #666666">=</span> x <span style="color: #666666">+</span> (<span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">-</span> x)<span style="color: #666666">*</span>c <span style="color: #666666">-</span> (<span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">-</span> y)<span style="color: #666666">*</span>s
  803. ynew <span style="color: #666666">=</span> y <span style="color: #666666">+</span> (<span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">-</span> x)<span style="color: #666666">*</span>s <span style="color: #666666">+</span> (<span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">-</span> y)<span style="color: #666666">*</span>c
  804. <span style="color: #008000">self</span><span style="color: #666666">.</span>x <span style="color: #666666">=</span> xnew
  805. <span style="color: #008000">self</span><span style="color: #666666">.</span>y <span style="color: #666666">=</span> ynew
  806. </pre></div>
  807. <p>
  808. The <code>rotate</code> method in class <code>Shape</code> follows the principle of the
  809. <code>draw</code>, <code>scale</code>, and <code>translate</code> methods.
  810. <p>
  811. We have already seen the <code>rotate</code> method in action when animating the
  812. rolling wheel at the end of the section <a href="._pysketcher002.html#sketcher:vehicle1:anim">Animation: rolling the wheels</a>.
  813. <p>
  814. <!-- navigation buttons at the bottom of the page -->
  815. <ul class="pagination">
  816. <li><a href="._pysketcher004.html">&laquo;</a></li>
  817. <li><a href="._pysketcher000.html">1</a></li>
  818. <li><a href="._pysketcher001.html">2</a></li>
  819. <li><a href="._pysketcher002.html">3</a></li>
  820. <li><a href="._pysketcher003.html">4</a></li>
  821. <li><a href="._pysketcher004.html">5</a></li>
  822. <li class="active"><a href="._pysketcher005.html">6</a></li>
  823. </ul>
  824. <!-- ------------------- end of main content --------------- -->
  825. </div> <!-- end container -->
  826. <!-- include javascript, jQuery *first* -->
  827. <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  828. <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
  829. <!-- Bootstrap footer
  830. <footer>
  831. <a href="http://..."><img width="250" align=right src="http://..."></a>
  832. </footer>
  833. -->
  834. </body>
  835. </html>