Kivy Rotation resize ändert position

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Hallo,

ich habe ein rotiertes Rechteck, bei dem die Größe geändert wird.
Dadurch verändert sich aber auch die Position ein bisschen, das möchte ich aber nicht.

So ist es im moment: https://youtu.be/ofGYKKFGv7Q
und so soll es sein: https://youtu.be/ThvYjvCHamo (Ich habe da das center neu setzen deaktiviert, aber das soll natürlich neu gesetzt werden)

Ich glaube das liegt daran, das ich das center falsch setze, aber ich wüsste nicht, wie es richtig ist.

Das ist mein Code:

Code: Alles auswählen

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle, PushMatrix, Rotate, PopMatrix
from kivy.clock import Clock


class Canvas:
    def __init__(self, root, pos, size, rotation=0, color=(1, 0, 0, 1)):
        
        self._pos = pos
        self._size = size
        self._rotation = rotation
        self._color = color

        with root.canvas:
            PushMatrix()
            self._color_instruction = Color(rgba=self.color)
            self._rotation_instruction = Rotate(angle=self.rotation, origin=self.center())
            self._rectangle_instruction = Rectangle(pos=self.pos, size=self.size)
            PopMatrix()

        Clock.schedule_interval(self.step, 0)

    def step(self, dt):
        self.size= (self.size[0] + 5, self.size[1])

    def center(self):
        center_x = self.pos[0] + self.size[0]/2
        center_y= self.pos[1] + self.size[1]/2

        return (center_x, center_y)

    @property
    def pos(self):
        return self._pos

    @pos.setter
    def pos(self, value):
        self._pos = value
        self._rectangle_instruction.pos = self._pos

    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, value):
        self._size = value
        self._rectangle_instruction.size = self._size
        self._rotation_instruction.origin = self.center()

    @property
    def rotation(self):
        return self._rotation

    @rotation.setter
    def rotation(self, value):
        self._rotation = value
        self._rotation_instruction.angle= self._rotation
        self._rotation_instruction.origin = self.center()

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, value):
        self._color = value
        self._color_instruction.rgba = self._color


class Root(Widget):
    def __init__(self, **kwargs):
        super(Root, self).__init__(**kwargs)

        self.rectangle = Canvas(self, (100, 100), (100, 100), rotation=45)


class TestApp(App):
    def __init__(self, **kwargs):
        super(TestApp, self).__init__(**kwargs)

    def build(self):
        return Root()


def main():
    app = TestApp()
    app.run()


if __name__ == "__main__":
    main()

Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum speicherst Du die ganzen Informationen redundant? Das sollte man vermeiden.
Und wenn Du nicht willst, dass sich das Zentrum ändert, dann mußt Du beim Ändern der Größe auch die Position anpassen.

Code: Alles auswählen

class Canvas:
    def __init__(self, root, pos, size, rotation=0, color=(1, 0, 0, 1)):
        with root.canvas:
            PushMatrix()
            self._color_instruction = Color(rgba=color)
            self._rectangle_instruction = Rectangle(pos=pos, size=size)
            self._rotation_instruction = Rotate(angle=rotation, origin=self.center)
            PopMatrix()
        Clock.schedule_interval(self.step, 0)

    def step(self, dt):
        self.size = (self.size[0] + 5, self.size[1])

    @property
    def center(self):
        pos = self.pos
        size = self.size
        center_x = pos[0] + size[0] / 2
        center_y = pos[1] + size[1] / 2
        return (center_x, center_y)

    @property
    def pos(self):
        return self._rectangle_instruction.pos

    @pos.setter
    def pos(self, value):
        self._rectangle_instruction.pos = value

    @property
    def size(self):
        return self._rectangle_instruction.size

    @size.setter
    def size(self, new_size):
        old_pos = self._rectangle_instruction.pos
        old_size = self._rectangle_instruction.size
        new_pos = (old_pos[0] + (old_size[0] - new_size[0]) / 2,
                   old_pos[1] + (old_size[1] - new_size[1]) / 2)
        self._rectangle_instruction.pos = new_pos
        self._rectangle_instruction.size = new_size

    @property
    def rotation(self):
        return self._rotation_instruction.angle

    @rotation.setter
    def rotation(self, value):
        self._rotation_instruction.angle = rotation

    @property
    def color(self):
        return self._color_instruction.rgba

    @color.setter
    def color(self, value):
        self._color_instruction.rgba = value
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

- Warum soll man es vermeiden die Informationen redundant zu speichern?

- Ich möchte schon das sich das Zentrum ändert, es soll sich einfach nur nicht verschieben.
wie würde das funktionieren?
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

- Weil bei redundanten Informationen Du jeweils synchron an mehreren Stellen ändern mußt. Das ist sehr aufwändig, wie Du ja an Deinem Beispiel siehst. Und wenn man da einen Fehler macht, ist die Fehlersuche sehr schwierig.

- Diese beiden Aussagen widersprechen sich: das Zentrum soll sich ändern aber sich doch nicht ändern.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Also...

Ich möchte dass das Zentrum der Rotation dem Zentrum des Rechtecks entspricht, aber sich das Rechteck nicht bewegt.

So wie wenn ich das Rechteck zeichnen würde und dann weiter zeichne um es größer zu machen.
Da ändert sich die Position ja auch nicht (Natürlich änderst sie sich nicht und wahrscheinlich ist das auch ein blödes Beispiel mir fällt aber gerade kein besseres ein 😅)
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Also...

wie weicht jetzt meine Lösung von Deinen Wünschen ab?
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Vielleicht ist das gerade auch mein Fehler, ich schaue mir das später an.
hab gerade keine Zeit.

(Nur damit du nicht denkst das ich nicht mehr antworte :) )
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Also irgendetwas ist da ganz ganz komisch und es passieren Dinge, die ich nicht verstehe.

- bei der center property kommt immer 150, 150 als ergebnis. ich weiß nicht woran das liegt. wenn ich mir self.pos und self.size ausgeben lasse, passt noch alles.
wenn ich ich self.pos[0] + self.size[0] mache passt auch noch alles. sobald ich aber /2 hinzufüge kommt immer 150, 150. und das verstehe ich nicht so wirklich.

- ich wollte noch was mit center der rotation setzen ausprobieren (das ist das, was was bei deiner Lösung noch fehlt :))
allerdings kann ich das nicht, weil ja das mit dem center nicht funktioniert.
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Natürlich funktioniert das mit `center`. Wenn size setzt, ändert sich center nicht, das ist genau das Verhalten, das Du (soweit ich das verstanden habe) willst.
Wenn Du zusätzlich noch rotation.origin ändern willst, dann mußt Du halt dafür auch noch eine Funktion schreiben.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Ja, Funktioniert 👍
Das es nicht Funktioniert hat, war mein Fehler... 😅

Noch eine Frage:
Wie würde new_pos aussehen, wenn sich die Größe nur in eine Richtung verändern soll?
Momentan verändert sich das ja in beide Richtungen...
Das mit den 2 Richtugen ist aber perfekt so 😃
Würde das mit der einen Richtung nur gerne wissen, damit ich mal weiß wie es geht...
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Einfach 0, 0.5 oder 1 mal size abziehen.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Also das mit dem 0,5 funktioniert wie gesagt perfekt aber das *1 und *0 nicht :/
da bewegt es sich so wie oben erwähnt und und in dem Video oben gezeigt.
(weil wenn ich das *0 mache ist ja zu meinem Code am Anfang abgesehen davon das ich nichts mehr redundant speichere kein unterschied...)
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, was willst Du denn nun?
Wie soll sich denn das Rechteck bewegen. Mal sagst Du, Du möchtest, dass der linke Rand fix bleibt, dann wieder nicht?
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

also der linke Rand soll fix bleiben.
also *0, damit ist new_pos = old_pos und ich kann mir das komplett sparen...

wenn ich das mit *0,5 mache, also so, dass sich die Größe in beide Richtungen verändert funktioniert alles.
mache ich es aber so, dass der linke Rand fix bleibt, dann passiert das: https://youtu.be/ofGYKKFGv7Q, es verschiebt sich...
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Das ist das was passiert: https://youtu.be/Q4DkcBuptyM
und das hier ist mein Aktueller code:

Code: Alles auswählen

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle, PushMatrix, Rotate, PopMatrix
from kivy.clock import Clock
from kivy.core.window import Window



class Canvas:
    def __init__(self, root, pos, size, rotation=0, color=(1, 0, 0, 1)):
        with root.canvas:
            PushMatrix()
            self._color_instruction = Color(rgba=color)
            self._rotation_instruction = Rotate(angle=rotation, origin=(150, 150))
            self._rectangle_instruction = Rectangle(pos=pos, size=size)
            PopMatrix()

    def step(self, dt):
        self.size = (self.size[0] + 5, self.size[1])

    @property
    def center(self):
        pos = self.pos
        size = self.size
        center_x = pos[0] + size[0] / 2
        center_y = pos[1] + size[1] / 2
        return (center_x, center_y)

    @property
    def pos(self):
        return self._rectangle_instruction.pos

    @pos.setter
    def pos(self, value):
        self._rectangle_instruction.pos = value
        self._rotation_instruction.origin = self.center

    @property
    def size(self):
        return self._rectangle_instruction.size

    @size.setter
    def size(self, value):
        self._rectangle_instruction.size = value
        self._rotation_instruction.origin = self.center

    @property
    def rotation(self):
        return self._rotation_instruction.angle

    @rotation.setter
    def rotation(self, value):
        self._rotation_instruction.angle = value

    @property
    def color(self):
        return self._color_instruction.rgba

    @color.setter
    def color(self, value):
        self._color_instruction.rgba = value


