🌓
搜索
 找回密码
 立即注册

【SU Ruby教程】几何与变换(1):向量与点线面

admin 2022-9-12 19:42:23 80754

从这一篇开始,教程进入第三个部分,开始对SU Ruby API中的各个模块和类的方法和相关概念进行详细的介绍。首先不可避免的要谈及SU模型表示三维空间的方法。包括空间坐标、空间范围、空间变换以及更广域意义上的地理坐标表示等内容:这就需要使用到Geom模块。


Geom模块是几何与变换的核心模块,教程将分为四个部分介绍。以下是第一篇,有关如何表示一个点、一条直线或者一个平面。




几何与变换(1):向量与点线面


【本期目录】

(1)长度与角度单位

①长度单位

②角度单位

③自定义单位转换

④模型单位格式

(4)直线

①点向式表示法

②与Sketchup::Edge的关系

(2)点

①用数组表示点

②Geom::Point

③与Sketchup::Vertex的区别

(5)平面

①点法式表示法

②原点距式表示法

③与Sketchup::Face的关系

(3)向量

①用数组表示向量

②Geom::Vector

③向量计算

(6)立体几何

①交错与投影

②位置关系

③距离计算


(1)长度与角度单位


①长度单位


Sketchup的默认长度尺寸为英寸,因此在使用数字坐标的时候,(1000,0,0)并不代表距离原点1000毫米的点,而是距离原点1000英寸的点。这时如果需要使用1000毫米的尺寸,就需要使用 1000.mm



如上图可以发现 1000.mm 返回的并不是一个数字类(::Numeric),而是::Length类。这个类继承自::Numeric类,所有数值都继承自这个ruby自带的类,SU在其中追加了一些方法使之可以直接进行单位转换。而转换之后使用 .inspect 方法查看这个数值的存储值可以发现依然是以英寸作为单位的。


相似地,还可以将英寸转换为厘米(cm)、米(m)、千米(km)、英尺(feet)、英码(yard)和英里(mile),当然也可以是转换成英寸本身。下图是这些转换方式的测试,由于当前模型的单位为毫米,所以显示时为毫米单位:

1.feet#>> ~ 305mm1.yard#>> ~ 914mm1.inch#>> ~ 25mm1.mile#>> 1609344mm1.km#>> 1000000mm1.cm#>> 10mm1.m#>> 1000mm


随便打一个数字,查看数字以“to_”开头的方法,可以看到ruby风格的转换方法,其中除了 to_s、 to_f、 to_i、 to_c 这些ruby自带的数据类型转换以外,还有SU提供的单位转换。



这些是与上文相对应的逆过程方法,将以英寸计的尺寸转换成指定单位下的数值。例如:

1.m.to_mm#>> 1000.01.mile.to_yard#>> 1760.01.feet.to_inch#>> 12.01.km.to_m#>> 999.99999999999991.yard.to_feet#>> 3.01.mile.to_m#>> 1609.34399999999981.inch.to_mm#>> 25.4


②角度单位


SketchUp的角度单位默认是弧度制,因此如果需要使用角度制,需在角度数值之后添加 .degrees 。这里需要与前文长度单位区别一下,不同于 .mm 返回的是Length类, .degrees 方法返回的并不是一个特定的Angle类实例,而是浮点数类型(::Float)。这意味着这个返回值与原数值一样,都是不带单位的数字。因此这个方法是纯粹的数值转化,可以直接理解成是如下的定义:

class Numeric  def degrees    return(self / 180 * Math::PI)  end  def radians    return(self / Math::PI * 180)  endend


同理,还有一个 .radians 方法,用于将弧度制转换为角度制,同样返回的是Float类型。因此,角度制弧度制的转换不是像英寸和毫米的转换那样有主次关系,表现为一对 .mm 和 .to_mm ,而是 .degrees 和 .radians 对称的转换方法。


③自定义单位转换


在理解了角度单位转换的方式之后,也可以使用相同的手法追加其他自定义方法到Numeric类以实现其他单位转换。由于Length继承自Numeric类,因此在往Numeric中追加新方法的同时,Length类也会同步更新这些方法。以下以英制单位“弗隆”和市制单位“寸”“尺”“丈”“引”“里”为例子:

class Numeric  def furlong    return (self*220*3*12).to_l  end  def to_furlong    return (self/220/3/12)  end  def cun    return (self*100/3).mm  end  def to_cun    return (self.to_mm*3/100)  end  def chi    return (self*10).cun  end  def to_chi    return (self.to_cun/10)  end  def zhang    return (self*100).cun  end  def to_zhang    return (self.to_cun/100)  end  def yin    return (self*1000).cun  end  def to_yin    return (self.to_cun/1000)  end  def li    return (self*15000).cun  end  def to_li    return (self.to_cun/15000)  endend


其中furlong的转换方法中出现了 .to_l 方法,这是将Numeric类转换成英寸单位的Length类的方法;而市制单位的换算中都是用 .mm 方法绕开了这一步骤。下图为效果测试:

1.li.to_m#>> 500.000000000000061.zhang.to_cm#>> 333.31.cun.to_inch#>> 1.2992125984251971.chi.to_feet#>> 1.09251968503937011.zhang.to_yard#>> 3.64501312335958041.yin.to_furlong#>> 0.165697327606776441.li.to_mile#>> 0.310685596118667031.m.to_cun#>> 30.01.mile.to_furlong#>> 8.0


④模型单位格式


在Sketchup模块下定义有这样一系列的方法,它们通过给定的英寸单位的数据返回当前模型的单位数值。包括 .format_length.format_area、 .format_angle、 .format_degrees 和 .format_volume如下图,如果是2019.2以前的版本,是没有 .format_volume 这个方法的。



在当前模型以毫米为单位时,就会是以下的结果:

puts Sketchup.format_degrees(Math::PI.radians)#>> 180.0puts Sketchup.format_angle(Math::PI)#>> 180.0puts Sketchup.format_length(100.m)#>> 100000mmputs Sketchup.format_length(1.mile)#>> 1609344mmputs Sketchup.format_area(1)#>> 645 mm²puts Sketchup.format_area(3.mm*9.mm)#>> 27 mm²puts Sketchup.format_volume(3.mm*4.mm*5.mm)#>> 60 mm³


对于2019以前的版本,也可以通过追加的方法在Sketchup模块中自己添加 .format_volume 方法,不过需要涉及到判断当前模型的单位设置,这会在之后有关Sketchup:: OptionsManager的章节中涉及。


(2)点


①用数组表示空间点


Sketchup 中可以使用数组来表示空间坐标,例如坐标 (0,100,100) 在ruby中表示为 [0,100,100]。其中三个维度的顺序是 [x,y,z],或说 [width, height, depth],同时也接受xy平面上的二维坐标 [x,y],或者说 [width, height]


了解了这种最简易的表示空间点坐标的方式,就已经可以利用Entities类中的添加图元方法,向图元容器中绘制新图形了:



用Geom::Point表示空间点


数组只是方便使用者快速地表示空间坐标,而模型中的图元的存档则有专门的定义,二维和三维的空间坐标有分别的类定义,即Geom:: Point2d 和 Geom:: Point3d。


这里以Geom:: Point3d为例,同样绘制上一部分中的那条线段,同时额外展示一些创建Point3d实例的方法:

point_O  = Geom::Point3d.new(0,0,0)point_P  = Geom::Point3d.new([100.mm,100.mm,0])point_P1 = Geom::Point3d.new(point_P)point_P2 = point_P.clone
Sketchup.active_model.entities.add_line(point_O,point_P)
puts point_O==point_P  #>> falseputs point_P==point_P1 #>> trueputs point_P==point_P2 #>> true


其中第一行和第二行是比较常见的创建点的方式,一种是使用三个参数表示xyz,另一种则是使用数组表示三维坐标。第三行是通过已有点的坐标创建新的点,其实它与第二行的表达只有一步之遥,可以写成这样: point_P1 = Geom:: Point3d. new( point_P. to_a) 。第四行则完全不需要使用类名,直接使用已有点创建副本,是比较方便的做法8-10行对这些点进行比较,P1和P2是通过P创建的,因此相等;O与P则不相等。


如果需要访问Point类实例的坐标,有两种方式可以选择:

p = Geom::Point3d.new(4.mm,8.mm,12.mm)#方法一puts p.x  #>> 4mmputs p.y  #>> 8mmputs p.z  #>> 12mm#方法二puts p[0]  #>> 4mmputs p[1]  #>> 8mmputs p[2]  #>> 12mm


如果需要修改Point类实例的坐标,有三种方式可以选择:

p = Geom::Point3d.new(4.mm,8.mm,12.mm)#方法一p.x=1.mmp.y=2.mmp.z=4.mmputs p    #>> (1mm, 2mm, 4mm)#方法二p[0]=0.mmp[1]=1.mmp[2]=2.mmputs p    #>> (0mm, 1mm, 2mm)#方法三p.set!([3.mm,6.mm,12.mm])#或者p.set!(3.mm,6.mm,12.mm)#或者也可以set!(OtherPoint)puts p    #>> (3mm, 6mm, 12mm)


访问与修改共五种方法,其中除了 .set! 以外,其余四种方法对于用数组形式的坐标也有效, .[] 和 .[]= 还是Array类固有的方法。


Geom:: Point2d 和 Geom:: Point3d 是专门的点类型,使用这种方式表示空间点坐标不如数组表示来得方便,但是具有两个优势:一是数组不仅可以用来表示空间坐标,还可以用来表示向量,混合使用数组可能会造成混淆,影响代码的可读性;二是这两个类的实例有数组类没有的方法,这些方法在点与向量的计算中能够提供方便。


③与Sketchup::Vertex的关系


Vertex是Sketchup模块下定义的端点类,是图元的一种,是直接与Geom:: Point类对标的图元类。Point类侧重于空间计算,而Vertex类是具体模型中的数据类型,包括了模型中端点与其它图元的关系,例如该端点参与了哪些边线、面的组成。Vertex作为表示端点的图元,包含空间点坐标的信息,所以每一个实例中包含一个Point类实例,通过Vertex类的 .position 方法可以访问:

#令当前模型中有且只有一个边线图元edge=Sketchup.active_model.entities[0]vertex=edge.vertice[1]puts vertex.position#>> (100mm, 100mm, 0mm)


其中, edge. vertice 返回边线的两个端点, edge. vertice[1] 表示的是其中的终点。这部分内容会在涉及Sketchup:: Edge类的章节中详解。


(3)向量


①使用数组表示向量


向量的表达方式与点坐标的表达方式如出一辙,同样是 [x,y,z] 或者 [x,y] 的顺序。在SU Ruby中,向量经常用于表示平面的方向。例如绘制一个圆时, .add_circle 中的第二个参数就是向量,表示绘制圆所在平面的法向量。

