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
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
# 对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
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
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
# 删除上一个控制点
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_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