Reported from real use: filling a bed with garlic leaves the first plop too far from the corner, and the corner plop "doesn't have to be a full diameter from the edge when you plant".
That is correct, and there are two separate defects behind it.
What's wrong
hexCenters anchors the lattice one radius inside the region's min corner and then keeps any candidate whose centre is in the region. On a 122 × 244 cm (4 × 8 ft) bed of garlic (15 cm spacing → plop radius 22.5, pitch 45):
BEFORE — 15 plops
row 0: 3 plops | W gap +0.0 cm | E gap -13.0 cm
row 1: 2 plops | W gap +22.5 cm | E gap +9.5 cm
row 2: 3 plops | W gap +0.0 cm | E gap -13.0 cm
** worst overhang outside the bed: 13.0 cm
Staggered rows start a full pitch in. Odd rows are offset by radius, so their first centre lands at MinX + 2·radius — a bare strip a whole plop wide down the west side of every other row.
All the slack piles up on the far edge, where plops then hang 13 cm outside the bed. Nothing clips them, so they draw over the bed outline.
The rule
Spacing is a constraint between neighbouring plants competing for the same soil, light and water. A bed edge is not a competitor, so the outer row only owes it half the spacing — the half it would otherwise share with a neighbour.
This is the arithmetic inside every square-foot-gardening chart: 4 per square is 6" apart and 3" from the square's edge; 9 per square is 4" apart and 2" from the edge. Garlic at 9 per square goes in 2" from the frame, not 6".
The wrinkle specific to pansy: a plop is a clump, not a plant.defaultPlopRadius is 1.5 × spacing, so a plop is three spacings across and its plants sit out to its rim. Keeping the whole circle inside the bed would inset the outer row by a full 1.5 spacings — three times what the rule allows. So the clump is allowed to cross the edge by up to half a spacing, which puts its outermost plants exactly the half-spacing from the edge the rule asks for. Capped there, and nowhere near the full radius: a clump mostly outside the bed is a drawing of plants in the path.
The fix
Centre the lattice in the region, with the minimum centre-inset at radius − spacing/2 rather than a full radius. Same bed:
AFTER — 15 plops
row 0: 3 plops | W gap -6.5 cm | E gap -6.5 cm
row 1: 2 plops | W gap +16.0 cm | E gap +16.0 cm
row 2: 3 plops | W gap -6.5 cm | E gap -6.5 cm
** worst overhang outside the bed: 6.5 cm
Same density, symmetric, and the overhang is now deliberate and inside the 7.5 cm half-spacing budget instead of an accidental 13 cm on one side only.
The stagger falls out of the centring for free: an offset row holds one fewer plop, and centring that run puts it exactly half a pitch off its neighbours.
Notes
TestFillRegionDeterministicPacking expected 4 plops in a 60 × 60 bed; the fourth was centred on the east edge with half of it outside, well past the half-spacing budget. It is 3 now — that is the fix working, not a regression in it.
Region.contains becomes dead: the lattice is bounded by construction, so the loop is count-bounded rather than condition-bounded.
Reported from real use: filling a bed with garlic leaves the first plop too far from the corner, and the corner plop "doesn't have to be a full diameter from the edge when you plant".
That is correct, and there are two separate defects behind it.
## What's wrong
`hexCenters` anchors the lattice one radius inside the region's **min corner** and then keeps any candidate whose *centre* is in the region. On a 122 × 244 cm (4 × 8 ft) bed of garlic (15 cm spacing → plop radius 22.5, pitch 45):
```
BEFORE — 15 plops
row 0: 3 plops | W gap +0.0 cm | E gap -13.0 cm
row 1: 2 plops | W gap +22.5 cm | E gap +9.5 cm
row 2: 3 plops | W gap +0.0 cm | E gap -13.0 cm
** worst overhang outside the bed: 13.0 cm
```
1. **Staggered rows start a full pitch in.** Odd rows are offset by `radius`, so their first centre lands at `MinX + 2·radius` — a bare strip a whole plop wide down the west side of every other row.
2. **All the slack piles up on the far edge**, where plops then hang 13 cm outside the bed. Nothing clips them, so they draw over the bed outline.
## The rule
Spacing is a constraint *between neighbouring plants* competing for the same soil, light and water. A bed edge is not a competitor, so the outer row only owes it **half the spacing** — the half it would otherwise share with a neighbour.
This is the arithmetic inside every square-foot-gardening chart: 4 per square is 6" apart and **3" from the square's edge**; 9 per square is 4" apart and 2" from the edge. [Garlic at 9 per square](https://squarefootgardening.org/2020/11/square-foot-garlic/) goes in 2" from the frame, not 6".
The wrinkle specific to pansy: **a plop is a clump, not a plant.** `defaultPlopRadius` is `1.5 × spacing`, so a plop is three spacings across and its plants sit out to its rim. Keeping the whole circle inside the bed would inset the outer row by a full 1.5 spacings — three times what the rule allows. So the clump is allowed to cross the edge by **up to half a spacing**, which puts its outermost plants exactly the half-spacing from the edge the rule asks for. Capped there, and nowhere near the full radius: a clump mostly outside the bed is a drawing of plants in the path.
## The fix
Centre the lattice in the region, with the minimum centre-inset at `radius − spacing/2` rather than a full `radius`. Same bed:
```
AFTER — 15 plops
row 0: 3 plops | W gap -6.5 cm | E gap -6.5 cm
row 1: 2 plops | W gap +16.0 cm | E gap +16.0 cm
row 2: 3 plops | W gap -6.5 cm | E gap -6.5 cm
** worst overhang outside the bed: 6.5 cm
```
Same density, symmetric, and the overhang is now deliberate and inside the 7.5 cm half-spacing budget instead of an accidental 13 cm on one side only.
The stagger falls out of the centring for free: an offset row holds one fewer plop, and centring *that* run puts it exactly half a pitch off its neighbours.
## Notes
- `TestFillRegionDeterministicPacking` expected 4 plops in a 60 × 60 bed; the fourth was centred **on** the east edge with half of it outside, well past the half-spacing budget. It is 3 now — that is the fix working, not a regression in it.
- `Region.contains` becomes dead: the lattice is bounded by construction, so the loop is count-bounded rather than condition-bounded.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Reported from real use: filling a bed with garlic leaves the first plop too far from the corner, and the corner plop "doesn't have to be a full diameter from the edge when you plant".
That is correct, and there are two separate defects behind it.
What's wrong
hexCentersanchors the lattice one radius inside the region's min corner and then keeps any candidate whose centre is in the region. On a 122 × 244 cm (4 × 8 ft) bed of garlic (15 cm spacing → plop radius 22.5, pitch 45):radius, so their first centre lands atMinX + 2·radius— a bare strip a whole plop wide down the west side of every other row.The rule
Spacing is a constraint between neighbouring plants competing for the same soil, light and water. A bed edge is not a competitor, so the outer row only owes it half the spacing — the half it would otherwise share with a neighbour.
This is the arithmetic inside every square-foot-gardening chart: 4 per square is 6" apart and 3" from the square's edge; 9 per square is 4" apart and 2" from the edge. Garlic at 9 per square goes in 2" from the frame, not 6".
The wrinkle specific to pansy: a plop is a clump, not a plant.
defaultPlopRadiusis1.5 × spacing, so a plop is three spacings across and its plants sit out to its rim. Keeping the whole circle inside the bed would inset the outer row by a full 1.5 spacings — three times what the rule allows. So the clump is allowed to cross the edge by up to half a spacing, which puts its outermost plants exactly the half-spacing from the edge the rule asks for. Capped there, and nowhere near the full radius: a clump mostly outside the bed is a drawing of plants in the path.The fix
Centre the lattice in the region, with the minimum centre-inset at
radius − spacing/2rather than a fullradius. Same bed:Same density, symmetric, and the overhang is now deliberate and inside the 7.5 cm half-spacing budget instead of an accidental 13 cm on one side only.
The stagger falls out of the centring for free: an offset row holds one fewer plop, and centring that run puts it exactly half a pitch off its neighbours.
Notes
TestFillRegionDeterministicPackingexpected 4 plops in a 60 × 60 bed; the fourth was centred on the east edge with half of it outside, well past the half-spacing budget. It is 3 now — that is the fix working, not a regression in it.Region.containsbecomes dead: the lattice is bounded by construction, so the loop is count-bounded rather than condition-bounded.gitea-actions bot referenced this issue2026-07-21 14:21:47 +00:00
gitea-actions bot referenced this issue2026-07-21 17:37:43 +00:00
gitea-actions bot referenced this issue2026-07-21 22:23:33 +00:00