python之二维几何学习笔记

打印 上一主题 下一主题

主题 805|帖子 805|积分 2415

一、概要

资料泉源《机器工程师Python编程:入门、实战与进阶》安琪儿·索拉·奥尔巴塞塔 2024年6月


  • 点和向量:向量的缩放、范数、点乘、叉乘、旋转、平行、垂直、夹角
  • 直线和线段:线段中点、离线段最近的点、线段的交点、直线交点、线段的垂直中分线
  • 多边形:一样平常多边形、圆、矩形
  • 仿射变更
书中强调了单位测试的紧张性。
二、点和向量

数字比力

  1. # geom2d/nums.py
  2. import math
  3. def are_close_enough(a,b,tolerance=1e-10):
  4.     return math.fabs(a-b)<tolerance
  5. def is_close_to_zero(a,tolerance=1e-10):
  6.     return are_close_enough(a,0.0,tolerance)
  7. def is_close_to_one(a,tolerance=1e-10):
  8.     return are_close_enough(a,1.0,tolerance)
复制代码


  1. # geom2d/point.py
  2. import math
  3. from geom2d import nums
  4. from geom2d.vector import Vector
  5. class Point:
  6.     def __init__(self, x, y):
  7.         self.x = x
  8.         self.y = y
  9.     # 计算两点间的距离
  10.     def distance_to(self, other):
  11.         delta_x = other.x - self.x
  12.         delta_y = other.y - self.y
  13.         return math.sqrt(delta_x ** 2 + delta_y ** 2)
  14.     # 对点进行加操作
  15.     def __add__(self, other):
  16.         return Point(
  17.             self.x + other.x,
  18.             self.y + other.y
  19.         )
  20.     # 对点进行减操作
  21.     def __sub__(self, other):
  22.         return Vector(
  23.             self.x - other.x,
  24.             self.y - other.y
  25.         )
  26.     # 用向量移动点
  27.     def displaced(self, vector: Vector, times=1):
  28.         scaled_vec = vector.scaled_by(times)
  29.         return Point(
  30.             self.x + scaled_vec.u,
  31.             self.y + scaled_vec.v
  32.         )
  33.     # 比较点是否相等
  34.     def __eq__(self, other):
  35.         if self is other:
  36.             return True
  37.         if not isinstance(other, Point):
  38.             return False
  39.         return nums.are_close_enough(self.x, other.x) and \
  40.             nums.are_close_enough(self.y, other.y)
  41.     def __str__(self):
  42.         return f'({self.x},{self.y})'
