DXF 文件 - 法向量和相对于此的位置如何提供对块位置的全面理解
DXF files - How does the normal vector and position relative to this provide full understanding of block position
几天来我一直在摸不着头脑,想了解如何完整描述要插入到 DXF 中的块。对于背景,我对如何执行此操作的规则和行为感兴趣,而不仅仅是尝试查找现有库。
我可以在 dxf 中创建一个块,假设这是一个简单的立方体。然后我可以在实体部分插入这个块。
为此,我输入了相对于对象坐标系代码的坐标:(10, 20, 30),以及对象代码的法向量:(210,220,230)。还有一个绕法向量旋转的值代码:50.
所以有两件事要做:
- 计算我的对象的法向量
- 给定对象的世界坐标,计算对象坐标。
为了计算法向量,我使用四元数来计算应用到世界 z 轴 (0,0,1) 的旋转,如果应用了偏航角、俯仰角或滚动角(如果没有,我只是使用世界 z 轴)。我可以使用相同的四元数来计算任意 x 轴和 y 轴,对现实世界中的每个 x 轴和 y 轴使用相同的旋转。
对于那些寻找更多信息的人,这个 link 对如何计算它们有非常清楚的解释。
https://danceswithcode.net/engineeringnotes/quaternions/quaternions.html
为了帮助您,此计算器可以通过检查结果来帮助确认是否已正确实施:
然后为了计算物体坐标,我简单地将世界坐标与任意 axes/normal 轴中的每一个交叉相乘。
对于大多数情况,这似乎都有效。但是,我对在任何地方都看不到的其他规则和要求感到茫然:
如果只应用偏航角(绕z旋转),那么法线仍然是(0,0,1)。我们需要在俯仰和滚动为零的情况下应用关于 z 的旋转。
如果我改变俯仰角,法向量可以从负 y 坐标变为正坐标。斜率的这种变化似乎会影响矢量的前进方向,如果为正,则需要绕法线旋转 180 度才能校正它。
由于某些未知原因,当我应用滚动角时,我的对象绕法线旋转了 90 度,我需要在此处应用校正。
我正在努力在这个网上找到更明确的方向。有没有人有任何详尽的解释来描述上述行为,或任何指针 material?
正如另一个主题所建议的,一点点矩阵微积分就可以解决这个问题。
简而言之,它们主要是两种描述方式相同Euler angles:
- Tait-Bryan 角度(偏航、俯仰、横滚)绕 X、Y、Z 轴旋转;
- 围绕 Z、X、Z 轴的固有欧拉角(进动、章动、本征旋转)旋转;
AutoCAD 使用第二个来描述块方向。进动和章动的组合提供了OCS变换矩阵,固有旋转是块的旋转值。
使用 3x3 矩阵描述 3d 旋转的 F# 小示例。
type vector = float list
module Vector =
// Computes the dot product of two vectors
let dotProduct (v1: vector) (v2: vector) = List.map2 (*) v1 v2 |> List.sum
type matrix(rows: vector list) =
// Gets the row vectors
member _.Rows = rows
// Transposes a matrix
member _.Transpose = List.transpose rows |> matrix
// Applies a matrix to a vector
static member (*)(m: matrix, v: vector) =
List.map (Vector.dotProduct v) m.Rows
// Multipies two matrices
static member (*)(m: matrix, q: matrix) =
let trp = q.Transpose
List.map (fun r -> trp * r) m.Rows |> matrix
// Describes a coordinate system data
type CoordinateSystem =
{ WcsToOcs: matrix
Normal: vector
Rotation: float }
// Matrix 3x3
module Matrix3x3 =
// Gets the identity matrix
let identity =
matrix [ [ 1.0; 0.0; 0.0 ]
[ 0.0; 1.0; 0.0 ]
[ 0.0; 0.0; 1.0 ] ]
// Gets the rotation matrix about X axis
let xRotation a =
matrix [ [ 1.0; 0.0; 0.0 ]
[ 0.0; cos a; -sin a ]
[ 0.0; sin a; cos a ] ]
// Gets the rotation matrix about Y axis
let yRotation a =
matrix [ [ cos a; 0.0; sin a ]
[ 0.0; 1.0; 0.0 ]
[ -sin a; 0.0; cos a ] ]
// Gets the rotation matrix about Z axis
let zRotation a =
matrix [ [ cos a; -sin a; 0.0 ]
[ sin a; cos a; 0.0 ]
[ 0.0; 0.0; 1.0 ] ]
// Creates the matrix according to Yaw, Pitch and Roll values
let createFromYawPitchRoll yaw pitch roll =
zRotation yaw * yRotation pitch * xRotation roll
// Gets the coordinate system data from a matrix 3x3
let getCoordinateSystem (mat: matrix) =
match mat.Rows with
| [ [ m00; m01; m02 ]; [ m10; m11; m12 ]; [ m20; m21; m22 ] ] ->
let nutation = acos m22
if abs nutation < 1e-8 then
{ WcsToOcs = identity
Normal = [ 0.0; 0.0; 1.0 ]
Rotation = atan2 m10 m11 }
else
let precession = atan2 m02 -m12
let spin = atan2 m20 m21
let xform =
(zRotation precession * xRotation nutation)
.Transpose
let normal = xform.Rows.Item 2
{ WcsToOcs = xform
Normal = normal
Rotation = spin }
| _ -> invalidArg "mat" "Invalid 3x3 matrix"
// Testing
module test =
let radians x = x * System.Math.PI / 180.0
// Input yaw, pitch, roll angles and WCS point
let yaw = radians 10.0
let pitch = radians 20.0
let roll = radians 30.0
let wcsPoint = [ 8.0; 5.0; 3.0 ]
// Computation of the coordinate system
let ocs =
Matrix3x3.createFromYawPitchRoll yaw pitch roll
|> Matrix3x3.getCoordinateSystem
let ocsPoint = ocs.WcsToOcs * wcsPoint
// Print results
printfn "Normal X (210): %f" (ocs.Normal.Item 0)
printfn "Normal Y (220): %f" (ocs.Normal.Item 1)
printfn "Normal Z (230): %f" (ocs.Normal.Item 2)
printfn "Rotation (50): %f" ocs.Rotation
printfn "OCS point X (10): %f" (ocsPoint.Item 0)
printfn "OCS point Y (20): %f" (ocsPoint.Item 1)
printfn "OCS point Z (30): %f" (ocsPoint.Item 2)
C# 实现:
using static System.Math;
using static System.Console;
namespace CsharpMatrix3d
{
class Program
{
/// <summary>
/// Console testing
/// </summary>
static void Main()
{
double yaw = D2R(10.0);
double pitch = D2R(20.0);
double roll = D2R(30.0);
var wcsPoint = new Vector(8.0, 5.0, 3.0);
var ocs = ObjectCoordinateSystem.FromYawPitchRoll(yaw, pitch, roll);
var ocsPoint = ocs.WorldToPlane * wcsPoint;
WriteLine($"Normal X (210): {ocs.Normal.X}");
WriteLine($"Normal Y (220): {ocs.Normal.Y}");
WriteLine($"Normal Z (230): {ocs.Normal.Z}");
WriteLine($"Rotation (50): {ocs.Rotation}");
WriteLine($"OCS point X (10): {ocsPoint.X}");
WriteLine($"OCS point Y (20): {ocsPoint.Y}");
WriteLine($"OCS point Z (30): {ocsPoint.Z}");
}
static double D2R(double x) => x * PI / 180.0;
}
/// <summary>
/// Provides properties and method for an object coordinate system
/// </summary>
struct ObjectCoordinateSystem
{
public ObjectCoordinateSystem(Matrix3x3 m)
{
double nutation = Acos(m.Row2.Z);
if (Abs(nutation) < 1e-8)
{
Normal = new Vector(0.0, 0.0, 1.0);
Rotation = Atan2(m.Row1.X, m.Row1.Y);
WorldToPlane = Matrix3x3.Identity;
}
else
{
var precession = Atan2(m.Row0.Z, -m.Row1.Z);
WorldToPlane = (Matrix3x3.ZRotate(precession) * Matrix3x3.XRotate(nutation)).Transpose();
Normal = WorldToPlane.Row2;
Rotation = Atan2(m.Row2.X, m.Row2.Y);
}
}
public Vector Normal { get; }
public Matrix3x3 WorldToPlane { get; }
public double Rotation { get; }
public static ObjectCoordinateSystem FromYawPitchRoll(double yaw, double pitch, double roll) =>
new ObjectCoordinateSystem(
Matrix3x3.ZRotate(yaw) *
Matrix3x3.YRotate(pitch) *
Matrix3x3.XRotate(roll));
}
/// <summary>
/// Provides methods for vector calculus
/// </summary>
struct Vector
{
public Vector(double x, double y, double z)
{ X = x; Y = y; Z = z; }
public double X { get; }
public double Y { get; }
public double Z { get; }
public double DotProduct(Vector v) =>
X * v.X + Y * v.Y + Z * v.Z;
public static Vector operator *(Matrix3x3 m, Vector v) =>
new Vector(m.Row0.DotProduct(v), m.Row1.DotProduct(v), m.Row2.DotProduct(v));
}
/// <summary>
/// Provides methods for matrix calculus
/// </summary>
struct Matrix3x3
{
public Matrix3x3(Vector row0, Vector row1, Vector row2)
{ Row0 = row0; Row1 = row1; Row2 = row2; }
public Vector Row0 { get; }
public Vector Row1 { get; }
public Vector Row2 { get; }
public static Matrix3x3 Identity =>
new Matrix3x3(
new Vector(1.0, 0.0, 0.0),
new Vector(0.0, 1.0, 0.0),
new Vector(0.0, 0.0, 1.0));
public Matrix3x3 Transpose() =>
new Matrix3x3(
new Vector(Row0.X, Row1.X, Row2.X),
new Vector(Row0.Y, Row1.Y, Row2.Y),
new Vector(Row0.Z, Row1.Z, Row2.Z));
public static Matrix3x3 XRotate(double a) =>
new Matrix3x3(
new Vector(1.0, 0.0, 0.0),
new Vector(0.0, Cos(a), -Sin(a)),
new Vector(0.0, Sin(a), Cos(a)));
public static Matrix3x3 YRotate(double a) =>
new Matrix3x3(
new Vector(Cos(a), 0.0, Sin(a)),
new Vector(0.0, 1.0, 0.0),
new Vector(-Sin(a), 0.0, Cos(a)));
public static Matrix3x3 ZRotate(double a) =>
new Matrix3x3(
new Vector(Cos(a), -Sin(a), 0.0),
new Vector(Sin(a), Cos(a), 0.0),
new Vector(0.0, 0.0, 1.0));
public static Matrix3x3 operator *(Matrix3x3 a, Matrix3x3 b)
{
var trp = b.Transpose();
return new Matrix3x3(trp * a.Row0, trp * a.Row1, trp * a.Row2);
}
}
}
几天来我一直在摸不着头脑,想了解如何完整描述要插入到 DXF 中的块。对于背景,我对如何执行此操作的规则和行为感兴趣,而不仅仅是尝试查找现有库。
我可以在 dxf 中创建一个块,假设这是一个简单的立方体。然后我可以在实体部分插入这个块。 为此,我输入了相对于对象坐标系代码的坐标:(10, 20, 30),以及对象代码的法向量:(210,220,230)。还有一个绕法向量旋转的值代码:50.
所以有两件事要做:
- 计算我的对象的法向量
- 给定对象的世界坐标,计算对象坐标。
为了计算法向量,我使用四元数来计算应用到世界 z 轴 (0,0,1) 的旋转,如果应用了偏航角、俯仰角或滚动角(如果没有,我只是使用世界 z 轴)。我可以使用相同的四元数来计算任意 x 轴和 y 轴,对现实世界中的每个 x 轴和 y 轴使用相同的旋转。 对于那些寻找更多信息的人,这个 link 对如何计算它们有非常清楚的解释。 https://danceswithcode.net/engineeringnotes/quaternions/quaternions.html
为了帮助您,此计算器可以通过检查结果来帮助确认是否已正确实施:
然后为了计算物体坐标,我简单地将世界坐标与任意 axes/normal 轴中的每一个交叉相乘。
对于大多数情况,这似乎都有效。但是,我对在任何地方都看不到的其他规则和要求感到茫然:
如果只应用偏航角(绕z旋转),那么法线仍然是(0,0,1)。我们需要在俯仰和滚动为零的情况下应用关于 z 的旋转。
如果我改变俯仰角,法向量可以从负 y 坐标变为正坐标。斜率的这种变化似乎会影响矢量的前进方向,如果为正,则需要绕法线旋转 180 度才能校正它。
由于某些未知原因,当我应用滚动角时,我的对象绕法线旋转了 90 度,我需要在此处应用校正。
我正在努力在这个网上找到更明确的方向。有没有人有任何详尽的解释来描述上述行为,或任何指针 material?
正如另一个主题所建议的,一点点矩阵微积分就可以解决这个问题。
简而言之,它们主要是两种描述方式相同Euler angles:
- Tait-Bryan 角度(偏航、俯仰、横滚)绕 X、Y、Z 轴旋转;
- 围绕 Z、X、Z 轴的固有欧拉角(进动、章动、本征旋转)旋转; AutoCAD 使用第二个来描述块方向。进动和章动的组合提供了OCS变换矩阵,固有旋转是块的旋转值。
使用 3x3 矩阵描述 3d 旋转的 F# 小示例。
type vector = float list
module Vector =
// Computes the dot product of two vectors
let dotProduct (v1: vector) (v2: vector) = List.map2 (*) v1 v2 |> List.sum
type matrix(rows: vector list) =
// Gets the row vectors
member _.Rows = rows
// Transposes a matrix
member _.Transpose = List.transpose rows |> matrix
// Applies a matrix to a vector
static member (*)(m: matrix, v: vector) =
List.map (Vector.dotProduct v) m.Rows
// Multipies two matrices
static member (*)(m: matrix, q: matrix) =
let trp = q.Transpose
List.map (fun r -> trp * r) m.Rows |> matrix
// Describes a coordinate system data
type CoordinateSystem =
{ WcsToOcs: matrix
Normal: vector
Rotation: float }
// Matrix 3x3
module Matrix3x3 =
// Gets the identity matrix
let identity =
matrix [ [ 1.0; 0.0; 0.0 ]
[ 0.0; 1.0; 0.0 ]
[ 0.0; 0.0; 1.0 ] ]
// Gets the rotation matrix about X axis
let xRotation a =
matrix [ [ 1.0; 0.0; 0.0 ]
[ 0.0; cos a; -sin a ]
[ 0.0; sin a; cos a ] ]
// Gets the rotation matrix about Y axis
let yRotation a =
matrix [ [ cos a; 0.0; sin a ]
[ 0.0; 1.0; 0.0 ]
[ -sin a; 0.0; cos a ] ]
// Gets the rotation matrix about Z axis
let zRotation a =
matrix [ [ cos a; -sin a; 0.0 ]
[ sin a; cos a; 0.0 ]
[ 0.0; 0.0; 1.0 ] ]
// Creates the matrix according to Yaw, Pitch and Roll values
let createFromYawPitchRoll yaw pitch roll =
zRotation yaw * yRotation pitch * xRotation roll
// Gets the coordinate system data from a matrix 3x3
let getCoordinateSystem (mat: matrix) =
match mat.Rows with
| [ [ m00; m01; m02 ]; [ m10; m11; m12 ]; [ m20; m21; m22 ] ] ->
let nutation = acos m22
if abs nutation < 1e-8 then
{ WcsToOcs = identity
Normal = [ 0.0; 0.0; 1.0 ]
Rotation = atan2 m10 m11 }
else
let precession = atan2 m02 -m12
let spin = atan2 m20 m21
let xform =
(zRotation precession * xRotation nutation)
.Transpose
let normal = xform.Rows.Item 2
{ WcsToOcs = xform
Normal = normal
Rotation = spin }
| _ -> invalidArg "mat" "Invalid 3x3 matrix"
// Testing
module test =
let radians x = x * System.Math.PI / 180.0
// Input yaw, pitch, roll angles and WCS point
let yaw = radians 10.0
let pitch = radians 20.0
let roll = radians 30.0
let wcsPoint = [ 8.0; 5.0; 3.0 ]
// Computation of the coordinate system
let ocs =
Matrix3x3.createFromYawPitchRoll yaw pitch roll
|> Matrix3x3.getCoordinateSystem
let ocsPoint = ocs.WcsToOcs * wcsPoint
// Print results
printfn "Normal X (210): %f" (ocs.Normal.Item 0)
printfn "Normal Y (220): %f" (ocs.Normal.Item 1)
printfn "Normal Z (230): %f" (ocs.Normal.Item 2)
printfn "Rotation (50): %f" ocs.Rotation
printfn "OCS point X (10): %f" (ocsPoint.Item 0)
printfn "OCS point Y (20): %f" (ocsPoint.Item 1)
printfn "OCS point Z (30): %f" (ocsPoint.Item 2)
C# 实现:
using static System.Math;
using static System.Console;
namespace CsharpMatrix3d
{
class Program
{
/// <summary>
/// Console testing
/// </summary>
static void Main()
{
double yaw = D2R(10.0);
double pitch = D2R(20.0);
double roll = D2R(30.0);
var wcsPoint = new Vector(8.0, 5.0, 3.0);
var ocs = ObjectCoordinateSystem.FromYawPitchRoll(yaw, pitch, roll);
var ocsPoint = ocs.WorldToPlane * wcsPoint;
WriteLine($"Normal X (210): {ocs.Normal.X}");
WriteLine($"Normal Y (220): {ocs.Normal.Y}");
WriteLine($"Normal Z (230): {ocs.Normal.Z}");
WriteLine($"Rotation (50): {ocs.Rotation}");
WriteLine($"OCS point X (10): {ocsPoint.X}");
WriteLine($"OCS point Y (20): {ocsPoint.Y}");
WriteLine($"OCS point Z (30): {ocsPoint.Z}");
}
static double D2R(double x) => x * PI / 180.0;
}
/// <summary>
/// Provides properties and method for an object coordinate system
/// </summary>
struct ObjectCoordinateSystem
{
public ObjectCoordinateSystem(Matrix3x3 m)
{
double nutation = Acos(m.Row2.Z);
if (Abs(nutation) < 1e-8)
{
Normal = new Vector(0.0, 0.0, 1.0);
Rotation = Atan2(m.Row1.X, m.Row1.Y);
WorldToPlane = Matrix3x3.Identity;
}
else
{
var precession = Atan2(m.Row0.Z, -m.Row1.Z);
WorldToPlane = (Matrix3x3.ZRotate(precession) * Matrix3x3.XRotate(nutation)).Transpose();
Normal = WorldToPlane.Row2;
Rotation = Atan2(m.Row2.X, m.Row2.Y);
}
}
public Vector Normal { get; }
public Matrix3x3 WorldToPlane { get; }
public double Rotation { get; }
public static ObjectCoordinateSystem FromYawPitchRoll(double yaw, double pitch, double roll) =>
new ObjectCoordinateSystem(
Matrix3x3.ZRotate(yaw) *
Matrix3x3.YRotate(pitch) *
Matrix3x3.XRotate(roll));
}
/// <summary>
/// Provides methods for vector calculus
/// </summary>
struct Vector
{
public Vector(double x, double y, double z)
{ X = x; Y = y; Z = z; }
public double X { get; }
public double Y { get; }
public double Z { get; }
public double DotProduct(Vector v) =>
X * v.X + Y * v.Y + Z * v.Z;
public static Vector operator *(Matrix3x3 m, Vector v) =>
new Vector(m.Row0.DotProduct(v), m.Row1.DotProduct(v), m.Row2.DotProduct(v));
}
/// <summary>
/// Provides methods for matrix calculus
/// </summary>
struct Matrix3x3
{
public Matrix3x3(Vector row0, Vector row1, Vector row2)
{ Row0 = row0; Row1 = row1; Row2 = row2; }
public Vector Row0 { get; }
public Vector Row1 { get; }
public Vector Row2 { get; }
public static Matrix3x3 Identity =>
new Matrix3x3(
new Vector(1.0, 0.0, 0.0),
new Vector(0.0, 1.0, 0.0),
new Vector(0.0, 0.0, 1.0));
public Matrix3x3 Transpose() =>
new Matrix3x3(
new Vector(Row0.X, Row1.X, Row2.X),
new Vector(Row0.Y, Row1.Y, Row2.Y),
new Vector(Row0.Z, Row1.Z, Row2.Z));
public static Matrix3x3 XRotate(double a) =>
new Matrix3x3(
new Vector(1.0, 0.0, 0.0),
new Vector(0.0, Cos(a), -Sin(a)),
new Vector(0.0, Sin(a), Cos(a)));
public static Matrix3x3 YRotate(double a) =>
new Matrix3x3(
new Vector(Cos(a), 0.0, Sin(a)),
new Vector(0.0, 1.0, 0.0),
new Vector(-Sin(a), 0.0, Cos(a)));
public static Matrix3x3 ZRotate(double a) =>
new Matrix3x3(
new Vector(Cos(a), -Sin(a), 0.0),
new Vector(Sin(a), Cos(a), 0.0),
new Vector(0.0, 0.0, 1.0));
public static Matrix3x3 operator *(Matrix3x3 a, Matrix3x3 b)
{
var trp = b.Transpose();
return new Matrix3x3(trp * a.Row0, trp * a.Row1, trp * a.Row2);
}
}
}