From 1b286bfca48e861a02096ba741dbc402a1747208 Mon Sep 17 00:00:00 2001 From: Grimsace Date: Fri, 30 Jan 2026 10:55:15 -0600 Subject: [PATCH] fixed lake and river merging --- main.go | 22 ++++++++++++++++------ terrain.go | 49 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index 1ccabed..575fd36 100644 --- a/main.go +++ b/main.go @@ -100,10 +100,16 @@ func main() { if canvasImg.Image == nil || heightmapImg.Image == nil { // Initial image generation noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail), 100.0, settings.Seed) - lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed) - riverImage, riverPixels := GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakePixels, settings.Seed) + lakeImage, lakes := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed) + riverImage, riverPixels := GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakes, settings.Seed) finalImage := riverImage.(*image.RGBA) - allWaterPixels := append(lakePixels, riverPixels...) + + var flatLakePixels []image.Point + for _, lake := range lakes { + flatLakePixels = append(flatLakePixels, lake...) + } + allWaterPixels := append(flatLakePixels, riverPixels...) + GenerateTrees(finalImage, allWaterPixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed) darkenedHeightmap := DarkenLakeAreas(noiseImg, allWaterPixels) compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness) @@ -267,16 +273,20 @@ func main() { currentStep++ progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Lakes", currentStep, steps)) progressBar.SetValue(float64(currentStep) / float64(steps)) - lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed) + lakeImage, lakes := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed) // Step 3: Generating Rivers currentStep++ progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Rivers", currentStep, steps)) progressBar.SetValue(float64(currentStep) / float64(steps)) - riverImage, riverPixels := GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakePixels, settings.Seed) + riverImage, riverPixels := GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakes, settings.Seed) finalImage := riverImage.(*image.RGBA) - allWaterPixels := append(lakePixels, riverPixels...) + var flatLakePixels []image.Point + for _, lake := range lakes { + flatLakePixels = append(flatLakePixels, lake...) + } + allWaterPixels := append(flatLakePixels, riverPixels...) // Step 4: Darkening Water Areas currentStep++ diff --git a/terrain.go b/terrain.go index aabeed9..64975a9 100644 --- a/terrain.go +++ b/terrain.go @@ -45,8 +45,7 @@ func (pq *priorityQueue) Pop() interface{} { return item } -// GenerateLakes creates a specific number of lakes by dividing the image into chunks and placing one lake per chunk. -func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper float64, heightmap image.Image, seed int64) (image.Image, []image.Point) { +func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper float64, heightmap image.Image, seed int64) (image.Image, [][]image.Point) { canvas := image.NewRGBA(image.Rect(0, 0, width, height)) draw.Draw(canvas, canvas.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src) @@ -54,7 +53,7 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo return canvas, nil } - var allLakePixels []image.Point + var allLakes [][]image.Point randSrc := rand.New(rand.NewSource(seed)) // 1. Divide the image into a grid @@ -86,6 +85,8 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo break } + var currentLake []image.Point + // Each lake gets a random size within the defined range lakeSize := lakeSizeLower if lakeSizeUpper > lakeSizeLower { @@ -147,7 +148,7 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo // The pixel is valid, claim it. canvas.Set(current.point.X, current.point.Y, color.RGBA{R: 0, G: 0, B: 255, A: 255}) - allLakePixels = append(allLakePixels, current.point) + currentLake = append(currentLake, current.point) lakeCount++ // Add neighbors, constrained to the chunk rectangle @@ -170,9 +171,12 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo } } } + if len(currentLake) > 0 { + allLakes = append(allLakes, currentLake) + } } - return canvas, allLakePixels + return canvas, allLakes } type River struct { @@ -181,7 +185,7 @@ type River struct { Points []image.Point } -func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness float64, inputImage image.Image, lakePixels []image.Point, seed int64) (image.Image, []image.Point) { +func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness float64, inputImage image.Image, lakes [][]image.Point, seed int64) (image.Image, []image.Point) { if numRivers == 0 { return inputImage, nil } @@ -197,8 +201,12 @@ func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness avgDim := float64(width+height) / 2.0 isWater := make(map[image.Point]bool) - for _, p := range lakePixels { - isWater[p] = true + lakePixelMap := make(map[image.Point]int) + for i, lake := range lakes { + for _, p := range lake { + isWater[p] = true + lakePixelMap[p] = i + } } rivers := make([]River, numRivers) @@ -232,7 +240,14 @@ func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness for _, p := range path { if isWater[p] { - r.End = p + if lakeIndex, isLake := lakePixelMap[p]; isLake { + // Intersection is with a lake, find its center + lakeCenter := findCenter(lakes[lakeIndex]) + r.End = lakeCenter + } else { + // Intersection is with another river + r.End = p + } path = calculatePath(r.Start, r.End, curvyness/100.0, avgDim, randSrc, numControlPoints) break } @@ -242,6 +257,7 @@ func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness radius := riverWidthPx / 2.0 for _, p := range path { + // When drawing river pixels, add them to isWater to detect river-river intersections drawCircle(canvas, p, radius, color.RGBA{R: 0, G: 0, B: 255, A: 255}, &allRiverPixels, isWater) } r.Points = path @@ -250,6 +266,21 @@ func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness return canvas, allRiverPixels } +func findCenter(pixels []image.Point) image.Point { + if len(pixels) == 0 { + return image.Point{} + } + var sumX, sumY int + for _, p := range pixels { + sumX += p.X + sumY += p.Y + } + return image.Point{ + X: sumX / len(pixels), + Y: sumY / len(pixels), + } +} + func getPointOnEdge(width, height, edge int, randSrc *rand.Rand) image.Point { switch edge { case 0: // Top