wrap_sketcher.html 130 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head>
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  6. <title>Using Pysketcher to Create Principal Sketches of Physics Problems &mdash; Using Pysketcher to Create Principal Sketches of Physics Problems 1.0 documentation</title>
  7. <link rel="stylesheet" href="_static/pyramid.css" type="text/css" />
  8. <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
  9. <script type="text/javascript">
  10. var DOCUMENTATION_OPTIONS = {
  11. URL_ROOT: '',
  12. VERSION: '1.0',
  13. COLLAPSE_INDEX: false,
  14. FILE_SUFFIX: '.html',
  15. HAS_SOURCE: true
  16. };
  17. </script>
  18. <script type="text/javascript" src="_static/jquery.js"></script>
  19. <script type="text/javascript" src="_static/underscore.js"></script>
  20. <script type="text/javascript" src="_static/doctools.js"></script>
  21. <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
  22. <link rel="top" title="Using Pysketcher to Create Principal Sketches of Physics Problems 1.0 documentation" href="index.html" />
  23. <link rel="prev" title="Using Pysketcher to Create Principal Sketches of Physics Problems" href="index.html" />
  24. <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  25. <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  26. <!--[if lte IE 6]>
  27. <link rel="stylesheet" href="_static/ie6.css" type="text/css" media="screen" charset="utf-8" />
  28. <![endif]-->
  29. </head>
  30. <body>
  31. <div class="related">
  32. <h3>Navigation</h3>
  33. <ul>
  34. <li class="right" style="margin-right: 10px">
  35. <a href="genindex.html" title="General Index"
  36. accesskey="I">index</a></li>
  37. <li class="right" >
  38. <a href="index.html" title="Using Pysketcher to Create Principal Sketches of Physics Problems"
  39. accesskey="P">previous</a> |</li>
  40. <li><a href="index.html">Using Pysketcher to Create Principal Sketches of Physics Problems 1.0 documentation</a> &raquo;</li>
  41. </ul>
  42. </div>
  43. <div class="document">
  44. <div class="documentwrapper">
  45. <div class="bodywrapper">
  46. <div class="body">
  47. <div class="section" id="using-pysketcher-to-create-principal-sketches-of-physics-problems">
  48. <h1>Using Pysketcher to Create Principal Sketches of Physics Problems<a class="headerlink" href="#using-pysketcher-to-create-principal-sketches-of-physics-problems" title="Permalink to this headline">¶</a></h1>
  49. <table class="docutils field-list" frame="void" rules="none">
  50. <col class="field-name" />
  51. <col class="field-body" />
  52. <tbody valign="top">
  53. <tr class="field-odd field"><th class="field-name">Author:</th><td class="field-body">Hans Petter Langtangen</td>
  54. </tr>
  55. <tr class="field-even field"><th class="field-name">Date:</th><td class="field-body">Apr 1, 2012</td>
  56. </tr>
  57. </tbody>
  58. </table>
  59. <p><em>Abstract.</em> Pysketcher is a Python package which allows principal sketches of
  60. physics and mechanics problems to be realized through short programs
  61. instead of interactive (and potentially tedious and inaccurate)
  62. drawing. Elements of the sketch, such as lines, circles, angles,
  63. forces, coordinate systems, etc., are realized as objects and
  64. collected in hierarchical structures. Parts of the hierarchical
  65. structures can easily change line styles and colors, or be copied,
  66. scaled, translated, and rotated. These features make it
  67. straightforward to move parts of the sketch to create animation,
  68. usually in accordance with the physics of the underlying problem.
  69. Exact dimensioning of the elements in the sketch is trivial to obtain
  70. since distances are specified in computer code.</p>
  71. <p>Pysketcher is easy to learn from a number of examples. Beyond
  72. essential Python programming and a knowledge about mechanics problems,
  73. no further background is required.</p>
  74. </div>
  75. <div class="section" id="a-first-glimpse-of-pysketcher">
  76. <h1>A First Glimpse of Pysketcher<a class="headerlink" href="#a-first-glimpse-of-pysketcher" title="Permalink to this headline">¶</a></h1>
  77. <p>Formulation of physical problems makes heavy use of <em>principal sketches</em>
  78. such as the one in Figure <a class="reference internal" href="#sketcher-fig-inclinedplane"><em>Sketch of a physics problem</em></a>.
  79. This particular sketch illustrates the classical mechanics problem
  80. of a rolling wheel on an inclined plane.
  81. The figure
  82. is made up many individual elements: a rectangle
  83. filled with a pattern (the inclined plane), a hollow circle with color
  84. (the wheel), arrows with label (the <span class="math">\(N\)</span> and <span class="math">\(Mg\)</span> forces, and the <span class="math">\(x\)</span>
  85. axis), an angle with symbol <span class="math">\(\theta\)</span>, and a dashed line indicating the
  86. starting location of the wheel. Drawing software and plotting
  87. programs can produce such figures quite easily in principle, but the
  88. amount of details the user needs to control with the mouse can be
  89. substantial. Software more tailored to producing sketches of this type
  90. would work with more convenient abstractions, such as circle, wall,
  91. angle, force arrow, axis, and so forth. And as soon we start
  92. <em>programming</em> to construct the figure we get a range of other
  93. powerful tools at disposal. For example, we can easily translate and
  94. rotate parts of the figure and make an animation that illustrates the
  95. physics of the problem.</p>
  96. <div class="figure" id="sketcher-fig-inclinedplane">
  97. <img alt="_images/wheel_on_inclined_plane.png" src="_images/wheel_on_inclined_plane.png" style="width: 600px;" />
  98. <p class="caption"><em>Sketch of a physics problem</em></p>
  99. </div>
  100. <div class="section" id="basic-construction-of-sketches">
  101. <h2>Basic Construction of Sketches<a class="headerlink" href="#basic-construction-of-sketches" title="Permalink to this headline">¶</a></h2>
  102. <p>Before attacking real-life sketches as in Figure <em class="xref std std-ref">sketcher:fig1</em>
  103. we focus on the significantly simpler drawing shown in
  104. in Figure <a class="reference internal" href="#sketcher-fig-vehicle0"><em>Sketch of a simple figure</em></a>. This toy sketch consists of
  105. several elements: two circles, two rectangles, and a &#8220;ground&#8221; element.</p>
  106. <div class="figure" id="sketcher-fig-vehicle0">
  107. <img alt="_images/vehicle0_dim.png" src="_images/vehicle0_dim.png" style="width: 600px;" />
  108. <p class="caption"><em>Sketch of a simple figure</em></p>
  109. </div>
  110. <div class="section" id="basic-drawing">
  111. <h3>Basic Drawing<a class="headerlink" href="#basic-drawing" title="Permalink to this headline">¶</a></h3>
  112. <p>A typical program creating these five elements is shown next.
  113. After importing the <tt class="docutils literal"><span class="pre">pysketcher</span></tt> package, the first task is always to
  114. define a coordinate system. Some graphics operations are done with
  115. a helper object called <tt class="docutils literal"><span class="pre">drawing_tool</span></tt> (imported from <tt class="docutils literal"><span class="pre">pysketcher</span></tt>).
  116. With the drawing area in place we can make the first <tt class="docutils literal"><span class="pre">Circle</span></tt> object
  117. in an intuitive fashion:</p>
  118. <div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">pysketcher</span> <span class="kn">import</span> <span class="o">*</span>
  119. <span class="n">R</span> <span class="o">=</span> <span class="mi">1</span> <span class="c"># radius of wheel</span>
  120. <span class="n">L</span> <span class="o">=</span> <span class="mi">4</span> <span class="c"># distance between wheels</span>
  121. <span class="n">H</span> <span class="o">=</span> <span class="mi">2</span> <span class="c"># height of vehicle body</span>
  122. <span class="n">w_1</span> <span class="o">=</span> <span class="mi">5</span> <span class="c"># position of front wheel</span>
  123. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">set_coordinate_system</span><span class="p">(</span><span class="n">xmin</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">xmax</span><span class="o">=</span><span class="n">w_1</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">L</span> <span class="o">+</span> <span class="mi">3</span><span class="o">*</span><span class="n">R</span><span class="p">,</span>
  124. <span class="n">ymin</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ymax</span><span class="o">=</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span> <span class="o">+</span> <span class="mi">3</span><span class="o">*</span><span class="n">H</span><span class="p">)</span>
  125. <span class="n">wheel1</span> <span class="o">=</span> <span class="n">Circle</span><span class="p">(</span><span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">),</span> <span class="n">radius</span><span class="o">=</span><span class="n">R</span><span class="p">)</span>
  126. </pre></div>
  127. </div>
  128. <p>By using symbols for characteristic lengths in the drawing, rather than
  129. absolute lengths, it is easier
  130. to change dimensions later.</p>
  131. <p>To translate the geometric information about the <tt class="docutils literal"><span class="pre">wheel1</span></tt> object to
  132. instructions for the plotting engine (in this case Matplotlib), one calls the
  133. <tt class="docutils literal"><span class="pre">wheel1.draw()</span></tt>. To display all drawn objects, one issues
  134. <tt class="docutils literal"><span class="pre">drawing_tool.display()</span></tt>. The typical steps are hence:</p>
  135. <div class="highlight-python"><div class="highlight"><pre><span class="n">wheel1</span> <span class="o">=</span> <span class="n">Circle</span><span class="p">(</span><span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">),</span> <span class="n">radius</span><span class="o">=</span><span class="n">R</span><span class="p">)</span>
  136. <span class="n">wheel1</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  137. <span class="c"># Define other objects and call their draw() methods</span>
  138. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">display</span><span class="p">()</span>
  139. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.png&#39;</span><span class="p">)</span> <span class="c"># store picture</span>
  140. </pre></div>
  141. </div>
  142. <p>The next wheel can be made by taking a copy of <tt class="docutils literal"><span class="pre">wheel1</span></tt> and
  143. translating the object a distance (to the right) described by the
  144. vector <span class="math">\((4,0)\)</span>:</p>
  145. <div class="highlight-python"><div class="highlight"><pre><span class="n">wheel2</span> <span class="o">=</span> <span class="n">wheel1</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
  146. <span class="n">wheel2</span><span class="o">.</span><span class="n">translate</span><span class="p">((</span><span class="n">L</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
  147. </pre></div>
  148. </div>
  149. <p>The two rectangles are made in an intuitive way:</p>
  150. <div class="highlight-python"><div class="highlight"><pre><span class="n">under</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">lower_left_corner</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">),</span>
  151. <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span> <span class="o">+</span> <span class="n">L</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="n">H</span><span class="p">)</span>
  152. <span class="n">over</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">lower_left_corner</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span> <span class="o">+</span> <span class="n">H</span><span class="p">),</span>
  153. <span class="n">width</span><span class="o">=</span><span class="mf">2.5</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mf">1.25</span><span class="o">*</span><span class="n">H</span><span class="p">)</span>
  154. </pre></div>
  155. </div>
  156. </div>
  157. <div class="section" id="groups-of-objects">
  158. <h3>Groups of Objects<a class="headerlink" href="#groups-of-objects" title="Permalink to this headline">¶</a></h3>
  159. <p>Instead of calling the <tt class="docutils literal"><span class="pre">draw</span></tt> method of every object, we can
  160. group objects and call <tt class="docutils literal"><span class="pre">draw</span></tt>, or perform other operations, for
  161. the whole group. For example, we may collect the two wheels
  162. in a <tt class="docutils literal"><span class="pre">wheels</span></tt> group and the <tt class="docutils literal"><span class="pre">over</span></tt> and <tt class="docutils literal"><span class="pre">under</span></tt> rectangles
  163. in a <tt class="docutils literal"><span class="pre">body</span></tt> group. The whole vehicle is a composition
  164. of its <tt class="docutils literal"><span class="pre">wheels</span></tt> and <tt class="docutils literal"><span class="pre">body</span></tt> groups. The codes goes like</p>
  165. <div class="highlight-python"><div class="highlight"><pre><span class="n">wheels</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">({</span><span class="s">&#39;wheel1&#39;</span><span class="p">:</span> <span class="n">wheel1</span><span class="p">,</span> <span class="s">&#39;wheel2&#39;</span><span class="p">:</span> <span class="n">wheel2</span><span class="p">})</span>
  166. <span class="n">body</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">({</span><span class="s">&#39;under&#39;</span><span class="p">:</span> <span class="n">under</span><span class="p">,</span> <span class="s">&#39;over&#39;</span><span class="p">:</span> <span class="n">over</span><span class="p">})</span>
  167. <span class="n">vehicle</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">({</span><span class="s">&#39;wheels&#39;</span><span class="p">:</span> <span class="n">wheels</span><span class="p">,</span> <span class="s">&#39;body&#39;</span><span class="p">:</span> <span class="n">body</span><span class="p">})</span>
  168. </pre></div>
  169. </div>
  170. <p>The ground is illustrated by an object of type <tt class="docutils literal"><span class="pre">Wall</span></tt>,
  171. mostly used to indicate walls in sketches of mechanical systems.
  172. A <tt class="docutils literal"><span class="pre">Wall</span></tt> takes the <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> coordinates of some curve,
  173. and a <tt class="docutils literal"><span class="pre">thickness</span></tt> parameter, and creates a &#8220;thick&#8221; curve filled
  174. with a simple pattern. In this case the curve is just a flat
  175. line so the construction is made of two points on the
  176. ground line (<span class="math">\((w_1-L,0)\)</span> and <span class="math">\((w_1+3L,0)\)</span>):</p>
  177. <div class="highlight-python"><div class="highlight"><pre><span class="n">ground</span> <span class="o">=</span> <span class="n">Wall</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="p">[</span><span class="n">w_1</span> <span class="o">-</span> <span class="n">L</span><span class="p">,</span> <span class="n">w_1</span> <span class="o">+</span> <span class="mi">3</span><span class="o">*</span><span class="n">L</span><span class="p">],</span> <span class="n">y</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="n">thickness</span><span class="o">=-</span><span class="mf">0.3</span><span class="o">*</span><span class="n">R</span><span class="p">)</span>
  178. </pre></div>
  179. </div>
  180. <p>The negative thickness makes the pattern-filled rectangle appear below
  181. the defined line, otherwise it appears above.</p>
  182. <p>We may now collect all the objects in a &#8220;top&#8221; object that contains
  183. the whole figure:</p>
  184. <div class="highlight-python"><div class="highlight"><pre><span class="n">fig</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">({</span><span class="s">&#39;vehicle&#39;</span><span class="p">:</span> <span class="n">vehicle</span><span class="p">,</span> <span class="s">&#39;ground&#39;</span><span class="p">:</span> <span class="n">ground</span><span class="p">})</span>
  185. <span class="n">fig</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span> <span class="c"># send all figures to plotting backend</span>
  186. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">display</span><span class="p">()</span>
  187. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.png&#39;</span><span class="p">)</span>
  188. </pre></div>
  189. </div>
  190. <p>The <tt class="docutils literal"><span class="pre">fig.draw()</span></tt> call will visit
  191. all subgroups, their subgroups,
  192. and so in the hierarchical tree structure that we have collected,
  193. and call <tt class="docutils literal"><span class="pre">draw</span></tt> for every object.</p>
  194. </div>
  195. <div class="section" id="changing-line-styles-and-colors">
  196. <h3>Changing Line Styles and Colors<a class="headerlink" href="#changing-line-styles-and-colors" title="Permalink to this headline">¶</a></h3>
  197. <p>Controlling the line style, line color, and line width is
  198. fundamental when designing figures. The <tt class="docutils literal"><span class="pre">pysketcher</span></tt>
  199. package allows the user to control such properties in
  200. single objects, but also set global properties that are
  201. used if the object has no particular specification of
  202. the properties. Setting the global properties are done like</p>
  203. <div class="highlight-python"><div class="highlight"><pre><span class="n">drawing_tool</span><span class="o">.</span><span class="n">set_linestyle</span><span class="p">(</span><span class="s">&#39;dashed&#39;</span><span class="p">)</span>
  204. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">set_linecolor</span><span class="p">(</span><span class="s">&#39;black&#39;</span><span class="p">)</span>
  205. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">set_linewidth</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
  206. </pre></div>
  207. </div>
  208. <p>At the object level the properties are specified in a similar
  209. way:</p>
  210. <div class="highlight-python"><div class="highlight"><pre><span class="n">wheel1</span><span class="o">.</span><span class="n">set_linestyle</span><span class="p">(</span><span class="s">&#39;solid&#39;</span><span class="p">)</span>
  211. <span class="n">wheel1</span><span class="o">.</span><span class="n">set_linecolor</span><span class="p">(</span><span class="s">&#39;red&#39;</span><span class="p">)</span>
  212. </pre></div>
  213. </div>
  214. <p>and so on.</p>
  215. <p>Geometric figures can be specified as <em>filled</em>, either with a color or with a
  216. special visual pattern:</p>
  217. <div class="highlight-py"><div class="highlight"><pre><span class="c"># Set filling of all curves</span>
  218. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s">&#39;blue&#39;</span><span class="p">,</span> <span class="n">pattern</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
  219. <span class="c"># Turn off filling of all curves</span>
  220. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
  221. <span class="c"># Fill the wheel with red color</span>
  222. <span class="n">wheel1</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="s">&#39;red&#39;</span><span class="p">)</span>
  223. </pre></div>
  224. </div>
  225. </div>
  226. <div class="section" id="the-figure-composition-as-an-object-hierarchy">
  227. <h3>The Figure Composition as an Object Hierarchy<a class="headerlink" href="#the-figure-composition-as-an-object-hierarchy" title="Permalink to this headline">¶</a></h3>
  228. <p>The composition of objects is hierarchical, as in a family, where
  229. each object has a parent and a number of children. Do a
  230. <tt class="docutils literal"><span class="pre">print</span> <span class="pre">fig</span></tt> to display the relations:</p>
  231. <div class="highlight-python"><pre>ground
  232. wall
  233. vehicle
  234. body
  235. over
  236. rectangle
  237. under
  238. rectangle
  239. wheels
  240. wheel1
  241. arc
  242. wheel2
  243. arc</pre>
  244. </div>
  245. <p>The indentation reflects how deep down in the hierarchy (family)
  246. we are.
  247. This output is to be interpreted as follows:</p>
  248. <blockquote>
  249. <div><ul class="simple">
  250. <li><tt class="docutils literal"><span class="pre">fig</span></tt> contains two objects, <tt class="docutils literal"><span class="pre">ground</span></tt> and <tt class="docutils literal"><span class="pre">vehicle</span></tt></li>
  251. <li><tt class="docutils literal"><span class="pre">ground</span></tt> contains an object <tt class="docutils literal"><span class="pre">wall</span></tt></li>
  252. <li><tt class="docutils literal"><span class="pre">vehicle</span></tt> contains two objects, <tt class="docutils literal"><span class="pre">body</span></tt> and <tt class="docutils literal"><span class="pre">wheels</span></tt></li>
  253. <li><tt class="docutils literal"><span class="pre">body</span></tt> contains two objects, <tt class="docutils literal"><span class="pre">over</span></tt> and <tt class="docutils literal"><span class="pre">under</span></tt></li>
  254. <li><tt class="docutils literal"><span class="pre">wheels</span></tt> contains two objects, <tt class="docutils literal"><span class="pre">wheel1</span></tt> and <tt class="docutils literal"><span class="pre">wheel2</span></tt></li>
  255. </ul>
  256. </div></blockquote>
  257. <p>More detailed information can be printed by</p>
  258. <div class="highlight-python"><div class="highlight"><pre><span class="k">print</span> <span class="n">fig</span><span class="o">.</span><span class="n">show_hierarchy</span><span class="p">(</span><span class="s">&#39;std&#39;</span><span class="p">)</span>
  259. </pre></div>
  260. </div>
  261. <p>yielding the output</p>
  262. <div class="highlight-python"><pre>ground (Wall):
  263. wall (Curve): 4 coords fillcolor='white' fillpattern='/'
  264. vehicle (Composition):
  265. body (Composition):
  266. over (Rectangle):
  267. rectangle (Curve): 5 coords
  268. under (Rectangle):
  269. rectangle (Curve): 5 coords
  270. wheels (Composition):
  271. wheel1 (Circle):
  272. arc (Curve): 181 coords
  273. wheel2 (Circle):
  274. arc (Curve): 181 coords</pre>
  275. </div>
  276. <p>Here we can see the class type for each figure object, how many
  277. coordinates that are involved in basic figures (<tt class="docutils literal"><span class="pre">Curve</span></tt> objects), and
  278. special settings of the basic figure (fillcolor, line types, etc.).
  279. For example, <tt class="docutils literal"><span class="pre">wheel2</span></tt> is a <tt class="docutils literal"><span class="pre">Circle</span></tt> object consisting of an <tt class="docutils literal"><span class="pre">arc</span></tt>,
  280. which is a <tt class="docutils literal"><span class="pre">Curve</span></tt> object consisting of 181 coordinates (the
  281. points needed to draw a smooth circle). The <tt class="docutils literal"><span class="pre">Curve</span></tt> objects are the
  282. only objects that really holds specific coordinates to be drawn.
  283. The other object types are just compositions used to group
  284. parts of the complete figure.</p>
  285. <p>One can also get a graphical overview of the hierarchy of figure objects
  286. that build up a particular figure <tt class="docutils literal"><span class="pre">fig</span></tt>.
  287. Just call <tt class="docutils literal"><span class="pre">fig.graphviz_dot('fig')</span></tt> to produce a file <tt class="docutils literal"><span class="pre">fig.dot</span></tt> in
  288. the <em>dot format</em>. This file contains relations between parent and
  289. child objects in the figure and can be turned into an image,
  290. as in Figure <a class="reference internal" href="#sketcher-fig-vehicle0-hier1"><em>Hierarchical relation between figure objects</em></a>, by
  291. running the <tt class="docutils literal"><span class="pre">dot</span></tt> program:</p>
  292. <div class="highlight-console"><div class="highlight"><pre><span class="go">Terminal&gt; dot -Tpng -o fig.png fig.dot</span>
  293. </pre></div>
  294. </div>
  295. <div class="figure" id="sketcher-fig-vehicle0-hier1">
  296. <img alt="_images/vehicle0_hier1.png" src="_images/vehicle0_hier1.png" style="width: 500px;" />
  297. <p class="caption"><em>Hierarchical relation between figure objects</em></p>
  298. </div>
  299. <p>The call <tt class="docutils literal"><span class="pre">fig.graphviz_dot('fig',</span> <span class="pre">classname=True)</span></tt> makes a <tt class="docutils literal"><span class="pre">fig.dot</span></tt> file
  300. where the class type of each object is also visible, see
  301. Figure <em class="xref std std-ref">sketcher:fig:vehicle0:hier2</em>. The ability to write out the
  302. object hierarchy or view it graphically can be of great help when
  303. working with complex figures that involve layers of subfigures.</p>
  304. <div class="figure" id="sketcher-fig-vehicle0-hier2">
  305. <img alt="_images/vehicle0_hier2.png" src="_images/vehicle0_hier2.png" style="width: 500px;" />
  306. <p class="caption"><em>Hierarchical relation between figure objects, including their class names</em></p>
  307. </div>
  308. <p>Any of the objects can in the program be reached through their names, e.g.,</p>
  309. <div class="highlight-python"><div class="highlight"><pre><span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">]</span>
  310. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">]</span>
  311. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">]</span>
  312. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">][</span><span class="s">&#39;arc&#39;</span><span class="p">]</span>
  313. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">][</span><span class="s">&#39;arc&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">x</span> <span class="c"># x coords</span>
  314. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">][</span><span class="s">&#39;arc&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">y</span> <span class="c"># y coords</span>
  315. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">][</span><span class="s">&#39;arc&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">linestyle</span>
  316. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">][</span><span class="s">&#39;arc&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">linetype</span>
  317. </pre></div>
  318. </div>
  319. <p>Grabbing a part of the figure this way is very handy for
  320. changing properties of that part, for example, colors, line styles
  321. (see Figure <a class="reference internal" href="#sketcher-fig-vehicle0-v2"><em>Left: Basic line-based drawing. Right: Thicker lines and filled parts</em></a>):</p>
  322. <div class="highlight-python"><div class="highlight"><pre><span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="s">&#39;blue&#39;</span><span class="p">)</span>
  323. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">set_linewidth</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
  324. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">set_linecolor</span><span class="p">(</span><span class="s">&#39;black&#39;</span><span class="p">)</span>
  325. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;body&#39;</span><span class="p">][</span><span class="s">&#39;under&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="s">&#39;red&#39;</span><span class="p">)</span>
  326. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;body&#39;</span><span class="p">][</span><span class="s">&#39;over&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="n">pattern</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
  327. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;body&#39;</span><span class="p">][</span><span class="s">&#39;over&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">set_linewidth</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span>
  328. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;body&#39;</span><span class="p">][</span><span class="s">&#39;over&#39;</span><span class="p">][</span><span class="s">&#39;rectangle&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">linewidth</span> <span class="o">=</span> <span class="mi">4</span>
  329. </pre></div>
  330. </div>
  331. <p>The last line accesses the <tt class="docutils literal"><span class="pre">Curve</span></tt> object directly, while the line above,
  332. accesses the <tt class="docutils literal"><span class="pre">Rectangle</span></tt> object which will then set the linewidth of
  333. its <tt class="docutils literal"><span class="pre">Curve</span></tt> object, and other objects if it had any.
  334. The result of the actions above is shown in Figure <a class="reference internal" href="#sketcher-fig-vehicle0-v2"><em>Left: Basic line-based drawing. Right: Thicker lines and filled parts</em></a>.</p>
  335. <div class="figure" id="sketcher-fig-vehicle0-v2">
  336. <img alt="_images/vehicle0.png" src="_images/vehicle0.png" style="width: 700px;" />
  337. <p class="caption"><em>Left: Basic line-based drawing. Right: Thicker lines and filled parts</em></p>
  338. </div>
  339. <p>We can also change position of parts of the figure and thereby make
  340. animations, as shown next.</p>
  341. </div>
  342. <div class="section" id="animation-translating-the-vehicle">
  343. <h3>Animation: Translating the Vehicle<a class="headerlink" href="#animation-translating-the-vehicle" title="Permalink to this headline">¶</a></h3>
  344. <p>Can we make our little vehicle roll? A first attempt will be to
  345. fake rolling by just displacing all parts of the vehicle.
  346. The relevant parts constitute the <tt class="docutils literal"><span class="pre">fig['vehicle']</span></tt> object.
  347. This part of the figure can be translated, rotated, and scaled.
  348. A translation along the ground means a translation in <span class="math">\(x\)</span> direction,
  349. say a length <span class="math">\(L\)</span> to the right:</p>
  350. <div class="highlight-python"><div class="highlight"><pre><span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">translate</span><span class="p">((</span><span class="n">L</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
  351. </pre></div>
  352. </div>
  353. <p>You need to erase, draw, and display to see the movement:</p>
  354. <div class="highlight-python"><div class="highlight"><pre><span class="n">drawing_tool</span><span class="o">.</span><span class="n">erase</span><span class="p">()</span>
  355. <span class="n">fig</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  356. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">display</span><span class="p">()</span>
  357. </pre></div>
  358. </div>
  359. <p>Without erasing the old position of the vehicle will remain in
  360. the figure so you get two vehicles. Without <tt class="docutils literal"><span class="pre">fig.draw()</span></tt> the
  361. new coordinates of the vehicle will not be communicated to
  362. the drawing tool, and without calling display the updated
  363. drawing will not be visible.</p>
  364. <p>Let us make a velocity function and move the object according
  365. to that velocity in small steps of time:</p>
  366. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">v</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
  367. <span class="k">return</span> <span class="o">-</span><span class="mi">8</span><span class="o">*</span><span class="n">R</span><span class="o">*</span><span class="n">t</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">t</span><span class="o">/</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">))</span>
  368. <span class="n">animate</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">tp</span><span class="p">,</span> <span class="n">action</span><span class="p">)</span>
  369. </pre></div>
  370. </div>
  371. <p>For small time steps <tt class="docutils literal"><span class="pre">dt</span></tt> the corresponding displacement is
  372. well approximated by <tt class="docutils literal"><span class="pre">dt*v(t)</span></tt> (we could integrate the velocity
  373. to obtain the exact position, but we would anyway need to
  374. calculate the displacement from time step to time step).
  375. The <tt class="docutils literal"><span class="pre">animate</span></tt> function takes as arguments some figure <tt class="docutils literal"><span class="pre">fig</span></tt>, a set of
  376. time points <tt class="docutils literal"><span class="pre">tp</span></tt>, and a user function <tt class="docutils literal"><span class="pre">action</span></tt>,
  377. and then a new figure is drawn for each time point and the user
  378. can through the provided <tt class="docutils literal"><span class="pre">action</span></tt> function modify desired parts
  379. of the figure. Here the <tt class="docutils literal"><span class="pre">action</span></tt> function will move the vehicle:</p>
  380. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">move_vehicle</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">fig</span><span class="p">):</span>
  381. <span class="n">x_displacement</span> <span class="o">=</span> <span class="n">dt</span><span class="o">*</span><span class="n">v</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
  382. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">translate</span><span class="p">((</span><span class="n">x_displacement</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
  383. </pre></div>
  384. </div>
  385. <p>Defining a set of time points for the frames in the animation
  386. and performing the animation is done by</p>
  387. <div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">numpy</span>
  388. <span class="n">tp</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span>
  389. <span class="n">dt</span> <span class="o">=</span> <span class="n">tp</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">tp</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c"># time step</span>
  390. <span class="n">animate</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">tp</span><span class="p">,</span> <span class="n">move_vehicle</span><span class="p">,</span> <span class="n">pause_per_frame</span><span class="o">=</span><span class="mf">0.2</span><span class="p">)</span>
  391. </pre></div>
  392. </div>
  393. <p>The <tt class="docutils literal"><span class="pre">pause_per_frame</span></tt> adds a pause, here 0.2 seconds, between
  394. each frame.</p>
  395. <p>We can also make a movie file of the animation:</p>
  396. <div class="highlight-python"><div class="highlight"><pre><span class="n">files</span> <span class="o">=</span> <span class="n">animate</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">tp</span><span class="p">,</span> <span class="n">move_vehicle</span><span class="p">,</span> <span class="n">moviefiles</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
  397. <span class="n">pause_per_frame</span><span class="o">=</span><span class="mf">0.2</span><span class="p">)</span>
  398. </pre></div>
  399. </div>
  400. <p>The <tt class="docutils literal"><span class="pre">files</span></tt> variable holds a string with the family of
  401. files constituting the frames in the movie, here
  402. <tt class="docutils literal"><span class="pre">'tmp_frame*.png'</span></tt>. Making a movie out of the individual
  403. frames can be done in many ways.
  404. A simple approach is to make an animated GIF file with help of
  405. <tt class="docutils literal"><span class="pre">convert</span></tt>, a program in the ImageMagick software suite:</p>
  406. <div class="highlight-console"><div class="highlight"><pre><span class="go">Terminal&gt; convert -delay 20 tmp_frame*.png anim.gif</span>
  407. <span class="go">Terminal&gt; animate anim.gif # play movie</span>
  408. </pre></div>
  409. </div>
  410. <p>The delay between frames governs the speed of the movie.
  411. The <tt class="docutils literal"><span class="pre">anim.gif</span></tt> file can be embedded in a web page and shown as
  412. a movie the page is loaded into a web browser (just insert
  413. <tt class="docutils literal"><span class="pre">&lt;img</span> <span class="pre">src=&quot;anim.gif&quot;&gt;</span></tt> in the HTML code to play the GIF animation).</p>
  414. <p>The tool <tt class="docutils literal"><span class="pre">ffmpeg</span></tt> can alternatively be used, e.g.,</p>
  415. <div class="highlight-console"><div class="highlight"><pre><span class="go">Terminal&gt; ffmpeg -i &quot;tmp_frame_%04d.png&quot; -b 800k -r 25 \</span>
  416. <span class="go"> -vcodec mpeg4 -y -qmin 2 -qmax 31 anim.mpeg</span>
  417. </pre></div>
  418. </div>
  419. <p>An easy-to-use interface to movie-making tools is provided by the
  420. SciTools package:</p>
  421. <div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">scitools.std</span> <span class="kn">import</span> <span class="n">movie</span>
  422. <span class="c"># HTML page showing individual frames</span>
  423. <span class="n">movie</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">encoder</span><span class="o">=</span><span class="s">&#39;html&#39;</span><span class="p">,</span> <span class="n">output_file</span><span class="o">=</span><span class="s">&#39;anim.html&#39;</span><span class="p">)</span>
  424. <span class="c"># Standard GIF file</span>
  425. <span class="n">movie</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">encoder</span><span class="o">=</span><span class="s">&#39;convert&#39;</span><span class="p">,</span> <span class="n">output_file</span><span class="o">=</span><span class="s">&#39;anim.gif&#39;</span><span class="p">)</span>
  426. <span class="c"># AVI format</span>
  427. <span class="n">movie</span><span class="p">(</span><span class="s">&#39;tmp_*.png&#39;</span><span class="p">,</span> <span class="n">encoder</span><span class="o">=</span><span class="s">&#39;ffmpeg&#39;</span><span class="p">,</span> <span class="n">fps</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>
  428. <span class="n">output_file</span><span class="o">=</span><span class="s">&#39;anim.avi&#39;</span><span class="p">)</span> <span class="c"># requires ffmpeg package</span>
  429. <span class="c"># MPEG format</span>
  430. <span class="n">movie</span><span class="p">(</span><span class="s">&#39;tmp_*.png&#39;</span><span class="p">,</span> <span class="n">encoder</span><span class="o">=</span><span class="s">&#39;ffmpeg&#39;</span><span class="p">,</span> <span class="n">fps</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>
  431. <span class="n">output_file</span><span class="o">=</span><span class="s">&#39;anim2.mpeg&#39;</span><span class="p">,</span> <span class="n">vodec</span><span class="o">=</span><span class="s">&#39;mpeg2video&#39;</span><span class="p">)</span>
  432. <span class="c"># or</span>
  433. <span class="n">movie</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">encoder</span><span class="o">=</span><span class="s">&#39;ppmtompeg&#39;</span><span class="p">,</span> <span class="n">fps</span><span class="o">=</span><span class="mi">24</span><span class="p">,</span>
  434. <span class="n">output_file</span><span class="o">=</span><span class="s">&#39;anim.mpeg&#39;</span><span class="p">)</span> <span class="c"># requires the netpbm package</span>
  435. </pre></div>
  436. </div>
  437. <p>When difficulties with encoders and players arise, the simple
  438. web page for showing a movie, here <tt class="docutils literal"><span class="pre">anim.html</span></tt> (generated by the
  439. first <tt class="docutils literal"><span class="pre">movie</span></tt> command above), is a safe method that you always
  440. can rely on.
  441. You can try loading <tt class="docutils literal"><span class="pre">anim.html</span></tt> into a web browser, after first
  442. having run the present example in the file
  443. <a href="#id1"><span class="problematic" id="id2">``</span></a>vehicle0.py` &lt;<a class="reference external" href="http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle0.py">http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle0.py</a>&gt;`_. Alternatively, you can view a ready-made <a class="reference external" href="http://hplgit.github.com/pysketcher/doc/src/sketcher/movies-sketcher/anim.html_vehicle0/anim.html">movie</a>.</p>
  444. </div>
  445. <div class="section" id="animation-rolling-the-wheels">
  446. <span id="sketcher-vehicle1-anim"></span><h3>Animation: Rolling the Wheels<a class="headerlink" href="#animation-rolling-the-wheels" title="Permalink to this headline">¶</a></h3>
  447. <p>It is time to show rolling wheels. To this end, we make somewhat
  448. more complicated wheels with spokes as on a bicyle, formed by
  449. two crossing lines, see Figure <a class="reference internal" href="#sketcher-fig-vehicle1"><em>Wheels with spokes to show rotation</em></a>.
  450. The construction of the wheels will now involve a circle
  451. and two lines:</p>
  452. <div class="highlight-python"><div class="highlight"><pre><span class="n">wheel1</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">({</span>
  453. <span class="s">&#39;wheel&#39;</span><span class="p">:</span> <span class="n">Circle</span><span class="p">(</span><span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">),</span> <span class="n">radius</span><span class="o">=</span><span class="n">R</span><span class="p">),</span>
  454. <span class="s">&#39;cross&#39;</span><span class="p">:</span> <span class="n">Composition</span><span class="p">({</span><span class="s">&#39;cross1&#39;</span><span class="p">:</span> <span class="n">Line</span><span class="p">((</span><span class="n">w_1</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="n">w_1</span><span class="p">,</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">)),</span>
  455. <span class="s">&#39;cross2&#39;</span><span class="p">:</span> <span class="n">Line</span><span class="p">((</span><span class="n">w_1</span><span class="o">-</span><span class="n">R</span><span class="p">,</span><span class="n">R</span><span class="p">),</span> <span class="p">(</span><span class="n">w_1</span><span class="o">+</span><span class="n">R</span><span class="p">,</span><span class="n">R</span><span class="p">))})})</span>
  456. <span class="n">wheel2</span> <span class="o">=</span> <span class="n">wheel1</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
  457. <span class="n">wheel2</span><span class="o">.</span><span class="n">translate</span><span class="p">((</span><span class="n">L</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
  458. </pre></div>
  459. </div>
  460. <p>Observe that <tt class="docutils literal"><span class="pre">wheel1.copy()</span></tt> copies all the objects that make
  461. up the first wheel, and <tt class="docutils literal"><span class="pre">wheel2.translate</span></tt> translates all
  462. the copied objects.</p>
  463. <div class="figure" id="sketcher-fig-vehicle1">
  464. <img alt="_images/vehicle1.png" src="_images/vehicle1.png" style="width: 400px;" />
  465. <p class="caption"><em>Wheels with spokes to show rotation</em></p>
  466. </div>
  467. <p>The <tt class="docutils literal"><span class="pre">move_vehicle</span></tt> function now needs to displace all the objects in the
  468. entire vehicle and also rotate the <tt class="docutils literal"><span class="pre">cross1</span></tt> and <tt class="docutils literal"><span class="pre">cross2</span></tt>
  469. objects in both wheels.
  470. The rotation angle follows from the fact that the arc length
  471. of a rolling wheel equals the displacement of the center of
  472. the wheel, leading to a rotation angle</p>
  473. <div class="highlight-python"><div class="highlight"><pre><span class="n">angle</span> <span class="o">=</span> <span class="o">-</span> <span class="n">x_displacement</span><span class="o">/</span><span class="n">R</span>
  474. </pre></div>
  475. </div>
  476. <p>With <tt class="docutils literal"><span class="pre">w_1</span></tt> tracking the <span class="math">\(x\)</span> coordinate of the center
  477. of the front wheel, we can rotate that wheel by</p>
  478. <div class="highlight-python"><div class="highlight"><pre><span class="n">w1</span> <span class="o">=</span> <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel1&#39;</span><span class="p">]</span>
  479. <span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">degrees</span>
  480. <span class="n">w1</span><span class="o">.</span><span class="n">rotate</span><span class="p">(</span><span class="n">degrees</span><span class="p">(</span><span class="n">angle</span><span class="p">),</span> <span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">))</span>
  481. </pre></div>
  482. </div>
  483. <p>The <tt class="docutils literal"><span class="pre">rotate</span></tt> function takes two parameters: the rotation angle
  484. (in degrees) and the center point of the rotation, which is the
  485. center of the wheel in this case. The other wheel is rotated by</p>
  486. <div class="highlight-python"><div class="highlight"><pre><span class="n">w2</span> <span class="o">=</span> <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">]</span>
  487. <span class="n">w2</span><span class="o">.</span><span class="n">rotate</span><span class="p">(</span><span class="n">degrees</span><span class="p">(</span><span class="n">angle</span><span class="p">),</span> <span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span> <span class="o">+</span> <span class="n">L</span><span class="p">,</span> <span class="n">R</span><span class="p">))</span>
  488. </pre></div>
  489. </div>
  490. <p>That is, the angle is the same, but the rotation point is different.
  491. The update of the center point is done by <tt class="docutils literal"><span class="pre">w_1</span> <span class="pre">+=</span> <span class="pre">displacement[0]</span></tt>.
  492. The complete <tt class="docutils literal"><span class="pre">move_vehicle</span></tt> function then becomes</p>
  493. <div class="highlight-python"><div class="highlight"><pre><span class="n">w_1</span> <span class="o">=</span> <span class="n">w_1</span> <span class="o">+</span> <span class="n">L</span> <span class="c"># start position</span>
  494. <span class="k">def</span> <span class="nf">move_vehicle</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">fig</span><span class="p">):</span>
  495. <span class="n">x_displacement</span> <span class="o">=</span> <span class="n">dt</span><span class="o">*</span><span class="n">v</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
  496. <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">translate</span><span class="p">((</span><span class="n">x_displacement</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
  497. <span class="c"># Rotate wheels</span>
  498. <span class="k">global</span> <span class="n">w_1</span>
  499. <span class="n">w_1</span> <span class="o">+=</span> <span class="n">x_displacement</span>
  500. <span class="c"># R*angle = -x_displacement</span>
  501. <span class="n">angle</span> <span class="o">=</span> <span class="o">-</span> <span class="n">x_displacement</span><span class="o">/</span><span class="n">R</span>
  502. <span class="n">w1</span> <span class="o">=</span> <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel1&#39;</span><span class="p">]</span>
  503. <span class="n">w1</span><span class="o">.</span><span class="n">rotate</span><span class="p">(</span><span class="n">degrees</span><span class="p">(</span><span class="n">angle</span><span class="p">),</span> <span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">))</span>
  504. <span class="n">w2</span> <span class="o">=</span> <span class="n">fig</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">][</span><span class="s">&#39;wheel2&#39;</span><span class="p">]</span>
  505. <span class="n">w2</span><span class="o">.</span><span class="n">rotate</span><span class="p">(</span><span class="n">degrees</span><span class="p">(</span><span class="n">angle</span><span class="p">),</span> <span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span> <span class="o">+</span> <span class="n">L</span><span class="p">,</span> <span class="n">R</span><span class="p">))</span>
  506. </pre></div>
  507. </div>
  508. <p>The complete example is found in the file
  509. <a href="#id3"><span class="problematic" id="id4">``</span></a>vehicle1.py` &lt;<a class="reference external" href="http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle1.py">http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle1.py</a>&gt;`_. You may run this file or watch a <a class="reference external" href="http://hplgit.github.com/pysketcher/doc/src/sketcher/movies-sketcher/anim.html_vehicle1/anim.html">ready-made movie</a>.</p>
  510. <p>The advantages with making figures this way through programming,
  511. rather than using interactive drawing programs, are numerous. For
  512. example, the objects are parameterized by variables so that various
  513. dimensions can easily be changed. Subparts of the figure, possible
  514. involving a lot of figure objects, can change
  515. color, linetype, filling or other properties through a <em>single</em> function
  516. call. Subparts of the figure can be rotated, translated, or scaled.
  517. Subparts of the figure can also be copied and moved to other parts of the
  518. drawing area. However, the single most important feature is probably
  519. the ability to make animations governed by mathematical formulas or
  520. data coming from physics simulations of the problem sketched in
  521. the drawing, as very simplistically shown in the example above.</p>
  522. </div>
  523. </div>
  524. </div>
  525. <div class="section" id="inner-workings-of-the-pysketcher-tool">
  526. <h1>Inner Workings of the Pysketcher Tool<a class="headerlink" href="#inner-workings-of-the-pysketcher-tool" title="Permalink to this headline">¶</a></h1>
  527. <p>We shall now explain how we can, quite easily, realize software with
  528. the capabilities demonstrated in the previous examples. Each object in
  529. the figure is represented as a class in a class hierarchy. Using
  530. inheritance, classes can inherit properties from parent classes and
  531. add new geometric features.</p>
  532. <p>Class programming is a key technology for realizing Pysketcher.
  533. As soon as some classes are established, more are easily
  534. added. Enhanced functionality for all the classes is also easy to
  535. implement in common, generic code that can immediately be shared by
  536. all present and future classes. The fundamental data structure
  537. involved in the <tt class="docutils literal"><span class="pre">pysketcher</span></tt> package is a hierarchical tree, and much
  538. of the material on implementation issues targets how to traverse tree
  539. structures with recursive function calls in object hierarchies. This
  540. topic is of key relevance in a wide range of other applications as
  541. well. In total, the inner workings of Pysketcher constitute an
  542. excellent example on the power of class programming.</p>
  543. <div class="section" id="example-of-classes-for-geometric-objects">
  544. <h2>Example of Classes for Geometric Objects<a class="headerlink" href="#example-of-classes-for-geometric-objects" title="Permalink to this headline">¶</a></h2>
  545. <p>We introduce class <tt class="docutils literal"><span class="pre">Shape</span></tt> as superclass for all specialized objects
  546. in a figure. This class does not store any data, but provides a
  547. series of functions that add functionality to all the subclasses.
  548. This will be shown later.</p>
  549. <div class="section" id="simple-geometric-objects">
  550. <h3>Simple Geometric Objects<a class="headerlink" href="#simple-geometric-objects" title="Permalink to this headline">¶</a></h3>
  551. <p>One simple subclass is <tt class="docutils literal"><span class="pre">Rectangle</span></tt>:</p>
  552. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="n">Shape</span><span class="p">):</span>
  553. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lower_left_corner</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
  554. <span class="n">p</span> <span class="o">=</span> <span class="n">lower_left_corner</span> <span class="c"># short form</span>
  555. <span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">width</span><span class="p">,</span>
  556. <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">width</span><span class="p">,</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  557. <span class="n">y</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">height</span><span class="p">,</span>
  558. <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">height</span><span class="p">,</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
  559. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;rectangle&#39;</span><span class="p">:</span> <span class="n">Curve</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">)}</span>
  560. </pre></div>
  561. </div>
  562. <p>Any subclass of <tt class="docutils literal"><span class="pre">Shape</span></tt> will have a constructor which takes
  563. geometric information about the shape of the object and
  564. creates a dictionary <tt class="docutils literal"><span class="pre">self.shapes</span></tt> with the shape built of
  565. simpler shapes. The most fundamental shape is <tt class="docutils literal"><span class="pre">Curve</span></tt>, which is
  566. just a collection of <span class="math">\((x,y)\)</span> coordinates in two arrays <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt>.
  567. Drawing the <tt class="docutils literal"><span class="pre">Curve</span></tt> object is a matter of plotting <tt class="docutils literal"><span class="pre">y</span></tt> versus <tt class="docutils literal"><span class="pre">x</span></tt>.</p>
  568. <p>The <tt class="docutils literal"><span class="pre">Rectangle</span></tt> class illustrates how the constructor takes information
  569. about the lower left corner, the width and the height, and
  570. creates coordinate arrays <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> consisting of the four corners,
  571. plus the first one repeated such that plotting <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> will
  572. form a closed four-sided rectangle. This construction procedure
  573. demands that the rectangle will always be aligned with the <span class="math">\(x\)</span> and
  574. <span class="math">\(y\)</span> axis. However, we may easily rotate the rectangle about
  575. any point once the object is constructed.</p>
  576. <p>Class <tt class="docutils literal"><span class="pre">Line</span></tt> constitutes a similar example:</p>
  577. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Line</span><span class="p">(</span><span class="n">Shape</span><span class="p">):</span>
  578. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
  579. <span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="n">start</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">end</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  580. <span class="n">y</span> <span class="o">=</span> <span class="p">[</span><span class="n">start</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">end</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
  581. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;line&#39;</span><span class="p">:</span> <span class="n">Curve</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)}</span>
  582. </pre></div>
  583. </div>
  584. <p>Here we only need two points, the start and end point on the line.
  585. However, we may add some useful functionality, e.g., the ability
  586. to give an <span class="math">\(x\)</span> coordinate and have the class calculate the
  587. corresponding <span class="math">\(y\)</span> coordinate:</p>
  588. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
  589. <span class="sd">&quot;&quot;&quot;Given x, return y on the line.&quot;&quot;&quot;</span>
  590. <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;line&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;line&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">y</span>
  591. <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">y</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="o">/</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
  592. <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">y</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  593. <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span>
  594. </pre></div>
  595. </div>
  596. <p>Unfortunately, this is too simplistic because vertical lines cannot
  597. be handled (infinite <tt class="docutils literal"><span class="pre">self.a</span></tt>). The source code of <tt class="docutils literal"><span class="pre">Line</span></tt> therefore
  598. provides a more general solution at the cost of significantly
  599. longer code with more tests.</p>
  600. <p>A circle gives us somewhat increased complexity. Again we represent
  601. the geometric object by a <tt class="docutils literal"><span class="pre">Curve</span></tt> object, but this time the <tt class="docutils literal"><span class="pre">Curve</span></tt>
  602. object needs to store a large number of points on the curve such
  603. that a plotting program produces a visually smooth curve.
  604. The points on the circle must be calculated manually in the constructor
  605. of class <tt class="docutils literal"><span class="pre">Circle</span></tt>. The formulas for points <span class="math">\((x,y)\)</span> on a curve with radius
  606. <span class="math">\(R\)</span> and center at <span class="math">\((x_0, y_0)\)</span> are given by</p>
  607. <div class="math">
  608. \[\begin{split}x &amp;= x_0 + R\cos (t),\\
  609. y &amp;= y_0 + R\sin (t),\end{split}\]</div>
  610. <p>where <span class="math">\(t\in [0, 2\pi]\)</span>. A discrete set of <span class="math">\(t\)</span> values in this
  611. interval gives the corresponding set of <span class="math">\((x,y)\)</span> coordinates on
  612. the circle. The user must specify the resolution, i.e., the number
  613. of <span class="math">\(t\)</span> values, or equivalently, points on the circle. The circle&#8217;s
  614. radius and center must of course also be specified.</p>
  615. <p>We can write the <tt class="docutils literal"><span class="pre">Circle</span></tt> class as</p>
  616. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Circle</span><span class="p">(</span><span class="n">Shape</span><span class="p">):</span>
  617. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">center</span><span class="p">,</span> <span class="n">radius</span><span class="p">,</span> <span class="n">resolution</span><span class="o">=</span><span class="mi">180</span><span class="p">):</span>
  618. <span class="bp">self</span><span class="o">.</span><span class="n">center</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">radius</span> <span class="o">=</span> <span class="n">center</span><span class="p">,</span> <span class="n">radius</span>
  619. <span class="bp">self</span><span class="o">.</span><span class="n">resolution</span> <span class="o">=</span> <span class="n">resolution</span>
  620. <span class="n">t</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">pi</span><span class="p">,</span> <span class="n">resolution</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
  621. <span class="n">x0</span> <span class="o">=</span> <span class="n">center</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="n">y0</span> <span class="o">=</span> <span class="n">center</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
  622. <span class="n">R</span> <span class="o">=</span> <span class="n">radius</span>
  623. <span class="n">x</span> <span class="o">=</span> <span class="n">x0</span> <span class="o">+</span> <span class="n">R</span><span class="o">*</span><span class="n">cos</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
  624. <span class="n">y</span> <span class="o">=</span> <span class="n">y0</span> <span class="o">+</span> <span class="n">R</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
  625. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;circle&#39;</span><span class="p">:</span> <span class="n">Curve</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)}</span>
  626. </pre></div>
  627. </div>
  628. <p>As in class <tt class="docutils literal"><span class="pre">Line</span></tt> we can offer the possibility to give an angle
  629. <span class="math">\(\theta\)</span> (equivalent to <span class="math">\(t\)</span> in the formulas above)
  630. and then get the corresponding <span class="math">\(x\)</span> and <span class="math">\(y\)</span> coordinates:</p>
  631. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">theta</span><span class="p">):</span>
  632. <span class="sd">&quot;&quot;&quot;Return (x, y) point corresponding to angle theta.&quot;&quot;&quot;</span>
  633. <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">center</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">radius</span><span class="o">*</span><span class="n">cos</span><span class="p">(</span><span class="n">theta</span><span class="p">),</span> \
  634. <span class="bp">self</span><span class="o">.</span><span class="n">center</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">radius</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
  635. </pre></div>
  636. </div>
  637. <p>There is one flaw with this method: it yields illegal values after
  638. a translation, scaling, or rotation of the circle.</p>
  639. <p>A part of a circle, an arc, is a frequent geometric object when
  640. drawing mechanical systems. The arc is constructed much like
  641. a circle, but <span class="math">\(t\)</span> runs in <span class="math">\([\theta_0, \theta_1]\)</span>. Giving
  642. <span class="math">\(\theta_1\)</span> and <span class="math">\(\theta_2\)</span> the slightly more descriptive names
  643. <tt class="docutils literal"><span class="pre">start_angle</span></tt> and <tt class="docutils literal"><span class="pre">arc_angle</span></tt>, the code looks like this:</p>
  644. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Arc</span><span class="p">(</span><span class="n">Shape</span><span class="p">):</span>
  645. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">center</span><span class="p">,</span> <span class="n">radius</span><span class="p">,</span>
  646. <span class="n">start_angle</span><span class="p">,</span> <span class="n">arc_angle</span><span class="p">,</span>
  647. <span class="n">resolution</span><span class="o">=</span><span class="mi">180</span><span class="p">):</span>
  648. <span class="bp">self</span><span class="o">.</span><span class="n">center</span> <span class="o">=</span> <span class="n">center</span>
  649. <span class="bp">self</span><span class="o">.</span><span class="n">radius</span> <span class="o">=</span> <span class="n">radius</span>
  650. <span class="bp">self</span><span class="o">.</span><span class="n">start_angle</span> <span class="o">=</span> <span class="n">start_angle</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="mi">180</span> <span class="c"># radians</span>
  651. <span class="bp">self</span><span class="o">.</span><span class="n">arc_angle</span> <span class="o">=</span> <span class="n">arc_angle</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="mi">180</span>
  652. <span class="bp">self</span><span class="o">.</span><span class="n">resolution</span> <span class="o">=</span> <span class="n">resolution</span>
  653. <span class="n">t</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">start_angle</span><span class="p">,</span>
  654. <span class="bp">self</span><span class="o">.</span><span class="n">start_angle</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">arc_angle</span><span class="p">,</span>
  655. <span class="n">resolution</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
  656. <span class="n">x0</span> <span class="o">=</span> <span class="n">center</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="n">y0</span> <span class="o">=</span> <span class="n">center</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
  657. <span class="n">R</span> <span class="o">=</span> <span class="n">radius</span>
  658. <span class="n">x</span> <span class="o">=</span> <span class="n">x0</span> <span class="o">+</span> <span class="n">R</span><span class="o">*</span><span class="n">cos</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
  659. <span class="n">y</span> <span class="o">=</span> <span class="n">y0</span> <span class="o">+</span> <span class="n">R</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
  660. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;arc&#39;</span><span class="p">:</span> <span class="n">Curve</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)}</span>
  661. </pre></div>
  662. </div>
  663. <p>Having the <tt class="docutils literal"><span class="pre">Arc</span></tt> class, a <tt class="docutils literal"><span class="pre">Circle</span></tt> can alternatively befined as
  664. a subclass specializing the arc to a circle:</p>
  665. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Circle</span><span class="p">(</span><span class="n">Arc</span><span class="p">):</span>
  666. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">center</span><span class="p">,</span> <span class="n">radius</span><span class="p">,</span> <span class="n">resolution</span><span class="o">=</span><span class="mi">180</span><span class="p">):</span>
  667. <span class="n">Arc</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">center</span><span class="p">,</span> <span class="n">radius</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">360</span><span class="p">,</span> <span class="n">resolution</span><span class="p">)</span>
  668. </pre></div>
  669. </div>
  670. <p>A wall is about drawing a curve, displacing the curve vertically by
  671. some thickness, and then filling the space between the curves
  672. by some pattern. The input is the <tt class="docutils literal"><span class="pre">x</span></tt> and <tt class="docutils literal"><span class="pre">y</span></tt> coordinate arrays
  673. of the curve and a thickness parameter. The computed coordinates
  674. will be a polygon: going along the originally curve and then back again
  675. along the vertically displaced curve. The relevant code becomes</p>
  676. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">CurveWall</span><span class="p">(</span><span class="n">Shape</span><span class="p">):</span>
  677. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">thickness</span><span class="p">):</span>
  678. <span class="c"># User&#39;s curve</span>
  679. <span class="n">x1</span> <span class="o">=</span> <span class="n">asarray</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nb">float</span><span class="p">)</span>
  680. <span class="n">y1</span> <span class="o">=</span> <span class="n">asarray</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="nb">float</span><span class="p">)</span>
  681. <span class="c"># Displaced curve (according to thickness)</span>
  682. <span class="n">x2</span> <span class="o">=</span> <span class="n">x1</span>
  683. <span class="n">y2</span> <span class="o">=</span> <span class="n">y1</span> <span class="o">+</span> <span class="n">thickness</span>
  684. <span class="c"># Combine x1,y1 with x2,y2 reversed</span>
  685. <span class="kn">from</span> <span class="nn">numpy</span> <span class="kn">import</span> <span class="n">concatenate</span>
  686. <span class="n">x</span> <span class="o">=</span> <span class="n">concatenate</span><span class="p">((</span><span class="n">x1</span><span class="p">,</span> <span class="n">x2</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">::</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>
  687. <span class="n">y</span> <span class="o">=</span> <span class="n">concatenate</span><span class="p">((</span><span class="n">y1</span><span class="p">,</span> <span class="n">y2</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">::</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>
  688. <span class="n">wall</span> <span class="o">=</span> <span class="n">Curve</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
  689. <span class="n">wall</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="n">color</span><span class="o">=</span><span class="s">&#39;white&#39;</span><span class="p">,</span> <span class="n">pattern</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
  690. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;wall&#39;</span><span class="p">:</span> <span class="n">wall</span><span class="p">}</span>
  691. </pre></div>
  692. </div>
  693. </div>
  694. <div class="section" id="class-curve">
  695. <h3>Class Curve<a class="headerlink" href="#class-curve" title="Permalink to this headline">¶</a></h3>
  696. <p>Class <tt class="docutils literal"><span class="pre">Curve</span></tt> sits on the coordinates to be drawn, but how is
  697. that done? The constructor just stores the coordinates, while
  698. a method <tt class="docutils literal"><span class="pre">draw</span></tt> sends the coordinates to the plotting program
  699. to make a graph.
  700. Or more precisely, to avoid a lot of (e.g.) Matplotlib-specific
  701. plotting commands we have created a small layer with a
  702. simple programming interface to plotting programs. This makes it
  703. straightforward to change from Matplotlib to another plotting
  704. program. The programming interface is represented by the <tt class="docutils literal"><span class="pre">drawing_tool</span></tt>
  705. object and has a few functions:</p>
  706. <blockquote>
  707. <div><ul class="simple">
  708. <li><tt class="docutils literal"><span class="pre">plot_curve</span></tt> for sending a curve in terms of <span class="math">\(x\)</span> and <span class="math">\(y\)</span> coordinates
  709. to the plotting program,</li>
  710. <li><tt class="docutils literal"><span class="pre">set_coordinate_system</span></tt> for specifying the graphics area,</li>
  711. <li><tt class="docutils literal"><span class="pre">erase</span></tt> for deleting all elements of the graph,</li>
  712. <li><tt class="docutils literal"><span class="pre">set_grid</span></tt> for turning on a grid (convenient while constructing the plot),</li>
  713. <li><tt class="docutils literal"><span class="pre">set_instruction_file</span></tt> for creating a separate file with all
  714. plotting commands (Matplotlib commands in our case),</li>
  715. <li>a series of <tt class="docutils literal"><span class="pre">set_X</span></tt> functions where <tt class="docutils literal"><span class="pre">X</span></tt> is some property like
  716. <tt class="docutils literal"><span class="pre">linecolor</span></tt>, <tt class="docutils literal"><span class="pre">linestyle</span></tt>, <tt class="docutils literal"><span class="pre">linewidth</span></tt>, <tt class="docutils literal"><span class="pre">filled_curves</span></tt>.</li>
  717. </ul>
  718. </div></blockquote>
  719. <p>This is basically all we need to communicate to a plotting program.</p>
  720. <p>Any class in the <tt class="docutils literal"><span class="pre">Shape</span></tt> hierarchy inherits <tt class="docutils literal"><span class="pre">set_X</span></tt> functions for
  721. setting properties of curves. This information is propagated to
  722. all other shape objects that make up the figure. Class
  723. <tt class="docutils literal"><span class="pre">Curve</span></tt> stores the line properties together with the coordinates
  724. of its curve and propagates this information to the plotting program.
  725. When saying <tt class="docutils literal"><span class="pre">vehicle.set_linewidth(10)</span></tt>, all objects that make
  726. up the <tt class="docutils literal"><span class="pre">vehicle</span></tt> object will get a <tt class="docutils literal"><span class="pre">set_linewidth(10)</span></tt> call,
  727. but only the <tt class="docutils literal"><span class="pre">Curve</span></tt> object at the end of the chain will actually
  728. store the information and send it to the plotting program.</p>
  729. <p>A rough sketch of class <tt class="docutils literal"><span class="pre">Curve</span></tt> reads</p>
  730. <div class="highlight-python"><pre>class Curve(Shape):
  731. """General curve as a sequence of (x,y) coordintes."""
  732. def __init__(self, x, y):
  733. self.x = asarray(x, dtype=float)
  734. self.y = asarray(y, dtype=float)
  735. self.linestyle = None
  736. self.linewidth = None
  737. self.linecolor = None
  738. self.fillcolor = None
  739. self.fillpattern = None
  740. self.arrow = None
  741. def draw(self):
  742. drawing_tool.plot_curve(
  743. self.x, self.y,
  744. self.linestyle, self.linewidth, self.linecolor,
  745. self.arrow, self.fillcolor, self.fillpattern)
  746. def set_linewidth(self, width):
  747. self.linewidth = width
  748. det set_linestyle(self, style):
  749. self.linestyle = style
  750. ...</pre>
  751. </div>
  752. </div>
  753. <div class="section" id="compound-geometric-objects">
  754. <h3>Compound Geometric Objects<a class="headerlink" href="#compound-geometric-objects" title="Permalink to this headline">¶</a></h3>
  755. <p>The simple classes <tt class="docutils literal"><span class="pre">Line</span></tt>, <tt class="docutils literal"><span class="pre">Arc</span></tt>, and <tt class="docutils literal"><span class="pre">Circle</span></tt> could define the geometric
  756. shape through just one <tt class="docutils literal"><span class="pre">Curve</span></tt> object. More complicated figure elements
  757. are built from instances of various subclasses of <tt class="docutils literal"><span class="pre">Shape</span></tt>. Classes used
  758. for professional drawings soon get quite complex in composition and
  759. have a lot of geometric details, so here we prefer to make a very simple
  760. composition: the already drawn vehicle from
  761. Figure ref:ref:<cite>sketcher:fig:vehicle0</cite>.
  762. That is, instead of composing the drawing in a Python code we make a class
  763. <tt class="docutils literal"><span class="pre">Vehicle0</span></tt> for doing the same thing, and derive it from <tt class="docutils literal"><span class="pre">Shape</span></tt>.</p>
  764. <p>The <tt class="docutils literal"><span class="pre">Shape</span></tt> hierarchy is found in the <tt class="docutils literal"><span class="pre">pysketcher</span></tt> package, so to use these
  765. classes or derive a new one, we need to import <tt class="docutils literal"><span class="pre">pysketcher</span></tt>. The constructor
  766. of class <tt class="docutils literal"><span class="pre">Vehicle0</span></tt> performs approximately the same statements as
  767. in the example program we developed for making the drawing in
  768. Figure ref:ref:<cite>sketcher:fig:vehicle0</cite>.</p>
  769. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Vehicle0</span><span class="p">(</span><span class="n">Shape</span><span class="p">):</span>
  770. <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">H</span><span class="p">):</span>
  771. <span class="n">wheel1</span> <span class="o">=</span> <span class="n">Circle</span><span class="p">(</span><span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">),</span> <span class="n">radius</span><span class="o">=</span><span class="n">R</span><span class="p">)</span>
  772. <span class="n">wheel2</span> <span class="o">=</span> <span class="n">wheel1</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
  773. <span class="n">wheel2</span><span class="o">.</span><span class="n">translate</span><span class="p">((</span><span class="n">L</span><span class="p">,</span><span class="mi">0</span><span class="p">))</span>
  774. <span class="n">under</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">lower_left_corner</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">),</span>
  775. <span class="n">width</span><span class="o">=</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span> <span class="o">+</span> <span class="n">L</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="n">H</span><span class="p">)</span>
  776. <span class="n">over</span> <span class="o">=</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">lower_left_corner</span><span class="o">=</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span> <span class="o">+</span> <span class="n">H</span><span class="p">),</span>
  777. <span class="n">width</span><span class="o">=</span><span class="mf">2.5</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mf">1.25</span><span class="o">*</span><span class="n">H</span><span class="p">)</span>
  778. <span class="n">wheels</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">(</span>
  779. <span class="p">{</span><span class="s">&#39;wheel1&#39;</span><span class="p">:</span> <span class="n">wheel1</span><span class="p">,</span> <span class="s">&#39;wheel2&#39;</span><span class="p">:</span> <span class="n">wheel2</span><span class="p">})</span>
  780. <span class="n">body</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">(</span>
  781. <span class="p">{</span><span class="s">&#39;under&#39;</span><span class="p">:</span> <span class="n">under</span><span class="p">,</span> <span class="s">&#39;over&#39;</span><span class="p">:</span> <span class="n">over</span><span class="p">})</span>
  782. <span class="n">vehicle</span> <span class="o">=</span> <span class="n">Composition</span><span class="p">({</span><span class="s">&#39;wheels&#39;</span><span class="p">:</span> <span class="n">wheels</span><span class="p">,</span> <span class="s">&#39;body&#39;</span><span class="p">:</span> <span class="n">body</span><span class="p">})</span>
  783. <span class="n">xmax</span> <span class="o">=</span> <span class="n">w_1</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">L</span> <span class="o">+</span> <span class="mi">3</span><span class="o">*</span><span class="n">R</span>
  784. <span class="n">ground</span> <span class="o">=</span> <span class="n">Wall</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="n">xmax</span><span class="p">],</span> <span class="n">y</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="n">thickness</span><span class="o">=-</span><span class="mf">0.3</span><span class="o">*</span><span class="n">R</span><span class="p">)</span>
  785. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;vehicle&#39;</span><span class="p">:</span> <span class="n">vehicle</span><span class="p">,</span> <span class="s">&#39;ground&#39;</span><span class="p">:</span> <span class="n">ground</span><span class="p">}</span>
  786. </pre></div>
  787. </div>
  788. <p>Any subclass of <tt class="docutils literal"><span class="pre">Shape</span></tt> <em>must</em> define the <tt class="docutils literal"><span class="pre">shapes</span></tt> attribute, otherwise
  789. the inherited <tt class="docutils literal"><span class="pre">draw</span></tt> method (and a lot of other methods too) will
  790. not work.</p>
  791. <p>The painting of the vehicle could be offered by a method:</p>
  792. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">colorful</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
  793. <span class="n">wheels</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;wheels&#39;</span><span class="p">]</span>
  794. <span class="n">wheels</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="s">&#39;blue&#39;</span><span class="p">)</span>
  795. <span class="n">wheels</span><span class="o">.</span><span class="n">set_linewidth</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
  796. <span class="n">wheels</span><span class="o">.</span><span class="n">set_linecolor</span><span class="p">(</span><span class="s">&#39;black&#39;</span><span class="p">)</span>
  797. <span class="n">under</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;body&#39;</span><span class="p">][</span><span class="s">&#39;under&#39;</span><span class="p">]</span>
  798. <span class="n">under</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="s">&#39;red&#39;</span><span class="p">)</span>
  799. <span class="n">over</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">][</span><span class="s">&#39;body&#39;</span><span class="p">][</span><span class="s">&#39;over&#39;</span><span class="p">]</span>
  800. <span class="n">over</span><span class="o">.</span><span class="n">set_filled_curves</span><span class="p">(</span><span class="n">pattern</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
  801. <span class="n">over</span><span class="o">.</span><span class="n">set_linewidth</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span>
  802. </pre></div>
  803. </div>
  804. <p>The usage of the class is simple: after having set up an appropriate
  805. coordinate system a s previously shown, we can do</p>
  806. <div class="highlight-python"><div class="highlight"><pre><span class="n">vehicle</span> <span class="o">=</span> <span class="n">Vehicle0</span><span class="p">(</span><span class="n">w_1</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">H</span><span class="p">)</span>
  807. <span class="n">vehicle</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  808. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">display</span><span class="p">()</span>
  809. </pre></div>
  810. </div>
  811. <p>The color from Figure <a class="reference internal" href="#sketcher-fig-vehicle0-v2"><em>Left: Basic line-based drawing. Right: Thicker lines and filled parts</em></a> is realized by</p>
  812. <div class="highlight-python"><div class="highlight"><pre><span class="n">drawing_tool</span><span class="o">.</span><span class="n">erase</span><span class="p">()</span>
  813. <span class="n">vehicle</span><span class="o">.</span><span class="n">colorful</span><span class="p">()</span>
  814. <span class="n">vehicle</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  815. <span class="n">drawing_tool</span><span class="o">.</span><span class="n">display</span><span class="p">()</span>
  816. </pre></div>
  817. </div>
  818. <p>A complete code defining and using class <tt class="docutils literal"><span class="pre">Vehicle0</span></tt> is found in the file
  819. <a href="#id5"><span class="problematic" id="id6">``</span></a>vehicle2.py` &lt;<a class="reference external" href="http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle2.py">http://hplgit.github.com/pysketcher/doc/src/sketcher/src-sketcher/vehicle2.py</a>&gt;`_.</p>
  820. <p>The <tt class="docutils literal"><span class="pre">pysketcher</span></tt> package contains a wide range of classes for various
  821. geometrical objects, particularly those that are frequently used in
  822. drawings of mechanical systems.</p>
  823. </div>
  824. </div>
  825. </div>
  826. <div class="section" id="adding-functionality-via-recursion">
  827. <h1>Adding Functionality via Recursion<a class="headerlink" href="#adding-functionality-via-recursion" title="Permalink to this headline">¶</a></h1>
  828. <p>The really powerful feature of our class hierarchy is that we can add
  829. much functionality to the superclass <tt class="docutils literal"><span class="pre">Shape</span></tt> and to the &#8220;bottom&#8221; classes
  830. <tt class="docutils literal"><span class="pre">Curve</span></tt>, and all other classes for all types of geometrical shapes
  831. immediately get the new functionality. To explain the idea we first have
  832. to look at the <tt class="docutils literal"><span class="pre">draw</span></tt> method, which all classes in the <tt class="docutils literal"><span class="pre">Shape</span></tt>
  833. hierarchy must have. The inner workings of the <tt class="docutils literal"><span class="pre">draw</span></tt> method explain
  834. the secrets of how a series of other useful operations on figures
  835. can be implemented.</p>
  836. <p>We work with two types of class hierarchies: one Python class hierarchy,
  837. with <tt class="docutils literal"><span class="pre">Shape</span></tt> as superclass, and one <em>object hierarchy</em> of figure elements
  838. in a specific figure. A subclass of <tt class="docutils literal"><span class="pre">Shape</span></tt> stores its figure in the
  839. <tt class="docutils literal"><span class="pre">self.shapes</span></tt> dictionary. This dictionary represents the object hierarchy
  840. of figure elements for that class. We want to make one <tt class="docutils literal"><span class="pre">draw</span></tt> call
  841. for an instance, say our class <tt class="docutils literal"><span class="pre">Vehicle0</span></tt>, and then we want this call
  842. to be propagated to <em>all</em> objects that are contained in
  843. <tt class="docutils literal"><span class="pre">self.shapes</span></tt> and all is nested subdictionaries. How is this done?</p>
  844. <p>The natural starting point is to call <tt class="docutils literal"><span class="pre">draw</span></tt> for each <tt class="docutils literal"><span class="pre">Shape</span></tt> object
  845. in the <tt class="docutils literal"><span class="pre">self.shapes</span></tt> dictionary:</p>
  846. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
  847. <span class="k">for</span> <span class="n">shape</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">:</span>
  848. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="n">shape</span><span class="p">]</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  849. </pre></div>
  850. </div>
  851. <p>This general method can be provided by class <tt class="docutils literal"><span class="pre">Shape</span></tt> and inherited in
  852. subclasses like <tt class="docutils literal"><span class="pre">Vehicle0</span></tt>. Let <tt class="docutils literal"><span class="pre">v</span></tt> be a <tt class="docutils literal"><span class="pre">Vehicle0</span></tt> instance.
  853. Seemingly, a call <tt class="docutils literal"><span class="pre">v.draw()</span></tt> just calls</p>
  854. <div class="highlight-python"><div class="highlight"><pre><span class="n">v</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;vehicle&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  855. <span class="n">v</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="s">&#39;ground&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
  856. </pre></div>
  857. </div>
  858. <p>However, in the former call we call the <tt class="docutils literal"><span class="pre">draw</span></tt> method of a <tt class="docutils literal"><span class="pre">Composition</span></tt> object
  859. whose <tt class="docutils literal"><span class="pre">self.shapes</span></tt> attributed has two elements: <tt class="docutils literal"><span class="pre">wheels</span></tt> and <tt class="docutils literal"><span class="pre">body</span></tt>.
  860. Since class <tt class="docutils literal"><span class="pre">Composition</span></tt> inherits the same <tt class="docutils literal"><span class="pre">draw</span></tt> method, this method will
  861. run through <tt class="docutils literal"><span class="pre">self.shapes</span></tt> and call <tt class="docutils literal"><span class="pre">wheels.draw()</span></tt> and <tt class="docutils literal"><span class="pre">body.draw()</span></tt>.
  862. Now, the <tt class="docutils literal"><span class="pre">wheels</span></tt> object is also a <tt class="docutils literal"><span class="pre">Composition</span></tt> with the same <tt class="docutils literal"><span class="pre">draw</span></tt>
  863. method, which will run through the <tt class="docutils literal"><span class="pre">shapes</span></tt> dictionary, now containing
  864. the <tt class="docutils literal"><span class="pre">wheel1</span></tt> and and <tt class="docutils literal"><span class="pre">wheel2</span></tt> objects. The <tt class="docutils literal"><span class="pre">wheel1</span></tt> object is a <tt class="docutils literal"><span class="pre">Circle</span></tt>,
  865. so calling <tt class="docutils literal"><span class="pre">wheel1.draw()</span></tt> calls the <tt class="docutils literal"><span class="pre">draw</span></tt> method in class <tt class="docutils literal"><span class="pre">Circle</span></tt>,
  866. but this is the same <tt class="docutils literal"><span class="pre">draw</span></tt> method as shown above. This method will
  867. therefore traverse the circle&#8217;s <tt class="docutils literal"><span class="pre">shapes</span></tt> dictionary, which we have seen
  868. consists of one <tt class="docutils literal"><span class="pre">Curve</span></tt> element.</p>
  869. <p>The <tt class="docutils literal"><span class="pre">Curve</span></tt> object holds the coordinates to be plotted so here <tt class="docutils literal"><span class="pre">draw</span></tt>
  870. really needs to do something &#8220;physical&#8221;, namely send the coordinates to
  871. the plotting program. The <tt class="docutils literal"><span class="pre">draw</span></tt> method is outlined in the short listing
  872. of class <tt class="docutils literal"><span class="pre">Curve</span></tt> shown previously.</p>
  873. <p>We can go to any of the other shape objects that appear in the figure
  874. hierarchy and follow their <tt class="docutils literal"><span class="pre">draw</span></tt> calls in the similar way. Every time,
  875. a <tt class="docutils literal"><span class="pre">draw</span></tt> call will invoke a new <tt class="docutils literal"><span class="pre">draw</span></tt> call, until we eventually hit
  876. a <tt class="docutils literal"><span class="pre">Curve</span></tt> object in the &#8220;botton&#8221; of the figure hierarchy, and then that part
  877. of the figure is really plotted (or more precisely, the coordinates
  878. are sent to a plotting program).</p>
  879. <p>When a method calls itself, such as <tt class="docutils literal"><span class="pre">draw</span></tt> does, the calls are known as
  880. <em>recursive</em> and the programming principle is referred to as
  881. <em>recursion</em>. This technique is very often used to traverse hierarchical
  882. structures like the figure structures we work with here. Even though the
  883. hierarchy of objects building up a figure are of different types, they
  884. all inherit the same <tt class="docutils literal"><span class="pre">draw</span></tt> method and therefore exhibit the same
  885. behavior with respect to drawing. Only the <tt class="docutils literal"><span class="pre">Curve</span></tt> object has a different
  886. <tt class="docutils literal"><span class="pre">draw</span></tt> method, which does not lead to more recursion. Without this
  887. different <tt class="docutils literal"><span class="pre">draw</span></tt> method in class <tt class="docutils literal"><span class="pre">Curve</span></tt>, the repeated <tt class="docutils literal"><span class="pre">draw</span></tt> calls would
  888. go on forever.</p>
  889. <p>Understanding recursion is usually a challenge. To get a better idea of
  890. how recursion works, we have equipped class <tt class="docutils literal"><span class="pre">Shape</span></tt> with a method <tt class="docutils literal"><span class="pre">recurse</span></tt>
  891. which just visits all the objects in the <tt class="docutils literal"><span class="pre">shapes</span></tt> dictionary and prints
  892. out a message for each object.
  893. This feature allows us to trace the execution and see exactly where
  894. we are in the hierarchy and which objects that are visited.</p>
  895. <p>The <tt class="docutils literal"><span class="pre">recurse</span></tt> method is very similar to <tt class="docutils literal"><span class="pre">draw</span></tt>:</p>
  896. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">recurse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
  897. <span class="c"># print message where we are (name is where we come from)</span>
  898. <span class="k">for</span> <span class="n">shape</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">:</span>
  899. <span class="c"># print message about which object to visit</span>
  900. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="n">shape</span><span class="p">]</span><span class="o">.</span><span class="n">recurse</span><span class="p">(</span><span class="n">indent</span><span class="o">+</span><span class="mi">2</span><span class="p">,</span> <span class="n">shape</span><span class="p">)</span>
  901. </pre></div>
  902. </div>
  903. <p>The <tt class="docutils literal"><span class="pre">indent</span></tt> parameter governs how much the message from this
  904. <tt class="docutils literal"><span class="pre">recurse</span></tt> method is intended. We increase <tt class="docutils literal"><span class="pre">indent</span></tt> by 2 for every level
  905. in the hierarchy. This makes it easy to see on the printout how far
  906. down in the hierarchy we are.</p>
  907. <p>A typical message written by <tt class="docutils literal"><span class="pre">recurse</span></tt> when <tt class="docutils literal"><span class="pre">name</span></tt> is <tt class="docutils literal"><span class="pre">body</span></tt> and
  908. the <tt class="docutils literal"><span class="pre">shapes</span></tt> dictionary contains two entries, <tt class="docutils literal"><span class="pre">over</span></tt> and <tt class="docutils literal"><span class="pre">under</span></tt>,
  909. will be</p>
  910. <div class="highlight-python"><pre>Composition: body.shapes has entries 'over', 'under'
  911. call body.shapes["over"].recurse("over", 6)</pre>
  912. </div>
  913. <p>The number of leading blanks on each line corresponds to the value of
  914. <tt class="docutils literal"><span class="pre">indent</span></tt>. The code printing out such messages looks like</p>
  915. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">recurse</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
  916. <span class="n">space</span> <span class="o">=</span> <span class="s">&#39; &#39;</span><span class="o">*</span><span class="n">indent</span>
  917. <span class="k">print</span> <span class="n">space</span><span class="p">,</span> <span class="s">&#39;</span><span class="si">%s</span><span class="s">: </span><span class="si">%s</span><span class="s">.shapes has entries&#39;</span> <span class="o">%</span> \
  918. <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__class__</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">name</span><span class="p">),</span> \
  919. <span class="nb">str</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="o">.</span><span class="n">keys</span><span class="p">()))[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
  920. <span class="k">for</span> <span class="n">shape</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">:</span>
  921. <span class="k">print</span> <span class="n">space</span><span class="p">,</span>
  922. <span class="k">print</span> <span class="s">&#39;call </span><span class="si">%s</span><span class="s">.shapes[&quot;</span><span class="si">%s</span><span class="s">&quot;].recurse(&quot;</span><span class="si">%s</span><span class="s">&quot;, </span><span class="si">%d</span><span class="s">)&#39;</span> <span class="o">%</span> \
  923. <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">shape</span><span class="p">,</span> <span class="n">shape</span><span class="p">,</span> <span class="n">indent</span><span class="o">+</span><span class="mi">2</span><span class="p">)</span>
  924. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="n">shape</span><span class="p">]</span><span class="o">.</span><span class="n">recurse</span><span class="p">(</span><span class="n">shape</span><span class="p">,</span> <span class="n">indent</span><span class="o">+</span><span class="mi">2</span><span class="p">)</span>
  925. </pre></div>
  926. </div>
  927. <p>Let us follow a <tt class="docutils literal"><span class="pre">v.recurse('vehicle')</span></tt> call in detail, <tt class="docutils literal"><span class="pre">v</span></tt> being
  928. a <tt class="docutils literal"><span class="pre">Vehicle0</span></tt> instance. Before looking into the output from <tt class="docutils literal"><span class="pre">recurse</span></tt>,
  929. let us get an overview of the figure hierarchy in the <tt class="docutils literal"><span class="pre">v</span></tt> object
  930. (as produced by <tt class="docutils literal"><span class="pre">print</span> <span class="pre">v</span></tt>)</p>
  931. <div class="highlight-python"><pre>ground
  932. wall
  933. vehicle
  934. body
  935. over
  936. rectangle
  937. under
  938. rectangle
  939. wheels
  940. wheel1
  941. arc
  942. wheel2
  943. arc</pre>
  944. </div>
  945. <p>The <tt class="docutils literal"><span class="pre">recurse</span></tt> method performs the same kind of traversal of the
  946. hierarchy, but writes out and explains a lot more.</p>
  947. <p>The data structure represented by <tt class="docutils literal"><span class="pre">v.shapes</span></tt> is known as a <em>tree</em>.
  948. As in physical trees, there is a <em>root</em>, here the <tt class="docutils literal"><span class="pre">v.shapes</span></tt>
  949. dictionary. A graphical illustration of the tree (upside down) is
  950. shown in Figure <em class="xref std std-ref">sketcher:fig:Vehicle0:hier2</em>.
  951. From the root there are one or more branches, here two:
  952. <tt class="docutils literal"><span class="pre">ground</span></tt> and <tt class="docutils literal"><span class="pre">vehicle</span></tt>. Following the <tt class="docutils literal"><span class="pre">vehicle</span></tt> branch, it has two new
  953. branches, <tt class="docutils literal"><span class="pre">body</span></tt> and <tt class="docutils literal"><span class="pre">wheels</span></tt>. Relationships as in family trees
  954. are often used to describe the relations in object trees too: we say
  955. that <tt class="docutils literal"><span class="pre">vehicle</span></tt> is the parent of <tt class="docutils literal"><span class="pre">body</span></tt> and that <tt class="docutils literal"><span class="pre">body</span></tt> is a child of
  956. <tt class="docutils literal"><span class="pre">vehicle</span></tt>. The term <em>node</em> is also often used to describe an element
  957. in a tree. A node may have several other nodes as <em>descendants</em>.</p>
  958. <div class="figure" id="id7">
  959. <img alt="_images/Vehicle0_hier2.png" src="_images/Vehicle0_hier2.png" style="width: 600px;" />
  960. <p class="caption"><em>Hierarchy of figure elements in an instance of class `Vehicle0`</em></p>
  961. </div>
  962. <p>Recursion is the principal programming technique to traverse tree structures.
  963. Any object in the tree can be viewed as a root of a subtree. For
  964. example, <tt class="docutils literal"><span class="pre">wheels</span></tt> is the root of a subtree that branches into
  965. <tt class="docutils literal"><span class="pre">wheel1</span></tt> and <tt class="docutils literal"><span class="pre">wheel2</span></tt>. So when processing an object in the tree,
  966. we imagine we process the root and then recurse into a subtree, but the
  967. first object we recurse into can be viewed as the root of the subtree, so the
  968. processing procedure of the parent object can be repeated.</p>
  969. <p>A recommended next step is to simulate the <tt class="docutils literal"><span class="pre">recurse</span></tt> method by hand and
  970. carefully check that what happens in the visits to <tt class="docutils literal"><span class="pre">recurse</span></tt> is
  971. consistent with the output listed below. Although tedious, this is
  972. a major exercise that guaranteed will help to demystify recursion.
  973. Also remember that it requires some efforts to understand recursion.</p>
  974. <p>A part of the printout of <tt class="docutils literal"><span class="pre">v.recurse('vehicle')</span></tt> looks like</p>
  975. <div class="highlight-python"><pre> Vehicle0: vehicle.shapes has entries 'ground', 'vehicle'
  976. call vehicle.shapes["ground"].recurse("ground", 2)
  977. Wall: ground.shapes has entries 'wall'
  978. call ground.shapes["wall"].recurse("wall", 4)
  979. reached "bottom" object Curve
  980. call vehicle.shapes["vehicle"].recurse("vehicle", 2)
  981. Composition: vehicle.shapes has entries 'body', 'wheels'
  982. call vehicle.shapes["body"].recurse("body", 4)
  983. Composition: body.shapes has entries 'over', 'under'
  984. call body.shapes["over"].recurse("over", 6)
  985. Rectangle: over.shapes has entries 'rectangle'
  986. call over.shapes["rectangle"].recurse("rectangle", 8)
  987. reached "bottom" object Curve
  988. call body.shapes["under"].recurse("under", 6)
  989. Rectangle: under.shapes has entries 'rectangle'
  990. call under.shapes["rectangle"].recurse("rectangle", 8)
  991. reached "bottom" object Curve
  992. ...</pre>
  993. </div>
  994. <p>This example should clearly demonstrate the principle that we
  995. can start at any object in the tree and do a recursive set
  996. of calls with that object as root.</p>
  997. <div class="section" id="scaling-translating-and-rotating-a-figure">
  998. <span id="sketcher-scaling"></span><h2>Scaling, Translating, and Rotating a Figure<a class="headerlink" href="#scaling-translating-and-rotating-a-figure" title="Permalink to this headline">¶</a></h2>
  999. <p>With recursion, as explained in the previous section, we can within
  1000. minutes equip <em>all</em> classes in the <tt class="docutils literal"><span class="pre">Shape</span></tt> hierarchy, both present and
  1001. future ones, with the ability to scale the figure, translate it,
  1002. or rotate it. This added functionality requires only a few lines
  1003. of code.</p>
  1004. <div class="section" id="scaling">
  1005. <h3>Scaling<a class="headerlink" href="#scaling" title="Permalink to this headline">¶</a></h3>
  1006. <p>We start with the simplest of the three geometric transformations,
  1007. namely scaling.
  1008. For a <tt class="docutils literal"><span class="pre">Curve</span></tt> instance containing a set of <span class="math">\(n\)</span> coordinates
  1009. <span class="math">\((x_i,y_i)\)</span> that make up a curve, scaling by
  1010. a factor <span class="math">\(a\)</span> means that we multiply all the <span class="math">\(x\)</span> and <span class="math">\(y\)</span> coordinates
  1011. by <span class="math">\(a\)</span>:</p>
  1012. <div class="math">
  1013. \[x_i \leftarrow ax_i,\quad y_i\leftarrow ay_i,
  1014. \quad i=0,\ldots,n-1\thinspace .\]</div>
  1015. <p>Here we apply the arrow as an assignment operator.
  1016. The corresponding Python implementation in
  1017. class <tt class="docutils literal"><span class="pre">Curve</span></tt> reads</p>
  1018. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Curve</span><span class="p">:</span>
  1019. <span class="o">...</span>
  1020. <span class="k">def</span> <span class="nf">scale</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">factor</span><span class="p">):</span>
  1021. <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">factor</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">x</span>
  1022. <span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">factor</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">y</span>
  1023. </pre></div>
  1024. </div>
  1025. <p>Note here that <tt class="docutils literal"><span class="pre">self.x</span></tt> and <tt class="docutils literal"><span class="pre">self.y</span></tt> are Numerical Python arrays,
  1026. so that multiplication by a scalar number <tt class="docutils literal"><span class="pre">factor</span></tt> is
  1027. a vectorized operation.</p>
  1028. <p>An even more efficient implementation is
  1029. to make use of in-place multiplication in the arrays, as this saves the creation
  1030. of temporary arrays like <tt class="docutils literal"><span class="pre">factor*self.x</span></tt>, which is then assigned to
  1031. <tt class="docutils literal"><span class="pre">self.x</span></tt>:</p>
  1032. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Curve</span><span class="p">:</span>
  1033. <span class="o">...</span>
  1034. <span class="k">def</span> <span class="nf">scale</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">factor</span><span class="p">):</span>
  1035. <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">*=</span> <span class="n">factor</span>
  1036. <span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">*=</span> <span class="n">factor</span>
  1037. </pre></div>
  1038. </div>
  1039. <p>In an instance of a subclass of <tt class="docutils literal"><span class="pre">Shape</span></tt>,
  1040. the meaning of a method <tt class="docutils literal"><span class="pre">scale</span></tt> is
  1041. to run through all objects in the dictionary <tt class="docutils literal"><span class="pre">shapes</span></tt> and ask
  1042. each object to scale itself. This is the same delegation of actions
  1043. to subclass instances as we do in the <tt class="docutils literal"><span class="pre">draw</span></tt> (or <tt class="docutils literal"><span class="pre">recurse</span></tt>) method. All
  1044. all objects, except <tt class="docutils literal"><span class="pre">Curve</span></tt> instances, can share the same
  1045. implementation of the <tt class="docutils literal"><span class="pre">scale</span></tt> method. Therefore, we place
  1046. the <tt class="docutils literal"><span class="pre">scale</span></tt> method in the superclass <tt class="docutils literal"><span class="pre">Shape</span></tt> such that all
  1047. subclasses inherit the method.
  1048. Since <tt class="docutils literal"><span class="pre">scale</span></tt> and <tt class="docutils literal"><span class="pre">draw</span></tt> are so similar,
  1049. we can easily implement the <tt class="docutils literal"><span class="pre">scale</span></tt> method in class <tt class="docutils literal"><span class="pre">Shape</span></tt> by
  1050. copying and editing the <tt class="docutils literal"><span class="pre">draw</span></tt> method:</p>
  1051. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Shape</span><span class="p">:</span>
  1052. <span class="o">...</span>
  1053. <span class="k">def</span> <span class="nf">scale</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">factor</span><span class="p">):</span>
  1054. <span class="k">for</span> <span class="n">shape</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">:</span>
  1055. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="n">shape</span><span class="p">]</span><span class="o">.</span><span class="n">scale</span><span class="p">(</span><span class="n">factor</span><span class="p">)</span>
  1056. </pre></div>
  1057. </div>
  1058. <p>This is all we have to do in order to equip all subclasses of
  1059. <tt class="docutils literal"><span class="pre">Shape</span></tt> with scaling functionality!
  1060. Any piece of the figure will scale itself, in the same manner
  1061. as it can draw itself.</p>
  1062. </div>
  1063. <div class="section" id="translation">
  1064. <h3>Translation<a class="headerlink" href="#translation" title="Permalink to this headline">¶</a></h3>
  1065. <p>A set of coordinates <span class="math">\((x_i, y_i)\)</span> can be translated <span class="math">\(v_0\)</span> units in
  1066. the <span class="math">\(x\)</span> direction and <span class="math">\(v_1\)</span> units in the <span class="math">\(y\)</span> direction using the formulas</p>
  1067. <div class="math">
  1068. \[x_i\leftarrow x_i+v_0,\quad y_i\leftarrow y_i+v_1,\quad i=0,\ldots,n-1\thinspace .\]</div>
  1069. <p>The natural specification of the translation is in terms of a
  1070. vector <span class="math">\(v=(v_0,v_1)\)</span>.
  1071. The corresponding Python implementation in class <tt class="docutils literal"><span class="pre">Curve</span></tt> becomes</p>
  1072. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Curve</span><span class="p">:</span>
  1073. <span class="o">...</span>
  1074. <span class="k">def</span> <span class="nf">translate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
  1075. <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">+=</span> <span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  1076. <span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">+=</span> <span class="n">v</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
  1077. </pre></div>
  1078. </div>
  1079. <p>The translation operation for a shape object is very similar to the
  1080. scaling and drawing operations. This means that we can implement a
  1081. common method <tt class="docutils literal"><span class="pre">translate</span></tt> in the superclass <tt class="docutils literal"><span class="pre">Shape</span></tt>. The code
  1082. is parallel to the <tt class="docutils literal"><span class="pre">scale</span></tt> method:</p>
  1083. <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Shape</span><span class="p">:</span>
  1084. <span class="o">....</span>
  1085. <span class="k">def</span> <span class="nf">translate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
  1086. <span class="k">for</span> <span class="n">shape</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">:</span>
  1087. <span class="bp">self</span><span class="o">.</span><span class="n">shapes</span><span class="p">[</span><span class="n">shape</span><span class="p">]</span><span class="o">.</span><span class="n">translate</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
  1088. </pre></div>
  1089. </div>
  1090. </div>
  1091. <div class="section" id="rotation">
  1092. <h3>Rotation<a class="headerlink" href="#rotation" title="Permalink to this headline">¶</a></h3>
  1093. <p>Rotating a figure is more complicated than scaling and translating.
  1094. A counter clockwise rotation of <span class="math">\(\theta\)</span> degrees for a set of
  1095. coordinates <span class="math">\((x_i,y_i)\)</span> is given by</p>
  1096. <div class="math">
  1097. \[\begin{split}\bar x_i &amp;\leftarrow x_i\cos\theta - y_i\sin\theta,\\
  1098. \bar y_i &amp;\leftarrow x_i\sin\theta + y_i\cos\theta\thinspace .\end{split}\]</div>
  1099. <p>This rotation is performed around the origin. If we want the figure
  1100. to be rotated with respect to a general point <span class="math">\((x,y)\)</span>, we need to
  1101. extend the formulas above:</p>
  1102. <div class="math">
  1103. \[\begin{split}\bar x_i &amp;\leftarrow x + (x_i -x)\cos\theta - (y_i -y)\sin\theta,\\
  1104. \bar y_i &amp;\leftarrow y + (x_i -x)\sin\theta + (y_i -y)\cos\theta\thinspace .\end{split}\]</div>
  1105. <p>The Python implementation in class <tt class="docutils literal"><span class="pre">Curve</span></tt>, assuming that <span class="math">\(\theta\)</span>
  1106. is given in degrees and not in radians, becomes</p>
  1107. <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">rotate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">angle</span><span class="p">,</span> <span class="n">center</span><span class="p">):</span>
  1108. <span class="n">angle</span> <span class="o">=</span> <span class="n">radians</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
  1109. <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">center</span>
  1110. <span class="n">c</span> <span class="o">=</span> <span class="n">cos</span><span class="p">(</span><span class="n">angle</span><span class="p">);</span> <span class="n">s</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
  1111. <span class="n">xnew</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">-</span> <span class="n">x</span><span class="p">)</span><span class="o">*</span><span class="n">c</span> <span class="o">-</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span><span class="o">*</span><span class="n">s</span>
  1112. <span class="n">ynew</span> <span class="o">=</span> <span class="n">y</span> <span class="o">+</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">-</span> <span class="n">x</span><span class="p">)</span><span class="o">*</span><span class="n">s</span> <span class="o">+</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span><span class="o">*</span><span class="n">c</span>
  1113. <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">xnew</span>
  1114. <span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">ynew</span>
  1115. </pre></div>
  1116. </div>
  1117. <p>The <tt class="docutils literal"><span class="pre">rotate</span></tt> method in class <tt class="docutils literal"><span class="pre">Shape</span></tt> is identical to the
  1118. <tt class="docutils literal"><span class="pre">draw</span></tt>, <tt class="docutils literal"><span class="pre">scale</span></tt>, and <tt class="docutils literal"><span class="pre">translate</span></tt> methods except that we
  1119. recurse into <tt class="docutils literal"><span class="pre">self.rotate(angle,</span> <span class="pre">center)</span></tt>.</p>
  1120. <p>We have already seen the <tt class="docutils literal"><span class="pre">rotate</span></tt> method in action when animating the
  1121. rolling wheel at the end of the section <a class="reference internal" href="#sketcher-vehicle1-anim"><em>Animation: Rolling the Wheels</em></a>.</p>
  1122. </div>
  1123. </div>
  1124. </div>
  1125. </div>
  1126. </div>
  1127. </div>
  1128. <div class="sphinxsidebar">
  1129. <div class="sphinxsidebarwrapper">
  1130. <h3><a href="index.html">Table Of Contents</a></h3>
  1131. <ul>
  1132. <li><a class="reference internal" href="#">Using Pysketcher to Create Principal Sketches of Physics Problems</a></li>
  1133. <li><a class="reference internal" href="#a-first-glimpse-of-pysketcher">A First Glimpse of Pysketcher</a><ul>
  1134. <li><a class="reference internal" href="#basic-construction-of-sketches">Basic Construction of Sketches</a><ul>
  1135. <li><a class="reference internal" href="#basic-drawing">Basic Drawing</a></li>
  1136. <li><a class="reference internal" href="#groups-of-objects">Groups of Objects</a></li>
  1137. <li><a class="reference internal" href="#changing-line-styles-and-colors">Changing Line Styles and Colors</a></li>
  1138. <li><a class="reference internal" href="#the-figure-composition-as-an-object-hierarchy">The Figure Composition as an Object Hierarchy</a></li>
  1139. <li><a class="reference internal" href="#animation-translating-the-vehicle">Animation: Translating the Vehicle</a></li>
  1140. <li><a class="reference internal" href="#animation-rolling-the-wheels">Animation: Rolling the Wheels</a></li>
  1141. </ul>
  1142. </li>
  1143. </ul>
  1144. </li>
  1145. <li><a class="reference internal" href="#inner-workings-of-the-pysketcher-tool">Inner Workings of the Pysketcher Tool</a><ul>
  1146. <li><a class="reference internal" href="#example-of-classes-for-geometric-objects">Example of Classes for Geometric Objects</a><ul>
  1147. <li><a class="reference internal" href="#simple-geometric-objects">Simple Geometric Objects</a></li>
  1148. <li><a class="reference internal" href="#class-curve">Class Curve</a></li>
  1149. <li><a class="reference internal" href="#compound-geometric-objects">Compound Geometric Objects</a></li>
  1150. </ul>
  1151. </li>
  1152. </ul>
  1153. </li>
  1154. <li><a class="reference internal" href="#adding-functionality-via-recursion">Adding Functionality via Recursion</a><ul>
  1155. <li><a class="reference internal" href="#scaling-translating-and-rotating-a-figure">Scaling, Translating, and Rotating a Figure</a><ul>
  1156. <li><a class="reference internal" href="#scaling">Scaling</a></li>
  1157. <li><a class="reference internal" href="#translation">Translation</a></li>
  1158. <li><a class="reference internal" href="#rotation">Rotation</a></li>
  1159. </ul>
  1160. </li>
  1161. </ul>
  1162. </li>
  1163. </ul>
  1164. <h4>Previous topic</h4>
  1165. <p class="topless"><a href="index.html"
  1166. title="previous chapter">Using Pysketcher to Create Principal Sketches of Physics Problems</a></p>
  1167. <div id="searchbox" style="display: none">
  1168. <h3>Quick search</h3>
  1169. <form class="search" action="search.html" method="get">
  1170. <input type="text" name="q" size="18" />
  1171. <input type="submit" value="Go" />
  1172. <input type="hidden" name="check_keywords" value="yes" />
  1173. <input type="hidden" name="area" value="default" />
  1174. </form>
  1175. <p class="searchtip" style="font-size: 90%">
  1176. Enter search terms or a module, class or function name.
  1177. </p>
  1178. </div>
  1179. <script type="text/javascript">$('#searchbox').show(0);</script>
  1180. </div>
  1181. </div>
  1182. <div class="clearer"></div>
  1183. </div>
  1184. <div class="related">
  1185. <h3>Navigation</h3>
  1186. <ul>
  1187. <li class="right" style="margin-right: 10px">
  1188. <a href="genindex.html" title="General Index"
  1189. >index</a></li>
  1190. <li class="right" >
  1191. <a href="index.html" title="Using Pysketcher to Create Principal Sketches of Physics Problems"
  1192. >previous</a> |</li>
  1193. <li><a href="index.html">Using Pysketcher to Create Principal Sketches of Physics Problems 1.0 documentation</a> &raquo;</li>
  1194. </ul>
  1195. </div>
  1196. <div class="footer">
  1197. &copy; Copyright 2012, 0.1.
  1198. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.2.
  1199. </div>
  1200. </body>
  1201. </html>