• 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?

Cloud VPS
0 głosów
772 wizyt
pytanie zadane 29 marca 2023 w Matematyka, fizyka, logika przez rafal.budzis Szeryf (85,700 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 (281,530 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,700 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 (57,400 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 243 wizyt
pytanie zadane 5 sierpnia 2020 w JavaScript przez Bakkit Dyskutant (7,600 p.)
0 głosów
1 odpowiedź 409 wizyt
pytanie zadane 24 marca 2016 w JavaScript przez niezalogowany
+2 głosów
1 odpowiedź 293 wizyt
pytanie zadane 12 stycznia 2023 w JavaScript przez Czang Kai Shrek Obywatel (1,990 p.)

93,469 zapytań

142,404 odpowiedzi

322,716 komentarzy

62,852 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

Kursy INF.02 i INF.03
...