├── 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 | ![astar](images/astar.gif) 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 | }); --------------------------------------------------------------------------------