update
This commit is contained in:
141
scripts/netz.gd
Normal file
141
scripts/netz.gd
Normal file
@@ -0,0 +1,141 @@
|
||||
class_name Netz
|
||||
extends RefCounted
|
||||
## Ein Wegenetz: Knoten und Kanten in der Ebene. Wege wachsen daran, indem
|
||||
## neue Punkte an die nächste Stelle des bestehenden Netzes angebunden werden.
|
||||
## Wiederverwendung ist gratis -- deshalb bündeln sich Wege und gabeln sich,
|
||||
## statt sternförmig zur Mitte zu laufen.
|
||||
|
||||
var knoten: Array[Vector3] = []
|
||||
var kanten: Array = [] # [a, b] Indexpaare
|
||||
|
||||
|
||||
func fuege_knoten(p: Vector3) -> int:
|
||||
var q := Vector3(p.x, 0.0, p.z)
|
||||
for i in range(knoten.size()):
|
||||
if knoten[i].distance_to(q) < 0.6:
|
||||
return i
|
||||
knoten.append(q)
|
||||
return knoten.size() - 1
|
||||
|
||||
|
||||
func verbinde(a: int, b: int) -> void:
|
||||
if a == b:
|
||||
return
|
||||
for k in kanten:
|
||||
if (k[0] == a and k[1] == b) or (k[0] == b and k[1] == a):
|
||||
return
|
||||
kanten.append([a, b])
|
||||
|
||||
|
||||
func nachbarn(i: int) -> Array:
|
||||
var out: Array = []
|
||||
for k in kanten:
|
||||
if k[0] == i:
|
||||
out.append(k[1])
|
||||
elif k[1] == i:
|
||||
out.append(k[0])
|
||||
return out
|
||||
|
||||
|
||||
## Nächster Punkt auf einer Kante -- gibt {knoten, dist} zurück und teilt die
|
||||
## Kante, falls der Fußpunkt zwischen zwei Knoten liegt.
|
||||
func andocken(p: Vector3) -> Dictionary:
|
||||
var q := Vector3(p.x, 0.0, p.z)
|
||||
var beste := INF
|
||||
var best_i := -1
|
||||
var best_k := -1
|
||||
var best_p := Vector3.ZERO
|
||||
for i in range(knoten.size()):
|
||||
var d := knoten[i].distance_to(q)
|
||||
if d < beste:
|
||||
beste = d
|
||||
best_i = i
|
||||
best_k = -1
|
||||
for ki in range(kanten.size()):
|
||||
var a: Vector3 = knoten[kanten[ki][0]]
|
||||
var b: Vector3 = knoten[kanten[ki][1]]
|
||||
var f := Geometry3D.get_closest_point_to_segment(q, a, b)
|
||||
var d := f.distance_to(q)
|
||||
if d < beste - 0.01 and f.distance_to(a) > 1.5 and f.distance_to(b) > 1.5:
|
||||
beste = d
|
||||
best_k = ki
|
||||
best_p = f
|
||||
if best_k < 0:
|
||||
return {"knoten": best_i, "dist": beste, "punkt": knoten[best_i]}
|
||||
# Kante teilen
|
||||
var alt: Array = kanten[best_k]
|
||||
kanten.remove_at(best_k)
|
||||
var neu := fuege_knoten(best_p)
|
||||
verbinde(alt[0], neu)
|
||||
verbinde(neu, alt[1])
|
||||
return {"knoten": neu, "dist": beste, "punkt": knoten[neu]}
|
||||
|
||||
|
||||
## Kürzester Weg als Knotenfolge, leer wenn es keinen gibt.
|
||||
func pfad(start: int, ziel: int) -> Array:
|
||||
if start == ziel:
|
||||
return [start]
|
||||
var offen: Array = [start]
|
||||
var kosten: Dictionary = {start: 0.0}
|
||||
var her: Dictionary = {}
|
||||
while not offen.is_empty():
|
||||
var best := 0
|
||||
for i in range(1, offen.size()):
|
||||
if float(kosten[offen[i]]) < float(kosten[offen[best]]):
|
||||
best = i
|
||||
var k: int = offen[best]
|
||||
offen.remove_at(best)
|
||||
if k == ziel:
|
||||
break
|
||||
for n in nachbarn(k):
|
||||
var neu: float = float(kosten[k]) + knoten[k].distance_to(knoten[n])
|
||||
if not kosten.has(n) or neu < float(kosten[n]) - 0.001:
|
||||
kosten[n] = neu
|
||||
her[n] = k
|
||||
if not offen.has(n):
|
||||
offen.append(n)
|
||||
if not kosten.has(ziel):
|
||||
return []
|
||||
var weg: Array = [ziel]
|
||||
var cur: int = ziel
|
||||
while cur != start:
|
||||
cur = int(her[cur])
|
||||
weg.push_front(cur)
|
||||
return weg
|
||||
|
||||
|
||||
func weglaenge(start: int, ziel: int) -> float:
|
||||
var w := pfad(start, ziel)
|
||||
if w.size() < 2:
|
||||
return 0.0 if w.size() == 1 else INF
|
||||
var l := 0.0
|
||||
for i in range(w.size() - 1):
|
||||
l += knoten[w[i]].distance_to(knoten[w[i + 1]])
|
||||
return l
|
||||
|
||||
|
||||
## Hängen alle Knoten zusammen?
|
||||
func zusammenhaengend() -> bool:
|
||||
if knoten.is_empty():
|
||||
return true
|
||||
var gesehen: Dictionary = {0: true}
|
||||
var stapel: Array = [0]
|
||||
while not stapel.is_empty():
|
||||
var k: int = stapel.pop_back()
|
||||
for n in nachbarn(k):
|
||||
if not gesehen.has(n):
|
||||
gesehen[n] = true
|
||||
stapel.append(n)
|
||||
return gesehen.size() == knoten.size()
|
||||
|
||||
|
||||
## Schneidet die Strecke a-b ein Rechteck (Mittelpunkt c, Größe s, mit Rand)?
|
||||
static func trifft_rechteck(a: Vector3, b: Vector3, c: Vector3, s: Vector2, rand: float) -> bool:
|
||||
var hx := s.x * 0.5 + rand
|
||||
var hz := s.y * 0.5 + rand
|
||||
var schritte := int(maxf(2.0, a.distance_to(b) / 1.5))
|
||||
for i in range(schritte + 1):
|
||||
var p := a.lerp(b, float(i) / float(schritte))
|
||||
if absf(p.x - c.x) <= hx and absf(p.z - c.z) <= hz:
|
||||
return true
|
||||
return false
|
||||
Reference in New Issue
Block a user