Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Oberon

Treue Seele

  • »Oberon« ist der Autor dieses Themas

Beiträge: 181

Wohnort: Österreich

Beruf: Student

  • Private Nachricht senden

1

19.12.2010, 12:41

Bounding Box bei gedrehtem Objekt mit verschobenem Ursprung

Mein Problem ist mathematischer Natur:
Ich programmiere mit der SFML (2) und möchte die Bounding-Box(d.h. das umschließende Rechteck) eines Objekts/Sprites berechnen. Bei einem ungedrehtem Objekt ist das ja trivial, und auch ein gedrehtes habe ich noch geschafft.
Wenn ich allerdings SetOrigin aufrufe (mit Werten ungleich 0 natürlich) funktioniert es nicht mehr richtig: Ich bin ziemlich sicher das Höhe und Breite noch stimmen, aber der linke obere Punkt stimmt nicht mehr. Je weiter ich, das Sprite drehe, desto mehr wandert der wahre Linksoben-Punkt in Richtung meines berechneten Rechtsunten-Punktes, bis die beiden Punkte schließlich völlig vertauscht sind (bei 180° und Ursprung im Mittelpunkt des Bildes, je näher der Ursprung bei der linken oberen Ecke des Objektes (0|0) ist, desto "richtiger" wird die Bounding-Box).
Das ist mein bisheriger Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
static const float pi = 3.14159265;

inline float rad(float degval)
{
    return degval * pi / 180;
}

inline float deg(float degval)
{
    return degval * 180 / pi;
}

template <typename T>
inline T abs(sf::Vector2<T> v)
{
    return sqrt(v.x * v.x + v.y * v.y);
}

float get_rotation(sf::Vector2f p, sf::Vector2f o)
{
    const sf::Vector2f op = p - o;
    if ((op.x == 0 && op.y == 0))
        return 0.f;
    static const sf::Vector2f zero_rotation(1, 0);
    return acos((op.x * zero_rotation.x + op.y * zero_rotation.y) / (abs(op) * abs(zero_rotation)));
}

const sf::Vector2f set_point_rotation(sf::Vector2f p, sf::Vector2f o, float angle)
{
    const sf::Vector2f op = p - o;
    if ((op.x == 0 && op.y == 0))
        return p;
    const float r = abs(op);
    const sf::Vector2f zero_rotation(r, 0);
    const float rotation_rad = get_rotation(p, o);
    p.x = o.x + r * cos(rad(angle) - rotation_rad);
    p.y = o.y - r * sin(rad(angle) - rotation_rad);
    return p;
}

float min4(float a, float b, float c, float d)
{
    return std::min(std::min(a, b), std::min(c, d));
}

float max4(float a, float b, float c, float d)
{
    return std::max(std::max(a, b), std::max(c, d));
}

void TestExecutable::set_rotation(const int angle)
{
    m_sprite.SetRotation(angle);
    const float alpha = m_sprite.GetRotation();

    // A+----------+D
    //  |          |
    //  |          |
    // B+----------+C
    const sf::Vector2f A = m_sprite.GetPosition();
    sf::Vector2f C = m_sprite.GetPosition() + m_sprite.GetSize(); 
    sf::Vector2f B  (A.x, C.y);
    sf::Vector2f D  (C.x, A.y);
    
    B = set_point_rotation(B, A, alpha);
    C = set_point_rotation(C, A, alpha);
    D = set_point_rotation(D, A, alpha);

    const sf::Vector2f lt(min4(A.x, B.x, C.x, D.x), min4(A.y, B.y, C.y, D.y));  // Left-Top
    const sf::Vector2f rb(max4(A.x, B.x, C.x, D.x), max4(A.y, B.y, C.y, D.y));  // Right-Bottom

    sf::Vector2f P = lt - m_sprite.GetOrigin();
    P = set_point_rotation(P, A, get_rotation(P, A));
    
    const sf::FloatRect bounding_box(
        lt.x - m_sprite.GetOrigin().x,
        lt.y - m_sprite.GetOrigin().y,
        rb.x - lt.x,
        rb.y - lt.y
    );

    m_bounding_box = sf::Shape::Rectangle(bounding_box, sf::Color(0, 0, 0, 0), 1.f, sf::Color::White);
}


Was mache ich falsch bzw. wie kriege ich es hin, das die Bounding-Box auch bei verschobenem Ursprung korrekt berechnet wird?

Hurra, mit Opera 11 funktioniert das Posten endlich! :thumbsup:

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

19.12.2010, 13:24

Wofür genau brauchst du das denn? Ich mein, je nachdem wie das Sprite verdreht ist wird das ziemlich ungenau eine AABB drüberzulegen. Kannst du nicht vielleicht einfach mit TransformToLocal() arbeiten?

Oberon

Treue Seele

  • »Oberon« ist der Autor dieses Themas

Beiträge: 181

Wohnort: Österreich

Beruf: Student

  • Private Nachricht senden

3

19.12.2010, 15:00

Danke dot! Das ist genau das, was ich brauche! :thumbup: Mir war irgendwie nicht klar was diese Funktion alles kann, bevor ich mir die Doku nochmal durchgelesen habe: "[...] Applies the inverse of all the transformations applied to the object (origin, translation, rotation and scale)."

Der Code sieht jetzt so aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void TestExecutable::set_rotation(const int angle)
{
    m_sprite.SetRotation(angle);

    // A+----------+D
    // | |
    // | |
    // B+----------+C
    const sf::Vector2f A = m_sprite.TransformToGlobal(sf::Vector2f(0, 0));
    const sf::Vector2f C = m_sprite.TransformToGlobal(m_sprite.GetSize()); 
    const sf::Vector2f B = m_sprite.TransformToGlobal(sf::Vector2f(0, m_sprite.GetSize().y)); 
    const sf::Vector2f D = m_sprite.TransformToGlobal(sf::Vector2f(m_sprite.GetSize().x, 0)); 
    
    const sf::Vector2f lt(min4(A.x, B.x, C.x, D.x), min4(A.y, B.y, C.y, D.y));  // Left-Top
    const sf::Vector2f rb(max4(A.x, B.x, C.x, D.x), max4(A.y, B.y, C.y, D.y));  // Right-Bottom

    const sf::FloatRect bounding_box(
lt.x,
lt.y,
rb.x - lt.x,
rb.y - lt.y
    );

    m_bounding_box = sf::Shape::Rectangle(bounding_box, sf::Color(0, 0, 0, 0), 1.f, sf::Color::White);
}

Kurz und korrekt, so soll es sein!
Falls irgendjemand weiß, was bei obigem Code falsch war, würde es mich trotzdem interessieren.

EDIT: So korrekt, dann doch nicht, denn um auch bei veränderter Skalierung richtige Ergebnisse zu liefern, muss man m_sprite.GetSize().x bzw. .y durch m_sprite.GetSubrect().Width bzw. .Height ersetzen (Zeile 10-12 in diesem Post).

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Oberon« (19.12.2010, 15:23)


Werbeanzeige