前言

想必大家在使用 SolidWorks 时,尤其是想要截图的时候,因为视角的关系,竖直方向的边线通常会出现锯齿(1080p 及以下的屏幕尤为明显)。而如果手动通过鼠标改变视角到边线无锯齿又很困难,因此打算写一个能自动改变视角的宏程序。

Y向边线有锯齿
Y 向边线有锯齿
Y向边线无锯齿
Y 向边线无锯齿

原理

根据 SolidWorks 官方 api 文档 IMathTransform Interface - 2022 - SOLIDWORKS API Help 可知,控制视角的是一个 3x3 的单位矩阵(a~i)。再结合 Orientation3 这个函数,就有矩阵数组

abcn
defo
ghip
jklm
01213
34514
67815
9101112
  • 第 0-2 位:X 轴单位向量(坐标)。
  • 第 3-5 位:Y 轴单位向量(坐标)。
  • 第 6-7 位:Z 轴单位向量(坐标)。
  • 第 9-11 位:平移分量,本文不涉及。
  • 第 12 位:缩放因子,一般为 1 就行。
  • 第 13-15 位:保留,不使用。

可能有些人会很奇怪,坐标轴的向量不应该是固定的吗,为什么会变呢。这是因为旋转时,视角和绝对坐标系不是重合的,这里的 XYZ 坐标轴单位向量可以理解为两者之差。

主要算法逻辑:把 XYZ 其中的一个坐标轴向量的 X 值设置为 0,算出旋转轴和角度后,再按公式计算出剩下的坐标轴向量。

实操

获取当前视角

这里注意,Orientation3 的返回值不是 Variant 而是 Object,所以需要用 ArrayData 方法获取 VariantFor 循环里的就是按 XYZ 打印的视角向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swModelView As SldWorks.ModelView
Dim Matrix As SldWorks.MathTransform

Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
Set swModelView = swModel.ActiveView
Set Matrix = swModelView.Orientation3

Debug.Print "Orignal Orientation Martrix: "
Dim i As Integer
For i = 0 To 9 Step 3
Debug.Print Matrix.ArrayData(i), Matrix.ArrayData(i + 1), Matrix.ArrayData(i + 2)
Next i

向量点乘

因为 VBA 没有专门的向量点乘方法,所以需要自己写一个。关于什么是向量点乘请参阅几何向量的点乘 - 小时百科

1
2
3
Function Dot(m As Variant, n As Variant) As Variant
Dot = Array(m(0) * n(0), m(1) * n(1), m(2) * n(2))
End Function

向量叉乘

因为 VBA 没有专门的向量叉乘方法,所以需要自己写一个。关于什么是向量叉乘请参阅几何向量的叉乘 - 小时百科

1
2
3
4
5
6
Function Cross(m As Variant, n As Variant) As Variant
Cross = Array( _
m(1) * n(2) - m(2) * n(1), _
m(2) * n(0) - m(0) * n(2), _
m(0) * n(1) - m(1) * n(0))
End Function

罗德里格旋转公式

这是三维空间中,一个向量绕旋转轴旋转给定角度以后得到的新向量的计算公式。这个公式使用原向量、旋转轴及它们叉积表示出旋转后的向量。详情可参阅罗德里格旋转公式、定轴旋转矩阵 - 小时百科

罗德里格旋转公式图示
罗德里格旋转公式图示

$$ \boldsymbol{\mathbf{r}} ‘ = \boldsymbol{\mathbf{r}} \cos\theta + \overrightarrow{\boldsymbol{\mathbf{A}}} \boldsymbol\times \boldsymbol{\mathbf{r}} \sin\theta + \overrightarrow{\boldsymbol{\mathbf{A}}} ( \overrightarrow{\boldsymbol{\mathbf{A}}} \boldsymbol\cdot \boldsymbol{\mathbf{r}} ) (1 - \cos\theta)~ $$

