{ "packages":["numpy"] }

Cohesion 1.0 Separation 1.0 Alighment 1.0 TendToGoal 1.0 RecogRange 50 MaxSpeed 2.0
from pyodide.ffi import create_proxy, to_js from js import window from js import Math from js import THREE from js import Object from js import document import asyncio import js, pyodide scene = THREE.Scene.new() setcolor = "#bbbbbb" scene.background = THREE.Color.new(setcolor) renderer = THREE.WebGLRenderer.new() renderer.antialias = True renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild( renderer.domElement ) camera = THREE.PerspectiveCamera.new(75, window.innerWidth / window.innerHeight, 0.1, 1000.0 ) camera.position.set(80, 80, 80) controls = THREE.OrbitControls.new(camera, renderer.domElement) controls.listenToKeyEvents(window) def window_onsize(event): camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize( window.innerWidth, window.innerHeight ) window.onresize = window_onsize scene.pause = True # ================ Light setting ==================== ambientLight = THREE.AmbientLight.new(0xaaaaaa) scene.add(ambientLight) dirLight = THREE.DirectionalLight.new(0xffffff) dirLight.position.set(1,1,1) dirLight.castSHadow = True scene.add(dirLight) lightBack = THREE.PointLight.new(0x0FFFFF, 1) lightBack.position.set(0, -3, -1) scene.add(lightBack) # ===========Creating Bound Box ============ ## You can modify the size of box boundRange = 50 bound_material = THREE.MeshStandardMaterial.new() bound_material.color = THREE.Color.new(0x444488) bound_material.transparent = True bound_material.opacity = 0.1 edge_material = THREE.LineBasicMaterial.new() edge_material.color = THREE.Color.new(0xfffffff) bound = THREE.Mesh.new(THREE.BoxGeometry.new(boundRange * 2, boundRange * 2, boundRange * 2), bound_material) edges = THREE.LineSegments.new(THREE.EdgesGeometry.new(THREE.BoxGeometry.new(boundRange * 2, boundRange * 2, boundRange * 2)), ) scene.add(bound) scene.add(edges) #===================== CORE ===================== import numpy as np import math boidsP = None boidsV = None boidsN = 0 boidsShapes = [] predetorP = None predetorV = None predetorN = 0 obstacleP = None obstacleN = 0 obstacleShapes = [] obsSize = [] goalShape = None cohesion = 1 separation = 1 alignment = 1 goal_tend = 1 max_speed = 2 pred_max_speed = 2 show_goal = False show_predetor = False show_obstacle = False recog_range = 50 coll = 0 def create_boids(num): global boidsN, boidsP, boidsV, boidsShapes boidsN = num boidsP = -np.random.rand(num, 3) * (boundRange * 2) + boundRange boidsV = (-np.random.rand(num, 3) * 3 + 1.5) for i in range(num): geometry = THREE.CylinderGeometry.new(0.0,0.75,2.25,4,1) material = THREE.MeshPhongMaterial.new() material.color = THREE.Color.new(0x993333) material.flatShading = True boidShape = THREE.Mesh.new(geometry, material) boidsShapes.append(boidShape) scene.add(boidShape) def create_goal(): global goalShape, goalP geometry = THREE.SphereGeometry.new(0.5, 32, 32) material = THREE.MeshBasicMaterial.new() material.color = THREE.Color.new(0x0000ff) goalShape = THREE.Mesh.new(geometry, material) goalP = np.random.rand(3) * (boundRange * 2) - boundRange goalShape.position.set(goalP[0], goalP[1], goalP[2]) if show_goal: scene.add(goalShape) def toggle_goal(): global show_goal show_goal = not show_goal if show_goal: scene.add(goalShape) else: scene.remove(goalShape) def create_predetors(num): global predetorN, predetorP, predetorV, predetorShapes predetorN = num predetorP = -np.random.rand(num, 3) * (boundRange * 2) + boundRange predetorV = -np.random.rand(num, 3) * 3 + 1.5 predetorShapes = [] for i in range(num): geometry = THREE.CylinderGeometry.new(0.0,1.5,3.5,4,1) material = THREE.MeshPhongMaterial.new() material.color = THREE.Color.new(0x333377) material.flatShading = True predetorShape = THREE.Mesh.new(geometry, material) predetorShapes.append(predetorShape) if show_predetor: scene.add(predetorShape) def toggle_predetors(): global show_predetor show_predetor = not show_predetor if show_predetor: for i in range(predetorN): scene.add(predetorShapes[i]) else: for i in range(predetorN): scene.remove(predetorShapes[i]) def create_obstacles(num): global obstacleN, obstacleP, obstacleShapes, obsSize obstacleN = num obstacleP = -np.random.rand(num, 3) * (boundRange * 2) + boundRange for i in range(num): obsSize = np.random.rand(num) * 30 + 1 geometry = THREE.SphereGeometry.new(obsSize[i], 16, 16) material = THREE.MeshPhongMaterial.new() material.color = THREE.Color.new(0x111111) obstacleShape = THREE.Mesh.new(geometry, material) obstacleShape.position.set(obstacleP[i][0], obstacleP[i][1], obstacleP[i][2]) obstacleShapes.append(obstacleShape) if show_obstacle: scene.add(obstacleShape) def toggle_obstacles(): global show_obstacle, obstacleN, obstacleShapes show_obstacle = not show_obstacle if show_obstacle: for i in range(obstacleN): scene.add(obstacleShapes[i]) else: for i in range(obstacleN): scene.remove(obstacleShapes[i]) def reset_scene(): reset_boids() reset_goal() reset_predetors() reset_obstacles() def reset_boids(): global boidsP, boidsV, boidsN, boundRange boidsP = -np.random.rand(boidsN, 3) * (boundRange * 2) + boundRange boidsV = -np.random.rand(boidsN, 3) * 3 + 1.5 def reset_goal(): global goalP, goalShape goalP = np.random.rand(3) * (boundRange * 2) - boundRange goalShape.position.set(goalP[0], goalP[1], goalP[2]) if show_goal: scene.add(goalShape) def reset_predetors(): global predetorP, predetorV, predetorN, boundRange predetorP = -np.random.rand(predetorN, 3) * (boundRange * 2) + boundRange predetorV = -np.random.rand(predetorN, 3) * 3 + 1.5 def reset_obstacles(): global obstacleP, obstacleN, obsSize, obstacleShapes for i in range(obstacleN): scene.remove(obstacleShapes[i]) obstacleP = -np.random.rand(obstacleN, 3) * (boundRange * 2) + boundRange obstacleShapes = [] for i in range(obstacleN): obsSize = np.random.rand(obstacleN) * 30 + 1 geometry = THREE.SphereGeometry.new(obsSize[i], 16, 16) material = THREE.MeshPhongMaterial.new() material.color = THREE.Color.new(0x111111) obstacleShape = THREE.Mesh.new(geometry, material) obstacleShape.position.set(obstacleP[i][0], obstacleP[i][1], obstacleP[i][2]) obstacleShapes.append(obstacleShape) if show_obstacle: scene.add(obstacleShape) def normalize(v): norm = np.linalg.norm(v) if norm == 0: return v return v / norm def draw_boids(): ## Sync boidData and boidShape global boidsV, boidsShapes, boidsP, boidsN for i in range(boidsN): boidsShapes[i].position.set(boidsP[i][0], boidsP[i][1], boidsP[i][2]) axis = normalize(np.cross([0, 1, 0], boidsV[i])) angle = math.acos(np.dot([0, 1, 0], boidsV[i]) / (np.linalg.norm([0, 1, 0]) * np.linalg.norm(boidsV[i]))) ## TODO Update rotation to align the boid heading direction with its velocity boidsShapes[i].setRotationFromAxisAngle(THREE.Vector3.new(axis[0], axis[1], axis[2]), angle) def draw_predetors(): global predetorShapes, predetorP, predetorN, predetorV for i in range(predetorN): predetorShapes[i].position.set(predetorP[i][0], predetorP[i][1], predetorP[i][2]) axis = normalize(np.cross([0, 1, 0], predetorV[i])) angle = math.acos(np.dot([0, 1, 0], predetorV[i]) / (np.linalg.norm([0, 1, 0]) * np.linalg.norm(predetorV[i]))) predetorShapes[i].setRotationFromAxisAngle(THREE.Vector3.new(axis[0], axis[1], axis[2]), angle) def dist(p1, p2): return np.linalg.norm(p1 - p2) def cohesion_rule(): global boidsP, boidsV, boidsN, cohesion cohV = np.zeros((boidsN, 3)) for i in range(boidsN): cnt = 0 for j in range(boidsN): if i != j and dist(boidsP[j], boidsP[i]) < recog_range: cohV[i] += boidsP[j] cnt += 1 if cnt > 0: cohV[i] /= cnt else: cohV[i] = boidsP[i] return (cohV - boidsP) / 100 * cohesion def separation_rule(): global boidsP, boidsV, boidsN sepV = np.zeros((boidsN, 3)) for i in range(boidsN): for j in range(boidsN): if i != j: if abs(dist(boidsP[j], boidsP[i])) < 7: sepV[i] -= (boidsP[j] - boidsP[i]) return sepV / 50 * separation def alignment_rule(): global boidsP, boidsV, boidsN alignV = np.zeros((boidsN, 3)) for i in range(boidsN): cnt = 0 for j in range(boidsN): if i != j and dist(boidsP[j], boidsP[i]) < recog_range: alignV[i] += boidsV[j] cnt += 1 if cnt > 0: alignV[i] /= cnt else: alignV[i] = boidsV[i] return (alignV - boidsV) / 600 * alignment def toward_goal_rule(): global boidsP, boidsV, boidsN, goalP goalV = np.zeros((boidsN, 3)) for i in range(boidsN): goalV[i] = normalize(goalP - boidsP[i]) if show_goal == False: return goalV * 0 return goalV * goal_tend def avoid_predetors_rule(): global boidsP, boidsV, boidsN, predetorP, predetorN avoidV = np.zeros((boidsN, 3)) for i in range(boidsN): for j in range(predetorN): if dist(boidsP[i], predetorP[j]) < recog_range: avoidV[i] -= (predetorP[j] - boidsP[i]) if show_predetor == False: return avoidV * 0 return avoidV / 100 def avoid_obstacles_rule(): global boidsP, boidsV, boidsN, obstacleP, obstacleN, obsSize if obstacleN == 0: return np.zeros((boidsN, 3)) avoidV = np.zeros((boidsN, 3)) for i in range(boidsN): for j in range(obstacleN): distToObs = dist(boidsP[i], obstacleP[j]) - obsSize[j] if distToObs < recog_range: if distToObs < 0.1: avoidV[i] += 100 * normalize(boidsP[i] - obstacleP[j]) else: avoidV[i] += 10 / (distToObs ** 2) * normalize(boidsP[i] - obstacleP[j]) return avoidV def limit_velocity(): global boidsV, boidsN, max_speed for i in range(boidsN): if np.linalg.norm(boidsV[i]) > max_speed: boidsV[i] = boidsV[i] / np.linalg.norm(boidsV[i]) * max_speed def avoid_bound(): # decrease the velocity of boids when they are close to the bound smoothly global boidsP, boidsV, boidsN bound = 5 amount = 0.5 for i in range(boidsN): if boidsP[i][0] + bound < -boundRange: boidsV[i][0] += amount if boidsP[i][0] - bound > boundRange: boidsV[i][0] -= amount if boidsP[i][1] + bound < -boundRange: boidsV[i][1] += amount if boidsP[i][1] - bound > boundRange: boidsV[i][1] -= amount if boidsP[i][2] + bound < -boundRange: boidsV[i][2] += amount if boidsP[i][2] - bound > boundRange: boidsV[i][2] -= amount def update_boids(): global boidsP, boidsV, boidsN vel1 = cohesion_rule() vel2 = separation_rule() vel3 = alignment_rule() vel4 = toward_goal_rule() vel5 = avoid_predetors_rule() vel6 = avoid_obstacles_rule() boidsV += vel1 + vel2 + vel3 + vel4 + vel5 + vel6 avoid_bound() limit_velocity() boidsP += boidsV def pred_chase_boid_rule(): # similar with cohesion rule global predetorP, predetorV, predetorN, boidsP, boidsN chaseV = np.zeros((predetorN, 3)) for i in range(predetorN): cnt = 0 for j in range(boidsN): if dist(boidsP[j], predetorP[i]) < recog_range: chaseV[i] += boidsP[j] cnt += 1 if cnt > 0: chaseV[i] /= (cnt) else: chaseV[i] = predetorP[i] return (chaseV - predetorP) / 100 def pred_limit_velocity(): global predetorV, predetorN, pred_max_speed for i in range(predetorN): if np.linalg.norm(predetorV[i]) > pred_max_speed: predetorV[i] = predetorV[i] / np.linalg.norm(predetorV[i]) * pred_max_speed def pred_avoid_bound(): global predetorP, predetorV, predetorN bound = 5 amount = 0.5 for i in range(predetorN): if predetorP[i][0] + bound < -boundRange: predetorV[i][0] += amount if predetorP[i][0] - bound > boundRange: predetorV[i][0] -= amount if predetorP[i][1] + bound < -boundRange: predetorV[i][1] += amount if predetorP[i][1] - bound > boundRange: predetorV[i][1] -= amount if predetorP[i][2] + bound < -boundRange: predetorV[i][2] += amount if predetorP[i][2] - bound > boundRange: predetorV[i][2] -= amount def update_predetors(): global predetorP, predetorV, predetorN vel1 = pred_chase_boid_rule() predetorV += vel1 pred_avoid_bound() pred_limit_velocity() predetorP += predetorV def run(): scene.pause = not scene.pause def animate(): if not scene.pause: update_boids() draw_boids() update_predetors() draw_predetors() renderer.render(scene, camera) ## Example code for slider def slider01_function(event): global cohesion value = int(Element("Slider01").value) cohesion = value / 100 document.getElementById("SliderValue01").innerHTML = str(round(value / 100, 2)) document.getElementById("Slider01").oninput = slider01_function def slider02_function(event): global separation value = int(Element("Slider02").value) separation = value / 100 document.getElementById("SliderValue02").innerHTML = str(round(value / 100, 2)) document.getElementById("Slider02").oninput = slider02_function def slider03_function(event): global alignment value = int(Element("Slider03").value) alignment = value / 100 document.getElementById("SliderValue03").innerHTML = str(round(value / 100, 2)) document.getElementById("Slider03").oninput = slider03_function def slider04_function(event): global recog_range value = int(Element("Slider04").value) recog_range = value document.getElementById("SliderValue04").innerHTML = str(value) document.getElementById("Slider04").oninput = slider04_function def slider05_function(event): global max_speed value = int(Element("Slider05").value) max_speed = value / 10 document.getElementById("SliderValue05").innerHTML = str(value / 10) document.getElementById("Slider05").oninput = slider05_function def slider06_function(event): global goal_tend value = int(Element("Slider06").value) goal_tend = value / 100 document.getElementById("SliderValue06").innerHTML = str(round(value / 100, 2)) document.getElementById("Slider06").oninput = slider06_function async def main(): boid_num = 30 predetor_num = 2 create_boids(boid_num) create_predetors(predetor_num) create_goal() create_obstacles(3) draw_boids() draw_predetors() while True: animate() await asyncio.sleep(0.0001) asyncio.ensure_future(main())