瀏覽代碼

adding a Gallery

Gilbert Brault 5 年之前
父節點
當前提交
62293dc07b

+ 2 - 0
README.md

@@ -1,3 +1,5 @@
+![Gallery](#fig/JupyterPySketcher.png)
+
 ## jupytersketcher
 
 Tool for creating sketches of physics problems in terms of Python code. This work is based upon [pysketcher](https://github.com/hplgit/pysketcher) originally developped by HPL [see Wikipedia](https://en.wikipedia.org/wiki/Hans_Petter_Langtangen).

二進制
fig/JupyterPySketcher.png


File diff suppressed because it is too large
+ 93 - 39
notebooks/.ipynb_checkpoints/DryFriction-checkpoint.ipynb


File diff suppressed because it is too large
+ 226 - 287
notebooks/.ipynb_checkpoints/test-checkpoint.ipynb


File diff suppressed because it is too large
+ 128 - 83
notebooks/DryFriction.ipynb


+ 163 - 0
notebooks/Gallery.ipynb

@@ -0,0 +1,163 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Gallery"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib widget"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from ipywidgets import HBox, VBox, Output, Label"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "url = \"https://raw.githubusercontent.com/gbrault/jupytersketches/master/\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "application/vnd.jupyter.widget-view+json": {
+       "model_id": "d78995ed95724bb68c60298b74ef60d8",
+       "version_major": 2,
+       "version_minor": 0
+      },
+      "text/plain": [
+       "Output()"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gallery = Output()\n",
+    "gallery"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from pysketcher import *"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import time"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import HTML, SVG, display, clear_output"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "files = [(\"dryfriction\",\"friction\"),(\"pendulum\",\"pendulum\"),(\"car\",\"car\"),(\"springmass\",\"springmass\"),(\"springdashpotmass\",\"springdashpotmass\")]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "i = 0\n",
+    "horiz = []\n",
+    "vert = []\n",
+    "outputs = [Output() for file in files]\n",
+    "for output in outputs:\n",
+    "    if i == 3:\n",
+    "        vert.append(HBox(horiz))\n",
+    "        horiz = [output]\n",
+    "        i = 0\n",
+    "    else:\n",
+    "        i += 1\n",
+    "        horiz.append(output)\n",
+    "if i >= 0 and i < 3:\n",
+    "    vert.append(HBox(horiz))\n",
+    "with gallery:\n",
+    "    display(VBox(vert))\n",
+    "\n",
+    "i = 0\n",
+    "for file in files:\n",
+    "    sketch = Sketch({})\n",
+    "    sketch.url2Sketch(url + file[0] +\".yml\")\n",
+    "    drawing_tool.erase()\n",
+    "    sketch.container[file[1]].draw()\n",
+    "    with outputs[i]:\n",
+    "        clear_output(wait=True)\n",
+    "        display(HTML(f\"<a target=\\\"_blank\\\" href=\\\"{url + file[0] +'.yml'}\\\"><h3>{file[0]}</h3></a>\"),SVG(Sketch.matplotlib2SVG()))\n",
+    "    i += 1\n",
+    "    time.sleep(0.1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.5"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 43 - 0
notebooks/car.yml

@@ -0,0 +1,43 @@
+!!omap
+- name: car
+- parts:
+  - name: head
+    shapes:
+      libraries: ['from math import tan, radians, sin, cos', 'from pysketcher import *']
+  - name: constants
+    shapes:
+        R: 1.0    # radius of wheel
+        L: 4.0    # distance between wheels
+        H: 2.0    # height of vehicle body
+        w_1: 5.0  # position of front wheel
+  - name: frame
+    shapes:
+        xmax: w_1 + 2*L + 3*R
+        setframe:
+            action: drawing_tool.set_coordinate_system(xmin=0, xmax=xmax,
+                                   ymin=-1, ymax=2*R + 3*H,
+                                   axis=False)
+        wheel1: |
+                 Composition({'wheel': Circle(center=(w_1, R), radius=R),
+                    'cross': Composition({'cross1': Line((w_1,0),(w_1,2*R)),
+                    'cross2': Line((w_1-R,R), (w_1+R,R))})})
+        wheel2: 
+            formula: wheel1.copy()
+            transform: translate((L,0))
+
+        under: Rectangle(lower_left_corner=(w_1-2*R, 2*R),
+                  width=2*R + L + 2*R, height=H)
+        over: Rectangle(lower_left_corner=(w_1, 2*R + H),
+                  width=2.5*R, height=1.25*H)
+
+        wheels: | 
+                  Composition({'wheel1': wheel1, 'wheel2': wheel2})
+        body:  |
+                 Composition({'under': under, 'over': over})
+
+        vehicle: |
+                  Composition({'wheels': wheels, 'body': body})
+        ground: Wall(x=[R, xmax], y=[0, 0], thickness=-0.3*R)
+
+        car: |
+               Composition({'vehicle': vehicle, 'ground': ground})

+ 3 - 2
notebooks/dryfriction.yml

@@ -1,5 +1,5 @@
 !!omap
-- name: unknown
+- name: dryfriction
 - parts:
   - name: head
     shapes:
@@ -38,7 +38,8 @@
         formula: Rectangle(contact, rl, rL)
         style:
           linecolor: blue
-          filled_curves: blue
+          filled_curves:
+              color: blue
         transform: ['rotate(-theta, contact)', translate(-rl/2*tangent_vec)]
       N:
         formula: Force(contact - rl*normal_vec, contact, r'$N$', text_pos='start')

+ 38 - 2
notebooks/other examples/Introduction.ipynb

@@ -20,7 +20,43 @@
    "cell_type": "code",
    "execution_count": 2,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{\n",
+       "'vehicle': {\n",
+       "    'wheels': {\n",
+       "        'wheel1': {\n",
+       "            'wheel': {\n",
+       "                'arc': \"181 (x,y) coords\",},\n",
+       "            'cross': {\n",
+       "                'cross1': {\n",
+       "                    'line': \"2 (x,y) coords\",},\n",
+       "                'cross2': {\n",
+       "                    'line': \"2 (x,y) coords\",},},},\n",
+       "        'wheel2': {\n",
+       "            'wheel': {\n",
+       "                'arc': \"181 (x,y) coords\",},\n",
+       "            'cross': {\n",
+       "                'cross1': {\n",
+       "                    'line': \"2 (x,y) coords\",},\n",
+       "                'cross2': {\n",
+       "                    'line': \"2 (x,y) coords\",},},},},\n",
+       "    'body': {\n",
+       "        'under': {\n",
+       "            'rectangle': \"5 (x,y) coords\",},\n",
+       "        'over': {\n",
+       "            'rectangle': \"5 (x,y) coords\",},},},\n",
+       "'ground': {\n",
+       "    'wall': \"4 (x,y) coords fillcolor='white' fillpattern='/'\",},}"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "from pysketcher import *\n",
     "\n",
@@ -64,7 +100,7 @@
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "f410cc4d6ed04658bfdee411622e6e60",
+       "model_id": "ae74d9c0c99348429e9caee48e646a0b",
        "version_major": 2,
        "version_minor": 0
       },

+ 21 - 19
notebooks/other examples/beam1.ipynb

@@ -20,22 +20,7 @@
    "cell_type": "code",
    "execution_count": 2,
    "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "803251c3e4b1454682f45545d26b341c",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
+   "outputs": [],
    "source": [
     "\"\"\"A very simple beam.\"\"\"\n",
     "from pysketcher import *\n",
@@ -70,10 +55,27 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 4,
    "metadata": {},
-   "outputs": [],
-   "source": []
+   "outputs": [
+    {
+     "data": {
+      "application/vnd.jupyter.widget-view+json": {
+       "model_id": "e76980b046b2452286ab2aa80d6206f1",
+       "version_major": 2,
+       "version_minor": 0
+      },
+      "text/plain": [
+       "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "drawing_tool.mpl.gcf().canvas"
+   ]
   }
  ],
  "metadata": {

+ 3 - 3
notebooks/other examples/beam2.ipynb

@@ -141,13 +141,13 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "8ddace09b4ed4476a8217d191eb07ce1",
+       "model_id": "80c268fe8be04b52b488fdeb4bcb8c58",
        "version_major": 2,
        "version_minor": 0
       },
@@ -160,7 +160,7 @@
     }
    ],
    "source": [
-    "beam()"
+    "drawing_tool.mpl.gcf().canvas"
    ]
   },
   {

+ 52 - 0
notebooks/pendulum.yml

@@ -0,0 +1,52 @@
+!!omap
+- name: pendulum
+- parts:
+  - name: head
+    shapes:
+      libraries: ['from math import tan, radians, sin, cos', 'from pysketcher import *']
+  - name: constants
+    shapes:
+        H: 7.0
+        W: 6.0
+  - name: frame
+    shapes:
+        setframe:          # sketch setup
+            action: drawing_tool.set_coordinate_system(xmin=0, xmax=W,
+                                   ymin=0, ymax=H,
+                                   axis=False)
+        setblackline:      # default frame values and actions
+            action: drawing_tool.set_linecolor('blue')
+        L: 5*H/7          # length
+        P: (W/6, 0.85*H)  # rotation point
+        a: 40             # angle
+        vertical: 
+            formula: Line(P, P-point(0,L))
+            style:
+                linecolor: black
+                linewidth: 1
+        path: 
+            formula: Arc(P, L, -90, a)
+            style:
+                linecolor: black
+                linewidth: 1
+        angle: Arc_wText(r'$\theta$', P, L/4, -90, a, text_spacing=1/30.)
+
+        #rod: Line(P, P + L*point(sin(radians(a)), -L*cos(radians(a)))) is a less reliable alternative
+        mass_pt: path.geometric_features()['end']
+        rod: Line(P, mass_pt)
+
+        mass: 
+            formula: Circle(center=mass_pt, radius=L/20.)
+            style:
+                filled_curves: 
+                    color: 'blue'
+        rod_vec: rod.geometric_features()['end'] - rod.geometric_features()['start']
+        unit_rod_vec: unit_vec(rod_vec)
+        mass_symbol: Text('$m$', mass_pt + L/10*unit_rod_vec)
+
+        length: 
+            formula: Distance_wText(P, mass_pt, '$L$') # Displace length indication
+            transform: translate(L/15*point(cos(radians(a)), sin(radians(a))))
+        gravity: Gravity(start=P+point(0.8*L,0), length=L/3)
+
+        pendulum: "Composition({'body': mass, 'rod': rod,'vertical': vertical, 'theta': angle, 'path': path,'g': gravity, 'L': length, 'm': mass_symbol})"

+ 56 - 0
notebooks/springdashpotmass.yml

@@ -0,0 +1,56 @@
+!!omap
+- name: springdashpotmass
+- parts:
+  - name: head
+    shapes:
+      libraries: ['from math import tan, radians, sin, cos', 'from pysketcher import *']
+  - name: constants
+    shapes:
+        L: 12.
+  - name: frame
+    shapes:
+        H: L/6
+        W: L/6
+        xmax: L
+        x: 0        
+        setframe:
+            action: drawing_tool.set_coordinate_system(xmin=-L, xmax=xmax,
+                                   ymin=-1, ymax=L+H,
+                                   axis=False,
+                                   instruction_file='tmp_mpl_spring_mass.py')
+        globallinecolor:
+            action: drawing_tool.set_linecolor('black')
+        d_start: (-L,2*H)
+        dashpot:
+            formula: Dashpot(start=d_start, total_length=L+x, width=W,
+                bar_length=3*H/2, dashpot_length=L/2, piston_pos=H+x)
+            transform: rotate(-90, d_start)
+        s_start: (-L,4*H)
+        spring: 
+            formula: Spring(start=s_start, length=L+x, bar_length=3*H/2, teeth=True)
+            transform: rotate(-90, s_start)
+        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: 
+            formula: wheel1.copy()
+            transform: translate(point(2*H, 0))
+
+        fontsize: 18
+        text_m: Text('$m$', (2*H, H+2*H), fontsize=fontsize)
+        text_ku: Text('$ku$', (-L/2, H+4*H), fontsize=fontsize)
+        text_bv: Text("$bu'$", (-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: 
+            formula: Line((2*H, L-H/4), (2*H, L+H/4))
+            style:
+                linewidth: 4
+
+        springdashpotmass: |
+                      Composition({
+                        'spring': spring, 'dashpot': dashpot, 'mass': M, 'left wall': left_wall,
+                        'ground': ground, 'wheel1': wheel1, 'wheel2': wheel2,
+                        'text_m': text_m, 'text_ku': text_ku,
+                        'x_axis': x_axis, 'x_axis_start': x_axis_start})

+ 51 - 0
notebooks/springmass.yml

@@ -0,0 +1,51 @@
+!!omap
+- name: springmass
+- parts:
+  - name: head
+    shapes:
+      libraries: ['from math import tan, radians, sin, cos', 'from pysketcher import *']
+  - name: constants
+    shapes:
+        L: 12.
+  - name: frame
+    shapes:
+        H: L/6
+        W: L/6
+        xmax: L
+        x: 0        
+        setframe:
+            action: drawing_tool.set_coordinate_system(xmin=-L, xmax=xmax,
+                                   ymin=-1, ymax=L+H,
+                                   axis=False,
+                                   instruction_file='tmp_mpl_spring_mass.py')
+        globallinecolor:
+            action: drawing_tool.set_linecolor('black')
+        s_start: (-L,4*H)
+        spring: 
+            formula: Spring(start=s_start, length=L+x, bar_length=3*H/2, teeth=True)
+            transform: rotate(-90, s_start)
+        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: 
+            formula: wheel1.copy()
+            transform: translate(point(2*H, 0))
+
+        fontsize: 18
+        text_m: Text('$m$', (2*H, H+2*H), fontsize=fontsize)
+        text_ku: Text('$ku$', (-L/2, H+4*H), fontsize=fontsize)
+        text_bv: Text("$bu'$", (-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: 
+            formula: Line((2*H, L-H/4), (2*H, L+H/4))
+            style:
+                linewidth: 4
+
+        springmass: |
+                      Composition({
+                        'spring': spring, 'mass': M, 'left wall': left_wall,
+                        'ground': ground, 'wheel1': wheel1, 'wheel2': wheel2,
+                        'text_m': text_m, 'text_ku': text_ku,
+                        'x_axis': x_axis, 'x_axis_start': x_axis_start})

File diff suppressed because it is too large
+ 416 - 3
notebooks/test.ipynb


+ 32 - 12
pysketcher/shapes.py

@@ -62,11 +62,11 @@ class Sketch():
         try:
             root = ast.parse(expression)
         except Exception as e:
-            return f"{sketch}/{key}: '''{expression}''' parse error {str(e)}"
+            return f"{sketchpart}/{key}: '''{expression}''' parse error {str(e)}"
         names = {node.id for node in ast.walk(root) if isinstance(node, ast.Name)}
         for name in names:
             if name not in self.container:
-                return f"{sketch}/{key}: {name} in {expression} is not defined"
+                return f"{sketchpart}/{key}: {name} in {expression} is not defined"
         return 1
 
     def url2Sketch(self, url):
@@ -138,6 +138,14 @@ class Sketch():
         gwd = psketch['shapes']
         return self.add(sketch_name, gwd)
 
+    def normalize(self, input):
+        # print(input,"  ",type(input))
+        t = str(type(input))
+        if t == "<class 'str'>" or t == "<class 'ruamel.yaml.scalarstring.LiteralScalarString'>":
+            return "".join([s.strip() for s in input.split("\n")])
+        else:
+            return input
+
     def add(self, sketch_name, gwd):
         """
         actual append work common to various different calls
@@ -156,8 +164,8 @@ class Sketch():
                     exec(l,self.container)
             #print(_k, _c, _t)
             if _t == "<class 'ruamel.yaml.scalarfloat.ScalarFloat'>" or \
-            _t == "<class 'str'>" or _t == "<class 'int'>":
-                _expression = f"{_c}".replace("<bslash>","\\") 
+            _t == "<class 'str'>" or _t == "<class 'int'>" or _t == "<class 'ruamel.yaml.scalarstring.LiteralScalarString'>":
+                _expression = f"{self.normalize(_c)}".replace("<bslash>","\\") 
                 _formula = f"{_k} = {_expression}"
                 #print(_formula)
                 _r = self.sVe(_k, _expression, sketch_name)
@@ -170,7 +178,7 @@ class Sketch():
                 _keys = list(_c.keys())
                 #print(_keys)
                 if 'formula' in _keys:
-                    _expression = f"{_c['formula']}".replace("<bslash>","\\") 
+                    _expression = f"{self.normalize(_c['formula'])}".replace("<bslash>","\\") 
                     _formula = f"{_k} = {_expression}"
                     #print(_formula)
                     _r = self.sVe(_k, _expression, sketch_name)
@@ -189,15 +197,27 @@ class Sketch():
                         __t = str(type(_param))
                         #print(__t)
                         if __t == "<class 'int'>":
-                            _style = f"{_k}.set_{_style}({_param})"
+                            if _style == 'shadow':
+                                _style = f"{_k}.set_{_style}(pixel_displacement={_param})"
+                            else:
+                                _style = f"{_k}.set_{_style}({_param})"
+                            exec(_style,self.container)
                         else:
-                            _style = f"{_k}.set_{_style}('{_param}')"
-                        #print(_style)
-                        exec(_style,self.container)
+                            if 'filled_curves' == _style:
+                                if 'color' in list(_param.keys()):
+                                    _style = f"{_k}.set_{_style}(color='{_param['color']}')"
+                                    exec(_style,self.container)
+                                if 'pattern' in list(_param.keys()):
+                                    _style = f"{_k}.set_{_style}(pattern='{_param['pattern']}')"
+                                    exec(_style,self.container)
+                            else:
+                                _style = f"{_k}.set_{_style}('{_param}')"
+                                exec(_style,self.container)
+                        #print(_style)                        
                 if 'transform' in _keys:
                     #print(_c['transform'])
                     if str(type(_c['transform'])) == "<class 'str'>":
-                        _t = f"{_k}.{_c['transform']}"
+                        _t = f"{_k}.{self.normalize(_c['transform'])}"
                         #print(_t)
                         _r = self.sVe(_k, _formula, sketch_name)
                         if type(_r) == str:
@@ -207,7 +227,7 @@ class Sketch():
                     else:
                         for _transform in _c["transform"]:
                         #  x_const.rotate(-theta, contact)
-                            _t = f"{_k}.{_transform}"
+                            _t = f"{_k}.{self.normalize(_transform)}"
                             #print(_t)
                             _r = self.sVe(_k, _t, sketch_name)
                             if type(_r) == str:
@@ -215,7 +235,7 @@ class Sketch():
                                 return False
                             exec(_t,self.container)
                 if "action" in _keys:
-                    _action = _c["action"]
+                    _action = self.normalize(_c["action"])
                     #print(_action)
                     _r = self.sVe(_k, _action, sketch_name)
                     if type(_r) == str: