• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[3D,Matma] Jak obliczyć kąty dla wierzchołków które są pochylone na jednej płaszczyźnie?

VPS Starter Arubacloud
0 głosów
480 wizyt
pytanie zadane 29 marca 2023 w Matematyka, fizyka, logika przez rafal.budzis Szeryf (85,260 p.)

Hej mam zbiór punktów w przestrzeni 3D

{x: -0.5, y: 0.3535533845424652, z: -0.3535533845424652}
{x: 0.5, y: 0.3535533845424652, z: -0.3535533845424652}
{x: -0.5, y: -0.3535533845424652, z: 0.3535533845424652}
{x: 0.5, y: -0.3535533845424652, z: 0.3535533845424652}

Tworzą one kwadrat pochylony o 45 stopni na osi Y. Mam obliczoną normalną dla tej płaszczyzny 

{x: 0, y: 0.7071067811865476, z: 0.7071067811865476}

Chciałbym to jakoś uprościć mając te dane do płaszczyzny 2 wymiarowej punktów 

{x:0.5, y:0.5}
{x:-0.5, y:0.5}
{x:0.5, y:-0.5}
{x:-0.5, y:-0.5}

A następnie za pomocą atan2 obliczyć sobie kąty i posortować całość zgodnie z ruchem wskazówek zegara :D Jednak brakuje mi wiedzy matematycznej co ja mam zrobić z tą normalną lub wiedzą o kącie nachylenia aby móc uprościć całość do 2D. Jeśli da się posortować punkty 3D bez upraszczania do 2D to też chętnie poznam rozwiązanie. 
 

2 odpowiedzi

0 głosów
odpowiedź 29 marca 2023 przez Wiciorny Ekspert (269,120 p.)
edycja 29 marca 2023 przez Wiciorny

Dla takie normalnej 

# normalna
normal_vector = {"x": 0, "y": 0.7071067811865476, "z": 0.7071067811865476}

To dla takiego wektora i twoich punktów zbioru przestrzeni 3D 

 

points_2d = []
for point in points_3d:
    # wyznaczamy wektor normalny płaszczyzny i wektor od punktu do środka
    vector = {"x": point["x"], "y": point["y"], "z": point["z"]}
    center_vector = {"x": 0, "y": 0, "z": 0}
    normal_length = math.sqrt(normal_vector["x"]**2 + normal_vector["y"]**2 + normal_vector["z"]**2)
    vector_length = math.sqrt(vector["x"]**2 + vector["y"]**2 + vector["z"]**2)
    
    # oblicz wyznacznik ilocznu skalarnego wektorów 
    dot_product = normal_vector["x"]*vector["x"] + normal_vector["y"]*vector["y"] + normal_vector["z"]*vector["z"]
    cos_alpha = dot_product / (normal_length * vector_length)
    sin_alpha = math.sqrt(1 - cos_alpha**2)
    
    # wyliczasz bezposrednio punkty na plaszczyznie 2D
    x_2d = vector_length * cos_alpha
    y_2d = vector_length * sin_alpha
    points_2d.append({"x": x_2d, "y": y_2d})

#ostatecznie co robisz to sortujesz
# sortujemy punkty zgodnie z ruchem wskazówek zegara
points_2d_sorted = sorted(points_2d, key=lambda p: math.atan2(p["y"], p["x"]))

Atan jako tangens kąta między dwoma punktami. To możesz obliczyć kąt dla wektora normalnego płaszczyzny i punktu, na którym płaszczyzna jest pozioma. I wykorzystać te funkcje przy sortowaniu ? 
Funkcja ostatecznie oblicza wartość kąta nachylenia wektora o współrzędnych (x,y) względem osi X. I potem sortujesz według wartości kąta rosnąćego - co daje Ci kierunek wzkazówek zegara
 

Jeśli masz już uproszczone punkty w 2D, to możesz obliczyć kąty między każdą parą punktów wokół ich środka ciężkości, i potem już posortować punkty zgodnie z kierunkiem wskazówek zegara.

Środek ciężkości punktów możesz obliczyć poprzez uśrednienie współrzędnych x, y i z:
 

center = {"x": sum(point["x"] for point in points)/len(points),
          "y": sum(point["y"] for point in points)/len(points),
          "z": sum(point["z"] for point in points)/len(points)}

kąty tutaj już z tym co wspominałem ata2 np.
 

# wyznacz delte przed obliczeniem kątów
delta_x = x2 - x1
delta_y = y2 - y1
angle = atan2(delta_y, delta_x)

i teraz sortować zbiór np. points  jako zbiór punktów 2D na płaszczyźnie, które chcesz posortować zgodnie z ruchem wskazówek zegara, z wczesniejszego zbioru 3D 
 

sorted_points = sorted(points, key=lambda p: math.atan2(p["y"]-center["y"], p["x"]-center["x"]), reverse=True)

revers dlatego ze maja byc zgodne z ruchem 

komentarz 29 marca 2023 przez rafal.budzis Szeryf (85,260 p.)

Odpowiedz wygląda bardzo ładnie ale nie działa.

Po tych wszystkich obliczeniach dostałem 4 takie same punkty 2D
 

x_2d: 0, y_2d: 0.707106775135739
x_2d: 0, y_2d: 0.707106775135739
x_2d: 0, y_2d: 0.707106775135739
x_2d: 0, y_2d: 0.707106775135739

Z ciekawości spytam chatGTP to wypluł? Bo zanim zadałem pytanie próbowałem się wspomagać AI ale wyniki były marne. Dodatkowo popełnił dokładnie ten sam błąd wiele razy :D 

0 głosów
odpowiedź 30 marca 2023 przez Whistleroosh Maniak (56,900 p.)

Zakładając, że są tylko 4 punkty, które tworzą kwadrat i znamy normalną:

const EPSILON = 0.0000001;

let points = [
    {x: -0.5, y: 0.3535533845424652, z: -0.3535533845424652},
    {x: 0.5, y: 0.3535533845424652, z: -0.3535533845424652},
    {x: -0.5, y: -0.3535533845424652, z: 0.3535533845424652},
    {x: 0.5, y: -0.3535533845424652, z: 0.3535533845424652},
];

const normal = {x: 0, y: 0.7071067811865476, z: 0.7071067811865476};

Przyda się kilka funkcji pomocniczych: liczenie odległości między punktami, sprawdzanie czy wektory są skierowane w tę samą stronę i cross product:

function distance(point_a, point_b) {
    return Math.hypot(point_a.x-point_b.x, point_a.y-point_b.y, point_a.z-point_b.z);
}

function same_direction(v1, v2) {
    let len1 = Math.hypot(v1.x, v1.y, v1.z);
    let len2 = Math.hypot(v2.x, v2.y, v2.z);
    let normalized_v1 = {x: v1.x / len1, y: v1.y / len1, z: v1.z / len1};
    let normalized_v2 = {x: v2.x / len2, y: v2.y / len2, z: v2.z / len2};

    return Math.hypot(
        normalized_v1.x - normalized_v2.x, normalized_v1.y - normalized_v2.y,
        normalized_v1.z - normalized_v2.z
        ) < EPSILON;
}

function cross_product(v1, v2) {
    let x = v1.y * v2.z - v1.z * v2.y;
    let y = v1.z * v2.x - v1.x * v2.z;
    let z = v1.x * v2.y - v1.y * v2.x;

    return {x, y, z};
}

Załózmy że chcemy znaleźć takie punkty a, b, c, d, ze one są posortowane zgodnie z ruchem wskazówek zegara. Za a możemy wziać dowolny punkt. Posortujemy pozostałem punkty względem odległości do a. Oczywiście najdalszy punkt będzie punktem c.

let a = points[0];

points.sort((point_a, point_b) => distance(a, point_a) - distance(a, point_b));

let c = points[3], b, d;

Pozostało znaleźć który to b i d. W tym celu liczymy wektory c->points[1] (bok kwadratu), c->points[2] (bok kwadratu) i c->a (przekątna kwadratu). Następnie liczymy cross product pierwszego z policzonych wcześniej wektorów z trzecim i drugiego z trzecim. Jeden z tych wektorów będzie miał ten sam kierunek co normalna. To jest szukane b.

let cp1 = {x: points[1].x - c.x, y: points[1].y - c.y, z: points[1].z - c.z}; // wektor c -> p1
let cp2 = {x: points[2].x - c.x, y: points[2].y - c.y, z: points[2].z - c.z}; // wektor c -> p2
let ca = {x: a.x - c.x, y: a.y - c.y, z: a.z - c.z}; // wektor c -> a

let cr1 = cross_product(ca, cp1);
let cr2 = cross_product(ca, cp2);

if(same_direction(normal, cr1)) {
    b = points[1];
    d = points[2];
}

else if (same_direction(normal, cr2)) {
    b = points[2];
    d = points[1];
}

else {
    console.log("ERROR");
}

console.log(a, b, c, d);

Otrzymaliśmy szukane punkty. Nie piszę normalnie w JS, więc nie wiem jak on sobie radzi z błedami w arytmetyce zmiennoprzecinkowej. Dlatego dałem epsilona żeby temu zaradzić. Trzeba go jakoś odpowiednio dobrać.

Podobne pytania

0 głosów
0 odpowiedzi 202 wizyt
pytanie zadane 5 sierpnia 2020 w JavaScript przez Bakkit Dyskutant (7,600 p.)
0 głosów
1 odpowiedź 326 wizyt
pytanie zadane 24 marca 2016 w JavaScript przez niezalogowany
+2 głosów
1 odpowiedź 168 wizyt
pytanie zadane 12 stycznia 2023 w JavaScript przez Czang Kai Shrek Obywatel (1,990 p.)

92,454 zapytań

141,262 odpowiedzi

319,099 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...