Ruby基础学习

学习资料:Ruby 教程 | 菜鸟教程 (runoob.com)Ruby 程序设计语言官方网站 (ruby-lang.org)

学习时长:五天

日常练习:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

学习知识点:熟悉基本语法、熟悉类和模块、熟悉Ruby的一些特性

考核内容:实现一个关于二叉排序树相关的程序

Sketchup Ruby API学习

学习资料:

Welcome! | SketchUp Developer

File: SketchUp Ruby API — SketchUp Ruby API Documentation

SketchUp/sketchup-ruby-api-tutorials: SketchUp Ruby API 教程和示例 (github.com)

ruby for sketchup(内网共享目录:\\JAVALITTERBOY\ruby for sketchup)内容

学习时长:两周

学习知识点:Sketchup实体操作(各种实体的类型以及区别特别是群组和组件)、简易参数界面、复杂dialog界面、实体属性操作、工具开发

日常练习:绘制四分之一圆柱、截面路径跟随绘制、学习Sketchup::Tool、绘制贝兹曲线工具、绘制四分之一圆柱工具、截面放样工具、开发一个简易窗体界面、熟悉SU内部的单位(长度、角度)

考核内容:三天完成指定的工具开发

如何在Sketchup内执行Ruby脚本

  1. 将需要加载执行的rb文件放在插件目录%appdata%\SketchUp\SketchUp 2023\SketchUp\Plugins,sketchup启动时会加载运行
  2. 使用Sketchup Ruby控制台加载,Sketchup2022 点击扩展程序->开发人员->Ruby控制台,在控制台内输入Ruby脚本即可执行,例如 load "xxxxx.rb"

绘制贝兹曲线

实现绘制贝兹曲线方法

# 贝兹曲线绘制
class Bezier
  def self.bz_compute_curve(control_pts, numseg)
    dt = 1.0 / numseg
    return [] if control_pts.length < 3
    (0..numseg).map { |i|
      evaluate(control_pts, i * dt)
    }
  end

  def self.evaluate(pts, t)
    degree = pts.length - 1

    t1 = 1.0 - t
    fact = 1.0
    n_choose_i = 1

    x = pts[0].x * t1
    y = pts[0].y * t1
    z = pts[0].z * t1

    (1...degree).each { |i|
      fact = fact * t
      n_choose_i = n_choose_i * (degree - i + 1) / i
      fn = fact * n_choose_i
      x = (x + fn * pts[i].x) * t1
      y = (y + fn * pts[i].y) * t1
      z = (z + fn * pts[i].z) * t1
    }

    x = x + fact * t * pts[degree].x
    y = y + fact * t * pts[degree].y
    z = z + fact * t * pts[degree].z

    Geom::Point3d.new(x, y, z)
  end
end

Sketchup.active_model.start_operation '绘制贝塞尔曲线', true
begin
  pts = [ORIGIN, Geom::Point3d.new(15.mm, 15.mm, 0), Geom::Point3d.new(30.mm, 0, 0), Geom::Point3d.new(30.mm, 30.mm, 0)]
  curve_pts = Bezier.bz_compute_curve(pts, 30)
  group = Sketchup.active_model.entities.add_group
  pts.each { |pt| group.entities.add_cpoint pt }
  pts.each_cons(2) do |p1, p2|
    group.entities.add_cline(p1, p2)
  end
  curve_pts.each_cons(2) do |p1, p2|
    group.entities.add_line(p1, p2)
  end
  Sketchup.active_model.commit_operation
rescue => e
  Sketchup.active_model.abort_operation
  raise e
end

绘制四分之一圆柱

实现四分之一圆柱方法

# @param [Sketchup::Model/Sketchup::Group/Sketchup::ComponentInstance] group  父级实体
# @param [Geom::Point3d] center 四分之一圆的中心点
# @param [Length] radius 四分之一圆的半径
# @param [Geom::Vector3d] normal 四分之一圆的法线方向
# @param [Length] height  四分之一圆柱体的长度
def draw_one_fourth_cylinder(group, center, radius, normal, height)
end

截面路径跟随绘制

任意绘制一个平面作为截面

任意绘制一条线(可以是多段线也可以是曲线)

选中两个实体,执行绘制脚本,将截面沿着放样路径生成模型,生成的模型需要成组

Sketchup.active_model.selection  # 可以用于获取当前模型选中的内容

学习Sketchup::Tool

掌握以下知识点:

  1. Sketchup::Tool 这个类下面的每一个方法都要研究透知道每个方法的意义
  2. 工具右键菜单Sketchup::Tool#getMenu
  3. 捕捉点(捕捉点能帮住用户更好的吸附到想要的位置)
  4. 锁定轴向(三维空间的情况下很难把控方向,需要锁定轴向),键盘方向键用于锁定绘制轴向
  5. 工具预览,(所见即所得是最好的体现方式,我们需要通过工具的预览效果预览出得到的内容) Sketchup::Tool#draw
  6. 鼠标事件、按键事件

如何在工具中预览点线面

class TestTool

  def activate

  end

  def suspend(view)
    view.invalidate
  end

  def deactivate(view)
    view.invalidate
  end

  def onLButtonUp(flags, x, y, view)
    @pts ||= []
    @pts << view.inputpoint(x, y).position
    view.invalidate
  end

  def getMenu(menu, flags, x, y, view)
    menu.add_item("完成") do
      if @pts.nil? || @pts.length < 2
        UI.messagebox '点击点少于两个无法绘制'
      else
        Sketchup.active_model.select_tool nil
        Sketchup.active_model.start_operation 'draw line', true
        @pts.each_cons(2) { |p1, p2|
          Sketchup.active_model.entities.add_line p1, p2
        }
        Sketchup.active_model.commit_operation
      end
    end
  end

  def onUserText(text, view)
    puts text
  end

  def onMouseMove(flags, x, y, view)
    @move_pt = view.inputpoint(x, y).position
    view.invalidate
  end

  def getExtents
    bounds = Geom::BoundingBox.new
    @pts&.each { |pt| bounds.add pt }
    bounds.add @move_pt if @move_pt
    bounds
  end

  def draw(view)
    view.drawing_color = 'red'
    view.line_width = 3
    view.line_stipple = ''
    @pts&.each_cons(2) do |p1, p2|
      view.draw_line p1, p2
    end
    view.line_stipple = '-'
    if @move_pt && @pts
      view.draw_line @pts[-1], @move_pt
    end
  end
end

Sketchup.active_model.select_tool TestTool.new

如何通过点击获取实体

参考API Sketchup::PickHelper

class TestTool
  def onLButtonUp(flags, x, y, view)
    ph = view.pick_helper
    ph.do_pick(x, y)
    puts "all_picked:#{ph.all_picked}"
    puts "best_picked:#{ph.best_picked}"
    # Sketchup::PickHelper 相关的方法都尝试使用下看看能得到什么结果
  end
end

Sketchup.active_model.select_tool TestTool.new

Transformation动态展示

class TranTool #< Sketchup::Tool

  # @param [Sketchup::Face] face
  def self.active_tool(face)
    Sketchup.active_model.select_tool(self.new(face))
  end

  # @param [Sketchup::Face] face
  def initialize(face)
    @face_pts = face.outer_loop.vertices.map(&:position)
    @face_pts_view = @point = nil
    @trans = Geom::Transformation.new
  end

  def onLButtonUp(flags, x, y, view)
    if @point
      Sketchup.active_model.entities.add_face(@face_pts_view || @face_pts)
      Sketchup.active_model.select_tool nil
    else
      @point = view.inputpoint(x, y).position
    end
    view.invalidate
  end

  def onMouseMove(flags, x, y, view)
    pt = view.inputpoint(x, y).position
    if @point
      @trans = Geom::Transformation.new(@point, pt - @point) if (pt - @point).valid?
    else
      @trans = Geom::Transformation.new(pt, Z_AXIS)
    end
    @face_pts_view = @face_pts.map { |p| p.transform(@trans) }
    view.invalidate
  end

  def draw(view)
    view.drawing_color = 'black'
    view.line_width = 2
    # 绘制平面
    @face_pts_view&.each_index do |index|
      view.draw_line @face_pts_view[index - 1], @face_pts_view[index]
    end

    # 绘制面当前使用的tran
    # 视图像素大小转成实际的长度
    length = view.pixels_to_model(50, @trans.origin)
    view.drawing_color = 'red'
    view.draw_line @trans.origin, @trans.origin.offset(@trans.xaxis, length)
    view.drawing_color = 'green'
    view.draw_line @trans.origin, @trans.origin.offset(@trans.yaxis, length)
    view.drawing_color = 'blue'
    view.draw_line @trans.origin, @trans.origin.offset(@trans.zaxis, length)
  end
end

TranTool.active_tool(Sketchup.active_model.selection[0])

绘制贝兹曲线工具

绘制工具需满足以下条件

  1. 绘制过程中需要实时预览出曲线以及放样点

  2. 曲线点支持按Esc键撤销放样点

    贝兹曲线

截面放样工具

认真学习s4u_linetool代码内容

绘制工具需满足以下条件

  1. 激活工具第一步选择一个需要被放样的面,点击选择的面后就进入第二步

  2. 第二步点击放样的路径点,并预览出放样的效果(预览效果需要考虑转折点模型拼接问题)

  3. 右键完成生成放样后的模型

    四分之一圆柱放样

# 根据路径点求解每个路径点对应的面,该方法可优化(每次增加点和减少点的时候只有部分面需要更新)
# face_pts 待放样的截面点
# path_pts 放样路径点
# points_face  存放每个点对应的平面
vec = path_pts[1] - path_pts[0]
tr = Geom::Transformation.new(path_pts[0], vec) * Geom::Transformation.new(ORIGIN - face_pts[0])
points_face = [face_pts.map { |pt| pt.transform(tr) }]
# 求解角平分线所在平面
path_pts.each_cons(3) do |p1, p2, p3|
    v1, v2 = (p1 - p2).normalize, (p3 - p2).normalize
    # 同向点不应该出现在路径点内 添加的时候要注意判断
    if v1.parallel?(v2)
        plane = [p2, v1]
    else
        # 角平分线向量
        v = v1 + v2
        plane = [p2, v1 * v2 * v]
    end
    points_face << points_face[-1].map { |pt| Geom.intersect_line_plane([pt, v1], plane) }
end
plane = [path_pts[-1], path_pts[-1] - path_pts[-2]]
points_face << points_face[-1].map { |pt| pt.project_to_plane(plane) }

SU单位(长度、角度)

数值与长度的转换、长度与字符串之间转换、角度同理

Class: Length — SketchUp Ruby API Documentation

Class: Numeric — SketchUp Ruby API Documentation

Class: String — SketchUp Ruby API Documentation

属性记录

Sketchup::Entity以及其子类都支持进行属性记录,属性结构可以理解为一个两层结构的Hash,通过字典名找到字典,在在字典中通过Key找到值。

仔细属性并尝试以下API,可以配合Sketchup官方提供的属性查看器验证自己对API的尝试

实体属性操作相关API:添加属性删除属性读取属性获取实体上所有字典,获取实体上指定字典

属性字典相关API:Sketchup::AttributeDictionaries Sketchup::AttributeDictionary

属性字典名 以及 key均为字符串,属性值可以是多种数据类型(数字、字符串、数组...)虽然一些对象也支持直接存入,但是取出数据的时候是有影响的

几何相关运算

重点熟悉以下内容

GeomGeom::BoundingBoxGeom::Point3d Geom::Transformation Geom::Vector3d

开发一个简易窗体界面

可以参照学习ruby for sketchup目录下的run_code以及UI_DEMO

熟悉使用UI::HtmlDialog并掌握前端数据与后台数据的交互

开发一个窗口实现简单的加减乘除运算,运算一定要交给Ruby后台 不可以使用JS直接计算

image-20230415110557149

Sketchup Ruby C Extension

学习资料:The Ruby C API (silverhammermba.github.io)SketchUp/ruby-c-extension-examples: Ruby C extension examples (github.com)

学习时长:两周半

学习知识点:使用Ruby C API 开发Ruby下能直接使用的动态链接库、安装使用VS2022

日常练习:参照SU官方给出的案例

考核内容:三天完成指定Ruby脚本内容迁移至C Extension