diff --git a/cmd/test/trianlge.go b/cmd/test/trianlge.go index 053b04c..f6f57f6 100644 --- a/cmd/test/trianlge.go +++ b/cmd/test/trianlge.go @@ -33,7 +33,7 @@ func NewTri() *Tri { func (t *Tri) Update(c *Context) { dt := c.Dt().Seconds() - if t.ContainsPoint(c.AbsCursorPosition()) { + if !t.ContainedPoints(gg.Points{c.AbsCursorPosition()}).Empty() { t.Color = gg.Rgba(0, 1, 0, 1) } else { t.Color = gg.Rgba(1, 0, 1, 1) diff --git a/collider.go b/collider.go index d2f33b2..2f16c79 100644 --- a/collider.go +++ b/collider.go @@ -6,19 +6,25 @@ package gg // if the the bigger collider that // contains more complicated (accurate) structure // do collide. -type ColliderSimplifier interface { - ColliderSimplify() Triangle -} +//type ColliderSimplifier interface { + //ColliderSimplify() Triangle +//} -// The structure represents all -// information on collisions. +type CollisionType int +const ( + CollisionTypePhys CollisionType = iota + CollisionTypeTrigger +) + +// The structure contains collision information. type Collision struct { - Current, With any + Type CollisionType + What, With Objecter + // Points of Self contained in + Points Points + Crosses []LineCross } -type PointContainer interface { - ContainsPoint(Point) bool -} // Implementing the interface lets the engine // to determine if the object collides with anything. @@ -26,8 +32,60 @@ type PointContainer interface { // inner structure field as first argument. // The Collide method will be called on collisions. type Collider interface { - Collides(Collider) *Collision - Collide(*Collision) + // Checks whether the object is collidable + IsCollidable() bool + Verticer + Edger + PointContainer +} +type Collidability struct { + Collidable bool +} +func (c Collidability) IsCollidable() bool { + return c.Collidable +} +type PointContainer interface { + ContainedPoints(Points) Points +} +type Verticer interface { + Vertices() Vertices +} +type Edger interface { + Edges() Edges } +// Implementing the type provides way +// to resolve collisions and other problems. +// The Resolve() is called right after the Update() of Updater. +type Resolver interface { + IsResolvable() bool + Resolve(c *Context) + Collider +} +type Resolvability struct { + Resolvable bool +} +func (r Resolvability) IsResolvable() bool { + return r.Resolvable +} +func Collide(c1, c2 Collider) (Collision, bool) { + vertices := c1.Vertices() + es1, es2 := c1.Edges(), c2.Edges() + crosses, doCross := es1.LineCrossWith(es2) + pts := c2.ContainedPoints(vertices) + return Collision{ + Points: pts, + Crosses: crosses, + }, len(pts) != 0 || doCross +} + +func GetCollisions(c Collider, cs []Collider) []Collision { + ret := []Collision{} + for _, ic := range cs { + if !ic.IsCollidable() || ic == c { + continue + } + } + return ret +} diff --git a/context.go b/context.go index 0b4a3fc..63f9c6e 100644 --- a/context.go +++ b/context.go @@ -4,6 +4,7 @@ type contextType int const ( startContext contextType = iota updateContext + resolveContext eventContext drawContext deleteContext @@ -12,6 +13,7 @@ const ( type Context struct { typ contextType Events []any + Collisions []Collision *Engine *Image } diff --git a/edge.go b/edge.go new file mode 100644 index 0000000..fcc58ee --- /dev/null +++ b/edge.go @@ -0,0 +1,78 @@ +package gg + +type Edges []Edge +// Get crosses of edges. +func (what Edges) LineCrossWith(with Edges) ([]LineCross, bool) { + ret := []LineCross{} + for i := range what { + for j := range with { + cross, doCross := LinersCross(what[i], with[j]) + if doCross { + ret = append(ret, cross) + } + } + } + + if len(ret) == 0 { + return nil, false + } + + return ret, true +} + +// The type represents a line segment. The name is for short. +type Edge [2]Vector + +// Returns corresponding to the segment Line. +func (l Edge) Line() Line { + p0 := l[0] + p1 := l[1] + + k := (p0.Y - p1.Y) / (p0.X - p1.X) + c := p0.Y - p0.X*k + + return Line{k, c} +} + +func (l Edge) ContainedPoints(pts Points) (Points) { + ret := Points{} + for i := range pts { + if l.ContainsPoint(pts[i]) { + ret = append(ret, pts[i]) + } + } + return ret +} + +// Check if the edge contains the specified point. +func (l Edge) ContainsPoint(p Point) bool { + line := l.Line() + if !line.ContainsPoint(p) { + return false + } + + xMax := Max(l[0].X, l[1].X) + xMin := Min(l[0].X, l[1].X) + + yMax := Max(l[0].Y, l[1].Y) + yMin := Min(l[0].Y, l[1].Y) + + if !(xMin < p.X && p.X < xMax) || + !(yMin < p.Y && p.Y < yMax) { + return false + } + + return true +} + +// Get square of length of line segment (for performance sometimes). +func (ls Edge) LenSqr() Float { + return Sqr(ls[0].X - ls[1].X) + + Sqr(ls[0].Y - ls[1].Y) +} + +// Get length of the line segment. +func (ls Edge) Len() Float { + return Sqrt(ls.LenSqr()) +} + diff --git a/engine.go b/engine.go index 7b82329..6a97039 100644 --- a/engine.go +++ b/engine.go @@ -232,14 +232,9 @@ func (e *Engine) Runes() []rune { return e.runes } -func (e *engine) Update() error { +func (e *engine) UpdateEvents() []any { + eng := (*Engine)(e) - e.dt = time.Since(e.lastTime) - - e.runes = ebiten.AppendInputChars(e.runes[:0]) - fmt.Println("runes:", e.runes) - // Buffering the context for faster. - e.prevKeys = e.keys e.keys = inpututil. AppendPressedKeys(e.keys[:0]) @@ -261,10 +256,10 @@ func (e *engine) Update() error { } x, y := ebiten.Wheel() - eng.wheel = V(x, y) - if !(eng.wheel.Eq(ZV)) { + e.wheel = V(x, y) + if !(e.wheel.Eq(ZV)) { events = append(events, &WheelChange{ - Offset: eng.wheel, + Offset: e.wheel, }) } @@ -284,22 +279,34 @@ func (e *engine) Update() error { } realPos := eng.cursorPosition() - if !realPos.Eq(eng.cursorPos) { + if !realPos.Eq(e.cursorPos) { absM := eng.Camera.AbsMatrix() - absPrevPos :=eng.cursorPos.Apply(absM) + absPrevPos := e.cursorPos.Apply(absM) absPos := realPos.Apply(absM) events = append(events, &MouseMove{ - Real: realPos.Sub(eng.cursorPos), + Real: realPos.Sub(e.cursorPos), Abs: absPos.Sub(absPrevPos), }) - eng.cursorPos = realPos + e.cursorPos = realPos } + return events +} +func (e *engine) Update() error { + + eng := (*Engine)(e) + e.dt = time.Since(e.lastTime) + + e.runes = ebiten.AppendInputChars(e.runes[:0]) + fmt.Println("runes:", e.runes) + // Buffering the context for faster. // Providing the events to the objects. // Maybe should think of the better way, // but for it is simple enough. + + events := e.UpdateEvents() c := &Context{ Engine: eng, typ: updateContext, @@ -310,11 +317,26 @@ func (e *engine) Update() error { object.Input() <- c } e.wg.Wait() + e.Resolve() + e.lastTime = time.Now() e.frame++ return nil } +func (e *engine) Resolve() { + colliders := []Collider{} + resolvers := []Resolver{} + for _, object := range e.Objects.store { + if object.IsCollidable() { + colliders = append(colliders, object) + } + if object.IsResolvable() { + resolvers = append(resolvers, object) + } + } +} + var ( fullPageIndexes = func() [MaxVertices]uint16 { ret := [MaxVertices]uint16{} diff --git a/line.go b/line.go index 5068fc2..e89664b 100644 --- a/line.go +++ b/line.go @@ -1,88 +1,15 @@ package gg -import ( - "math" - //"fmt" -) - // The type represents mathematical equation of line and line itself. type Line struct { K, C Float } -type Liner interface { - Line() Line -} - -type LinerPointContainer interface { - Liner - PointContainer -} - -// The type represents a line segment. -type LineSegment [2]Point - -// The type represents multiple line segments. -type LineSegments []LineSegment - -type Edge = LineSegment -type Edges = LineSegments - - - -// Check if two LinerPointContainers do cross and return the -// crossing point. -func LinersCross(lp1, lp2 LinerPointContainer) (Point, bool) { - l1 := lp1.Line() - l2 := lp2.Line() - - p, crosses := l1.crossesLine(l2) - if !crosses || - !lp1.ContainsPoint(p) || - !lp2.ContainsPoint(p) { - return Point{}, false - } - - return p, true -} - -// Check whether the liner is parallel to the other liner. -func LinersParallel(first, second Liner) bool { - l1 := first.Line() - l2 := second.Line() - - return l1.K == l2.K -} - -// Returns angle between liners in radians. -// The value fits the -Pi < Value < Pi condition. -func LinersAngle(first, second Liner) Float { - l1 := first.Line() - l2 := second.Line() - - if l1.K == l2.K { - return 0 - } - - return math.Atan(l1.K/l2.K) -} - // Returns the line itself. Made to implement the Liner interface. func (l Line) Line() Line { return l } -// Returns corresponding to the segment line line. -func (l LineSegment) Line() Line { - p0 := l[0] - p1 := l[1] - - k := (p0.Y - p1.Y) / (p0.X - p1.X) - c := p0.Y - p0.X*k - - return Line{k, c} -} - func (l Line) ContainsPoint(p Point) bool { buf := Line{0, p.Y} pc, ok := l.crossesLine(buf) @@ -93,26 +20,6 @@ func (l Line) ContainsPoint(p Point) bool { return pc == p } -func (l LineSegment) ContainsPoint(p Point) bool { - line := l.Line() - if !line.ContainsPoint(p) { - return false - } - - xMax := Max(l[0].X, l[1].X) - xMin := Min(l[0].X, l[1].X) - - yMax := Max(l[0].Y, l[1].Y) - yMin := Min(l[0].Y, l[1].Y) - - if !(xMin < p.X && p.X < xMax) || - !(yMin < p.Y && p.Y < yMax) { - return false - } - - return true -} - func (l1 Line) crossesLine(l2 Line) (Point, bool) { if LinersParallel(l1, l2) { return Point{}, false @@ -124,30 +31,3 @@ func (l1 Line) crossesLine(l2 Line) (Point, bool) { } -// Get square of length of line segment. -func (ls LineSegment) LenSqr() Float { - return Sqr(ls[0].X - ls[1].X) + - Sqr(ls[0].Y - ls[1].Y) -} - -// Get length of the line segment. -func (ls LineSegment) Len() Float { - return math.Sqrt(ls.LenSqr()) -} - -func (what LineSegments) Cross(with LineSegments) ([][2]int, Points) { - indexes := [][2]int{} - points := Points{} - for i := range what { - for j := range with { - p, cross := LinersCross(what[i], with[j]) - if cross { - points = append(points, p) - indexes = append(indexes, [2]int{i, j}) - } - } - } - - return indexes, points -} - diff --git a/liner.go b/liner.go new file mode 100644 index 0000000..965511e --- /dev/null +++ b/liner.go @@ -0,0 +1,56 @@ +package gg + +type Liner interface { + Line() Line +} + +type LinerPointContainer interface { + Liner + PointContainer +} + +// The type represents the cross of 2 Liners. +type LineCross struct { + Pair [2]Line + Point Point +} + +// Check if two LinerPointContainers do cross and return the +// crossing point. +func LinersCross(lp1, lp2 LinerPointContainer) (LineCross, bool) { + l1 := lp1.Line() + l2 := lp2.Line() + + crossPt, doCross := l1.crossesLine(l2) + if !doCross || + len(lp1.ContainedPoints([]Point{crossPt}))==0 || + len(lp2.ContainedPoints([]Point{crossPt}))==0 { + return LineCross{}, false + } + + return LineCross{ + Pair: [2]Line{l1, l2}, + Point: crossPt, + }, true +} + +// Check whether the liner is parallel to the other liner. +func LinersParallel(first, second Liner) bool { + l1 := first.Line() + l2 := second.Line() + + return l1.K == l2.K +} + +// Returns angle between liners in radians. +// The value fits the -Pi < Value < Pi condition. +func LinersAngle(first, second Liner) Float { + l1 := first.Line() + l2 := second.Line() + + if l1.K == l2.K { + return 0 + } + + return Atan(l1.K/l2.K) +} diff --git a/math.go b/math.go index 6b18728..98a67bf 100644 --- a/math.go +++ b/math.go @@ -24,6 +24,10 @@ func Sqr(v Float) Float { return v * v } +func Sqrt(v Float) Float { + return math.Sqrt(v) +} + func Asin(v Float) Float { return math.Asin(v) } diff --git a/object.go b/object.go index 1c54efc..8f0082e 100644 --- a/object.go +++ b/object.go @@ -54,14 +54,19 @@ type Objecter interface { Input() chan *Context Starter Updater + Collider + Resolver Drawer Deleter } // The type for embedding into engine-in types. type Object struct { + Collidability + Resolvability Layer Visibility + input chan *Context } @@ -69,7 +74,15 @@ func (o *Object) GetObject() *Object { return o } func (o *Object) Input() chan *Context { return o.input } func (o *Object) Start(c *Context) {} + func (o *Object) Update(c *Context) {} + +func (o *Object) ContainedPoints(Points) Points {return Points{}} +func (o *Object) Vertices() Vertices {return Vertices{}} +func (o *Object) Edges() Edges {return Edges{}} + +func (o *Object) Resolve(c *Context) {} + func (o *Object) Draw(c *Context) []EVertex { return nil} func (o *Object) Delete(c *Context) {} diff --git a/point.go b/point.go new file mode 100644 index 0000000..b8080d4 --- /dev/null +++ b/point.go @@ -0,0 +1,8 @@ +package gg + +type Point = Vector +type Points []Point + +func (pts Points) Empty() bool { + return len(pts) == 0 +} diff --git a/polygon.go b/polygon.go index bf4b15f..0f31c4a 100644 --- a/polygon.go +++ b/polygon.go @@ -10,17 +10,8 @@ type Polygon struct { Triangles } -func (p Polygon) ContainsPoint(pnt Point) bool { - return p.MakeTriangles().ContainsPoint(pnt) -} - -// Polygon that can be drawn. -type DrawablePolygon struct { - Polygon - - ShaderOptions - Visibility - Colority +func (p Polygon) ContainedPoints(pts Points) (Points) { + return p.MakeTriangles().ContainedPoints(pts) } func (p Polygon) MakeTriangles() Triangles { @@ -37,6 +28,14 @@ func (p Polygon) MakeTriangles() Triangles { return ret } +// Polygon that can be drawn. +type DrawablePolygon struct { + Polygon + ShaderOptions + Visibility + Colority +} + func (p *DrawablePolygon) Draw(c *Context) []EVertex { return (&DrawableTriangles{ Colority: p.Colority, diff --git a/rect.go b/rect.go index ba644a4..4b70ea2 100644 --- a/rect.go +++ b/rect.go @@ -7,28 +7,16 @@ import ( //"image" ) -// The type describes rectangle geometry. +// The type describes rectangle geometry with +// way to move, rotate and scale it. type Rectangle struct { Object Transform -} - -// The type describes rectangle that can be drawn. -type DrawableRectangle struct { - Rectangle - ShaderOptions - - // Solid color of the rectangle. - // Will be ignored if the Shader - // field is not nil. - Colority - - // Should be draw or not. - Visibility + Width, Height Float } // Return points of vertices of the rectangle. -func (r Rectangle) Vertices() Points { +func (r Rectangle) Vertices() Vertices { m := r.Matrix() p1 := V(0, 0).Apply(m) p2 := V(1, 0).Apply(m) @@ -39,11 +27,11 @@ func (r Rectangle) Vertices() Points { func (r Rectangle) Edges() Edges { vs := r.Vertices() - return LineSegments{ - LineSegment{vs[0], vs[1]}, - LineSegment{vs[1], vs[2]}, - LineSegment{vs[2], vs[3]}, - LineSegment{vs[3], vs[0]}, + return Edges{ + Edge{vs[0], vs[1]}, + Edge{vs[1], vs[2]}, + Edge{vs[2], vs[3]}, + Edge{vs[3], vs[0]}, } } @@ -62,8 +50,23 @@ func (r Rectangle) MakeTriangles() Triangles { } // Check whether the rectangle contains the point. -func (r Rectangle) ContainsPoint(p Point) bool { - return r.MakeTriangles().ContainsPoint(p) +func (r Rectangle) ContainedPoints(pts Points) (Points) { + return r.MakeTriangles().ContainedPoints(pts) +} + +// The type describes rectangle that can be drawn. +type DrawableRectangle struct { + Rectangle + ShaderOptions + + // Solid color of the rectangle. + // Will be ignored if the Shader + // field is not nil. + Colority + + // Should be draw or not. + Visibility + Collidability } // Check whether the drawable rectangle should be drawn. diff --git a/size.go b/size.go new file mode 100644 index 0000000..02877ab --- /dev/null +++ b/size.go @@ -0,0 +1,12 @@ +package gg + +// The type describes a simple +// rectangle without transformations. +type Size struct { + // The upper left corner position point. + Position Point + // Absolute width and height. + Width, Height Float +} + +//type (s Size) ContainsPoint diff --git a/sprite.go b/sprite.go index 26733f9..d0d8970 100644 --- a/sprite.go +++ b/sprite.go @@ -11,6 +11,8 @@ type Sprite struct { ShaderOptions Floating bool Visibility + Collidability + Resolvability } var ( diff --git a/triangle.go b/triangle.go index 72d8daf..4106f66 100644 --- a/triangle.go +++ b/triangle.go @@ -15,7 +15,6 @@ type Vertex struct { } type Triangle [3]Vector -type Triangles []Triangle type DrawableTriangles struct { Triangles Colority @@ -53,9 +52,9 @@ func sqr(v Float) Float { // Return squares of lengths of sides of the triangle. func (t Triangle) SideLengthSquares() ([3]Float) { - l1 := LineSegment{t[0], t[1]}.LenSqr() - l2 := LineSegment{t[1], t[2]}.LenSqr() - l3 := LineSegment{t[2], t[0]}.LenSqr() + l1 := Edge{t[0], t[1]}.LenSqr() + l2 := Edge{t[1], t[2]}.LenSqr() + l3 := Edge{t[2], t[0]}.LenSqr() return [3]Float{l1, l2, l3} } @@ -72,19 +71,36 @@ func (t Triangle) ContainsPoint(p Point) bool { return !(neg && pos) } +func (t Triangle) ContainedPoints(pts Points) []Point { + ret := []Point{} + for i := range pts { + if t.ContainsPoint(pts[i]) { + ret = append(ret, pts[i]) + } + } + return ret +} + func (t Triangle) Sgn() Float { return (t[0].X - t[2].X) * (t[1].Y - t[2].Y) - (t[1].X - t[2].X) * (t[0].Y - t[2].Y) } -func (ts Triangles) ContainsPoint(p Point) bool { +func (t Triangle) Vertices() Vertices { + return Vertices{ + t[0], t[1], t[2], + } +} +type Triangles []Triangle + +func (ts Triangles) ContainedPoints(pts Points) (Points) { + ret := []Point{} for _, t := range ts { - if t.ContainsPoint(p) { - return true - } + ps := t.ContainedPoints(pts) + ret = append(ret, ps...) } - return false + return ret } func (r *DrawableTriangles) MakeEVertices(c *Context) []EVertex { diff --git a/vector.go b/vector.go index 3babf6c..8d3175c 100644 --- a/vector.go +++ b/vector.go @@ -15,11 +15,7 @@ type Vector struct { func (v Vector) XY() (Float, Float) { return v.X, v.Y } -type Point = Vector -//type Vertex = Vector - type Vectors []Vector -type Points []Point func V(x, y Float) Vector { return Vector{x, y} @@ -94,11 +90,11 @@ func (v Vector) Norm() Vector { return V(v.X / l, v.Y / l) } -func (pts Points) Contained(c PointContainer) Points { +func (pts Points) ContainedIn(c PointContainer) Points { ret := make([]Point, 0, len(pts)) for _, pt := range pts { - if c.ContainsPoint(pt) { + if !c.ContainedPoints(Points{pt}).Empty() { ret = append(ret, pt) } } diff --git a/vertice.go b/vertice.go new file mode 100644 index 0000000..c6d4cb1 --- /dev/null +++ b/vertice.go @@ -0,0 +1,5 @@ +package gg + +type Vertice = Point +type Vertices = []Vertice +