如何在ruby2d中创建边框?

mwngjboj  于 2023-05-17  发布在  Ruby
关注(0)|答案(1)|浏览(93)

我用ruby2d做了一个2d形状编辑器。它非常简单,但它需要支持一些东西。现在我被创建一个边框卡住了(我做了一个比主边框稍大的对象,然后把它放在后面)。主要的问题是当我改变主对象的形状时,它的边界就像它的阴影一样,按比例变化。
这就是它在默认状态下的样子:

这就是它在变化后的状态:

def check_point(event)
  $array_of_figures.each do |element|
    element.hash_of_cp.each{|name,cp| @element=element,cp if cp.contains?event.x,event.y}
  end
end
update do
  on :mouse_down do |event|
    check_point(event)
    on :mouse_move do  |smth|
      @element[0].change(smth,@element[1]) if @element!=0
    end
    on :mouse_up do|this|
      if @element!=0
        @element[0].change(this,@element[1])
        @element=0
      end
    end
  end
end
show

class N3 < Figure
  def initialize
    super
    @premitive=Triangle.new(
      x1: 60,  y1: 10,
      x2: 110, y2: 100,
      x3: 10,   y3: 100,
      color: 'yellow',
      z: 100
    )
    @arc=Triangle.new(
      x1: @premitive.x1,  y1: @premitive.y1.to_i-2,
      x2: @premitive.x2.to_i+2, y2: @premitive.y2.to_i+2,
      x3: @premitive.x3.to_i-2,   y3: @premitive.y3.to_i+2,
      color: 'white',
      z: 99
    )
    @hash_of_cp[:x1_cp]=Circle.new(
      x: @premitive.x1, y: @premitive.y1,
      radius: 3,
      sectors: 32,
      color: 'lime',
      z: 101
    )
    @hash_of_cp[:x2_cp]=Circle.new(
      x: @premitive.x2, y: @premitive.y2,
      radius: 3,
      sectors: 32,
      color: 'lime',
      z: 101
    )
    @hash_of_cp[:x3_cp]=Circle.new(
      x: @premitive.x3, y: @premitive.y3,
      radius: 3,
      sectors: 32,
      color: 'lime',
      z: 101
    )
    @center=Circle.new(
      x: (@premitive.x1+@premitive.x2+@premitive.x3)/3 , y: (@premitive.y1+@premitive.y2+@premitive.y3)/3,
      radius: 3,
      sectors: 32,
      color: 'lime',
      z: 101
    )
  end

  def change(smth,cp)
    if @premitive.x1==cp.x && @premitive.y1==cp.y
      @premitive.x1=smth.x
      @premitive.y1=smth.y
      @arc.x1=smth.x
      @arc.y1=smth.y
    elsif  @premitive.x2==cp.x && @premitive.y2==cp.y
      @premitive.x2=smth.x
      @premitive.y2=smth.y
      @arc.x2=@premitive.x2
      @arc.y2=@premitive.y2
    elsif  @premitive.x3==cp.x && @premitive.y3==cp.y
      @premitive.x3=smth.x
      @premitive.y3=smth.y
      @arc.x3=@premitive.x3
      @arc.y3=@premitive.y3
    end
    @center.x=(@premitive.x1+@premitive.x2+@premitive.x3)/3
    @center.y=(@premitive.y1+@premitive.y2+@premitive.y3)/3
    cp.x =smth.x
    cp.y =smth.y
  end
end
dauxcl2d

dauxcl2d1#

用线做边框,然后添加圆将其圆化:

width, color = 20, "green"
radius = width / 2
# triangle verticies
v1 = {x: x1, y: y1}
v2 = {x: x2, y: y2}
v3 = {x: x3, y: y3}
# triangle edges
e1 = {x1: x1, y1: y1, x2: x2, y2: y2}
e2 = {x1: x2, y1: y2, x2: x3, y2: y3}
e3 = {x1: x3, y1: y3, x2: x1, y2: y1}
# lines over the edges
Line.new(width:, color:, **e1),
Line.new(width:, color:, **e2),
Line.new(width:, color:, **e3),
# hide the shame with circles
Circle.new(radius:, color:, **v1),
Circle.new(radius:, color:, **v2),
Circle.new(radius:, color:, **v3)

这是一个有趣的一点点。但它是如此低的水平,我的意思是,更新是如此困难。

# Game of triangle. You can rotate, move, and resize it,
# and it has borders.

require "ruby2d"

COLORS = %w[blue aqua teal olive green lime yellow orange red brown fuchsia purple maroon]
UPDATE_CALLBACKS = []
def window_center = [Window.get(:width) / 2, Window.get(:height) / 2]
def sqrt(...) = Math.sqrt(...)
def cos(...) = Math.cos(...)
def sin(...) = Math.sin(...)
def rads(degrees) = degrees * Math::PI / 180

class Collection
  def initialize(**shapes_hash)
    @shapes_hash = shapes_hash
    @shapes = shapes_hash.values
  end
  def add = @shapes.each(&:add)
  def remove = @shapes.each(&:remove)
  def contains?(x, y) = @shapes.select { |shape| shape.contains?(x, y) }
  def key(shape) = @shapes_hash.rassoc(shape).first
  def update_all attached
    @shapes_hash.each do |key, shape|
      coordinates = attached.send(key)
      coordinates.each do |coordinate, value|
        shape.send("#{coordinate}=", value)
      end
    end
  end