复制代码
向量

  1. # geom2d/vector.py
  2. import math
  3. from geom2d import nums
  4. class Vector:
  5.     def __init__(self, u, v):
  6.         self.u = u
  7.         self.v = v
  8.     # 向量的加法
  9.     def __add__(self, other):
  10.         return Vector(
  11.             self.u+other.u,
  12.             self.v+other.v
  13.         )
  14.     # 向量的减法
  15.     def __sub__(self, other):
  16.         return Vector(
  17.             self.u-other.u,
  18.             self.v-other.v
  19.         )
  20.     # 向量的缩放
  21.     def scaled_by(self,factor):
  22.         return Vector(factor*self.u,factor*self.v)
  23.     # 计算向量的范数
  24.     @property
  25.     def norm(self):
  26.         return math.sqrt(self.u**2+self.v**2)
  27.    # 验证向量是否为单位向量
  28.     @property
  29.     def is_normal(self):
  30.         return nums.is_close_to_one(self.norm)
  31.     # 计算单位长度的向量
  32.     def normalized(self):
  33.         return self.scaled_by(1.0/self.norm)
  34.    # 计算指定长度的向量
  35.     def with_length(self,length):
  36.         return self.normalized().scaled_by(length)
  37.     # 向量的投影
  38.     def projection_over(self,direction):
  39.         return self.dot(direction.normalized())
  40.     # 向量点乘
  41.     def dot(self,other):
  42.         return (self.u*other.u)+(self.v*other.v)
  43.     # 向量叉乘
  44.     def cross(self,other):
  45.         return (self.u*other.v)-(self.v*other.u)
  46.     # 检验两向量是否平行,即叉乘是否为0
  47.     def is_parallel_to(self,other):
  48.         return nums.is_close_to_zero(self.cross(other))
  49.     # 检验两向量是否垂直,即点乘是否为0
  50.     def is_perpendicular_to(self,other):
  51.         return nums.is_close_to_zero(self.dot(other))
  52.     # 向量的夹角(角度值)
  53.     def angle_value_to(self,other):
  54.         dot_product=self.dot(other)  # 计算点乘值
  55.         norm_product=self.norm*other.norm # 范数的乘积
  56.         return math.acos(dot_product/norm_product) # (点乘值/范数乘积)取反余弦,即角度值
  57.     # 向量的夹角(带叉乘符号的角度值)
  58.     def angle_to(self,other):
  59.         value=self.angle_value_to(other)
  60.         cross_product=self.cross(other)
  61.         return math.copysign(value,cross_product) #math.copysign(x, y)函数返回x的大小和y的符号
  62.     # 向量的旋转,旋转一定角度
  63.     def rotated_radians(self,radians):
  64.         cos=math.cos(radians)
  65.         sin=math.sin(radians)
  66.         return Vector(
  67.             self.u*cos-self.v*sin,
  68.             self.u*sin+self.v*cos
  69.         )
  70.     # 垂直向量,旋转90度
  71.     def perpendicular(self):
  72.         return Vector(-self.v,self.u)
  73.     # 相反向量,旋转180度
  74.     def opposite(self):
  75.         return Vector(-self.u,-self.v)
  76.     # 向量的正旋和余弦
  77.     @property
  78.     def sine(self):
  79.         return self.v/self.norm
  80.     @property
  81.     def cosine(self):
  82.         return self.u/self.norm
  83.     # 比较向量是否相等
  84.     def __eq__(self, other):
  85.         # 检查是否在比较相同的实例
  86.         if self is other:
  87.             return True
  88.         # other不是Vector类的实例
  89.         if not isinstance(other,Vector):
  90.             return False
  91.         return nums.are_close_enough(self.u,other.u) and \
  92.             nums.are_close_enough(self.v,other.v)
  93.     def __str__(self):
  94.         return f'({self.u},{self.v}) with norm {self.norm}'
复制代码
  1. #geom2d/vectors.py
  2. # 向量工厂
  3. from geom2d.point import Point
  4. from geom2d.vector import Vector
  5. # 创建一个从点p到点q的向量
  6. def make_vector_between(p:Point,q:Point):
  7.     return q-p
  8. # 创建单位方向向量
  9. def make_versor(u:float,v:float):
  10.     return Vector(u,v).normalized()
  11. # 在两点之间创建一个单位方向向量
  12. def make_versor_between(p:Point,q:Point):
  13.     return make_vector_between(p,q).normalized()
复制代码
向量范数

向量的范数(norm)是指它的长度。单位范数的长度为一个单位。拥有单位范数的向量在确认向量方向时非常有用,因此,我们经常会想知道一个向量的范数是否为单位范数(它是否是单位向量)​。我们也经常需要归一化(normalize)一个向量:方向不变,长度变为1。
向量点乘



点乘(dot product)会得到一个标量,它可以反映两个向量方向的差异。
图上有一个参考向量
和另外三个向量:
。一条垂直于
的直线将空间分成两个半平面。向量
在直线上,因此
的夹角θ等于90°。而cos(90°)=0,因此
。垂直向量的点乘为零。向量
所在的半平面和
相同,因此,
。末了,
在与
相对的半平面上,因此,

向量叉乘



向量叉乘(cross product)会得到一个垂直于这两个向量所在平面的新向量。向量的顺序很紧张,它决定了效果向量的方向。可以使用右手法则得到叉乘的方向。
叉乘不满足互换律:

二维向量叉乘的一个紧张应用是确定角度的旋转方向。
,因为从
的角度为正(逆时针)​。相反,从
的角度为负,因此叉乘
。末了,平行向量的叉乘为0,这很显然,因为sin 0=0。
向量旋转




cos(π/2)=0, sin(π/2)=1,cos(π)=-1, sin(π)=0
向量的正弦和余弦



三、直线和线段 

