likes
comments
collection
share

编程与数学 | 一维空间的中心缩放

作者站长头像
站长
· 阅读数 23

编程与数学 | 一维空间的中心缩放

0. 问题描述:

最近在研究一个功能,期间演化出一些比较有趣的小知识。我把它们整理成独立的问题,来分享给大家。首先来解释一下,什么叫 一维区域空间的中心缩放

比如,现在有一个刻度尺,视口区域是 [4,12] ,现在求:

若以 8 为缩放中心,将尺子放大两倍。求前视口的刻度区域。

编程与数学 | 一维空间的中心缩放


缩放中心,就是在缩放变换过程中的不动点。我们可以把尺子放大两倍后,将区域对应的缩放中心和 8 对齐。下面可以从图中看出,当前视口区域的在 [6,10]:

编程与数学 | 一维空间的中心缩放

于是,就可以抛出本文研究的目标:

一维空间中的 [a,b] 区域,以 c 点为缩放中心,缩放 s 倍,求区域此的范围。


1. 解决思路

现在先分析一下上面案例中的细节,从数学的角度上理解一下,为什么结果是 [6,10]

[4,12] 以 8 为中心放大两倍。

  • 原区域刻度长度: 12 - 4 = 8
  • 当刻度尺放大两倍,而视口区域不变。则视口刻度长度将会减半。
  • 缩放中心的刻度保持不变。

编程与数学 | 一维空间的中心缩放

根据上面三点特征,我们就可以将模型简化为,求解区域前后刻度的问题。如下所示,两个问号分别是多少:

编程与数学 | 一维空间的中心缩放

小学毕业了的朋友应该可以很轻松地报出答案:

左侧: 8 - 4/2 = 6 右侧: 8 + 4/2 = 10

我们真的将尺子放大两倍,然后观测效果,对规律进行总结,这是 物理学。而对于 数学 来说,根据已知条件,通过逻辑进行推演,就能得到答案。


2. 缩放执行非中点

如下所示,如果此时将缩放中心设置为 10 ,通过真实的缩放观察可以看出结果是 [7~11]。此时缩放中心左侧空间的占比是 (10-4)/(12-4)。尺子放大之后,区域总刻度是 4 ,则左侧空间程度 (12-4)/2 * (10-4)/(12-4) = 3 。 于是缩放后左侧刻度 10-3 = 7,右侧自然也就是 10+(4-3) = 11

编程与数学 | 一维空间的中心缩放


3. 一般化的一维空间中心缩放

现在我们可以根据推演的逻辑,将这个规律适用于一般化的场合

一维空间中的 [a,b] 区域,以 c 点为缩放中心,缩放 s 倍,求区域此的范围。

  • 原区域刻度长度: b - a
  • 当刻度尺放大 s 倍,而视口区域不变。则视口刻度长度将缩小 s 倍。
  • 缩放中心的刻度保持不变。

编程与数学 | 一维空间的中心缩放

左侧坐标: c - (b-a)/s * [(c-a)/(b-a)] 右侧坐标: c + (b-a)/s * [(b-c)/(b-a)]


4. 演算的编程实现

如下所示,定义一个 Area 类型标识区间左右刻度;scale 方法将用于对 Area 对象以 c 为中心,缩放 s 倍:

编程与数学 | 一维空间的中心缩放

void main() {
  Area area = Area(4, 12);
  double center = 10;
  double s = 2;

  Area ret = scale(area, center, s);
  print(ret);
}

Area scale(Area area, double c, double s) {
  double len = area.b - area.a;
  double lenL = c - area.a;
  double lenR = area.b - c;
  return Area(
    c - len / s * (lenL / len),
    c + len / s * (lenR / len),
  );
}


class Area {
  final double a;
  final double b;

  Area(this.a, this.b);

  @override
  String toString() {
    return 'Area[$a ~ $b]';
  }
}

5. 从矩阵变换的角度思考

其实刻度尺放大 2 倍,然后区域的刻度范围,等价于坐标尺不变,将区域以缩放中心缩小 2 倍。如下所示,可以构造一个变换矩阵 Matrix4 ,将区域起止刻度视为两个维度,通过 transform3 进行运算:

编程与数学 | 一维空间的中心缩放

Area scale(Area area, double c, double s) {
  Matrix4 m4 = Matrix4.identity();
  Matrix4 scaleM = Matrix4.diagonal3Values(1 / s, 1 / s, 1);
  Matrix4 moveM = Matrix4.translationValues(c, c, 0);
  Matrix4 backM = Matrix4.translationValues(-c, -c, 0);

  m4.multiply(moveM);
  m4.multiply(scaleM);
  m4.multiply(backM);
  
  Vector3 v3 = m4.transform3(Vector3(area.a, area.b, 0));
  return Area(v3.x, v3.y);
}

可能会有人问,这有什么用? 下一篇,将向你展示这个小方法的强大威力。敬请期待 ~

转载自:https://juejin.cn/post/7395389351641743372
评论
请登录