UVa12303 Composite Transformations
UVa12303 Composite Transformations
- 题目链接
- 题意
- 输入格式
- 输出格式
- 分析
- AC 代码
题目链接
UVa12303 Composite Transformations
题意
空间中有n个点和m个平面,你的任务是按顺序向它们施加t个变换,输出每个点的最终位置和每个平面的最终方程。一共有3种变换,如表下表所示。
变换 | 说明 |
---|---|
TRANSLATE a b c | 点(x,y,z)变成(x+a,y+b,z+c) |
ROTATE a b c theta | 把每个点旋转theta 度。旋转轴是向量(a,b,c),并且穿过原点。当旋转轴指向你时,旋转看上去是逆时针的 |
SCALE a b c | 点(x,y,z)变成(ax,by,cz) |
输入格式
输入只有一组数据。第一行是3个整数n, m, t(1≤n,m≤50 000,1≤t≤1 000);以下n行每行包含一个点的坐标;用以下m行每行4个整数a, b, c, d,描述一个平面ax+by+cz+d=0(a, b, c 不全为0);以下t行描述各个操作。操作中的参数a, b, c, d 是绝对值不超过10 的实数,最多保留小数点后两位。参数theta 是0~359 之间的整数。
输出格式
对于每个点,输出一行,即变换后的坐标。对于每个平面,输出一行,即变换后平面方程的参数a, b, c 和d。输出应保证 a 2 + b 2 + c 2 = 1 a^2+b^2+c^2=1 a2+b2+c2=1,但如果有多种表示方法,任意一种均可。输出应保留两位小数。本题允许一定的浮点误差。
分析
先说几个坑点:1、题面交代输入用4个整数a, b, c, d描述一个平面,实际数据会有浮点数,所以应该用double接收,否则WA;2、题面说输出接受0.05的误差,可能是相对误差(即5%),因为生成测试数据后Udebug输出的结果和lrj仓库代码输出的结果相差挺大的,所以果断全程double来算就行了。
三个仿射变换矩阵中,平移、缩放矩阵好写,难的是旋转矩阵,具体可参考三维仿射变换矩阵。
绕原点且单位法向量为
(
a
,
b
,
c
)
(a,b,c)
(a,b,c)的轴旋转的变换矩阵为:
[
cos
θ
+
a
2
(
1
−
cos
θ
)
a
b
(
1
−
cos
θ
)
−
c
sin
θ
a
c
(
1
−
cos
θ
)
+
b
sin
θ
0
a
b
(
1
−
cos
θ
)
+
c
sin
θ
cos
θ
+
b
2
(
1
−
cos
θ
)
b
c
(
1
−
c
o
s
θ
)
−
a
sin
θ
0
a
c
(
1
−
cos
θ
)
−
b
sin
θ
b
c
(
1
−
cos
θ
)
+
a
sin
θ
cos
θ
+
c
2
(
1
−
cos
θ
)
0
0
0
0
1
]
\begin{bmatrix} \cos\theta + a^2(1-\cos\theta) & ab(1-\cos\theta)-c\sin\theta & ac(1-\cos\theta)+b\sin\theta & 0 \\ ab(1-\cos\theta)+c\sin\theta & \cos\theta+b^2(1-\cos\theta) & bc(1-cos\theta)-a\sin\theta & 0 \\ ac(1-\cos\theta)-b\sin\theta & bc(1-\cos\theta)+a\sin\theta & \cos\theta+c^2(1-\cos\theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
cosθ+a2(1−cosθ)ab(1−cosθ)+csinθac(1−cosθ)−bsinθ0ab(1−cosθ)−csinθcosθ+b2(1−cosθ)bc(1−cosθ)+asinθ0ac(1−cosθ)+bsinθbc(1−cosθ)−asinθcosθ+c2(1−cosθ)00001
《训练指南》建议用三点式求解平面的变换结果,那么需要解决如下问题:已知平面方程,如何让找出平面上不共线的三个点?具体可参考平面的四种方程及一些应用。
已知平面的一般式方程
a
x
+
b
y
+
c
z
+
d
=
0
\bm{ax+by+cz+d=0}
ax+by+cz+d=0,由于
a
,
b
,
c
不全为
0
\bm{a,b,c}不全为0
a,b,c不全为0,则:
a
≠
0
\bm{a \ne 0}
a=0时,点
(
−
d
a
,
0
,
0
)
,
(
−
c
+
d
a
,
0
,
1
)
,
(
−
b
+
d
a
,
1
,
0
)
\bm{(-\frac{d}{a},0,0),(-\frac{c+d}{a},0,1),(-\frac{b+d}{a},1,0)}
(−ad,0,0),(−ac+d,0,1),(−ab+d,1,0)在平面上且三点不共线;
b
≠
0
\bm{b \ne 0}
b=0时,点
(
0
,
−
d
b
,
0
)
,
(
0
,
−
c
+
d
b
,
1
)
,
(
1
,
−
a
+
d
b
,
0
)
\bm{(0,-\frac{d}{b},0),(0,-\frac{c+d}{b},1),(1,-\frac{a+d}{b},0)}
(0,−bd,0),(0,−bc+d,1),(1,−ba+d,0)在平面上且三点不共线;
c
≠
0
\bm{c \ne 0}
c=0时,点
(
0
,
0
,
−
d
c
)
,
(
0
,
1
,
−
b
+
d
c
)
,
(
1
,
0
,
−
a
+
d
c
)
\bm{(0,0, -\frac{d}{c}),(0,1,-\frac{b+d}{c}),(1,0,-\frac{a+d}{c})}
(0,0,−cd),(0,1,−cb+d),(1,0,−ca+d)在平面上且三点不共线。
编程时推荐选择
a
,
b
,
c
\bm{a,b,c}
a,b,c中绝对值最大那个来构造点。
AC 代码
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
#define N 50010
double pl[N][4], p[N][3], l[3][4], r[4][4], v[3][4], e = M_PI/180;
double mul(const double (&r)[4], const double *v3) {
return r[0]*v3[0] + r[1]*v3[1] + r[2]*v3[2] + + r[3];
}
void load_trans(double a, double b, double c) {
l[0][0] = 1.; l[0][1] = 0.; l[0][2] = 0.; l[0][3] = a;
l[1][0] = 0.; l[1][1] = 1.; l[1][2] = 0.; l[1][3] = b;
l[2][0] = 0.; l[2][1] = 0.; l[2][2] = 1.; l[2][3] = c;
}
void load_rot(double a, double b, double c, double theta) {
double si = sin(theta), co = cos(theta), s = sqrt(a*a+b*b+c*c); a /= s; b /= s; c /= s;
l[0][0] = co+a*a*(1-co); l[0][1] = a*b*(1-co)-c*si; l[0][2] = a*c*(1-co)+b*si; l[0][3] = 0.;
l[1][0] = a*b*(1-co)+c*si; l[1][1] = co+b*b*(1-co); l[1][2] = b*c*(1-co)-a*si; l[1][3] = 0.;
l[2][0] = a*c*(1-co)-b*si; l[2][1] = b*c*(1-co)+a*si; l[2][2] = co+c*c*(1-co); l[2][3] = 0.;
}
void load_scale(double a, double b, double c) {
l[0][0] = a; l[0][1] = 0.; l[0][2] = 0.; l[0][3] = 0.;
l[1][0] = 0.; l[1][1] = b; l[1][2] = 0.; l[1][3] = 0.;
l[2][0] = 0.; l[2][1] = 0.; l[2][2] = c; l[2][3] = 0.;
}
void trans_plane(double a, double b, double c, double d) {
double x = max(max(abs(a), abs(b)), abs(c));
if (abs(a) == x) {
v[0][0] = -d/a; v[0][1] = 0.; v[0][2] = .0;
v[1][0] = -(c+d)/a; v[1][1] = 0.; v[1][2] = 1.;
v[2][0] = -(b+d)/a; v[2][1] = 1.; v[2][2] = 0.;
} else if (abs(b) == x) {
v[0][0] = 0.; v[0][1] = -d/b; v[0][2] = 0.;
v[1][0] = 0.; v[1][1] = -(c+d)/b; v[1][2] = 1.;
v[2][0] = 1.; v[2][1] = -(a+d)/b; v[2][2] = 0.;
} else {
v[0][0] = 0.; v[0][1] = 0.; v[0][2] = -d/c;
v[1][0] = 0.; v[1][1] = 1.; v[1][2] = -(b+d)/c;
v[2][0] = 1.; v[2][1] = 0.; v[2][2] = -(a+d)/c;
}
for (int i=0; i<3; ++i)
l[i][0] = mul(r[0], v[i]), l[i][1] = mul(r[1], v[i]), l[i][2] = mul(r[2], v[i]);
double x1 = l[1][0]-l[0][0], y1 = l[1][1]-l[0][1], z1 = l[1][2]-l[0][2],
x2 = l[2][0]-l[0][0], y2 = l[2][1]-l[0][1], z2 = l[2][2]-l[0][2],
e = y1*z2 - z1*y2, f = x2*z1 - x1*z2, g = x1*y2 - x2*y1, s = sqrt(e*e + f*f + g*g);
e /= s; f /= s; g /= s;
cout << e << ' ' << f << ' ' << g << ' ' << -(e*l[0][0] + f*l[0][1] + g*l[0][2]) << endl;
}
void solve() {
int m, n, t; char s[10]; double a, b, c, d; cin >> n >> m >> t;
for (int i=0; i<n; ++i) cin >> p[i][0] >> p[i][1] >> p[i][2];
for (int i=0; i<m; ++i) cin >> pl[i][0] >> pl[i][1] >> pl[i][2] >> pl[i][3];
for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) r[i][j] = i==j ? 1. : 0.;
while (t--) {
cin >> s >> a >> b >> c;
if (s[0] == 'T') load_trans(a, b, c);
else if (s[0] == 'R') cin >> d, load_rot(a, b, c, d*e);
else load_scale(a, b, c);
for (int i=0; i<3; ++i) for (int j=0; j<4; ++j)
v[i][j] = l[i][0]*r[0][j] + l[i][1]*r[1][j] + l[i][2]*r[2][j] + l[i][3]*r[3][j];
for (int i=0; i<3; ++i) for (int j=0; j<4; ++j) r[i][j] = v[i][j];
}
for (int i=0; i<n; ++i) cout << mul(r[0], p[i]) << ' ' << mul(r[1], p[i]) << ' ' << mul(r[2], p[i]) << endl;
for (int i=0; i<m; ++i) trans_plane(pl[i][0], pl[i][1], pl[i][2], pl[i][3]);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cout << fixed << setprecision(2);
solve();
return 0;
}