线段

  1. # geom2d/segment.py
  2. from geom2d.point import Point
  3. from geom2d.vectors import make_vector_between,make_versor_between
  4. from geom2d import tparam
  5. class Segment:
  6.     def __init__(self,start:Point,end:Point):
  7.         self.start=start
  8.         self.end=end
  9.     # 线段的方向向量
  10.     @property
  11.     def direction_vector(self):
  12.         return make_vector_between(self.start,self.end)
  13.     # 线段的单位方向向量
  14.     @property
  15.     def direction_versor(self):
  16.         return make_versor_between(self.start,self.end)
  17.     # 垂直于线段方向的向量,法向量
  18.     # 调用self的direction_versor来得到线段的方向向量,同时也是Vector类的实例
  19.     # 调用perpendicular方法,返回垂直于线段方向的向量
  20.     def normal_versor(self):
  21.         return self.direction_versor.perpendicular()
  22.     # 线段的长度
  23.     @property
  24.     def length(self):
  25.         return self.start.distance_to(self.end)
  26.     # 使用参数t获取线段上的任意一点
  27.     def point_at(self,t:float):
  28.         tparam.ensure_valid(t)  # 验证t值
  29.         return self.start.displaced(self.direction_vector,t)
  30.     # 线段的中点
  31.     @property
  32.     def middle(self):
  33.         return self.point_at(tparam.MIDDLE)
  34.     # 线段上的最近点
  35.     # 计算从线段的端点S到外部点P的向量v
  36.     # 计算在线段方向上投影的单位向量d
  37.     # 将投影的长度设为vs
  38.     def closest_point_to(self,p:Point):
  39.         v=make_vector_between(self.start,p)
  40.         d=self.direction_versor
  41.         vs=v.projection_over(d)
  42.         if vs<0:
  43.             return self.start
  44.         if vs>self.length:
  45.             return self.end
  46.         return self.start.displaced(d,vs)
复制代码
线段的方向向量

方向(direction)是线段的一个紧张性子,定义为从起点S到尽头E的向量。用
来表现该向量。
方向向量(direction vector)是一个与线段平行且长度相同的向量,其方向是从起点到尽头。
单位方向(direction versor)是方向向量的归一化版本,即与方向向量的方向相同,但长度为一个单位。
垂直于线段的方向也同样紧张。将单位方向向量
旋转方向π/4 rad(90°),就可以得到线段的单位法向量(normalversor)。

线段上最靠近外部点的点

如果外部点没有与线段对齐,穿过该点且垂直于线段的直线不与线段相交,那么最近的点必然是两个端点S或E中的一个。
如果该点与该段对齐,则垂直线与线段的交点就是最近的点。
如图:点S≡A'是离A最近的点,点E≡B'是最靠近B的点,C'是最靠近C的点。

线段的交点



四、多边形

一样平常多边形——用它们的顶点来定义;
圆是平面内与指定点(圆心)的距离(半径)相同的所有点的集合。因此,圆由圆心C的位置和半径R的值定义
矩形——由原点、宽度和高度定义
多边形中一个紧张性子是质心(centroid),即所有顶点坐标的算术均匀值。
五、仿射变更

仿射变更:它使我们可以或许通过缩放、旋转、平移和剪切来改变几何形状。
六、单位测试




断言方法

断言方法形貌assertAlmostEqual定义在我们引用的类unittest.TestCase中,用指定的公差来检查浮点数是否相称,公差用小数点后的位数表现,默认是7。请记住,在比力浮点数时,必须有公差,或者像上述例子,给定小数点后的位数assertEqual使用==操作符来比力这两个参数assertTrue检验给定表达式的计算效果是否为TrueassertFalse检验给定表达式的计算效果是否为FalseassertRaises向其传入三个参数。首先是预期要触发的非常(TParamError)。其次,传入了期望触发非常的方法。末了,传入需要转达给前面的方法(在本例中为point_at)的实参。assertIsNone检查传入的值是否是None(无)​ 单位测试的三个规则

1、失败原因须唯一
单位测试应该有且仅有一个失败的原因。如果测试失败只有一个原因,那么很容易找到代码中的错误。如果一个测试失败可能有五个不同的原因。当测试失败时,你会发现本身耗费太多时间去阅读错误消息和调试代码。
2、受控环境
测试的输入和输出应该是已知的。发生在测试中的一切都应该是确定的(deterministic),也就是说,不应该出现随机性或依赖任何你无法控制的东西:日期或时间、操作系统、未在测试中设置的机器环境变量,等等。
3、测试独立性
测试不应依赖于其他测试。每个测试都应该独立运行,绝不能依赖于其他测试所设置的运行测试的环境。这至少有三个原因。首先,你需要独立地运行或调试测试。其次,很多测试框架并不能保证测试的执行顺序。末了,不依赖于其他测试的测试要易读得多。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

花瓣小跑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表