end

class Triangle < Ruby2D::Triangle
  def self.generate(size: 500, rotation: 0, center: window_center, color: COLORS.sample)
    radius = size * sqrt(3) / 6
    rotation -= 90 # pointy end up
    cx, cy = center
    coordinates = {
      x1: cx + radius * cos(rads(rotation + 0)),
      y1: cy + radius * sin(rads(rotation + 0)),
      x2: cx + radius * cos(rads(rotation + 120)),
      y2: cy + radius * sin(rads(rotation + 120)),
      x3: cx + radius * cos(rads(rotation + 240)),
      y3: cy + radius * sin(rads(rotation + 240))
    }
    t = new(color:, **coordinates)
    t.on_drag
    t
  end

  def rotate degrees = 1
    cx, cy = center.values
    verticies.map do |x, y|
      [x - cx, y - cy]
    end.map do |x, y|
      [x * cos(rads(degrees)) - y * sin(rads(degrees)), x * sin(rads(degrees)) + y * cos(rads(degrees))]
    end.map do |x, y|
      [x + cx, y + cy]
    end.each.with_index(1) do |(x, y), index|
      send("x#{index}=", x)
      send("y#{index}=", y)
    end
    @borders&.update_all(self)
    @points&.update_all(self)
  end

  def coordinates = {x1:, y1:, x2:, y2:, x3:, y3:}
  def verticies = [[x1, y1], [x2, y2], [x3, y3]]
  def v1 = {x: x1, y: y1}
  def v2 = {x: x2, y: y2}
  def v3 = {x: x3, y: y3}
  def e1 = {x1: x1, y1: y1, x2: x2, y2: y2}
  def e2 = {x1: x2, y1: y2, x2: x3, y2: y3}
  def e3 = {x1: x3, y1: y3, x2: x1, y2: y1}
  def center = {x: (x1 + x2 + x3) / 3, y: (y1 + y2 + y3) / 3}

  def points radius: 15, color: "fuchsia"
    @points ||= Collection.new(
      v1: Circle.new(radius:, color:, sectors: 16, **v1),
      v2: Circle.new(radius:, color:, sectors: 16, **v2),
      v3: Circle.new(radius:, color:, sectors: 16, **v3),
      center: Circle.new(radius:, color:, sectors: 16, **center)
    )
  end

  def borders width: 10, color: "green"
    @borders ||= begin
      radius = width / 2.0
      Collection.new(
        e1: Line.new(width:, color:, **e1),
        e2: Line.new(width:, color:, **e2),
        e3: Line.new(width:, color:, **e3),
        v1: Circle.new(radius:, color:, sectors: 16, **v1),
        v2: Circle.new(radius:, color:, sectors: 16, **v2),
        v3: Circle.new(radius:, color:, sectors: 16, **v3)
      )
    end
  end

  def on_drag
    Window.on(:mouse_down) do |event|
      @dragging = @points.contains?(event.x, event.y).first if @points
    end
    Window.on(:mouse_up) { @dragging = nil }
    UPDATE_CALLBACKS << proc do
      if @dragging
        key = points.key(@dragging)
        x, y = Window.mouse_x, Window.mouse_y
        case key
        when :v1
          self.x1, self.y1 = x, y
        when :v2
          self.x2, self.y2 = x, y
        when :v3
          self.x3, self.y3 = x, y
        when :center
          cx, cy = center.values
          self.x1, self.y1 = (x1 + x - cx), (y1 + y - cy)
          self.x2, self.y2 = (x2 + x - cx), (y2 + y - cy)
          self.x3, self.y3 = (x3 + x - cx), (y3 + y - cy)
        end
        @points&.update_all(self)
        @borders&.update_all(self)
      end
    end
    self
  end
end

class Button
  def initialize(name:, x: 0, y: 0)
    @text = Text.new(name, x:, y:)
    @w = @text.instance_variable_get(:@width)
    @h = @text.instance_variable_get(:@height)
  end
  attr_reader :text, :w, :h

  def y= num
    text.y = num
  end

  def on_click
    Window.on(:mouse_down) do |event|
      yield if @text.contains? event.x, event.y
    end
    self
  end

  def on_mouse_down
    Window.on(:mouse_down) do |event|
      @down = true if @text.contains? event.x, event.y
    end
    Window.on(:mouse_up) { @down = nil }
    UPDATE_CALLBACKS << proc do
      yield if @down
    end
    self
  end
end

class Menu
  def initialize(*buttons)
    @buttons = buttons
    heights = @buttons.map(&:h)
    widths = @buttons.map(&:w)
    Rectangle.new(x: 0, y: 0, width: widths.max, height: heights.sum, color: "maroon", z: -1)
    y = 0
    @buttons.each do |btn|
      btn.y = y
      y += btn.h
    end
  end
end

triangle = Triangle.generate
Menu.new(
  Button.new(name: "show points").on_click { triangle.points.add },
  Button.new(name: "hide points").on_click { triangle.points.remove },
  Button.new(name: "show borders").on_click { triangle.borders(width: 20).add },
  Button.new(name: "hide borders").on_click { triangle.borders.remove },
  Button.new(name: "rotate").on_mouse_down { triangle.rotate }
)

update do
  UPDATE_CALLBACKS.each(&:call)
end

show

# ruby v3.2.2

相关问题