Нахождение нормали к плоскости

Различные геометрические алгоритмы.
Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Нахождение нормали к плоскости

Сообщение Admiralisimys » 28.12.2015 (Пн) 15:42

Нахождение нормали к плоскости

Добрый день.
Быстрый поиск натолкнул на видео.

Краткое содержание.
Плоскость
Изображение
задана тремя координатами A, B, C.
Требуется найти вектор n.

Векторы нормали перпендикулярны векторам плоскости
Рассчитаем векторы плоскости AB и AC (от конца отрезка отнимем начало).
Подставив исходные значения получим систему из двух уравнений с тремя неизвестными. Допустим что одна из неизвестных равна единице.
В видео говорится, что если в дальнейшем расчёт станет невозможным (на видео не объясняется, при каких условиях такое может произойти, но как реализовывавший алгоритм на практике, видя деления, могу предположить что на таком пути может встретится деление на ноль), то взять за единицу следующую. Если и с ней не получится, то оставшуюся последнею. Напрашивается вывод что, в любом случаи существует вектор нормали к плоскости у которого одна из составляющих равна единице.
Рассчитываем вторую и первую составляющую вектора.
Получаем результирующий вектор нормали и ещё один вектор нормали полученный из исходного путём умножение на два.
Автор видео пояснил нахождения последнего тем что в данном конкретном случае он удобнее для расчётов, так как не содержит дробей.

Изображение

В реализации я же предложу и вовсе нормализовать вектор так компу то все равно.
Обозначу вектор AB как V1, а вектор AC как V1.
Выразим уравнения для нахождения n1 из первого уравнения этой системы.
Это не вопрос этой темы, но отмечу, что этого момента нет на видео. Автор пошёл путём сложения уравнений. Для расчёта человеком, для данного примера, способ удобен, однако не вполне ясно насколько он возможен в реализации в коде.
Подставим n1 во второе уравнение системы, выведем n2 и n3 из данного уравнения

Изображение

Для принятия за единицу n2 или n3 данных уравнений достаточно. Для случая когда потребуется принять за единицу n1 необходимы уравнения в которых собственно n1 фигурирует. Для этого выведем n2 из первого уравнения исходной системы уравнений
Подставим n2 во второе уравнение системы и выведем n3

Изображение

Пример реализации на языке C#
Код: Выделить всё
using System;

namespace Geometry
{
   public class Surface
   {
      public Point mPoint;
      public Vector mVector1;
      public Vector mVector2;

      public Surface(Point aPoint, Vector aVector1, Vector aVector2)
      {
         mPoint = aPoint;
         mVector1 = aVector1;
         mVector2 = aVector2;
      }

      public Surface(Point aA, Point aB, Point aC) : this(aA, new Vector(aA, aB), new Vector(aA, aC))
      {
      }
      
      public static Vector GetNormal(Surface aSurface)
      {
         Vector v = null;
         
         double V1x = aSurface.mVector1.X;
         double V1y = aSurface.mVector1.Y;
         double V1z = aSurface.mVector1.Z;
         
         double V2x = aSurface.mVector2.X;
         double V2y = aSurface.mVector2.Y;
         double V2z = aSurface.mVector2.Z;

         v = GetNormalWithZSetToOne(V1x, V1y, V1z, V2x, V2y, V2z);
         v = Vector.Normal(v);
         Console.WriteLine(v.X + " " + v.Y + " " + v.Z);
         v = GetNormalWithYSetToOne(V1x, V1y, V1z, V2x, V2y, V2z);
         v = Vector.Normal(v);
         Console.WriteLine(v.X + " " + v.Y + " " + v.Z);
         v = GetNormalWithXSetToOne(V1x, V1y, V1z, V2x, V2y, V2z);
         v = Vector.Normal(v);
         Console.WriteLine(v.X + " " + v.Y + " " + v.Z);
         
         return v;
      }
      
      private static Vector GetNormalWithXSetToOne(double aV1x, double aV1y, double aV1z, double aV2x, double aV2y, double aV2z)
      {
         Vector v = new Vector(Point.Empty, Point.Empty);

         const double n1 = 1.0;
         
         double denominator = (aV2z * aV1y - aV1z * aV2y);

         if (Math.Abs(denominator) < 2 * double.Epsilon)
         {
            throw new Exception("Failed to found vector of normal");
         }
         
         double numerator = n1 * (aV1x * aV2y - aV2x * aV1y);
         double n3 = numerator / denominator;
         double n2 = ((-1) * (n1 * aV1x + n3 * aV1z)) / aV1y;
   
         v.X = n1;
         v.Y = n2;
         v.Z = n3;
            
         return v;
      }
      
