Plops editor: focus mode, place/move/resize, semantic zoom (#15)
Build image / build-and-push (push) Successful in 14s
Build image / build-and-push (push) Successful in 14s
Co-authored-by: Steve Dudenhoeffer <[email protected]>
This commit was merged in pull request #34.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
// Plantings ("plops") data layer: the zod shape for a /full planting plus the
|
||||
// EditorPlanting the canvas renders. Optimistic create/update/remove mutations
|
||||
// live in objects.ts alongside the FullGarden cache they patch (a plop is part
|
||||
// of the one-shot editor payload).
|
||||
|
||||
import { z } from 'zod'
|
||||
|
||||
export const serverPlantingSchema = z.object({
|
||||
id: z.number(),
|
||||
objectId: z.number(),
|
||||
plantId: z.number(),
|
||||
xCm: z.number(),
|
||||
yCm: z.number(),
|
||||
radiusCm: z.number(),
|
||||
count: z.number().nullable().optional(), // null/absent = derived
|
||||
label: z.string().nullable().optional(),
|
||||
plantedAt: z.string().nullable().optional(),
|
||||
removedAt: z.string().nullable().optional(),
|
||||
derivedCount: z.number(), // computed server-side from area / spacing²
|
||||
version: z.number(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
})
|
||||
export type ServerPlanting = z.infer<typeof serverPlantingSchema>
|
||||
|
||||
/** The plop shape the canvas renders. x/y/radius are in the parent object's
|
||||
* local frame (origin at object center), so a plop tracks its object. */
|
||||
export interface EditorPlanting {
|
||||
id: number
|
||||
objectId: number
|
||||
plantId: number
|
||||
xCm: number
|
||||
yCm: number
|
||||
radiusCm: number
|
||||
count: number | null
|
||||
derivedCount: number
|
||||
label: string | null
|
||||
plantedAt: string | null
|
||||
version: number
|
||||
}
|
||||
|
||||
export function toEditorPlanting(p: ServerPlanting): EditorPlanting {
|
||||
return {
|
||||
id: p.id,
|
||||
objectId: p.objectId,
|
||||
plantId: p.plantId,
|
||||
xCm: p.xCm,
|
||||
yCm: p.yCm,
|
||||
radiusCm: p.radiusCm,
|
||||
count: p.count ?? null,
|
||||
derivedCount: p.derivedCount,
|
||||
label: p.label ?? null,
|
||||
plantedAt: p.plantedAt ?? null,
|
||||
version: p.version,
|
||||
}
|
||||
}
|
||||
|
||||
/** The count a plop shows: its explicit override, else the derived value. */
|
||||
export function effectiveCount(p: { count: number | null; derivedCount: number }): number {
|
||||
return p.count ?? p.derivedCount
|
||||
}
|
||||
|
||||
/** Client-side mirror of the server's derived-count formula, for live display
|
||||
* while resizing a plop (before the PATCH round-trips). max(1, round(π·r² /
|
||||
* spacing²)), capped like the server. */
|
||||
export function computeDerivedCount(radiusCm: number, spacingCm: number): number {
|
||||
if (radiusCm <= 0 || spacingCm <= 0) return 1
|
||||
const n = Math.round((Math.PI * radiusCm * radiusCm) / (spacingCm * spacingCm))
|
||||
return Math.max(1, Math.min(1_000_000, n))
|
||||
}
|
||||
Reference in New Issue
Block a user