265 lines
6.2 KiB
Go
265 lines
6.2 KiB
Go
package delaunay
|
|
|
|
//Original algo courtesy of github.com/esimov/triangle
|
|
|
|
import "lab.zaar.be/thefish/alchemyst-go/engine/types"
|
|
|
|
var nodeId = 0
|
|
var nodeList = make(map[types.Coords]Node, 0)
|
|
|
|
// Node defines a struct having as components the node X and Y coordinate position.
|
|
type Node struct {
|
|
Id int
|
|
types.Coords
|
|
}
|
|
|
|
// Struct which defines a circle geometry element.
|
|
type circle struct {
|
|
types.Coords
|
|
Radius int
|
|
}
|
|
|
|
// newNode creates a new node.
|
|
func newNode(coords types.Coords) Node {
|
|
if n, ok := nodeList[coords]; ok {
|
|
return n
|
|
}
|
|
neue := Node{Id: nodeId, Coords: coords}
|
|
nodeList[coords] = neue
|
|
nodeId++
|
|
return neue
|
|
}
|
|
|
|
// isEq check if two Nodes are approximately equals.
|
|
func (this Node) isEq(other Node) bool {
|
|
dx := this.X - other.X
|
|
dy := this.Y - other.Y
|
|
|
|
if dx < 0 {
|
|
dx = -dx
|
|
}
|
|
if dy < 0 {
|
|
dy = -dy
|
|
}
|
|
if float64(dx) < 0.0001 && float64(dy) < 0.0001 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Edge struct having as component the node list.
|
|
type Edge struct {
|
|
Nodes []Node
|
|
}
|
|
|
|
// newEdge creates a new Edge.
|
|
func newEdge(this, next Node) []Node {
|
|
nodes := []Node{this, next}
|
|
return nodes
|
|
}
|
|
|
|
// isEq check if two Edge are approximately equals.
|
|
func (e Edge) isEq(edge Edge) bool {
|
|
na := e.Nodes
|
|
nb := edge.Nodes
|
|
na0, na1 := na[0], na[1]
|
|
nb0, nb1 := nb[0], nb[1]
|
|
|
|
if (na0.isEq(nb0) && na1.isEq(nb1)) ||
|
|
(na0.isEq(nb1) && na1.isEq(nb0)) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Triangle struct defines the basic components of a triangle.
|
|
// It's constructed by Nodes, it's Edges and the circumcircle which describes the triangle circumference.
|
|
type Triangle struct {
|
|
Nodes []Node
|
|
Edges []Edge
|
|
circle circle
|
|
}
|
|
|
|
var t = Triangle{}
|
|
|
|
// newTriangle creates a new triangle which circumcircle encloses the point to be added.
|
|
func (t Triangle) newTriangle(p0, p1, p2 Node) Triangle {
|
|
t.Nodes = []Node{p0, p1, p2}
|
|
t.Edges = []Edge{{newEdge(p0, p1)}, {newEdge(p1, p2)}, {newEdge(p2, p0)}}
|
|
|
|
// Create a circumscribed circle of this triangle.
|
|
// The circumcircle of a triangle is the circle which has the three vertices of the triangle lying on its circumference.
|
|
circle := t.circle
|
|
ax, ay := p1.X-p0.X, p1.Y-p0.Y
|
|
bx, by := p2.X-p0.X, p2.Y-p0.Y
|
|
|
|
m := p1.X*p1.X - p0.X*p0.X + p1.Y*p1.Y - p0.Y*p0.Y
|
|
u := p2.X*p2.X - p0.X*p0.X + p2.Y*p2.Y - p0.Y*p0.Y
|
|
s := 1.0 / (2.0 * (float64(ax*by) - float64(ay*bx)))
|
|
|
|
circle.Coords.X = int(float64((p2.Y-p0.Y)*m+(p0.Y-p1.Y)*u) * s)
|
|
circle.Coords.Y = int(float64((p0.X-p2.X)*m+(p1.X-p0.X)*u) * s)
|
|
|
|
// Calculate the distance between the node points and the triangle circumcircle.
|
|
dx := p0.X - circle.Coords.X
|
|
dy := p0.Y - circle.Coords.Y
|
|
|
|
// Calculate the circle radius.
|
|
circle.Radius = dx*dx + dy*dy
|
|
t.circle = circle
|
|
|
|
return t
|
|
}
|
|
|
|
// Delaunay defines the main components for the triangulation.
|
|
type Delaunay struct {
|
|
width int
|
|
height int
|
|
triangles []Triangle
|
|
}
|
|
|
|
// Init initialize the delaunay structure.
|
|
func (d *Delaunay) Init(width, height int) *Delaunay {
|
|
d.width = width
|
|
d.height = height
|
|
|
|
d.triangles = nil
|
|
d.clear()
|
|
|
|
return d
|
|
}
|
|
|
|
var supertriangle1, supertriangle2 Triangle
|
|
|
|
// clear method clears the delaunay triangles slice.
|
|
func (d *Delaunay) clear() {
|
|
p0 := newNode(types.Coords{X: 0, Y: 0})
|
|
p1 := newNode(types.Coords{X: d.width, Y: 0})
|
|
p2 := func() Node {
|
|
var coords types.Coords = types.Coords{X: d.width, Y: d.height}
|
|
if n, ok := nodeList[coords]; ok {
|
|
return n
|
|
}
|
|
neue := Node{Id: nodeId, Coords: coords}
|
|
nodeList[coords] = neue
|
|
nodeId++
|
|
return neue
|
|
}()
|
|
p3 := newNode(types.Coords{X: 0, Y: d.height})
|
|
|
|
// Create the supertriangle, an artificial triangle which encompasses all the points.
|
|
// At the end of the triangulation process any triangles which share Edges with the supertriangle are deleted from the triangle list.
|
|
supertriangle1 = t.newTriangle(p0, p1, p2)
|
|
supertriangle2 = t.newTriangle(p0, p2, p3)
|
|
d.triangles = []Triangle{supertriangle1, supertriangle2}
|
|
}
|
|
|
|
// Insert will insert new triangles into the triangles slice.
|
|
func (d *Delaunay) Insert(points []types.Coords) *Delaunay {
|
|
var (
|
|
i, j, k int
|
|
x, y, dx, dy int
|
|
distSq int
|
|
polygon []Edge
|
|
edges []Edge
|
|
temps []Triangle
|
|
)
|
|
|
|
for k = 0; k < len(points); k++ {
|
|
x = points[k].X
|
|
y = points[k].Y
|
|
|
|
triangles := d.triangles
|
|
edges = nil
|
|
temps = nil
|
|
|
|
for i = 0; i < len(d.triangles); i++ {
|
|
t := triangles[i]
|
|
|
|
//Check whether the points are inside the triangle circumcircle.
|
|
circle := t.circle
|
|
dx = circle.Coords.X - x
|
|
dy = circle.Coords.Y - y
|
|
distSq = dx*dx + dy*dy
|
|
|
|
if distSq < circle.Radius {
|
|
// Save triangle Edges in case they are included.
|
|
edges = append(edges, t.Edges[0], t.Edges[1], t.Edges[2])
|
|
} else {
|
|
// If not included carry over.
|
|
temps = append(temps, t)
|
|
}
|
|
}
|
|
|
|
polygon = nil
|
|
// Check duplication of Edges, delete if duplicates.
|
|
edgesLoop:
|
|
for i = 0; i < len(edges); i++ {
|
|
edge := edges[i]
|
|
for j = 0; j < len(polygon); j++ {
|
|
// Remove identical Edges.
|
|
if edge.isEq(polygon[j]) {
|
|
// Remove polygon from the polygon slice.
|
|
polygon = append(polygon[:j], polygon[j+1:]...)
|
|
continue edgesLoop
|
|
}
|
|
}
|
|
// Insert new Edge into the polygon slice.
|
|
polygon = append(polygon, edge)
|
|
|
|
}
|
|
for i = 0; i < len(polygon); i++ {
|
|
edge := polygon[i]
|
|
temps = append(temps, t.newTriangle(edge.Nodes[0], edge.Nodes[1], newNode(types.Coords{X: x, Y: y})))
|
|
}
|
|
d.triangles = temps
|
|
}
|
|
|
|
//Clean up supertriangles
|
|
z := 0 // output index
|
|
var valid bool
|
|
cleanList := make([]Triangle, 0)
|
|
for _, triangle := range d.triangles {
|
|
valid = true
|
|
for _, checkedNode := range triangle.Nodes {
|
|
for _, superNode := range supertriangle1.Nodes {
|
|
if checkedNode == superNode {
|
|
valid = false
|
|
break
|
|
}
|
|
}
|
|
for _, superNode := range supertriangle2.Nodes {
|
|
if checkedNode == superNode {
|
|
valid = false
|
|
break
|
|
}
|
|
}
|
|
if !valid {
|
|
break
|
|
}
|
|
}
|
|
if !valid {
|
|
continue
|
|
}
|
|
cleanList = append(cleanList, triangle)
|
|
z++
|
|
}
|
|
d.triangles = cleanList
|
|
|
|
//update node indices
|
|
return d
|
|
}
|
|
|
|
// GetTriangles return the generated triangles.
|
|
func (d *Delaunay) GetTriangles() []Triangle {
|
|
return d.triangles
|
|
}
|
|
|
|
func (d *Delaunay) GetEdges() []Edge {
|
|
edges := make([]Edge, 0)
|
|
for _, trs := range d.triangles {
|
|
edges = append(edges, trs.Edges...)
|
|
}
|
|
return edges
|
|
}
|