      private static Vector GetNormalWithYSetToOne(double aV1x, double aV1y, double aV1z, double aV2x, double aV2y, double aV2z)
      {
         Vector v = new Vector(Point.Empty, Point.Empty);
         
         const double n2 = 1.0;
         
         double denominator = (aV2x * aV1z - aV2z * aV1x);
         
         if (Math.Abs(denominator) < 2 * double.Epsilon)
         {
            throw new Exception("Failed to found vector of normal");
         }
         
         double numerator = n2 * (aV2y * aV1x - aV2x * aV1y);
         double n3 = numerator / denominator;
         double n1 = ((-1) * ((n2 * aV1y + n3 * aV1z))) / aV1x;
         
         v.X = n1;
         v.Y = n2;
         v.Z = n3;
         
         return v;
      }
      
      private static Vector GetNormalWithZSetToOne(double aV1x, double aV1y, double aV1z, double aV2x, double aV2y, double aV2z)
      {
         Vector v = new Vector(Point.Empty, Point.Empty);
         
         const double n3 = 1.0;

         double denominator = (aV2y * aV1x - aV2x * aV1y);

         if (Math.Abs(denominator) < 2 * double.Epsilon)
         {
            throw new Exception("Failed to found vector of normal");
         }

         double numerator = n3 * (aV2x * aV1z - aV2z * aV1x);
         double n2 = numerator / denominator;
         double n1 = ((-1) * ((n2 * aV1y + n3 * aV1z))) / aV1x;
         
         v.X = n1;
         v.Y = n2;
         v.Z = n3;
            
         return v;
      }
   }

   public class Point
   {
      public double X;
      public double Y;
      public double Z;
      
      public Point(double X, double Y)
      {
         this.X = X;
         this.Y = Y;
         this.Z = 0;
      }
      
      public Point(double X, double Y, double Z)
      {
         this.X = X;
         this.Y = Y;
         this.Z = Z;
      }
      
      public static Point Empty
      {
         get
         {
            return new Point(0, 0, 0);
         }
      }
   }

   public class Vector : Point
   {
      public Vector(Point aA, Point aB) : base(aB.X - aA.X, aB.Y - aA.Y, aB.Z - aA.Z)
      {
      }

      public static double Length(double aX, double aY, double aZ)
      {
         return Math.Sqrt(Math.Pow(aX, 2) + Math.Pow(aY, 2) + Math.Pow(aZ, 2));
      }
      
      public static Point Normal(Point aVector)
      {
         double length = Length(aVector.X, aVector.Y, aVector.Z);

         if (length < 2 * double.Epsilon)
         {
            return Point.Empty;
         }
      
         length = 1 / length;
         return new Point(aVector.X * length, aVector.Y * length, aVector.Z * length);
      }
      
      public static Vector Normal(Vector aVector)
      {
         Point point = Normal(new Point(aVector.X, aVector.Y, aVector.Z));
         return new Vector(Point.Empty, point);
      }
      
      public static double DotProduct(Point aVector1, Point aVector2)
      {
         return (aVector1.X * aVector2.X + aVector1.Y * aVector2.Y + aVector1.Z * aVector2.Z);
      }
   }
}

namespace Project
{
   public class Program
   {
      public static void Main()
      {
         Geometry.Point A = new Geometry.Point(1, 2, 3);
         Geometry.Point B = new Geometry.Point(0, -1, 1);
         Geometry.Point C = new Geometry.Point(2, 1, -1);
         Geometry.Surface s = new Geometry.Surface(A, B, C);
         Geometry.Vector n = Geometry.Surface.GetNormal(s);
      }
   }
}
Вложения
0_Input.png
0_Input.png (6.16 Кб) Просмотров: 1211
1.png
1.png (39.05 Кб) Просмотров: 1211
2.png
2.png (41.17 Кб) Просмотров: 1211
3.png
3.png (24.91 Кб) Просмотров: 1211

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4147
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Нахождение нормали к плоскости

Сообщение Mikle » 28.12.2015 (Пн) 15:51

Вообще-то нормаль к плоскости по трём точкам ищется так:
Код: Выделить всё
Vec3Subtract v1, b, a
Vec3Subtract v2, c, a
Vec3Cross vN, v1, v2
Vec3Normalize vN. vN

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Re: Нахождение нормали к плоскости

Сообщение Admiralisimys » 28.12.2015 (Пн) 16:36

O, даже так. Благодарю Mikle.
В таком случае в выше опубликованной реализации к классу Vector добавляется новый метод
Код: Выделить всё
      public static Point CrossProduct(Point aVector1, Point aVector2)
      {
         return new Point(aVector1.Y * aVector2.Z - aVector1.Z * aVector2.Y,
                          aVector1.Z * aVector2.X - aVector1.X * aVector2.Z,
                          aVector1.X * aVector2.Y - aVector1.Y * aVector2.X);
      }

А сам вызов в классе Surface сводится к
Код: Выделить всё
      public static Point GetNormal(Surface aSurface)
      {
         Point v = Vector.CrossProduct(aSurface.mVector1, aSurface.mVector2);

         v = Vector.Normal(v);
         Console.WriteLine(v.X + " " + v.Y + " " + v.Z);
         
         return v;
      }

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Нахождение нормали к плоскости

Сообщение Хакер » 28.12.2015 (Пн) 16:38

Да уж.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.


Вернуться в Геометрия

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

    TopList