Sketchup::Entities.add_circle(  centre,        #圆心  normal,        #平面法向量  radius         #半径  [,segnum=24]   #边线段数量) => Array of Sketchup::Edge#返回创建的边线数组


以下为了便于展示,在画圆的基础上创建了相应的面域:


②使用Geom::Vector表示向量


向量类Geom::Vector类与Geom::Point类一样,分为2d和3d两种,即 Geom:: Vector2d 和 Geom:: Vector3d ,前者用来表示xy平面上的向量。

center   = Geom::Point3d.new(0,0,0)normal   = Geom::Vector3d.new([1,1,1])normal_1 = Geom::Vector3d.new(normal)normal_2 = normal.cloneradius   = 100.mm
es=Sketchup.active_model.entities.add_circle(center,normal,radius)Sketchup.active_model.entities.add_face es


Vector类与Point类有着相似的声明方式,同样支持多种创建实例的方式。向量的坐标访问与修改也与点坐标一致,同样包括 .[] .[]= .x.x= 和 .set! 五种。Point类不同的是,向量有一些特殊的概念:


向量是有方向的,因此Vector类提供反向变换方法:

vector = Geom::Vector3d.new([1,1,1])puts vector.reverse #>> (-1, -1, -1)puts vector         #>> (1, 1, 1)vector.reverse!     #带感叹号的方法才会修改实例自身属性puts vector         #>> (-1, -1, -1)


向量是有长度的,因此Vector类实例能对长度进行访问和修改。还可以判断向量是否为单位向量以及对向量进行标准化。

vector = Geom::Vector3d.new([1,1,1])puts vector.length         #>> ~44mmvector.length=500.mm       #设置向量长度puts vector                #>> (11.3652, 11.3652, 11.3652)puts vector.unitvector?    #>> falsevector.normalize!          #标准化puts vector.length.to_inch #>> 1.0vector.unitvector?         #>> trueputs vector                #>> (0.57735, 0.57735, 0.57735)


③向量计算


向量与点存在一系列的运算和转换方法,Vector类提供了 .+.-.==、 .cross 和 .dot 等方法,分别用于表示向量加法、减法、比较、叉乘和点乘;其中前三个方法通过运算符重载使得向量计算和解析几何上的表示非常相似。


以下通过在三角形OPQ中的PQ上作中点M的例子,展示向量与点之间的加减运算方法:

#定义三个坐标点point_O = Geom::Point3d.new(0,0,0)point_P = Geom::Point3d.new(3,0,0)point_Q = Geom::Point3d.new(0,4,0)#根据两点只差创建向量vector_OP = point_P - point_Ovector_OQ = point_Q - point_O  #vector_OP = point_O.vector_to(point_P)  #vector_OQ = point_O.vector_to(point_Q)  #这两行注释与前两行代码等价#通过向量减法创建PQvector_PQ = vector_OQ - vector_OP#令PM=0.5*PQvector_PM = vector_PQ.clonevector_PM.length= 0.5 * vector_PQ.length#计算得到向量PM之后,得到点M坐标,有几种做法:  #方法一(两行完全等价)  point_M1 = point_P + vector_PM  point_M2 = point_P.offset(vector_PM)  #方法二(获得原点)   vector_OM = vector_OP + vector_PM  point_M3 = Geom::Point3d.new(vector_OM.to_a)#检验结果puts point_M.to_a            #>> [1.5, 2.0, 0.0]puts point_M1 == point_M2    #>> trueputs point_M1 == point_M3    #>> trueputs point_M1 == point_P     #>> false


在前一个例子的基础上,展示向量间关系的判断:

#判断向量是否相等  puts vector_OP == vector_OQ        #>> false  puts vector_PM == vector_PQ        #>> false  puts vector_PM == vector_PM.clone  #>> true#判断向量是否垂直  puts vector_OP.perpendicular?(vector_OQ)  #>> true  puts vector_OP.perpendicular?(vector_PQ)  #>> false#判断向量是否平行  puts vector_PM.parallel?(vector_PQ)          #>> true  puts vector_PM.parallel?(vector_PQ.reverse)  #>> true#判断向量是否同向   puts vector_PM.samedirection?(vector_PQ)          #>> true  puts vector_PM.samedirection?(vector_PQ.reverse)  #>> false#计算向量夹角  puts vector_OP.angle_between(vector_OQ).radians  #>> 90.0  puts vector_OP.angle_between(vector_PQ).radians  #>> 126.86989764584403  puts vector_OP.angle_between(vector_PQ.reverse).radians  #>> 53.13010235415597


除此之外,向量计算还有叉乘和点乘,分别常用于构造正交和投影:

vector_OA=Geom::Vector3d.new(3,1,-4)vector_OB=Geom::Vector3d.new(-2,4,1)vector_OC=vector_OA.cross(vector_OB)puts vector_OC#>> (0, 0, 12)puts vector_OC.perpendicular?(vector_OA)#>> trueputs vector_OC.perpendicular?(vector_OB)#>> true#若OA与OB相交,则OC必然同时垂直于OA和OB
vector_OA = Geom::Vector3d.new(1,6,3)vector_OB = Geom::Vector3d.new(-2,4,6)dot_OA_OB = vector_OA.dot(vector_OB)vector_OM = vector_OA.clonevector_OM.length = dot_OA_OB / vector_OA.lengthvector_MB = vector_OB - vector_OMputs vector_MB.perpendicular?(vector_OA)#>> true#通过点乘可以将点B投影在OA上,或是将点A投影在OB上。


以上代码中的坐标如下图所示:

使用叉乘构造所在平面法线
使用点乘获得向量的投影


(4)直线


①表示直线的数组


SketchUp中的直线使用点向式表示,即直线上一点加方向向量的形式。具体在ruby中是一个有两个元素的数组。第0个元素为直线上任意一点,第1个元素为直线的方向向量。


例如以下两段代码均定义了直线line: x=y=z-1:

point_1 = Geom::Point3d.new(0,0,1)vector_1 = Geom::Vector3d.new(1,1,1)line_1 = [point_1,vector_1]
point_2 = Geom::Point3d.new(-1,-1,0)vector_2 = Geom::Vector3d.new(-2,-2,-2)line_2 = [point_2,vector_2]


这种直线的表达存在一个问题,就是同一个直线有无数中表达方式,因此在判断两个表达是否是同一个直线时就会比较麻烦,需要比较两个直线方程的方向向量是否相同,然后再判断他们是否存在至少一个共同点。由于直线没有专门的类定义,没有专门的判断是否相等的方法,也就不能通过运算符重载来实现相等的判断,所以需要在Geom模块中追加一个函数判断:

module Geom  def Geom.line_line_equal?(la,lb)    la[1].parallel?(lb[1]) and la[0].on_line?(lb)  endend


②与Sketchup::Edge的关系


直线在空间中是无限延伸的,而Sketchup::Edge是边线图元,是有限的线段。尽管如此,Edge类图元可以通过 .line 方法返回其所在的直线。使用 .line 方法得到的直线,其点坐标为边线的起始点(第一个端点),而方向向量是与两个端点坐标之差同向的单位向量。因此可以理解成如下的方法定义:

module Sketchup  class Edge    def line      res = [self.start.position]      vec = self.end.position - self.start.position      res << vec.normalize #此时返回追加元素后的数组res    end    #...  end  #...end


其中, edge. start 返回边线的起点,相当于 edge. vertice[0]; edge. end 返回边线的终点,相当于 edge. vertice[1] 。这部分内容会在涉及Sketchup:: Edge类的章节中详解。


(5)平面


①表示平面的数组


SketchUp中的平面有两种表示方法,一种是“点法式”,另一个是“原点距式”。


点法式。与直线的表示形式完全相同,表现为由1个点坐标和1个向量组成的数组。第0元素为平面上任意一点,第1元素为平面法向量。所以有着相同形式的直线与平面一定相互垂直,并且相交于第0元素表示的点。


原点距式。和直线的点向式一样,平面的点法式也存在同一个平面有多个表达方式这情况,这对于比较平面是否相等十分不方便。不过平面的情况比直线好一些,不同于直线要五个参数才能确定唯一性,平面只需要四个参数;并且这四个参数都有几何意义。也就是说,在法向量确定的情况下,原点和交点在法向量方向上的位置对于每一个不同的平面是唯一的。所以如下图,可以用四个数表示三维空间中的所有平面,记为 [x, y, z, d]前三位 x, y, z 为平面的单位法向量PQ, d 则表示交点到原点沿PQ方向的有向距离。



所以平面plane: x+y+z-1=0的“原点距式”就是:

plane = [0.5773502691896258,         0.5773502691896258,         0.5773502691896258,        -0.5773502691896258]


对于平面 plane 来说, plane[0..2] 表示该平面的法向量, plane[3] 表示该平面的“原点距”。将数组的四个元素分别取相反数,即 plane. map{|i| -i} 或 plane. map(&:-@),也是相同的平面,只不过法向量相反。


Geom模块中提供了一个名为 .fit_plane_to_points 的方法,能够通过已知点坐标列表最大程度地创建拟合平面,可以通过这个方法,用三个点坐标创建平面。其返回结果也是“原点距式”。

points=[]points<<[0,0,0]points<<[0,1,0]points<<[1,0,0]points<<[1,1,0]Geom.fit_plane_to_points(points)#>> [-0.0, -0.0, 1.0, -0.0]
points=[]points<<[1,0,0]points<<[0,1,0]points<<[0,0,1]Geom.fit_plane_to_points(points)#>> [0.5773502691896258, 0.5773502691896258, #>> 0.5773502691896258, -0.5773502691896258]


②与Sketchup::Face的关系


平面和直线一样,在空间中无限延伸,与之相关的Sketchup:: Face则是有具体形状的面域。Face类图元可以通过 .plane 方法返回其所在的平面,返回格式为“原点距式”。


法向量相反的两个表达方式对于平面来说是相同的,但对于 Face 图元 .plane 方法返回结果而言,却有一点区别。如下图代码所展示,这一点区别能够反映该平面图元的材质正面是朝向原点还是背向原点,相关内容在涉及 Sketchup:: Face 的章节中详解。

#令当前模型中有且只有一个平面图元face=Sketchup.active_model.entities.grep(Sketchup::Face)[0]puts "#{face.plane.map{|i|i.round(3)}}"#>> [0.577, 0.577, 0.577, -0.577]face.reverse!puts "#{face.plane.map{|i|i.round(3)}}"#>> [-0.577, -0.577, -0.577, 0.577]


(6)立体几何


现在已经完整地介绍了点线面在SU Ruby中的表达方式,其中连带地也涉及了一些有关同类图形之间的立体几何计算。在最后这一部分会补充不同图形之间关系判断和有关的计算方法。


本部分中灰色字体部分不是 SU Ruby API 目前提供的方法,系额外追加的方法,这部分段落之后代码为个人实现方案,可选择性阅读。


①交错与投影


Geom模块中提供了三个交错方法 : .intersect_line_line.intersect_line_plane 和 .intersect_plane_plane 。分别用于返回线线交点、线面交点和面面交线。三个方法均只返回唯一的结果,如果是两线重合、两线异面、线在面上、线面平行或者面面重合的情况,则返回nil。


Geom:: Point类实例提供 .project_to_line 和 .project_to_plane 两个方法,用于将点投影到线或面上。

point_1 = Geom::Point3d.new(0,0,0)point_2 = Geom::Point3d.new(500.mm,300.mm,1000.mm)line_1 = [point_1,Geom::Vector3d.new(0,0,1)]line_2 = [point_1,Geom::Vector3d.new(0,1,1)]plane_1 = [point_2,Geom::Vector3d.new(0,0,1)]plane_2 = [point_1,Geom::Vector3d.new(1,1,1)]
intersect_point_1 = Geom.intersect_line_line(line_1,line_2)intersect_point_2 = Geom.intersect_line_plane(line_1,plane_2)intersect_line = Geom.intersect_plane_plane(plane_1,plane_2)#>> (0mm, 0mm, 0mm)#>> (0mm, 0mm, 0mm)#>> [Point3d(0, -39.3701, 39.3701), Vector3d(-0.707107, 0.707107, 0)]
proj_pt1=point_2.project_to_line(line_1)proj_pt2=point_2.project_to_line(line_2)#>> (0mm, 0mm, 1000mm)#>> (0mm, 650mm, 650mm)


②位置关系


Geom:: Point类中提供了两个方法用于判断点是否在直线或者平面上,分别是: .on_line? 和 .on_plane? 

point_1 = Geom::Point3d.new(0,0,0)point_2 = Geom::Point3d.new(0,0,1000.mm)line_1 = [point_1,Geom::Vector3d.new(0,0,1)]line_2 = [point_1,Geom::Vector3d.new(0,1,1)]plane_1 = [point_2,Geom::Vector3d.new(0,0,1)]plane_2 = [point_1,Geom::Vector3d.new(0,0,1)]puts point_1.on_line?(line_1)puts point_2.on_line?(line_2)puts point_1.on_plane?(plane_1)puts point_1.on_plane?(plane_2)#>> true#>> false#>> false#>> true


至于线线关系、线面关系和面面关系则可以在向量关系的基础上进行补充。例如,判断线与线、线与面和面与面之间是否垂直或平行,可以如此实现:

module Geom  def Geom.line_line_perpendicular?(la,lb)    return la[1].perpendicular?(lb[1])  end  def Geom.line_line_parallel?(la,lb)    return la[1].parallel?(lb[1])  end  def Geom.line_plane_perpendicular?(l,p)    case p.count      when 2 then       return l[1].parallel?(p[1])      when 4 then       return l[1].parallel?(p[0..2])    end  end  def Geom.line_plane_parallel?(l,p)    case p.count      when 2 then       return l[1].perpendicular?(p[1])      when 4 then       return l[1].perpendicular?(p[0..2])    end  end  def Geom.plane_plane_perpendicular?(p1,p2)    case p1.count      when 2 then n1 = p1[1]      when 4 then n1 = p1[0..2]    end    case p2.count      when 2 then n2 = p2[1]      when 4 then n2 = p2[0..2]    end    return Geom::Vector3d.new(n1).perpendicular?(n2)  end  def Geom.plane_plane_parallel?(p1,p2)    case p1.count      when 2 then n1 = p1[1]      when 4 then n1 = p1[0..2]    end    case p2.count      when 2 then n2 = p2[1]      when 4 then n2 = p2[0..2]    end    return Geom::Vector3d.new(n1).parallel?(n2)  endend


在这几个方法的基础上,结合上文三个计算交点交线的方法,就可以综合判断点线面之间的位置关系了。


③距离


Geom:: Point 类提供了点-点、点-线和点-面距离的计算方法。这些方法也追加到 Array 类中,因此直接使用数组表示点坐标也可以使用这些方法。

p1 = Geom::Point3d.new(0,0,0)p2 = Geom::Point3d.new(3000.mm,4000.mm,0)line = [p2,Geom::Vector3d.new(-1,1,-1)]plane1 = [p2,Geom::Vector3d.new(-1,1,-1)]plane2 = [0,0,1,1000.mm]
puts Sketchup.format_length p1.distance(p2)#>> 5000mmputs Sketchup.format_length p1.distance_to_line(line)#>> ~ 4967mmputs Sketchup.format_length p1.distance_to_plane(plane1)#>> ~ 577mmputs Sketchup.format_length p1.distance_to_plane(plane2)#>> 1000mm


对于线-面距离,Geom 模块中有一个 .closest_points 方法,可以获取两条直线中彼此距离最近的一对点坐标。如果两条直线为异面直线,则返回唯一的最近距离点对。如果两条直线相交或重合,则返回的两点相等且为交点(相交)或第一条直线的定位点(重合)。如果两条线平行而不重合,点对为第一条直线的定位点和另一直线上对应的最近点。


由于没有直线与直线最近距离的方法,可以在 .closest_points 方法的基础上在 Geom 模块中追加一个自定义的方法:

module Geom  def Geom.distance_line_line(la,lb)    points = closest_points(la,lb)    return(points[0].distance(points[1]))  endend


对于面-面距离就只针对平行面了,因而适用范围更小,在此处仅列出一小部分实现方法仅供参考:

module Geom  def Geom.distance_plane_plane(pa,pb)    if pa.count==4 and pb.count==4 then      unless Geom::Vector3d.new(pa[0..2]).parallel?(pb[0..2]) then        return 0      else        if Geom::Vector3d.new(pa[0..2]).samedirection?(pb[0..2]) then          return (pa[3]-pb[3]).abs        else          return (pa[3]+pb[3]).abs        end      end     else      #包含[Point,Vector]格式的计算方法    end  endend




最后还是按照惯例放一些补充内容:


①文中例子的点或者向量的定义都采用的“point_”/“vector_”+大写字母的规则来命名。这一方面是为了区分点和向量,更重要的是,ruby中大写字母开头的标识符为模块、类或者常量名。例子中虽然没有去编辑这几个点和向量的坐标,但也视之为变量,因此还是应该使用小写字母或者下划线开头。


②一个关于方法定义名称的疑问。为什么判断向量是否为单位向量的方法 .unitvector? 不用构词上更具有联系的 .normalized? 的表述,或至少将其列为别名呢?


③以下提供一个简易的点法式转原点距式的函数:

def plane2_to_plane4(p2)  vec=p2[1]  dist=p2[0].distance_to_plane(p2)  axis=[Geom::Point3d.new,vec]  intersect=Geom.intersect_line_plane(axis,p2)  vec_ori=intersect.vector_to([0,0,0])  res=vec.normalize.to_a  case vec_ori.dot(vec)<=>0    when 1 then res << vec_ori.length    when -1 then res << -vec_ori.length    when 0 then res << 0  end  return resend


这是一个图省事的做法,用到了Geom中的种种构造。但实际上这是一个纯粹的数学问题,可以仅使用数组坐标的计算就能很好地解决这个问题。




这是Apiglio SU Ruby教程的第三篇,编号为“SU-R06”,本系列的其他文章可以在公众号后台输入“SU-R”+两位数编号查看,或者直接点击菜单中的“SU Ruby”选项获得全部有关SketchUp文章的目录。





扫一扫

109287.jpg

0 回复

高级模式
游客
返回顶部