world refactor
This commit is contained in:
+360
-7
@@ -12,12 +12,17 @@ var (
|
||||
errIDExhausted = errors.New("primitive id exhausted")
|
||||
)
|
||||
|
||||
// indexState stores the viewport-dependent parameters required to rebuild the
|
||||
// spatial grid after object or style-affecting mutations.
|
||||
type indexState struct {
|
||||
initialized bool
|
||||
viewportW int
|
||||
viewportH int
|
||||
zoomFp int
|
||||
}
|
||||
|
||||
// derivedStyleKey identifies one cached derived style by its base style and
|
||||
// stable override fingerprint.
|
||||
type derivedStyleKey struct {
|
||||
base StyleID
|
||||
fp uint64
|
||||
@@ -163,7 +168,7 @@ func (w *World) SetTheme(theme StyleTheme) {
|
||||
w.themeDefaultCircleStyleID = w.styles.AddStyle(theme.CircleStyle())
|
||||
w.themeDefaultPointStyleID = w.styles.AddStyle(theme.PointStyle())
|
||||
|
||||
w.reresolveThemeManagedStyles()
|
||||
w.refreshThemeManagedStyles()
|
||||
|
||||
// Full redraw to apply new background and base styles.
|
||||
w.renderState.Reset()
|
||||
@@ -183,7 +188,9 @@ func (w *World) themeBaseStyleID(base styleBase) StyleID {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *World) reresolveThemeManagedStyles() {
|
||||
// refreshThemeManagedStyles recomputes resolved StyleID values for primitives
|
||||
// that track the active theme rather than a fixed explicit style.
|
||||
func (w *World) refreshThemeManagedStyles() {
|
||||
th := w.Theme()
|
||||
|
||||
for id, it := range w.objects {
|
||||
@@ -243,13 +250,15 @@ func (w *World) reresolveThemeManagedStyles() {
|
||||
w.objects[id] = v
|
||||
|
||||
default:
|
||||
panic("reresolveThemeManagedStyles: unknown item type")
|
||||
panic("refreshThemeManagedStyles: unknown item type")
|
||||
}
|
||||
}
|
||||
|
||||
w.ForceFullRedrawNext()
|
||||
}
|
||||
|
||||
// derivedStyleID resolves a derived style, reusing the per-world cache when an
|
||||
// identical base style and override combination has already been materialized.
|
||||
func (w *World) derivedStyleID(base StyleID, ov StyleOverride) StyleID {
|
||||
if ov.IsZero() {
|
||||
return base
|
||||
@@ -297,7 +306,7 @@ func (w *World) rebuildIndexFromLastState() {
|
||||
return
|
||||
}
|
||||
|
||||
w.indexOnViewportChangeZoomFp(w.index.viewportW, w.index.viewportH, w.index.zoomFp)
|
||||
w.rebuildIndexForViewportZoomFp(w.index.viewportW, w.index.viewportH, w.index.zoomFp)
|
||||
w.indexDirty = false
|
||||
}
|
||||
|
||||
@@ -598,12 +607,16 @@ func (w *World) IndexOnViewportChange(viewportWidthPx, viewportHeightPx int, cam
|
||||
w.index.viewportH = viewportHeightPx
|
||||
w.index.zoomFp = zoomFp
|
||||
|
||||
w.indexOnViewportChangeZoomFp(viewportWidthPx, viewportHeightPx, zoomFp)
|
||||
w.rebuildIndexForViewportZoomFp(viewportWidthPx, viewportHeightPx, zoomFp)
|
||||
w.indexDirty = false
|
||||
}
|
||||
|
||||
// indexOnViewportChangeZoomFp performs indexing logic using fixed-point zoom.
|
||||
func (w *World) indexOnViewportChangeZoomFp(viewportWidthPx, viewportHeightPx int, zoomFp int) {
|
||||
// rebuildIndexForViewportZoomFp rebuilds the spatial grid for a particular
|
||||
// viewport size and fixed-point zoom.
|
||||
//
|
||||
// The chosen cell size is derived from the currently visible world span and
|
||||
// then clamped into the package-wide cell-size bounds.
|
||||
func (w *World) rebuildIndexForViewportZoomFp(viewportWidthPx, viewportHeightPx int, zoomFp int) {
|
||||
worldWidth, worldHeight := viewportPxToWorldFixed(viewportWidthPx, viewportHeightPx, zoomFp)
|
||||
|
||||
cellsAcrossMin := 8
|
||||
@@ -659,3 +672,343 @@ func circleRadiusEffFp(rawRadiusFp, circleRadiusScaleFp int) int {
|
||||
}
|
||||
return int(v)
|
||||
}
|
||||
|
||||
// PointClassID classifies Point primitives for theme-level style overrides.
|
||||
//
|
||||
// Themes may use the class to derive a final style from the point base style
|
||||
// without changing the primitive geometry itself.
|
||||
type PointClassID uint8
|
||||
|
||||
const (
|
||||
// PointClassDefault selects the theme's default point styling.
|
||||
PointClassDefault PointClassID = iota
|
||||
// PointClassTrackUnknown marks a point as an unknown track marker.
|
||||
PointClassTrackUnknown
|
||||
// PointClassTrackIncoming marks a point as an incoming track marker.
|
||||
PointClassTrackIncoming
|
||||
// PointClassTrackOutgoing marks a point as an outgoing track marker.
|
||||
PointClassTrackOutgoing
|
||||
)
|
||||
|
||||
// LineClassID classifies Line primitives for theme-level style overrides.
|
||||
type LineClassID uint8
|
||||
|
||||
const (
|
||||
// LineClassDefault selects the theme's default line styling.
|
||||
LineClassDefault LineClassID = iota
|
||||
// LineClassTrackIncoming marks a line as an incoming track.
|
||||
LineClassTrackIncoming
|
||||
// LineCLassTrackOutgoing marks a line as an outgoing track.
|
||||
// The unusual spelling is preserved for backward compatibility.
|
||||
LineCLassTrackOutgoing
|
||||
// LineClassMeasurement marks a line as a measurement helper.
|
||||
LineClassMeasurement
|
||||
)
|
||||
|
||||
// CircleClassID classifies Circle primitives for theme-level style overrides.
|
||||
type CircleClassID uint8
|
||||
|
||||
const (
|
||||
// CircleClassDefault selects the theme's default circle styling.
|
||||
CircleClassDefault CircleClassID = iota
|
||||
// CircleClassHome marks a circle as a home-world area.
|
||||
CircleClassHome
|
||||
// CircleClassAcquired marks a circle as an acquired world area.
|
||||
CircleClassAcquired
|
||||
// CircleClassOccupied marks a circle as an occupied world area.
|
||||
CircleClassOccupied
|
||||
// CircleClassFree marks a circle as a free world area.
|
||||
CircleClassFree
|
||||
)
|
||||
|
||||
// PrimitiveID is a compact stable identifier for primitives stored in the World.
|
||||
// It is allocated by the World and may be reused after deletion (free-list).
|
||||
type PrimitiveID uint32
|
||||
|
||||
// MapItem is the common interface implemented by all world primitives.
|
||||
type MapItem interface {
|
||||
ID() PrimitiveID
|
||||
}
|
||||
|
||||
// styleBase describes how a primitive resolves its base style across theme changes.
|
||||
type styleBase uint8
|
||||
|
||||
const (
|
||||
styleBaseFixed styleBase = iota
|
||||
styleBaseThemeLine
|
||||
styleBaseThemeCircle
|
||||
styleBaseThemePoint
|
||||
)
|
||||
|
||||
// Point is a point primitive in fixed-point world coordinates.
|
||||
type Point struct {
|
||||
Id PrimitiveID
|
||||
X, Y int
|
||||
|
||||
// Priority controls per-object draw ordering. Smaller draws earlier.
|
||||
Priority int
|
||||
// StyleID references a resolved style in the world's style table.
|
||||
StyleID StyleID
|
||||
// Theme style binding. If Base==styleBaseFixed => StyleID stays as-is across theme changes.
|
||||
Base styleBase
|
||||
// Override is applied relative to current theme base style (only when Base is theme* and Override is non-zero).
|
||||
Override StyleOverride
|
||||
Class PointClassID
|
||||
|
||||
// HitSlopPx expands hit-test radius in screen pixels (per-object override).
|
||||
// 0 means "use primitive default".
|
||||
HitSlopPx int
|
||||
}
|
||||
|
||||
// Line is a line segment primitive in fixed-point world coordinates.
|
||||
type Line struct {
|
||||
Id PrimitiveID
|
||||
X1, Y1 int
|
||||
X2, Y2 int
|
||||
|
||||
// Priority controls per-object draw ordering. Smaller draws earlier.
|
||||
Priority int
|
||||
// StyleID references a resolved style in the world's style table.
|
||||
StyleID StyleID
|
||||
// Theme style binding. If Base==styleBaseFixed => StyleID stays as-is across theme changes.
|
||||
Base styleBase
|
||||
// Override is applied relative to current theme base style (only when Base is theme* and Override is non-zero).
|
||||
Override StyleOverride
|
||||
Class LineClassID
|
||||
|
||||
// HitSlopPx expands hit-test radius in screen pixels (per-object override).
|
||||
// 0 means "use primitive default".
|
||||
HitSlopPx int
|
||||
}
|
||||
|
||||
// Circle is a circle primitive in fixed-point world coordinates.
|
||||
type Circle struct {
|
||||
Id PrimitiveID
|
||||
X, Y int
|
||||
Radius int
|
||||
|
||||
// Priority controls per-object draw ordering. Smaller draws earlier.
|
||||
Priority int
|
||||
// StyleID references a resolved style in the world's style table.
|
||||
StyleID StyleID
|
||||
// Theme style binding. If Base==styleBaseFixed => StyleID stays as-is across theme changes.
|
||||
Base styleBase
|
||||
// Override is applied relative to current theme base style (only when Base is theme* and Override is non-zero).
|
||||
Override StyleOverride
|
||||
Class CircleClassID
|
||||
|
||||
// HitSlopPx expands hit-test radius in screen pixels (per-object override).
|
||||
// 0 means "use primitive default".
|
||||
HitSlopPx int
|
||||
}
|
||||
|
||||
// ID returns the point identifier.
|
||||
func (p Point) ID() PrimitiveID { return p.Id }
|
||||
|
||||
// ID returns the line identifier.
|
||||
func (l Line) ID() PrimitiveID { return l.Id }
|
||||
|
||||
// ID returns the circle identifier.
|
||||
func (c Circle) ID() PrimitiveID { return c.Id }
|
||||
|
||||
// MinX returns the minimum X endpoint coordinate of the line.
|
||||
func (l Line) MinX() int { return min(l.X1, l.X2) }
|
||||
|
||||
// MaxX returns the maximum X endpoint coordinate of the line.
|
||||
func (l Line) MaxX() int { return max(l.X1, l.X2) }
|
||||
|
||||
// MinY returns the minimum Y endpoint coordinate of the line.
|
||||
func (l Line) MinY() int { return min(l.Y1, l.Y2) }
|
||||
|
||||
// MaxY returns the maximum Y endpoint coordinate of the line.
|
||||
func (l Line) MaxY() int { return max(l.Y1, l.Y2) }
|
||||
|
||||
// MinX returns the minimum X coordinate of the circle bbox.
|
||||
func (c Circle) MinX() int { return c.X - c.Radius }
|
||||
|
||||
// MaxX returns the maximum X coordinate of the circle bbox.
|
||||
func (c Circle) MaxX() int { return c.X + c.Radius }
|
||||
|
||||
// MinY returns the minimum Y coordinate of the circle bbox.
|
||||
func (c Circle) MinY() int { return c.Y - c.Radius }
|
||||
|
||||
// MaxY returns the maximum Y coordinate of the circle bbox.
|
||||
func (c Circle) MaxY() int { return c.Y + c.Radius }
|
||||
|
||||
// PointOpt applies optional point-construction parameters to PointOptions.
|
||||
type PointOpt func(*PointOptions)
|
||||
|
||||
// PointOptions stores optional arguments accepted by World.AddPoint.
|
||||
//
|
||||
// Defaults are resolved before applying user-provided PointOpt values.
|
||||
type PointOptions struct {
|
||||
Priority int
|
||||
StyleID StyleID
|
||||
Override StyleOverride
|
||||
Class PointClassID
|
||||
|
||||
HitSlopPx int
|
||||
|
||||
hasStyleID bool
|
||||
}
|
||||
|
||||
// defaultPointOptions returns the default option set used by World.AddPoint.
|
||||
func defaultPointOptions() PointOptions {
|
||||
return PointOptions{
|
||||
Priority: DefaultPriorityPoint,
|
||||
StyleID: StyleIDDefaultPoint,
|
||||
Class: PointClassDefault,
|
||||
}
|
||||
}
|
||||
|
||||
// PointWithPriority sets point draw priority.
|
||||
//
|
||||
// Lower priorities render earlier within the same tile.
|
||||
func PointWithPriority(p int) PointOpt {
|
||||
return func(o *PointOptions) {
|
||||
o.Priority = p
|
||||
}
|
||||
}
|
||||
|
||||
// PointWithStyleID forces the point to use a pre-registered style.
|
||||
func PointWithStyleID(id StyleID) PointOpt {
|
||||
return func(o *PointOptions) {
|
||||
o.StyleID = id
|
||||
o.hasStyleID = true
|
||||
// Explicit style ID wins over overrides.
|
||||
o.Override = StyleOverride{}
|
||||
}
|
||||
}
|
||||
|
||||
// PointWithClass selects the theme class used for point style resolution.
|
||||
func PointWithClass(c PointClassID) PointOpt {
|
||||
return func(o *PointOptions) { o.Class = c }
|
||||
}
|
||||
|
||||
// PointWithStyleOverride applies a user override on top of the resolved point base style.
|
||||
//
|
||||
// If PointWithStyleID is also supplied, the explicit style ID wins.
|
||||
func PointWithStyleOverride(ov StyleOverride) PointOpt {
|
||||
return func(o *PointOptions) {
|
||||
o.Override = ov
|
||||
}
|
||||
}
|
||||
|
||||
// PointWithHitSlopPx overrides the default point hit slop in screen pixels.
|
||||
func PointWithHitSlopPx(px int) PointOpt {
|
||||
return func(o *PointOptions) { o.HitSlopPx = px }
|
||||
}
|
||||
|
||||
// CircleOpt applies optional circle-construction parameters to CircleOptions.
|
||||
type CircleOpt func(*CircleOptions)
|
||||
|
||||
// CircleOptions stores optional arguments accepted by World.AddCircle.
|
||||
type CircleOptions struct {
|
||||
Priority int
|
||||
StyleID StyleID
|
||||
Override StyleOverride
|
||||
Class CircleClassID
|
||||
|
||||
HitSlopPx int
|
||||
|
||||
hasStyleID bool
|
||||
}
|
||||
|
||||
// defaultCircleOptions returns the default option set used by World.AddCircle.
|
||||
func defaultCircleOptions() CircleOptions {
|
||||
return CircleOptions{
|
||||
Priority: DefaultPriorityCircle,
|
||||
StyleID: StyleIDDefaultCircle,
|
||||
Class: CircleClassDefault,
|
||||
}
|
||||
}
|
||||
|
||||
// CircleWithPriority sets circle draw priority.
|
||||
func CircleWithPriority(p int) CircleOpt {
|
||||
return func(o *CircleOptions) {
|
||||
o.Priority = p
|
||||
}
|
||||
}
|
||||
|
||||
// CircleWithStyleID forces the circle to use a pre-registered style.
|
||||
func CircleWithStyleID(id StyleID) CircleOpt {
|
||||
return func(o *CircleOptions) {
|
||||
o.StyleID = id
|
||||
o.hasStyleID = true
|
||||
o.Override = StyleOverride{}
|
||||
}
|
||||
}
|
||||
|
||||
// CircleWithClass selects the theme class used for circle style resolution.
|
||||
func CircleWithClass(c CircleClassID) CircleOpt {
|
||||
return func(o *CircleOptions) { o.Class = c }
|
||||
}
|
||||
|
||||
// CircleWithStyleOverride applies a user override on top of the resolved circle base style.
|
||||
func CircleWithStyleOverride(ov StyleOverride) CircleOpt {
|
||||
return func(o *CircleOptions) {
|
||||
o.Override = ov
|
||||
}
|
||||
}
|
||||
|
||||
// CircleWithHitSlopPx overrides the default circle hit slop in screen pixels.
|
||||
func CircleWithHitSlopPx(px int) CircleOpt {
|
||||
return func(o *CircleOptions) { o.HitSlopPx = px }
|
||||
}
|
||||
|
||||
// LineOpt applies optional line-construction parameters to LineOptions.
|
||||
type LineOpt func(*LineOptions)
|
||||
|
||||
// LineOptions stores optional arguments accepted by World.AddLine.
|
||||
type LineOptions struct {
|
||||
Priority int
|
||||
StyleID StyleID
|
||||
Override StyleOverride
|
||||
Class LineClassID
|
||||
|
||||
HitSlopPx int
|
||||
|
||||
hasStyleID bool
|
||||
}
|
||||
|
||||
// defaultLineOptions returns the default option set used by World.AddLine.
|
||||
func defaultLineOptions() LineOptions {
|
||||
return LineOptions{
|
||||
Priority: DefaultPriorityLine,
|
||||
StyleID: StyleIDDefaultLine,
|
||||
Class: LineClassDefault,
|
||||
}
|
||||
}
|
||||
|
||||
// LineWithPriority sets line draw priority.
|
||||
func LineWithPriority(p int) LineOpt {
|
||||
return func(o *LineOptions) {
|
||||
o.Priority = p
|
||||
}
|
||||
}
|
||||
|
||||
// LineWithStyleID forces the line to use a pre-registered style.
|
||||
func LineWithStyleID(id StyleID) LineOpt {
|
||||
return func(o *LineOptions) {
|
||||
o.StyleID = id
|
||||
o.hasStyleID = true
|
||||
o.Override = StyleOverride{}
|
||||
}
|
||||
}
|
||||
|
||||
// LineWithClass selects the theme class used for line style resolution.
|
||||
func LineWithClass(c LineClassID) LineOpt {
|
||||
return func(o *LineOptions) { o.Class = c }
|
||||
}
|
||||
|
||||
// LineWithStyleOverride applies a user override on top of the resolved line base style.
|
||||
func LineWithStyleOverride(ov StyleOverride) LineOpt {
|
||||
return func(o *LineOptions) {
|
||||
o.Override = ov
|
||||
}
|
||||
}
|
||||
|
||||
// LineWithHitSlopPx overrides the default line hit slop in screen pixels.
|
||||
func LineWithHitSlopPx(px int) LineOpt {
|
||||
return func(o *LineOptions) { o.HitSlopPx = px }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user