-
-
Save mirichi/04391d40b53ce2acf64579ea6ce432f0 to your computer and use it in GitHub Desktop.
ベジェ曲線体験ツール
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'dxruby' | |
# 3次ベジェ曲線をImageオブジェクトに描画する | |
class BezierDrawer | |
attr_reader :image | |
KAPPA90 = 0.5522847493 | |
TESS_TOL = 0.25 / 1.0 | |
def initialize | |
@points = [] | |
@image = Image.new(640,480) | |
end | |
def tesselate_bezier(x1, y1, x2, y2, x3, y3, x4, y4, level) | |
return if level > 10 | |
dx = x4 - x1 | |
dy = y4 - y1 | |
d2 = ((x2 - x4) * dy - (y2 - y4) * dx).abs | |
d3 = ((x3 - x4) * dy - (y3 - y4) * dx).abs | |
if (d2 + d3) * (d2 + d3) < TESS_TOL * (dx * dx + dy * dy) | |
@points << [x4, y4] | |
return | |
end | |
x12 = (x1 + x2) * 0.5 | |
y12 = (y1 + y2) * 0.5 | |
x23 = (x2 + x3) * 0.5 | |
y23 = (y2 + y3) * 0.5 | |
x34 = (x3 + x4) * 0.5 | |
y34 = (y3 + y4) * 0.5 | |
x123 = (x12 + x23) * 0.5 | |
y123 = (y12 + y23) * 0.5 | |
x234 = (x23 + x34) * 0.5 | |
y234 = (y23 + y34) * 0.5 | |
x1234 = (x123 + x234) * 0.5 | |
y1234 = (y123 + y234) * 0.5 | |
tesselate_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1) | |
tesselate_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1) | |
end | |
def draw_bezier(x1, y1, x2, y2, x3, y3, x4, y4) | |
tesselate_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0) | |
@image.line(x1, y1, @points[0][0], @points[0][1], C_WHITE) | |
@points.each_cons(2) do |p1, p2| | |
@image.line(p1[0], p1[1], p2[0], p2[1], C_WHITE) | |
end | |
end | |
end | |
class Slider | |
attr_reader :pos | |
def initialize | |
@pos = 0.5 | |
@btn = Sprite.new(@pos * 629, 430, Image.new(10, 40, C_YELLOW)) | |
@mouse = Sprite.new(0, 0) | |
@mouse.collision = [0, 0] | |
@drag = false | |
@dx = 0 | |
end | |
def update | |
if Input.mouse_push?(M_LBUTTON) | |
@mouse.x, @mouse.y = Input.mouse_x, Input.mouse_y | |
if @mouse === @btn | |
@drag = true | |
@dx = Input.mouse_x - @pos * 629 | |
end | |
elsif !Input.mouse_down?(M_LBUTTON) | |
@drag = false | |
end | |
if @drag | |
@pos = (Input.mouse_x - @dx) / 629 | |
@pos = 0.0 if @pos < 0.0 | |
@pos = 1.0 if @pos > 1.0 | |
@btn.x = @pos * 629 | |
end | |
end | |
def draw | |
Window.draw_line(0, 450, 639, 450, C_YELLOW) | |
@btn.draw | |
end | |
end | |
class Handle < Sprite | |
def initialize(x, y, image) | |
super | |
@mouse = Sprite.new(0, 0) | |
@mouse.collision = [0, 0] | |
@drag = false | |
@dx = 0 | |
@dy = 0 | |
end | |
def update | |
if Input.mouse_push?(M_LBUTTON) | |
@mouse.x, @mouse.y = Input.mouse_x, Input.mouse_y | |
if @mouse === self | |
@drag = true | |
@dx = Input.mouse_x - self.x | |
@dy = Input.mouse_y - self.y | |
end | |
elsif !Input.mouse_down?(M_LBUTTON) | |
@drag = false | |
end | |
if @drag | |
self.x, self.y = Input.mouse_x - @dx, Input.mouse_y - @dy | |
end | |
end | |
end | |
Anchor = Handle | |
x1, y1, x2, y2, x3, y3, x4, y4 = 50, 240, 300, 10, 150, 400, 600, 240 | |
bd = BezierDrawer.new | |
bd.draw_bezier(x1, y1, x2, y2, x3, y3, x4, y4) | |
slider = Slider.new | |
anchor1 = Anchor.new(x1-2, y1-2, Image.new(5, 5, C_BLUE)) | |
handle1 = Handle.new(x2-2, y2-2, Image.new(5, 5, C_GREEN)) | |
handle2 = Handle.new(x3-2, y3-2, Image.new(5, 5, C_GREEN)) | |
anchor2 = Anchor.new(x4-2, y4-2, Image.new(5, 5, C_BLUE)) | |
Window.loop do | |
anchor1.update | |
handle2.update | |
handle1.update | |
anchor2.update | |
x1, y1 = anchor1.x+2, anchor1.y+2 | |
x2, y2 = handle1.x+2, handle1.y+2 | |
x3, y3 = handle2.x+2, handle2.y+2 | |
x4, y4 = anchor2.x+2, anchor2.y+2 | |
# ベジェ曲線ひきなおし | |
bd = BezierDrawer.new | |
bd.draw_bezier(x1, y1, x2, y2, x3, y3, x4, y4) | |
Window.draw(0, 0, bd.image) | |
# 制御線 | |
Window.draw_line(x1, y1, x2, y2, C_YELLOW) | |
Window.draw_line(x2, y2, x3, y3, C_YELLOW) | |
Window.draw_line(x3, y3, x4, y4, C_YELLOW) | |
pos = slider.pos | |
ipos = 1 - pos | |
x12 = x1 * ipos + x2 * pos | |
y12 = y1 * ipos + y2 * pos | |
x23 = x2 * ipos + x3 * pos | |
y23 = y2 * ipos + y3 * pos | |
x34 = x3 * ipos + x4 * pos | |
y34 = y3 * ipos + y4 * pos | |
Window.draw_line(x12, y12, x23, y23, C_CYAN) | |
Window.draw_line(x23, y23, x34, y34, C_CYAN) | |
Window.draw_box_fill(x12-2, y12-2, x12+2, y12+2, C_YELLOW) | |
Window.draw_box_fill(x23-2, y23-2, x23+2, y23+2, C_YELLOW) | |
Window.draw_box_fill(x34-2, y34-2, x34+2, y34+2, C_YELLOW) | |
x123 = x12 * ipos + x23 * pos | |
y123 = y12 * ipos + y23 * pos | |
x234 = x23 * ipos + x34 * pos | |
y234 = y23 * ipos + y34 * pos | |
Window.draw_line(x123, y123, x234, y234, C_MAGENTA) | |
Window.draw_box_fill(x123-2, y123-2, x123+2, y123+2, C_CYAN) | |
Window.draw_box_fill(x234-2, y234-2, x234+2, y234+2, C_CYAN) | |
x1234 = x123 * ipos + x234 * pos | |
y1234 = y123 * ipos + y234 * pos | |
Window.draw_box_fill(x1234-2, y1234-2, x1234+2, y1234+2, C_MAGENTA) | |
# アンカー | |
anchor1.draw | |
anchor2.draw | |
# ハンドル | |
handle1.draw | |
handle2.draw | |
# スライダー | |
slider.update | |
slider.draw | |
break if Input.key_push?(K_ESCAPE) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment