diff --git a/main.go b/main.go index c4a8c71..bb9222d 100644 --- a/main.go +++ b/main.go @@ -125,26 +125,6 @@ func main() { var roadImage *image.RGBA - roadPixels, roadImage = GenerateRoads(settings.Width, settings.Height, settings, noiseImg) - - // Draw roadImage over finalImage - - for y := 0; y < roadImage.Bounds().Max.Y; y++ { - - for x := 0; x < roadImage.Bounds().Max.X; x++ { - - r, g, b, a := roadImage.At(x, y).RGBA() - - if a > 0 { - - finalImage.Set(x, y, color.RGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)}) - - } - - } - - } - var flatLakePixels []image.Point for _, lake := range lakes { @@ -154,6 +134,15 @@ func main() { } allWaterPixels := append(flatLakePixels, riverPixels...) + roadPixels, roadImage = GenerateRoads(settings.Width, settings.Height, settings, noiseImg, allWaterPixels) + for y := 0; y < roadImage.Bounds().Max.Y; y++ { + for x := 0; x < roadImage.Bounds().Max.X; x++ { + r, g, b, a := roadImage.At(x, y).RGBA() + if a > 0 { + finalImage.Set(x, y, color.RGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)}) + } + } + } treePixels = GenerateTrees(finalImage, allWaterPixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed) @@ -299,7 +288,7 @@ func main() { treeClumpinessSlider.SetValue(settings.TreeClumpiness) numRoadsLabel := widget.NewLabel(fmt.Sprintf("Number of Roads: %d", settings.NumRoads)) - numRoadsSlider := widget.NewSlider(1, 1000) + numRoadsSlider := widget.NewSlider(0, 1000) numRoadsSlider.OnChanged = func(val float64) { settings.NumRoads = int(val) numRoadsLabel.SetText(fmt.Sprintf("Number of Roads: %d", settings.NumRoads)) @@ -332,7 +321,7 @@ func main() { maxRoadWidthSlider.SetValue(settings.MaxRoadWidth) roadExitsLabel := widget.NewLabel(fmt.Sprintf("Road Exits: %d", settings.RoadExits)) - roadExitsSlider := widget.NewSlider(1, 100) + roadExitsSlider := widget.NewSlider(0, 100) roadExitsSlider.OnChanged = func(val float64) { if val > float64(settings.NumRoads) { val = float64(settings.NumRoads) @@ -417,6 +406,12 @@ func main() { finalImage := riverImage.(*image.RGBA) + var flatLakePixels []image.Point + for _, lake := range lakes { + flatLakePixels = append(flatLakePixels, lake...) + } + allWaterPixels := append(flatLakePixels, riverPixels...) + // Step 4: Generating Roads currentStep++ fyne.Do(func() { @@ -424,7 +419,7 @@ func main() { progressBar.SetValue(float64(currentStep) / float64(steps)) }) var roadImage *image.RGBA - roadPixels, roadImage = GenerateRoads(settings.Width, settings.Height, settings, noiseImg) // Draw roadImage over finalImage + roadPixels, roadImage = GenerateRoads(settings.Width, settings.Height, settings, noiseImg, allWaterPixels) // Draw roadImage over finalImage for y := 0; y < roadImage.Bounds().Max.Y; y++ { for x := 0; x < roadImage.Bounds().Max.X; x++ { r, g, b, a := roadImage.At(x, y).RGBA() @@ -434,12 +429,6 @@ func main() { } } - var flatLakePixels []image.Point - for _, lake := range lakes { - flatLakePixels = append(flatLakePixels, lake...) - } - allWaterPixels := append(flatLakePixels, riverPixels...) - // Step 5: Darkening Water Areas currentStep++ fyne.Do(func() { diff --git a/roads.go b/roads.go index cb52657..3b8b9bf 100644 --- a/roads.go +++ b/roads.go @@ -22,7 +22,7 @@ type Road struct { Importance int } -func GenerateRoads(width, height int, settings *Settings, noiseImg image.Image) ([]image.Point, *image.RGBA) { +func GenerateRoads(width, height int, settings *Settings, noiseImg image.Image, allWaterPixels []image.Point) ([]image.Point, *image.RGBA) { img := image.NewRGBA(image.Rect(0, 0, width, height)) // Transparent background for y := 0; y < height; y++ { @@ -34,7 +34,7 @@ func GenerateRoads(width, height int, settings *Settings, noiseImg image.Image) rand.Seed(settings.Seed) roadColor := color.RGBA{R: 139, G: 69, B: 19, A: 255} - pois := generatePOIs(width, height, settings) + pois := generatePOIs(width, height, settings, allWaterPixels) if len(pois) == 0 { return nil, img } @@ -51,18 +51,23 @@ func GenerateRoads(width, height int, settings *Settings, noiseImg image.Image) return allRoadPixels, img } -func generatePOIs(width, height int, settings *Settings) []*PointOfInterest { +func generatePOIs(width, height int, settings *Settings, allWaterPixels []image.Point) []*PointOfInterest { numPOIs := settings.NumRoads / 2 if numPOIs == 0 { return nil } + waterMap := make(map[image.Point]bool) + for _, p := range allWaterPixels { + waterMap[p] = true + } + numExits := settings.RoadExits if numExits > settings.NumRoads { numExits = settings.NumRoads } - pois := make([]*PointOfInterest, numPOIs) + pois := make([]*PointOfInterest, 0, numPOIs) centerX := width / 2 centerY := height / 2 @@ -71,33 +76,40 @@ func generatePOIs(width, height int, settings *Settings) []*PointOfInterest { radius := maxRadius * (settings.RoadDistribution / 100.0) for i := 0; i < numPOIs; i++ { - // Handle exits - if i < numExits { - side := rand.Intn(4) - var x, y int - switch side { - case 0: // Top - x = rand.Intn(width) - y = 0 - case 1: // Bottom - x = rand.Intn(width) - y = height - 1 - case 2: // Left - x = 0 - y = rand.Intn(height) - case 3: // Right - x = width - 1 - y = rand.Intn(height) + var x, y int + found := false + for j := 0; j < 100; j++ { // 100 retries to find a land spot + if i < numExits { + side := rand.Intn(4) + switch side { + case 0: // Top + x = rand.Intn(width) + y = 0 + case 1: // Bottom + x = rand.Intn(width) + y = height - 1 + case 2: // Left + x = 0 + y = rand.Intn(height) + case 3: // Right + x = width - 1 + y = rand.Intn(height) + } + } else { + angle := rand.Float64() * 2 * math.Pi + r := rand.Float64() * radius + x = int(float64(centerX) + r*math.Cos(angle)) + y = int(float64(centerY) + r*math.Sin(angle)) } - pois[i] = &PointOfInterest{X: x, Y: y} - continue - } - angle := rand.Float64() * 2 * math.Pi - r := rand.Float64() * radius - x := int(float64(centerX) + r*math.Cos(angle)) - y := int(float64(centerY) + r*math.Sin(angle)) - pois[i] = &PointOfInterest{X: x, Y: y} + if !waterMap[image.Point{X: x, Y: y}] { + found = true + break + } + } + if found { + pois = append(pois, &PointOfInterest{X: x, Y: y}) + } } return pois @@ -119,6 +131,9 @@ func connectPOIs(pois []*PointOfInterest, width, height int, settings *Settings) minDist := -1.0 for _, poi := range pois { + if poi == nil { + continue + } dist := math.Sqrt(math.Pow(float64(poi.X-centerX), 2) + math.Pow(float64(poi.Y-centerY), 2)) if startNode == nil || dist < minDist { minDist = dist @@ -126,6 +141,10 @@ func connectPOIs(pois []*PointOfInterest, width, height int, settings *Settings) } } + if startNode == nil { + return nil + } + visited[startNode] = true for len(visited) < len(pois) { @@ -135,6 +154,9 @@ func connectPOIs(pois []*PointOfInterest, width, height int, settings *Settings) for poi := range visited { for _, other := range pois { + if poi == nil || other == nil { + continue + } if !visited[other] { dist := math.Sqrt(math.Pow(float64(poi.X-other.X), 2) + math.Pow(float64(poi.Y-other.Y), 2)) @@ -182,6 +204,9 @@ func connectPOIs(pois []*PointOfInterest, width, height int, settings *Settings) End: closest, Control: &PointOfInterest{X: controlX, Y: controlY}, }) + } else { + // No more reachable POIs + break } }