Merge a49ca19a42
into f931f69d6e
This commit is contained in:
commit
b9bd6112e7
58
geometry.go
58
geometry.go
|
@ -187,6 +187,11 @@ func (u Vec) Project(v Vec) Vec {
|
|||
return v.Unit().Scaled(len)
|
||||
}
|
||||
|
||||
// AngleTo returns angle between u and v.
|
||||
func (u Vec) AngleTo(v Vec) float64 {
|
||||
return math.Cos(u.Unit().Dot(v.Unit()))
|
||||
}
|
||||
|
||||
// Map applies the function f to both x and y components of the vector u and returns the modified
|
||||
// vector.
|
||||
//
|
||||
|
@ -1107,3 +1112,56 @@ func (m Matrix) Unproject(u Vec) Vec {
|
|||
(-m[1]*(u.X-m[4]) + m[0]*(u.Y-m[5])) / det,
|
||||
}
|
||||
}
|
||||
|
||||
// Bezier is cubic Bézier curve used for interpolation. For more info
|
||||
// see https://en.wikipedia.org/wiki/B%C3%A9zier_curve,
|
||||
// In case you are looking for visualization see https://www.desmos.com/calculator/d1ofwre0fr
|
||||
type Bezier struct {
|
||||
Start, StartHandle, EndHandle, End Vec
|
||||
redundant bool
|
||||
}
|
||||
|
||||
// ZB is Zero Bezier Curve that skips calculation and always returns V(1, 0)
|
||||
// Its mainly because Calculation uses lot of function calls and in case of
|
||||
// particles, it can make some difference
|
||||
var ZB = Constant(V(1, 0))
|
||||
|
||||
// B returns new curve. if curve is just placeholder use constant. Handles are
|
||||
// relative to start and end point so:
|
||||
//
|
||||
// pixel.B(ZV, ZV, ZV, V(1, 0)) == Bezier{ZV, ZV, V(1, 0), V(1, 0)}
|
||||
func B(start, startHandle, endHandle, end Vec) Bezier {
|
||||
return Bezier{start, startHandle.Add(start), endHandle.Add(end), end, false}
|
||||
}
|
||||
|
||||
// Linear returns linear Bezier curve
|
||||
func Linear(start, end Vec) Bezier {
|
||||
return B(start, ZV, ZV, end)
|
||||
}
|
||||
|
||||
// Constant returns Bezier curve that always return same point,
|
||||
// This is usefull as placeholder, because it skips calculation
|
||||
func Constant(constant Vec) Bezier {
|
||||
return Bezier{
|
||||
Start: constant,
|
||||
redundant: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Point returns point along the curve determinate by t (0 - 1)
|
||||
// You can of course pass any value though its really hard to
|
||||
// predict what value will it return
|
||||
func (b Bezier) Point(t float64) Vec {
|
||||
if b.redundant || b.Start == b.End {
|
||||
b.redundant = true
|
||||
return b.Start
|
||||
}
|
||||
|
||||
inv := 1.0 - t
|
||||
c, d, e, f := inv*inv*inv, inv*inv*t*3.0, inv*t*t*3.0, t*t*t
|
||||
|
||||
return V(
|
||||
b.Start.X*c+b.StartHandle.X*d+b.EndHandle.X*e+b.End.X*f,
|
||||
b.Start.Y*c+b.StartHandle.Y*d+b.EndHandle.Y*e+b.End.Y*f,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1599,3 +1599,54 @@ func BenchmarkRect_IsIntersect(b *testing.B) {
|
|||
// do a thing
|
||||
}
|
||||
}
|
||||
|
||||
type sub struct {
|
||||
result pixel.Vec
|
||||
t float64
|
||||
}
|
||||
|
||||
func TestBezier(t *testing.T) {
|
||||
tests := []struct {
|
||||
curve pixel.Bezier
|
||||
|
||||
subTest []sub
|
||||
name string
|
||||
}{
|
||||
{
|
||||
pixel.Constant(pixel.V(1, 0)),
|
||||
[]sub{
|
||||
{pixel.V(1, 0), 0.0},
|
||||
{pixel.V(1, 0), 100.0},
|
||||
},
|
||||
"constant",
|
||||
},
|
||||
{
|
||||
pixel.Linear(pixel.V(1, 0), pixel.ZV),
|
||||
[]sub{
|
||||
{pixel.V(1, 0), 0.0},
|
||||
{pixel.ZV, 1.0},
|
||||
},
|
||||
"lenear",
|
||||
},
|
||||
{
|
||||
pixel.B(pixel.V(0, 1), pixel.V(1, 0), pixel.V(-1, 0), pixel.V(1, 0)),
|
||||
[]sub{
|
||||
{pixel.V(0, 1), 0.0},
|
||||
{pixel.V(1, 0), 1.0},
|
||||
{pixel.V(.5, .5), 0.5},
|
||||
},
|
||||
"curved",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range tests {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
for _, st := range c.subTest {
|
||||
val := c.curve.Point(st.t)
|
||||
if val != st.result {
|
||||
t.Errorf("inputted: %v expected: %v got: %v", st.t, st.result, val)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.12
|
|||
require (
|
||||
github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380
|
||||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3
|
||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 // indirect
|
||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72
|
||||
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
|
|
|
@ -45,7 +45,7 @@ func (w *Window) SetMousePosition(v pixel.Vec) {
|
|||
mainthread.Call(func() {
|
||||
if (v.X >= 0 && v.X <= w.bounds.W()) &&
|
||||
(v.Y >= 0 && v.Y <= w.bounds.H()) {
|
||||
w.window.SetCursorPos(
|
||||
w.Window.SetCursorPos(
|
||||
v.X+w.bounds.Min.X,
|
||||
(w.bounds.H()-v.Y)+w.bounds.Min.Y,
|
||||
)
|
||||
|
@ -359,7 +359,7 @@ var buttonNames = map[Button]string{
|
|||
|
||||
func (w *Window) initInput() {
|
||||
mainthread.Call(func() {
|
||||
w.window.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
|
||||
w.Window.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
|
||||
switch action {
|
||||
case glfw.Press:
|
||||
w.tempInp.buttons[Button(button)] = true
|
||||
|
@ -368,7 +368,7 @@ func (w *Window) initInput() {
|
|||
}
|
||||
})
|
||||
|
||||
w.window.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
||||
w.Window.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
||||
if key == glfw.KeyUnknown {
|
||||
return
|
||||
}
|
||||
|
@ -382,23 +382,23 @@ func (w *Window) initInput() {
|
|||
}
|
||||
})
|
||||
|
||||
w.window.SetCursorEnterCallback(func(_ *glfw.Window, entered bool) {
|
||||
w.Window.SetCursorEnterCallback(func(_ *glfw.Window, entered bool) {
|
||||
w.cursorInsideWindow = entered
|
||||
})
|
||||
|
||||
w.window.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
|
||||
w.Window.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
|
||||
w.tempInp.mouse = pixel.V(
|
||||
x+w.bounds.Min.X,
|
||||
(w.bounds.H()-y)+w.bounds.Min.Y,
|
||||
)
|
||||
})
|
||||
|
||||
w.window.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) {
|
||||
w.Window.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) {
|
||||
w.tempInp.scroll.X += xoff
|
||||
w.tempInp.scroll.Y += yoff
|
||||
})
|
||||
|
||||
w.window.SetCharCallback(func(_ *glfw.Window, r rune) {
|
||||
w.Window.SetCharCallback(func(_ *glfw.Window, r rune) {
|
||||
w.tempInp.typed += string(r)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -79,7 +79,7 @@ type WindowConfig struct {
|
|||
|
||||
// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
|
||||
type Window struct {
|
||||
window *glfw.Window
|
||||
*glfw.Window
|
||||
|
||||
bounds pixel.Rect
|
||||
canvas *Canvas
|
||||
|
@ -138,10 +138,10 @@ func NewWindow(cfg WindowConfig) (*Window, error) {
|
|||
|
||||
var share *glfw.Window
|
||||
if currWin != nil {
|
||||
share = currWin.window
|
||||
share = currWin.Window
|
||||
}
|
||||
_, _, width, height := intBounds(cfg.Bounds)
|
||||
w.window, err = glfw.CreateWindow(
|
||||
w.Window, err = glfw.CreateWindow(
|
||||
width,
|
||||
height,
|
||||
cfg.Title,
|
||||
|
@ -153,8 +153,8 @@ func NewWindow(cfg WindowConfig) (*Window, error) {
|
|||
}
|
||||
|
||||
if cfg.Position.X != 0 || cfg.Position.Y != 0 {
|
||||
w.window.SetPos(int(cfg.Position.X), int(cfg.Position.Y))
|
||||
w.window.Show()
|
||||
w.Window.SetPos(int(cfg.Position.X), int(cfg.Position.Y))
|
||||
w.Window.Show()
|
||||
}
|
||||
|
||||
// enter the OpenGL context
|
||||
|
@ -175,7 +175,7 @@ func NewWindow(cfg WindowConfig) (*Window, error) {
|
|||
imgs[i] = pic.Image()
|
||||
}
|
||||
mainthread.Call(func() {
|
||||
w.window.SetIcon(imgs)
|
||||
w.Window.SetIcon(imgs)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ func NewWindow(cfg WindowConfig) (*Window, error) {
|
|||
// Destroy destroys the Window. The Window can't be used any further.
|
||||
func (w *Window) Destroy() {
|
||||
mainthread.Call(func() {
|
||||
w.window.Destroy()
|
||||
w.Window.Destroy()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ func (w *Window) Update() {
|
|||
func (w *Window) SwapBuffers() {
|
||||
mainthread.Call(func() {
|
||||
_, _, oldW, oldH := intBounds(w.bounds)
|
||||
newW, newH := w.window.GetSize()
|
||||
newW, newH := w.Window.GetSize()
|
||||
w.bounds = w.bounds.ResizedMin(w.bounds.Size().Add(pixel.V(
|
||||
float64(newW-oldW),
|
||||
float64(newH-oldH),
|
||||
|
@ -222,7 +222,7 @@ func (w *Window) SwapBuffers() {
|
|||
mainthread.Call(func() {
|
||||
w.begin()
|
||||
|
||||
framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
|
||||
framebufferWidth, framebufferHeight := w.Window.GetFramebufferSize()
|
||||
glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)
|
||||
|
||||
glhf.Clear(0, 0, 0, 0)
|
||||
|
@ -239,7 +239,7 @@ func (w *Window) SwapBuffers() {
|
|||
} else {
|
||||
glfw.SwapInterval(0)
|
||||
}
|
||||
w.window.SwapBuffers()
|
||||
w.Window.SwapBuffers()
|
||||
w.end()
|
||||
})
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ func (w *Window) SwapBuffers() {
|
|||
// Window from within the program.
|
||||
func (w *Window) SetClosed(closed bool) {
|
||||
mainthread.Call(func() {
|
||||
w.window.SetShouldClose(closed)
|
||||
w.Window.SetShouldClose(closed)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -260,7 +260,7 @@ func (w *Window) SetClosed(closed bool) {
|
|||
func (w *Window) Closed() bool {
|
||||
var closed bool
|
||||
mainthread.Call(func() {
|
||||
closed = w.window.ShouldClose()
|
||||
closed = w.Window.ShouldClose()
|
||||
})
|
||||
return closed
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ func (w *Window) Closed() bool {
|
|||
// SetTitle changes the title of the Window.
|
||||
func (w *Window) SetTitle(title string) {
|
||||
mainthread.Call(func() {
|
||||
w.window.SetTitle(title)
|
||||
w.Window.SetTitle(title)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ func (w *Window) SetBounds(bounds pixel.Rect) {
|
|||
w.bounds = bounds
|
||||
mainthread.Call(func() {
|
||||
_, _, width, height := intBounds(bounds)
|
||||
w.window.SetSize(width, height)
|
||||
w.Window.SetSize(width, height)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -290,7 +290,7 @@ func (w *Window) SetBounds(bounds pixel.Rect) {
|
|||
func (w *Window) SetPos(pos pixel.Vec) {
|
||||
mainthread.Call(func() {
|
||||
left, top := int(pos.X), int(pos.Y)
|
||||
w.window.SetPos(left, top)
|
||||
w.Window.SetPos(left, top)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ func (w *Window) SetPos(pos pixel.Vec) {
|
|||
func (w *Window) GetPos() pixel.Vec {
|
||||
var v pixel.Vec
|
||||
mainthread.Call(func() {
|
||||
x, y := w.window.GetPos()
|
||||
x, y := w.Window.GetPos()
|
||||
v = pixel.V(float64(x), float64(y))
|
||||
})
|
||||
return v
|
||||
|
@ -312,12 +312,12 @@ func (w *Window) Bounds() pixel.Rect {
|
|||
|
||||
func (w *Window) setFullscreen(monitor *Monitor) {
|
||||
mainthread.Call(func() {
|
||||
w.restore.xpos, w.restore.ypos = w.window.GetPos()
|
||||
w.restore.width, w.restore.height = w.window.GetSize()
|
||||
w.restore.xpos, w.restore.ypos = w.Window.GetPos()
|
||||
w.restore.width, w.restore.height = w.Window.GetSize()
|
||||
|
||||
mode := monitor.monitor.GetVideoMode()
|
||||
|
||||
w.window.SetMonitor(
|
||||
w.Window.SetMonitor(
|
||||
monitor.monitor,
|
||||
0,
|
||||
0,
|
||||
|
@ -330,7 +330,7 @@ func (w *Window) setFullscreen(monitor *Monitor) {
|
|||
|
||||
func (w *Window) setWindowed() {
|
||||
mainthread.Call(func() {
|
||||
w.window.SetMonitor(
|
||||
w.Window.SetMonitor(
|
||||
nil,
|
||||
w.restore.xpos,
|
||||
w.restore.ypos,
|
||||
|
@ -361,7 +361,7 @@ func (w *Window) SetMonitor(monitor *Monitor) {
|
|||
func (w *Window) Monitor() *Monitor {
|
||||
var monitor *glfw.Monitor
|
||||
mainthread.Call(func() {
|
||||
monitor = w.window.GetMonitor()
|
||||
monitor = w.Window.GetMonitor()
|
||||
})
|
||||
if monitor == nil {
|
||||
return nil
|
||||
|
@ -375,7 +375,7 @@ func (w *Window) Monitor() *Monitor {
|
|||
func (w *Window) Focused() bool {
|
||||
var focused bool
|
||||
mainthread.Call(func() {
|
||||
focused = w.window.GetAttrib(glfw.Focused) == glfw.True
|
||||
focused = w.Window.GetAttrib(glfw.Focused) == glfw.True
|
||||
})
|
||||
return focused
|
||||
}
|
||||
|
@ -395,9 +395,9 @@ func (w *Window) SetCursorVisible(visible bool) {
|
|||
w.cursorVisible = visible
|
||||
mainthread.Call(func() {
|
||||
if visible {
|
||||
w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
|
||||
w.Window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
|
||||
} else {
|
||||
w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
|
||||
w.Window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -405,10 +405,10 @@ func (w *Window) SetCursorVisible(visible bool) {
|
|||
// SetCursorDisabled hides the cursor and provides unlimited virtual cursor movement
|
||||
// make cursor visible using SetCursorVisible
|
||||
func (w *Window) SetCursorDisabled() {
|
||||
w.cursorVisible = false
|
||||
mainthread.Call(func() {
|
||||
w.window.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
|
||||
})
|
||||
w.cursorVisible = false
|
||||
mainthread.Call(func() {
|
||||
w.Window.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
|
||||
})
|
||||
}
|
||||
|
||||
// CursorVisible returns the visibility status of the mouse cursor.
|
||||
|
@ -419,7 +419,7 @@ func (w *Window) CursorVisible() bool {
|
|||
// Note: must be called inside the main thread.
|
||||
func (w *Window) begin() {
|
||||
if currWin != w {
|
||||
w.window.MakeContextCurrent()
|
||||
w.Window.MakeContextCurrent()
|
||||
currWin = w
|
||||
}
|
||||
}
|
||||
|
@ -491,6 +491,6 @@ func (w *Window) Canvas() *Canvas {
|
|||
// already visible or is in full screen mode, this function does nothing.
|
||||
func (w *Window) Show() {
|
||||
mainthread.Call(func() {
|
||||
w.window.Show()
|
||||
w.Window.Show()
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue