added user settings to control river width variability and bank detail

This commit is contained in:
Grimsace
2026-02-18 14:33:09 -06:00
parent a6d9946b50
commit 6227ecb3fd
3 changed files with 79 additions and 43 deletions
+29 -2
View File
@@ -158,7 +158,7 @@ func main() {
// Step 3: Generating Rivers
var riverImage image.Image
riverImage, riverPixels = GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakes, seedProvider.Next(), noiseImg)
riverImage, riverPixels = GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakes, seedProvider.Next(), noiseImg, settings.RiverWidthVariability, settings.RiverEdgeRoughness)
finalImage := riverImage.(*image.RGBA)
@@ -338,6 +338,31 @@ func main() {
val, _ := riverCurvynessSlider.value.Get()
settings.RiverCurvyness = val
}))
riverWidthVariabilitySlider := newNumericInputSlider(0, 100, settings.RiverWidthVariability, "%.0f%%", "River Width Variability")
riverWidthVariabilitySlider.entry.OnChanged = func(s string) {
riverWidthVariabilitySlider.validate(s, func(hasError bool) {
errorStates["riverWidthVariability"] = hasError
updateGenerateBtnState()
})
}
riverWidthVariabilitySlider.value.AddListener(binding.NewDataListener(func() {
val, _ := riverWidthVariabilitySlider.value.Get()
settings.RiverWidthVariability = val
}))
riverEdgeRoughnessSlider := newNumericInputSlider(0, 100, settings.RiverEdgeRoughness, "%.0f%%", "River Edge Roughness")
riverEdgeRoughnessSlider.entry.OnChanged = func(s string) {
riverEdgeRoughnessSlider.validate(s, func(hasError bool) {
errorStates["riverEdgeRoughness"] = hasError
updateGenerateBtnState()
})
}
riverEdgeRoughnessSlider.value.AddListener(binding.NewDataListener(func() {
val, _ := riverEdgeRoughnessSlider.value.Get()
settings.RiverEdgeRoughness = val
}))
minTreeSizeSlider := newNumericInputSlider(1, 150, settings.MinTreeSize, "%.0fpx", "Min Tree Size")
minTreeSizeSlider.entry.OnChanged = func(s string) {
minTreeSizeSlider.validate(s, func(hasError bool) {
@@ -516,7 +541,7 @@ func main() {
progressBar.SetValue(3.0 / 11.0)
})
var riverImage image.Image
riverImage, riverPixels = GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakes, seedProvider.Next(), noiseImg)
riverImage, riverPixels = GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakes, seedProvider.Next(), noiseImg, settings.RiverWidthVariability, settings.RiverEdgeRoughness)
finalImage := riverImage.(*image.RGBA)
@@ -682,6 +707,8 @@ func main() {
minRiverWidthSlider,
maxRiverWidthSlider,
riverCurvynessSlider,
riverWidthVariabilitySlider,
riverEdgeRoughnessSlider,
))
roadsTab := container.NewTabItem("Roads", container.NewVBox(
+4
View File
@@ -23,6 +23,8 @@ type Settings struct {
MinRiverWidth float64 `json:"min_river_width"`
MaxRiverWidth float64 `json:"max_river_width"`
RiverCurvyness float64 `json:"river_curvyness"`
RiverWidthVariability float64 `json:"river_width_variability"`
RiverEdgeRoughness float64 `json:"river_edge_roughness"`
// Tree settings
MinTreeSize float64 `json:"min_tree_size"`
@@ -122,6 +124,8 @@ func LoadSettings() (*Settings, error) {
MinRiverWidth: 1,
MaxRiverWidth: 5,
RiverCurvyness: 50,
RiverWidthVariability: 50,
RiverEdgeRoughness: 50,
NumRoads: 100,
MinRoadWidth: 2,
MaxRoadWidth: 8,
+12 -7
View File
@@ -190,7 +190,7 @@ type River struct {
}
// GenerateRivers creates rivers on the map.
func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness float64, inputImage image.Image, lakes [][]image.Point, seed int64, heightmap image.Image) (image.Image, []image.Point) {
func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness float64, inputImage image.Image, lakes [][]image.Point, seed int64, heightmap image.Image, riverWidthVariability, riverEdgeRoughness float64) (image.Image, []image.Point) {
if numRivers == 0 {
return inputImage, nil
}
@@ -267,7 +267,7 @@ func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness
radius := riverWidthPx / 2.0
for _, p := range path {
drawCircle(canvas, p, radius, color.RGBA{R: 0, G: 0, B: 255, A: 255}, &allRiverPixels, isWater, heightmap)
drawCircle(canvas, p, radius, color.RGBA{R: 0, G: 0, B: 255, A: 255}, &allRiverPixels, isWater, heightmap, riverWidthVariability, riverEdgeRoughness)
}
r.Points = path
}
@@ -410,14 +410,19 @@ func getPointOnEdge(width, height, edge int, randSrc *rand.Rand) image.Point {
// drawCircle draws a circle on the image and adds its pixels to the given slice.
// The outer edges are roughened using dual sin waves for natural-looking banks.
func drawCircle(img *image.RGBA, center image.Point, radius float64, c color.Color, pixels *[]image.Point, isWater map[image.Point]bool, heightmap image.Image) {
// riverWidthVariability controls the amplitude of width changes (0-100%).
// riverEdgeRoughness controls the detail level of the edge roughness (0-100%).
func drawCircle(img *image.RGBA, center image.Point, radius float64, c color.Color, pixels *[]image.Point, isWater map[image.Point]bool, heightmap image.Image, riverWidthVariability, riverEdgeRoughness float64) {
bounds := img.Bounds()
// Calculate dual sin wave amplitudes for outer edge roughening
// Large amplitude represents major variations in river width
// Small amplitude is 1/4 of large for subtle details
largeAmplitude := radius * 0.25
smallAmplitude := largeAmplitude / 4.0
// Large amplitude represents major variations in river width (controlled by riverWidthVariability)
// At 0%, no width variation; at 100%, amplitude is 50% of radius
largeAmplitude := (radius * 0.5) * (riverWidthVariability / 100.0)
// Small amplitude is controlled by riverEdgeRoughness
// At 0%, no detail; at 100%, detail amplitude equals large amplitude
smallAmplitude := largeAmplitude * (riverEdgeRoughness / 100.0)
for y := int(math.Floor(float64(center.Y) - radius)); y <= int(math.Ceil(float64(center.Y)+radius)); y++ {
for x := int(math.Floor(float64(center.X) - radius)); x <= int(math.Ceil(float64(center.X)+radius)); x++ {