added multiple lake shapes
This commit is contained in:
@@ -153,7 +153,7 @@ func main() {
|
|||||||
// Step 2: Generating Lakes
|
// Step 2: Generating Lakes
|
||||||
var lakeImage image.Image
|
var lakeImage image.Image
|
||||||
|
|
||||||
lakeImage, lakes = GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, seedProvider.Next(), settings.LakeEdgeRoughness)
|
lakeImage, lakes = GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, seedProvider.Next(), settings.LakeEdgeRoughness, settings.LakeShape)
|
||||||
|
|
||||||
// Step 3: Generating Rivers
|
// Step 3: Generating Rivers
|
||||||
var riverImage image.Image
|
var riverImage image.Image
|
||||||
@@ -304,6 +304,12 @@ func main() {
|
|||||||
settings.LakeEdgeRoughness = val
|
settings.LakeEdgeRoughness = val
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
lakeShapeLabel := widget.NewLabel("Lake Shape:")
|
||||||
|
lakeShapeSelect := widget.NewSelect([]string{"circle", "oval", "procedural"}, func(s string) {
|
||||||
|
settings.LakeShape = s
|
||||||
|
})
|
||||||
|
lakeShapeSelect.SetSelected(settings.LakeShape)
|
||||||
|
|
||||||
riversSlider := newNumericInputSlider(0, 5, float64(settings.Rivers), "%.0f", "Rivers")
|
riversSlider := newNumericInputSlider(0, 5, float64(settings.Rivers), "%.0f", "Rivers")
|
||||||
riversSlider.entry.OnChanged = func(s string) {
|
riversSlider.entry.OnChanged = func(s string) {
|
||||||
riversSlider.validate(s, func(hasError bool) {
|
riversSlider.validate(s, func(hasError bool) {
|
||||||
@@ -545,7 +551,7 @@ func main() {
|
|||||||
progressBar.SetValue(2.0 / 11.0)
|
progressBar.SetValue(2.0 / 11.0)
|
||||||
})
|
})
|
||||||
var lakeImage image.Image
|
var lakeImage image.Image
|
||||||
lakeImage, lakes = GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, seedProvider.Next(), settings.LakeEdgeRoughness)
|
lakeImage, lakes = GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, seedProvider.Next(), settings.LakeEdgeRoughness, settings.LakeShape)
|
||||||
|
|
||||||
// Step 3: Generating Rivers
|
// Step 3: Generating Rivers
|
||||||
|
|
||||||
@@ -714,6 +720,8 @@ func main() {
|
|||||||
lakeSizeLowerSlider,
|
lakeSizeLowerSlider,
|
||||||
lakeSizeUpperSlider,
|
lakeSizeUpperSlider,
|
||||||
lakeEdgeRoughnessSlider,
|
lakeEdgeRoughnessSlider,
|
||||||
|
lakeShapeLabel,
|
||||||
|
lakeShapeSelect,
|
||||||
|
|
||||||
widget.NewLabel(""), // Spacer
|
widget.NewLabel(""), // Spacer
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Settings struct {
|
|||||||
LakeSizeLower float64 `json:"lake_size_lower"`
|
LakeSizeLower float64 `json:"lake_size_lower"`
|
||||||
LakeSizeUpper float64 `json:"lake_size_upper"`
|
LakeSizeUpper float64 `json:"lake_size_upper"`
|
||||||
LakeEdgeRoughness float64 `json:"lake_edge_roughness"`
|
LakeEdgeRoughness float64 `json:"lake_edge_roughness"`
|
||||||
|
LakeShape string `json:"lake_shape"`
|
||||||
Rivers int `json:"rivers"`
|
Rivers int `json:"rivers"`
|
||||||
MinRiverWidth float64 `json:"min_river_width"`
|
MinRiverWidth float64 `json:"min_river_width"`
|
||||||
MaxRiverWidth float64 `json:"max_river_width"`
|
MaxRiverWidth float64 `json:"max_river_width"`
|
||||||
@@ -117,6 +118,7 @@ func LoadSettings() (*Settings, error) {
|
|||||||
LakeSizeLower: 1,
|
LakeSizeLower: 1,
|
||||||
LakeSizeUpper: 5,
|
LakeSizeUpper: 5,
|
||||||
LakeEdgeRoughness: 50,
|
LakeEdgeRoughness: 50,
|
||||||
|
LakeShape: "circle",
|
||||||
MinTreeSize: 5,
|
MinTreeSize: 5,
|
||||||
MaxTreeSize: 20,
|
MaxTreeSize: 20,
|
||||||
TreeCoverage: 20,
|
TreeCoverage: 20,
|
||||||
@@ -162,6 +164,10 @@ func LoadSettings() (*Settings, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.LakeShape == "" {
|
||||||
|
settings.LakeShape = "circle"
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure BuildingShapeRatios is initialized
|
// Ensure BuildingShapeRatios is initialized
|
||||||
if settings.BuildingShapeRatios == nil {
|
if settings.BuildingShapeRatios == nil {
|
||||||
settings.BuildingShapeRatios = map[string]float64{
|
settings.BuildingShapeRatios = map[string]float64{
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ type riverParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateLakes creates lakes on the map using a priority queue growth algorithm
|
// GenerateLakes creates lakes on the map using a priority queue growth algorithm
|
||||||
func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper float64, seed int64, lakeEdgeRoughness float64) (image.Image, [][]image.Point) {
|
func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper float64, seed int64, lakeEdgeRoughness float64, lakeShape string) (image.Image, [][]image.Point) {
|
||||||
canvas := image.NewRGBA(image.Rect(0, 0, width, height))
|
canvas := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||||
draw.Draw(canvas, canvas.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
|
draw.Draw(canvas, canvas.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
|
||||||
|
|
||||||
@@ -95,6 +95,12 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo
|
|||||||
totalArea := float64(width * height)
|
totalArea := float64(width * height)
|
||||||
noiseGen := opensimplex.New(seed)
|
noiseGen := opensimplex.New(seed)
|
||||||
|
|
||||||
|
type lakeBlob struct {
|
||||||
|
dx, dy float64
|
||||||
|
a, b float64
|
||||||
|
angle float64
|
||||||
|
}
|
||||||
|
|
||||||
// Generate each lake
|
// Generate each lake
|
||||||
for i := range numLakes {
|
for i := range numLakes {
|
||||||
if i >= len(chunkIndices) {
|
if i >= len(chunkIndices) {
|
||||||
@@ -103,12 +109,70 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo
|
|||||||
|
|
||||||
var currentLake []image.Point
|
var currentLake []image.Point
|
||||||
|
|
||||||
// Randomize lake size within specified range
|
getRadius := func() float64 {
|
||||||
lakeSize := lakeSizeLower
|
s := lakeSizeLower
|
||||||
if lakeSizeUpper > lakeSizeLower {
|
if lakeSizeUpper > lakeSizeLower {
|
||||||
lakeSize = lakeSizeLower + randSrc.Float64()*(lakeSizeUpper-lakeSizeLower)
|
s = lakeSizeLower + randSrc.Float64()*(lakeSizeUpper-lakeSizeLower)
|
||||||
|
}
|
||||||
|
// Maintain the same scale heuristic as original code
|
||||||
|
pixels := totalArea * (s / 100.0) / 2
|
||||||
|
if pixels < 1 {
|
||||||
|
pixels = 1
|
||||||
|
}
|
||||||
|
return math.Sqrt(pixels / math.Pi)
|
||||||
}
|
}
|
||||||
targetPixelsPerLake := int(math.Round(totalArea*(lakeSize/100.0))) / 2
|
|
||||||
|
var blobs []lakeBlob
|
||||||
|
var primaryRadius float64
|
||||||
|
|
||||||
|
switch lakeShape {
|
||||||
|
case "oval":
|
||||||
|
r1 := getRadius()
|
||||||
|
r2 := getRadius()
|
||||||
|
angle := randSrc.Float64() * math.Pi * 2
|
||||||
|
blobs = append(blobs, lakeBlob{0, 0, r1, r2, angle})
|
||||||
|
primaryRadius = (r1 + r2) / 2
|
||||||
|
case "procedural":
|
||||||
|
complexity := 2 + randSrc.Intn(3) // 2 to 4 blobs
|
||||||
|
r1 := getRadius()
|
||||||
|
r2 := r1
|
||||||
|
if randSrc.Float64() > 0.5 {
|
||||||
|
r2 = getRadius()
|
||||||
|
}
|
||||||
|
angle := randSrc.Float64() * math.Pi * 2
|
||||||
|
blobs = append(blobs, lakeBlob{0, 0, r1, r2, angle})
|
||||||
|
primaryRadius = (r1 + r2) / 2
|
||||||
|
|
||||||
|
for k := 1; k < complexity; k++ {
|
||||||
|
parent := blobs[randSrc.Intn(len(blobs))]
|
||||||
|
subR1 := getRadius() * 0.7
|
||||||
|
subR2 := subR1
|
||||||
|
if randSrc.Float64() > 0.5 {
|
||||||
|
subR2 = getRadius() * 0.7
|
||||||
|
}
|
||||||
|
subAngle := randSrc.Float64() * math.Pi * 2
|
||||||
|
dir := randSrc.Float64() * math.Pi * 2
|
||||||
|
dist := (parent.a + subR1) * 0.6 // Overlap
|
||||||
|
newX := parent.dx + math.Cos(dir)*dist
|
||||||
|
newY := parent.dy + math.Sin(dir)*dist
|
||||||
|
blobs = append(blobs, lakeBlob{newX, newY, subR1, subR2, subAngle})
|
||||||
|
}
|
||||||
|
default: // "circle"
|
||||||
|
r := getRadius()
|
||||||
|
blobs = append(blobs, lakeBlob{0, 0, r, r, 0})
|
||||||
|
primaryRadius = r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate estimated target pixels based on blobs (rough approximation)
|
||||||
|
// Since we grow until count is reached, we can just sum areas and discount for overlap
|
||||||
|
estimatedArea := 0.0
|
||||||
|
for _, b := range blobs {
|
||||||
|
estimatedArea += math.Pi * b.a * b.b
|
||||||
|
}
|
||||||
|
if len(blobs) > 1 {
|
||||||
|
estimatedArea *= 0.8 // Heuristic for overlap reduction
|
||||||
|
}
|
||||||
|
targetPixelsPerLake := int(estimatedArea)
|
||||||
if targetPixelsPerLake <= 0 {
|
if targetPixelsPerLake <= 0 {
|
||||||
targetPixelsPerLake = 1
|
targetPixelsPerLake = 1
|
||||||
}
|
}
|
||||||
@@ -141,17 +205,35 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo
|
|||||||
// Setup noise generation for natural lake shapes
|
// Setup noise generation for natural lake shapes
|
||||||
seedX := randSrc.Float64() * 10000.0
|
seedX := randSrc.Float64() * 10000.0
|
||||||
seedY := randSrc.Float64() * 10000.0
|
seedY := randSrc.Float64() * 10000.0
|
||||||
radius := math.Sqrt(float64(targetPixelsPerLake) / math.Pi)
|
|
||||||
noiseFreq := 0.01 + (0.2 / (radius + 1.0))
|
noiseFreq := 0.01 + (0.2 / (primaryRadius + 1.0))
|
||||||
|
|
||||||
// Score function determines which pixels to add to lake
|
// Score function determines which pixels to add to lake
|
||||||
getScore := func(pt image.Point) float64 {
|
getScore := func(pt image.Point) float64 {
|
||||||
dx, dy := pt.X-startPt.X, pt.Y-startPt.Y
|
dxGlobal := float64(pt.X - startPt.X)
|
||||||
dist := math.Sqrt(float64(dx*dx + dy*dy))
|
dyGlobal := float64(pt.Y - startPt.Y)
|
||||||
distPenalty := math.Pow(dist/radius, 3.0)
|
|
||||||
|
minNormalizedDist := 1e9
|
||||||
|
|
||||||
|
for _, b := range blobs {
|
||||||
|
bdx := dxGlobal - b.dx
|
||||||
|
bdy := dyGlobal - b.dy
|
||||||
|
|
||||||
|
cosA := math.Cos(-b.angle)
|
||||||
|
sinA := math.Sin(-b.angle)
|
||||||
|
rx := bdx*cosA - bdy*sinA
|
||||||
|
ry := bdx*sinA + bdy*cosA
|
||||||
|
|
||||||
|
d := math.Sqrt(math.Pow(rx/b.a, 2) + math.Pow(ry/b.b, 2))
|
||||||
|
if d < minNormalizedDist {
|
||||||
|
minNormalizedDist = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
distPenalty := math.Pow(minNormalizedDist, 3.0)
|
||||||
|
|
||||||
if lakeEdgeRoughness > 0 {
|
if lakeEdgeRoughness > 0 {
|
||||||
noise := noiseGen.Eval2(seedX+float64(dx)*noiseFreq, seedY+float64(dy)*noiseFreq)
|
noise := noiseGen.Eval2(seedX+float64(dxGlobal)*noiseFreq, seedY+float64(dyGlobal)*noiseFreq)
|
||||||
noiseContribution := noise * (lakeEdgeRoughness / 100.0)
|
noiseContribution := noise * (lakeEdgeRoughness / 100.0)
|
||||||
return noiseContribution - distPenalty
|
return noiseContribution - distPenalty
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user