seolpyo-mplchart 1.4.1__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. seolpyo_mplchart/__init__.py +53 -333
  2. seolpyo_mplchart/_chart/__init__.py +145 -0
  3. seolpyo_mplchart/_chart/_base.py +217 -0
  4. seolpyo_mplchart/_chart/_cursor/__init__.py +2 -0
  5. seolpyo_mplchart/_chart/_cursor/_artist.py +217 -0
  6. seolpyo_mplchart/_chart/_cursor/_cursor.py +165 -0
  7. seolpyo_mplchart/_chart/_cursor/_info.py +187 -0
  8. seolpyo_mplchart/_chart/_draw/__init__.py +2 -0
  9. seolpyo_mplchart/_chart/_draw/_artist.py +50 -0
  10. seolpyo_mplchart/_chart/_draw/_data.py +314 -0
  11. seolpyo_mplchart/_chart/_draw/_draw.py +103 -0
  12. seolpyo_mplchart/_chart/_draw/_lim.py +265 -0
  13. seolpyo_mplchart/_chart/_slider/__init__.py +1 -0
  14. seolpyo_mplchart/_chart/_slider/_base.py +268 -0
  15. seolpyo_mplchart/_chart/_slider/_data.py +105 -0
  16. seolpyo_mplchart/_chart/_slider/_mouse.py +176 -0
  17. seolpyo_mplchart/_chart/_slider/_nav.py +204 -0
  18. seolpyo_mplchart/_chart/base/__init__.py +111 -0
  19. seolpyo_mplchart/_chart/base/a_canvas.py +250 -0
  20. seolpyo_mplchart/_chart/base/b_artist.py +143 -0
  21. seolpyo_mplchart/_chart/base/c_draw.py +100 -0
  22. seolpyo_mplchart/_chart/base/d_segment.py +262 -0
  23. seolpyo_mplchart/_chart/base/e_axis.py +267 -0
  24. seolpyo_mplchart/_chart/base/f_background.py +62 -0
  25. seolpyo_mplchart/_chart/base/g_event.py +66 -0
  26. seolpyo_mplchart/_chart/base/h_data.py +138 -0
  27. seolpyo_mplchart/_chart/base/test.py +58 -0
  28. seolpyo_mplchart/_chart/cursor/__init__.py +125 -0
  29. seolpyo_mplchart/_chart/cursor/b_artist.py +130 -0
  30. seolpyo_mplchart/_chart/cursor/c_draw.py +96 -0
  31. seolpyo_mplchart/_chart/cursor/d_segment.py +359 -0
  32. seolpyo_mplchart/_chart/cursor/e_axis.py +65 -0
  33. seolpyo_mplchart/_chart/cursor/g_event.py +233 -0
  34. seolpyo_mplchart/_chart/cursor/h_data.py +61 -0
  35. seolpyo_mplchart/_chart/cursor/test.py +69 -0
  36. seolpyo_mplchart/_chart/slider/__init__.py +169 -0
  37. seolpyo_mplchart/_chart/slider/a_canvas.py +260 -0
  38. seolpyo_mplchart/_chart/slider/b_artist.py +91 -0
  39. seolpyo_mplchart/_chart/slider/c_draw.py +54 -0
  40. seolpyo_mplchart/_chart/slider/d_segment.py +166 -0
  41. seolpyo_mplchart/_chart/slider/e_axis.py +70 -0
  42. seolpyo_mplchart/_chart/slider/f_background.py +37 -0
  43. seolpyo_mplchart/_chart/slider/g_event.py +353 -0
  44. seolpyo_mplchart/_chart/slider/h_data.py +102 -0
  45. seolpyo_mplchart/_chart/slider/test.py +71 -0
  46. seolpyo_mplchart/_chart/test.py +121 -0
  47. seolpyo_mplchart/_config/__init__.py +3 -0
  48. seolpyo_mplchart/_config/ax.py +28 -0
  49. seolpyo_mplchart/_config/candle.py +31 -0
  50. seolpyo_mplchart/_config/config.py +21 -0
  51. seolpyo_mplchart/_config/cursor.py +49 -0
  52. seolpyo_mplchart/_config/figure.py +40 -0
  53. seolpyo_mplchart/_config/format.py +51 -0
  54. seolpyo_mplchart/_config/ma.py +17 -0
  55. seolpyo_mplchart/_config/slider/__init__.py +2 -0
  56. seolpyo_mplchart/_config/slider/config.py +24 -0
  57. seolpyo_mplchart/_config/slider/figure.py +19 -0
  58. seolpyo_mplchart/_config/slider/nav.py +10 -0
  59. seolpyo_mplchart/_config/unit.py +19 -0
  60. seolpyo_mplchart/_config/utils.py +67 -0
  61. seolpyo_mplchart/_config/volume.py +27 -0
  62. seolpyo_mplchart/_cursor.py +27 -25
  63. seolpyo_mplchart/_draw.py +7 -18
  64. seolpyo_mplchart/_slider.py +26 -20
  65. seolpyo_mplchart/_utils/__init__.py +10 -0
  66. seolpyo_mplchart/_utils/nums.py +67 -0
  67. seolpyo_mplchart/_utils/theme/__init__.py +15 -0
  68. seolpyo_mplchart/_utils/theme/dark.py +57 -0
  69. seolpyo_mplchart/_utils/theme/light.py +56 -0
  70. seolpyo_mplchart/_utils/utils.py +28 -0
  71. seolpyo_mplchart/_utils/xl/__init__.py +15 -0
  72. seolpyo_mplchart/_utils/xl/csv.py +46 -0
  73. seolpyo_mplchart/_utils/xl/xlsx.py +49 -0
  74. seolpyo_mplchart/sample/apple.txt +6058 -0
  75. seolpyo_mplchart/sample/samsung.txt +5938 -0
  76. seolpyo_mplchart/test.py +172 -56
  77. seolpyo_mplchart/xl_to_dict.py +47 -0
  78. seolpyo_mplchart-2.1.0.dist-info/METADATA +718 -0
  79. seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
  80. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/WHEEL +1 -1
  81. seolpyo_mplchart-1.4.1.dist-info/METADATA +0 -57
  82. seolpyo_mplchart-1.4.1.dist-info/RECORD +0 -17
  83. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,176 @@
1
+ from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
2
+
3
+ from ._data import BaseMixin as Base
4
+
5
+
6
+ class MouseMoveMixin(Base):
7
+ in_slider = False
8
+ is_click_slider = False
9
+
10
+ def _erase_crossline(self):
11
+ boolen = super()._erase_crossline()
12
+ if boolen:
13
+ return boolen
14
+
15
+ seg = self.collection_slider_vline.get_segments()
16
+ if seg:
17
+ self.collection_slider_vline.set_segments([])
18
+ return True
19
+ return False
20
+
21
+ def _on_move_action(self, e):
22
+ super()._on_move_action(e)
23
+
24
+ if self.in_slider:
25
+ self._restore_region(self.is_click_slider)
26
+ self._on_move_slider(e)
27
+ self.figure.canvas.blit()
28
+ elif self.in_price_chart or self.in_volume_chart:
29
+ self._restore_region()
30
+ self._draw_crossline(e)
31
+ self.figure.canvas.blit()
32
+ else:
33
+ if self._erase_crossline():
34
+ self._restore_region()
35
+ self.figure.canvas.blit()
36
+ return
37
+
38
+ def _on_move_action(self, e: MouseEvent):
39
+ self._check_ax(e)
40
+
41
+ self.intx = None
42
+ if self.in_slider or self.in_price_chart or self.in_volume_chart:
43
+ self._get_x(e)
44
+
45
+ self._set_cursor(e)
46
+ return
47
+
48
+ def _set_cursor(self, e: MouseEvent):
49
+ # 마우스 커서 변경
50
+ if self.is_click_slider:
51
+ return
52
+ elif not self.in_slider:
53
+ self.figure.canvas.set_cursor(cursors.POINTER)
54
+ return
55
+
56
+ navleft, navright = self.navcoordinate
57
+ if navleft == navright:
58
+ return
59
+
60
+ x = e.xdata.__round__()
61
+
62
+ leftmin = navleft - self._navLineWidth
63
+ leftmax = navleft + self._navLineWidth_half
64
+ rightmin = navright - self._navLineWidth_half
65
+ rightmax = navright + self._navLineWidth
66
+ if x < leftmin:
67
+ self.figure.canvas.set_cursor(cursors.POINTER)
68
+ elif x <= leftmax:
69
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
70
+ elif x < rightmin:
71
+ self.figure.canvas.set_cursor(cursors.MOVE)
72
+ elif x <= rightmax:
73
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
74
+ else:
75
+ self.figure.canvas.set_cursor(cursors.POINTER)
76
+ return
77
+
78
+ def _check_ax(self, e: MouseEvent):
79
+ ax = e.inaxes
80
+ if not ax or e.xdata is None or e.ydata is None:
81
+ self.in_slider, self.in_price_chart, self.in_volume_chart = (False, False, False)
82
+ else:
83
+ if ax is self.ax_slider:
84
+ self.in_slider = True
85
+ self.in_price_chart = False
86
+ self.in_volume_chart = False
87
+ elif ax is self.ax_price:
88
+ self.in_slider = False
89
+ self.in_price_chart = True
90
+ self.in_volume_chart = False
91
+ elif ax is self.ax_volume:
92
+ self.in_slider = False
93
+ self.in_price_chart = False
94
+ self.in_volume_chart = True
95
+ else:
96
+ self.in_slider = False
97
+ self.in_price_chart = False
98
+ self.in_volume_chart = False
99
+ return
100
+
101
+ def _on_move_slider(self, e: MouseEvent):
102
+ x = e.xdata
103
+ if self.intx is not None:
104
+ renderer = self.figure.canvas.renderer
105
+ seg = [((x, self.slider_ymin), (x, self.slider_ymax))]
106
+ self.collection_slider_vline.set_segments(seg)
107
+ self.collection_slider_vline.draw(renderer)
108
+
109
+ if self.in_slider:
110
+ self.artist_text_slider.set_text(f'{self.df["date"][self.intx]}')
111
+ self.artist_text_slider.set_x(x)
112
+ self.artist_text_slider.draw(renderer)
113
+ return
114
+
115
+ def _draw_crossline(self, e: MouseEvent):
116
+ x = e.xdata
117
+ self.collection_slider_vline.set_segments([((x-0.5, self.slider_ymin), (x-0.5, self.slider_ymax))])
118
+ self.collection_slider_vline.draw(self.figure.canvas.renderer)
119
+ return super()._draw_crossline(e)
120
+
121
+
122
+ class ClickMixin(MouseMoveMixin):
123
+ x_click = None
124
+ is_move = False
125
+ click_navleft, click_navright = (False, False)
126
+
127
+ def _connect_events(self):
128
+ super()._connect_events()
129
+
130
+ self.figure.canvas.mpl_connect('button_press_event', lambda x: self.on_click(x))
131
+ return
132
+
133
+ def on_click(self, e: MouseEvent):
134
+ self._on_click(e)
135
+ return
136
+
137
+ def _on_click(self, e: MouseEvent):
138
+ if self.in_slider and not self.is_click_slider:
139
+ if e.button == MouseButton.LEFT:
140
+ self._on_click_slider(e)
141
+ return
142
+
143
+ def _on_click_slider(self, e: MouseEvent):
144
+ self.background_with_nav_pre = self.background_with_nav
145
+
146
+ self.is_click_slider = True
147
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
148
+
149
+ navleft, navright = self.navcoordinate
150
+ x = e.xdata.__round__()
151
+
152
+ leftmax = navleft + self._navLineWidth_half
153
+ rightmin = navright - self._navLineWidth_half
154
+
155
+ grater_than_left = leftmax < x
156
+ less_then_right = x < rightmin
157
+ if grater_than_left and less_then_right:
158
+ self.is_move = True
159
+ self.x_click = x
160
+ else:
161
+ leftmin = navleft - self._navLineWidth
162
+ rightmax = navright + self._navLineWidth
163
+ if not grater_than_left and leftmin <= x:
164
+ self.click_navleft = True
165
+ self.x_click = navright
166
+ elif not less_then_right and x <= rightmax:
167
+ self.click_navright = True
168
+ self.x_click = navleft
169
+ else:
170
+ self.x_click = x
171
+ return
172
+
173
+
174
+ class BaseMixin(ClickMixin):
175
+ pass
176
+
@@ -0,0 +1,204 @@
1
+ from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
2
+
3
+ from ._mouse import BaseMixin as Base
4
+
5
+
6
+ class SliderSelectMixin(Base):
7
+ limit_ma = 8_000
8
+
9
+ def _on_move_slider(self, e):
10
+ if self.is_click_slider:
11
+ self._set_navcoordinate(e)
12
+ return super()._on_move_slider(e)
13
+
14
+ def _set_navcoordinate(self, e: MouseEvent):
15
+ navmin, navmax = self.navcoordinate
16
+
17
+ x = e.xdata.__int__()
18
+ if self.is_move:
19
+ xsub = self.x_click - x
20
+ navmin, navmax = (navmin-xsub, navmax-xsub)
21
+
22
+ # 값 보정
23
+ if navmax < 0:
24
+ navmin, navmax = (navmin-navmax, 0)
25
+ if self.index_list[-1] < navmin:
26
+ navmin, navmax = (self.index_list[-1], self.index_list[-1] + (navmax-navmin))
27
+
28
+ self.navcoordinate = (navmin, navmax)
29
+ self.x_click = x
30
+
31
+ self.axis(navmin, xmax=navmax+1, simpler=True, draw_ma=(navmax-navmin < self.limit_ma))
32
+
33
+ self._axis_navigator(navmin, navmax)
34
+ self.collection_navigator.draw(self.figure.canvas.renderer)
35
+
36
+ self.draw_artists()
37
+ self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
38
+ self._restore_region()
39
+ else:
40
+ navmin, navmax = (x, self.x_click) if x < self.x_click else (self.x_click, x)
41
+
42
+ # 슬라이더가 차트를 벗어나지 않도록 선택 영역 제한
43
+ if navmax < 0 or self.index_list[-1] < navmin:
44
+ seg = self.collection_navigator.get_segments()
45
+ navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
46
+
47
+ nsub = navmax - navmin
48
+ min_distance = 5 if not self.min_distance or self.min_distance < 5 else self.min_distance
49
+ if nsub < min_distance:
50
+ self._restore_region(False, False)
51
+ self._axis_navigator(navmin, navmax)
52
+ self.collection_navigator.draw(self.figure.canvas.renderer)
53
+ else:
54
+ self.axis(navmin, xmax=navmax+1, simpler=True, draw_ma=(nsub < self.limit_ma))
55
+ self._axis_navigator(navmin, navmax)
56
+
57
+ self.collection_navigator.draw(self.figure.canvas.renderer)
58
+
59
+ self.draw_artists()
60
+ self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
61
+ self._restore_region(False, True)
62
+ return
63
+
64
+
65
+ class ReleaseMixin(SliderSelectMixin):
66
+ def _connect_events(self):
67
+ super()._connect_events()
68
+
69
+ self.figure.canvas.mpl_connect('button_release_event', lambda x: self.on_release(x))
70
+ return
71
+
72
+ def on_release(self, e: MouseEvent):
73
+ self._on_release(e)
74
+ return
75
+
76
+ def _on_release(self, e: MouseEvent):
77
+ if self.in_slider and self.is_click_slider:
78
+ if e.button == MouseButton.LEFT:
79
+ self._on_release_slider(e)
80
+ self.axis(self.vxmin, xmax=self.vxmax, simpler=False, draw_ma=True)
81
+ self.figure.canvas.draw()
82
+ return
83
+
84
+ def _on_release_slider(self, e: MouseEvent):
85
+ if not self.is_move:
86
+ seg = self.collection_navigator.get_segments()
87
+ navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
88
+ nsub = navmax - navmin
89
+ min_distance = 5 if not self.min_distance or self.min_distance < 5 else self.min_distance
90
+ if min_distance <= nsub:
91
+ self.navcoordinate = (navmin, navmax)
92
+ else:
93
+ self.background_with_nav = self.background_with_nav_pre
94
+ navmin, navmax = self.navcoordinate
95
+ self.axis(navmin, xmax=navmax+1, simpler=False, draw_ma=True)
96
+ self._restore_region(False, True)
97
+ self.figure.canvas.blit()
98
+ self._axis_navigator(*self.navcoordinate)
99
+
100
+ self.is_click_slider = False
101
+ self.is_move = False
102
+ self.click_navleft, self.click_navright = (False, False)
103
+
104
+ # self.figure.canvas.draw()
105
+ return
106
+
107
+
108
+ class ChartClickMixin(ReleaseMixin):
109
+ is_click_chart = False
110
+
111
+ def _on_click(self, e: MouseEvent):
112
+ if e.button == MouseButton.LEFT:
113
+ if (
114
+ not self.is_click_chart
115
+ and (self.in_price_chart or self.in_volume_chart)
116
+ ):
117
+ self._on_click_chart(e)
118
+ elif not self.is_click_slider and self.in_slider:
119
+ self._on_click_slider(e)
120
+ return
121
+
122
+ def _on_click_chart(self, e: MouseEvent):
123
+ self.is_click_chart = True
124
+ x = e.xdata.__int__()
125
+ self.x_click = x - self.navcoordinate[0]
126
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
127
+ return
128
+
129
+ def _on_release(self, e):
130
+ if e.button == MouseButton.LEFT:
131
+ if (
132
+ self.is_click_chart
133
+ and (self.in_price_chart or self.in_volume_chart)
134
+ ):
135
+ self._on_release_chart(e)
136
+ self.axis(self.vxmin, xmax=self.vxmax, simpler=False, draw_ma=True)
137
+ self.figure.canvas.draw()
138
+ elif self.is_click_slider and self.in_slider:
139
+ self._on_release_slider(e)
140
+ self.axis(self.vxmin, xmax=self.vxmax, simpler=False, draw_ma=True)
141
+ self.figure.canvas.draw()
142
+ return
143
+
144
+ def _on_release_chart(self, e):
145
+ self.is_click_chart = False
146
+ self.figure.canvas.set_cursor(cursors.POINTER)
147
+ return
148
+
149
+ def _set_cursor(self, e):
150
+ if self.is_click_chart:
151
+ return
152
+ return super()._set_cursor(e)
153
+
154
+ def _on_move_action(self, e):
155
+ super()._on_move_action(e)
156
+
157
+ if self.in_slider:
158
+ self._restore_region(self.is_click_slider)
159
+ self._on_move_slider(e)
160
+ self.figure.canvas.blit()
161
+ elif self.in_price_chart or self.in_volume_chart:
162
+ self._restore_region(self.is_click_chart)
163
+ if not self.is_click_chart:
164
+ self._draw_crossline(e)
165
+ else:
166
+ self._move_chart(e)
167
+ self.figure.canvas.blit()
168
+ else:
169
+ if self._erase_crossline():
170
+ self._restore_region()
171
+ self.figure.canvas.blit()
172
+ return
173
+
174
+ def _move_chart(self, e: MouseEvent):
175
+ left, right = self.navcoordinate
176
+ x = e.xdata.__int__() - left
177
+ xsub = x - self.x_click
178
+ if not xsub:
179
+ self.collection_navigator.draw(self.figure.canvas.renderer)
180
+ self.draw_artists()
181
+ else:
182
+ left, right = (left-xsub, right-xsub)
183
+ if right < 0 or self.df.index[-1] < left:
184
+ self._restore_region()
185
+ else:
186
+ self.x_click = x
187
+ self.navcoordinate = (left, right)
188
+ self.axis(left, xmax=right+1, simpler=True, draw_ma=(right-left < self.limit_ma))
189
+ self._axis_navigator(left, right)
190
+ self.collection_navigator.draw(self.figure.canvas.renderer)
191
+
192
+ self.draw_artists()
193
+ self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
194
+ self._restore_region()
195
+ return
196
+
197
+
198
+ class BaseMixin(ChartClickMixin):
199
+ pass
200
+
201
+
202
+ class Chart(BaseMixin):
203
+ pass
204
+
@@ -0,0 +1,111 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.collections import LineCollection
3
+ from matplotlib.text import Text
4
+ from matplotlib.axes import Axes
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ from ..._config import DEFAULTCONFIG, ConfigData
9
+
10
+ from .a_canvas import CanvasMixin, Figure
11
+ from .b_artist import ArtistMixin
12
+ from .c_draw import DrawMixin
13
+ from .d_segment import SegmentMixin
14
+ from .e_axis import AxisMixin
15
+ from .f_background import BackgroundMixin
16
+ from .g_event import EventMixin
17
+ from .h_data import DataMixin
18
+
19
+
20
+ class Chart(
21
+ CanvasMixin,
22
+ ArtistMixin,
23
+ DrawMixin,
24
+ SegmentMixin,
25
+ AxisMixin,
26
+ BackgroundMixin,
27
+ EventMixin,
28
+ DataMixin,
29
+ ):
30
+ limit_candle = 400
31
+ limit_wick = 2_000
32
+ candle_on_ma = True
33
+
34
+ key_date = 'date'
35
+ key_open, key_high, key_low, key_close = ('open', 'high', 'low', 'close')
36
+ key_volume = 'volume'
37
+
38
+ index_list: list[int] = []
39
+
40
+ df: pd.DataFrame
41
+
42
+ CONFIG: ConfigData
43
+
44
+ figure: Figure
45
+ ax_legend: Axes
46
+ ax_price: Axes
47
+ ax_volume: Axes
48
+
49
+ artist_watermark: Text
50
+ collection_candle: LineCollection
51
+ collection_volume: LineCollection
52
+ collection_ma: LineCollection
53
+
54
+ ###
55
+
56
+ segment_volume: np.ndarray
57
+ segment_volume_wick: np.ndarray
58
+ facecolor_volume: np.ndarray
59
+ edgecolor_volume: np.ndarray
60
+
61
+ segment_candle: np.ndarray
62
+ segment_candle_wick: np.ndarray
63
+ segment_priceline: np.ndarray
64
+ facecolor_candle: np.ndarray
65
+ edgecolor_candle: np.ndarray
66
+
67
+ segment_ma: np.ndarray
68
+ edgecolor_ma: np.ndarray
69
+
70
+ price_ymin: int
71
+ price_ymax: int
72
+ volume_ymax: int
73
+
74
+ chart_price_ymax: float
75
+ chart_volume_ymax: float
76
+
77
+ vxmin: int
78
+ vxmax: int
79
+
80
+ ###
81
+
82
+ _visible_ma: set[int] = set()
83
+
84
+ _background = None
85
+ _background_background = None
86
+ _creating_background = False
87
+
88
+ def __init__(self, config=DEFAULTCONFIG):
89
+ self.CONFIG = config
90
+ super().__init__()
91
+
92
+ self.add_artists()
93
+ self.connect_events()
94
+ return
95
+
96
+ def refresh(self):
97
+ self.set_artists()
98
+
99
+ self.set_candle_segments()
100
+ self.set_color_segments()
101
+ ma_alpha = self.collection_ma.get_alpha()
102
+ # print(f'{ma_alpha=}')
103
+ if ma_alpha is not None:
104
+ self.collection_ma.set_alpha([1 for _ in ma_alpha])
105
+
106
+ self.axis(self.vxmin, xmax=self.vxmax-1)
107
+
108
+ self.set_canvas()
109
+ self.figure.canvas.draw()
110
+ return
111
+