From 6eb4dad5c8c2284dfe1764214baac32872a4429d Mon Sep 17 00:00:00 2001 From: Grimsace Date: Fri, 6 Feb 2026 09:01:09 -0600 Subject: [PATCH] added mixed building generation --- buildings.go | 32 +++++++++++- main.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++- settings.go | 21 +++++++- 3 files changed, 188 insertions(+), 4 deletions(-) diff --git a/buildings.go b/buildings.go index ea05248..c5d6146 100644 --- a/buildings.go +++ b/buildings.go @@ -118,7 +118,11 @@ func GenerateBuildings(img *image.RGBA, width, height int, settings *Settings, r // Attempt to create a building at the selected center size := settings.MinBuildingSize + randSrc.Float64()*(settings.MaxBuildingSize-settings.MinBuildingSize) - pixels, ok := getBuildingPixels(center, size, settings.BuildingShape, isWater, isRoad, isBuilding, width, height, randSrc) + shape := settings.BuildingShape + if shape == "mixed" { + shape = chooseShape(randSrc, settings.BuildingShapeRatios) + } + pixels, ok := getBuildingPixels(center, size, shape, isWater, isRoad, isBuilding, width, height, randSrc) if ok { // If successful, draw the building and update data structures for _, p := range pixels { @@ -134,6 +138,32 @@ func GenerateBuildings(img *image.RGBA, width, height int, settings *Settings, r return buildingPixels } +// chooseShape selects a building shape based on the provided ratios. +func chooseShape(randSrc *rand.Rand, ratios map[string]float64) string { + // Create a slice of shapes and their cumulative weights + var shapes []string + var weights []float64 + var cumulativeWeight float64 + for shape, weight := range ratios { + shapes = append(shapes, shape) + cumulativeWeight += weight + weights = append(weights, cumulativeWeight) + } + + // Generate a random number between 0 and the total weight + randNum := randSrc.Float64() * cumulativeWeight + + // Find the shape corresponding to the random number + for i, weight := range weights { + if randNum < weight { + return shapes[i] + } + } + + // Default to the first shape if something goes wrong + return shapes[0] +} + // getBuildingPixels determines the pixels for a single building based on its shape and checks for collisions. func getBuildingPixels(center image.Point, size float64, shape string, isWater, isRoad, isBuilding map[image.Point]bool, width, height int, randSrc *rand.Rand) ([]image.Point, bool) { var pixels []image.Point diff --git a/main.go b/main.go index a3bdcea..0c486c3 100644 --- a/main.go +++ b/main.go @@ -650,11 +650,147 @@ func main() { buildingDistributionSlider.SetValue(settings.BuildingDistribution) buildingShapeLabel := widget.NewLabel("Building Shape:") - buildingShapeSelect := widget.NewSelect([]string{"squares", "circles", "rectangles"}, func(s string) { + buildingShapeSelect := widget.NewSelect([]string{"squares", "circles", "rectangles", "mixed"}, func(s string) { settings.BuildingShape = s }) buildingShapeSelect.SetSelected(settings.BuildingShape) + // Create sliders for building shape ratios (visible only when "mixed" is selected) + squareRatioSlider := widget.NewSlider(0, 100) + circleRatioSlider := widget.NewSlider(0, 100) + rectangleRatioSlider := widget.NewSlider(0, 100) + + squareRatioLabel := widget.NewLabel(fmt.Sprintf("Squares: %.0f%%", settings.BuildingShapeRatios["squares"])) + circleRatioLabel := widget.NewLabel(fmt.Sprintf("Circles: %.0f%%", settings.BuildingShapeRatios["circles"])) + rectangleRatioLabel := widget.NewLabel(fmt.Sprintf("Rectangles: %.0f%%", settings.BuildingShapeRatios["rectangles"])) + + squareRatioSlider.SetValue(settings.BuildingShapeRatios["squares"]) + circleRatioSlider.SetValue(settings.BuildingShapeRatios["circles"]) + rectangleRatioSlider.SetValue(settings.BuildingShapeRatios["rectangles"]) + + ratioContainer := container.NewVBox( + squareRatioLabel, + squareRatioSlider, + circleRatioLabel, + circleRatioSlider, + rectangleRatioLabel, + rectangleRatioSlider, + ) + + updateRatioSliders := func() { + squareRatioSlider.SetValue(settings.BuildingShapeRatios["squares"]) + circleRatioSlider.SetValue(settings.BuildingShapeRatios["circles"]) + rectangleRatioSlider.SetValue(settings.BuildingShapeRatios["rectangles"]) + squareRatioLabel.SetText(fmt.Sprintf("Squares: %.0f%%", settings.BuildingShapeRatios["squares"])) + circleRatioLabel.SetText(fmt.Sprintf("Circles: %.0f%%", settings.BuildingShapeRatios["circles"])) + rectangleRatioLabel.SetText(fmt.Sprintf("Rectangles: %.0f%%", settings.BuildingShapeRatios["rectangles"])) + } + + squareRatioSlider.OnChanged = func(val float64) { + if settings.BuildingShape != "mixed" { + return + } + oldVal := settings.BuildingShapeRatios["squares"] + diff := val - oldVal + settings.BuildingShapeRatios["squares"] = val + settings.BuildingShapeRatios["circles"] -= diff / 2 + settings.BuildingShapeRatios["rectangles"] -= diff / 2 + + // Clamp values to 0-100 range + if settings.BuildingShapeRatios["circles"] < 0 { + settings.BuildingShapeRatios["rectangles"] += settings.BuildingShapeRatios["circles"] + settings.BuildingShapeRatios["circles"] = 0 + } + if settings.BuildingShapeRatios["rectangles"] < 0 { + settings.BuildingShapeRatios["circles"] += settings.BuildingShapeRatios["rectangles"] + settings.BuildingShapeRatios["rectangles"] = 0 + } + if settings.BuildingShapeRatios["circles"] > 100 { + settings.BuildingShapeRatios["rectangles"] += settings.BuildingShapeRatios["circles"] - 100 + settings.BuildingShapeRatios["circles"] = 100 + } + if settings.BuildingShapeRatios["rectangles"] > 100 { + settings.BuildingShapeRatios["circles"] += settings.BuildingShapeRatios["rectangles"] - 100 + settings.BuildingShapeRatios["rectangles"] = 100 + } + + updateRatioSliders() + } + + circleRatioSlider.OnChanged = func(val float64) { + if settings.BuildingShape != "mixed" { + return + } + oldVal := settings.BuildingShapeRatios["circles"] + diff := val - oldVal + settings.BuildingShapeRatios["circles"] = val + settings.BuildingShapeRatios["squares"] -= diff / 2 + settings.BuildingShapeRatios["rectangles"] -= diff / 2 + // Clamp values to 0-100 range + if settings.BuildingShapeRatios["squares"] < 0 { + settings.BuildingShapeRatios["rectangles"] += settings.BuildingShapeRatios["squares"] + settings.BuildingShapeRatios["squares"] = 0 + } + if settings.BuildingShapeRatios["rectangles"] < 0 { + settings.BuildingShapeRatios["squares"] += settings.BuildingShapeRatios["rectangles"] + settings.BuildingShapeRatios["rectangles"] = 0 + } + if settings.BuildingShapeRatios["squares"] > 100 { + settings.BuildingShapeRatios["rectangles"] += settings.BuildingShapeRatios["squares"] - 100 + settings.BuildingShapeRatios["squares"] = 100 + } + if settings.BuildingShapeRatios["rectangles"] > 100 { + settings.BuildingShapeRatios["squares"] += settings.BuildingShapeRatios["rectangles"] - 100 + settings.BuildingShapeRatios["rectangles"] = 100 + } + + updateRatioSliders() + } + + rectangleRatioSlider.OnChanged = func(val float64) { + if settings.BuildingShape != "mixed" { + return + } + oldVal := settings.BuildingShapeRatios["rectangles"] + diff := val - oldVal + settings.BuildingShapeRatios["rectangles"] = val + settings.BuildingShapeRatios["squares"] -= diff / 2 + settings.BuildingShapeRatios["circles"] -= diff / 2 + + // Clamp values to 0-100 range + if settings.BuildingShapeRatios["squares"] < 0 { + settings.BuildingShapeRatios["circles"] += settings.BuildingShapeRatios["squares"] + settings.BuildingShapeRatios["squares"] = 0 + } + if settings.BuildingShapeRatios["circles"] < 0 { + settings.BuildingShapeRatios["squares"] += settings.BuildingShapeRatios["circles"] + settings.BuildingShapeRatios["circles"] = 0 + } + if settings.BuildingShapeRatios["squares"] > 100 { + settings.BuildingShapeRatios["circles"] += settings.BuildingShapeRatios["squares"] - 100 + settings.BuildingShapeRatios["squares"] = 100 + } + if settings.BuildingShapeRatios["circles"] > 100 { + settings.BuildingShapeRatios["squares"] += settings.BuildingShapeRatios["circles"] - 100 + settings.BuildingShapeRatios["circles"] = 100 + } + + updateRatioSliders() + } + + buildingShapeSelect.OnChanged = func(s string) { + settings.BuildingShape = s + if s == "mixed" { + ratioContainer.Show() + } else { + ratioContainer.Hide() + } + } + // Initial visibility check + if settings.BuildingShape != "mixed" { + ratioContainer.Hide() + } + buildingsTab := container.NewTabItem("Buildings", container.NewVBox( numBuildingsLabel, numBuildingsSlider, @@ -666,6 +802,7 @@ func main() { buildingDistributionSlider, buildingShapeLabel, buildingShapeSelect, + ratioContainer, )) imageTab := container.NewTabItem("Image", container.NewVBox( diff --git a/settings.go b/settings.go index b994280..86c3779 100644 --- a/settings.go +++ b/settings.go @@ -45,6 +45,9 @@ type Settings struct { BuildingDistribution float64 `json:"building_distribution"` BuildingShape string `json:"building_shape"` + // Ratios for mixed building shapes + BuildingShapeRatios map[string]float64 `json:"building_shape_ratios"` + // General settings Seed int64 `json:"seed"` LastExportPath string `json:"last_export_path"` @@ -123,8 +126,13 @@ func LoadSettings() (*Settings, error) { MinBuildingSize: 10, MaxBuildingSize: 30, BuildingDistribution: 20, - BuildingShape: "squares", - LastExportPath: homeDir, + BuildingShape: "mixed", + BuildingShapeRatios: map[string]float64{ + "squares": 40, + "circles": 30, + "rectangles": 30, + }, + LastExportPath: homeDir, }, nil } return nil, err @@ -138,6 +146,15 @@ func LoadSettings() (*Settings, error) { return nil, err } + // Ensure BuildingShapeRatios is initialized + if settings.BuildingShapeRatios == nil { + settings.BuildingShapeRatios = map[string]float64{ + "squares": 40, + "circles": 30, + "rectangles": 30, + } + } + // Ensure LastExportPath is set to a default value if it's empty if settings.LastExportPath == "" { homeDir, err := os.UserHomeDir()