ManimCommunity / manim

A community-maintained Python framework for creating mathematical animations.
https://www.manim.community
MIT License
26.59k stars 1.82k forks source link

Select SVG shapes by id #3941

Open tobiasBora opened 1 month ago

tobiasBora commented 1 month ago

Description of proposed feature

In the SVG format, it is possible to add an id attribute on a shape/group of shapes. It would be great to allow manim to get the list of sub-objects corresponding to a given id (for now get_mobjects_from will not be stable if later-on I add elements to my object).

How can the new feature be used?

I could do a complicated drawing in inkscape, and animate it in manim, for instance by hiding/showing/moving elements based on their ID etc.

uwezi commented 1 month ago

you can already now address all separate paths and features from an SVG file by its position in the file - they all get an index in the list of sub-mobjects depending on their ordering in the SVG file. But yes, this is easily confused when you later modify the file.

tobiasBora commented 1 month ago

Yes exactly. And I guess it is not obvious how to find the good element in a huge list.

uwezi commented 1 month ago

My approach:

bild

class index(Scene):
    def construct(self):
        for x in range(-7, 8):
            for y in range(-4, 5):
                if (x==0) and (y==0):
                    self.add(Dot(np.array([x, y, 0]),radius=0.03, color=RED))
                else:
                    self.add(Dot(np.array([x, y, 0]),radius=0.03, color=DARK_GREY))

        circuit = SVGMobject(r"bilder/20240924_rinrut_03.svg",
            stroke_color=WHITE, stroke_width=3, fill_color=WHITE,width=10)

        self.add(circuit)
        #self.wait(2)

        bbox = Rectangle(height=circuit.height, width=circuit.width).move_to(circuit.get_center(),ORIGIN)
        bbox.scale(1.4)
        loc = bbox.get_critical_point(UL)
        w = bbox.width
        h = bbox.height
        cf = 2*w + 2*h
        dl = cf / (len(circuit)+3)

        dir   = [dl,0,0]
        edge = 0
        positions = []
        for i in range(len(circuit)):
            positions.append(loc)
            loc = loc + dir
            if (edge == 0) and (loc[0] > bbox.get_critical_point(UP+RIGHT)[0]):
                edge = 1
                loc = loc - dir
                dir = [0,-dl,0]
                loc = loc + dir

            if (edge == 1) and (loc[1] < bbox.get_critical_point(DOWN+RIGHT)[1]):
                edge = 2
                loc = loc - dir
                dir = [-dl,0,0]
                loc = loc + dir

            if (edge == 2) and (loc[0] < bbox.get_critical_point(DOWN+LEFT)[0]):
                edge = 3
                loc = loc - dir
                dir = [0,+dl,0]
                loc = loc + dir

        for i in range(len(circuit)):
            shortest = 1e6
            found = 0
            for j in range(len(positions)):
                dist = np.sqrt((circuit[i].get_center()[0]-positions[j][0])**2 + (circuit[i].get_center()[1]-positions[j][1])**2)
                if dist < shortest:
                    shortest = dist
                    found = j

            color = [BLUE,GREEN,RED,YELLOW,PURPLE][i%5]
            circuit[i].set_color(color)
            txt = Text("{}".format(i)).scale(0.15).move_to(positions[found]).set_color(color)

            line = Line(circuit[i].get_center(),end=txt.get_center(), stroke_width=1).set_color(color)

            self.add(line)
            self.add(txt)
            # self.wait(1)

            positions.pop(found)
tobiasBora commented 1 month ago

Ahah fun workaround to identify the element on medium-sized picture, thanks. Another advantage of id also is that it would (hopefully) also work for groups, so that I can refer to hundred elements at once using a single name.