redid wall generation completely.
This commit is contained in:
@@ -34,7 +34,7 @@
|
|||||||
"turret_size": 2.5000000000000004,
|
"turret_size": 2.5000000000000004,
|
||||||
"turret_shape": "circular",
|
"turret_shape": "circular",
|
||||||
"turret_spacing": 20,
|
"turret_spacing": 20,
|
||||||
"gate_spacing": 34,
|
"gate_count": 3,
|
||||||
"num_buildings": 1500,
|
"num_buildings": 1500,
|
||||||
"min_building_size": 1,
|
"min_building_size": 1,
|
||||||
"max_building_size": 2.5,
|
"max_building_size": 2.5,
|
||||||
@@ -51,4 +51,4 @@
|
|||||||
"seed": 1773254459149268515,
|
"seed": 1773254459149268515,
|
||||||
"last_export_path": "",
|
"last_export_path": "",
|
||||||
"image_view_state": 0
|
"image_view_state": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"turret_size": 2.5000000000000004,
|
"turret_size": 2.5000000000000004,
|
||||||
"turret_shape": "circular",
|
"turret_shape": "circular",
|
||||||
"turret_spacing": 20,
|
"turret_spacing": 20,
|
||||||
"gate_spacing": 34,
|
"gate_count": 3,
|
||||||
"num_buildings": 50,
|
"num_buildings": 50,
|
||||||
"min_building_size": 2.5,
|
"min_building_size": 2.5,
|
||||||
"max_building_size": 5,
|
"max_building_size": 5,
|
||||||
@@ -51,4 +51,4 @@
|
|||||||
"seed": 1773254246832144883,
|
"seed": 1773254246832144883,
|
||||||
"last_export_path": "",
|
"last_export_path": "",
|
||||||
"image_view_state": 0
|
"image_view_state": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"turret_size": 2.5000000000000004,
|
"turret_size": 2.5000000000000004,
|
||||||
"turret_shape": "circular",
|
"turret_shape": "circular",
|
||||||
"turret_spacing": 20,
|
"turret_spacing": 20,
|
||||||
"gate_spacing": 34,
|
"gate_count": 3,
|
||||||
"num_buildings": 7000,
|
"num_buildings": 7000,
|
||||||
"min_building_size": 0.5,
|
"min_building_size": 0.5,
|
||||||
"max_building_size": 1,
|
"max_building_size": 1,
|
||||||
@@ -51,4 +51,4 @@
|
|||||||
"seed": 1773254695515253606,
|
"seed": 1773254695515253606,
|
||||||
"last_export_path": "",
|
"last_export_path": "",
|
||||||
"image_view_state": 0
|
"image_view_state": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"turret_size": 2.5000000000000004,
|
"turret_size": 2.5000000000000004,
|
||||||
"turret_shape": "circular",
|
"turret_shape": "circular",
|
||||||
"turret_spacing": 20,
|
"turret_spacing": 20,
|
||||||
"gate_spacing": 34,
|
"gate_count": 3,
|
||||||
"num_buildings": 500,
|
"num_buildings": 500,
|
||||||
"min_building_size": 1.5,
|
"min_building_size": 1.5,
|
||||||
"max_building_size": 3.5,
|
"max_building_size": 3.5,
|
||||||
@@ -51,4 +51,4 @@
|
|||||||
"seed": 1773254407932680563,
|
"seed": 1773254407932680563,
|
||||||
"last_export_path": "",
|
"last_export_path": "",
|
||||||
"image_view_state": 0
|
"image_view_state": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"turret_size": 2.5000000000000004,
|
"turret_size": 2.5000000000000004,
|
||||||
"turret_shape": "circular",
|
"turret_shape": "circular",
|
||||||
"turret_spacing": 20,
|
"turret_spacing": 20,
|
||||||
"gate_spacing": 34,
|
"gate_count": 3,
|
||||||
"num_buildings": 10,
|
"num_buildings": 10,
|
||||||
"min_building_size": 2.5,
|
"min_building_size": 2.5,
|
||||||
"max_building_size": 5,
|
"max_building_size": 5,
|
||||||
@@ -51,4 +51,4 @@
|
|||||||
"seed": 1773254133615935858,
|
"seed": 1773254133615935858,
|
||||||
"last_export_path": "",
|
"last_export_path": "",
|
||||||
"image_view_state": 0
|
"image_view_state": 0
|
||||||
}
|
}
|
||||||
|
|||||||
+515
-658
File diff suppressed because it is too large
Load Diff
@@ -169,23 +169,23 @@ func GenerateRoadsWithPOIs(
|
|||||||
if len(roads) == 0 {
|
if len(roads) == 0 {
|
||||||
return NewPixelMask(width, height), NewPixelMask(width, height), NewPixelMask(width, height), nil, nil
|
return NewPixelMask(width, height), NewPixelMask(width, height), NewPixelMask(width, height), nil, nil
|
||||||
}
|
}
|
||||||
roads = applyWallCrossingRules(roads, wallLayout, waterMask, randSrc)
|
|
||||||
if len(roads) == 0 {
|
|
||||||
return NewPixelMask(width, height), NewPixelMask(width, height), NewPixelMask(width, height), nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add gate roads after wall-crossing rules (so they are never filtered out).
|
|
||||||
if wallLayout != nil && len(wallLayout.Gates) > 0 {
|
if wallLayout != nil && len(wallLayout.Gates) > 0 {
|
||||||
gateRoads := generateGateRoads(wallLayout, settings, waterMask, width, height, randSrc)
|
gateRoads := generateGateRoads(wallLayout, settings, waterMask, width, height, randSrc)
|
||||||
roads = append(roads, gateRoads...)
|
roads = append(roads, gateRoads...)
|
||||||
roads = ensureGateRoadConnections(gateRoads, roads, wallLayout, settings, waterMask, width, height, randSrc)
|
roads = ensureGateRoadConnections(gateRoads, roads, wallLayout, settings, waterMask, width, height, randSrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roads = applyWallCrossingRules(roads, wallLayout, waterMask, randSrc)
|
||||||
|
if len(roads) == 0 {
|
||||||
|
return NewPixelMask(width, height), NewPixelMask(width, height), NewPixelMask(width, height), nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
roads = reduceRepeatedBridges(roads, waterMask, width, height, randSrc)
|
roads = reduceRepeatedBridges(roads, waterMask, width, height, randSrc)
|
||||||
if len(roads) == 0 {
|
if len(roads) == 0 {
|
||||||
return NewPixelMask(width, height), NewPixelMask(width, height), NewPixelMask(width, height), nil, nil
|
return NewPixelMask(width, height), NewPixelMask(width, height), NewPixelMask(width, height), nil, nil
|
||||||
}
|
}
|
||||||
roads = ensureRoadNetworkConnected(roads, settings, randSrc, waterMask, wallLayout, width, height)
|
roads = ensureRoadNetworkConnected(roads, settings, randSrc, waterMask, wallLayout, width, height)
|
||||||
|
roads = applyWallCrossingRules(roads, wallLayout, waterMask, randSrc)
|
||||||
assignRoadWidths(roads, settings, randSrc, width, height, wallLayout)
|
assignRoadWidths(roads, settings, randSrc, width, height, wallLayout)
|
||||||
|
|
||||||
roadMask := NewPixelMask(width, height)
|
roadMask := NewPixelMask(width, height)
|
||||||
@@ -655,32 +655,6 @@ func appendExitRoads(roads []*Road, pois []*PointOfInterest, width, height int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
path := calculateRoadPath(anchor, edgeNode, settings.RoadCurvyness/100.0, avgDim, randSrc, waterMask, wallLayout)
|
path := calculateRoadPath(anchor, edgeNode, settings.RoadCurvyness/100.0, avgDim, randSrc, waterMask, wallLayout)
|
||||||
if wallLayout != nil && wallLayout.Mask != nil && len(crossedWallIDs(path, wallLayout)) == 0 {
|
|
||||||
bestScore := -1.0
|
|
||||||
bestAnchor := anchor
|
|
||||||
bestPath := path
|
|
||||||
for _, cand := range pois {
|
|
||||||
testPath := calculateRoadPath(cand, edgeNode, settings.RoadCurvyness/100.0, avgDim, randSrc, waterMask, wallLayout)
|
|
||||||
if len(crossedWallIDs(testPath, wallLayout)) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
d := math.Hypot(float64(cand.X-edgeNode.X), float64(cand.Y-edgeNode.Y))
|
|
||||||
score := cand.ArterialWeight*2.0 + clamp(1.0-d/2000.0, 0, 1)
|
|
||||||
if score > bestScore {
|
|
||||||
bestScore = score
|
|
||||||
bestAnchor = cand
|
|
||||||
bestPath = testPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anchor = bestAnchor
|
|
||||||
path = bestPath
|
|
||||||
}
|
|
||||||
if wallLayout != nil && wallLayout.Mask != nil && len(wallLayout.Coverages) > 0 {
|
|
||||||
// Exit roads always use the gate-cheat when walls exist so they are always placeable.
|
|
||||||
if forced, ok := forcePathThroughWallGate(anchor, edgeNode, wallLayout, waterMask); ok {
|
|
||||||
path = forced
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anchor.Connections++
|
anchor.Connections++
|
||||||
edgeNode.IsExit = true
|
edgeNode.IsExit = true
|
||||||
@@ -1081,14 +1055,14 @@ func calculateRoadPath(start, end *PointOfInterest, curvyness, avgDim float64, r
|
|||||||
curve := clamp(curvyness, 0, 1)
|
curve := clamp(curvyness, 0, 1)
|
||||||
if curve <= 0 {
|
if curve <= 0 {
|
||||||
points := bresenhamRoad([]image.Point{{X: start.X, Y: start.Y}, {X: end.X, Y: end.Y}})
|
points := bresenhamRoad([]image.Point{{X: start.X, Y: start.Y}, {X: end.X, Y: end.Y}})
|
||||||
return straightenPathAcrossWalls(toPathPoints(points, waterMask), wallLayout, waterMask)
|
return toPathPoints(points, waterMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-linear scaling: low values stay fairly straight, high values become very winding.
|
// Non-linear scaling: low values stay fairly straight, high values become very winding.
|
||||||
strength := math.Pow(curve, 1.35)
|
strength := math.Pow(curve, 1.35)
|
||||||
if strength < 0.001 {
|
if strength < 0.001 {
|
||||||
points := bresenhamRoad([]image.Point{{X: start.X, Y: start.Y}, {X: end.X, Y: end.Y}})
|
points := bresenhamRoad([]image.Point{{X: start.X, Y: start.Y}, {X: end.X, Y: end.Y}})
|
||||||
return straightenPathAcrossWalls(toPathPoints(points, waterMask), wallLayout, waterMask)
|
return toPathPoints(points, waterMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
baseControls := int(math.Max(12, dist/(22.0-14.0*strength)))
|
baseControls := int(math.Max(12, dist/(22.0-14.0*strength)))
|
||||||
@@ -1149,7 +1123,7 @@ func calculateRoadPath(start, end *PointOfInterest, curvyness, avgDim float64, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
points := bresenhamRoad(controlPoints)
|
points := bresenhamRoad(controlPoints)
|
||||||
return straightenPathAcrossWalls(toPathPoints(points, waterMask), wallLayout, waterMask)
|
return toPathPoints(points, waterMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPathPoints(points []image.Point, waterMask *PixelMask) []PathPoint {
|
func toPathPoints(points []image.Point, waterMask *PixelMask) []PathPoint {
|
||||||
@@ -1352,95 +1326,41 @@ func containsWallID(ids []int, wallID int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyWallCrossingRules(roads []*Road, wallLayout *FortificationLayout, waterMask *PixelMask, randSrc *rand.Rand) []*Road {
|
func applyWallCrossingRules(roads []*Road, wallLayout *FortificationLayout, waterMask *PixelMask, randSrc *rand.Rand) []*Road {
|
||||||
if len(roads) == 0 || wallLayout == nil || wallLayout.Mask == nil || len(wallLayout.Coverages) == 0 {
|
if len(roads) == 0 || wallLayout == nil || wallLayout.Mask == nil {
|
||||||
return roads
|
return roads
|
||||||
}
|
}
|
||||||
|
_ = waterMask
|
||||||
// Straighten each wall crossing segment first.
|
_ = randSrc
|
||||||
for _, road := range roads {
|
|
||||||
road.Points = straightenPathAcrossWalls(road.Points, wallLayout, waterMask)
|
|
||||||
}
|
|
||||||
|
|
||||||
type roadInfo struct {
|
|
||||||
road *Road
|
|
||||||
ids []int
|
|
||||||
}
|
|
||||||
infos := make([]roadInfo, 0, len(roads))
|
|
||||||
for _, road := range roads {
|
|
||||||
infos = append(infos, roadInfo{road: road, ids: crossedWallIDs(road.Points, wallLayout)})
|
|
||||||
}
|
|
||||||
|
|
||||||
const repeatWallFactor = 0.55
|
|
||||||
wallCrossCount := make(map[int]int)
|
|
||||||
requiredWalls := make(map[int]bool)
|
|
||||||
for i, cov := range wallLayout.Coverages {
|
|
||||||
if cov < 95 {
|
|
||||||
requiredWalls[i+1] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keep := make([]bool, len(infos))
|
|
||||||
for i, info := range infos {
|
|
||||||
if len(info.ids) == 0 {
|
|
||||||
keep[i] = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if info.road.Start.IsExit || info.road.End.IsExit {
|
|
||||||
keep[i] = true
|
|
||||||
for _, wid := range info.ids {
|
|
||||||
wallCrossCount[wid]++
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if crossesSameWallMultipleTimes(info.road.Points, wallLayout) {
|
|
||||||
keep[i] = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
keepProb := 1.0
|
|
||||||
for _, wid := range info.ids {
|
|
||||||
c := wallCrossCount[wid]
|
|
||||||
if c > 0 {
|
|
||||||
keepProb *= math.Pow(repeatWallFactor, float64(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if randSrc.Float64() <= keepProb {
|
|
||||||
keep[i] = true
|
|
||||||
for _, wid := range info.ids {
|
|
||||||
wallCrossCount[wid]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure at least one crossing on each wall unless its configured coverage is >= 95%.
|
|
||||||
for wallID := range requiredWalls {
|
|
||||||
if wallCrossCount[wallID] > 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for i, info := range infos {
|
|
||||||
if keep[i] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !containsWallID(info.ids, wallID) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
keep[i] = true
|
|
||||||
for _, wid := range info.ids {
|
|
||||||
wallCrossCount[wid]++
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered := make([]*Road, 0, len(roads))
|
filtered := make([]*Road, 0, len(roads))
|
||||||
for i, info := range infos {
|
for _, road := range roads {
|
||||||
if keep[i] {
|
if pathRespectsWallPassages(road.Points, wallLayout.Mask, wallLayout.GateMask) {
|
||||||
filtered = append(filtered, info.road)
|
filtered = append(filtered, road)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pathRespectsWallPassages(points []PathPoint, exclusionMask, gateMask *PixelMask) bool {
|
||||||
|
if len(points) == 0 || exclusionMask == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, pp := range points {
|
||||||
|
x := pp.Point.X
|
||||||
|
y := pp.Point.Y
|
||||||
|
if !exclusionMask.InBounds(x, y) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !exclusionMask.GetXY(x, y) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gateMask != nil && gateMask.GetXY(x, y) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func crossesSameWallMultipleTimes(points []PathPoint, wallLayout *FortificationLayout) bool {
|
func crossesSameWallMultipleTimes(points []PathPoint, wallLayout *FortificationLayout) bool {
|
||||||
if wallLayout == nil || wallLayout.Mask == nil || len(points) < 2 {
|
if wallLayout == nil || wallLayout.Mask == nil || len(points) < 2 {
|
||||||
return false
|
return false
|
||||||
|
|||||||
+23
-4
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@@ -54,7 +55,7 @@ type Settings struct {
|
|||||||
TurretSize float64 `json:"turret_size"`
|
TurretSize float64 `json:"turret_size"`
|
||||||
TurretShape string `json:"turret_shape"`
|
TurretShape string `json:"turret_shape"`
|
||||||
TurretSpacing float64 `json:"turret_spacing"`
|
TurretSpacing float64 `json:"turret_spacing"`
|
||||||
GateSpacing float64 `json:"gate_spacing"` // percent of wall circumference between gates (0=no gates)
|
GateCount int `json:"gate_count"` // number of gates on the outer wall before inner-wall halving
|
||||||
|
|
||||||
// Building settings
|
// Building settings
|
||||||
NumBuildings int `json:"num_buildings"`
|
NumBuildings int `json:"num_buildings"`
|
||||||
@@ -161,7 +162,7 @@ func LoadSettings() (*Settings, error) {
|
|||||||
TurretSize: 0.6,
|
TurretSize: 0.6,
|
||||||
TurretShape: "circular",
|
TurretShape: "circular",
|
||||||
TurretSpacing: 55,
|
TurretSpacing: 55,
|
||||||
GateSpacing: 25,
|
GateCount: 3,
|
||||||
NumBuildings: 200,
|
NumBuildings: 200,
|
||||||
MinBuildingSize: 3.5,
|
MinBuildingSize: 3.5,
|
||||||
MaxBuildingSize: 10.0,
|
MaxBuildingSize: 10.0,
|
||||||
@@ -264,8 +265,20 @@ func normalizeSettings(settings *Settings, rawKeys map[string]json.RawMessage) {
|
|||||||
if _, ok := rawKeys["turret_spacing"]; !ok {
|
if _, ok := rawKeys["turret_spacing"]; !ok {
|
||||||
settings.TurretSpacing = 55
|
settings.TurretSpacing = 55
|
||||||
}
|
}
|
||||||
if _, ok := rawKeys["gate_spacing"]; !ok {
|
if _, ok := rawKeys["gate_count"]; !ok {
|
||||||
settings.GateSpacing = 25
|
if legacyRaw, legacy := rawKeys["gate_spacing"]; legacy && settings.GateCount == 0 {
|
||||||
|
var legacySpacing float64
|
||||||
|
if err := json.Unmarshal(legacyRaw, &legacySpacing); err == nil {
|
||||||
|
legacyCount := int(math.Round(100.0 / clamp(legacySpacing, 1, 100)))
|
||||||
|
if legacyCount < 1 {
|
||||||
|
legacyCount = 1
|
||||||
|
}
|
||||||
|
settings.GateCount = legacyCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if settings.GateCount == 0 {
|
||||||
|
settings.GateCount = 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wall widths are percentages of average image dimension.
|
// Wall widths are percentages of average image dimension.
|
||||||
@@ -289,6 +302,12 @@ func normalizeSettings(settings *Settings, rawKeys map[string]json.RawMessage) {
|
|||||||
settings.WallCurvyness = clamp(settings.WallCurvyness, 0, 100)
|
settings.WallCurvyness = clamp(settings.WallCurvyness, 0, 100)
|
||||||
settings.TurretSize = snapTurretSizePercent(settings.TurretSize)
|
settings.TurretSize = snapTurretSizePercent(settings.TurretSize)
|
||||||
settings.TurretSpacing = clamp(settings.TurretSpacing, 0, 100)
|
settings.TurretSpacing = clamp(settings.TurretSpacing, 0, 100)
|
||||||
|
if settings.GateCount < 1 {
|
||||||
|
settings.GateCount = 1
|
||||||
|
}
|
||||||
|
if settings.GateCount > 20 {
|
||||||
|
settings.GateCount = 20
|
||||||
|
}
|
||||||
|
|
||||||
// Tree sizes are percentages of average image dimension.
|
// Tree sizes are percentages of average image dimension.
|
||||||
// Migrate older pixel-based values when they exceed the valid percentage range.
|
// Migrate older pixel-based values when they exceed the valid percentage range.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"image/png"
|
"image/png"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -322,6 +323,12 @@ func main() {
|
|||||||
return GenerateTrees(treeBase, waterMask, placementMask, buildingMask, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, seedProvider.Next())
|
return GenerateTrees(treeBase, waterMask, placementMask, buildingMask, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, seedProvider.Next())
|
||||||
}); ok {
|
}); ok {
|
||||||
treeMask = out
|
treeMask = out
|
||||||
|
if wallMask != nil {
|
||||||
|
drawWallMask(treeBase, wallMask)
|
||||||
|
}
|
||||||
|
if turretMask != nil {
|
||||||
|
drawTurretMask(treeBase, turretMask)
|
||||||
|
}
|
||||||
finalImage = treeBase
|
finalImage = treeBase
|
||||||
} else {
|
} else {
|
||||||
log.Println("GenerateTrees timed out after 1 minute; continuing.")
|
log.Println("GenerateTrees timed out after 1 minute; continuing.")
|
||||||
@@ -789,16 +796,16 @@ func main() {
|
|||||||
markPresetDirty()
|
markPresetDirty()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
gateSpacingSlider := newNumericInputSlider(0, 100, settings.GateSpacing, "%.0f%%", "Gate Spacing")
|
gateCountSlider := newNumericInputSliderWithStep(1, 20, float64(settings.GateCount), 1, "%.0f", "Gate Count")
|
||||||
gateSpacingSlider.entry.OnChanged = func(s string) {
|
gateCountSlider.entry.OnChanged = func(s string) {
|
||||||
gateSpacingSlider.validate(s, func(hasError bool) {
|
gateCountSlider.validate(s, func(hasError bool) {
|
||||||
errorStates["gateSpacing"] = hasError
|
errorStates["gateCount"] = hasError
|
||||||
updateGenerateBtnState()
|
updateGenerateBtnState()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
gateSpacingSlider.value.AddListener(binding.NewDataListener(func() {
|
gateCountSlider.value.AddListener(binding.NewDataListener(func() {
|
||||||
val, _ := gateSpacingSlider.value.Get()
|
val, _ := gateCountSlider.value.Get()
|
||||||
settings.GateSpacing = val
|
settings.GateCount = int(math.Round(val))
|
||||||
markPresetDirty()
|
markPresetDirty()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -1111,6 +1118,12 @@ func main() {
|
|||||||
return GenerateTrees(treeBase, waterMask, placementMask, buildingMask, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, seedProvider.Next())
|
return GenerateTrees(treeBase, waterMask, placementMask, buildingMask, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, seedProvider.Next())
|
||||||
}); ok {
|
}); ok {
|
||||||
treeMask = out
|
treeMask = out
|
||||||
|
if wallMask != nil {
|
||||||
|
drawWallMask(treeBase, wallMask)
|
||||||
|
}
|
||||||
|
if turretMask != nil {
|
||||||
|
drawTurretMask(treeBase, turretMask)
|
||||||
|
}
|
||||||
finalImage = treeBase
|
finalImage = treeBase
|
||||||
} else {
|
} else {
|
||||||
log.Println("GenerateTrees timed out after 1 minute; continuing.")
|
log.Println("GenerateTrees timed out after 1 minute; continuing.")
|
||||||
@@ -1240,7 +1253,7 @@ func main() {
|
|||||||
numWallsSlider,
|
numWallsSlider,
|
||||||
cityCoverageSlider,
|
cityCoverageSlider,
|
||||||
wallCurvynessSlider,
|
wallCurvynessSlider,
|
||||||
gateSpacingSlider,
|
gateCountSlider,
|
||||||
showTurretsCheck,
|
showTurretsCheck,
|
||||||
turretControls,
|
turretControls,
|
||||||
))
|
))
|
||||||
@@ -1553,7 +1566,7 @@ func main() {
|
|||||||
numWallsSlider.value.Set(float64(settings.NumWalls))
|
numWallsSlider.value.Set(float64(settings.NumWalls))
|
||||||
cityCoverageSlider.value.Set(settings.CityCoverage)
|
cityCoverageSlider.value.Set(settings.CityCoverage)
|
||||||
wallCurvynessSlider.value.Set(settings.WallCurvyness)
|
wallCurvynessSlider.value.Set(settings.WallCurvyness)
|
||||||
gateSpacingSlider.value.Set(settings.GateSpacing)
|
gateCountSlider.value.Set(float64(settings.GateCount))
|
||||||
showTurretsCheck.SetChecked(settings.ShowTurrets)
|
showTurretsCheck.SetChecked(settings.ShowTurrets)
|
||||||
turretSizeSlider.value.Set(settings.TurretSize)
|
turretSizeSlider.value.Set(settings.TurretSize)
|
||||||
turretShapeSelect.SetSelected(settings.TurretShape)
|
turretShapeSelect.SetSelected(settings.TurretShape)
|
||||||
|
|||||||
Reference in New Issue
Block a user