-
-
Save mirichi/1b9719badaeb003d1e5408d1b2a37e00 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' | |
# ラスタライズ結果を見やすく描画する | |
module Viewer | |
def self.draw(image, triangles, points) | |
s = 400.0 / image.width | |
Window.mag_filter = TEXF_POINT | |
Window.draw_ex(50, 50, image, scale_x:s, scale_y:s, center_x:0, center_y:0) | |
image.width.times do |c| | |
Window.draw_line(c * s + 50, 50, c * s + 50, image.width * s + 50, [100, 100, 100]) | |
Window.draw_line(50, c * s + 50, image.width * s + 50, c * s + 50, [100, 100, 100]) | |
end | |
triangles.each do |ary| | |
x1, y1, x2, y2, x3, y3 = *ary | |
Window.draw_line(x1 * s + 50, y1 * s + 50, x2 * s + 50, y2 * s + 50, C_RED) | |
Window.draw_line(x2 * s + 50, y2 * s + 50, x3 * s + 50, y3 * s + 50, C_RED) | |
Window.draw_line(x3 * s + 50, y3 * s + 50, x1 * s + 50, y1 * s + 50, C_RED) | |
end | |
points.each do |p| | |
Window.draw_box_fill(p.v.x * s - 2 + 50, p.v.y * s - 2 + 50, p.v.x * s + 2 + 50, p.v.y * s + 2 + 50, C_GREEN) | |
end | |
end | |
end | |
class OreVG | |
attr_accessor :image, :triangles, :points | |
class Vector < Array | |
def x;self[0];end | |
def y;self[1];end | |
def x=(v);self[0]=v;end | |
def y=(v);self[1]=v;end | |
def initialize(x, y) | |
self[0] = x | |
self[1] = y | |
end | |
def +(v) | |
Vector.new(self[0] + v.x, self[1] + v.y) | |
end | |
def -(v) | |
Vector.new(self[0] - v.x, self[1] - v.y) | |
end | |
def *(v) | |
Vector.new(self[0] * v, self[1] * v) | |
end | |
def /(v) | |
Vector.new(self[0] / v, self[1] / v) | |
end | |
def normalize | |
dx = self[0] | |
dy = self[1] | |
len = Math.sqrt(dx * dx + dy * dy) | |
if len > 1e-6 | |
ilen = 1.0 / len | |
dx *= ilen | |
dy *= ilen | |
end | |
Vector.new(dx, dy) | |
end | |
def rperp | |
Vector.new(self[1], -self[0]) | |
end | |
end | |
class MoveTo < Vector;end | |
class LineTo < Vector;end | |
class Point < Struct.new(:v, :dv, :dmv);end | |
def initialize(l) | |
@image = Image.new(l, l, C_WHITE) | |
@triangles = [] | |
@commands = [] | |
@width = 1 | |
@points = [] | |
@verts = [] | |
end | |
# EdgeFunction | |
# E(x, y) = (x - X) * dy - (y - Y) * dx | |
# E(x, y) <= 0で中と判定。3つのエッジの内側であれば塗る。 | |
private def rasterize(x1, y1, x2, y2, x3, y3) | |
dx12 = x2 - x1 | |
dy12 = y2 - y1 | |
dx23 = x3 - x2 | |
dy23 = y3 - y2 | |
dx31 = x1 - x3 | |
dy31 = y1 - y3 | |
# 基点。ピクセルの中心で計算する。 | |
e1 = (0.5 - x1) * dy12 - (0.5 - y1) * dx12 | |
e2 = (0.5 - x2) * dy23 - (0.5 - y2) * dx23 | |
e3 = (0.5 - x3) * dy31 - (0.5 - y3) * dx31 | |
@image.height.times do |y| | |
et1 = e1 | |
et2 = e2 | |
et3 = e3 | |
@image.width.times do |x| | |
yield x, y if et1 <= 0 and et2 <= 0 and et3 <= 0 | |
et1 += dy12 | |
et2 += dy23 | |
et3 += dy31 | |
end | |
e1 -= dx12 | |
e2 -= dx23 | |
e3 -= dx31 | |
end | |
end | |
private def triangle(x1, y1, x2, y2, x3, y3) | |
rasterize(x1, y1, x2, y2, x3, y3) do |x, y| | |
@image[x,y] = [100, 100, 255] | |
end | |
@triangles << [x1, y1, x2, y2, x3, y3] | |
end | |
private def render_stroke | |
@verts.each_cons(3).with_index do |ary, i| | |
if i.even? | |
p1, p0, p2 = ary | |
else | |
p0, p1, p2 = ary | |
end | |
triangle(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y) | |
end | |
end | |
# コマンドをコマンド配列に追加 | |
# このタイミングで変形などの処理も行う | |
private def append_commands(cmd) | |
@commands << cmd | |
end | |
def move_to(x, y) | |
append_commands(MoveTo.new(x, y)) | |
self | |
end | |
def line_to(x, y) | |
append_commands(LineTo.new(x, y)) | |
self | |
end | |
def width=(w) | |
@width = w | |
end | |
# コマンド配列から点配列を作成 | |
# ベジェ曲線の展開はこのタイミング | |
private def flatten_paths | |
@commands.each do |cmd| | |
case cmd | |
when MoveTo | |
@points << Point.new(cmd) | |
when LineTo | |
@points << Point.new(cmd) | |
end | |
end | |
# 点の次の線の向きを計算 | |
# ループしない場合は最後の点の情報は使われない | |
([@points.last] + @points).each_cons(2) do |p0, p1| | |
p0.dv = (p1.v - p0.v).normalize | |
end | |
end | |
# 線の接続に必要な情報を計算する | |
private def calculate_joins | |
# 点の両側の線の中間を算出する | |
([@points.last] + @points).each_cons(2) do |p0, p1| | |
p1.dmv = (p0.dv.rperp + p1.dv.rperp) * 0.5 | |
dmr2 = p1.dmv.x * p1.dmv.x + p1.dmv.y * p1.dmv.y | |
if dmr2 > 0.000001 | |
scale = 1.0 / dmr2 | |
if scale > 600.0 | |
scale = 600.0 | |
end | |
p1.dmv *= scale | |
end | |
end | |
end | |
# 始点・終点のぶつ切り | |
private def butt_cap(p0, dv, w, d) | |
pv = p0 - dv * d | |
dlv = dv.rperp * w | |
@verts << pv + dlv - dv | |
@verts << pv - dlv - dv | |
@verts << pv + dlv | |
@verts << pv - dlv | |
end | |
# 頂点配列作成 | |
# capやjoinの処理はここで | |
private def expand_stroke(w) | |
calculate_joins | |
# 始点の処理(BUTT固定) | |
p0 = @points[0] | |
butt_cap(p0.v, p0.dv, w, -0.5) | |
# 中間の頂点生成 | |
@points[1..-2].each do |p0| | |
@verts << p0.v + p0.dmv * w | |
@verts << p0.v - p0.dmv * w | |
end | |
# 終点の処理(BUTT固定) | |
p0 = @points[-2] | |
p1 = @points[-1] | |
butt_cap(p1.v, p0.dv, w, -0.5) | |
end | |
def stroke | |
flatten_paths | |
expand_stroke(@width / 2.0) | |
render_stroke | |
end | |
end | |
vg = OreVG.new(55) | |
vg.move_to(5, 5) | |
vg.line_to(42, 25) | |
vg.line_to(22, 40) | |
vg.width = 10 | |
vg.stroke | |
Window.loop do | |
Viewer.draw(vg.image, vg.triangles, vg.points) | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment