Address fill-mode review: grid edge inset + drop dead coverage append
Build image / build-and-push (push) Successful in 18s
Build image / build-and-push (push) Successful in 18s
Gadfly findings on #95: - Correctness (3 models): the shared inset formula radius-spacing/2 collapses to 0 in grid mode (radius = spacing/2), so grid's outer row planted flush on / overhanging the bed edge instead of the half-spacing in that the rule wants. The inset genuinely differs by layout — a grid plant sits AT the plop centre (inset spacing/2), a clump's plants reach its rim (inset radius-spacing/2, overhanging by a half). Split it into a new edgeInset(radius, spacing, layout); hexCenters now takes a precomputed inset and is pure geometry (no spacing/layout knowledge). Regression guard: grid plants land at ±25 on a 60cm bed, not ±30. - Performance (2 findings): the in-loop `existing = append(existing, *p)` was dead — every plop in one fill shares a radius and sits on a distinct lattice point, and a plop is "covered" only when wholly inside another, impossible between equal-radius circles at different centres. Removing it stops the coveredByExisting scan growing during the fill (an empty-bed grid fill's check was needlessly quadratic in the plop count). - Docs: FillRegion/fillLoaded/hexCenters comments and the DESIGN.md bullets updated for plopRadiusFor/edgeInset (were still citing defaultPlopRadius and "written out in hexCenters"). - Test hygiene: split the grid + bad-layout cases out of TestFillAndClearAPI into TestFillLayoutAPI (one concern per test). The enum-tag finding is a non-issue: majordomo's DefineTool derives its arg schema from the same struct-tag reflection as Generate (proven by the vision SeedPacket enum), and the service validates mode regardless. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -95,22 +95,39 @@ func TestFillAndClearAPI(t *testing.T) {
|
||||
if n := int(decodeMap(t, w.Body.Bytes())["cleared"].(float64)); n != 0 {
|
||||
t.Errorf("second clear removed %d, want 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
// Grid layout (#77) packs individual plants at true spacing — many more, small
|
||||
// plops than the clump default on the same bed.
|
||||
// TestFillLayoutAPI covers the layout selector (#77): grid packs denser than the
|
||||
// clump default, and an unknown layout is a 400 rather than a silent clump fill.
|
||||
func TestFillLayoutAPI(t *testing.T) {
|
||||
r := authEngine(t, localCfg())
|
||||
cookie := registerAndCookie(t, r, "[email protected]")
|
||||
_, objID, plantID := seedFillableBed(t, r, cookie, 200, 200, 20)
|
||||
|
||||
// Clump (default) for the baseline count.
|
||||
w := doJSON(t, r, http.MethodPost, fillPath(objID), map[string]any{
|
||||
"plantId": plantID, "region": "all",
|
||||
}, cookie)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("clump fill: status %d, body %s", w.Code, w.Body.String())
|
||||
}
|
||||
clumpN := int(decodeMap(t, w.Body.Bytes())["created"].(float64))
|
||||
if w := doJSON(t, r, http.MethodPost, clearPath(objID), nil, cookie); w.Code != http.StatusOK {
|
||||
t.Fatalf("clear between fills: %d", w.Code)
|
||||
}
|
||||
|
||||
// Grid packs individual plants at true spacing — many more, small plops.
|
||||
w = doJSON(t, r, http.MethodPost, fillPath(objID), map[string]any{
|
||||
"plantId": plantID, "region": "all", "layout": "grid",
|
||||
}, cookie)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("grid fill: status %d, body %s", w.Code, w.Body.String())
|
||||
}
|
||||
if gridN := int(decodeMap(t, w.Body.Bytes())["created"].(float64)); gridN <= created {
|
||||
t.Errorf("grid fill created %d, want more than the clump fill's %d", gridN, created)
|
||||
if gridN := int(decodeMap(t, w.Body.Bytes())["created"].(float64)); gridN <= clumpN {
|
||||
t.Errorf("grid fill created %d, want more than the clump fill's %d", gridN, clumpN)
|
||||
}
|
||||
|
||||
// An unknown layout is a 400, not a silent clump fill.
|
||||
if w := doJSON(t, r, http.MethodPost, clearPath(objID), nil, cookie); w.Code != http.StatusOK {
|
||||
t.Fatalf("clear before bad-layout: %d", w.Code)
|
||||
}
|
||||
if w := doJSON(t, r, http.MethodPost, fillPath(objID), map[string]any{
|
||||
"plantId": plantID, "region": "all", "layout": "spiral",
|
||||
}, cookie); w.Code != http.StatusBadRequest {
|
||||
|
||||
Reference in New Issue
Block a user