kolizja punktu z odcinkiem

kolizja punktu z odcinkiem
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
1

algorytm do wyznaczenia przecięcia punktu z odcinkiem potrzebuje , chce dopisać soft shadow do bump mappingu
EDIT; wpadłem na pomysł aby wykorzystać algorytm do rysowania odcinka i w miejscu putpixel zrobić po prostu warunek i to tyle ale jesli ktos ma lepszy pomysł chętnie wysłucham ...

Kopiuj
	bool LinePoint(const int x1, const int y1, const int x2, const int y2,int x3,int y3)
	{
     // zmienne pomocnicze
     int d, dx, dy, ai, bi, xi, yi;
     int x = x1, y = y1;
     // ustalenie kierunku rysowania
     if (x1 < x2)
     {
         xi = 1;
         dx = x2 - x1;
     }
     else
     {
         xi = -1;
         dx = x1 - x2;
     }
     // ustalenie kierunku rysowania
     if (y1 < y2)
     {
         yi = 1;
         dy = y2 - y1;
     }
     else
     {
         yi = -1;
         dy = y1 - y2;
     }
     // pierwszy piksel
	 
	if(x==x3 && y==y3)
		return true;

     // oś wiodąca OX
     if (dx > dy)
     {
         ai = (dy - dx) * 2;
         bi = dy * 2;
         d = bi - dx;
         // pętla po kolejnych x
         while (x != x2)
         {
             // test współczynnika
             if (d >= 0)
             {
                 x += xi;
                 y += yi;
                 d += ai;
             }
             else
             {
                 d += bi;
                 x += xi;
             }
            
			if(x==x3 && y==y3)
				return true;

         }
     }
     // oś wiodąca OY
     else
     {
         ai = ( dx - dy ) * 2;
         bi = dx * 2;
         d = bi - dy;
         // pętla po kolejnych y
         while (y != y2)
         {
             // test współczynnika
             if (d >= 0)
             {
                 x += xi;
                 y += yi;
                 d += ai;
             }
             else
             {
                 d += bi;
                 y += yi;
             }
          
			if(x==x3 && y==y3)
				return true;
         }
     }

	 return false;
	}
Althorion
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1620
3

Punkt koliduje z odcinkiem wtedy i tylko, gdy suma odległości od niego do końców odcinka jest równa długości tego odcinka (plus-minus mała delta na niepewności operacji zmiennoprzecinkowych). Sam wtedy jest swoim miejscem przecięcia:

Kopiuj
bool LinePoint(double line_start_x, double line_start_y, double line_stop_x, double line_stop_y,
               double point_x, double point_y) {
  const double FLOAT_INACCURACY = 1000 * std::numeric_limits<double>::epsilon; // zmienić w ramach potrzeb

  double line_length = std::sqrt((line_start_x - line_stop_x)*(line_start_x - line_stop_x) +
                                 (line_start_y - line_stop_y)*(line_start_y - line_stop_y));

  double distance_from_start = …;
  double distance_from_end = …;

  return std::abs(line_length - distance_from_start - distance_from_end) <= FLOAT_INACCURACY;
}

Swoją drogą, sugerowałbym zacząć już upakowywać dane w struktury, bo sześcioargumentowa funkcja przyjmująca tak naprawdę dwa argumenty (odcinek i punkt) zaczyna być niewygodna…

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
1

też tak myślałem ale wpadłem na pomysł aby aby po lini sprawdać podczas rasteryzacji odcinka kolizje wysokościowe a nie zapętlać sie w tablicach i orać na żywo talice aby znaleŹć kolizje bo to koszmrna jest ilosć danych do przetworzenia i strasznie muli

LB
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 75
1

Zakładając, że odcinek jest dany przez A=(x1, y1), B=(x2, y2), punkt to C=(x3, y3), to sprawdź czy:

  1. x3 jest pomiędzy x1 i x2
  2. y3 jest pomiędzy y1 i y2
  3. nachylenie odcinka A->C jest równe nachyleniu odcinka C->B
    (x1-x3)/(y1-y3) = (x3-x2)/(y3-y2) (przekształć to na mnożenie, będzie szybciej)

To tak na szybko - wiadomo, że trzeba dodać obsługę edge caseów.

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0

dzięki ale ja z matmy i fizyki miałem średnio czwórki i nie wiem jak to przekształcić ... sorry ...tylko za prace technika informatyka dostałem szóstkę 😀

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
0

@wilkwielki dobry kod podał @Althorion. Przepisałem na SFML i działa ale możesz użyć struct dla punktów:

Kopiuj
bool isPointInLine(sf::Vector2f point, sf::Vector2f line_start, sf::Vector2f line_end) {

    const float delta = 1000 * std::numeric_limits<float>::epsilon();

    float line_len = sqrt(pow(line_end.x - line_start.x, 2) + pow(line_end.y - line_start.y, 2));

    float distance_from_start = sqrt(pow(line_start.x - point.x, 2) + pow(line_start.y - point.y, 2));
    float distance_from_end = sqrt(pow(line_end.x - point.x, 2) + pow(line_end.y - point.y, 2));

    return std::abs(line_len - distance_from_start - distance_from_end) <= delta;

}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0
  1. o co chodzi z tym : const float delta = 1000 * std::numeric_limits<float>::epsilon(); ?
  2. co znaczy pow() ?
  3. co znaczy abs?
  4. wolał bym struct ...mozna to rozpisać zmiennymi?
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
0

@wilkwielki

  1. const float delta = 1000 * std::numeric_limits<float>::epsilon(); to minimalna dopuszczalna odległość od odcinka, która dopuszcza kolizję
  2. pow czyli power - znaczy się potęga
  3. abs to wartość absolutna czyli dodatnia
  4. zastąp więc sf::Vector2f strukturą struct { float x, float y }
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0

co oznacza epsilon()? skąd jest pobierana wartość tego wyrażenia? i jaka to wartość?

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
1

@wilkwielki std::numeric_limits<float>::epsilon() to stała określająca najmniejszą dodatnią różnicę między liczbą zmiennoprzecinkową (tutaj typu float) a 1.0, która może być reprezentowana w tym typie danych.

W skrócie:
To mówi: "Jak mała musi być liczba, żeby 1.0 + coś było rozróżnialne od 1.0 w pamięci komputera?"

Kopiuj
#include <iostream>
#include <limits>

int main() {
    std::cout << "Epsilon dla float: " << std::numeric_limits<float>::epsilon() << '\n';
    return 0;
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0

ja nie posiadam u siebie #include <limits> i jeśli wartość tego jest stałą to podaj mi tą wartość to sobie sam wkleje ...epsilon() tez u mnie nie ma std ja jade na VC++6.0 Pro

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
0

Możesz wygenerować epsilon przy pomocy poniższego kodu

Kopiuj
#include <iostream>

int main() {
    float epsilon = 1.0f;

    while ((1.0f + epsilon) != 1.0f) {
        epsilon /= 2.0f;
    }

    epsilon *= 2.0f; // ostatnia wartość, która jeszcze zmienia wynik

    std::cout << "Epsilon (float): " << epsilon << '\n';

    return 0;
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0

u mnie pisze tak Epsilon (float): 2.22045e-016 zawsze ta sama wartość jak wartość PI , o co chodzi z tym abs bo z tego co widze tam jest warunek jak to działa na chłopski rozum?

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
0

abs to inaczej if( value < 0.0f ) value*=-1.0f

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0

ok minus razy minus daje plus czyli

Kopiuj
abs(line_len - distance_from_start - distance_from_end) <= delta;

liczy sprawdza jesli line_len - distance_from_start - distance_from_end <= 0.0 to line_len - distance_from_start - distance_from_end *=-1.0 a potem
jest <= delta to jak mam to rozumieć?

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
0

zrób po prostu ten warunek przed returnem

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
1

to będzie tak ze abs<=delta zwraca true w przeciwnym razie false tylko o to chodzi?

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 539
0

@wilkwielki dokładnie tak

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 679
0

teraz wszystko jasne u mnie , dzięki za pomoc 😀

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
3

Rozwiązanie od Althorion tBane otacza odcinek elipsą (matematycznie test sprawdza, czy punkt wypada wewnątrz bardzo wydłużonej epilepsy, której ogniska to końce odcinka).
To znaczy, że test jest szerszy w środku niż przy końcach.

IMO najlepiej testować przez obliczanie odległości punktu od prostej przechodzącej przez odcinek i stanowiącej oś symetrii odcinka.

Moje rozwiązanie:

Kopiuj
namespace geometry {
using Real = double;

struct Point {
    Real x = {};
    Real y = {};
};

struct Vect {
    Real dx;
    Real dy;

    [[nodiscard]] constexpr Real length() const noexcept
    {
        return std::sqrt(dx * dx + dy * dy);
    }

    [[nodiscard]] constexpr Vect normalized() const noexcept
    {
        auto len = length();
        return { dx / len, dy / len };
    }

    [[nodiscard]] constexpr Vect left90() const noexcept
    {
        return { -dy, dx };
    }

    [[nodiscard]] constexpr Vect right90() const noexcept
    {
        return { dy, -dx };
    }

    [[nodiscard]] Real angle() const noexcept
    {
        return std::atan2(dy, dx);
    }
};

[[nodiscard]] constexpr Vect operator-(Point a, Point b) noexcept
{
    return { a.x - b.x, a.y - b.y };
}

[[nodiscard]] constexpr Point operator+(Vect v, Point p) noexcept
{
    return { v.dx + p.x, v.dy + p.y };
}

[[nodiscard]] constexpr Real operator*(Vect v, Point p) noexcept
{
    return v.dx * p.x + v.dy * p.y;
}

[[nodiscard]] constexpr Vect operator*(Vect v, Real fac) noexcept
{
    return { v.dx * fac, v.dy * fac };
}

class Line {
    Vect ab;
    Real c;

public:
    Line(Vect direction, Point p0)
        : ab { direction.left90().normalized() }
        , c { -(ab * p0) }
    {
    }

    Line(Point a, Point b)
        : Line(b - a, a)
    {
    }

    Real distanceTo(Point p) const
    {
        assert(std::abs(ab.length() - 1.0) < std::numeric_limits<Real>::epsilon() * 8);
        return ab * p + c;
    }
};

class LineSegmentTester {
    Line line;
    Line symetry;
    Real halfLen;

public:
    LineSegmentTester(Point a, Point b)
        : line { a, b }
        , symetry { (b - a).left90(), (b - a) * 0.5 + a }
        , halfLen { (a - b).length() * 0.5 }
    {
    }

    [[nodiscard]] bool test(Point p, Real epsilon) const noexcept
    {
        return std::abs(line.distanceTo(p)) <= epsilon && std::abs(symetry.distanceTo(p)) <= halfLen + epsilon;
    }
};
}

Demo z testami.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.