赞 | 15 |
VIP | 0 |
好人卡 | 0 |
积分 | 19 |
经验 | 16801 |
最后登录 | 2024-7-10 |
在线时间 | 403 小时 |
Lv3.寻梦者
- 梦石
- 0
- 星屑
- 1939
- 在线时间
- 403 小时
- 注册时间
- 2015-8-30
- 帖子
- 395
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 有丘直方 于 2018-12-2 20:29 编辑
class Vector include(Enumerable) attr_accessor :x attr_accessor :y def initialize(*args) case args.size when 0 @x = 0 @y = 0 when 1 @x = args[0] @y = args[0] when 2 @x = args[0] @y = args[1] end end def +@ self end def -@ Vector.new(-@x, -@y) end def +(other) Vector.new(@x + other.x, @y + other.y) end def -(other) self + -other end def *(other) case other when Numeric Vector.new(@x * other, @y * other) when Vector @x * other.x + @y * other.y end end def dot(other) self * other end def cross(other) self.x * other.y - other.x * self.y end def slope y / x end def square_length self * self end def length Math.sqrt(square_length) end def is_out_of?(other) @x.abs > other.x.abs || @y.abs > other.y.abs end def abs Vector.new(@x.abs, @y.abs) end def each yield @x yield @y end end module Math def self.definite_proportion(point1, point2, proportion) point1 * (1 - proportion) + point2 * proportion end end class Rect alias ulysses201811181949_initialize initialize def initialize(*args) args = case args.size when 0, 4 args when 2 [args[0].x, args[0].y, args[1].x, args[1].y] end ulysses201811181949_initialize(*args) end def position Vector.new(x, y) end def position=(position) self.x = position.x self.y = position.y end def size Vector.new(width, height) end def size=(size) self.width = size.x self.height = size.y end end module Kernel def do_by_step(from, to, step) if block_given? i = from until i >= to yield i i += step end else to_enum(__method__, from, to, step) end end end class Bitmap alias ulysses201811181708_initialize initialize def initialize(*args) args = if args.size == 2 || args[0].is_a?(String) args elsif args.size == 1 [args[0].x, args[0].y] else [0, 0] end ulysses201811181708_initialize(*args) end def fill_circle(center, radius, color) i_max = (radius * 707) / 1000 + 1 square_distance_max = radius * radius + radius / 2 x = radius fill_rect(center.x - radius, center.y, 2 * radius, 1, color) for i in 1..i_max if Vector.new(center.x, i).square_length > square_distance_max if x > i_max fill_rect(center.x - i + 1, center.y + x, 2 * (i - 1), 1, color) fill_rect(center.x - i + 1, center.y - x, 2 * (i - 1), 1, color) end x -= 1 end fill_rect(center.x - x, center.y + i, 2 * x, 1, color) fill_rect(center.x - x, center.y - i, 2 * x, 1, color) end end def draw_line(point1, point2, color) difference = point2 - point1 abs_difference = difference.abs total_step = abs_difference.max delta = difference * (1.0 / total_step) vector = point1 (total_step - 1).to_i.times do set_pixel(vector, color) vector += delta end end alias ulysses201811182036_set_pixel set_pixel def set_pixel(*args) args = case args.size when 3 args when 2 [args[0].x, args[0].y, args[1]] end ulysses201811182036_set_pixel(*args) end def text_vector(text) text_size(text).size end def size Vector.new(width, height) end def size=(size) self.rect.size = size end alias ulysses201811181952_draw_text draw_text def draw_text(*args) args = case args[0] when Numeric, Rect args when Vector a = [args[0].x, args[0].y, args[1].x, args[1].y, args[2]] a.push(args[3]) if args.size >= 4 a end ulysses201811181952_draw_text(*args) end end class Sprite alias ulysses201811181903_initialize initialize def initialize(viewport = nil) ulysses201811181903_initialize(viewport) self.bitmap = Bitmap.new end def position=(position) self.x = position.x self.y = position.y end end module Graphics def self.size Vector.new(width, height) end end module Mouse def self.position Vector.new(x, y) end end module Scene POINT_RADIUS = 3 POINT_COLOR = Color.new(255, 255, 255, 255) POINT_Z = 10 CURVE_RADIUS = 2 CURVE_COLOR = Color.new(255, 255, 255, 255) LINE_COLOR = Color.new(255, 255, 255, 255) T_BEGINNING = 0.0 T_END = 1.0 T_STEP = 0.001 UPDATE_STEPS = 20 def self.init Graphics.resize_screen(1024, 768) @graph_sprite = Sprite.new @graph_sprite.bitmap = Bitmap.new(Graphics.size) @line_sprite = Sprite.new @line_sprite.bitmap = Bitmap.new(Graphics.size) @points = [] @point_sprites = [] @temp_bitmap = Bitmap.new(32, 32) @line_visible = true @point_visible = true @fast_mode = false @covering_drawing = 0 end def self.create_point(point) sprite = Sprite.new sprite.visible = @point_visible radius_vector = Vector.new(POINT_RADIUS) text_vector = @temp_bitmap.text_vector(@points.size) radius_text_vector = radius_vector + text_vector sprite.bitmap = Bitmap.new(radius_text_vector + radius_vector) flip = (point + radius_text_vector).is_out_of?(Graphics.size) excursion = flip ? radius_text_vector : radius_vector sprite.position = point - excursion sprite.bitmap.fill_circle(excursion, POINT_RADIUS, POINT_COLOR) text_position = flip ? Vector.new : radius_vector * 2 sprite.bitmap.draw_text(text_position, text_vector, @points.size) sprite.z = POINT_Z @points.push(point) @point_sprites.push(sprite) refresh_graph end def self.delete_point @points.pop @point_sprites.pop.dispose refresh_graph end def self.refresh_graph @covering_drawing += 1 @graph_sprite.bitmap.clear return if @points.empty? step_count = 0 will_update = false do_by_step(T_BEGINNING, T_END, T_STEP) do |t| will_update = true if !@fast_mode && (step_count += 1) >= UPDATE_STEPS @line_sprite.bitmap.clear sequences = [@points] (@points.size - 1).times do |i| last_sequence = sequences.last sequences.push([]) (last_sequence.size - 1).times do |j| if @line_visible && will_update @line_sprite.bitmap.draw_line(*last_sequence[j, 2], LINE_COLOR) end sequences.last.push(Math.definite_proportion(*last_sequence[j, 2], t)) end end @graph_sprite.bitmap.fill_circle(*sequences.last, CURVE_RADIUS, CURVE_COLOR) if will_update step_count = 0 will_update = false last_covering_drawing = @covering_drawing update return if @covering_drawing > last_covering_drawing end end (@points.size - 1).times do |i| @line_sprite.bitmap.draw_line(*@points[i, 2], LINE_COLOR) end end def self.toggle_line_visible @line_visible = !@line_visible @line_sprite.visible = @line_visible end def self.toggle_point_visible @point_visible = !@point_visible @point_sprites.each {|sprite| sprite.visible = @point_visible } end def self.toggle_fast_mode @fast_mode = !@fast_mode end def self.update_basic Graphics.update Input.update Mouse.update end def self.update update_basic if Mouse.click?(:L) create_point(Mouse.position) elsif Mouse.click?(:R) delete_point elsif Input.trigger?(:A) toggle_fast_mode elsif Input.trigger?(:B) toggle_line_visible elsif Input.trigger?(:C) toggle_point_visible end end def self.main init loop { update } end end Scene.main
class Vector
include(Enumerable)
attr_accessor :x
attr_accessor :y
def initialize(*args)
case args.size
when 0
@x = 0
@y = 0
when 1
@x = args[0]
@y = args[0]
when 2
@x = args[0]
@y = args[1]
end
end
def +@
self
end
def -@
Vector.new(-@x, -@y)
end
def +(other)
Vector.new(@x + other.x, @y + other.y)
end
def -(other)
self + -other
end
def *(other)
case other
when Numeric
Vector.new(@x * other, @y * other)
when Vector
@x * other.x + @y * other.y
end
end
def dot(other)
self * other
end
def cross(other)
self.x * other.y - other.x * self.y
end
def slope
y / x
end
def square_length
self * self
end
def length
Math.sqrt(square_length)
end
def is_out_of?(other)
@x.abs > other.x.abs || @y.abs > other.y.abs
end
def abs
Vector.new(@x.abs, @y.abs)
end
def each
yield @x
yield @y
end
end
module Math
def self.definite_proportion(point1, point2, proportion)
point1 * (1 - proportion) + point2 * proportion
end
end
class Rect
alias ulysses201811181949_initialize initialize
def initialize(*args)
args = case args.size
when 0, 4
args
when 2
[args[0].x, args[0].y, args[1].x, args[1].y]
end
ulysses201811181949_initialize(*args)
end
def position
Vector.new(x, y)
end
def position=(position)
self.x = position.x
self.y = position.y
end
def size
Vector.new(width, height)
end
def size=(size)
self.width = size.x
self.height = size.y
end
end
module Kernel
def do_by_step(from, to, step)
if block_given?
i = from
until i >= to
yield i
i += step
end
else
to_enum(__method__, from, to, step)
end
end
end
class Bitmap
alias ulysses201811181708_initialize initialize
def initialize(*args)
args = if args.size == 2 || args[0].is_a?(String)
args
elsif args.size == 1
[args[0].x, args[0].y]
else
[0, 0]
end
ulysses201811181708_initialize(*args)
end
def fill_circle(center, radius, color)
i_max = (radius * 707) / 1000 + 1
square_distance_max = radius * radius + radius / 2
x = radius
fill_rect(center.x - radius, center.y, 2 * radius, 1, color)
for i in 1..i_max
if Vector.new(center.x, i).square_length > square_distance_max
if x > i_max
fill_rect(center.x - i + 1, center.y + x, 2 * (i - 1), 1, color)
fill_rect(center.x - i + 1, center.y - x, 2 * (i - 1), 1, color)
end
x -= 1
end
fill_rect(center.x - x, center.y + i, 2 * x, 1, color)
fill_rect(center.x - x, center.y - i, 2 * x, 1, color)
end
end
def draw_line(point1, point2, color)
difference = point2 - point1
abs_difference = difference.abs
total_step = abs_difference.max
delta = difference * (1.0 / total_step)
vector = point1
(total_step - 1).to_i.times do
set_pixel(vector, color)
vector += delta
end
end
alias ulysses201811182036_set_pixel set_pixel
def set_pixel(*args)
args = case args.size
when 3
args
when 2
[args[0].x, args[0].y, args[1]]
end
ulysses201811182036_set_pixel(*args)
end
def text_vector(text)
text_size(text).size
end
def size
Vector.new(width, height)
end
def size=(size)
self.rect.size = size
end
alias ulysses201811181952_draw_text draw_text
def draw_text(*args)
args = case args[0]
when Numeric, Rect
args
when Vector
a = [args[0].x, args[0].y, args[1].x, args[1].y, args[2]]
a.push(args[3]) if args.size >= 4
a
end
ulysses201811181952_draw_text(*args)
end
end
class Sprite
alias ulysses201811181903_initialize initialize
def initialize(viewport = nil)
ulysses201811181903_initialize(viewport)
self.bitmap = Bitmap.new
end
def position=(position)
self.x = position.x
self.y = position.y
end
end
module Graphics
def self.size
Vector.new(width, height)
end
end
module Mouse
def self.position
Vector.new(x, y)
end
end
module Scene
POINT_RADIUS = 3
POINT_COLOR = Color.new(255, 255, 255, 255)
POINT_Z = 10
CURVE_RADIUS = 2
CURVE_COLOR = Color.new(255, 255, 255, 255)
LINE_COLOR = Color.new(255, 255, 255, 255)
T_BEGINNING = 0.0
T_END = 1.0
T_STEP = 0.001
UPDATE_STEPS = 20
def self.init
Graphics.resize_screen(1024, 768)
@graph_sprite = Sprite.new
@graph_sprite.bitmap = Bitmap.new(Graphics.size)
@line_sprite = Sprite.new
@line_sprite.bitmap = Bitmap.new(Graphics.size)
@points = []
@point_sprites = []
@temp_bitmap = Bitmap.new(32, 32)
@line_visible = true
@point_visible = true
@fast_mode = false
@covering_drawing = 0
end
def self.create_point(point)
sprite = Sprite.new
sprite.visible = @point_visible
radius_vector = Vector.new(POINT_RADIUS)
text_vector = @temp_bitmap.text_vector(@points.size)
radius_text_vector = radius_vector + text_vector
sprite.bitmap = Bitmap.new(radius_text_vector + radius_vector)
flip = (point + radius_text_vector).is_out_of?(Graphics.size)
excursion = flip ? radius_text_vector : radius_vector
sprite.position = point - excursion
sprite.bitmap.fill_circle(excursion, POINT_RADIUS, POINT_COLOR)
text_position = flip ? Vector.new : radius_vector * 2
sprite.bitmap.draw_text(text_position, text_vector, @points.size)
sprite.z = POINT_Z
@points.push(point)
@point_sprites.push(sprite)
refresh_graph
end
def self.delete_point
@points.pop
@point_sprites.pop.dispose
refresh_graph
end
def self.refresh_graph
@covering_drawing += 1
@graph_sprite.bitmap.clear
return if @points.empty?
step_count = 0
will_update = false
do_by_step(T_BEGINNING, T_END, T_STEP) do |t|
will_update = true if !@fast_mode && (step_count += 1) >= UPDATE_STEPS
@line_sprite.bitmap.clear
sequences = [@points]
(@points.size - 1).times do |i|
last_sequence = sequences.last
sequences.push([])
(last_sequence.size - 1).times do |j|
if @line_visible && will_update
@line_sprite.bitmap.draw_line(*last_sequence[j, 2], LINE_COLOR)
end
sequences.last.push(Math.definite_proportion(*last_sequence[j, 2], t))
end
end
@graph_sprite.bitmap.fill_circle(*sequences.last, CURVE_RADIUS, CURVE_COLOR)
if will_update
step_count = 0
will_update = false
last_covering_drawing = @covering_drawing
update
return if @covering_drawing > last_covering_drawing
end
end
(@points.size - 1).times do |i|
@line_sprite.bitmap.draw_line(*@points[i, 2], LINE_COLOR)
end
end
def self.toggle_line_visible
@line_visible = !@line_visible
@line_sprite.visible = @line_visible
end
def self.toggle_point_visible
@point_visible = !@point_visible
@point_sprites.each {|sprite| sprite.visible = @point_visible }
end
def self.toggle_fast_mode
@fast_mode = !@fast_mode
end
def self.update_basic
Graphics.update
Input.update
Mouse.update
end
def self.update
update_basic
if Mouse.click?(:L)
create_point(Mouse.position)
elsif Mouse.click?(:R)
delete_point
elsif Input.trigger?(:A)
toggle_fast_mode
elsif Input.trigger?(:B)
toggle_line_visible
elsif Input.trigger?(:C)
toggle_point_visible
end
end
def self.main
init
loop { update }
end
end
Scene.main
马上要睡了,不多说了,看图
本来想用shader的,后来还是屈服于Bitmap了……
按左键添加节点,按右键去除节点,按A开启快速模式(不显示绘制过程),按B切换线段的显示,按C切换节点的显示(此处ABC不是键盘上的ABC)
# 二维向量 class Vector # 可以进行枚举,参见#each include(Enumerable) attr_accessor :x # x方向上的分量 attr_accessor :y # y方向上的分量 # 初始化 # 无参数时:(0, 0) # 有一个参数r时:(r, r) # 有两个参数x, y时:(x, y) def initialize(*args) case args.size when 0 @x = 0 @y = 0 when 1 @x = args[0] @y = args[0] when 2 @x = args[0] @y = args[1] end end def +@ self end # 反向量 def -@ Vector.new(-@x, -@y) end # 加法 def +(other) Vector.new(@x + other.x, @y + other.y) end # 减法 def -(other) self + -other end # 点乘或数乘运算 def *(other) case other when Numeric Vector.new(@x * other, @y * other) when Vector @x * other.x + @y * other.y end end # 点积 def dot(other) @x * other.x + @y * other.y end # 叉积 def cross(other) self.x * other.y - other.x * self.y end # 斜率 def slope x =! 0 ? y / x : Float::Infinity end # 模的平方 def square_length self * self end # 模 def length Math.sqrt(square_length) end # 超出范围 def is_out_of?(other) @x.abs > other.x.abs || @y.abs > other.y.abs end # 对x和y方向的分量分别取绝对值 def abs Vector.new(@x.abs, @y.abs) end # 切向 def normal Vector.new(-@y, @x) end # 枚举,先对x计算,后对y计算 def each yield @x yield @y end def inspect "(#{@x}, #{@y})" end end module Math # 获得两点之间由某个比例决定的分点 def self.definite_proportion(point1, point2, proportion) point1 * (1 - proportion) + point2 * proportion end end class Rect # 初始化 # 除了原先的参数列表,支持由两个向量来决定其位置大小 # 如果用向量表示,第一个向量是position,第二个向量是size alias ulysses201811181949_initialize initialize def initialize(*args) args = case args.size when 0, 4 args when 2 [args[0].x, args[0].y, args[1].x, args[1].y] end ulysses201811181949_initialize(*args) end def position Vector.new(x, y) end def position=(position) self.x = position.x self.y = position.y end def size Vector.new(width, height) end def size=(size) self.width = size.x self.height = size.y end end module Kernel # 令变量从from到to按照step进行线性变化并传递到块中 def do_by_step(from, to, step) if block_given? i = from until i >= to yield i i += step end else to_enum(__method__, from, to, step) end end def gets(*args) $stdin.gets(*args) end def Vector(*args) Vector.new(*args) end end class Bitmap # 初始化 # 支持向量表示 alias ulysses201811181708_initialize initialize def initialize(*args) args = if args.size == 2 || args[0].is_a?(String) args elsif args.size == 1 [args[0].x, args[0].y] else [0, 0] end ulysses201811181708_initialize(*args) end # 填充一个圆 def fill_circle(center, radius, color) i_max = (radius * 707) / 1000 + 1 square_distance_max = radius * radius + radius / 2 x = radius fill_rect(center.x - radius, center.y, 2 * radius, 1, color) for i in 1..i_max if Vector.new(center.x, i).square_length > square_distance_max if x > i_max fill_rect(center.x - i + 1, center.y + x, 2 * (i - 1), 1, color) fill_rect(center.x - i + 1, center.y - x, 2 * (i - 1), 1, color) end x -= 1 end fill_rect(center.x - x, center.y + i, 2 * x, 1, color) fill_rect(center.x - x, center.y - i, 2 * x, 1, color) end end # 用一条1像素宽的线段联结两点 def draw_line(point1, point2, color) difference = point2 - point1 abs_difference = difference.abs total_step = abs_difference.max delta = difference * (1.0 / total_step) vector = point1 (total_step.to_i + 1).times do set_pixel(vector, color) vector += delta end end alias ulysses201811182036_set_pixel set_pixel def set_pixel(*args) args = case args.size when 3 args when 2 [args[0].x, args[0].y, args[1]] end ulysses201811182036_set_pixel(*args) end def text_vector(text) text_size(text).size end def size Vector.new(width, height) end def size=(size) self.rect.size = size end alias ulysses201811181952_draw_text draw_text def draw_text(*args) args = case args[0] when Numeric, Rect args when Vector a = [args[0].x, args[0].y, args[1].x, args[1].y, args[2]] a.push(args[3]) if args.size >= 4 a end ulysses201811181952_draw_text(*args) end end class Sprite def position=(position) self.x = position.x self.y = position.y end end class Viewport # 让add_fast_layer接受默认参数blend_type = 0 alias ulysses201812021851_add_fast_layer add_fast_layer def add_fast_layer(z, blend_type = 0) ulysses201812021851_add_fast_layer(z, blend_type) end end # 重写了Input模块 # 支持全键盘和鼠标按键 # 如果不需要判断鼠标位置的话,可以不update module Input REPEAT_TIME = 4 # 当key被按超过此数时,repeat?(key) == true GetKeyState = Win32API.new("user32", "GetAsyncKeyState", ['I'], 'I') @key_states = {} @key_repeat_times = {} def self.press?(key) key < 0x08 ? Mouse.press?(key) : GetKeyState.call(key) != 0 end def self.repeat?(key) if press?(key) unless @key_repeat_times[key] @key_repeat_times[key] = 0 return true end @key_repeat_times[key] += 1 else @key_repeat_times[key] = nil @key_states[key] = 0 end if !@key_repeat_times[key].nil? && @key_repeat_times[key] > REPEAT_TIME @key_repeat_times[key] = 0 return true end false end def self.trigger?(key) if press?(key) return false if @key_states[key] == 1 @key_states[key] = 1 true else @key_states[key] = 0 false end end def self.update Mouse.update end end class << Graphics def size Vector.new(width, height) end alias ulysses201811251830_resize_screen resize_screen def resize_screen(*args) case args.size when 2 ulysses201811251830_resize_screen(*args) when 1 ulysses201811251830_resize_screen(args[0].x, args[0].y) end end end Graphics.resize_screen(1024, 768) # 更改分辨率 module Mouse def self.position Vector.new(x, y) end end # 封装为模块的场景 module Scene # 绘制控制点的参数 POINTS_RADIUS = 3 POINTS_COLOR = Color.new(255, 255, 255, 255) POINTS_Z = 10 # 绘制曲线的参数 CURVE_COLOR = Color.new(255, 255, 255, 255) CURVE_Z = 0 # 绘制线段的参数 LINES_COLOR = Color.new(255, 255, 255, 127) LINES_Z = -10 # 用于调整绘制的范围和精度 T_BEGINNING = 0.0 T_END = 1.0 T_STEP = 0.01 UPDATE_STEPS = 5 TEMP_BITMAP = Bitmap.new(32, 32) # 临时位图,用于获取文字大小 # 按键 CREATE_CONTROL_POINT_KEY = 0x01 # left DELETE_CONTROL_POINT_KEY = 0x02 # right FAST_MODE_KEY = 0x41 # A LINES_VISIBLE_KEY = 0x42 # B CONTROL_POINTS_VISIBLE_KEY = 0x43 # C # 显示端口 @viewport = Viewport.new @viewport.add_fast_layer(POINTS_Z) # 曲线 @curve_sprite = Sprite.new(@viewport) @curve_sprite.bitmap = Bitmap.new(Graphics.size) @curve_sprite.z = CURVE_Z # 线段 @lines_sprite = Sprite.new(@viewport) @lines_sprite.bitmap = Bitmap.new(Graphics.size) @lines_sprite.z = LINES_Z # 控制点 @control_points = [] @control_point_sprites = [] # 模式控制 @lines_visible = true @control_points_visible = true @fast_mode = false # 阻断刷新控制 @covering_drawing = 0 # 创建控制点 def self.create_control_point(point) sprite = Sprite.new(@viewport) sprite.visible = @control_points_visible radius_vector = Vector.new(POINTS_RADIUS) text_vector = TEMP_BITMAP.text_vector(@control_points.size) radius_text_vector = radius_vector + text_vector sprite.bitmap = Bitmap.new(radius_text_vector + radius_vector) sprite.bitmap.font.color = POINTS_COLOR x_flip = (point + radius_text_vector).x > Graphics.width y_flip = (point + radius_text_vector).y > Graphics.height excursion = Vector.new excursion.x = x_flip ? radius_text_vector.x : radius_vector.x excursion.y = y_flip ? radius_text_vector.y : radius_vector.y sprite.position = point - excursion sprite.bitmap.fill_circle(excursion, POINTS_RADIUS, POINTS_COLOR) text_position = Vector.new text_position.x = x_flip ? 0 : radius_vector.x * 2 text_position.y = y_flip ? 0 : radius_vector.y * 2 sprite.bitmap.draw_text(text_position, text_vector, @control_points.size) sprite.z = POINTS_Z @control_point_sprites.push(sprite) @control_points.push(point) refresh_graph point end # 删除上一个控制点 def self.delete_control_point return if @control_points.empty? point = @control_points.pop sprite = @control_point_sprites.pop sprite.bitmap.dispose sprite.dispose refresh_graph point end # 刷新图形 # 该方法在非快速模式下会阻塞,但update不会停且可以被打断 def self.refresh_graph @covering_drawing += 1 @curve_sprite.bitmap.clear return if @control_points.empty? graph_points = [] steps_count = 0 do_by_step(T_BEGINNING, T_END, T_STEP) do |t| will_update = !@fast_mode && (steps_count += 1) >= UPDATE_STEPS graph_points.push(next_graph_point(t, will_update)) if graph_points.size > 1 @curve_sprite.bitmap.draw_line(*graph_points.last(2), CURVE_COLOR) end if will_update steps_count = 0 last_covering_drawing = @covering_drawing update return if @covering_drawing > last_covering_drawing end end connect_control_points_with_lines end # 根据t返回曲线上的点坐标 # 若@lines_visible && will_update则会绘制线段 def self.next_graph_point(t, will_update = false) is_drawing = @lines_visible && will_update @lines_sprite.bitmap.clear if is_drawing sequences = [@control_points] (@control_points.size - 1).times do |i| last_sequence = sequences.last sequences.push([]) (last_sequence.size - 1).times do |j| if is_drawing @lines_sprite.bitmap.draw_line(*last_sequence[j, 2], LINES_COLOR) end sequences.last.push(Math.definite_proportion(*last_sequence[j, 2], t)) end end sequences.last.first end # 用线段将控制点联结 def self.connect_control_points_with_lines @lines_sprite.bitmap.clear (@control_points.size - 1).times do |i| @lines_sprite.bitmap.draw_line(*@control_points[i, 2], LINES_COLOR) end nil end # 切换线段可见 def self.toggle_lines_visible @lines_visible = !@lines_visible @lines_sprite.visible = @lines_visible end # 切换控制点可见 def self.toggle_points_visible @control_points_visible = !@control_points_visible @control_point_sprites.each do |sprite| sprite.visible = @control_points_visible end end # 切换快速模式 def self.toggle_fast_mode @fast_mode = !@fast_mode end # 基础更新,必须每帧调用 def self.update_basic Graphics.update Input.update end # 更新,需每帧调用 def self.update update_basic if Input.trigger?(CREATE_CONTROL_POINT_KEY) create_control_point(Mouse.position) elsif Input.trigger?(DELETE_CONTROL_POINT_KEY) delete_control_point elsif Input.trigger?(FAST_MODE_KEY) toggle_fast_mode elsif Input.trigger?(LINES_VISIBLE_KEY) toggle_lines_visible elsif Input.trigger?(CONTROL_POINTS_VISIBLE_KEY) toggle_points_visible end end # 简单的irb def self.simple_irb_start Thread.start do ans = nil loop { puts("=> #{(ans = eval(gets)).inspect}") rescue puts($!.message) } end end # 主进程 def self.main simple_irb_start loop { update } end end # 入口 Scene.main
# 二维向量
class Vector
# 可以进行枚举,参见#each
include(Enumerable)
attr_accessor :x # x方向上的分量
attr_accessor :y # y方向上的分量
# 初始化
# 无参数时:(0, 0)
# 有一个参数r时:(r, r)
# 有两个参数x, y时:(x, y)
def initialize(*args)
case args.size
when 0
@x = 0
@y = 0
when 1
@x = args[0]
@y = args[0]
when 2
@x = args[0]
@y = args[1]
end
end
def +@
self
end
# 反向量
def -@
Vector.new(-@x, -@y)
end
# 加法
def +(other)
Vector.new(@x + other.x, @y + other.y)
end
# 减法
def -(other)
self + -other
end
# 点乘或数乘运算
def *(other)
case other
when Numeric
Vector.new(@x * other, @y * other)
when Vector
@x * other.x + @y * other.y
end
end
# 点积
def dot(other)
@x * other.x + @y * other.y
end
# 叉积
def cross(other)
self.x * other.y - other.x * self.y
end
# 斜率
def slope
x =! 0 ? y / x : Float::Infinity
end
# 模的平方
def square_length
self * self
end
# 模
def length
Math.sqrt(square_length)
end
# 超出范围
def is_out_of?(other)
@x.abs > other.x.abs || @y.abs > other.y.abs
end
# 对x和y方向的分量分别取绝对值
def abs
Vector.new(@x.abs, @y.abs)
end
# 切向
def normal
Vector.new(-@y, @x)
end
# 枚举,先对x计算,后对y计算
def each
yield @x
yield @y
end
def inspect
"(#{@x}, #{@y})"
end
end
module Math
# 获得两点之间由某个比例决定的分点
def self.definite_proportion(point1, point2, proportion)
point1 * (1 - proportion) + point2 * proportion
end
end
class Rect
# 初始化
# 除了原先的参数列表,支持由两个向量来决定其位置大小
# 如果用向量表示,第一个向量是position,第二个向量是size
alias ulysses201811181949_initialize initialize
def initialize(*args)
args = case args.size
when 0, 4
args
when 2
[args[0].x, args[0].y, args[1].x, args[1].y]
end
ulysses201811181949_initialize(*args)
end
def position
Vector.new(x, y)
end
def position=(position)
self.x = position.x
self.y = position.y
end
def size
Vector.new(width, height)
end
def size=(size)
self.width = size.x
self.height = size.y
end
end
module Kernel
# 令变量从from到to按照step进行线性变化并传递到块中
def do_by_step(from, to, step)
if block_given?
i = from
until i >= to
yield i
i += step
end
else
to_enum(__method__, from, to, step)
end
end
def gets(*args)
$stdin.gets(*args)
end
def Vector(*args)
Vector.new(*args)
end
end
class Bitmap
# 初始化
# 支持向量表示
alias ulysses201811181708_initialize initialize
def initialize(*args)
args = if args.size == 2 || args[0].is_a?(String)
args
elsif args.size == 1
[args[0].x, args[0].y]
else
[0, 0]
end
ulysses201811181708_initialize(*args)
end
# 填充一个圆
def fill_circle(center, radius, color)
i_max = (radius * 707) / 1000 + 1
square_distance_max = radius * radius + radius / 2
x = radius
fill_rect(center.x - radius, center.y, 2 * radius, 1, color)
for i in 1..i_max
if Vector.new(center.x, i).square_length > square_distance_max
if x > i_max
fill_rect(center.x - i + 1, center.y + x, 2 * (i - 1), 1, color)
fill_rect(center.x - i + 1, center.y - x, 2 * (i - 1), 1, color)
end
x -= 1
end
fill_rect(center.x - x, center.y + i, 2 * x, 1, color)
fill_rect(center.x - x, center.y - i, 2 * x, 1, color)
end
end
# 用一条1像素宽的线段联结两点
def draw_line(point1, point2, color)
difference = point2 - point1
abs_difference = difference.abs
total_step = abs_difference.max
delta = difference * (1.0 / total_step)
vector = point1
(total_step.to_i + 1).times do
set_pixel(vector, color)
vector += delta
end
end
alias ulysses201811182036_set_pixel set_pixel
def set_pixel(*args)
args = case args.size
when 3
args
when 2
[args[0].x, args[0].y, args[1]]
end
ulysses201811182036_set_pixel(*args)
end
def text_vector(text)
text_size(text).size
end
def size
Vector.new(width, height)
end
def size=(size)
self.rect.size = size
end
alias ulysses201811181952_draw_text draw_text
def draw_text(*args)
args = case args[0]
when Numeric, Rect
args
when Vector
a = [args[0].x, args[0].y, args[1].x, args[1].y, args[2]]
a.push(args[3]) if args.size >= 4
a
end
ulysses201811181952_draw_text(*args)
end
end
class Sprite
def position=(position)
self.x = position.x
self.y = position.y
end
end
class Viewport
# 让add_fast_layer接受默认参数blend_type = 0
alias ulysses201812021851_add_fast_layer add_fast_layer
def add_fast_layer(z, blend_type = 0)
ulysses201812021851_add_fast_layer(z, blend_type)
end
end
# 重写了Input模块
# 支持全键盘和鼠标按键
# 如果不需要判断鼠标位置的话,可以不update
module Input
REPEAT_TIME = 4 # 当key被按超过此数时,repeat?(key) == true
GetKeyState = Win32API.new("user32", "GetAsyncKeyState", ['I'], 'I')
@key_states = {}
@key_repeat_times = {}
def self.press?(key)
key < 0x08 ? Mouse.press?(key) : GetKeyState.call(key) != 0
end
def self.repeat?(key)
if press?(key)
unless @key_repeat_times[key]
@key_repeat_times[key] = 0
return true
end
@key_repeat_times[key] += 1
else
@key_repeat_times[key] = nil
@key_states[key] = 0
end
if !@key_repeat_times[key].nil? && @key_repeat_times[key] > REPEAT_TIME
@key_repeat_times[key] = 0
return true
end
false
end
def self.trigger?(key)
if press?(key)
return false if @key_states[key] == 1
@key_states[key] = 1
true
else
@key_states[key] = 0
false
end
end
def self.update
Mouse.update
end
end
class << Graphics
def size
Vector.new(width, height)
end
alias ulysses201811251830_resize_screen resize_screen
def resize_screen(*args)
case args.size
when 2
ulysses201811251830_resize_screen(*args)
when 1
ulysses201811251830_resize_screen(args[0].x, args[0].y)
end
end
end
Graphics.resize_screen(1024, 768) # 更改分辨率
module Mouse
def self.position
Vector.new(x, y)
end
end
# 封装为模块的场景
module Scene
# 绘制控制点的参数
POINTS_RADIUS = 3
POINTS_COLOR = Color.new(255, 255, 255, 255)
POINTS_Z = 10
# 绘制曲线的参数
CURVE_COLOR = Color.new(255, 255, 255, 255)
CURVE_Z = 0
# 绘制线段的参数
LINES_COLOR = Color.new(255, 255, 255, 127)
LINES_Z = -10
# 用于调整绘制的范围和精度
T_BEGINNING = 0.0
T_END = 1.0
T_STEP = 0.01
UPDATE_STEPS = 5
TEMP_BITMAP = Bitmap.new(32, 32) # 临时位图,用于获取文字大小
# 按键
CREATE_CONTROL_POINT_KEY = 0x01 # left
DELETE_CONTROL_POINT_KEY = 0x02 # right
FAST_MODE_KEY = 0x41 # A
LINES_VISIBLE_KEY = 0x42 # B
CONTROL_POINTS_VISIBLE_KEY = 0x43 # C
# 显示端口
@viewport = Viewport.new
@viewport.add_fast_layer(POINTS_Z)
# 曲线
@curve_sprite = Sprite.new(@viewport)
@curve_sprite.bitmap = Bitmap.new(Graphics.size)
@curve_sprite.z = CURVE_Z
# 线段
@lines_sprite = Sprite.new(@viewport)
@lines_sprite.bitmap = Bitmap.new(Graphics.size)
@lines_sprite.z = LINES_Z
# 控制点
@control_points = []
@control_point_sprites = []
# 模式控制
@lines_visible = true
@control_points_visible = true
@fast_mode = false
# 阻断刷新控制
@covering_drawing = 0
# 创建控制点
def self.create_control_point(point)
sprite = Sprite.new(@viewport)
sprite.visible = @control_points_visible
radius_vector = Vector.new(POINTS_RADIUS)
text_vector = TEMP_BITMAP.text_vector(@control_points.size)
radius_text_vector = radius_vector + text_vector
sprite.bitmap = Bitmap.new(radius_text_vector + radius_vector)
sprite.bitmap.font.color = POINTS_COLOR
x_flip = (point + radius_text_vector).x > Graphics.width
y_flip = (point + radius_text_vector).y > Graphics.height
excursion = Vector.new
excursion.x = x_flip ? radius_text_vector.x : radius_vector.x
excursion.y = y_flip ? radius_text_vector.y : radius_vector.y
sprite.position = point - excursion
sprite.bitmap.fill_circle(excursion, POINTS_RADIUS, POINTS_COLOR)
text_position = Vector.new
text_position.x = x_flip ? 0 : radius_vector.x * 2
text_position.y = y_flip ? 0 : radius_vector.y * 2
sprite.bitmap.draw_text(text_position, text_vector, @control_points.size)
sprite.z = POINTS_Z
@control_point_sprites.push(sprite)
@control_points.push(point)
refresh_graph
point
end
# 删除上一个控制点
def self.delete_control_point
return if @control_points.empty?
point = @control_points.pop
sprite = @control_point_sprites.pop
sprite.bitmap.dispose
sprite.dispose
refresh_graph
point
end
# 刷新图形
# 该方法在非快速模式下会阻塞,但update不会停且可以被打断
def self.refresh_graph
@covering_drawing += 1
@curve_sprite.bitmap.clear
return if @control_points.empty?
graph_points = []
steps_count = 0
do_by_step(T_BEGINNING, T_END, T_STEP) do |t|
will_update = !@fast_mode && (steps_count += 1) >= UPDATE_STEPS
graph_points.push(next_graph_point(t, will_update))
if graph_points.size > 1
@curve_sprite.bitmap.draw_line(*graph_points.last(2), CURVE_COLOR)
end
if will_update
steps_count = 0
last_covering_drawing = @covering_drawing
update
return if @covering_drawing > last_covering_drawing
end
end
connect_control_points_with_lines
end
# 根据t返回曲线上的点坐标
# 若@lines_visible && will_update则会绘制线段
def self.next_graph_point(t, will_update = false)
is_drawing = @lines_visible && will_update
@lines_sprite.bitmap.clear if is_drawing
sequences = [@control_points]
(@control_points.size - 1).times do |i|
last_sequence = sequences.last
sequences.push([])
(last_sequence.size - 1).times do |j|
if is_drawing
@lines_sprite.bitmap.draw_line(*last_sequence[j, 2], LINES_COLOR)
end
sequences.last.push(Math.definite_proportion(*last_sequence[j, 2], t))
end
end
sequences.last.first
end
# 用线段将控制点联结
def self.connect_control_points_with_lines
@lines_sprite.bitmap.clear
(@control_points.size - 1).times do |i|
@lines_sprite.bitmap.draw_line(*@control_points[i, 2], LINES_COLOR)
end
nil
end
# 切换线段可见
def self.toggle_lines_visible
@lines_visible = !@lines_visible
@lines_sprite.visible = @lines_visible
end
# 切换控制点可见
def self.toggle_points_visible
@control_points_visible = !@control_points_visible
@control_point_sprites.each do |sprite|
sprite.visible = @control_points_visible
end
end
# 切换快速模式
def self.toggle_fast_mode
@fast_mode = !@fast_mode
end
# 基础更新,必须每帧调用
def self.update_basic
Graphics.update
Input.update
end
# 更新,需每帧调用
def self.update
update_basic
if Input.trigger?(CREATE_CONTROL_POINT_KEY)
create_control_point(Mouse.position)
elsif Input.trigger?(DELETE_CONTROL_POINT_KEY)
delete_control_point
elsif Input.trigger?(FAST_MODE_KEY)
toggle_fast_mode
elsif Input.trigger?(LINES_VISIBLE_KEY)
toggle_lines_visible
elsif Input.trigger?(CONTROL_POINTS_VISIBLE_KEY)
toggle_points_visible
end
end
# 简单的irb
def self.simple_irb_start
Thread.start do
ans = nil
loop { puts("=> #{(ans = eval(gets)).inspect}") rescue puts($!.message) }
end
end
# 主进程
def self.main
simple_irb_start
loop { update }
end
end
# 入口
Scene.main
更新了很多地方,懒得说了,依赖这玩意https://rpg.blue/thread-400462-1-1.html |
评分
-
查看全部评分
|