tBane Temat założony przez niniejszego użytkownika |
Kolizje dowolnego trójkąta z prostokątem (zaznaczanie trójkątów) » 2024-11-10 14:46:40 Witam. Piszę sobie grę. Potrzebuję wykrywać kolizje dowolnego trójkąta opisanego przez trzy punkty z prostokątem w celu wybierania obiektów poprzez zaznaczenie. To znaczy chciałbym zaznaczyć pewien obszar i wszystkie trójkątne obiekty wewnątrz tego zaznaczenia.. wybrać. Do tego właśnie potrzebuje algorytmu tej kolizji. |
|
DejaVu |
» 2024-11-10 14:56:22 Zrób prostokątną otoczkę wokół trójkąta, czyli znajdź min i max dla współrzędnych wierzchołków trójkąta osobno w osi X oraz Y. To będzie prostokąt otaczający. Jeżeli te prostokąty się przecinają, to 'jest' kolizja w rozumieniu 'ten element chcę zaznaczyć'. Natomiast jeżeli chcesz zrobić 'precyzyjne' zaznaczanie, to musisz zaimplementować dodatkowy algorytm, który będzie wywoływany, gdy "prostokąty się przecinają". Wówczas uruchamiasz 'bardziej zaawansowany algorytm'. Takie 'dwuetapowe' wykrywanie kolizji jest popularne i wydaje mi się powszechnie stosowane. Prostokąty bardzo szybko i łatwo się wykrywa, natomiast wykrywanie przecięć linii pod różnymi kątami to już jest nieco więcej zabawy i znacznie więcej kosztuje obliczeniowo. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2024-11-10 14:57:55 No dobra. Zrobię kolizję dwuetapową. Ale jak sprawdzić w takim razie przecięcia linii? |
|
DejaVu |
» 2024-11-10 15:02:46 ChatGPT 4o: Aby wykryć kolizję dowolnego trójkąta z prostokątem w SFML, możesz zastosować podejście dwuetapowe:
1. **Etap wstępny**: Oblicz prostokątną otoczkę trójkąta (ang. bounding box). Wykorzystaj minimalne i maksymalne wartości współrzędnych wierzchołków trójkąta do utworzenia prostokąta, który go otacza. Następnie sprawdź, czy ten prostokąt przecina się z prostokątem zaznaczenia. Jeśli nie ma przecięcia, odrzuć trójkąt.
2. **Etap dokładniejszy**: Jeśli otoczka prostokątna trójkąta przecina się z zaznaczeniem, wykonaj bardziej szczegółowe sprawdzenie kolizji: - Sprawdź, czy dowolny z wierzchołków trójkąta znajduje się wewnątrz prostokąta zaznaczenia. - Oblicz przecięcia pomiędzy bokami trójkąta a krawędziami prostokąta zaznaczenia, co wymaga wykorzystania funkcji do sprawdzania przecięć odcinków.
Przykładowa implementacja do sprawdzenia przecięcia dwóch odcinków może wyglądać tak:
```cpp struct Point { float x, y; };
bool onSegment(Point p, Point q, Point r) { return q.x <= std::max(p.x, r.x) && q.x >= std::min(p.x, r.x) && q.y <= std::max(p.y, r.y) && q.y >= std::min(p.y, r.y); }
int orientation(Point p, Point q, Point r) { float val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); if (val == 0) return 0; // kolinearne return (val > 0) ? 1 : 2; // 1 = zgodnie ze wskazówkami zegara, 2 = przeciwnie }
bool doIntersect(Point p1, Point q1, Point p2, Point q2) { int o1 = orientation(p1, q1, p2); int o2 = orientation(p1, q1, q2); int o3 = orientation(p2, q2, p1); int o4 = orientation(p2, q2, q1);
if (o1 != o2 && o3 != o4) return true;
if (o1 == 0 && onSegment(p1, p2, q1)) return true; if (o2 == 0 && onSegment(p1, q2, q1)) return true; if (o3 == 0 && onSegment(p2, p1, q2)) return true; if (o4 == 0 && onSegment(p2, q1, q2)) return true;
return false; } ```
Funkcja `doIntersect` sprawdza, czy dwa odcinki (p1, q1) oraz (p2, q2) przecinają się, co pozwala dokładnie określić kolizję.
Natomiast nie jestem przekonany czy to poprawny algorytm do wykrywania przecięć linii, więc będziesz musiał poczytać/poeksperymentować. |
|
DejaVu |
» 2024-11-10 15:12:52 Nawet działa: #include <SFML/Graphics.hpp> #include <iostream> #include <cmath>
struct Point { float x, y; };
bool onSegment( Point p, Point q, Point r ) { return q.x <= std::max( p.x, r.x ) && q.x >= std::min( p.x, r.x ) && q.y <= std::max( p.y, r.y ) && q.y >= std::min( p.y, r.y ); }
int orientation( Point p, Point q, Point r ) { float val =( q.y - p.y ) *( r.x - q.x ) -( q.x - p.x ) *( r.y - q.y ); if( val == 0 ) return 0; return( val > 0 ) ? 1 : 2; }
bool doIntersect( Point p1, Point q1, Point p2, Point q2 ) { int o1 = orientation( p1, q1, p2 ); int o2 = orientation( p1, q1, q2 ); int o3 = orientation( p2, q2, p1 ); int o4 = orientation( p2, q2, q1 ); if( o1 != o2 && o3 != o4 ) return true; if( o1 == 0 && onSegment( p1, p2, q1 ) ) return true; if( o2 == 0 && onSegment( p1, q2, q1 ) ) return true; if( o3 == 0 && onSegment( p2, p1, q2 ) ) return true; if( o4 == 0 && onSegment( p2, q1, q2 ) ) return true; return false; }
class Line { public: Line( sf::Vector2f start, float length, float rotationSpeed ) : startPosition( start ) , length( length ) , rotationSpeed( rotationSpeed ) , angle( 0 ) { vertices[ 0 ] = sf::Vertex( startPosition ); vertices[ 1 ] = sf::Vertex( sf::Vector2f( startPosition.x + length, startPosition.y ) ); } void update() { angle += rotationSpeed; float cosAngle = std::cos( angle ); float sinAngle = std::sin( angle ); vertices[ 1 ].position = sf::Vector2f( startPosition.x + length * cosAngle, startPosition.y + length * sinAngle ); } sf::Vertex * getVertices() { return vertices; } Point getStartPoint() const { return { vertices[ 0 ].position.x, vertices[ 0 ].position.y }; } Point getEndPoint() const { return { vertices[ 1 ].position.x, vertices[ 1 ].position.y }; } void setColor( const sf::Color & color ) { vertices[ 0 ].color = color; vertices[ 1 ].color = color; } private: sf::Vector2f startPosition; float length; float rotationSpeed; float angle; sf::Vertex vertices[ 2 ]; };
int main() { sf::RenderWindow window( sf::VideoMode( 800, 600 ), "Line Intersection Test" ); window.setFramerateLimit( 60 ); Line line1( sf::Vector2f( 400, 300 ), 150, 0.02f ); Line line2( sf::Vector2f( 450, 350 ), 150, 0.03f ); while( window.isOpen() ) { sf::Event event; while( window.pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window.close(); } line1.update(); line2.update(); if( doIntersect( line1.getStartPoint(), line1.getEndPoint(), line2.getStartPoint(), line2.getEndPoint() ) ) { line1.setColor( sf::Color::Green ); line2.setColor( sf::Color::Green ); } else { line1.setColor( sf::Color::Red ); line2.setColor( sf::Color::Red ); } window.clear(); window.draw( line1.getVertices(), 2, sf::Lines ); window.draw( line2.getVertices(), 2, sf::Lines ); window.display(); } return 0; }
|
|
tBane Temat założony przez niniejszego użytkownika |
» 2024-11-10 15:58:25 Ok. Dzieki. Spróbuję jeszcze jednej metody tzn wymyśliłem algorytm z wykorzystaniem y=ax+b. Jak moja nie zadziała to skorzystam z tej, którą podałeś. Jak mój algorytm będzie działał to się nim podzielę |
|
DejaVu |
» 2024-11-10 16:01:34 Spoko, ale ja pamiętam, że dawno temu jak implementowałem algorytm do wykrywania czy odcinki się przecinają, to był on wielokrotnie dłuższy, więc ta implementacja skoro 'o dziwo' działa, to bym jej używał na Twoim miejscu :) Tu masz bardziej ucywilizowany kod, który 'wydziela' algorytmikę kolizji 'linii' do dedykowanej klasy. class Line { public: sf::Vector2f start; sf::Vector2f end; Line( const sf::Vector2f & _start, const sf::Vector2f & _end ) : start( _start ) , end( _end ) { } bool intersects( const Line & _other ) const { if( start == end || _other.start == _other.end ) { return false; } return doIntersect( start, end, _other.start, _other.end ); } private: bool onSegment( const sf::Vector2f & _p, const sf::Vector2f & _q, const sf::Vector2f & _r ) const { return _q.x <= std::max( _p.x, _r.x ) && _q.x >= std::min( _p.x, _r.x ) && _q.y <= std::max( _p.y, _r.y ) && _q.y >= std::min( _p.y, _r.y ); } int orientation( const sf::Vector2f & _p, const sf::Vector2f & _q, const sf::Vector2f & _r ) const { float val =( _q.y - _p.y ) *( _r.x - _q.x ) -( _q.x - _p.x ) *( _r.y - _q.y ); if( val == 0 ) return 0; return( val > 0 ) ? 1 : 2; } bool doIntersect( const sf::Vector2f & _p1, const sf::Vector2f & _q1, const sf::Vector2f & _p2, const sf::Vector2f & _q2 ) const { int o1 = orientation( _p1, _q1, _p2 ); int o2 = orientation( _p1, _q1, _q2 ); int o3 = orientation( _p2, _q2, _p1 ); int o4 = orientation( _p2, _q2, _q1 ); if( o1 != o2 && o3 != o4 ) return true; if( o1 == 0 && onSegment( _p1, _p2, _q1 ) ) return true; if( o2 == 0 && onSegment( _p1, _q2, _q1 ) ) return true; if( o3 == 0 && onSegment( _p2, _p1, _q2 ) ) return true; if( o4 == 0 && onSegment( _p2, _q1, _q2 ) ) return true; return false; } };
Kompletny przykład: #include <SFML/Graphics.hpp> #include <cmath> #include <algorithm>
class Line { public: sf::Vector2f start; sf::Vector2f end; Line( const sf::Vector2f & _start, const sf::Vector2f & _end ) : start( _start ) , end( _end ) { } bool intersects( const Line & _other ) const { if( start == end || _other.start == _other.end ) { return false; } return doIntersect( start, end, _other.start, _other.end ); } private: bool onSegment( const sf::Vector2f & _p, const sf::Vector2f & _q, const sf::Vector2f & _r ) const { return _q.x <= std::max( _p.x, _r.x ) && _q.x >= std::min( _p.x, _r.x ) && _q.y <= std::max( _p.y, _r.y ) && _q.y >= std::min( _p.y, _r.y ); } int orientation( const sf::Vector2f & _p, const sf::Vector2f & _q, const sf::Vector2f & _r ) const { float val =( _q.y - _p.y ) *( _r.x - _q.x ) -( _q.x - _p.x ) *( _r.y - _q.y ); if( val == 0 ) return 0; return( val > 0 ) ? 1 : 2; } bool doIntersect( const sf::Vector2f & _p1, const sf::Vector2f & _q1, const sf::Vector2f & _p2, const sf::Vector2f & _q2 ) const { int o1 = orientation( _p1, _q1, _p2 ); int o2 = orientation( _p1, _q1, _q2 ); int o3 = orientation( _p2, _q2, _p1 ); int o4 = orientation( _p2, _q2, _q1 ); if( o1 != o2 && o3 != o4 ) return true; if( o1 == 0 && onSegment( _p1, _p2, _q1 ) ) return true; if( o2 == 0 && onSegment( _p1, _q2, _q1 ) ) return true; if( o3 == 0 && onSegment( _p2, _p1, _q2 ) ) return true; if( o4 == 0 && onSegment( _p2, _q1, _q2 ) ) return true; return false; } };
class DrawableLine { public: DrawableLine( const sf::Vector2f & _start, const sf::Vector2f & _end, float _rotationSpeed ) : line( _start, _end ) , rotationSpeed( _rotationSpeed ) , angle( 0 ) , color( sf::Color::Red ) { } void update() { angle += rotationSpeed; float cosAngle = std::cos( angle ); float sinAngle = std::sin( angle ); line.end = sf::Vector2f( line.start.x + length() * cosAngle, line.start.y + length() * sinAngle ); } bool intersects( const DrawableLine & _other ) const { return line.intersects( _other.line ); } void setColor( const sf::Color & _color ) { color = _color; } sf::Vertex * getVertices() { vertices[ 0 ] = sf::Vertex( line.start, color ); vertices[ 1 ] = sf::Vertex( line.end, color ); return vertices; } private: Line line; float rotationSpeed; float angle; sf::Color color; sf::Vertex vertices[ 2 ]; float length() const { return std::sqrt( std::pow( line.end.x - line.start.x, 2 ) + std::pow( line.end.y - line.start.y, 2 ) ); } };
int main() { sf::RenderWindow window( sf::VideoMode( 800, 600 ), "Line Intersection Test" ); window.setFramerateLimit( 60 ); DrawableLine line1( sf::Vector2f( 400, 300 ), sf::Vector2f( 550, 300 ), 0.02f ); DrawableLine line2( sf::Vector2f( 450, 350 ), sf::Vector2f( 600, 350 ), 0.03f ); while( window.isOpen() ) { sf::Event event; while( window.pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window.close(); } line1.update(); line2.update(); if( line1.intersects( line2 ) ) { line1.setColor( sf::Color::Green ); line2.setColor( sf::Color::Green ); } else { line1.setColor( sf::Color::Red ); line2.setColor( sf::Color::Red ); } window.clear(); window.draw( line1.getVertices(), 2, sf::Lines ); window.draw( line2.getVertices(), 2, sf::Lines ); window.display(); } return 0; }
|
|
DejaVu |
» 2024-11-10 16:53:12 Jeszcze 'bonus' - ten kod umożliwi Ci weryfikację 'wzrokową' czy istnieją przypadki, w których wykrywanie przecięć 'linii' działa źle, bo 'przecinają' się np. pod jakimś specyficznym kątem. Przyda Ci się to, jeżeli planujesz implementować 'własną' wersję algorytmu. #include <SFML/Graphics.hpp> #include <cmath> #include <algorithm> #include <vector> #include <cstdlib> #include <ctime>
class Line { public: sf::Vector2f start; sf::Vector2f end; Line( const sf::Vector2f & _start, const sf::Vector2f & _end ) : start( _start ) , end( _end ) { } bool intersects( const Line & _other ) const { if( start == end || _other.start == _other.end ) { return false; } return doIntersect( start, end, _other.start, _other.end ); } private: bool onSegment( const sf::Vector2f & _p, const sf::Vector2f & _q, const sf::Vector2f & _r ) const { return _q.x <= std::max( _p.x, _r.x ) && _q.x >= std::min( _p.x, _r.x ) && _q.y <= std::max( _p.y, _r.y ) && _q.y >= std::min( _p.y, _r.y ); } int orientation( const sf::Vector2f & _p, const sf::Vector2f & _q, const sf::Vector2f & _r ) const { float val =( _q.y - _p.y ) *( _r.x - _q.x ) -( _q.x - _p.x ) *( _r.y - _q.y ); if( val == 0 ) return 0; return( val > 0 ) ? 1 : 2; } bool doIntersect( const sf::Vector2f & _p1, const sf::Vector2f & _q1, const sf::Vector2f & _p2, const sf::Vector2f & _q2 ) const { int o1 = orientation( _p1, _q1, _p2 ); int o2 = orientation( _p1, _q1, _q2 ); int o3 = orientation( _p2, _q2, _p1 ); int o4 = orientation( _p2, _q2, _q1 ); if( o1 != o2 && o3 != o4 ) return true; if( o1 == 0 && onSegment( _p1, _p2, _q1 ) ) return true; if( o2 == 0 && onSegment( _p1, _q2, _q1 ) ) return true; if( o3 == 0 && onSegment( _p2, _p1, _q2 ) ) return true; if( o4 == 0 && onSegment( _p2, _q1, _q2 ) ) return true; return false; } };
class DrawableLine { public: DrawableLine( const sf::Vector2f & _start, const sf::Vector2f & _end, float _rotationSpeed ) : line( _start, _end ) , rotationSpeed( _rotationSpeed ) , angle( 0 ) , color( sf::Color::Red ) { } void update() { angle += rotationSpeed; float cosAngle = std::cos( angle ); float sinAngle = std::sin( angle ); line.end = sf::Vector2f( line.start.x + length() * cosAngle, line.start.y + length() * sinAngle ); } bool intersects( const DrawableLine & _other ) const { return line.intersects( _other.line ); } void setColor( const sf::Color & _color ) { color = _color; } sf::Vertex * getVertices() { vertices[ 0 ] = sf::Vertex( line.start, color ); vertices[ 1 ] = sf::Vertex( line.end, color ); return vertices; } private: Line line; float rotationSpeed; float angle; sf::Color color; sf::Vertex vertices[ 2 ]; float length() const { return std::sqrt( std::pow( line.end.x - line.start.x, 2 ) + std::pow( line.end.y - line.start.y, 2 ) ); } };
int main() { std::srand( static_cast < unsigned >( std::time( 0 ) ) ); sf::RenderWindow window( sf::VideoMode( 800, 600 ), "Line Intersection Test" ); window.setFramerateLimit( 60 ); std::vector < DrawableLine > lines; for( int i = 0; i < 100; ++i ) { sf::Vector2f start( static_cast < float >( std::rand() % 800 ), static_cast < float >( std::rand() % 600 ) ); sf::Vector2f end( start.x + 100.0f, start.y ); float rotationSpeed =( std::rand() % 101 - 50 ) / 5000.0f; lines.emplace_back( start, end, rotationSpeed ); } while( window.isOpen() ) { sf::Event event; while( window.pollEvent( event ) ) { if( event.type == sf::Event::Closed ) window.close(); } for( auto & line: lines ) { line.update(); line.setColor( sf::Color::Red ); } for( std::size_t i = 0; i < lines.size(); ++i ) { for( std::size_t j = i + 1; j < lines.size(); ++j ) { if( lines[ i ].intersects( lines[ j ] ) ) { lines[ i ].setColor( sf::Color::Green ); lines[ j ].setColor( sf::Color::Green ); } } } window.clear(); for( auto & line: lines ) { window.draw( line.getVertices(), 2, sf::Lines ); } window.display(); } return 0; }
|
|
« 1 » 2 |