Hans Petter Langtangen 10 سال پیش
والد
کامیت
52f660cc1d

+ 35 - 6
README.do.txt

@@ -11,21 +11,26 @@ FIGURE: [doc/src/tut/fig-tut/wheel_on_inclined_plane, width=600 frac=0.6]
 Such figures can easily be *interactively* made using a lot of drawing programs.
 A Pysketcher figure, however, is defined in terms of computer code. This gives
 a great advantage: geometric features can be parameterized in term
-of variables, as here:
+of variables. Geometric variations are then trivially generated, and
+complicated figures can be built as a hierarchy of simpler elements.
+
+Here is a very simple figure that illustrates how geometric features are
+parameterized by variables (H, R, L, etc.):
 
 FIGURE: [doc/src/tut/fig-tut/vehicle0_dim, width=600 frac=0.6]
 
-One can then quickly change parameters, here to
+One can then quickly change parameters, below to
 `R=0.5; L=5; H=2` and `R=2; L=7; H=1`, and get new figures that would be
 tedious to draw manually in an interactive tool.
 
 FIGURE: [doc/src/tut/fig-tut/vehicle_v23, width=800]
 
-Another major feature of Pysketcher is the ability to let animate the
-sketch. Here is an example of a very simple vehicle on a bumpy road,
+Another major feature of Pysketcher is the ability to let the
+sketch be dynamic and make an animation of the time evolution.
+Here is an example of a very simple vehicle on a bumpy road,
 where the solution of a differential equation (upper blue line) is fed
 back to the sketch to make a vertical displacement of the spring and
