mirror of
https://github.com/SleepingInsomniac/pixelfaucet
synced 2025-02-02 20:45:54 +01:00
Add bezier paths
This commit is contained in:
parent
2cf7bf3df9
commit
ab9176c00d
4 changed files with 184 additions and 0 deletions
90
examples/cubic_bezier.cr
Normal file
90
examples/cubic_bezier.cr
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
require "../src/game"
|
||||||
|
require "../src/bezier"
|
||||||
|
|
||||||
|
module PF
|
||||||
|
class CubicBezier < PF::Game
|
||||||
|
@curve : BezierCubic(Float64)
|
||||||
|
|
||||||
|
@hover_point : Vector2(Float64)*? = nil
|
||||||
|
@selected_point : Vector2(Float64)*? = nil
|
||||||
|
|
||||||
|
@mouse_pos : Vector2(Int32) = Vector[0, 0]
|
||||||
|
|
||||||
|
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]
|
||||||
|
)
|
||||||
|
|
||||||
|
@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
|
||||||
|
@selected_point = @hover_point
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.released? && event.button == 1
|
||||||
|
@selected_point = nil
|
||||||
|
end
|
||||||
|
when SDL::Event::MouseMotion
|
||||||
|
@mouse_pos = Vector[event.x, event.y] // scale
|
||||||
|
|
||||||
|
unless point = @selected_point
|
||||||
|
@hover_point = @curve.points.find { |p| @mouse_pos.distance(p.value) < 4 }
|
||||||
|
else
|
||||||
|
point.value.x = @mouse_pos.x.to_f
|
||||||
|
point.value.y = @mouse_pos.y.to_f
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if point = @hover_point
|
||||||
|
draw_circle(point.value.to_i, 5, Pixel.yellow)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
engine = PF::CubicBezier.new(500, 500, 2).run!
|
1
src/bezier.cr
Normal file
1
src/bezier.cr
Normal file
|
@ -0,0 +1 @@
|
||||||
|
require "./bezier/*"
|
48
src/bezier/cubic.cr
Normal file
48
src/bezier/cubic.cr
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
_length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
45
src/bezier/quad.cr
Normal file
45
src/bezier/quad.cr
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
_length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue