87 lines
2.5 KiB
Go

package model
import (
"fmt"
"math"
"time"
)
// ItemSpec holds mask parameters (from template CLI).
type ItemSpec struct {
// SizeMM is legacy: square product when width_mm/height_mm are unset.
SizeMM float64 `json:"size_mm,omitempty"`
WidthMM float64 `json:"width_mm,omitempty"`
HeightMM float64 `json:"height_mm,omitempty"`
CornerRadiusMM float64 `json:"corner_radius_mm,omitempty"`
BleedMM float64 `json:"bleed_mm"`
MarginMM float64 `json:"margin_mm"`
PaddingMM float64 `json:"padding_mm"`
}
// ViewportWidth returns the SVG viewport width used for layout packing.
func (s ItemSpec) ViewportWidth() float64 {
if s.WidthMM > 0 {
return s.WidthMM
}
return s.SizeMM
}
// ViewportHeight returns the SVG viewport height used for layout packing.
func (s ItemSpec) ViewportHeight() float64 {
if s.HeightMM > 0 {
return s.HeightMM
}
return s.SizeMM
}
// markLengthMM is crop-mark extension outside the product (must match svgtemplate).
const markLengthMM = 6
// TrimOffsetMM is bleed + margin + crop marks drawn outside the product rect.
func (s ItemSpec) TrimOffsetMM() float64 {
return s.BleedMM + s.MarginMM + markLengthMM
}
// CanvasWidthMM is the full item SVG width including trim and crop marks.
func (s ItemSpec) CanvasWidthMM() float64 {
return s.ViewportWidth() + 2*s.TrimOffsetMM()
}
// CanvasHeightMM is the full item SVG height including trim and crop marks.
func (s ItemSpec) CanvasHeightMM() float64 {
return s.ViewportHeight() + 2*s.TrimOffsetMM()
}
// Normalize fills width/height from legacy size_mm and validates dimensions.
func (s *ItemSpec) Normalize() error {
if s.WidthMM <= 0 && s.HeightMM <= 0 && s.SizeMM > 0 {
s.WidthMM = s.SizeMM
s.HeightMM = s.SizeMM
}
if s.WidthMM <= 0 || s.HeightMM <= 0 {
return fmt.Errorf("width_mm and height_mm must be positive")
}
if s.CornerRadiusMM < 0 {
return fmt.Errorf("corner_radius_mm must be non-negative")
}
maxR := math.Min(s.WidthMM, s.HeightMM) / 2
if s.CornerRadiusMM > maxR {
return fmt.Errorf("corner_radius_mm must be at most %.2f", maxR)
}
return nil
}
// Item is a printable product type (SVG mask) placed on a plate.
type Item struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Spec ItemSpec `json:"spec"`
SVGTemplate string `json:"svg_template"`
CreatedAt time.Time `json:"created_at"`
}
// MetaFilename returns the sidecar metadata path for an SVG basename.
func MetaFilename(svgBasename string) string {
return svgBasename + ".meta.json"
}