-other objects in the vehicle, "view animation": "http://hplgit.github.io/bumpy/doc/src/mov-bumpy/m2_k1_5_b0_2/index.html" (the animation was created by
+other objects in the vehicle. "View animation": "http://hplgit.github.io/bumpy/doc/src/mov-bumpy/m2_k1_5_b0_2/index.html" (the animation was created by
 "this Pysketcher script": "https://github.com/hplgit/bumpy/blob/master/doc/src/fig-bumpy/bumpy_road_fig.py").
 
 FIGURE: [http://hplgit.github.io/bumpy/doc/src/mov-bumpy/m2_k1_5_b0_2/tmp_frame_0030.png, width=600]
@@ -36,6 +41,30 @@ FIGURE: [http://hplgit.github.io/bumpy/doc/src/mov-bumpy/m2_k1_5_b0_2/tmp_frame_
 For an introduction to Pysketcher, see the tutorial in "HTML": "http://hplgit.github.io/pysketcher/doc/pub/pysketcher.html", "Sphinx": "http://hplgit.github.io/pysketcher/doc/pub/html/index.html", or "PDF": "http://hplgit/github.io/pysketcher/doc/pub/pysketcher.pdf" format (or a simplified version of
 the tutorial in Chapter 9 in "A Primer on Scientific Programming with Python": "http://www.amazon.com/Scientific-Programming-Computational-Science-Engineering/dp/3642549586/ref=sr_1_2?s=books&ie=UTF8&qid=1407225588&sr=1-2&keywords=langtangen", by H. P. Langtangen, Springer, 2014).
 
+===== Examples =====
+
+See the `examples` directory for some examples beyond the more basic
+ones in the tutorial.
+For example, a pendulum and its body diagram,
+
+FIGURE: [examples/pendulum2, width=800 frac=1]
+
+can be created by the program "`examples/pendulum.py`": "https://github.com/hplgit/pysketcher/tree/master/examples/pendulum.py".
+
+===== Technology =====
+
+Pysketcher applies Matplotlib to make the drawings, but it is quite
+easy to replace the backend `MatplotlibDraw.py` by similar code utilizing
+TikZ or another plotting package. The Pysketcher software is a thin
+layer basically constructing a tree structure of elements in the
+sketch. A lot of classes are offered for different type of basic
+elements, such as Circle, Rectangle, Text, Text with arrow, Force,
+arbitrary curve, etc.
+Complicated figures can be created by sticking one
+figure into another
+(i.e., hierarchical building of figures by sticking one tree
+structure into another).
+
 ===== Citation =====
 
 If you use Pysketcher and want to cite it, you can either cite this
@@ -90,4 +119,4 @@ Pysketcher was first constructed as a powerful educational example on
 object-oriented programming for the book
 *A Primer on Scientific Programming With Python*, but the tool quickly
 became so useful for the author that it was further developed and
-heavily used for creating figures in other books by the author.
+heavily used for creating figures in other documents.

+ 26 - 1
README.md

@@ -40,6 +40,31 @@ other objects in the vehicle, [view animation](http://hplgit.github.io/bumpy/doc
 For an introduction to Pysketcher, see the tutorial in [HTML](http://hplgit.github.io/pysketcher/doc/pub/pysketcher.html), [Sphinx](http://hplgit.github.io/pysketcher/doc/pub/html/index.html), or [PDF](http://hplgit/github.io/pysketcher/doc/pub/pysketcher.pdf) format (or a simplified version of
 the tutorial in Chapter 9 in [A Primer on Scientific Programming with Python](http://www.amazon.com/Scientific-Programming-Computational-Science-Engineering/dp/3642549586/ref=sr_1_2?s=books&ie=UTF8&qid=1407225588&sr=1-2&keywords=langtangen), by H. P. Langtangen, Springer, 2014).
 
+### Examples
+
+See the `examples` directory for some examples beyond the more basic
+ones in the tutorial.
+For example, a pendulum and its body diagram,
+
+<!-- <img src="examples/pendulum2.png" width=800> -->
+![](examples/pendulum2.png)
+
+can be created by the program [`examples/pendulum.py`](https://github.com/hplgit/pysketcher/tree/master/examples/pendulum.py).
+
+### Technology
+
+Pysketcher applies Matplotlib to make the drawings, but it is quite
+easy to replace the backend `MatplotlibDraw.py` by similar code utilizing
+TikZ or another plotting package. The Pysketcher software is a thin
+layer basically constructing a tree structure of elements in the
+sketch. A lot of classes are offered for different type of basic
+elements, such as Circle, Rectangle, Text, Text with arrow, Force,
+arbitrary curve, etc.
+Complicated figures can be created by sticking one
+figure into another
+(i.e., hierarchical building of figures by sticking one tree
+structure into another).
+
 ### Citation
 
 If you use Pysketcher and want to cite it, you can either cite this
@@ -96,5 +121,5 @@ Pysketcher was first constructed as a powerful educational example on
 object-oriented programming for the book
 *A Primer on Scientific Programming With Python*, but the tool quickly
 became so useful for the author that it was further developed and
-heavily used for creating figures in other books by the author.
+heavily used for creating figures in other documents.
 

+ 7 - 3
doc/src/tut/classes.do.txt

@@ -5,6 +5,9 @@ This section presents many of the basic shapes in Pysketcher:
 `Spring`, `Dashpot`, and `Wavy`.
 Each shape is demonstrated with a figure and a
 unit test that shows how the figure is constructed in Python code.
+These demos rely heavily on the method `draw_dimensions` in
+the shape classes, which annotates the basic drawing of the shape
+with the various geometric parameters that govern the shape.
 
 
 ===== Axis =====
@@ -62,8 +65,9 @@ The above figure can be produced by the following code.
 
 @@@CODE ../../../pysketcher/shapes.py fromto: def test_Rectangle@drawing_tool.savefig\('tmp_Rectangle
 
-The `draw_dimension` method adds explanation of dimensions and various
-important argument in the construction of a shape.
+Note that the `draw_dimension` method adds explanation of dimensions and various
+important argument in the construction of a shape. It adapts the annotations
+to the geometry of the current shape.
 
 ===== Triangle =====
 
@@ -98,7 +102,7 @@ FIGURE: [fig-tut/Spring, width=800 frac=1]
 <linebreak>
 <linebreak>
 
-The code for making this spring is
+The code for making these two springs goes like this:
 
 @@@CODE ../../../pysketcher/shapes.py fromto: def test_Spring@drawing_tool.savefig\('tmp_Spring
 

BIN
examples/FE_comic_strip.pdf


BIN
examples/FE_comic_strip.png


+ 46 - 11
examples/osc1.py

@@ -6,10 +6,11 @@ W = L/6
 
 xmax = L
 drawing_tool.set_coordinate_system(xmin=-L, xmax=xmax,
-                                   ymin=-1, ymax=L,
-                                   axis=True,
+                                   ymin=-1, ymax=L+H,
+                                   axis=False,
                                    instruction_file='tmp_mpl.py')
 x = 0
+drawing_tool.set_linecolor('black')
 
 def make_dashpot(x):
     d_start = (-L,2*H)
@@ -27,21 +28,55 @@ def make_spring(x):
 d = make_dashpot(0)
 s = make_spring(0)
 
-M = Rectangle((0,H), 4*H, 4*H)
-left_wall = Rectangle((-L,0),H/10,4*H).set_filled_curves(pattern='/')
+M = Rectangle((0,H), 4*H, 4*H).set_linewidth(4)
+left_wall = Rectangle((-L,0),H/10,L).set_filled_curves(pattern='/')
 ground = Wall(x=[-L/2,L], y=[0,0], thickness=-H/10)
 wheel1 = Circle((H,H/2), H/2)
 wheel2 = wheel1.copy()
 wheel2.translate(point(2*H, 0))
+
+fontsize = 18
+text_m = Text('$m$', (2*H, H+2*H), fontsize=fontsize)
+text_kx = Text('$kx$', (-L/2, H+4*H), fontsize=fontsize)
+text_bv = Text('$b\dot x$', (-L/2, H), fontsize=fontsize)
+x_axis = Axis((2*H, L), H, '$x(t)$', fontsize=fontsize,
+              label_spacing=(0.04, -0.01))
+x_axis_start = Line((2*H, L-H/4), (2*H, L+H/4)).set_linewidth(4)
+
 fig = Composition({
-    'dashpot': d, 'spring': s, 'mass': M, 'left wall': left_wall,
-    'ground': ground, 'wheel1': wheel1, 'wheel2': wheel2})
+    'spring': s, 'mass': M, 'left wall': left_wall,
+    'ground': ground, 'wheel1': wheel1, 'wheel2': wheel2,
+    'text_m': text_m, 'text_kx': text_kx,
+    'x_axis': x_axis, 'x_axis_start': x_axis_start})
+
+fig.draw()
+drawing_tool.display()
+drawing_tool.savefig('tmp_oscillator_spring')
+
+drawing_tool.erase()
 
+fig['dashpot'] = d
+fig['text_bv'] = text_bv
+
+# or fig = Composition(dict(fig=fig, dashpot=d, text_bv=text_bv))
 fig.draw()
-#s.draw()
-print s
-print s.shapes['bar1']['line'].x, s.shapes['bar1']['line'].y
-print s.shapes['bar2']['line'].x, s.shapes['bar2']['line'].y
+
 drawing_tool.display()
-drawing_tool.savefig('oscillator')
+drawing_tool.savefig('tmp_oscillator')
+
+drawing_tool.erase()
+
+text_kx = Text('$s(u)$', (-L/2, H+4*H), fontsize=fontsize)
+text_bv = Text('$f(\dot u)$', (-L/2, H), fontsize=fontsize)
+x_axis = Axis((2*H, L), H, '$u(t)$', fontsize=fontsize,
+              label_spacing=(0.04, -0.01))
+F_force = Force((4*H, H+2*H), (4*H+H, H+2*H), '$F(t)$',
+                text_spacing=(0.035, -0.01), text_pos='end', fontsize=fontsize)
+fig['text_kx'] = text_kx
+fig['text_bv'] = text_bv
+fig['x_axis'] = x_axis
+fig['F_force'] = F_force
+fig.draw()
+drawing_tool.savefig('tmp_oscillator_general')
+
 raw_input()

+ 73 - 0
examples/osc2.py

@@ -0,0 +1,73 @@
+"""As osc.py, but without wheels."""
+from pysketcher import *
+
+L = 12.
+H = L/6
+W = L/6
+
+xmax = L
+drawing_tool.set_coordinate_system(xmin=-L, xmax=xmax,
+                                   ymin=-1, ymax=L+H,
+                                   axis=False,
+                                   instruction_file='tmp_mpl.py')
+x = 0
+drawing_tool.set_linecolor('black')
+
+def make_dashpot(x):
+    d_start = (-L,2*H)
+    d = Dashpot(start=d_start, total_length=L+x, width=W,
+                bar_length=3*H/2, dashpot_length=L/2, piston_pos=H+x)
+    d.rotate(-90, d_start)
+    return d
+
+def make_spring(x):
+    s_start = (-L,4*H)
+    s = Spring(start=s_start, length=L+x, bar_length=3*H/2, teeth=True)
+    s.rotate(-90, s_start)
+    return s
+
+d = make_dashpot(0)
+s = make_spring(0)
+
+M = Rectangle((0,H), 4*H, 4*H).set_linewidth(4)
+left_wall = Rectangle((-L,H),H/10,L-H).set_filled_curves(pattern='/')
+ground = Wall(x=[-L/2,L], y=[H,H], thickness=-H/10)
+
+fontsize = 18
+text_m = Text('$m$', (2*H, H+2*H), fontsize=fontsize)
+text_kx = Text('$s(u)$', (-L/2, H+4*H), fontsize=fontsize)
+text_bv = Text('$f(u)$', (-L/2, H), fontsize=fontsize)
+x_axis = Axis((2*H, L), H, '$u(t)$', fontsize=fontsize,
+              label_spacing=(0.04, -0.01))
+x_axis_start = Line((2*H, L-H/4), (2*H, L+H/4)).set_linewidth(4)
+
+fig = Composition({
+    'spring': s, 'mass': M, 'left wall': left_wall,
+    'ground': ground,
+    'text_m': text_m, 'text_kx': text_kx,
+    'x_axis': x_axis, 'x_axis_start': x_axis_start})
+
+fig.draw()
+drawing_tool.display()
+drawing_tool.savefig('tmp_oscillator2_spring')
+
+drawing_tool.erase()
+
+fig['dashpot'] = d
+fig['text_bv'] = text_bv
+
+# or fig = Composition(dict(fig=fig, dashpot=d, text_bv=text_bv))
+fig.draw()
+
+drawing_tool.display()
+drawing_tool.savefig('tmp_oscillator2')
+
+drawing_tool.erase()
+
+F_force = Force((4*H, H+2*H), (4*H+H, H+2*H), '$F(t)$',
+                text_spacing=(0.035, -0.01), text_pos='end', fontsize=fontsize)
+fig['F_force'] = F_force
+fig.draw()
+drawing_tool.savefig('tmp_oscillator2_force')
+
+raw_input()

BIN
examples/pendulum.pdf


BIN
examples/pendulum.png


+ 24 - 5
examples/pendulum.py

@@ -48,8 +48,6 @@ dims = Composition(
 
 fig = Composition({'body': mass, 'rod': rod, 'dims': dims})
 
-#drawing_tool.ax.set_xlim(4,10)
-#drawing_tool.ax.set_ylim(1,8)
 fig.draw()
 drawing_tool.display()
 drawing_tool.savefig('tmp_pendulum1')
@@ -62,7 +60,7 @@ drawing_tool.erase()
 drawing_tool.set_linecolor('black')
 mg_force = Force(mass_pt, mass_pt + L/5*point(0,-1), '$mg$', text_pos='end')
 rod_force = Force(mass_pt, mass_pt - L/3*unit_vec(rod_vec),
-                  '$S$', text_pos='end')
+                  '$S$', text_pos='end', text_spacing=(0.03, 0.01))
 
 rod_start = rod.geometric_features()['start']
 vertical2 = Line(rod_start, rod_start + point(0,-L/3))
@@ -82,11 +80,32 @@ body_diagram = Composition(
      'body': mass, 'm': mass_symbol})
 
 body_diagram.draw()
-drawing_tool.display('Body diagram')
+#drawing_tool.display('Body diagram')
 drawing_tool.savefig('tmp_pendulum2')
 
 drawing_tool.adjust_coordinate_system(body_diagram.minmax_coordinates(), 90)
-drawing_tool.display('Body diagram')
+#drawing_tool.display('Body diagram')
 drawing_tool.savefig('tmp_pendulum3')
 
+drawing_tool.erase()
+air_force = Force(mass_pt, mass_pt - L/6*unit_vec((rod_vec[1], -rod_vec[0])),
+                  '$\sim|v|v$', text_pos='end', text_spacing=(0.04,0.005))
+
+body_diagram['air'] = air_force
+body_diagram.draw()
+#drawing_tool.display('Body diagram')
+drawing_tool.savefig('tmp_pendulum4')
+
+drawing_tool.erase()
+ir = Force(P, P + L/10*unit_vec(rod_vec),
+           r'${\bf i}_r$', text_pos='end', text_spacing=(0.015,0))
+ith = Force(P, P + L/10*unit_vec((-rod_vec[1], rod_vec[0])),
+           r'${\bf i}_{\theta}$', text_pos='end', text_spacing=(0.02,0.005))
+
+body_diagram['ir'] = ir
+body_diagram['ith'] = ith
+body_diagram.draw()
+#drawing_tool.display('Body diagram')
+drawing_tool.savefig('tmp_pendulum5')
+
 raw_input()

BIN
examples/pendulum2.pdf


BIN
examples/pendulum2.png


BIN
examples/pendulum_body_diagram.pdf


BIN
examples/pendulum_body_diagram.png


+ 22 - 5
pysketcher/shapes.py

@@ -1348,10 +1348,13 @@ class Axis(Shape):
         Then return `rotation_angle` (in degrees).
         The `label_spacing` denotes the space between the label
         and the arrow tip as a fraction of the length of the plot
-        in x direction. With `label_alignment` one can place
+        in x direction. A tuple can be given to adjust the position
+        in both the x and y directions (with one parameter, the
+        x position is adjusted).
+        With `label_alignment` one can place
         the axis label text such that the arrow tip is to the 'left',
         'right', or 'center' with respect to the text field.
-        The `label_spacing` and `label_alignment` parameters can
+        The `label_spacing` and `label_alignment`parameters can
         be used to fine-tune the location of the label.
         """
         # Arrow is vertical arrow, make it horizontal
@@ -1387,7 +1390,15 @@ class Force(Arrow1):
     def __init__(self, start, end, text, text_spacing=1./60,
                  fontsize=0, text_pos='start', text_alignment='center'):
         Arrow1.__init__(self, start, end, style='->')
-        spacing = drawing_tool.xrange*text_spacing
+        if isinstance(text_spacing, (tuple,list)):
+            if len(text_spacing) == 2:
+                spacing = point(drawing_tool.xrange*text_spacing[0],
+                                drawing_tool.xrange*text_spacing[1])
+            else:
+                spacing = drawing_tool.xrange*text_spacing[0]
+        else:
+            # just a number, this is x spacing
+            spacing = drawing_tool.xrange*text_spacing
         start, end = arr2D(start), arr2D(end)
 
         # Two cases: label at bottom of line or top, need more
@@ -1400,12 +1411,18 @@ class Force(Arrow1):
                 spacing_dir = unit_vec(start - end)
                 if upward:
                     spacing *= 1.7
-                text_pos = start + spacing*spacing_dir
+                if isinstance(spacing, (int, float)):
+                    text_pos = start + spacing*spacing_dir
+                else:
+                    text_pos = start + spacing
             elif text_pos == 'end':
                 spacing_dir = unit_vec(end - start)
                 if downward:
                     spacing *= 1.7
-                text_pos = end + spacing*spacing_dir
+                if isinstance(spacing, (int, float)):
+                    text_pos = end + spacing*spacing_dir
+                else:
+                    text_pos = end + spacing
         self.shapes['text'] = Text(text, text_pos, fontsize=fontsize,
                                    alignment=text_alignment)