SketchUp留头留尾的边线简化

Apiglio

共 2812字,需浏览 6分钟

 · 2021-12-31

SketchUp 模型中,越多的顶点数意味着占用更大的空间资源和更慢的程序处理时间。因此,在保证形态特征的前提下,对边线进行简化非常有意义。例如以下这种情况:

一般在导入其他格式模型后,很可能出现一段边线由多条不平行的线段组成的情况。而从认知的角度,它就是两点之间的连线而已,因此可以对此进行边线的简化。


期望的功能是:在选中这些边线后,运行一段 ruby 代码即可以在不破坏两侧的面图元结构的前提下“截弯取直”,创建新的直线段。效果见以下视频:



一、实现原理


整个功能的原理其实很好理解,选中一系列的边线,从中挑选出首尾相连的部分,然后将这些边线删除,然后从起点和终点创建一条新的边线。不过,具体实现起来还会有一些细节问题。


首先是,为了简化新建边线可能带来的情况,应当对边线采取两两简化的方法这样可以将简化前后的情况用一个三角形表示,以便考虑各种特殊情况。

特殊情况包括两个边线没有共同端点、两个边线平行(包括重合)和两个边线不同时构成同一组平面。这三种情况都会影响简化效果。所以首先创建一个方法 simplify_edge( e1, e2) 用来执行给定两个边线的简化,使得执行后 e1 和 e2 都被删除,而新生成 n_line


在这个过程中,只要先创建新的 n_line,再删除 e1 和 e2,那么原先边线所构成的平面就会首先被新边线切分,删除原先的边线也能够正好合并边线两侧的平面。


另外还要单独排除原边线平行的情况,这是因为在这种条件下,新边线没有切分旧平面,删除旧边线时就会删除本应该保留的平面。


在有了  simplify_edge( e1, e2)  方法以后,就需要对同时选中好几个边线的情况进行考虑。本例中通过创建一个 find_pair 方法枚举给定数组中符合块参数的元素对,从而通过每次从选中的边线中选出可以用 simplify_edge 方法简化的边线对。执行完成后将新的边线结果加入数组中并排除简化完成的两个边线,直到数组中只有一个边线或找不到合适的边线对。


二、代码实现


以下是代码的实现方法,所有功能封装在 EdgeCombine 模块中,使用时只需要在选择边线之后在命令行调用以下代码:

EdgeCombine.simplify_edges(Sketchup.active_model.selection.to_a)


完整的代码实现如下:

module EdgeCombine  #删除两个边线之前先创建一条新的边线以保证平面不被删除    def self.simplify_edge(e1,e2,do_not_update_operation=false)    #首先排除集中不适用的情况,    #如果出现这些情况则不执行合并,并且报错:        raise ArgumentError.new("e1 is not a Edge") unless e1.is_a?(Sketchup::Edge)    raise ArgumentError.new("e2 is not a Edge") unless e2.is_a?(Sketchup::Edge)    #判断两个边线参数是否都是Edge类        raise ArgumentError.new("Edges should have the same parent.") unless e1.parent==e2.parent    #排除两个边线不在同一个层次中的情况        verts=e1.vertices+e2.vertices    verts.uniq!    raise ArgumentError.new("Two Edges should share (just) one vertex.") unless verts.length==3    #判断边线是否仅共享一个端点        #判断边线与新边线构成的三角形是否符合要求:    faces=(e1.faces&e2.faces).sort_by(&:persistent_id)    no_face=(e1.faces+e2.faces).uniq.sort_by(&:persistent_id)    raise ArgumentError.new("Two Edges should share exactly the same faces.") unless faces==no_face    #排除边线所连接的平面不相同的情况    unless faces.empty? then      normals=faces.map{|f|f.normal.to_a}.uniq      case normals.length        when 1 then nil        when 2 then raise ArgumentError.new("Two Edges should share parallel faces.") unless Geom::Vector3d.new(normals[0]).parallel?(normals[1])        else raise ArgumentError.new("Two Edges should share at most two faces.")      end      #排除边线所连平面不平行的情况    end    raise ArgumentError.new("Two Edges should not be parallell.") if e1.line[1].parallel?(e2.line[1])    #排除两条边线平行的情况        Sketchup.active_model.start_operation("simplify_edge",trueunless do_not_update_operation    #开始一个可撤销操作        shared=e1.vertices&e2.vertices    verts-=shared    #排除公共顶点的点集就是起讫点    parent=e1.parent    n_line=parent.entities.add_line(verts.map(&:position))    #创建新的边线    parent.entities.add_face(e1,e2,n_line) if n_line.faces.length<2    #保险起见单独创建一次平面    e1.erase!    e2.erase!    #清除简化前的边线    Sketchup.active_model.commit_operation unless do_not_update_operation    #提交可撤销操作    return n_line  end    def self.find_pair(enmerable_instance,&block)    list_1=enmerable_instance.to_a    list_2=enmerable_instance.to_a    res=[]    for i in 0..list_1.length-1 do      for j in i+1..list_2.length-1 do        #两两进行对比,块参数执行结果为true的元素对加入到输出的数组中        res<<[list_1[i],list_2[j]] if block.call(list_1[i],list_2[j])      end    end    return res  end  private_class_method :find_pair    def self.simplify_edges(arr=nil)    arr=Sketchup.active_model.selection.to_a if arr.nil?    #无参数时使用当前选区作为参数    raise ArgumentError.new("parameter is not an Array") unless arr.is_a?(Array)    #排除参数不是数组的情况    Sketchup.active_model.start_operation("simplify_edges",true)    #开始一个可撤销操作    tmp_list=arr.to_a    tmp_list.select!{|i|i.is_a?(Sketchup::Edge)}    #排除数组中的非边线图元    while arr.length>1 do      found_edges=find_pair(tmp_list){|i,j|i.vertices&j.vertices!=[]}      break if found_edges.empty?      #如果找不到有效配对就退出循环      index=0      begin        nl=simplify_edge(found_edges[index][0],found_edges[index][1],true)        #尝试对有效配对的边线进行简化      rescue        index+=1        return(nil) if index>=found_edges.length        #如果简化失败且没有更多配对情况就退出        retry        #如果有备用配对则重新尝试下一对边线      end      tmp_list-=found_edges[index]      #在数组参数中排除简化并删除的边线      tmp_list.unshift(nl)      #在数组参数开头追加新的边线      #追加在开头而不是尾部是因为简化后的边线大概率能够继续简化,      #这样可以使retry的情况少一些,提高一点点运行效率。    end    Sketchup.active_model.commit_operation    #提交修改操作  end    end


本期功能没什么好补充的,就是一个很实用的功能而已,一些细节在代码实现的注释中。




本文编号:SU-2021-11


浏览 69
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报