呪筆で使われている、ストローク処理ライブラリを紹介します。自由にご利用ください。
使い方
local pts = {} stroke_add( pts, 0, 0, 0 ) stroke_add( pts, 60, -60, w/2 ) stroke_add( pts, 60, -120, w ) stroke_add( pts, 0, -180, w/2 ) stroke_add( pts, 60, -240, w ) stroke_add( pts, 120, -200, 0 ) stroke_set( pts ) bs_polygon_move( x, y ) local r,g,b = bs_fore() bs_fill( r,g,b, 255 )
このように、"XY座標" と "線の幅" を指定することで、ブラシストロークのような形状を描画できるようになるライブラリです。追加された頂点列 (三点以上指定) を、滑らかに繋いだ形状に変換して(多角形近似して)、多角形スタック (bs_polygonのスタック)に追加します。
ストローク用の変数は、「local pts = {}」という形で初期化します。stroke_add( pts, X座標, Y座標, 線の直径 ) 関数で頂点を追加していき、追加が終わったら、stroke_set( pts ) で決定します。セットされた多角形スタックは、bs_polygon系の命令で操作可能です。
以下、ライブラリです。結構長いですが、stroke_add, stroke_set 命令以外は呼び出す必要はありません。
ライブラリ
--------------------------------------------------------------------------- -- begin stroke library --------------------------------------------------------------------------- _gStrokeNum = 0 function bezier( b1, b2, b3, b4, t ) local ti = (1.0 - t); local p0 = ti * ti * ti * b1 local p1 = 3*t * ti*ti * b2 local p2 = 3*t*t * ti * b3 local p3 = t*t*t * b4 return p0 + p1 + p2 + p3 end function normalize( x, y ) local dist = bs_distance( x, y ) if dist == 0 then return x,y end return x/dist, y/dist end function stroke_pos( pts, idx ) if idx < 1 then local vx1 = pts[1].x - pts[2].x local vy1 = pts[1].y - pts[2].y local vx2 = pts[2].x - pts[3].x local vy2 = pts[2].y - pts[3].y local a1 = bs_atan( vx1, vy1 ) local a2 = bs_atan( vx2, vy2 ) local adif = a1 - a2 local x,y = bs_rotate( vx1, vy1, adif ) return pts[1].x + x, pts[1].y + y end local s = _gStrokeNum if idx > s then local vx1 = pts[s].x - pts[s-1].x local vy1 = pts[s].y - pts[s-1].y local vx2 = pts[s-1].x - pts[s-2].x local vy2 = pts[s-1].y - pts[s-2].y local a1 = bs_atan( vx1, vy1 ) local a2 = bs_atan( vx2, vy2 ) local adif = a1 - a2 local x,y = bs_rotate( vx1, vy1, adif ) return pts[s].x + x, pts[s].y + y end return pts[idx].x, pts[idx].y end function stroke_width( pts, idx ) if idx < 1 then return pts[1].w end local s = _gStrokeNum if idx > s then return pts[s].w end return pts[idx].w end function stroke_interpolate( pts, t ) local idx = math.floor( t ) local f = t - idx local px4,py4 = stroke_pos( pts, idx+2 ) local px3,py3 = stroke_pos( pts, idx+1 ) local px2,py2 = stroke_pos( pts, idx+0 ) local px1,py1 = stroke_pos( pts, idx-1 ) local vx2 = px4 - px2 local vy2 = py4 - py2 local vx1 = px3 - px1 local vy1 = py3 - py1 local len = bs_distance( px3-px2, py3-py2 ) local m = 0.35 vx1,vy1 = normalize( vx1, vy1 ) vx1 = vx1 * len * m vy1 = vy1 * len * m vx2,vy2 = normalize( vx2, vy2 ) vx2 = vx2 * len * m vy2 = vy2 * len * m local cx1 = pts[idx].x + vx1 local cy1 = pts[idx].y + vy1 local cx2 = pts[idx+1].x - vx2 local cy2 = pts[idx+1].y - vy2 local x = bezier( px2, cx1, cx2, px3, f ) local y = bezier( py2, cy1, cy2, py3, f ) local w1 = stroke_width( pts, idx ) local w2 = stroke_width( pts, idx+1 ) local w = w1 + (w2 - w1) * f return x,y,w end function stroke_normal( x_, x, y_, y, w ) local nx = x_ - x local ny = y_ - y nx,ny = normalize( nx, ny ) nx,ny = bs_rotate( nx, ny, 3.14159/2 ) return nx*w/2, ny*w/2 end function stroke_subdiv( pts, idx ) local x1,y1,w1 = stroke_interpolate( pts, idx + 0.0 ) local x2,y2,w2 = stroke_interpolate( pts, idx + 0.25 ) local x3,y3,w3 = stroke_interpolate( pts, idx + 0.50 ) local x4,y4,w4 = stroke_interpolate( pts, idx + 0.75 ) local x5,y5,w5 = stroke_interpolate( pts, idx + 0.999 ) local dist = 0 dist = dist + bs_distance( x2-x1, y2-y1 ) dist = dist + bs_distance( x3-x2, y3-y2 ) dist = dist + bs_distance( x4-x3, y4-y3 ) dist = dist + bs_distance( x5-x4, y5-y4 ) local subdiv = dist / 4 if subdiv < 16 then subdiv = 16 end if subdiv > 128 then subdiv = 128 end return subdiv end function stroke_add( pts, x, y, w ) local idx = _gStrokeNum + 1 pts[idx] = {} pts[idx].x = x pts[idx].y = y pts[idx].w = w _gStrokeNum = _gStrokeNum + 1 end function stroke_set( pts ) local s = _gStrokeNum if s < 3 then return end local x,y,w = stroke_interpolate( pts, 1 ) local x_,y_,w_ = stroke_interpolate( pts, 1 + 0.0001 ) local nx,ny = stroke_normal( x_, x, y_, y, w ) bs_polygon( pts[1].x + nx, pts[1].y + ny ) -- side 1 local i,j for i=1,s-1 do local subdiv = math.floor( stroke_subdiv( pts, i ) ) for j=1,subdiv-1 do x,y,w = stroke_interpolate( pts, i + j/subdiv ) x_,y_,w_ = stroke_interpolate( pts, i + j/subdiv + 1/1000 ) nx,ny = stroke_normal( x_, x, y_, y, w ) bs_polygon( x + nx, y + ny ) end end x,y,w = stroke_interpolate( pts, s-1 + 0.999 ) x_,y_,w_ = stroke_interpolate( pts, s-1 + 0.9991 ) nx,ny = stroke_normal( x_, x, y_, y, w ) bs_polygon( pts[s].x + nx, pts[s].y + ny ) -- side 2 bs_polygon( pts[s].x - nx, pts[s].y - ny ) for i=1,s-1 do local idx = s+1 - i local subdiv = math.floor( stroke_subdiv( pts, idx-1 ) ) for j=1,subdiv-1 do x,y,w = stroke_interpolate( pts, idx - j/subdiv ) x_,y_,w_ = stroke_interpolate( pts, idx - j/subdiv - 1/1000 ) nx,ny = stroke_normal( x_, x, y_, y, w ) bs_polygon( x + nx, y + ny ) end end x,y,w = stroke_interpolate( pts, 1 + 0.001 ) x_,y_,w_ = stroke_interpolate( pts, 1 ) nx,ny = stroke_normal( x_, x, y_, y, w ) bs_polygon( pts[1].x + nx, pts[1].y + ny ) _gStrokeNum = 0 end --------------------------------------------------------------------------- -- end stroke library ---------------------------------------------------------------------------