1
2
3
4
5
6
7
'm原向量;n旋转轴向量;a旋转角度
Function Rodrigues(m As Variant, n As Variant, a As Double) As Variant
Rodrigues = Array( _
m(0) * Cos(a) + Sin(a) * Cross(n, m)(0) + (1 - Cos(a)) * Dot(n, Dot(n, m))(0), _
m(1) * Cos(a) + Sin(a) * Cross(n, m)(1) + (1 - Cos(a)) * Dot(n, Dot(n, m))(1), _
m(2) * Cos(a) + Sin(a) * Cross(n, m)(2) + (1 - Cos(a)) * Dot(n, Dot(n, m))(2))
End Function

小试牛刀

这里假设了把 Y 轴向量的 X 值设为 0,其 Y 值不变,先算出其 Z 值,再算出 Y 轴的旋转轴向量和角度,以此算出 X 和 Z 轴向量。其他情况同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Dim x(2) As Variant
Dim y(2) As Variant
Dim z(2) As Variant

'|Y|=1
y(0) = 0
y(1) = Matrix.ArrayData(4)
y(2) = IIf(Matrix.ArrayData(5) > 0, Sqrt(1 - Matrix.ArrayData(4) ^ 2), -Sqrt(1 - Matrix.ArrayData(4) ^ 2))

Dim ar(2) As Variant '旋转轴向量
ar(0) = Cross(Array(Matrix.ArrayData(3), Matrix.ArrayData(4), Matrix.ArrayData(5)), y)(0)
ar(1) = Cross(Array(Matrix.ArrayData(3), Matrix.ArrayData(4), Matrix.ArrayData(5)), y)(1)
ar(2) = Cross(Array(Matrix.ArrayData(3), Matrix.ArrayData(4), Matrix.ArrayData(5)), y)(2)

Dim k As Single '转换为单位向量(模=1)
k = 1 / Sqrt(ar(0) ^ 2 + ar(1) ^ 2 + ar(2) ^ 2)
ar(0) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(0)
ar(1) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(1)
ar(2) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(2)

Dim a As Double '旋转角度
a = Arccos(y(1) ^ 2 + Matrix.ArrayData(5) * y(2))

'按罗德里格旋转公式算出XZ轴旋转后的向量
x(0) = Rodrigues(Array(Matrix.ArrayData(0), Matrix.ArrayData(1), Matrix.ArrayData(2)), ar, a)(0)
x(1) = Rodrigues(Array(Matrix.ArrayData(0), Matrix.ArrayData(1), Matrix.ArrayData(2)), ar, a)(1)
x(2) = Rodrigues(Array(Matrix.ArrayData(0), Matrix.ArrayData(1), Matrix.ArrayData(2)), ar, a)(2)
z(0) = Rodrigues(Array(Matrix.ArrayData(6), Matrix.ArrayData(7), Matrix.ArrayData(8)), ar, a)(0)
z(1) = Rodrigues(Array(Matrix.ArrayData(6), Matrix.ArrayData(7), Matrix.ArrayData(8)), ar, a)(1)
z(2) = Rodrigues(Array(Matrix.ArrayData(6), Matrix.ArrayData(7), Matrix.ArrayData(8)), ar, a)(2)

那么怎么转过去呢,不能直接对 Orientation3 赋值,需要用 CreateTransform 转换为矩阵向量对象,具体如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
'新的视角矩阵向量
Dim rotationArray(15) As Double
rotationArray(0) = x(0): rotationArray(1) = x(1): rotationArray(2) = x(2): rotationArray(13) = 1
rotationArray(3) = y(0): rotationArray(4) = y(1): rotationArray(5) = y(2): rotationArray(14) = 0
rotationArray(6) = z(0): rotationArray(7) = z(1): rotationArray(8) = z(2): rotationArray(15) = 0
rotationArray(9) = Matrix.ArrayData(9): rotationArray(10) = Matrix.ArrayData(10): rotationArray(11) = Matrix.ArrayData(11): rotationArray(12) = 1

Dim swMathUtil As SldWorks.MathUtility
Dim swTransform As SldWorks.MathTransform
Set swMathUtil = swApp.GetMathUtility
Set swTransform = swMathUtil.CreateTransform(rotationArray)
swModelView.Orientation3 = swTransform
swModel.GraphicsRedraw2 '刷新画面

完整代码

点我以展示代码(点 Copy 可以复制)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
Option Explicit

Sub Main()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swModelView As SldWorks.ModelView
Dim viewMatrix As SldWorks.MathTransform
Dim i As Integer

Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
Set swModelView = swModel.ActiveView

' Orignal Orientation Martrix
Set viewMatrix = swModelView.Orientation3
Debug.Print "Orignal Orientation Martrix: "
For i = 0 To 9 Step 3
Debug.Print viewMatrix.ArrayData(i), viewMatrix.ArrayData(i + 1), viewMatrix.ArrayData(i + 2), viewMatrix.ArrayData(i / 3 + 12)
Next i

' Determine the Most Appropriate Axis
' X
Dim x(2) As Variant
Dim y(2) As Variant
Dim z(2) As Variant
Dim ar(2) As Variant
Dim alpha As Double
Dim k As Single
If viewMatrix.ArrayData(0) * viewMatrix.ArrayData(3) * viewMatrix.ArrayData(6) = 0 Then
Debug.Print "No Need to Rotate"
End
ElseIf Abs(viewMatrix.ArrayData(0)) < Abs(viewMatrix.ArrayData(3)) And Abs(viewMatrix.ArrayData(0)) < Abs(viewMatrix.ArrayData(6)) Then
x(0) = 0
x(1) = viewMatrix.ArrayData(1)
x(2) = IIf(viewMatrix.ArrayData(2) > 0, Sqrt(1 - viewMatrix.ArrayData(1) ^ 2), -Sqrt(1 - viewMatrix.ArrayData(1) ^ 2))

ar(0) = Cross(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), x)(0)
ar(1) = Cross(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), x)(1)
ar(2) = Cross(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), x)(2)
k = 1 / Sqrt(ar(0) ^ 2 + ar(1) ^ 2 + ar(2) ^ 2)
ar(0) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(0)
ar(1) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(1)
ar(2) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(2)

alpha = Arccos(x(1) ^ 2 + viewMatrix.ArrayData(3) * x(2))

y(0) = Rodrigues(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), ar, alpha)(0)
y(1) = Rodrigues(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), ar, alpha)(1)
y(2) = Rodrigues(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), ar, alpha)(2)
z(0) = Rodrigues(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), ar, alpha)(0)
z(1) = Rodrigues(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), ar, alpha)(1)
z(2) = Rodrigues(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), ar, alpha)(2)
' Y
ElseIf Abs(viewMatrix.ArrayData(3)) < Abs(viewMatrix.ArrayData(0)) And Abs(viewMatrix.ArrayData(3)) < Abs(viewMatrix.ArrayData(6)) Then
y(0) = 0
y(1) = viewMatrix.ArrayData(4)
y(2) = IIf(viewMatrix.ArrayData(5) > 0, Sqrt(1 - viewMatrix.ArrayData(4) ^ 2), -Sqrt(1 - viewMatrix.ArrayData(4) ^ 2))

ar(0) = Cross(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), y)(0)
ar(1) = Cross(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), y)(1)
ar(2) = Cross(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), y)(2)

k = 1 / Sqrt(ar(0) ^ 2 + ar(1) ^ 2 + ar(2) ^ 2)
ar(0) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(0)
ar(1) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(1)
ar(2) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(2)

alpha = Arccos(y(1) ^ 2 + viewMatrix.ArrayData(5) * y(2))

x(0) = Rodrigues(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), ar, alpha)(0)
x(1) = Rodrigues(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), ar, alpha)(1)
x(2) = Rodrigues(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), ar, alpha)(2)
z(0) = Rodrigues(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), ar, alpha)(0)
z(1) = Rodrigues(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), ar, alpha)(1)
z(2) = Rodrigues(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), ar, alpha)(2)
' Z
Else
z(0) = 0
z(1) = viewMatrix.ArrayData(7)
z(2) = IIf(viewMatrix.ArrayData(8) > 0, Sqrt(1 - viewMatrix.ArrayData(7) ^ 2), -Sqrt(1 - viewMatrix.ArrayData(7) ^ 2))

ar(0) = Cross(Array(viewMatrix.ArrayData(6), viewMatrix.ArrayData(7), viewMatrix.ArrayData(8)), z)(0)

k = 1 / Sqrt(ar(0) ^ 2 + ar(1) ^ 2 + ar(2) ^ 2)
ar(0) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(0)
ar(1) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(1)
ar(2) = Array(ar(0) * k, ar(1) * k, ar(2) * k)(2)

alpha = Arccos(z(1) ^ 2 + viewMatrix.ArrayData(8) * z(2))

x(0) = Rodrigues(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), ar, alpha)(0)
x(1) = Rodrigues(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), ar, alpha)(1)
x(2) = Rodrigues(Array(viewMatrix.ArrayData(0), viewMatrix.ArrayData(1), viewMatrix.ArrayData(2)), ar, alpha)(2)
y(0) = Rodrigues(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), ar, alpha)(0)
y(1) = Rodrigues(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), ar, alpha)(1)
y(2) = Rodrigues(Array(viewMatrix.ArrayData(3), viewMatrix.ArrayData(4), viewMatrix.ArrayData(5)), ar, alpha)(2)
End If

Debug.Print Chr(10) & "Rotation Axis: ", ar(0), ar(1), ar(2)
Debug.Print "Rotation Angel: " & alpha
Debug.Print Chr(10) & "New Orientation Martrix: "
Debug.Print x(0), x(1), x(2)
Debug.Print y(0), y(1), y(2)
Debug.Print z(0), z(1), z(2)

Dim rotationArray(15) As Double
rotationArray(0) = x(0): rotationArray(1) = x(1): rotationArray(2) = x(2): rotationArray(12) = 1
rotationArray(3) = y(0): rotationArray(4) = y(1): rotationArray(5) = y(2): rotationArray(13) = 0
rotationArray(6) = z(0): rotationArray(7) = z(1): rotationArray(8) = z(2): rotationArray(14) = 0
rotationArray(9) = viewMatrix.ArrayData(9): rotationArray(10) = viewMatrix.ArrayData(10): rotationArray(11) = viewMatrix.ArrayData(11): rotationArray(15) = 0

Dim swMathUtil As SldWorks.MathUtility
Dim swTransform As SldWorks.MathTransform
Set swMathUtil = swApp.GetMathUtility
Set swTransform = swMathUtil.CreateTransform(rotationArray)
swModelView.Orientation3 = swTransform
swModel.GraphicsRedraw2

End Sub

'Rodrigues' rotation formula
Function Rodrigues(m As Variant, n As Variant, alpha As Double) As Variant

Rodrigues = Array(m(0) * Cos(alpha) + Sin(alpha) * Cross(n, m)(0) + (1 - Cos(alpha)) * Dot(n, Dot(n, m))(0), m(1) * Cos(alpha) + Sin(alpha) * Cross(n, m)(1) + (1 - Cos(alpha)) * Dot(n, Dot(n, m))(1), m(2) * Cos(alpha) + Sin(alpha) * Cross(n, m)(2) + (1 - Cos(alpha)) * Dot(n, Dot(n, m))(2))

End Function

'Cross
Function Cross(m As Variant, n As Variant) As Variant

Cross = Array(m(1) * n(2) - m(2) * n(1), m(2) * n(0) - m(0) * n(2), m(0) * n(1) - m(1) * n(0))

End Function

'Dot
Function Dot(m As Variant, n As Variant) As Variant

Dot = Array(m(0) * n(0), m(1) * n(1), m(2) * n(2))

End Function


网站地图 | 状态监测 | 图片加密&解密 | File Server | 博友圈 | 博客说
Copyright 2022-2025 | Powered by Hexo 7.3.0 & Stellar 1.29.1
总访问量次 |