Добрый день.
Быстрый поиск натолкнул на видео.
Краткое содержание.
Плоскость
задана тремя координатами 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);
}
}
}