예제 #1
0
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()