256 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			6.0 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 := newNode(types.Coords{X: d.width, Y: d.height})
 | |
| 	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
 | |
| }
 |