class Timeline(AppElementSingle): def __init__(self): self.current_frame = 1 self.start_frame = 1 self.end_frame = 2 self.time_last = -1 self.frame_rate = AppSettings.standard_fps self.playing = False # ------------------------------------------------------------------------ # # App element default def setup_ui(self): x = 30 frame_range = (self.start_frame, self.end_frame) self.fps = Widgets.int_roller(self.p, self.frame_rate, self.update_frame_rate, AppSettings.fps_range, xy=(x, 2)) x += 52 Widgets.button(self.p, "<-", self.play_backward, (x, 0)) x += 90 Widgets.button(self.p, "||", self.play_stop, (x, 0)) x += 90 Widgets.button(self.p, "->", self.play_forward, (x, 0)) x += 100 self.frame_roller = Widgets.int_roller( self.p, self.current_frame, self.set_current_frame, frame_range, xy=(x, 2) ) x += 50 self.ticker = Widgets.ticker(self.p, self.tick) self.update_frame_rate() def keyPressEvent(self, e): if self.p.query_key("-", e.key()): self.tweak(-1) if self.p.query_key("=", e.key()): self.tweak(+1) def init_panel(self): self.p = Panel(AppSettings.timeline_size) self.p.draw = self.draw self.p.setup_ui = self.setup_ui self.p.init() # turns off default IO self.p.mouseReleaseEvent = self.empty self.p.mouseMoveEvent = self.empty self.p.wheelEvent = self.empty self.p.keyPressEvent = self.keyPressEvent return self.p def draw(self, d): sd = -AppSettings.timeline_size[0] * 0.4 ed = +AppSettings.timeline_size[0] * 0.4 hh = AppSettings.timeline_size[1] * 0.1 sf = self.start_frame ef = self.end_frame def lerp_drawing_range(frame): t = (frame - sf) / float(ef - sf) return sd + t * (ed - sd) # Timeline graph d.fill((0, 0, 0), a=0) d.stroke(Colors.null, a=125) d.line((sd - 10, 0), (ed + 10, 0)) for frame in range(sf, ef + 1): x = lerp_drawing_range(frame) d.line((x, -hh), (x, +hh)) # Current time marker d.fill(Colors.time_marker, a=200) d.stroke((0, 0, 0), a=0) x = lerp_drawing_range(self.current_frame) d.box((x, 0), (10, hh * 2)) # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Widget def tick(self, *args): self.current_frame += 1 * self.playing self.bound_current_frame() self.frame_roller.setValue(self.current_frame) Scene().evaluate_frame(self.current_frame) Animation.evaluate_frame(self.current_frame) self.p.update() # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Menu def play_forward(self): self.playing = 1 def play_stop(self): self.playing = 0 def play_backward(self): self.playing = -1 def update_frame_rate(self): self.frame_rate = self.fps.value() self.ticker.start(1000.0 / self.frame_rate) # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Playback interface def set_frame_range(self, s, f): self.start_frame = s self.end_frame = f self.n_frames = f - s + 1 self.frame_roller.setMinimum(s) self.frame_roller.setMaximum(f) def set_current_frame(self, frame): self.current_frame = frame self.bound_current_frame() Scene().evaluate_frame(self.current_frame) self.p.update() def bound_current_frame(self): if self.current_frame > self.end_frame: self.current_frame = self.start_frame if self.current_frame < self.start_frame: self.current_frame = self.end_frame def tweak(self, change): self.current_frame += change self.bound_current_frame() Scene().evaluate_frame(self.current_frame) self.p.update()
class Animation(AppElementSpawnable): # ------------------------------------------------------------------------ # # Functions that will update all instances (useful for update ticks) @staticmethod def evaluate_frame(frame): for inst in Animation._instances: inst.p.update() @staticmethod def mocap_reset(): for inst in Animation._instances: inst.update_motion_menu() def update_motion_menu(self): m = [(name, self.set_motion_curve_fn(name), False, False) for name in MocapHandle().get_names()] self.motion_menu.setMenu(Widgets.menu(self.p, m)) # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # -- def __init__(self, ref_toon, ref_name): # Motion self.ref_toon = ref_toon self.ref_name = ref_name self.mc_handle = "" # Algorithms self.params = Graph.params self.preprocess_curve = [] self.keyframes = [] self.splines = [] # Metrics self.metric_map = MetricMap() # Display, and other self.image_save_index = 0 self.display = Graph.draw_type[Graph.draw_type_default] def setup_ui(self): x = 10 inc = 100 self.joint_display_name = Widgets.label(self.p, self.ref_name, (x, 4)) self.mc_handle_label = Widgets.label(self.p, self.mc_handle, (x, 24)) x += 50 # -------------------------------------------------------------------- # # Menus m = [("None", self.empty, False, False)] self.motion_menu = Widgets.button_menu(self.p, "Motion", m, (x, 0)) self.update_motion_menu() x += inc # Application of curve m = [ ("Clear", self.clear_motion, False, False), ("tx", self.apply_curve_fn("tx"), False, False), ("ty", self.apply_curve_fn("ty"), False, False), ("rz", self.apply_curve_fn("rz"), False, False), ] Widgets.button_menu(self.p, "Apply", m, (x, 0)) x += inc # Grepping curvers from existing toon m = [ ("tx", self.grep_curve_fn("tx"), False, False), ("ty", self.grep_curve_fn("ty"), False, False), ("rz", self.grep_curve_fn("rz"), False, False), ] Widgets.button_menu(self.p, "Grep", m, (x, 0)) x += inc # Display menu m = [(key, self.toggle_display_fn(key), True, self.display[key]) for key in self.display.keys()] Widgets.button_menu(self.p, "Display", m, (x, 0)) x += inc # Algorithms menu m = [ ("Subdivide", self.subdivide, False, False), ("Douglas Peucker", self.douglas_peucker, False, False), ("Salient Points 1D", self.salient_points_1d, False, False), ("Salient Points FT", self.salient_points_ft, False, False), ("Salient Points it", self.salient_points_iter, False, False), ("Polyline", self.polyline, False, False), ("Flat tangents", self.flat_tangents, False, False), ("One-pass", self.cubic, False, False), ("Reset", self.reset_simplification, False, False), ("Save Graph", self.save_graph, False, False), ] Widgets.button_menu(self.p, "Simplify", m, (x, 0)) x += inc # # -------------------------------------------------------------------- # # -------------------------------------------------------------------- # # Sliders x = 100 y = AppSettings.anim_size[3] - 150 inc = 20 self.sliders = {} for key in self.params.keys(): if key != "": slider = Widgets.slider( self.p, key, self.update_slider_fn(key), mm=(1, 20), xy=(x, y), v=self.params[key] ) self.sliders[key] = slider y += inc # # -------------------------------------------------------------------- # # -------------------------------------------------------------------- # # Metric map x = AppSettings.anim_size[2] - 100 y = AppSettings.anim_size[3] * 0.5 m = self.metric_map.m self.metric_map_handle = {} for key in m.keys(): inner_m = m[key] l = Widgets.label(self.p, key, xy=(x - 50, y)) y += 20 self.metric_map_handle[key] = {} for inner_key in inner_m: l = Widgets.label(self.p, inner_key, xy=(x, y)) y += 20 self.metric_map_handle[key][inner_key] = l # # -------------------------------------------------------------------- # # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Key map def keyPressEvent(self, e): # Switch focus to current selection if self.p.query_key("e", e.key()): index, joint = Scene().get_selected() self.ref_toon = index self.ref_name = joint.name self.joint_display_name.setText(self.ref_name) # Fast save graph if self.p.query_key("s", e.key()): self.image_save_index += 1 index = self.image_save_index self.save_graph("%s/%s" % (AppSettings.default_graph_save_dir, "fast_save_" + str(index)) + ".svg") # Reset fast index if self.p.query_key("q", e.key()): self.image_save_index = 0 # Auto displays display_presets = ["1", "2", "3", "4", "5", "6"] for key in display_presets: if self.p.query_key(key, e.key()): self.toggle_display_type(key) # Algorithm hotkeys if self.p.query_key("y", e.key()): self.subdivide() if self.p.query_key("u", e.key()): self.douglas_peucker() if self.p.query_key("i", e.key()): self.salient_points_1d() if self.p.query_key("o", e.key()): self.salient_points_ft() if self.p.query_key("p", e.key()): self.salient_points_iter() if self.p.query_key("j", e.key()): self.cubic() if self.p.query_key("k", e.key()): self.flat_tangents() if self.p.query_key("l", e.key()): self.polyline() # Camera controls if self.p.query_key("x", e.key()): self.p.toggle_camera_lock() if self.p.query_key("c", e.key()): self.p.store_camera() if self.p.query_key("v", e.key()): self.p.recover_camera() # Quit hotkey if self.p.query_key("esc", e.key()): self.p.window_obj.close() self.p.update() # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Panel stuff def init_panel(self): self.p = Panel((AppSettings.anim_size[2], AppSettings.anim_size[3])) self.p.draw = self.draw self.p.setup_ui = self.setup_ui self.p.keyPressEvent = self.keyPressEvent self.p.init() return self.p def draw(self, d): sd = -AppSettings.anim_size[2] * 0.4 ed = +AppSettings.anim_size[2] * 0.4 sh = -AppSettings.anim_size[3] * 0.4 eh = +AppSettings.anim_size[3] * 0.4 s = Timeline().start_frame f = Timeline().end_frame def lerp_over_drawing(p_i): x, y = p_i t = (x - s) / float(f - s) return sd + t * (ed - sd) def y_scaling(p_i): x, y = p_i return y * self.params["y"] def curve_as_points(color, curve, offset=0.0, sizable=False): s = 1.0 if sizable: s = self.params["mocap"] d.fill(color, a=125) d.stroke((0, 0, 0), a=0) for i in range(len(curve)): x = lerp_over_drawing(curve[i]) y = y_scaling(curve[i]) + offset d.circle((x, y), s) def curve_as_polyline(color, curve): d.fill((0, 0, 0), a=0) d.stroke(color, a=125) for i in range(len(curve)): x1 = lerp_over_drawing(curve[i - 1]) x2 = lerp_over_drawing(curve[i]) y1 = y_scaling(curve[i - 2]) y2 = y_scaling(curve[i - 1]) d.line((x1, y1), (x2, y2)) def curve_as_steps(color, curve): d.fill((0, 0, 0), a=0) d.stroke(color, a=125) for i in range(len(curve)): x1 = lerp_over_drawing(curve[i - 1]) x2 = lerp_over_drawing(curve[i]) y1 = y_scaling(curve[i - 2]) y2 = y_scaling(curve[i - 1]) d.line((x1, y1), (x2, y1)) d.line((x2, y1), (x2, y2)) def curve_as_tangents(color, curve, tvs): d.fill((0, 0, 0), a=0) d.stroke(color, a=125) for i in range(len(curve)): tv = curve[i] + tvs[i] x1 = lerp_over_drawing(curve[i]) y1 = y_scaling(curve[i]) x2 = lerp_over_drawing(tv) y2 = y_scaling(tv) dv = np.array([x2 - x1, y2 - y1]) dv = dv / np.linalg.norm(dv) * self.params["vector_size"] x2 = x1 + dv[0] y2 = y1 + dv[1] d.line((x1, y1), (x2, y2)) def curve_as_normals(color, curve, tvs): d.fill((0, 0, 0), a=0) d.stroke(color, a=125) for i in range(len(curve)): tv = curve[i] + tvs[i] x1 = lerp_over_drawing(curve[i]) y1 = y_scaling(curve[i]) x2 = lerp_over_drawing(tv) y2 = y_scaling(tv) dv = np.array([x2 - x1, y2 - y1]) dv = dv / np.linalg.norm(dv) * self.params["vector_size"] x2 = x1 - dv[1] y2 = y1 + dv[0] d.line((x1, y1), (x2, y2)) def keyframes_as_dots(color, curve, keyframes): d.fill((0, 0, 0), a=0) d.stroke(color, a=255) d.fill(color, a=255) d.stroke((0, 0, 0), a=0) for k in keyframes: x = lerp_over_drawing(curve[k]) y = y_scaling(curve[k]) d.circle((x, y), 3) def spline_as_curves(color, interps): d.fill(color, a=255) d.stroke((0, 0, 0), a=0) for interp in interps: x = lerp_over_drawing(interp) y = y_scaling(interp) d.circle((x, y), 2) def spline_as_cubic(color, controls): d.fill((0, 0, 0), a=0) d.stroke(color, a=255) controls_scaled = [[lerp_over_drawing(c), y_scaling(c)] for c in controls] d.bezier(controls_scaled) def spline_as_handles(color, controls): controls_scaled = [[lerp_over_drawing(c), y_scaling(c)] for c in controls] p0, p1, p2, p3 = controls_scaled d.fill(color, a=255) d.stroke(color, a=255) d.line(p0, p1) d.line(p2, p3) d.stroke((0, 0, 0), a=0) d.box(p1, (5, 5)) d.box(p2, (5, 5)) if self.display["axis"]: d.fill((0, 0, 0), a=0) d.stroke(Colors.null, a=125) x = AppSettings.anim_size[2] * 0.5 d.line((-x, 0), (+x, 0)) x = lerp_over_drawing(s) d.line((x, sh), (x, eh)) x = lerp_over_drawing(f) d.line((x, sh), (x, eh)) if self.display["time"]: d.fill((0, 0, 0), a=0) d.stroke(Colors.time_marker, a=125) x = lerp_over_drawing(Timeline().current_frame) d.line((x, sh), (x, eh)) # Motion curves if self.mc_handle != "": curve = MocapHandle().get_curve(self.mc_handle) if self.display["mocap_dots"]: curve_as_points(Colors.mocap, curve, sizable=True) if self.display["mocap_lines"]: curve_as_polyline(Colors.mocap, curve) if self.display["mocap_steps"]: curve_as_steps(Colors.mocap, curve) if self.display["preprocess"] and self.preprocess_curve != []: curve_as_points(Colors.preprocess, self.preprocess_curve) if self.display["tangents"] and self.preprocess_curve != []: tvs = TangentApprox.from_motion_curve(self.preprocess_curve) curve_as_tangents(Colors.tangents, self.preprocess_curve, tvs) if self.display["normals"] and self.preprocess_curve != []: tvs = TangentApprox.from_motion_curve(self.preprocess_curve) curve_as_normals(Colors.normals, self.preprocess_curve, tvs) if self.display["keyframes"]: keyframes_as_dots(Colors.keyframes, self.preprocess_curve, self.keyframes) if self.display["splines"]: for spline in self.splines: spline_as_cubic(Colors.splines, spline.list_controls()) if self.display["handles"]: for spline in self.splines: spline_as_handles(Colors.handles, spline.list_controls()) # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Menu callbacks def save_meta_data(self, filepath): curve = MocapHandle().get_curve(self.mc_handle) keyframes = self.keyframes spline_handles = [s.list_controls()[1:3] for s in self.splines] meta_data = { "motion": { "file": MocapHandle().last_loaded, "name": self.ref_name, "curve_key": self.mc_handle, "curve": curve.tolist(), }, "simplify": {"keyframes": keyframes, "splines": spline_handles}, "metric": self.metric_map.m, } f = open(filepath, "w") f.write(json.dumps(meta_data, sort_keys=True, indent=4, separators=(",", ": "))) f.close() def save_graph(self, filepath=""): if filepath == "": filepath = Dialogs.save_file(self.p, AppSettings.demuddle_dir)[0] data_fp = filepath + ".meta.json" self.p.paintImage(filepath) self.save_meta_data(data_fp) def set_motion_curve_fn(self, name): def fn(): self.mc_handle = name self.mc_handle_label.setText(self.mc_handle) return fn def clear_motion(self): # TODO implement this # Scene().command_delete_motion(self.ref_toon, self.ref_name) self.p.update() def apply_curve_fn(self, target): def fn(): Scene().command_add_motion(self.ref_toon, self.ref_name, target, self.mc_handle) self.p.update() return fn def grep_curve_fn(self, target): def fn(): mobj = Scene().command_get_motion(self.ref_toon, self.ref_name, target) if mobj != None: self.mc_handle = "%s, %s" % (mobj.ref_name, mobj.ref_dof) self.mc_handle_label.setText(self.mc_handle) self.p.update() return fn def update_slider_fn(self, key): def fn(v): self.params[key] = v return fn # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Simplification def subdivide(self): curve = MocapHandle().get_curve(self.mc_handle) preprocess = Subdivide() preprocess.update_curve(curve) preprocess.update(self.params) self.preprocess_iters = preprocess.get_iters() self.preprocess_curve = preprocess.get_result() Scene().set_status("Curve preproccesed (Subdivision)", "normal") self.p.update() def douglas_peucker(self): curve = self.preprocess_curve keyframer = DouglasPeucker() keyframer.update_curve(curve) keyframer.update(self.params) self.keyframes = keyframer.get_keyframes() Scene().set_status("Keyframe selected (Douglas Peucker)", "normal") self.p.update() def salient_points_1d(self): curve = self.preprocess_curve keyframer = SalientPoints1D() keyframer.update_curve(curve) keyframer.learn() self.keyframer = keyframer Scene().set_status("zerotable (Salient Points, 1D)", "normal") self.p.update() def salient_points_ft(self): curve = self.preprocess_curve keyframer = SalientPointsFlatTangents() keyframer.update_curve(curve) keyframer.learn() self.keyframer = keyframer Scene().set_status("zerotable (Salient Points, FT)", "normal") self.p.update() def salient_points_iter(self): self.keyframer.update(self.params) self.keyframes = self.keyframer.get_keyframes() Scene().set_status("Keyframe selected (Salient Points, iter)", "normal") self.p.update() def polyline(self): curve = self.preprocess_curve keyframes = self.keyframes segments = zip(keyframes[:-1], keyframes[1:]) self.splines = [] for (k1, k2) in segments: spline_obj = PolyLine() spline_obj.update_curve(curve[k1 : k2 + 1]) spline_obj.update(self.params) spline_obj.run() self.splines.append(spline_obj) self.update_metric_map() Scene().set_status("Interpolated (polyline)", "normal") self.p.update() def flat_tangents(self): curve = self.preprocess_curve keyframes = self.keyframes segments = zip(keyframes[:-1], keyframes[1:]) self.splines = [] for (k1, k2) in segments: spline_obj = FlatTangent() spline_obj.update_curve(curve[k1 : k2 + 1]) spline_obj.update(self.params) spline_obj.run() self.splines.append(spline_obj) self.update_metric_map() Scene().set_status("Interpolated (flat spline)", "normal") self.p.update() def cubic(self): curve = self.preprocess_curve keyframes = self.keyframes segments = zip(keyframes[:-1], keyframes[1:]) self.splines = [] for (k1, k2) in segments: spline_obj = OnePass() spline_obj.update_curve(curve[k1 : k2 + 1]) spline_obj.update(self.params) spline_obj.run() self.splines.append(spline_obj) self.update_metric_map() Scene().set_status("Interpolated (cubic spline)", "normal") self.p.update() def reset_simplification(self): self.mc_handle = "" self.preprocess_curve = [] self.keyframes = [] self.splines = [] Scene().set_status("keyframes deleted", "normal") self.p.update() # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Metrics def update_metric_map(self): # Make a curve from spline pieces approx = [] approx_length = 0 for spline in self.splines: approx += spline.interps_nearest()[:-1] approx_length += spline.get_length() approx += [self.splines[-1].interps_nearest()[-1]] # Measure errors a = [v for v in self.preprocess_curve] b = [v for v in approx] self.metric_map.m["distance"]["normOne"] = Distance.normOne(a, b) self.metric_map.m["distance"]["normTwo"] = Distance.normTwo(a, b) self.metric_map.m["distance"]["normInf"] = Distance.normInf(a, b) self.metric_map.m["distance"]["meanSqu"] = Distance.meanSqu(a, b) # Measure lengths self.metric_map.m["length"]["original"] = 0 # TODO implement data length self.metric_map.m["length"]["simplified"] = approx_length # Record numbers of critical points c_org = len(Critical.local_minima(a) + Critical.local_maxima(a)) c_simp = len(Critical.local_minima(b) + Critical.local_maxima(b)) self.metric_map.m["critical"]["original"] = c_org self.metric_map.m["critical"]["simplified"] = c_simp # Save data to map m = self.metric_map_handle for key in m.keys(): inner_m = m[key] for inner_key in inner_m: v = self.metric_map.m[key][inner_key] inner_m[inner_key].setText("%s, %2.2f" % (inner_key, v)) # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Display stuff def toggle_display_fn(self, item): def fn(*args): self.display[item] = self.display[item] == False self.p.update() return fn def toggle_display_type(self, index): self.display = Graph.draw_type[index] self.p.update()
class Scene(AppElementSingle): # ------------------------------------------------------------------------ # # -- def __init__(self): self.toons = [] self.toon_filepaths = [] self.display = {"axis": True} def setup_ui(self): w = 0 menu = [ ("Load Scene", self.load_scene, False, False), ("Save Scene", self.save_scene, False, False), ("Load Mocap", self.load_mocap, False, False), ] Widgets.button_menu(self.p, "File", menu, (w, 0)) w += 100 menu = [ ("Load", self.load_toon, False, False), ("Examine", self.examine_toon, False, False), ("Offset", self.rotate_toon, False, False), ("Scale", self.scale_toon, False, False), ] Widgets.button_menu(self.p, "Character", menu, (w, 0)) w += 120 menu = [("Axis", self.toggle_display_fn("axis"), True, True)] Widgets.button_menu(self.p, "Display", menu, (w, 0)) w += 120 self.status_bar = Widgets.label( self.p, " Status", (10, AppSettings.scene_size[1] - 20), w=AppSettings.scene_size[0] - 20 ) self.set_status("loaded %s" % self.toon_filepaths, "normal") def mousePressEvent(self, e): if self.p.query_button(2, e.buttons()): self.command_select(e.x(), e.y()) def keyPressEvent(self, e): # Character hotkeys if self.p.query_key("left", e.key()): self.command_modify(Joint.Move_L) if self.p.query_key("right", e.key()): self.command_modify(Joint.Move_R) if self.p.query_key("down", e.key()): self.command_modify(Joint.Move_D) if self.p.query_key("up", e.key()): self.command_modify(Joint.Move_U) if self.p.query_key("a", e.key()): self.command_modify(Joint.Rot_L) if self.p.query_key("d", e.key()): self.command_modify(Joint.Rot_R) # Interface if self.p.query_key("e", e.key()): self.examine_toon() if self.p.query_key("esc", e.key()): Executable.quit() if self.p.query_key("-", e.key()): Timeline().tweak(-1) if self.p.query_key("=", e.key()): Timeline().tweak(+1) # Camera if self.p.query_key("x", e.key()): self.p.toggle_camera_lock() if self.p.query_key("c", e.key()): self.p.store_camera() if self.p.query_key("v", e.key()): self.p.recover_camera() def init_panel(self): self.p = Panel(AppSettings.scene_size) self.p.draw = self.draw self.p.setup_ui = self.setup_ui self.p.init() self.p.mousePressEvent = self.mousePressEvent self.p.keyPressEvent = self.keyPressEvent return self.p def draw(self, d): if self.display["axis"]: d.axis() for t in self.toons: t.draw(d) # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Toon API def add_toon_from_file(self, filepath): f = open(filepath, "r") toon = json.loads(f.read()) f.close() t = Toon() for joint in toon["joints"]: joint_obj = t.joint_add(joint["name"], joint["parent"], joint["type"], joint["length"], joint["flip"]) joint_obj.rotation_offset = joint["offset"] joint_obj.pos_scaling = joint["pos_s"] # Update cache and scene self.toon_filepaths.append(filepath) self.toons.append(t) self.p.update() def load_toon(self): filepath = Dialogs.open_file(self.p, AppSettings.toon_dir)[0] if os.path.isfile(filepath): self.add_toon_from_file(filepath) else: Scene().set_status("File selected is not valid", "error") def examine_toon(self): toon_index, selected = self.get_selected() if selected != None: Executable.spawn_window("Motion", AppSettings.anim_size, Animation(toon_index, selected.name).init_panel()) def rotate_toon(self): for t in self.toons: if t.selection_active(): v = Dialogs.int(self.p, "Rotate", "degrees anticlockwise") if v != None: t.modify_rotation_offset(v) def scale_toon(self): for t in self.toons: if t.joint_selected() != None: v = Dialogs.float(self.p, "Scale", "scale character") if v != None: t.modify_position_scaling(v) def get_selected(self): for i in range(len(self.toons)): t = self.toons[i] if t.joint_selected() != None: return i, t.joint_selected() return -1, None def curve_data_on_selected(self): for t in self.toons: if t.selection_active(): return t.sele.get_motion_info() return None def command_select(self, x, y): already_found = False for t in self.toons: if t.selection_cast(x, y, already_found): already_found = True self.p.update() def command_modify(self, t_type): for t in self.toons: t.modify_interactive(t_type) self.p.update() def command_add_motion(self, character, name, target, curve_key): mobj = MocapHandle().generate_motion_obj(curve_key) joint_obj = self.toons[character].joint_by_name(name) joint_obj.add_retarget(target, mobj) self.p.update() def command_get_motion(self, character, name, target): joint_obj = self.toons[character].joint_by_name(name) if target in joint_obj.retargets: return joint_obj.retargets[target].motion else: Scene().set_status("Target not found in joint", "warning") return None def command_delete_motion(self, character, name): joint_obj = self.toons[character].joint_by_name(name) joint_obj.clear_retargets() self.p.update() def evaluate_frame(self, frame): for t in self.toons: t.evaluate_frame(frame) self.p.update() # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Loading and saving def load_mocap(self): MocapHandle().load_mocap() def save_scene(self): # cache character info and motions character_motion_maps = [] character_infos = [] for t in self.toons: motion_maps = {} character_info = {} for joint in t.joint_list(): motion_maps[joint.name] = joint.generate_map_from_retargets() character_info[joint.name] = joint.get_info() character_motion_maps.append(motion_maps) character_infos.append(character_info) # organize into scene file scene_file = { "mocap": MocapHandle().last_loaded, "character_filepaths": self.toon_filepaths, "character_info": character_infos, "character_motion_maps": character_motion_maps, } # save filepath = Dialogs.save_file(self.p, AppSettings.save_dir)[0] f = open(filepath, "w") f.write(json.dumps(scene_file, sort_keys=True, indent=4, separators=(",", ": "))) f.close() def load_scene(self, filepath=""): if filepath == "": filepath = Dialogs.open_file(self.p, AppSettings.save_dir)[0] if not os.path.isfile(filepath): Scene().set_status("file selected is not valid.", "warning") return # open f = open(filepath, "r") scene_file = json.loads(f.read()) f.close() # Load mocap (if it was there) if scene_file["mocap"] != "": MocapHandle().load_mocap("%s/%s" % (AppSettings.demuddle_dir, scene_file["mocap"])) # Load characters. for fp in scene_file["character_filepaths"]: self.add_toon_from_file("%s/%s" % (AppSettings.demuddle_dir, fp)) # Apply info and motion onto characters cindex = 0 for cindex in range(len(scene_file["character_motion_maps"])): motion_maps = scene_file["character_motion_maps"][cindex] character_infos = scene_file["character_info"][cindex] for joint_name in motion_maps.keys(): character_info = character_infos[joint_name] motion_map = motion_maps[joint_name] joint_obj = self.toons[cindex].joint_by_name(joint_name) joint_obj.set_info(character_info) joint_obj.generate_retargets_from_map(motion_map) cindex += 1 self.p.update() # # ------------------------------------------------------------------------ # # ------------------------------------------------------------------------ # # Other def toggle_display_fn(self, item): def fn(*args): self.display[item] = self.display[item] == False self.p.update() return fn def set_status(self, message, message_type): self.status_bar.setText(" Status: %s" % message) style = "" if message_type == "normal": style = "QLabel { background-color : #1573BD; color : white; }" elif message_type == "warning": style = "QLabel { background-color : #F6AC41; color : white; }" elif message_type == "error": stlye = "QLabel { background-color : #DE3B3C; color : white; }" else: pass self.status_bar.setStyleSheet(style)