mirror of
https://github.com/SleepingInsomniac/pixelfaucet
synced 2025-01-15 15:41:08 +01:00
Refactor Bezier, small improvements
This commit is contained in:
parent
2c22abf2ea
commit
94df341310
10 changed files with 157 additions and 132 deletions
|
@ -3,7 +3,13 @@ require "../src/bezier"
|
|||
|
||||
module PF
|
||||
class CubicBezier < PF::Game
|
||||
@curve : BezierCubic(Float64)
|
||||
FONT_COLOR = Pixel.new(0xFFFFFFFF)
|
||||
POINT_COLOR = Pixel.new(0xFF0000FF)
|
||||
CTL_COLOR = Pixel.new(0x505050FF)
|
||||
CURVE_COLOR = Pixel.new(0x0077FFFF)
|
||||
SEL_COLOR = Pixel.new(0xFFFF00FF)
|
||||
|
||||
@curve : Bezier::Cubic(Float64)
|
||||
|
||||
@hover_point : Vector2(Float64)*? = nil
|
||||
@selected_point : Vector2(Float64)*? = nil
|
||||
|
@ -12,29 +18,15 @@ module PF
|
|||
|
||||
def initialize(*args, **kwargs)
|
||||
super
|
||||
@curve = BezierCubic.new(
|
||||
Vector[width * 0.25, height * 0.75],
|
||||
Vector[width * 0.33, height * 0.5],
|
||||
Vector[width * 0.66, height * 0.5],
|
||||
Vector[width * 0.75, height * 0.75]
|
||||
@curve = Bezier::Cubic.new(
|
||||
Vector[width * 0.25, height * 0.7],
|
||||
Vector[width * 0.33, height * 0.3],
|
||||
Vector[width * 0.66, height * 0.3],
|
||||
Vector[width * 0.75, height * 0.7]
|
||||
)
|
||||
|
||||
@controller = PF::Controller(Keys).new({
|
||||
Keys::KEY_1 => "p1",
|
||||
Keys::KEY_2 => "p2",
|
||||
Keys::KEY_3 => "p3",
|
||||
Keys::KEY_4 => "p4",
|
||||
|
||||
Keys::UP => "up",
|
||||
Keys::LEFT => "left",
|
||||
Keys::DOWN => "down",
|
||||
Keys::RIGHT => "right",
|
||||
})
|
||||
end
|
||||
|
||||
def update(dt, event)
|
||||
@controller.map_event(event)
|
||||
|
||||
case event
|
||||
when SDL::Event::MouseButton
|
||||
if event.pressed? && event.button == 1
|
||||
|
@ -59,29 +51,24 @@ module PF
|
|||
def draw
|
||||
clear
|
||||
|
||||
draw_line(@curve.p0, @curve.p1, Pixel.new(100, 100, 100))
|
||||
draw_line(@curve.p3, @curve.p2, Pixel.new(100, 100, 100))
|
||||
draw_line(@curve.p0, @curve.p1, CTL_COLOR)
|
||||
draw_line(@curve.p3, @curve.p2, CTL_COLOR)
|
||||
|
||||
point = @curve.p0
|
||||
0.upto(100) do |x|
|
||||
t = x / 100
|
||||
next_point = @curve.at(t)
|
||||
draw_line(point.to_i, next_point.to_i, Pixel.white)
|
||||
point = next_point
|
||||
end
|
||||
draw_string("Length: " + @curve.length.round(2).to_s, 5, 5, FONT_COLOR)
|
||||
draw_curve(@curve, CURVE_COLOR)
|
||||
|
||||
fill_circle(@curve.p0.to_i, 1, Pixel.blue)
|
||||
fill_circle(@curve.p1.to_i, 1, Pixel.blue)
|
||||
fill_circle(@curve.p2.to_i, 1, Pixel.blue)
|
||||
fill_circle(@curve.p3.to_i, 1, Pixel.blue)
|
||||
fill_circle(@curve.p0.to_i, 2, POINT_COLOR)
|
||||
fill_circle(@curve.p1.to_i, 2, POINT_COLOR)
|
||||
fill_circle(@curve.p2.to_i, 2, POINT_COLOR)
|
||||
fill_circle(@curve.p3.to_i, 2, POINT_COLOR)
|
||||
|
||||
draw_string("P1", @curve.p0.to_i, Pixel.white)
|
||||
draw_string("P2", @curve.p1.to_i, Pixel.white)
|
||||
draw_string("P3", @curve.p2.to_i, Pixel.white)
|
||||
draw_string("P4", @curve.p3.to_i, Pixel.white)
|
||||
draw_string("P1", @curve.p0.to_i, color: FONT_COLOR)
|
||||
draw_string("P2", @curve.p1.to_i, color: FONT_COLOR)
|
||||
draw_string("P3", @curve.p2.to_i, color: FONT_COLOR)
|
||||
draw_string("P4", @curve.p3.to_i, color: FONT_COLOR)
|
||||
|
||||
if point = @hover_point
|
||||
draw_circle(point.value.to_i, 5, Pixel.yellow)
|
||||
draw_circle(point.value.to_i, 5, SEL_COLOR)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ class Wind
|
|||
property position : PF::Vector2(Float64)
|
||||
property strength : PF::Vector2(Float64)
|
||||
|
||||
def initialize(@position, @strength)
|
||||
def initialize(@position, @strength = PF::Vector[rand(-5.0..5.0), rand(-5.0..5.0)])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -33,7 +33,7 @@ class Wind
|
|||
while y < @height
|
||||
x = step / 2
|
||||
while x < @width
|
||||
@gusts << Gust.new(PF::Vector[x, y], PF::Vector[rand(-1.0..1.0), rand(-1.0..1.0)])
|
||||
@gusts << Gust.new(PF::Vector[x, y])
|
||||
x += step
|
||||
end
|
||||
y += step
|
||||
|
@ -48,11 +48,11 @@ class Flake
|
|||
property velocity : PF::Vector2(Float64)
|
||||
|
||||
def initialize(@position, @shape = rand(0_u8..2_u8), @z_pos = rand(0.0..1.0), velocity : PF::Vector2(Float64)? = nil)
|
||||
@velocity = velocity || PF::Vector[rand(-2.0..2.0), rand(0.0..20.0)]
|
||||
@velocity = velocity || PF::Vector[rand(-2.0..2.0), rand(10.0..20.0)]
|
||||
end
|
||||
|
||||
def update(dt)
|
||||
@velocity.y = @velocity.y + 1.0 * dt
|
||||
@velocity.y = @velocity.y + 5.0 * dt
|
||||
@position += @velocity * dt
|
||||
end
|
||||
end
|
||||
|
@ -66,6 +66,9 @@ class Snow < PF::Game
|
|||
super
|
||||
|
||||
@wind = Wind.new(@width, @height)
|
||||
500.times do
|
||||
@flakes << Flake.new(position: PF::Vector[rand(0.0..@width.to_f64), rand(0.0..@height.to_f64)])
|
||||
end
|
||||
clear(0, 0, 15)
|
||||
end
|
||||
|
||||
|
|
|
@ -1 +1,24 @@
|
|||
module PF
|
||||
module Bezier
|
||||
alias Curve = Quad(Float64) | Cubic(Float64)
|
||||
|
||||
module Aproximations
|
||||
# Get the length of the curve by calculating the length of line segments
|
||||
# Increase *steps* for accuracy
|
||||
def length(steps : UInt32 = 10)
|
||||
_length = 0.0
|
||||
seg_p0 = Vector[@p0.x, @p0.y]
|
||||
|
||||
0.upto(steps) do |n|
|
||||
t = n / steps
|
||||
seg_p1 = at(t)
|
||||
_length += seg_p0.distance(seg_p1)
|
||||
seg_p0 = seg_p1
|
||||
end
|
||||
_length
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "./bezier/*"
|
||||
|
|
|
@ -1,48 +1,57 @@
|
|||
require "../bezier"
|
||||
|
||||
module PF
|
||||
struct BezierCubic(T)
|
||||
def self.point(t : Float64, p0 : Number, p1 : Number, p2 : Number, p3 : Number)
|
||||
(1 - t) ** 3 * p0 + 3 * (1 - t) ** 2 * t * p1 + 3 * (1 - t) * t ** 2 * p2 + t ** 3 * p3
|
||||
end
|
||||
module Bezier
|
||||
struct Cubic(T)
|
||||
include Aproximations
|
||||
|
||||
property p0 : Vector2(T)
|
||||
property p1 : Vector2(T)
|
||||
property p2 : Vector2(T)
|
||||
property p3 : Vector2(T)
|
||||
|
||||
def initialize(@p0 : Vector2(T), @p1 : Vector2(T), @p2 : Vector2(T), @p3 : Vector2(T))
|
||||
end
|
||||
|
||||
def [](index : Int)
|
||||
points[index]
|
||||
end
|
||||
|
||||
def points
|
||||
{pointerof(@p0), pointerof(@p1), pointerof(@p2), pointerof(@p3)}
|
||||
end
|
||||
|
||||
# Get the point at percentage *t* of the curve
|
||||
def at(t : Float64)
|
||||
Vector[
|
||||
BezierCubic.point(t, @p0.x, @p1.x, @p2.x, @p3.x),
|
||||
BezierCubic.point(t, @p0.y, @p1.y, @p2.y, @p3.y),
|
||||
]
|
||||
end
|
||||
|
||||
# Get the length of the curve by calculating the length of line segments
|
||||
def length(steps : UInt32 = 10)
|
||||
_length = 0.0
|
||||
seg_p0 = Vector[@p0.x, @p0.y]
|
||||
seg_p1 = uninitialized Vector2(T)
|
||||
|
||||
0.upto(steps) do |n|
|
||||
t = n / steps
|
||||
seg_p1 = at(t)
|
||||
_length += seg_p0.distance(seg_p1)
|
||||
seg_p0 = seg_p1
|
||||
def self.point(t : Float64, p0 : Number, p1 : Number, p2 : Number, p3 : Number)
|
||||
(1 - t) ** 3 * p0 + 3 * (1 - t) ** 2 * t * p1 + 3 * (1 - t) * t ** 2 * p2 + t ** 3 * p3
|
||||
end
|
||||
|
||||
def self.derivative(t : Float64, p0 : Number, p1 : Number, p2 : Number, p3 : Number)
|
||||
3 * (1 - t) ** 2 * (p1 - p0) + 6 * (1 - t) * t * (p2 - p1) + 3 * t ** 2 * (p3 - p2)
|
||||
end
|
||||
|
||||
def self.second_derivative(t : Float64, p0 : Number, p1 : Number, p2 : Number, p3 : Number)
|
||||
6 * (1 - t) * (p2 - 2 * p1 + p0) + 6 * t * (p3 - 2 * p2 + p1)
|
||||
end
|
||||
|
||||
property p0 : Vector2(T)
|
||||
property p1 : Vector2(T)
|
||||
property p2 : Vector2(T)
|
||||
property p3 : Vector2(T)
|
||||
|
||||
def initialize(@p0 : Vector2(T), @p1 : Vector2(T), @p2 : Vector2(T), @p3 : Vector2(T))
|
||||
end
|
||||
|
||||
def points
|
||||
{pointerof(@p0), pointerof(@p1), pointerof(@p2), pointerof(@p3)}
|
||||
end
|
||||
|
||||
# Get the point at percentage *t* < 0 < 1 of the curve
|
||||
def at(t : Float64)
|
||||
Vector[
|
||||
T.new(self.class.point(t, @p0.x, @p1.x, @p2.x, @p3.x)),
|
||||
T.new(self.class.point(t, @p0.y, @p1.y, @p2.y, @p3.y)),
|
||||
]
|
||||
end
|
||||
|
||||
# Get the tangent to a point at *t* < 0 < 1 on the spline
|
||||
def tangent(t : Float64)
|
||||
Vector[
|
||||
T.new(self.class.derivative(t, @p0.x, @p1.x, @p2.x, @p3.x)),
|
||||
T.new(self.class.derivative(t, @p0.y, @p1.y, @p2.y, @p3.y)),
|
||||
].normalized
|
||||
end
|
||||
|
||||
# Get the normal to a point at *t* < 0 < 1 on the spline
|
||||
def normal(t : Float64)
|
||||
Vector[
|
||||
T.new(self.class.derivative(t, @p0.y, @p1.y, @p2.y, @p3.y)),
|
||||
T.new(-self.class.derivative(t, @p0.x, @p1.x, @p2.x, @p3.x)),
|
||||
].normalized
|
||||
end
|
||||
_length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,45 +1,30 @@
|
|||
module PF
|
||||
struct BezierQuad(T)
|
||||
def self.point(t : Float64, p0 : Number, p1 : Number, p2 : Number)
|
||||
(1 - t) ** 2 * p0 + 2 * (1 - t) * t * p1 + t ** 2 * p2
|
||||
end
|
||||
module Bezier
|
||||
struct Quad(T)
|
||||
include Aproximations
|
||||
|
||||
property p0 : Vector2(T)
|
||||
property p1 : Vector2(T)
|
||||
property p2 : Vector2(T)
|
||||
|
||||
def initialize(@p0 : Vector2(T), @p1 : Vector2(T), @p2 : Vector2(T))
|
||||
end
|
||||
|
||||
def [](index : Int)
|
||||
points[index]
|
||||
end
|
||||
|
||||
def points
|
||||
{pointerof(@p0), pointerof(@p1), pointerof(@p2)}
|
||||
end
|
||||
|
||||
# Get the point at percentage *t* of the curve
|
||||
def at(t : Float64)
|
||||
Vector[
|
||||
BezierQuad.point(t, @p0.x, @p1.x, @p2.x),
|
||||
BezierQuad.point(t, @p0.y, @p1.y, @p2.y),
|
||||
]
|
||||
end
|
||||
|
||||
# Get the length of the curve by calculating the length of line segments
|
||||
def length(steps : UInt32 = 10)
|
||||
_length = 0.0
|
||||
seg_p0 = Vector[@p0.x, @p0.y]
|
||||
seg_p1 = uninitialized Vector2(T)
|
||||
|
||||
0.upto(steps) do |n|
|
||||
t = n / steps
|
||||
seg_p1 = at(t)
|
||||
_length += seg_p0.distance(seg_p1)
|
||||
seg_p0 = seg_p1
|
||||
def self.point(t : Float64, p0 : Number, p1 : Number, p2 : Number)
|
||||
(1 - t) ** 2 * p0 + 2 * (1 - t) * t * p1 + t ** 2 * p2
|
||||
end
|
||||
|
||||
property p0 : Vector2(T)
|
||||
property p1 : Vector2(T)
|
||||
property p2 : Vector2(T)
|
||||
|
||||
def initialize(@p0 : Vector2(T), @p1 : Vector2(T), @p2 : Vector2(T))
|
||||
end
|
||||
|
||||
def points
|
||||
{pointerof(@p0), pointerof(@p1), pointerof(@p2)}
|
||||
end
|
||||
|
||||
# Get the point at percentage *t* of the curve
|
||||
def at(t : Float64)
|
||||
Vector[
|
||||
self.class.point(t, @p0.x, @p1.x, @p2.x),
|
||||
self.class.point(t, @p0.y, @p1.y, @p2.y),
|
||||
]
|
||||
end
|
||||
_length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ module PF
|
|||
property running = true
|
||||
property screen : Sprite
|
||||
|
||||
delegate :draw_point, :draw_line, :scan_line, :draw_circle, :draw_triangle, :draw_rect, :draw_shape,
|
||||
delegate :draw_point, :draw_line, :draw_curve, :scan_line, :draw_circle, :draw_triangle, :draw_rect, :draw_shape,
|
||||
:fill_triangle, :fill_rect, :fill_circle, :fill_shape, :draw_string, to: @screen
|
||||
|
||||
@fps_lasttime : Float64 = Time.monotonic.total_milliseconds # the last recorded time.
|
||||
|
|
|
@ -39,10 +39,10 @@ module PF
|
|||
property r : UInt8, g : UInt8, b : UInt8, a : UInt8
|
||||
|
||||
def initialize(rgba : UInt32)
|
||||
@r = ((rgba >> 24) & 0xFF).to_u8
|
||||
@g = ((rgba >> 16) & 0xFF).to_u8
|
||||
@b = ((rgba >> 8) & 0xFF).to_u8
|
||||
@a = (rgba & 0xFF).to_u8
|
||||
@r = (rgba >> 24).to_u8!
|
||||
@g = (rgba >> 16).to_u8!
|
||||
@b = (rgba >> 8).to_u8!
|
||||
@a = (rgba).to_u8!
|
||||
end
|
||||
|
||||
def initialize(@r : UInt8 = 255, @g : UInt8 = 255, @b : UInt8 = 255, @a : UInt8 = 255)
|
||||
|
@ -73,6 +73,7 @@ module PF
|
|||
end
|
||||
|
||||
def to_u32
|
||||
value = uninitialized UInt32
|
||||
value = @r.to_u32 << 24
|
||||
value |= @g.to_u32 << 16
|
||||
value |= @b.to_u32 << 8
|
||||
|
|
|
@ -117,7 +117,8 @@ module PF
|
|||
end
|
||||
|
||||
# Sample a color with alhpa
|
||||
def sample(x : Int, y : Int, alpha = true)
|
||||
def sample(x : Int, y : Int, alpha : Boolean)
|
||||
return sample(x, y) unless alpha
|
||||
raw_pixel = pixel_pointer(x, y).value
|
||||
|
||||
r = uninitialized UInt8
|
||||
|
@ -130,13 +131,13 @@ module PF
|
|||
end
|
||||
|
||||
# ditto
|
||||
def sample(point : Vector2(Int), alpha = true)
|
||||
sample(point.x, point.y, true)
|
||||
def sample(point : Vector2(Int), alpha : Boolean)
|
||||
sample(point.x, point.y, alpha)
|
||||
end
|
||||
|
||||
# Get the pointer to a pixel
|
||||
def pixel_pointer(x : Int32, y : Int32)
|
||||
target = @surface.pixels + (y * @surface.pitch) + (x * 4)
|
||||
target = @surface.pixels + (y * @surface.pitch) + (x * sizeof(UInt32))
|
||||
target.as(Pointer(UInt32))
|
||||
end
|
||||
end
|
||||
|
|
17
src/sprite/draw_curve.cr
Normal file
17
src/sprite/draw_curve.cr
Normal file
|
@ -0,0 +1,17 @@
|
|||
module PF
|
||||
class Sprite
|
||||
def draw_curve(curve : Bezier::Cubic, samples : Int = 100, pixel : Pixel = Pixel.new)
|
||||
point = curve.p0
|
||||
0.upto(samples) do |x|
|
||||
t = x / samples
|
||||
next_point = curve.at(t)
|
||||
draw_line(point.to_i, next_point.to_i, pixel)
|
||||
point = next_point
|
||||
end
|
||||
end
|
||||
|
||||
def draw_curve(curve : Bezier::Cubic, pixel : Pixel)
|
||||
draw_curve(curve, pixel: pixel)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -94,10 +94,9 @@ module PF
|
|||
CHAR_WIDTH = 7
|
||||
CHAR_HEIGHT = 8
|
||||
|
||||
def draw_string(msg : String, x : Int, y : Int, color : Pixel = Pixel.black, bg : Pixel? = nil)
|
||||
def draw_string(msg : String, x : Int, y : Int, color : Pixel = Pixel.new, bg : Pixel? = nil, leading : Int = 2)
|
||||
cur_y = 0
|
||||
cur_x = 0
|
||||
leading = 2
|
||||
|
||||
msg.chars.each do |c|
|
||||
if c == '\n'
|
||||
|
|
Loading…
Reference in a new issue