added user settings to control river width variability and bank detail
This commit is contained in:
@@ -158,7 +158,7 @@ func main() {
|
|||||||
// Step 3: Generating Rivers
|
// Step 3: Generating Rivers
|
||||||
var riverImage image.Image
|
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)
|
finalImage := riverImage.(*image.RGBA)
|
||||||
|
|
||||||
@@ -338,6 +338,31 @@ func main() {
|
|||||||
val, _ := riverCurvynessSlider.value.Get()
|
val, _ := riverCurvynessSlider.value.Get()
|
||||||
settings.RiverCurvyness = val
|
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 := newNumericInputSlider(1, 150, settings.MinTreeSize, "%.0fpx", "Min Tree Size")
|
||||||
minTreeSizeSlider.entry.OnChanged = func(s string) {
|
minTreeSizeSlider.entry.OnChanged = func(s string) {
|
||||||
minTreeSizeSlider.validate(s, func(hasError bool) {
|
minTreeSizeSlider.validate(s, func(hasError bool) {
|
||||||
@@ -516,7 +541,7 @@ func main() {
|
|||||||
progressBar.SetValue(3.0 / 11.0)
|
progressBar.SetValue(3.0 / 11.0)
|
||||||
})
|
})
|
||||||
var riverImage image.Image
|
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)
|
finalImage := riverImage.(*image.RGBA)
|
||||||
|
|
||||||
@@ -682,6 +707,8 @@ func main() {
|
|||||||
minRiverWidthSlider,
|
minRiverWidthSlider,
|
||||||
maxRiverWidthSlider,
|
maxRiverWidthSlider,
|
||||||
riverCurvynessSlider,
|
riverCurvynessSlider,
|
||||||
|
riverWidthVariabilitySlider,
|
||||||
|
riverEdgeRoughnessSlider,
|
||||||
))
|
))
|
||||||
|
|
||||||
roadsTab := container.NewTabItem("Roads", container.NewVBox(
|
roadsTab := container.NewTabItem("Roads", container.NewVBox(
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ type Settings struct {
|
|||||||
MinRiverWidth float64 `json:"min_river_width"`
|
MinRiverWidth float64 `json:"min_river_width"`
|
||||||
MaxRiverWidth float64 `json:"max_river_width"`
|
MaxRiverWidth float64 `json:"max_river_width"`
|
||||||
RiverCurvyness float64 `json:"river_curvyness"`
|
RiverCurvyness float64 `json:"river_curvyness"`
|
||||||
|
RiverWidthVariability float64 `json:"river_width_variability"`
|
||||||
|
RiverEdgeRoughness float64 `json:"river_edge_roughness"`
|
||||||
|
|
||||||
// Tree settings
|
// Tree settings
|
||||||
MinTreeSize float64 `json:"min_tree_size"`
|
MinTreeSize float64 `json:"min_tree_size"`
|
||||||
@@ -122,6 +124,8 @@ func LoadSettings() (*Settings, error) {
|
|||||||
MinRiverWidth: 1,
|
MinRiverWidth: 1,
|
||||||
MaxRiverWidth: 5,
|
MaxRiverWidth: 5,
|
||||||
RiverCurvyness: 50,
|
RiverCurvyness: 50,
|
||||||
|
RiverWidthVariability: 50,
|
||||||
|
RiverEdgeRoughness: 50,
|
||||||
NumRoads: 100,
|
NumRoads: 100,
|
||||||
MinRoadWidth: 2,
|
MinRoadWidth: 2,
|
||||||
MaxRoadWidth: 8,
|
MaxRoadWidth: 8,
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ type River struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRivers creates rivers on the map.
|
// 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 {
|
if numRivers == 0 {
|
||||||
return inputImage, nil
|
return inputImage, nil
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,7 @@ func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness
|
|||||||
radius := riverWidthPx / 2.0
|
radius := riverWidthPx / 2.0
|
||||||
|
|
||||||
for _, p := range path {
|
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
|
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.
|
// 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.
|
// 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()
|
bounds := img.Bounds()
|
||||||
|
|
||||||
// Calculate dual sin wave amplitudes for outer edge roughening
|
// Calculate dual sin wave amplitudes for outer edge roughening
|
||||||
// Large amplitude represents major variations in river width
|
// Large amplitude represents major variations in river width (controlled by riverWidthVariability)
|
||||||
// Small amplitude is 1/4 of large for subtle details
|
// At 0%, no width variation; at 100%, amplitude is 50% of radius
|
||||||
largeAmplitude := radius * 0.25
|
largeAmplitude := (radius * 0.5) * (riverWidthVariability / 100.0)
|
||||||
smallAmplitude := largeAmplitude / 4.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 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++ {
|
for x := int(math.Floor(float64(center.X) - radius)); x <= int(math.Ceil(float64(center.X)+radius)); x++ {
|
||||||
|
|||||||
Reference in New Issue
Block a user