revectored app entrypoint
This commit is contained in:
211
internal/app/app.go
Normal file
211
internal/app/app.go
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"bspviz/internal/bsp"
|
||||||
|
"bspviz/internal/geom"
|
||||||
|
"bspviz/internal/mapfmt"
|
||||||
|
"bspviz/internal/viz"
|
||||||
|
"bspviz/internal/wad"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
WadPath string
|
||||||
|
MapMarker string
|
||||||
|
ListOnly bool
|
||||||
|
Info bool
|
||||||
|
Extract []string
|
||||||
|
OutDir string
|
||||||
|
GeomTest bool
|
||||||
|
BuildBSP bool
|
||||||
|
Alpha float64
|
||||||
|
Beta float64
|
||||||
|
Eps float64
|
||||||
|
LeafMax int
|
||||||
|
MaxDepth int
|
||||||
|
Cands int
|
||||||
|
Seed int64
|
||||||
|
DotOut string
|
||||||
|
TreePNG string
|
||||||
|
Overlay string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(opts Options) error {
|
||||||
|
if strings.TrimSpace(opts.WadPath) == "" {
|
||||||
|
return fmt.Errorf("wad path required")
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := wad.Open(opts.WadPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("open wad: %w", err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
if opts.ListOnly {
|
||||||
|
fmt.Printf("WAD: %s\n", opts.WadPath)
|
||||||
|
for i, d := range w.Dir() {
|
||||||
|
fmt.Printf("%3d: %-8s size=%-7d pos=%-8d\n", i, d.Name(), d.Size, d.FilePos)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
start, end, err := w.FindMap(opts.MapMarker)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("find map: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Map %s: Directory [%d, %d)\n", strings.ToUpper(opts.MapMarker), start, end)
|
||||||
|
|
||||||
|
if opts.Info {
|
||||||
|
lumps, err := w.LoadMapLumps(opts.MapMarker, "VERTEXES", "LINEDEFS")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("load map lumps: %w", err)
|
||||||
|
}
|
||||||
|
vb := lumps["VERTEXES"]
|
||||||
|
lb := lumps["LINEDEFS"]
|
||||||
|
m, err := mapfmt.LoadMap(lumps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
verts := len(vb) / 4
|
||||||
|
lines := len(lb) / 14
|
||||||
|
fmt.Printf("VERTEXES: bytes=%d count=%d\n", len(vb), verts)
|
||||||
|
fmt.Printf("LINEDEFS: bytes=%d count=%d\n", len(lb), lines)
|
||||||
|
|
||||||
|
fmt.Printf("Map has %d vertices and %d linedefs\n", len(m.Vertices), len(m.Linedefs))
|
||||||
|
fmt.Printf("First vertex: %+v\n", m.Vertices[0])
|
||||||
|
fmt.Printf("First linedef: %+v\n", m.Linedefs[0])
|
||||||
|
|
||||||
|
if len(vb)%4 != 0 {
|
||||||
|
fmt.Println("WARN: VERTEXES size ist kein Vielfaches von 4 → Format prüfen")
|
||||||
|
}
|
||||||
|
if len(lb)%14 != 0 {
|
||||||
|
fmt.Println("WARN: LINEDEFS size ist kein Vielfaches von 14 → Format prüfen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Extract) > 0 {
|
||||||
|
lumps, err := w.LoadMapLumps(opts.MapMarker, opts.Extract...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("extract: %w", err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(opts.OutDir, 0o755); err != nil {
|
||||||
|
return fmt.Errorf("mkdir %s: %w", opts.OutDir, err)
|
||||||
|
}
|
||||||
|
for name, data := range lumps {
|
||||||
|
dst := filepath.Join(opts.OutDir, fmt.Sprintf("%s.lmp", name))
|
||||||
|
if err := os.WriteFile(dst, data, 0o644); err != nil {
|
||||||
|
return fmt.Errorf("write %s: %w", dst, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("wrote %s (%d bytes)\n", dst, len(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.GeomTest {
|
||||||
|
raw, err := w.LoadMapLumps(opts.MapMarker, "VERTEXES", "LINEDEFS")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("load map lumps: %w", err)
|
||||||
|
}
|
||||||
|
m, err := mapfmt.LoadMap(raw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse map: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
segs := mapfmt.LinedefsToSegs(m.Vertices, m.Linedefs)
|
||||||
|
fmt.Printf("GEOM: vertices=%d linedefs=%d segs=%d\n", len(m.Vertices), len(m.Linedefs), len(segs))
|
||||||
|
if len(segs) == 0 {
|
||||||
|
fmt.Println("GEOM: keine Segmente gefunden – prüfe LINEDEFS/VERTEXES")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pts := make([]geom.Vec, 0, len(m.Vertices))
|
||||||
|
for _, v := range m.Vertices {
|
||||||
|
pts = append(pts, geom.V(float64(v.X), float64(v.Y)))
|
||||||
|
}
|
||||||
|
bb := geom.Bounds(pts)
|
||||||
|
w := bb.Max.X - bb.Min.X
|
||||||
|
h := bb.Max.Y - bb.Min.Y
|
||||||
|
fmt.Printf("AABB: min=(%.1f,%.1f) max=(%.1f,%.1f) size=(%.1f×%.1f)\n",
|
||||||
|
bb.Min.X, bb.Min.Y, bb.Max.X, bb.Max.Y, w, h)
|
||||||
|
|
||||||
|
O := segs[0].A
|
||||||
|
D := geom.Sub(segs[0].B, segs[0].A)
|
||||||
|
if geom.Len(D) < 1e-9 && len(segs) > 1 {
|
||||||
|
O = segs[1].A
|
||||||
|
D = geom.Sub(segs[1].B, segs[1].A)
|
||||||
|
}
|
||||||
|
var left, right, splits, degens int
|
||||||
|
for _, s := range segs {
|
||||||
|
f, b := geom.SplitSeg(s, O, D)
|
||||||
|
if len(f) == 0 && len(b) == 0 {
|
||||||
|
degens++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
left += len(f)
|
||||||
|
right += len(b)
|
||||||
|
if len(f) > 0 && len(b) > 0 {
|
||||||
|
splits++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("PROBE-SPLIT: O=(%.1f,%.1f) D=(%.1f,%.1f)\n", O.X, O.Y, D.X, D.Y)
|
||||||
|
fmt.Printf("PROBE-SPLIT: left=%d right=%d splits=%d degens=%d (EPS=%.1e)\n",
|
||||||
|
left, right, splits, degens, geom.EPS)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.BuildBSP {
|
||||||
|
raw, err := w.LoadMapLumps(opts.MapMarker, "VERTEXES", "LINEDEFS")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("load map lumps: %w", err)
|
||||||
|
}
|
||||||
|
m, err := mapfmt.LoadMap(raw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse map: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
segs := mapfmt.LinedefsToSegs(m.Vertices, m.Linedefs)
|
||||||
|
p := bsp.Params{
|
||||||
|
Alpha: opts.Alpha, Beta: opts.Beta, Eps: opts.Eps,
|
||||||
|
MaxDepth: opts.MaxDepth, LeafMax: opts.LeafMax, Cands: opts.Cands, Seed: opts.Seed,
|
||||||
|
}
|
||||||
|
root := bsp.Build(segs, p)
|
||||||
|
st := bsp.Measure(root)
|
||||||
|
|
||||||
|
fmt.Printf("BSP built.\n")
|
||||||
|
fmt.Printf(" nodes=%d leaves=%d maxDepth=%d totalLeafSegs=%d\n",
|
||||||
|
st.Nodes, st.Leaves, st.MaxDepth, st.TotalSegs)
|
||||||
|
fmt.Printf(" params: alpha=%.2f beta=%.2f eps=%.1e leafMax=%d maxDepth=%d cands=%d seed=%d\n",
|
||||||
|
p.Alpha, p.Beta, p.Eps, p.LeafMax, p.MaxDepth, p.Cands, p.Seed)
|
||||||
|
|
||||||
|
if opts.DotOut != "" {
|
||||||
|
if err := viz.EmitDOT(root, opts.DotOut); err != nil {
|
||||||
|
return fmt.Errorf("write DOT: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("DOT export geschrieben: %s\n", opts.DotOut)
|
||||||
|
|
||||||
|
if opts.TreePNG != "" {
|
||||||
|
if err := viz.RunGraphviz(opts.DotOut, opts.TreePNG); err != nil {
|
||||||
|
log.Printf("Graphviz fehlgeschlagen: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Graphviz PNG gebaut: %s\n", opts.TreePNG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Overlay != "" {
|
||||||
|
if err := viz.RenderPNG(m, root, opts.Overlay); err != nil {
|
||||||
|
return fmt.Errorf("write overlay PNG: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Overlay PNG geschrieben: %s\n", opts.Overlay)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
201
main.go
201
main.go
@@ -1,16 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bspviz/internal/bsp"
|
"bspviz/internal/app"
|
||||||
"bspviz/internal/geom"
|
"bspviz/internal/geom"
|
||||||
"bspviz/internal/mapfmt"
|
|
||||||
"bspviz/internal/viz"
|
|
||||||
"bspviz/internal/wad"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,178 +45,41 @@ func main() {
|
|||||||
usageAndExit("Flag -wad fehlt. Bitte Pfad zu einer Doom-kompatiblen WAD-Datei angeben.", 2)
|
usageAndExit("Flag -wad fehlt. Bitte Pfad zu einer Doom-kompatiblen WAD-Datei angeben.", 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
w, err := wad.Open(*wadPath)
|
var extractLumps []string
|
||||||
if err != nil {
|
if strings.TrimSpace(*extract) != "" {
|
||||||
log.Fatalf("open wad: %v", err)
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
if *listOnly {
|
|
||||||
fmt.Printf("WAD: %s\n", *wadPath)
|
|
||||||
for i, d := range w.Dir() {
|
|
||||||
fmt.Printf("%3d: %-8s size=%-7d pos=%-8d\n", i, d.Name(), d.Size, d.FilePos)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
start, end, err := w.FindMap(*mapMarker)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("find map: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Map %s: Directory [%d, %d)\n", strings.ToUpper(*mapMarker), start, end)
|
|
||||||
|
|
||||||
//info über die daten in WAD
|
|
||||||
if *info {
|
|
||||||
lumps, err := w.LoadMapLumps(*mapMarker, "VERTEXES", "LINEDEFS")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("load map lumps: %v", err)
|
|
||||||
}
|
|
||||||
vb := lumps["VERTEXES"]
|
|
||||||
lb := lumps["LINEDEFS"]
|
|
||||||
m, err := mapfmt.LoadMap(lumps)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
verts := len(vb) / 4
|
|
||||||
lines := len(lb) / 14
|
|
||||||
fmt.Printf("VERTEXES: bytes=%d count=%d\n", len(vb), verts)
|
|
||||||
fmt.Printf("LINEDEFS: bytes=%d count=%d\n", len(lb), lines)
|
|
||||||
|
|
||||||
fmt.Printf("Map has %d vertices and %d linedefs\n", len(m.Vertices), len(m.Linedefs))
|
|
||||||
fmt.Printf("First vertex: %+v\n", m.Vertices[0])
|
|
||||||
fmt.Printf("First linedef: %+v\n", m.Linedefs[0])
|
|
||||||
|
|
||||||
if len(vb)%4 != 0 {
|
|
||||||
fmt.Println("WARN: VERTEXES size ist kein Vielfaches von 4 → Format prüfen")
|
|
||||||
}
|
|
||||||
if len(lb)%14 != 0 {
|
|
||||||
fmt.Println("WARN: LINEDEFS size ist kein Vielfaches von 14 → Format prüfen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generiert einzelne Lump Dateien zum Debugen
|
|
||||||
if *extract != "" {
|
|
||||||
want := strings.Split(*extract, ",")
|
want := strings.Split(*extract, ",")
|
||||||
for i := range want {
|
extractLumps = make([]string, 0, len(want))
|
||||||
want[i] = strings.ToUpper(strings.TrimSpace(want[i]))
|
for _, name := range want {
|
||||||
}
|
n := strings.ToUpper(strings.TrimSpace(name))
|
||||||
lumps, err := w.LoadMapLumps(*mapMarker, want...)
|
if n == "" {
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("extract: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(*outdir, 0o755); err != nil {
|
|
||||||
log.Fatalf("mkdir %s: %v", *outdir, err)
|
|
||||||
}
|
|
||||||
for name, data := range lumps {
|
|
||||||
dst := filepath.Join(*outdir, fmt.Sprintf("%s.lmp", name))
|
|
||||||
if err := os.WriteFile(dst, data, 0o644); err != nil {
|
|
||||||
log.Fatalf("write %s: %v", dst, err)
|
|
||||||
}
|
|
||||||
fmt.Printf("wrote %s (%d bytes)\n", dst, len(data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zum debug test der geo functions
|
|
||||||
if *geomtest {
|
|
||||||
raw, err := w.LoadMapLumps(*mapMarker, "VERTEXES", "LINEDEFS")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("load map lumps: %v", err)
|
|
||||||
}
|
|
||||||
m, err := mapfmt.LoadMap(raw)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("parse map: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
segs := mapfmt.LinedefsToSegs(m.Vertices, m.Linedefs)
|
|
||||||
fmt.Printf("GEOM: vertices=%d linedefs=%d segs=%d\n", len(m.Vertices), len(m.Linedefs), len(segs))
|
|
||||||
if len(segs) == 0 {
|
|
||||||
fmt.Println("GEOM: keine Segmente gefunden – prüfe LINEDEFS/VERTEXES")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pts := make([]geom.Vec, 0, len(m.Vertices))
|
|
||||||
for _, v := range m.Vertices {
|
|
||||||
pts = append(pts, geom.V(float64(v.X), float64(v.Y)))
|
|
||||||
}
|
|
||||||
bb := geom.Bounds(pts)
|
|
||||||
w := bb.Max.X - bb.Min.X
|
|
||||||
h := bb.Max.Y - bb.Min.Y
|
|
||||||
fmt.Printf("AABB: min=(%.1f,%.1f) max=(%.1f,%.1f) size=(%.1f×%.1f)\n",
|
|
||||||
bb.Min.X, bb.Min.Y, bb.Max.X, bb.Max.Y, w, h)
|
|
||||||
|
|
||||||
O := segs[0].A
|
|
||||||
D := geom.Sub(segs[0].B, segs[0].A)
|
|
||||||
if geom.Len(D) < 1e-9 && len(segs) > 1 {
|
|
||||||
O = segs[1].A
|
|
||||||
D = geom.Sub(segs[1].B, segs[1].A)
|
|
||||||
}
|
|
||||||
var left, right, splits, degens int
|
|
||||||
for _, s := range segs {
|
|
||||||
f, b := geom.SplitSeg(s, O, D)
|
|
||||||
if len(f) == 0 && len(b) == 0 {
|
|
||||||
degens++
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
left += len(f)
|
extractLumps = append(extractLumps, n)
|
||||||
right += len(b)
|
|
||||||
if len(f) > 0 && len(b) > 0 {
|
|
||||||
splits++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fmt.Printf("PROBE-SPLIT: O=(%.1f,%.1f) D=(%.1f,%.1f)\n", O.X, O.Y, D.X, D.Y)
|
|
||||||
fmt.Printf("PROBE-SPLIT: left=%d right=%d splits=%d degens=%d (EPS=%.1e)\n",
|
|
||||||
left, right, splits, degens, geom.EPS)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *buildbsp {
|
opts := app.Options{
|
||||||
raw, err := w.LoadMapLumps(*mapMarker, "VERTEXES", "LINEDEFS")
|
WadPath: *wadPath,
|
||||||
if err != nil {
|
MapMarker: *mapMarker,
|
||||||
log.Fatalf("load map lumps: %v", err)
|
ListOnly: *listOnly,
|
||||||
}
|
Info: *info,
|
||||||
m, err := mapfmt.LoadMap(raw)
|
Extract: extractLumps,
|
||||||
if err != nil {
|
OutDir: *outdir,
|
||||||
log.Fatalf("parse map: %v", err)
|
GeomTest: *geomtest,
|
||||||
}
|
BuildBSP: *buildbsp,
|
||||||
|
Alpha: *alpha,
|
||||||
segs := mapfmt.LinedefsToSegs(m.Vertices, m.Linedefs)
|
Beta: *beta,
|
||||||
p := bsp.Params{
|
Eps: *eps,
|
||||||
Alpha: *alpha, Beta: *beta, Eps: *eps,
|
LeafMax: *leaf,
|
||||||
MaxDepth: *depth, LeafMax: *leaf, Cands: *cands, Seed: *seed,
|
MaxDepth: *depth,
|
||||||
}
|
Cands: *cands,
|
||||||
root := bsp.Build(segs, p)
|
Seed: *seed,
|
||||||
st := bsp.Measure(root)
|
DotOut: *dotOut,
|
||||||
|
TreePNG: *treePNG,
|
||||||
fmt.Printf("BSP built.\n")
|
Overlay: *overlay,
|
||||||
fmt.Printf(" nodes=%d leaves=%d maxDepth=%d totalLeafSegs=%d\n",
|
|
||||||
st.Nodes, st.Leaves, st.MaxDepth, st.TotalSegs)
|
|
||||||
fmt.Printf(" params: alpha=%.2f beta=%.2f eps=%.1e leafMax=%d maxDepth=%d cands=%d seed=%d\n",
|
|
||||||
p.Alpha, p.Beta, p.Eps, p.LeafMax, p.MaxDepth, p.Cands, p.Seed)
|
|
||||||
|
|
||||||
if *dotOut != "" {
|
|
||||||
if err := viz.EmitDOT(root, *dotOut); err != nil {
|
|
||||||
log.Fatalf("write DOT: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("DOT export geschrieben: %s\n", *dotOut)
|
|
||||||
|
|
||||||
if *treePNG != "" {
|
|
||||||
if err := viz.RunGraphviz(*dotOut, *treePNG); err != nil {
|
|
||||||
log.Printf("Graphviz fehlgeschlagen: %v", err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Graphviz PNG gebaut: %s\n", *treePNG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if *overlay != "" {
|
|
||||||
if err := viz.RenderPNG(m, root, *overlay); err != nil {
|
|
||||||
log.Fatalf("write overlay PNG: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Overlay PNG geschrieben: %s\n", *overlay)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := app.Run(opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user