delaunay/mst working
This commit is contained in:
256
util/delaunay/delaunay.go
Normal file
256
util/delaunay/delaunay.go
Normal file
@ -0,0 +1,256 @@
|
||||
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{0,0})
|
||||
p1 := newNode(types.Coords{d.width, 0})
|
||||
p2 := newNode(types.Coords{d.width, d.height})
|
||||
p3 := newNode(types.Coords{0, 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, 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 {
|
||||
for _, e := range trs.Edges {
|
||||
edges = append(edges, e)
|
||||
}
|
||||
|
||||
}
|
||||
return edges
|
||||
}
|
61
util/delaunay/mst.go
Normal file
61
util/delaunay/mst.go
Normal file
@ -0,0 +1,61 @@
|
||||
package delaunay
|
||||
|
||||
import (
|
||||
"lab.zaar.be/thefish/alchemyst-go/engine/types"
|
||||
"lab.zaar.be/thefish/alchemyst-go/util/kruskals"
|
||||
)
|
||||
//FIXME ПЕРЕПИСАТЬ К ЕБЕНЯМ ДЕЛОНЕ. Это пиздец и си с крестами головного мозга. Надо сделать по-человечески.
|
||||
|
||||
func GetTriangles(coords []types.Coords, w, h int) []types.Edge {
|
||||
d := &Delaunay{}
|
||||
edges := d.Init(100, 100).Insert(coords).GetEdges()
|
||||
output := make([]types.Edge, 0)
|
||||
for _, e := range edges{
|
||||
output = append(output, types.Edge{e.Nodes[0].Coords, e.Nodes[1].Coords})
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func GetMst(coords []types.Coords, w, h int) []types.Edge {
|
||||
|
||||
d := &Delaunay{}
|
||||
|
||||
edges := d.Init(w, h).Insert(coords).GetEdges()
|
||||
|
||||
//MST
|
||||
//fixme гребаный костыль
|
||||
nodeMap := make(map[int]int, 0)
|
||||
idx := 0
|
||||
nodeList := make(map[int]Node)
|
||||
for _, e := range edges{
|
||||
if _, ok := nodeMap[e.Nodes[0].Id]; !ok{
|
||||
nodeMap[e.Nodes[0].Id] = idx
|
||||
nodeList[idx] = e.Nodes[0]
|
||||
idx++
|
||||
}
|
||||
if _, ok := nodeMap[e.Nodes[1].Id]; !ok{
|
||||
nodeMap[e.Nodes[1].Id] = idx
|
||||
nodeList[idx] = e.Nodes[1]
|
||||
idx++
|
||||
}
|
||||
}
|
||||
//graph := make([]kruskals.WeightedEdge, len(edges))
|
||||
graph := make([]kruskals.WeightedEdge, 0)
|
||||
for _, e := range edges{
|
||||
//graph[i] = kruskals.SimpleWeightedEdge{e.Nodes[0].Id, e.Nodes[1].Id, int(e.Nodes[0].Coords.DistanceTo(e.Nodes[1].Coords))}
|
||||
graph = append(
|
||||
graph,
|
||||
kruskals.SimpleWeightedEdge{
|
||||
nodeMap[e.Nodes[0].Id],
|
||||
nodeMap[e.Nodes[1].Id],
|
||||
int(e.Nodes[0].Coords.DistanceTo(e.Nodes[1].Coords))},
|
||||
)
|
||||
}
|
||||
|
||||
result := kruskals.MinimumSpanningTree(graph)
|
||||
output := make([]types.Edge, 0)
|
||||
for _, we := range result{
|
||||
output = append(output, types.Edge{nodeList[we.From()].Coords, nodeList[we.To()].Coords})
|
||||
}
|
||||
return output
|
||||
}
|
Reference in New Issue
Block a user