├── astar
├── .properties.json
├── .DS_Store
├── index.html
├── astar.py
└── target
│ └── target_sketch.js
├── your_obstacle.json
├── .DS_Store
├── images
├── res1.png
└── astar.gif
├── .gitignore
├── README.md
└── AStar.py
/astar/.properties.json:
--------------------------------------------------------------------------------
1 | {"interpreter": "pyodide"}
--------------------------------------------------------------------------------
/your_obstacle.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": "[[1,1], [2,2], [0,2], [3,0], [3,1]]"
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ademakdogan/Implementation-of-A-Algorithm-Visualization-via-Pyp5js-/HEAD/.DS_Store
--------------------------------------------------------------------------------
/astar/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ademakdogan/Implementation-of-A-Algorithm-Visualization-via-Pyp5js-/HEAD/astar/.DS_Store
--------------------------------------------------------------------------------
/images/res1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ademakdogan/Implementation-of-A-Algorithm-Visualization-via-Pyp5js-/HEAD/images/res1.png
--------------------------------------------------------------------------------
/images/astar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ademakdogan/Implementation-of-A-Algorithm-Visualization-via-Pyp5js-/HEAD/images/astar.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | **/.DS_Store
3 | src/__pycache__
4 | __pycache__/
5 | src/.mypy_cache
6 | src/.ipynb_checkpoints
7 | .mypy_cache
8 | src/.mypy_cache
--------------------------------------------------------------------------------
/astar/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | astar - pyp5js (using pyodide)
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Python Implementation of A* Algorithm
2 | A* is a graph traversal and path search algorithm, which is often used in many fields of computer science due to its completeness, optimality, and optimal efficiency. One major practical drawback is its O(b^d) space complexity, as it stores all generated nodes in memory. Thus, in practical travel-routing systems, it is generally outperformed by algorithms which can pre-process the graph to attain better performance, as well as memory-bounded approaches; however, A* is still the best solution in many cases.
3 |
4 |
5 | --------------
6 |
7 | ### Pyp5js for Visualition
8 | This project has been created for A* algorithm implementation in Python 3. [Wikipedia](https://en.wikipedia.org/wiki/A*_search_algorithm) is used as the Pseudocode source. The repository consists of two main parts. The first part is about visualization. Pyp5js is used to visualize the algorithm. You can find the repository [here](https://github.com/berinhard/pyp5js). In this section, obstacles are created randomly. The algorithm finds the best path according to the obstacles that occur differently at each step. Some examples can be seen below.
9 |
10 |
11 | 
12 |
13 | --------------
14 |
15 | ### Define all parameters
16 |
17 | In the second one, the user can run the [AStar.py](AStar.py) file with the parameters that he/she has determined. The details of the parameters to be entered are shown below.
18 |
19 |
20 | arguments:
21 |
22 | * -h, --help show this help message and exit
23 | * -c, --cols, Total number of columns to be entered
24 | * -r, --rows, Total number of rows to be entered
25 | * -s, --start_x, X coordinate of the start point
26 | * -q, --start_y, Y coordinate of the start point
27 | * -e, --end_x, X coordinate of the end point
28 |
29 | * -t, --end_y, Y coordinate of the end point
30 |
31 | * -o, --obstacle_ratio, ratio of obstacle (black list)
32 | * -l , --obstacle_list,
33 | You can also create own obstacle. It should be list of
34 | list --> [[0,1],[3,2]] add in your [your_obstacle.json](your_obstacle.json) file
35 |
36 |
37 | Example usage:
38 |
39 | ```
40 | python AStar.py -c 25 -r 25 -s 1 -q 3 -e 23 -t 21 -l True
41 | ```
42 | Result:
43 | ```
44 | The way found!!!
45 | 23 20
46 | 23 19
47 | 23 18
48 | 23 17
49 | 23 16
50 | 23 15
51 | 23 14
52 | 23 13
53 | 23 12
54 | 23 11
55 | 23 10
56 | 23 9
57 | 23 8
58 | 23 7
59 | 23 6
60 | 23 5
61 | 23 4
62 | 23 3
63 | 22 3
64 | 21 3
65 | 20 3
66 | 19 3
67 | 18 3
68 | 17 3
69 | 16 3
70 | 15 3
71 | 14 3
72 | 13 3
73 | 12 3
74 | 11 3
75 | 10 3
76 | 9 3
77 | 8 3
78 | 7 3
79 | 6 3
80 | 5 3
81 | 4 3
82 | 3 3
83 | 2 3
84 | 1 3
85 |
86 | ```
87 |
88 |
89 | --------------
90 |
91 |
92 |
--------------------------------------------------------------------------------
/AStar.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | @author: A.Akdogan
5 | """
6 | from random import randint
7 | import argparse
8 |
9 |
10 | class Node:
11 |
12 | def __init__(self, x, y):
13 |
14 | self.x = x
15 | self.y = y
16 | self.f = 0
17 | self.g = 0
18 | self.h = 0
19 | self.neighbors = []
20 | self.previous = None
21 | self. obstacle = False
22 |
23 | def add_neighbors(self,grid, columns, rows):
24 |
25 | neighbor_x = self.x
26 | neighbor_y = self.y
27 |
28 | if neighbor_x < columns - 1:
29 | self.neighbors.append(grid[neighbor_x+1][neighbor_y])
30 | if neighbor_x > 0:
31 | self.neighbors.append(grid[neighbor_x-1][neighbor_y])
32 | if neighbor_y < rows -1:
33 | self.neighbors.append(grid[neighbor_x][neighbor_y +1])
34 | if neighbor_y > 0:
35 | self.neighbors.append(grid[neighbor_x][neighbor_y-1])
36 | #diagonals
37 | """ if neighbor_x > 0 and neighbor_y > 0:
38 | self.neighbors.append(grid[neighbor_x-1][neighbor_y-1])
39 | if neighbor_x < columns -1 and neighbor_y > 0:
40 | self.neighbors.append(grid[neighbor_x+1][neighbor_y-1])
41 | if neighbor_x > 0 and neighbor_y 0:
174 | open_set, closed_set, current_node, final_path = AStar.start_path(open_set, closed_set, current_node, self.end)
175 | if len(final_path) > 0:
176 | break
177 |
178 | return final_path
179 |
180 |
181 | if __name__ == "__main__":
182 |
183 | ap = argparse.ArgumentParser()
184 | ap.add_argument("-c", "--cols", required=True)
185 | ap.add_argument("-r", "--rows", required=True)
186 | ap.add_argument("-s", "--start_x", required=True, help = "x coor of start point")
187 | ap.add_argument("-q", "--start_y", required=True, help = "y coor of start point")
188 | ap.add_argument("-e", "--end_x", required=True, help = "x coor of end point")
189 | ap.add_argument("-t", "--end_y", required=True, help = "y coor of end point")
190 | ap.add_argument("-o", "--obstacle_ratio", required=False, help = "ratio of obstacle (black list)", default = 20)
191 | ap.add_argument("-l", "--obstacle_list", required=False, help = "You can also create own obstacle. It should be list of list --> [[0,1],[3,2]] add in your obstacle.json file", default = False, type = str)
192 |
193 | args = vars(ap.parse_args())
194 | if args["obstacle_list"] == "True":
195 | #print(args["obstacle_list"])
196 | import json
197 | json_file_path = "your_obstacle.json"
198 | with open(json_file_path, 'r') as j:
199 | contents = json.loads(j.read())
200 | data = json.loads(contents["data"])
201 | a_star = AStar(int(args["cols"]), int(args["rows"]), [int(args["start_x"]), int(args["start_y"])], [int(args["end_x"]), int(args["end_y"])], False,data)
202 | else:
203 | a_star = AStar(int(args["cols"]), int(args["rows"]), [int(args["start_x"]), int(args["start_y"])], [int(args["end_x"]), int(args["end_y"])], int(args["obstacle_ratio"]), False)
204 |
205 | final_path = a_star.main()
206 | if len(final_path) > 0:
207 | print("The way found!!!")
208 | for i in range(len(final_path)):
209 | print(final_path[i].x, final_path[i].y)
210 | else:
211 | print("There is no legal way...You can decrease obstacle ration (default 20)")
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/astar/astar.py:
--------------------------------------------------------------------------------
1 | from random import randint
2 | import os
3 | import sys
4 | import time
5 | from time import sleep
6 | print(sys.path)
7 |
8 | class Node:
9 |
10 | def __init__(self, x, y):
11 |
12 | self.x = x
13 | self.y = y
14 | self.f = 0
15 | self.g = 0
16 | self.h = 0
17 | self.neighbors = []
18 | self.previous = None
19 | self. obstacle = False
20 |
21 |
22 | def add_neighbors(self,grid, columns, rows):
23 |
24 | neighbor_x = self.x
25 | neighbor_y = self.y
26 |
27 | if neighbor_x < columns - 1:
28 | self.neighbors.append(grid[neighbor_x+1][neighbor_y])
29 | if neighbor_x > 0:
30 | self.neighbors.append(grid[neighbor_x-1][neighbor_y])
31 | if neighbor_y < rows -1:
32 | self.neighbors.append(grid[neighbor_x][neighbor_y +1])
33 | if neighbor_y > 0:
34 | self.neighbors.append(grid[neighbor_x][neighbor_y-1])
35 | #diagonals
36 | """ if neighbor_x > 0 and neighbor_y > 0:
37 | self.neighbors.append(grid[neighbor_x-1][neighbor_y-1])
38 | if neighbor_x < columns -1 and neighbor_y > 0:
39 | self.neighbors.append(grid[neighbor_x+1][neighbor_y-1])
40 | if neighbor_x > 0 and neighbor_y 0:
170 | open_set, closed_set, current_node, final_path = AStar.start_path(open_set, closed_set, current_node, self.end)
171 | if len(final_path) > 0:
172 | break
173 |
174 | return final_path
175 |
176 |
177 | cols = 25
178 | rows = 25
179 | start = [0,0]
180 | end = [24,24]
181 | open_set = []
182 | closed_set = []
183 | current_node = None
184 | final_path = []
185 | grid = []
186 |
187 |
188 | def show_func(grid_element,color, width,height):
189 | if grid_element.obstacle == True:
190 | fill("black")
191 | else:
192 | fill(color)
193 | noStroke()
194 | rect(grid_element.x * width, grid_element.y * height, width-1 , height-1)
195 |
196 |
197 | def setup():
198 | global grid
199 | createCanvas(500, 500)
200 | background(160)
201 |
202 |
203 |
204 | flag = False
205 |
206 |
207 | def draw():
208 |
209 | global grid
210 | global end
211 | global open_set
212 | global closed_set
213 | global final_path
214 | global current_node
215 | global flag
216 | global start
217 |
218 | global cols
219 | global rows
220 |
221 | frameRate(60)
222 | w = width / cols
223 | h = height / rows
224 | if flag == False:
225 |
226 |
227 |
228 | grid = AStar.create_grid(cols, rows)
229 | grid = AStar.fill_grids(grid, cols, rows, obstacle_ratio = 30)
230 | grid = AStar.get_neighbors(grid, cols, rows)
231 | start = grid[start[0]][start[1]]
232 | end = grid[end[0]][end[1]]
233 | end.obstacle = False
234 | start.obstacle = False
235 |
236 | background(0)
237 | for i in range(cols):
238 | for j in range(rows):
239 | show_func(grid[i][j], color(255),w,h)
240 | stroke(0,0,0)
241 | line(0, 0, 0, width)
242 | line(0,0,height, 1)
243 | open_set.append(start)
244 |
245 |
246 | flag = True
247 |
248 | if len(open_set) > 0:
249 | open_set, closed_set, current_node, final_path = AStar.start_path(open_set, closed_set, current_node, end)
250 |
251 |
252 | #grid
253 | show_func(start, "green", w,h)
254 | show_func(end, "red",w,h)
255 | for i in range(len(open_set)):
256 | #show_func(open_set[i], "#00ffbf",w,h)
257 | show_func(open_set[i], "#00ffbf",w,h)
258 |
259 | for i in range(len(closed_set)):
260 | show_func(closed_set[i], "#ffa500",w,h)
261 | show_func(start, "green", w,h)
262 |
263 | show_func(current_node, "#8a2be2",w,h)
264 |
265 | if len(open_set) == 0:
266 | print("No way!")
267 | #noLoop()
268 |
269 | frameRate(1)
270 | cols = 25
271 | rows = 25
272 | start = [0,0]
273 | end = [24,24]
274 | open_set = []
275 | closed_set = []
276 | current_node = None
277 | final_path = []
278 | grid = []
279 | flag = False
280 |
281 |
282 | if len(final_path) > 0:
283 | for i in range(len(final_path)):
284 | show_func(final_path[i], "#8a2be2",w,h)
285 | #show_func(final_path[i], "red",w,h)
286 | show_func(start, "green", w,h)
287 | show_func(end, "red",w,h)
288 |
289 | print("Done!!")
290 | frameRate(1)
291 | cols = 25
292 | rows = 25
293 | start = [0,0]
294 | end = [24,24]
295 | open_set = []
296 | closed_set = []
297 | current_node = None
298 | final_path = []
299 | grid = []
300 | flag = False
301 |
302 |
303 |
304 |
--------------------------------------------------------------------------------
/astar/target/target_sketch.js:
--------------------------------------------------------------------------------
1 | let wrapper_content = `
2 | class PythonFunctions: pass
3 |
4 | setattr(PythonFunctions, 'map', map)
5 | setattr(PythonFunctions, 'filter', filter)
6 | setattr(PythonFunctions, 'set', set)
7 |
8 |
9 | _P5_INSTANCE = None
10 |
11 | _CTX_MIDDLE = None
12 | _DEFAULT_FILL = None
13 | _DEFAULT_LEADMULT = None
14 | _DEFAULT_STROKE = None
15 | _DEFAULT_TEXT_FILL = None
16 |
17 | ADD = None
18 | ALT = None
19 | ARROW = None
20 | AUDIO = None
21 | AUTO = None
22 | AXES = None
23 | BACKSPACE = None
24 | BASELINE = None
25 | BEVEL = None
26 | BEZIER = None
27 | BLEND = None
28 | BLUR = None
29 | BOLD = None
30 | BOLDITALIC = None
31 | BOTTOM = None
32 | BURN = None
33 | CENTER = None
34 | CHORD = None
35 | CLAMP = None
36 | CLOSE = None
37 | CONTROL = None
38 | CORNER = None
39 | CORNERS = None
40 | CROSS = None
41 | CURVE = None
42 | DARKEST = None
43 | DEG_TO_RAD = None
44 | DEGREES = None
45 | DELETE = None
46 | DIFFERENCE = None
47 | DILATE = None
48 | DODGE = None
49 | DOWN_ARROW = None
50 | ENTER = None
51 | ERODE = None
52 | ESCAPE = None
53 | EXCLUSION = None
54 | FILL = None
55 | GRAY = None
56 | GRID = None
57 | HALF_PI = None
58 | HAND = None
59 | HARD_LIGHT = None
60 | HSB = None
61 | HSL = None
62 | IMAGE = None
63 | IMMEDIATE = None
64 | INVERT = None
65 | ITALIC = None
66 | LANDSCAPE = None
67 | LEFT = None
68 | LEFT_ARROW = None
69 | LIGHTEST = None
70 | LINE_LOOP = None
71 | LINE_STRIP = None
72 | LINEAR = None
73 | LINES = None
74 | MIRROR = None
75 | MITER = None
76 | MOVE = None
77 | MULTIPLY = None
78 | NEAREST = None
79 | NORMAL = None
80 | OPAQUE = None
81 | OPEN = None
82 | OPTION = None
83 | OVERLAY = None
84 | PI = None
85 | PIE = None
86 | POINTS = None
87 | PORTRAIT = None
88 | POSTERIZE = None
89 | PROJECT = None
90 | QUAD_STRIP = None
91 | QUADRATIC = None
92 | QUADS = None
93 | QUARTER_PI = None
94 | RAD_TO_DEG = None
95 | RADIANS = None
96 | RADIUS = None
97 | REPEAT = None
98 | REPLACE = None
99 | RETURN = None
100 | RGB = None
101 | RIGHT = None
102 | RIGHT_ARROW = None
103 | ROUND = None
104 | SCREEN = None
105 | SHIFT = None
106 | SOFT_LIGHT = None
107 | SQUARE = None
108 | STROKE = None
109 | SUBTRACT = None
110 | TAB = None
111 | TAU = None
112 | TEXT = None
113 | TEXTURE = None
114 | THRESHOLD = None
115 | TOP = None
116 | TRIANGLE_FAN = None
117 | TRIANGLE_STRIP = None
118 | TRIANGLES = None
119 | TWO_PI = None
120 | UP_ARROW = None
121 | VIDEO = None
122 | WAIT = None
123 | WEBGL = None
124 | P2D = None
125 | PI = None
126 |
127 | frameCount = None
128 | focused = None
129 | displayWidth = None
130 | displayHeight = None
131 | windowWidth = None
132 | windowHeight = None
133 | width = None
134 | height = None
135 | deviceOrientation = None
136 | accelerationX = None
137 | accelerationY = None
138 | accelerationZ = None
139 | pAccelerationX = None
140 | pAccelerationY = None
141 | pAccelerationZ = None
142 | rotationX = None
143 | rotationY = None
144 | rotationZ = None
145 | pRotationX = None
146 | pRotationY = None
147 | pRotationZ = None
148 | turnAxis = None
149 | keyIsPressed = None
150 | key = None
151 | keyCode = None
152 | mouseX = None
153 | mouseY = None
154 | pmouseX = None
155 | pmouseY = None
156 | winMouseX = None
157 | winMouseY = None
158 | pwinMouseX = None
159 | pwinMouseY = None
160 | mouseButton = None
161 | mouseIsPressed = None
162 | touches = None
163 | pixels = None
164 |
165 |
166 | def alpha(*args):
167 | return _P5_INSTANCE.alpha(*args)
168 |
169 | def blue(*args):
170 | return _P5_INSTANCE.blue(*args)
171 |
172 | def brightness(*args):
173 | return _P5_INSTANCE.brightness(*args)
174 |
175 | def color(*args):
176 | return _P5_INSTANCE.color(*args)
177 |
178 | def green(*args):
179 | return _P5_INSTANCE.green(*args)
180 |
181 | def hue(*args):
182 | return _P5_INSTANCE.hue(*args)
183 |
184 | def lerpColor(*args):
185 | return _P5_INSTANCE.lerpColor(*args)
186 |
187 | def lightness(*args):
188 | return _P5_INSTANCE.lightness(*args)
189 |
190 | def red(*args):
191 | return _P5_INSTANCE.red(*args)
192 |
193 | def saturation(*args):
194 | return _P5_INSTANCE.saturation(*args)
195 |
196 | def background(*args):
197 | return _P5_INSTANCE.background(*args)
198 |
199 | def clear(*args):
200 | p5_clear = _P5_INSTANCE.clear(*args)
201 | return p5_clear
202 |
203 | def erase(*args):
204 | return _P5_INSTANCE.erase(*args)
205 |
206 | def noErase(*args):
207 | return _P5_INSTANCE.noErase(*args)
208 |
209 | def colorMode(*args):
210 | return _P5_INSTANCE.colorMode(*args)
211 |
212 | def fill(*args):
213 | return _P5_INSTANCE.fill(*args)
214 |
215 | def noFill(*args):
216 | return _P5_INSTANCE.noFill(*args)
217 |
218 | def noStroke(*args):
219 | return _P5_INSTANCE.noStroke(*args)
220 |
221 | def stroke(*args):
222 | return _P5_INSTANCE.stroke(*args)
223 |
224 | def arc(*args):
225 | return _P5_INSTANCE.arc(*args)
226 |
227 | def ellipse(*args):
228 | return _P5_INSTANCE.ellipse(*args)
229 |
230 | def circle(*args):
231 | return _P5_INSTANCE.circle(*args)
232 |
233 | def line(*args):
234 | return _P5_INSTANCE.line(*args)
235 |
236 | def point(*args):
237 | return _P5_INSTANCE.point(*args)
238 |
239 | def quad(*args):
240 | return _P5_INSTANCE.quad(*args)
241 |
242 | def rect(*args):
243 | return _P5_INSTANCE.rect(*args)
244 |
245 | def square(*args):
246 | return _P5_INSTANCE.square(*args)
247 |
248 | def triangle(*args):
249 | return _P5_INSTANCE.triangle(*args)
250 |
251 | def plane(*args):
252 | return _P5_INSTANCE.plane(*args)
253 |
254 | def box(*args):
255 | return _P5_INSTANCE.box(*args)
256 |
257 | def sphere(*args):
258 | return _P5_INSTANCE.sphere(*args)
259 |
260 | def cylinder(*args):
261 | return _P5_INSTANCE.cylinder(*args)
262 |
263 | def cone(*args):
264 | return _P5_INSTANCE.cone(*args)
265 |
266 | def ellipsoid(*args):
267 | return _P5_INSTANCE.ellipsoid(*args)
268 |
269 | def torus(*args):
270 | return _P5_INSTANCE.torus(*args)
271 |
272 | def loadModel(*args):
273 | return _P5_INSTANCE.loadModel(*args)
274 |
275 | def model(*args):
276 | return _P5_INSTANCE.model(*args)
277 |
278 | def ellipseMode(*args):
279 | return _P5_INSTANCE.ellipseMode(*args)
280 |
281 | def noSmooth(*args):
282 | return _P5_INSTANCE.noSmooth(*args)
283 |
284 | def rectMode(*args):
285 | return _P5_INSTANCE.rectMode(*args)
286 |
287 | def smooth(*args):
288 | return _P5_INSTANCE.smooth(*args)
289 |
290 | def strokeCap(*args):
291 | return _P5_INSTANCE.strokeCap(*args)
292 |
293 | def strokeJoin(*args):
294 | return _P5_INSTANCE.strokeJoin(*args)
295 |
296 | def strokeWeight(*args):
297 | return _P5_INSTANCE.strokeWeight(*args)
298 |
299 | def bezier(*args):
300 | return _P5_INSTANCE.bezier(*args)
301 |
302 | def bezierDetail(*args):
303 | return _P5_INSTANCE.bezierDetail(*args)
304 |
305 | def bezierPoint(*args):
306 | return _P5_INSTANCE.bezierPoint(*args)
307 |
308 | def bezierTangent(*args):
309 | return _P5_INSTANCE.bezierTangent(*args)
310 |
311 | def curve(*args):
312 | return _P5_INSTANCE.curve(*args)
313 |
314 | def curveDetail(*args):
315 | return _P5_INSTANCE.curveDetail(*args)
316 |
317 | def curveTightness(*args):
318 | return _P5_INSTANCE.curveTightness(*args)
319 |
320 | def curvePoint(*args):
321 | return _P5_INSTANCE.curvePoint(*args)
322 |
323 | def curveTangent(*args):
324 | return _P5_INSTANCE.curveTangent(*args)
325 |
326 | def beginContour(*args):
327 | return _P5_INSTANCE.beginContour(*args)
328 |
329 | def beginShape(*args):
330 | return _P5_INSTANCE.beginShape(*args)
331 |
332 | def bezierVertex(*args):
333 | return _P5_INSTANCE.bezierVertex(*args)
334 |
335 | def curveVertex(*args):
336 | return _P5_INSTANCE.curveVertex(*args)
337 |
338 | def endContour(*args):
339 | return _P5_INSTANCE.endContour(*args)
340 |
341 | def endShape(*args):
342 | return _P5_INSTANCE.endShape(*args)
343 |
344 | def quadraticVertex(*args):
345 | return _P5_INSTANCE.quadraticVertex(*args)
346 |
347 | def vertex(*args):
348 | return _P5_INSTANCE.vertex(*args)
349 |
350 | def cursor(*args):
351 | return _P5_INSTANCE.cursor(*args)
352 |
353 | def frameRate(*args):
354 | return _P5_INSTANCE.frameRate(*args)
355 |
356 | def noCursor(*args):
357 | return _P5_INSTANCE.noCursor(*args)
358 |
359 | def fullscreen(*args):
360 | return _P5_INSTANCE.fullscreen(*args)
361 |
362 | def pixelDensity(*args):
363 | return _P5_INSTANCE.pixelDensity(*args)
364 |
365 | def displayDensity(*args):
366 | return _P5_INSTANCE.displayDensity(*args)
367 |
368 | def getURL(*args):
369 | return _P5_INSTANCE.getURL(*args)
370 |
371 | def getURLPath(*args):
372 | return _P5_INSTANCE.getURLPath(*args)
373 |
374 | def getURLParams(*args):
375 | return _P5_INSTANCE.getURLParams(*args)
376 |
377 | def preload(*args):
378 | return _P5_INSTANCE.preload(*args)
379 |
380 | def remove(*args):
381 | return _P5_INSTANCE.remove(*args)
382 |
383 | def noLoop(*args):
384 | return _P5_INSTANCE.noLoop(*args)
385 |
386 | def loop(*args):
387 | return _P5_INSTANCE.loop(*args)
388 |
389 | def push(*args):
390 | return _P5_INSTANCE.push(*args)
391 |
392 | def redraw(*args):
393 | return _P5_INSTANCE.redraw(*args)
394 |
395 | def resizeCanvas(*args):
396 | return _P5_INSTANCE.resizeCanvas(*args)
397 |
398 | def noCanvas(*args):
399 | return _P5_INSTANCE.noCanvas(*args)
400 |
401 | def createGraphics(*args):
402 | return _P5_INSTANCE.createGraphics(*args)
403 |
404 | def blendMode(*args):
405 | return _P5_INSTANCE.blendMode(*args)
406 |
407 | def setAttributes(*args):
408 | return _P5_INSTANCE.setAttributes(*args)
409 |
410 | def applyMatrix(*args):
411 | return _P5_INSTANCE.applyMatrix(*args)
412 |
413 | def resetMatrix(*args):
414 | return _P5_INSTANCE.resetMatrix(*args)
415 |
416 | def rotate(*args):
417 | return _P5_INSTANCE.rotate(*args)
418 |
419 | def rotateX(*args):
420 | return _P5_INSTANCE.rotateX(*args)
421 |
422 | def rotateY(*args):
423 | return _P5_INSTANCE.rotateY(*args)
424 |
425 | def rotateZ(*args):
426 | return _P5_INSTANCE.rotateZ(*args)
427 |
428 | def scale(*args):
429 | return _P5_INSTANCE.scale(*args)
430 |
431 | def shearX(*args):
432 | return _P5_INSTANCE.shearX(*args)
433 |
434 | def shearY(*args):
435 | return _P5_INSTANCE.shearY(*args)
436 |
437 | def translate(*args):
438 | return _P5_INSTANCE.translate(*args)
439 |
440 | def createStringDict(*args):
441 | return _P5_INSTANCE.createStringDict(*args)
442 |
443 | def createNumberDict(*args):
444 | return _P5_INSTANCE.createNumberDict(*args)
445 |
446 | def append(*args):
447 | return _P5_INSTANCE.append(*args)
448 |
449 | def arrayCopy(*args):
450 | return _P5_INSTANCE.arrayCopy(*args)
451 |
452 | def concat(*args):
453 | return _P5_INSTANCE.concat(*args)
454 |
455 | def reverse(*args):
456 | return _P5_INSTANCE.reverse(*args)
457 |
458 | def shorten(*args):
459 | return _P5_INSTANCE.shorten(*args)
460 |
461 | def shuffle(*args):
462 | return _P5_INSTANCE.shuffle(*args)
463 |
464 | def sort(*args):
465 | return _P5_INSTANCE.sort(*args)
466 |
467 | def splice(*args):
468 | return _P5_INSTANCE.splice(*args)
469 |
470 | def subset(*args):
471 | return _P5_INSTANCE.subset(*args)
472 |
473 | def float(*args):
474 | return _P5_INSTANCE.float(*args)
475 |
476 | def int(*args):
477 | return _P5_INSTANCE.int(*args)
478 |
479 | def str(*args):
480 | return _P5_INSTANCE.str(*args)
481 |
482 | def boolean(*args):
483 | return _P5_INSTANCE.boolean(*args)
484 |
485 | def byte(*args):
486 | return _P5_INSTANCE.byte(*args)
487 |
488 | def char(*args):
489 | return _P5_INSTANCE.char(*args)
490 |
491 | def unchar(*args):
492 | return _P5_INSTANCE.unchar(*args)
493 |
494 | def hex(*args):
495 | return _P5_INSTANCE.hex(*args)
496 |
497 | def unhex(*args):
498 | return _P5_INSTANCE.unhex(*args)
499 |
500 | def join(*args):
501 | return _P5_INSTANCE.join(*args)
502 |
503 | def match(*args):
504 | return _P5_INSTANCE.match(*args)
505 |
506 | def matchAll(*args):
507 | return _P5_INSTANCE.matchAll(*args)
508 |
509 | def nf(*args):
510 | return _P5_INSTANCE.nf(*args)
511 |
512 | def nfc(*args):
513 | return _P5_INSTANCE.nfc(*args)
514 |
515 | def nfp(*args):
516 | return _P5_INSTANCE.nfp(*args)
517 |
518 | def nfs(*args):
519 | return _P5_INSTANCE.nfs(*args)
520 |
521 | def split(*args):
522 | return _P5_INSTANCE.split(*args)
523 |
524 | def splitTokens(*args):
525 | return _P5_INSTANCE.splitTokens(*args)
526 |
527 | def trim(*args):
528 | return _P5_INSTANCE.trim(*args)
529 |
530 | def setMoveThreshold(*args):
531 | return _P5_INSTANCE.setMoveThreshold(*args)
532 |
533 | def setShakeThreshold(*args):
534 | return _P5_INSTANCE.setShakeThreshold(*args)
535 |
536 | def keyIsDown(*args):
537 | return _P5_INSTANCE.keyIsDown(*args)
538 |
539 | def createImage(*args):
540 | return _P5_INSTANCE.createImage(*args)
541 |
542 | def saveCanvas(*args):
543 | return _P5_INSTANCE.saveCanvas(*args)
544 |
545 | def saveFrames(*args):
546 | return _P5_INSTANCE.saveFrames(*args)
547 |
548 | def loadImage(*args):
549 | return _P5_INSTANCE.loadImage(*args)
550 |
551 | def image(*args):
552 | return _P5_INSTANCE.image(*args)
553 |
554 | def tint(*args):
555 | return _P5_INSTANCE.tint(*args)
556 |
557 | def noTint(*args):
558 | return _P5_INSTANCE.noTint(*args)
559 |
560 | def imageMode(*args):
561 | return _P5_INSTANCE.imageMode(*args)
562 |
563 | def blend(*args):
564 | return _P5_INSTANCE.blend(*args)
565 |
566 | def copy(*args):
567 | return _P5_INSTANCE.copy(*args)
568 |
569 | def filter(*args):
570 | if len(args) > 1 and (args[0] is None or callable(args[0])):
571 | return PythonFunctions.filter(*args)
572 | else:
573 | return _P5_INSTANCE.filter(*args)
574 |
575 | def get(*args):
576 | return _P5_INSTANCE.get(*args)
577 |
578 | def loadPixels(*args):
579 | return _P5_INSTANCE.loadPixels(*args)
580 |
581 | def set(*args):
582 | if len(args) <= 1:
583 | return PythonFunctions.set(*args)
584 | else:
585 | return _P5_INSTANCE.set(*args)
586 |
587 | def updatePixels(*args):
588 | return _P5_INSTANCE.updatePixels(*args)
589 |
590 | def loadJSON(*args):
591 | return _P5_INSTANCE.loadJSON(*args)
592 |
593 | def loadStrings(*args):
594 | return _P5_INSTANCE.loadStrings(*args)
595 |
596 | def loadTable(*args):
597 | return _P5_INSTANCE.loadTable(*args)
598 |
599 | def loadXML(*args):
600 | return _P5_INSTANCE.loadXML(*args)
601 |
602 | def loadBytes(*args):
603 | return _P5_INSTANCE.loadBytes(*args)
604 |
605 | def httpGet(*args):
606 | return _P5_INSTANCE.httpGet(*args)
607 |
608 | def httpPost(*args):
609 | return _P5_INSTANCE.httpPost(*args)
610 |
611 | def httpDo(*args):
612 | return _P5_INSTANCE.httpDo(*args)
613 |
614 | def createWriter(*args):
615 | return _P5_INSTANCE.createWriter(*args)
616 |
617 | def save(*args):
618 | return _P5_INSTANCE.save(*args)
619 |
620 | def saveJSON(*args):
621 | return _P5_INSTANCE.saveJSON(*args)
622 |
623 | def saveStrings(*args):
624 | return _P5_INSTANCE.saveStrings(*args)
625 |
626 | def saveTable(*args):
627 | return _P5_INSTANCE.saveTable(*args)
628 |
629 | def day(*args):
630 | return _P5_INSTANCE.day(*args)
631 |
632 | def hour(*args):
633 | return _P5_INSTANCE.hour(*args)
634 |
635 | def minute(*args):
636 | return _P5_INSTANCE.minute(*args)
637 |
638 | def millis(*args):
639 | return _P5_INSTANCE.millis(*args)
640 |
641 | def month(*args):
642 | return _P5_INSTANCE.month(*args)
643 |
644 | def second(*args):
645 | return _P5_INSTANCE.second(*args)
646 |
647 | def year(*args):
648 | return _P5_INSTANCE.year(*args)
649 |
650 | def createVector(*args):
651 | return _P5_INSTANCE.createVector(*args)
652 |
653 | def abs(*args):
654 | return _P5_INSTANCE.abs(*args)
655 |
656 | def ceil(*args):
657 | return _P5_INSTANCE.ceil(*args)
658 |
659 | def constrain(*args):
660 | return _P5_INSTANCE.constrain(*args)
661 |
662 | def dist(*args):
663 | return _P5_INSTANCE.dist(*args)
664 |
665 | def exp(*args):
666 | return _P5_INSTANCE.exp(*args)
667 |
668 | def floor(*args):
669 | return _P5_INSTANCE.floor(*args)
670 |
671 | def lerp(*args):
672 | return _P5_INSTANCE.lerp(*args)
673 |
674 | def log(*args):
675 | return _P5_INSTANCE.log(*args)
676 |
677 | def mag(*args):
678 | return _P5_INSTANCE.mag(*args)
679 |
680 | def map(*args):
681 | if len(args) > 1 and callable(args[0]):
682 | return PythonFunctions.map(*args)
683 | else:
684 | return _P5_INSTANCE.map(*args)
685 |
686 | def max(*args):
687 | return _P5_INSTANCE.max(*args)
688 |
689 | def min(*args):
690 | return _P5_INSTANCE.min(*args)
691 |
692 | def norm(*args):
693 | return _P5_INSTANCE.norm(*args)
694 |
695 | def pow(*args):
696 | return _P5_INSTANCE.pow(*args)
697 |
698 | def round(*args):
699 | return _P5_INSTANCE.round(*args)
700 |
701 | def sq(*args):
702 | return _P5_INSTANCE.sq(*args)
703 |
704 | def sqrt(*args):
705 | return _P5_INSTANCE.sqrt(*args)
706 |
707 | def noise(*args):
708 | return _P5_INSTANCE.noise(*args)
709 |
710 | def noiseDetail(*args):
711 | return _P5_INSTANCE.noiseDetail(*args)
712 |
713 | def noiseSeed(*args):
714 | return _P5_INSTANCE.noiseSeed(*args)
715 |
716 | def randomSeed(*args):
717 | return _P5_INSTANCE.randomSeed(*args)
718 |
719 | def random(*args):
720 | return _P5_INSTANCE.random(*args)
721 |
722 | def randomGaussian(*args):
723 | return _P5_INSTANCE.randomGaussian(*args)
724 |
725 | def acos(*args):
726 | return _P5_INSTANCE.acos(*args)
727 |
728 | def asin(*args):
729 | return _P5_INSTANCE.asin(*args)
730 |
731 | def atan(*args):
732 | return _P5_INSTANCE.atan(*args)
733 |
734 | def atan2(*args):
735 | return _P5_INSTANCE.atan2(*args)
736 |
737 | def cos(*args):
738 | return _P5_INSTANCE.cos(*args)
739 |
740 | def sin(*args):
741 | return _P5_INSTANCE.sin(*args)
742 |
743 | def tan(*args):
744 | return _P5_INSTANCE.tan(*args)
745 |
746 | def degrees(*args):
747 | return _P5_INSTANCE.degrees(*args)
748 |
749 | def radians(*args):
750 | return _P5_INSTANCE.radians(*args)
751 |
752 | def angleMode(*args):
753 | return _P5_INSTANCE.angleMode(*args)
754 |
755 | def textAlign(*args):
756 | return _P5_INSTANCE.textAlign(*args)
757 |
758 | def textLeading(*args):
759 | return _P5_INSTANCE.textLeading(*args)
760 |
761 | def textSize(*args):
762 | return _P5_INSTANCE.textSize(*args)
763 |
764 | def textStyle(*args):
765 | return _P5_INSTANCE.textStyle(*args)
766 |
767 | def textWidth(*args):
768 | return _P5_INSTANCE.textWidth(*args)
769 |
770 | def textAscent(*args):
771 | return _P5_INSTANCE.textAscent(*args)
772 |
773 | def textDescent(*args):
774 | return _P5_INSTANCE.textDescent(*args)
775 |
776 | def loadFont(*args):
777 | return _P5_INSTANCE.loadFont(*args)
778 |
779 | def text(*args):
780 | return _P5_INSTANCE.text(*args)
781 |
782 | def textFont(*args):
783 | return _P5_INSTANCE.textFont(*args)
784 |
785 | def orbitControl(*args):
786 | return _P5_INSTANCE.orbitControl(*args)
787 |
788 | def debugMode(*args):
789 | return _P5_INSTANCE.debugMode(*args)
790 |
791 | def noDebugMode(*args):
792 | return _P5_INSTANCE.noDebugMode(*args)
793 |
794 | def ambientLight(*args):
795 | return _P5_INSTANCE.ambientLight(*args)
796 |
797 | def directionalLight(*args):
798 | return _P5_INSTANCE.directionalLight(*args)
799 |
800 | def pointLight(*args):
801 | return _P5_INSTANCE.pointLight(*args)
802 |
803 | def lights(*args):
804 | return _P5_INSTANCE.lights(*args)
805 |
806 | def loadShader(*args):
807 | return _P5_INSTANCE.loadShader(*args)
808 |
809 | def createShader(*args):
810 | return _P5_INSTANCE.createShader(*args)
811 |
812 | def shader(*args):
813 | return _P5_INSTANCE.shader(*args)
814 |
815 | def resetShader(*args):
816 | return _P5_INSTANCE.resetShader(*args)
817 |
818 | def normalMaterial(*args):
819 | return _P5_INSTANCE.normalMaterial(*args)
820 |
821 | def texture(*args):
822 | return _P5_INSTANCE.texture(*args)
823 |
824 | def textureMode(*args):
825 | return _P5_INSTANCE.textureMode(*args)
826 |
827 | def textureWrap(*args):
828 | return _P5_INSTANCE.textureWrap(*args)
829 |
830 | def ambientMaterial(*args):
831 | return _P5_INSTANCE.ambientMaterial(*args)
832 |
833 | def specularMaterial(*args):
834 | return _P5_INSTANCE.specularMaterial(*args)
835 |
836 | def shininess(*args):
837 | return _P5_INSTANCE.shininess(*args)
838 |
839 | def camera(*args):
840 | return _P5_INSTANCE.camera(*args)
841 |
842 | def perspective(*args):
843 | return _P5_INSTANCE.perspective(*args)
844 |
845 | def ortho(*args):
846 | return _P5_INSTANCE.ortho(*args)
847 |
848 | def createCamera(*args):
849 | return _P5_INSTANCE.createCamera(*args)
850 |
851 | def setCamera(*args):
852 | return _P5_INSTANCE.setCamera(*args)
853 |
854 | def select(*args):
855 | return _P5_INSTANCE.select(*args)
856 |
857 | def selectAll(*args):
858 | return _P5_INSTANCE.selectAll(*args)
859 |
860 | def removeElements(*args):
861 | return _P5_INSTANCE.removeElements(*args)
862 |
863 | def changed(*args):
864 | return _P5_INSTANCE.changed(*args)
865 |
866 | def input(*args):
867 | return _P5_INSTANCE.input(*args)
868 |
869 | def createDiv(*args):
870 | return _P5_INSTANCE.createDiv(*args)
871 |
872 | def createP(*args):
873 | return _P5_INSTANCE.createP(*args)
874 |
875 | def createSpan(*args):
876 | return _P5_INSTANCE.createSpan(*args)
877 |
878 | def createImg(*args):
879 | return _P5_INSTANCE.createImg(*args)
880 |
881 | def createA(*args):
882 | return _P5_INSTANCE.createA(*args)
883 |
884 | def createSlider(*args):
885 | return _P5_INSTANCE.createSlider(*args)
886 |
887 | def createButton(*args):
888 | return _P5_INSTANCE.createButton(*args)
889 |
890 | def createCheckbox(*args):
891 | return _P5_INSTANCE.createCheckbox(*args)
892 |
893 | def createSelect(*args):
894 | return _P5_INSTANCE.createSelect(*args)
895 |
896 | def createRadio(*args):
897 | return _P5_INSTANCE.createRadio(*args)
898 |
899 | def createColorPicker(*args):
900 | return _P5_INSTANCE.createColorPicker(*args)
901 |
902 | def createInput(*args):
903 | return _P5_INSTANCE.createInput(*args)
904 |
905 | def createFileInput(*args):
906 | return _P5_INSTANCE.createFileInput(*args)
907 |
908 | def createVideo(*args):
909 | return _P5_INSTANCE.createVideo(*args)
910 |
911 | def createAudio(*args):
912 | return _P5_INSTANCE.createAudio(*args)
913 |
914 | def createCapture(*args):
915 | return _P5_INSTANCE.createCapture(*args)
916 |
917 | def createElement(*args):
918 | return _P5_INSTANCE.createElement(*args)
919 |
920 | def createCanvas(*args):
921 | canvas = _P5_INSTANCE.createCanvas(*args)
922 |
923 | global width, height
924 | width = _P5_INSTANCE.width
925 | height = _P5_INSTANCE.height
926 |
927 | return canvas
928 |
929 |
930 | def pop(*args):
931 | p5_pop = _P5_INSTANCE.pop(*args)
932 | return p5_pop
933 |
934 |
935 | # Processing Python or Java mode compatibility aliases
936 | size = createCanvas
937 | popMatrix = pop
938 | popStyle = pop
939 | pushMatrix = push
940 | pushStyle = push
941 |
942 | # PVector is a wrapper/helper class for p5.Vector objets
943 | # providing names similar to Processing Python or Java modes
944 | # but mostly keeping p5js functionality
945 |
946 | from numbers import Number
947 |
948 | class PVector:
949 |
950 | def __init__(self, x=0, y=0, z=0):
951 | self.__vector = createVector(x, y, z)
952 | self.add = self.__instance_add__
953 | self.sub = self.__instance_sub__
954 | self.mult = self.__instance_mult__
955 | self.div = self.__instance_div__
956 | self.cross = self.__instance_cross__
957 | self.dist = self.__instance_dist__
958 | self.dot = self.__instance_dot__
959 | self.lerp = self.__instance_lerp__
960 |
961 | @property
962 | def x(self):
963 | return self.__vector.x
964 |
965 | @x.setter
966 | def x(self, x):
967 | self.__vector.x = x
968 |
969 | @property
970 | def y(self):
971 | return self.__vector.y
972 |
973 | @y.setter
974 | def y(self, y):
975 | self.__vector.y = y
976 |
977 | @property
978 | def z(self):
979 | return self.__vector.z
980 |
981 | @z.setter
982 | def z(self, z):
983 | self.__vector.z = z
984 |
985 | def mag(self):
986 | return self.__vector.mag()
987 |
988 | def magSq(self):
989 | return self.__vector.magSq()
990 |
991 | def setMag(self, mag):
992 | self.__vector.setMag(mag)
993 | return self
994 |
995 | def normalize(self):
996 | self.__vector.normalize()
997 | return self
998 |
999 | def limit(self, max):
1000 | self.__vector.limit(max)
1001 | return self
1002 |
1003 | def heading(self):
1004 | return self.__vector.heading()
1005 |
1006 | def rotate(self, angle):
1007 | self.__vector.rotate(angle)
1008 | return self
1009 |
1010 | def __instance_add__(self, *args):
1011 | if len(args) == 1:
1012 | return PVector.add(self, args[0], self)
1013 | else:
1014 | return PVector.add(self, PVector(*args), self)
1015 |
1016 | def __instance_sub__(self, *args):
1017 | if len(args) == 1:
1018 | return PVector.sub(self, args[0], self)
1019 | else:
1020 | return PVector.sub(self, PVector(*args), self)
1021 |
1022 | def __instance_mult__(self, o):
1023 | return PVector.mult(self, o, self)
1024 |
1025 | def __instance_div__(self, f):
1026 | return PVector.div(self, f, self)
1027 |
1028 | def __instance_cross__(self, o):
1029 | return PVector.cross(self, o, self)
1030 |
1031 | def __instance_dist__(self, o):
1032 | return PVector.dist(self, o)
1033 |
1034 | def __instance_dot__(self, *args):
1035 | if len(args) == 1:
1036 | v = args[0]
1037 | else:
1038 | v = args
1039 | return self.x * v[0] + self.y * v[1] + self.z * v[2]
1040 |
1041 | def __instance_lerp__(self, *args):
1042 | if len(args) == 2:
1043 | return PVector.lerp(self, args[0], args[1], self)
1044 | else:
1045 | vx, vy, vz, f = args
1046 | return PVector.lerp(self, PVector(vx, vy, vz), f, self)
1047 |
1048 | def get(self):
1049 | return PVector(self.x, self.y, self.z)
1050 |
1051 | def copy(self):
1052 | return PVector(self.x, self.y, self.z)
1053 |
1054 | def __getitem__(self, k):
1055 | return getattr(self, ('x', 'y', 'z')[k])
1056 |
1057 | def __setitem__(self, k, v):
1058 | setattr(self, ('x', 'y', 'z')[k], v)
1059 |
1060 | def __copy__(self):
1061 | return PVector(self.x, self.y, self.z)
1062 |
1063 | def __deepcopy__(self, memo):
1064 | return PVector(self.x, self.y, self.z)
1065 |
1066 | def __repr__(self): # PROVISÓRIO
1067 | return f'PVector({self.x}, {self.y}, {self.z})'
1068 |
1069 | def set(self, *args):
1070 | """
1071 | Sets the x, y, and z component of the vector using two or three separate
1072 | variables, the data from a p5.Vector, or the values from a float array.
1073 | """
1074 | self.__vector.set(*args)
1075 |
1076 | @classmethod
1077 | def add(cls, a, b, dest=None):
1078 | if dest is None:
1079 | return PVector(a.x + b[0], a.y + b[1], a.z + b[2])
1080 | dest.__vector.set(a.x + b[0], a.y + b[1], a.z + b[2])
1081 | return dest
1082 |
1083 | @classmethod
1084 | def sub(cls, a, b, dest=None):
1085 | if dest is None:
1086 | return PVector(a.x - b[0], a.y - b[1], a.z - b[2])
1087 | dest.__vector.set(a.x - b[0], a.y - b[1], a.z - b[2])
1088 | return dest
1089 |
1090 | @classmethod
1091 | def mult(cls, a, b, dest=None):
1092 | if dest is None:
1093 | return PVector(a.x * b, a.y * b, a.z * b)
1094 | dest.__vector.set(a.x * b, a.y * b, a.z * b)
1095 | return dest
1096 |
1097 | @classmethod
1098 | def div(cls, a, b, dest=None):
1099 | if dest is None:
1100 | return PVector(a.x / b, a.y / b, a.z / b)
1101 | dest.__vector.set(a.x / b, a.y / b, a.z / b)
1102 | return dest
1103 |
1104 | @classmethod
1105 | def dist(cls, a, b):
1106 | return a.__vector.dist(b.__vector)
1107 |
1108 | @classmethod
1109 | def dot(cls, a, b):
1110 | return a.__vector.dot(b.__vector)
1111 |
1112 | def __add__(a, b):
1113 | return PVector.add(a, b, None)
1114 |
1115 | def __sub__(a, b):
1116 | return PVector.sub(a, b, None)
1117 |
1118 | def __isub__(a, b):
1119 | a.sub(b)
1120 | return a
1121 |
1122 | def __iadd__(a, b):
1123 | a.add(b)
1124 | return a
1125 |
1126 | def __mul__(a, b):
1127 | if not isinstance(b, Number):
1128 | raise TypeError(
1129 | "The * operator can only be used to multiply a PVector by a number")
1130 | return PVector.mult(a, float(b), None)
1131 |
1132 | def __rmul__(a, b):
1133 | if not isinstance(b, Number):
1134 | raise TypeError(
1135 | "The * operator can only be used to multiply a PVector by a number")
1136 | return PVector.mult(a, float(b), None)
1137 |
1138 | def __imul__(a, b):
1139 | if not isinstance(b, Number):
1140 | raise TypeError(
1141 | "The *= operator can only be used to multiply a PVector by a number")
1142 | a.__vector.mult(float(b))
1143 | return a
1144 |
1145 | def __truediv__(a, b):
1146 | if not isinstance(b, Number):
1147 | raise TypeError(
1148 | "The * operator can only be used to multiply a PVector by a number")
1149 | return PVector(a.x / float(b), a.y / float(b), a.z / float(b))
1150 |
1151 | def __itruediv__(a, b):
1152 | if not isinstance(b, Number):
1153 | raise TypeError(
1154 | "The /= operator can only be used to multiply a PVector by a number")
1155 | a.__vector.set(a.x / float(b), a.y / float(b), a.z / float(b))
1156 | return a
1157 |
1158 | def __eq__(a, b):
1159 | return a.x == b[0] and a.y == b[1] and a.z == b[2]
1160 |
1161 | def __lt__(a, b):
1162 | return a.magSq() < b.magSq()
1163 |
1164 | def __le__(a, b):
1165 | return a.magSq() <= b.magSq()
1166 |
1167 | def __gt__(a, b):
1168 | return a.magSq() > b.magSq()
1169 |
1170 | def __ge__(a, b):
1171 | return a.magSq() >= b.magSq()
1172 |
1173 | # Problematic class methods, we would rather use p5.Vector when possible...
1174 |
1175 | @classmethod
1176 | def lerp(cls, a, b, f, dest=None):
1177 | v = createVector(a.x, a.y, a.z)
1178 | v.lerp(b.__vector, f)
1179 | if dest is None:
1180 | return PVector(v.x, v.y, v.z)
1181 | dest.set(v.x, v.y, v.z)
1182 | return dest
1183 |
1184 | @classmethod
1185 | def cross(cls, a, b, dest=None):
1186 | x = a.y * b[2] - b[1] * a.z
1187 | y = a.z * b[0] - b[2] * a.x
1188 | z = a.x * b[1] - b[0] * a.y
1189 | if dest is None:
1190 | return PVector(x, y, z)
1191 | dest.set(x, y, z)
1192 | return dest
1193 |
1194 | @classmethod
1195 | def fromAngle(cls, angle, length=1):
1196 | # https://github.com/processing/p5.js/blob/3f0b2f0fe575dc81c724474154f5b23a517b7233/src/math/p5.Vector.js
1197 | return PVector(length * cos(angle), length * sin(angle), 0)
1198 |
1199 | @classmethod
1200 | def fromAngles(theta, phi, length=1):
1201 | # https://github.com/processing/p5.js/blob/3f0b2f0fe575dc81c724474154f5b23a517b7233/src/math/p5.Vector.js
1202 | cosPhi = cos(phi)
1203 | sinPhi = sin(phi)
1204 | cosTheta = cos(theta)
1205 | sinTheta = sin(theta)
1206 | return PVector(length * sinTheta * sinPhi,
1207 | -length * cosTheta,
1208 | length * sinTheta * cosPhi)
1209 |
1210 | @classmethod
1211 | def random2D(cls):
1212 | return PVector.fromAngle(random(TWO_PI))
1213 |
1214 | @classmethod
1215 | def random3D(cls, dest=None):
1216 | angle = random(TWO_PI)
1217 | vz = random(2) - 1
1218 | mult = sqrt(1 - vz * vz)
1219 | vx = mult * cos(angle)
1220 | vy = mult * sin(angle)
1221 | if dest is None:
1222 | return PVector(vx, vy, vz)
1223 | dest.set(vx, vy, vz)
1224 | return dest
1225 |
1226 | @classmethod
1227 | def angleBetween(cls, a, b):
1228 | return acos(a.dot(b) / sqrt(a.magSq() * b.magSq()))
1229 |
1230 | # Other harmless p5js methods
1231 |
1232 | def equals(self, v):
1233 | return self == v
1234 |
1235 | def heading2D(self):
1236 | return self.__vector.heading()
1237 |
1238 | def reflect(self, *args):
1239 | # Reflect the incoming vector about a normal to a line in 2D, or about
1240 | # a normal to a plane in 3D This method acts on the vector directly
1241 | r = self.__vector.reflect(*args)
1242 | return r
1243 |
1244 | def array(self):
1245 | # Return a representation of this vector as a float array. This is only
1246 | # for temporary use. If used in any w fashion, the contents should be
1247 | # copied by using the p5.Vector.copy() method to copy into your own
1248 | # array.
1249 | return self.__vector.array()
1250 |
1251 | def toString(self):
1252 | # Returns a string representation of a vector v by calling String(v) or v.toString().
1253 | # return self.__vector.toString() would be something like "p5.vector
1254 | # Object […, …, …]"
1255 | return str(self)
1256 |
1257 | def rem(self, *args):
1258 | # Gives remainder of a vector when it is divided by anw vector. See
1259 | # examples for more context.
1260 | self.__vector.rem(*args)
1261 | return self
1262 |
1263 |
1264 | def pre_draw(p5_instance, draw_func):
1265 | """
1266 | We need to run this before the actual draw to insert and update p5 env variables
1267 | """
1268 | global _CTX_MIDDLE, _DEFAULT_FILL, _DEFAULT_LEADMULT, _DEFAULT_STROKE, _DEFAULT_TEXT_FILL
1269 |
1270 | global ADD, ALT, ARROW, AUTO, AUDIO, AXES, BACKSPACE, BASELINE, BEVEL, BEZIER, BLEND, BLUR, BOLD, BOLDITALIC
1271 | global BOTTOM, BURN, CENTER, CHORD, CLAMP, CLOSE, CONTROL, CORNER, CORNERS, CROSS, CURVE, DARKEST
1272 | global DEG_TO_RAD, DEGREES, DELETE, DIFFERENCE, DILATE, DODGE, DOWN_ARROW, ENTER, ERODE, ESCAPE, EXCLUSION
1273 | global FILL, GRAY, GRID, HALF_PI, HAND, HARD_LIGHT, HSB, HSL, IMAGE, IMMEDIATE, INVERT, ITALIC, LANDSCAPE
1274 | global LEFT, LEFT_ARROW, LIGHTEST, LINE_LOOP, LINE_STRIP, LINEAR, LINES, MIRROR, MITER, MOVE, MULTIPLY, NEAREST
1275 | global NORMAL, OPAQUE, OPEN, OPTION, OVERLAY, P2D, P3D, PI, PIE, POINTS, PORTRAIT, POSTERIZE, PROJECT, QUAD_STRIP
1276 | global QUADRATIC, QUADS, QUARTER_PI, RAD_TO_DEG, RADIANS, RADIUS, REPEAT, REPLACE, RETURN, RGB, RIGHT, RIGHT_ARROW
1277 | global ROUND, SCREEN, SHIFT, SOFT_LIGHT, SQUARE, STROKE, SUBTRACT, TAB, TAU, TEXT, TEXTURE, THRESHOLD, TOP
1278 | global TRIANGLE_FAN, TRIANGLE_STRIP, TRIANGLES, TWO_PI, UP_ARROW, VIDEO, WAIT, WEBGL
1279 |
1280 | global frameCount, focused, displayWidth, displayHeight, windowWidth, windowHeight, width, height
1281 | global deviceOrientation, accelerationX, accelerationY, accelerationZ
1282 | global pAccelerationX, pAccelerationY, pAccelerationZ, rotationX, rotationY, rotationZ
1283 | global pRotationX, pRotationY, pRotationZ, turnAxis, keyIsPressed, key, keyCode, mouseX, mouseY, pmouseX, pmouseY
1284 | global winMouseX, winMouseY, pwinMouseX, pwinMouseY, mouseButton, mouseIsPressed, touches, pixels
1285 |
1286 | _CTX_MIDDLE = p5_instance._CTX_MIDDLE
1287 | _DEFAULT_FILL = p5_instance._DEFAULT_FILL
1288 | _DEFAULT_LEADMULT = p5_instance._DEFAULT_LEADMULT
1289 | _DEFAULT_STROKE = p5_instance._DEFAULT_STROKE
1290 | _DEFAULT_TEXT_FILL = p5_instance._DEFAULT_TEXT_FILL
1291 |
1292 | ADD = p5_instance.ADD
1293 | ALT = p5_instance.ALT
1294 | ARROW = p5_instance.ARROW
1295 | AUDIO = p5_instance.AUDIO
1296 | AUTO = p5_instance.AUTO
1297 | AXES = p5_instance.AXES
1298 | BACKSPACE = p5_instance.BACKSPACE
1299 | BASELINE = p5_instance.BASELINE
1300 | BEVEL = p5_instance.BEVEL
1301 | BEZIER = p5_instance.BEZIER
1302 | BLEND = p5_instance.BLEND
1303 | BLUR = p5_instance.BLUR
1304 | BOLD = p5_instance.BOLD
1305 | BOLDITALIC = p5_instance.BOLDITALIC
1306 | BOTTOM = p5_instance.BOTTOM
1307 | BURN = p5_instance.BURN
1308 | CENTER = p5_instance.CENTER
1309 | CHORD = p5_instance.CHORD
1310 | CLAMP = p5_instance.CLAMP
1311 | CLOSE = p5_instance.CLOSE
1312 | CONTROL = p5_instance.CONTROL
1313 | CORNER = p5_instance.CORNER
1314 | CORNERS = p5_instance.CORNERS
1315 | CROSS = p5_instance.CROSS
1316 | CURVE = p5_instance.CURVE
1317 | DARKEST = p5_instance.DARKEST
1318 | DEG_TO_RAD = p5_instance.DEG_TO_RAD
1319 | DEGREES = p5_instance.DEGREES
1320 | DELETE = p5_instance.DELETE
1321 | DIFFERENCE = p5_instance.DIFFERENCE
1322 | DILATE = p5_instance.DILATE
1323 | DODGE = p5_instance.DODGE
1324 | DOWN_ARROW = p5_instance.DOWN_ARROW
1325 | ENTER = p5_instance.ENTER
1326 | ERODE = p5_instance.ERODE
1327 | ESCAPE = p5_instance.ESCAPE
1328 | EXCLUSION = p5_instance.EXCLUSION
1329 | FILL = p5_instance.FILL
1330 | GRAY = p5_instance.GRAY
1331 | GRID = p5_instance.GRID
1332 | HALF_PI = p5_instance.HALF_PI
1333 | HAND = p5_instance.HAND
1334 | HARD_LIGHT = p5_instance.HARD_LIGHT
1335 | HSB = p5_instance.HSB
1336 | HSL = p5_instance.HSL
1337 | IMAGE = p5_instance.IMAGE
1338 | IMMEDIATE = p5_instance.IMMEDIATE
1339 | INVERT = p5_instance.INVERT
1340 | ITALIC = p5_instance.ITALIC
1341 | LANDSCAPE = p5_instance.LANDSCAPE
1342 | LEFT = p5_instance.LEFT
1343 | LEFT_ARROW = p5_instance.LEFT_ARROW
1344 | LIGHTEST = p5_instance.LIGHTEST
1345 | LINE_LOOP = p5_instance.LINE_LOOP
1346 | LINE_STRIP = p5_instance.LINE_STRIP
1347 | LINEAR = p5_instance.LINEAR
1348 | LINES = p5_instance.LINES
1349 | MIRROR = p5_instance.MIRROR
1350 | MITER = p5_instance.MITER
1351 | MOVE = p5_instance.MOVE
1352 | MULTIPLY = p5_instance.MULTIPLY
1353 | NEAREST = p5_instance.NEAREST
1354 | NORMAL = p5_instance.NORMAL
1355 | OPAQUE = p5_instance.OPAQUE
1356 | OPEN = p5_instance.OPEN
1357 | OPTION = p5_instance.OPTION
1358 | OVERLAY = p5_instance.OVERLAY
1359 | P2D = p5_instance.P2D
1360 | P3D = p5_instance.WEBGL
1361 | PI = p5_instance.PI
1362 | PIE = p5_instance.PIE
1363 | POINTS = p5_instance.POINTS
1364 | PORTRAIT = p5_instance.PORTRAIT
1365 | POSTERIZE = p5_instance.POSTERIZE
1366 | PROJECT = p5_instance.PROJECT
1367 | QUAD_STRIP = p5_instance.QUAD_STRIP
1368 | QUADRATIC = p5_instance.QUADRATIC
1369 | QUADS = p5_instance.QUADS
1370 | QUARTER_PI = p5_instance.QUARTER_PI
1371 | RAD_TO_DEG = p5_instance.RAD_TO_DEG
1372 | RADIANS = p5_instance.RADIANS
1373 | RADIUS = p5_instance.RADIUS
1374 | REPEAT = p5_instance.REPEAT
1375 | REPLACE = p5_instance.REPLACE
1376 | RETURN = p5_instance.RETURN
1377 | RGB = p5_instance.RGB
1378 | RIGHT = p5_instance.RIGHT
1379 | RIGHT_ARROW = p5_instance.RIGHT_ARROW
1380 | ROUND = p5_instance.ROUND
1381 | SCREEN = p5_instance.SCREEN
1382 | SHIFT = p5_instance.SHIFT
1383 | SOFT_LIGHT = p5_instance.SOFT_LIGHT
1384 | SQUARE = p5_instance.SQUARE
1385 | STROKE = p5_instance.STROKE
1386 | SUBTRACT = p5_instance.SUBTRACT
1387 | TAB = p5_instance.TAB
1388 | TAU = p5_instance.TAU
1389 | TEXT = p5_instance.TEXT
1390 | TEXTURE = p5_instance.TEXTURE
1391 | THRESHOLD = p5_instance.THRESHOLD
1392 | TOP = p5_instance.TOP
1393 | TRIANGLE_FAN = p5_instance.TRIANGLE_FAN
1394 | TRIANGLE_STRIP = p5_instance.TRIANGLE_STRIP
1395 | TRIANGLES = p5_instance.TRIANGLES
1396 | TWO_PI = p5_instance.TWO_PI
1397 | UP_ARROW = p5_instance.UP_ARROW
1398 | VIDEO = p5_instance.VIDEO
1399 | WAIT = p5_instance.WAIT
1400 | WEBGL = p5_instance.WEBGL
1401 |
1402 | frameCount = p5_instance.frameCount
1403 | focused = p5_instance.focused
1404 | displayWidth = p5_instance.displayWidth
1405 | displayHeight = p5_instance.displayHeight
1406 | windowWidth = p5_instance.windowWidth
1407 | windowHeight = p5_instance.windowHeight
1408 | width = p5_instance.width
1409 | height = p5_instance.height
1410 | deviceOrientation = p5_instance.deviceOrientation
1411 | accelerationX = p5_instance.accelerationX
1412 | accelerationY = p5_instance.accelerationY
1413 | accelerationZ = p5_instance.accelerationZ
1414 | pAccelerationX = p5_instance.pAccelerationX
1415 | pAccelerationY = p5_instance.pAccelerationY
1416 | pAccelerationZ = p5_instance.pAccelerationZ
1417 | rotationX = p5_instance.rotationX
1418 | rotationY = p5_instance.rotationY
1419 | rotationZ = p5_instance.rotationZ
1420 | pRotationX = p5_instance.pRotationX
1421 | pRotationY = p5_instance.pRotationY
1422 | pRotationZ = p5_instance.pRotationZ
1423 | turnAxis = p5_instance.turnAxis
1424 | keyIsPressed = p5_instance.keyIsPressed
1425 | key = p5_instance.key
1426 | keyCode = p5_instance.keyCode
1427 | mouseX = p5_instance.mouseX
1428 | mouseY = p5_instance.mouseY
1429 | pmouseX = p5_instance.pmouseX
1430 | pmouseY = p5_instance.pmouseY
1431 | winMouseX = p5_instance.winMouseX
1432 | winMouseY = p5_instance.winMouseY
1433 | pwinMouseX = p5_instance.pwinMouseX
1434 | pwinMouseY = p5_instance.pwinMouseY
1435 | mouseButton = p5_instance.mouseButton
1436 | mouseIsPressed = p5_instance.mouseIsPressed
1437 | touches = p5_instance.touches
1438 | pixels = p5_instance.pixels
1439 |
1440 | return draw_func()
1441 |
1442 |
1443 | def global_p5_injection(p5_sketch):
1444 | """
1445 | Injects the p5js's skecth instance as a global variable to setup and draw functions
1446 | """
1447 |
1448 | def decorator(f):
1449 | def wrapper():
1450 | global _P5_INSTANCE
1451 | _P5_INSTANCE = p5_sketch
1452 | return pre_draw(_P5_INSTANCE, f)
1453 |
1454 |
1455 | return wrapper
1456 |
1457 |
1458 | return decorator
1459 |
1460 |
1461 | def start_p5(setup_func, draw_func, event_functions):
1462 | """
1463 | This is the entrypoint function. It accepts 2 parameters:
1464 |
1465 | - setup_func: a Python setup callable
1466 | - draw_func: a Python draw callable
1467 | - event_functions: a config dict for the event functions in the format:
1468 | {"eventFunctionName": python_event_function}
1469 |
1470 | This method gets the p5js's sketch instance and injects them
1471 | """
1472 |
1473 | def sketch_setup(p5_sketch):
1474 | """
1475 | Callback function called to configure new p5 instance
1476 | """
1477 | p5_sketch.setup = global_p5_injection(p5_sketch)(setup_func)
1478 | p5_sketch.draw = global_p5_injection(p5_sketch)(draw_func)
1479 |
1480 |
1481 | window.instance = p5.new(sketch_setup, 'sketch-holder')
1482 |
1483 | # inject event functions into p5
1484 | event_function_names = (
1485 | "deviceMoved", "deviceTurned", "deviceShaken", "windowResized",
1486 | "keyPressed", "keyReleased", "keyTyped",
1487 | "mousePressed", "mouseReleased", "mouseClicked", "doubleClicked",
1488 | "mouseMoved", "mouseDragged", "mouseWheel",
1489 | "touchStarted", "touchMoved", "touchEnded"
1490 | )
1491 |
1492 | for f_name in [f for f in event_function_names if event_functions.get(f, None)]:
1493 | func = event_functions[f_name]
1494 | event_func = global_p5_injection(instance)(func)
1495 | setattr(instance, f_name, event_func)
1496 | `;
1497 |
1498 | let placeholder = `
1499 | def setup():
1500 | pass
1501 |
1502 | def draw():
1503 | pass
1504 | `;
1505 |
1506 | let userCode = `
1507 | from random import randint
1508 | import os
1509 | import sys
1510 | import time
1511 | from time import sleep
1512 | print(sys.path)
1513 |
1514 | class Node:
1515 |
1516 | def __init__(self, x, y):
1517 |
1518 | self.x = x
1519 | self.y = y
1520 | self.f = 0
1521 | self.g = 0
1522 | self.h = 0
1523 | self.neighbors = []
1524 | self.previous = None
1525 | self. obstacle = False
1526 |
1527 |
1528 | def add_neighbors(self,grid, columns, rows):
1529 |
1530 | neighbor_x = self.x
1531 | neighbor_y = self.y
1532 |
1533 | if neighbor_x < columns - 1:
1534 | self.neighbors.append(grid[neighbor_x+1][neighbor_y])
1535 | if neighbor_x > 0:
1536 | self.neighbors.append(grid[neighbor_x-1][neighbor_y])
1537 | if neighbor_y < rows -1:
1538 | self.neighbors.append(grid[neighbor_x][neighbor_y +1])
1539 | if neighbor_y > 0:
1540 | self.neighbors.append(grid[neighbor_x][neighbor_y-1])
1541 | #diagonals
1542 | """ if neighbor_x > 0 and neighbor_y > 0:
1543 | self.neighbors.append(grid[neighbor_x-1][neighbor_y-1])
1544 | if neighbor_x < columns -1 and neighbor_y > 0:
1545 | self.neighbors.append(grid[neighbor_x+1][neighbor_y-1])
1546 | if neighbor_x > 0 and neighbor_y 0:
1676 | open_set, closed_set, current_node, final_path = AStar.start_path(open_set, closed_set, current_node, self.end)
1677 | if len(final_path) > 0:
1678 | break
1679 |
1680 | return final_path
1681 |
1682 |
1683 | cols = 25
1684 | rows = 25
1685 | start = [0,0]
1686 | end = [24,24]
1687 | open_set = []
1688 | closed_set = []
1689 | current_node = None
1690 | final_path = []
1691 | grid = []
1692 |
1693 |
1694 | def show_func(grid_element,color, width,height):
1695 | if grid_element.obstacle == True:
1696 | fill("black")
1697 | else:
1698 | fill(color)
1699 | noStroke()
1700 | rect(grid_element.x * width, grid_element.y * height, width-1 , height-1)
1701 |
1702 |
1703 | def setup():
1704 | global grid
1705 | createCanvas(500, 500)
1706 | background(160)
1707 |
1708 |
1709 |
1710 | flag = False
1711 |
1712 |
1713 | def draw():
1714 |
1715 | global grid
1716 | global end
1717 | global open_set
1718 | global closed_set
1719 | global final_path
1720 | global current_node
1721 | global flag
1722 | global start
1723 |
1724 | global cols
1725 | global rows
1726 |
1727 | frameRate(60)
1728 | w = width / cols
1729 | h = height / rows
1730 | if flag == False:
1731 |
1732 |
1733 |
1734 | grid = AStar.create_grid(cols, rows)
1735 | grid = AStar.fill_grids(grid, cols, rows, obstacle_ratio = 30)
1736 | grid = AStar.get_neighbors(grid, cols, rows)
1737 | start = grid[start[0]][start[1]]
1738 | end = grid[end[0]][end[1]]
1739 | end.obstacle = False
1740 | start.obstacle = False
1741 |
1742 | background(0)
1743 | for i in range(cols):
1744 | for j in range(rows):
1745 | show_func(grid[i][j], color(255),w,h)
1746 | stroke(0,0,0)
1747 | line(0, 0, 0, width)
1748 | line(0,0,height, 1)
1749 | open_set.append(start)
1750 |
1751 |
1752 | flag = True
1753 |
1754 | if len(open_set) > 0:
1755 | open_set, closed_set, current_node, final_path = AStar.start_path(open_set, closed_set, current_node, end)
1756 |
1757 |
1758 | #grid
1759 | show_func(start, "green", w,h)
1760 | show_func(end, "red",w,h)
1761 | for i in range(len(open_set)):
1762 | #show_func(open_set[i], "#00ffbf",w,h)
1763 | show_func(open_set[i], "#00ffbf",w,h)
1764 |
1765 | for i in range(len(closed_set)):
1766 | show_func(closed_set[i], "#ffa500",w,h)
1767 | show_func(start, "green", w,h)
1768 |
1769 | show_func(current_node, "#8a2be2",w,h)
1770 |
1771 | if len(open_set) == 0:
1772 | print("No way!")
1773 | #noLoop()
1774 |
1775 | frameRate(1)
1776 | cols = 25
1777 | rows = 25
1778 | start = [0,0]
1779 | end = [24,24]
1780 | open_set = []
1781 | closed_set = []
1782 | current_node = None
1783 | final_path = []
1784 | grid = []
1785 | flag = False
1786 |
1787 |
1788 | if len(final_path) > 0:
1789 | for i in range(len(final_path)):
1790 | show_func(final_path[i], "#8a2be2",w,h)
1791 | #show_func(final_path[i], "red",w,h)
1792 | show_func(start, "green", w,h)
1793 | show_func(end, "red",w,h)
1794 |
1795 | print("Done!!")
1796 | frameRate(1)
1797 | cols = 25
1798 | rows = 25
1799 | start = [0,0]
1800 | end = [24,24]
1801 | open_set = []
1802 | closed_set = []
1803 | current_node = None
1804 | final_path = []
1805 | grid = []
1806 | flag = False
1807 |
1808 |
1809 |
1810 |
1811 | `;
1812 |
1813 | function runCode() {
1814 | let code = [
1815 | placeholder,
1816 | userCode,
1817 | wrapper_content,
1818 | 'start_p5(setup, draw, {});',
1819 | ].join('\n');
1820 |
1821 | if (window.instance) {
1822 | window.instance.canvas.remove();
1823 | }
1824 |
1825 | console.log("Python execution output:");
1826 | pyodide.runPython(code);
1827 | }
1828 |
1829 | languagePluginLoader.then(() => {
1830 | pyodide.runPython(`
1831 | import io, code, sys
1832 | from js import pyodide, p5, window, document
1833 | print(sys.version)
1834 | `)
1835 |
1836 | window.runSketchCode = (code) => {
1837 | userCode = code;
1838 | runCode();
1839 | }
1840 |
1841 | runCode();
1842 | });
--------------------------------------------------------------------------------