class Root(Widget):
    def __init__(self, **kwargs):
        super(Root, self).__init__(**kwargs)

        self.rectangle = Canvas(self, (100, 100), (100, 100), rotation=45)

        self.keys_pressed = set()

        self._keyboard = Window.request_keyboard(self._on_keyboard_closed, self)
        self._keyboard.bind(on_key_down = self._on_key_down)
        self._keyboard.bind(on_key_up = self._on_key_up)

        self.focused = self.rectangle

        Clock.schedule_interval(self.step, 0)


    def step(self, dt):
        x = self.focused.pos[0]
        y = self.focused.pos[1]
        width = self.focused.size[0]
        height = self.focused.size[1]
        rotation = self.focused.rotation

        step_size = 300 * dt

        if "up" in self.keys_pressed:
            y += step_size
        if "left" in self.keys_pressed:
            x -= step_size
        if "right" in self.keys_pressed:
            x += step_size
        if "down" in self.keys_pressed:
            y -= step_size
        if "y" in self.keys_pressed:
            rotation += step_size
        if "x" in self.keys_pressed:
            rotation -= step_size
        if "w" in self.keys_pressed:
            height = min(height + step_size, 300)
        if "a" in self.keys_pressed:
            width = max(width - step_size, 10)
        if "d" in self.keys_pressed:
            width = min(width + step_size, 300)
        if "s" in self.keys_pressed:
            height = max(height - step_size, 10)

        self.focused.pos = (x, y)
        self.focused.size = (width, height)
        self.focused.rotation = rotation

    def _on_keyboard_closed(self):
        self._keyboard.unbind(_on_key_down = self._on_key_down)
        self._keyboard.ubind(_on_key_up = self._on_key_up)

        self._keyboard = None

    def _on_key_down(self, keyboard, keycode, text, modifiers):
        self.keys_pressed.add(text if text != None else keycode[1])

    def _on_key_up(self, keyboard, keycode):
        if keycode[1] in self.keys_pressed:
            self.keys_pressed.remove(keycode[1])


class TestApp(App):
    def __init__(self, **kwargs):
        super(TestApp, self).__init__(**kwargs)

    def build(self):
        return Root()


def main():
    app = TestApp()
    app.run()


if __name__ == "__main__":
    main()

Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Und wo ist jetzt die Frage? Du schreibst immer noch nicht, was Du eigentlich ganz genau haben möchtest.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Das soll sich nicht bewegen...
es soll einfach nur die Größe andern, sich aber nicht so verschieben.
mit

Code: Alles auswählen

new_pos = (old_pos[0] + (old_size[0] - new_size[0]) / 2, old_pos[1] + (old_size[1] - new_size[1]) / 2)
funktioniert es ja auch ohne, dass es sich verschiebt
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast 4 Größen: Position, Größe, Drehzentrum und Drehwinkel. Und wenn Du eine dieser Größen änderst, dann ändert sich das Rechteck. Wenn Du ein anderes Verhalten möchtest, mußt Du gleichzeitig eine der anderen Größen mit anpassen.

Was Du genau willst, hast Du immer noch nicht gesagt, von daher mußt Du Dir selbst die Gedanken machen.
MupfSpace
User
Beiträge: 169
Registriert: Montag 25. Dezember 2017, 20:26

Ich habe eigentlich schon gesagt, wie ich das haben will aber wahrscheinlich habe ich das unverständlich gesagt...
sorry 😕

es soll so sein wie hier: https://youtu.be/PXUoib5Yl8w (in diesem Video habe ich das center setzen deaktiviert)
Allerdings soll das center gesetzt werden.

Meine Frage: wie mache ich das

Code: >siehe vorherige beiträge<
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Du willst also, dass die untere Ecke sich nicht bewegt. Dann mußt Du die Position der untere Ecke des Rechtecks relativ zum umschreibenden Rechteck berechnen.
Dafür brauchst Du, wie schon im letzten Beitrag geschrieben, die Position, Größe und Winkel, und etwas Mathematik, also Sinus und Cosinus.
Antworten