def load_from_json(sim, fname="sim"): """ Loads a simulation state. """ fname = get_file_path(fname, "json") if not os.path.exists(fname): Logger.log_error("File not found: '{}'. Can't load.".format(fname)) return data = dict() with open(fname, 'r') as savefile: data = json.load(savefile) # Clear previous state sim.entities.clear() # Construct sim state sim.lifetime = data["lifetime"] for e in data["particles"]: p = particle_factory.create_particle(e) if p is not None: sim.add_entity(p) else: Logger.log_warning("Loading particle failed.")
def toggle_debug_view(particle): if particle is None: return try: particle.debug_view = not particle.debug_view except: Logger.log_warning("Can't toggle debug view of selected entity.")
def delete_particle(particle): if particle is None: return particle.mfd = True Logger.log_custom("control", "Deleting particle.")
def save_to_json(sim, fname="sim"): """ Saves a simulation's state as a .json file. """ fname = get_file_path(fname, "json") with open(fname, 'w') as savefile: # Collect all data to be saved data = {"lifetime": sim.lifetime, "particles": list()} # Go over each entity and save its properties for e in sim.entities: if issubclass(type(e), particle.ForceParticle): try: p_data = dict() p_data["type"] = str(type(e)) p_data["pos"] = e.pos p_data["velocity"] = e.velocity p_data["mass"] = e.mass p_data["size"] = e.size p_data["can_move"] = e._can_move p_data["paused"] = e.paused data["particles"].append(p_data) except AttributeError as e: Logger.log_warning( "Corrupt particle. Can't save particle.") Logger.log_exception(e) continue # Dump data to json json.dump(data, savefile)
def set_global_particle_radius(sim, r): """ Sets all PrimordialParticle's radii. Note: This is rather costly """ for e in sim.entities: try: e.radius = r except AttributeError: Logger.log_warning("Can't set radius of entity: {}".format(e)) Logger.log_custom("control", "Set global particle radius to {}.".format(r))
def set_global_particle_velocity(sim, v): for e in sim.entities: try: e.velocity = [v, v] except AttributeError: Logger.log_warning("Can't set velocity of entity {}".format(e)) Logger.log_custom("control", "Set global particle velocity to {}.".format(v))
def add_state(self, s, ix=None): if s not in self.S: if ix is None: self.S.append(s) else: try: self.S.insert(ix, s) except: Logger.log_warning( "Can't insert state at position {}.".format(ix))
def toggle_pause(target): target.paused = (not target.paused) log_msg = str(type(target)) if target.paused: log_msg += " paused." else: log_msg += " unpaused." Logger.log_custom("control", log_msg)
def toggle_trail(particle): if particle is None: return try: particle.draw_trail_points = not particle.draw_trail_points if particle.draw_trail_points: # Clear previous trail if turning it back on particle.trail_points.clear() except AttributeError: Logger.log_warning("Can't draw trail of selected entity.")
def create_data_folder(): """ Creates the data folder if it doesn't exist. """ path = "./data" # default folder try: path = CONFIG["data_folder"] except KeyError: pass if not os.path.exists(path): os.mkdir(path) Logger.log_system("Created data directory.")
def cycle_trail_length(particle, increase=False): if particle is None: return try: old = particle.trail_points.limit inc = -1 * (int(math.log10(old)) + 1) if increase: inc *= -1 # Make increment positive particle.trail_points.set_limit(old + inc) except AttributeError: Logger.log_warning("Cannot change trail length of selected entity.")
def cycle_trail_density(particle, increase=False): if particle is None: return inc = -1 if increase: inc = 1 try: new = particle.trail_points_interval + inc if new > 0: particle.trail_points_interval = new except: Logger.log_warning("Can't adjust trail density of selected entity.")
def calculate_type_mod(self, e): result = 0 e_type = type(e) try: result = self.interacting_types[e_type] except KeyError: # This shouldn't be reachable, but let's be safe Logger.log_warning( "Trying to calculate mod for unknown type '{}'.".format( e_type)) return result
def calculate_reflect_force(self, e): try: return self.interacting_types[type(e)] except KeyError: # Add reflection mod / 0 for this new type F = 0 t_self = type(self) t_e = type(e) if t_self in e.interacting_types: F = e.interacting_types[t_self] self.interacting_types[t_e] = F Logger.log_info("Discovered unknown type {} -> {}".format( t_self, t_e)) return F
def set_tick_speed(controller, symbol): old_tps = controller.ticks_per_secs max_tps = 400 try: max_tps = CONFIG["max_TPS"] except KeyError: pass if symbol == key.UP: new_tps = min(old_tps * 2.0, max_tps) else: new_tps = max(1, old_tps / 2.0) pyglet.clock.unschedule(controller.tick) pyglet.clock.schedule_interval(controller.tick, 1.0 / new_tps) controller.ticks_per_secs = new_tps Logger.log_system("Set TPS to {}.".format(controller.ticks_per_secs))
def change_particle_velocity(symbol, particle): # Changes a particle's velocity based on the key pressed if particle is None: return speedmod = 0.1 mod = 1 if symbol == key.W: # Slow down, fella mod = (1 - speedmod) elif symbol == key.C: # Hurry up, buster mod = (1 + speedmod) elif symbol == key.X: # It's time to stop mod = 0 speed_incr = (0, 0) step = 0.0001 if symbol == key.LEFT: speed_incr = (-step, 0) elif symbol == key.RIGHT: speed_incr = (step, 0) elif symbol == key.UP: speed_incr = (0, step) elif symbol == key.DOWN: speed_incr = (0, -step) try: v_old = particle.velocity particle.velocity = ((v_old[0] * mod) + speed_incr[0], (v_old[1] * mod) + speed_incr[1]) except AttributeError: Logger.log_warning( "Selected entity is not a particle. Can't change velocity.")
import Particles from Particles.utils import Logger import datetime, cProfile # Time app start_time = datetime.datetime.now() # Initialize logfile Logger.clear_logfile() Logger.log("Started at " + str(start_time)) # Start the simulation Particles.run_app("./config.json") #cProfile.run("Particles.run_app()") # Finish up logfile end_time = datetime.datetime.now() Logger.log("Quit at " + str(end_time)) Logger.log("Ran for {}".format(str(end_time - start_time)))
def run_app(config=None): # Setup model & controller sim = simulation.setup() keyboard = pyglet.window.key.KeyStateHandler() controller = sim_control.SimController(sim, keyboard) # Add handlers app.win.push_handlers(controller) app.win.push_handlers(keyboard) # Profiling Profiler.make_profiler_category("draw_times") Profiler.make_profiler_category("tick_times") # Logger verbose level default_verbose = 3 if "verbose_level" in app.CONFIG: default_verbose = app.CONFIG["verbose_level"] Logger.set_verbose_level(default_verbose) fps_counter = None if "show_FPS" in app.CONFIG and app.CONFIG["show_FPS"]: fps_counter = pyglet.window.FPSDisplay(app.win) # Schedule initial simulation speed ticks_per_sec = 25.0 pyglet.clock.schedule_interval(controller.tick, 1 / ticks_per_sec) # Cap FPS max_fps = 120 if "max_FPS" in app.CONFIG: max_fps = app.CONFIG["max_FPS"] pyglet.clock.set_fps_limit(max_fps) # Draw event @app.win.event def on_draw(): starttime = time.time() app.win.clear() sim.draw() controller.draw() Profiler.add_profiler_data("draw_times", time.time() - starttime) if fps_counter: fps_counter.draw() # Generate history (run the simulation without opening the window) # This is useful to save time on drawing history_length = 0 if history_length > 0: paused_state = sim.paused Logger.log_system( "Generating {} ticks of history.".format(history_length)) starttime = time.time() sim.paused = False for _ in range(history_length): controller.tick() sim.paused = paused_state Logger.log_system("Finished generating history. ({} s)".format( (time.time() - starttime))) # Main app loop pyglet.app.run() # Collect & log some stats post_run_stats(controller, sim) return
def toggle_particle_movable(particle): try: particle._can_move = not particle._can_move except AttributeError: Logger.log_warning("Can't toggle movability of '{}.'".format(particle))
def post_run_stats(controller, sim): Logger.log("<----------------->") Logger.log("> Execution stats") Logger.log(">> Simulation lifespan: {} ticks".format(sim.lifetime)) avg_draw = Profiler.get_numeric_category_avg("draw_times") if avg_draw is not None: Logger.log(">> Avg. drawing time: {} ms".format(avg_draw * 1000)) avg_tick = Profiler.get_numeric_category_avg("tick_times") if avg_tick is not None: Logger.log(">> Avg. tick time: {} ms".format(avg_tick * 1000)) Logger.log("<----------------->")
def on_key_press(self, symbol, modifiers): # Handle key press # Mode switch if symbol in [key.M]: self.mode = ModeEnum((self.mode.value + 1) % len(list(ModeEnum))) Logger.log_custom("control", "Changed mode to {}.".format(self.mode)) if self.mode == ModeEnum.SELECT: if symbol == key.P: if self._cur_selected is None: controls.toggle_pause(self.sim) else: controls.toggle_pause(self._cur_selected) if symbol in [key.UP, key.DOWN ] and modifiers == 18: # 16 (base) + 2 (ctrl) controls.set_tick_speed(self, symbol) if symbol == key.H: if self._cur_selected is None: self._ui.visible = not self._ui.visible else: controls.toggle_debug_view(self._cur_selected) if symbol == key.T: if self._cur_selected is not None: controls.toggle_trail(self._cur_selected) if symbol == key.SPACE: if self._cur_selected is not None: controls.toggle_particle_movable(self._cur_selected) if symbol == key.S and modifiers == 18: # CRTL+S Logger.log_custom("control", "Saving current state...") persistence.save_to_json(self.sim) Logger.log_custom("control", "Finished saving.") if symbol == key.L and modifiers == 18: # CRTL+L Logger.log_custom("control", "Loading state...") persistence.load_from_json(self.sim) Logger.log_custom("control", "Finished loading.") if self.mode == ModeEnum.DESTROY: if symbol == key.BACKSPACE: controls.delete_particle(self._cur_selected) self._cur_selected = None if self.mode == ModeEnum.CREATE: # Cycling creatable types if symbol == key.LEFT: self.cur_creation_index = (self.cur_creation_index + 1) % len( self.creatable_types) Logger.log_custom( "control", "Changed creation type to {}.".format( self.creatable_types[self.cur_creation_index])) if symbol == key.RIGHT: self.cur_creation_index = ((self.cur_creation_index - 1) % len(self.creatable_types)) Logger.log_custom( "control", "Changed creation type to {}.".format( self.creatable_types[self.cur_creation_index]))
def create_particle(data): """ Accepts data and creates particle according to it. """ # Get particle type type_raw = None try: type_raw = data["type"] except KeyError as ke: Logger.log_warning("No particle type data. Can't spawn particle.") Logger.log_exception(ke) return None t = type_raw if type(type_raw) is str: # Get actual class reference t = get_particle_type(type_raw) if t is None: Logger.log_warning("Unrecognized particle type. Can't spawn particle.") return None # Construct particle p = None try: pos = data["pos"] p = t(pos[0], pos[1]) except KeyError as ke: Logger.log_warning("Incomplete particle data. Can't spawn particle.") Logger.log_exception(ke) return None # Set particle attributes try: p.mass = data["mass"] p.velocity = data["velocity"] p.size = data["size"] p._can_move = data["can_move"] p.paused = data["paused"] except KeyError as ke: Logger.log_info("Missing particle data.") Logger.log_exception(ke) return p