Documentation
¶
Overview ¶
Package fsmfile handles the .fsm file format (zip with machine.hex and labels.toml).
Index ¶
- Constants
- func AutoLayout(f *fsm.FSM, algorithm LayoutAlgorithm, width, height int) map[string][2]int
- func ComputeNodeMetrics(f *fsm.FSM) map[string]NodeMetrics
- func CreateBundle(inputs []string, outputPath string) error
- func FormatHex(records []Record, width int) string
- func FormatRecord(r Record) string
- func GenerateDOT(f *fsm.FSM, title string) string
- func GenerateLabels(f *fsm.FSM, states, inputs, outputs map[int]string) string
- func GenerateLayout(positions map[string][2]int, offsetX, offsetY int) string
- func GenerateSVGNative(f *fsm.FSM, opts SVGOptions) string
- func IsBundle(path string) (bool, error)
- func LayoutQuality(f *fsm.FSM, positions map[string][2]int) float64
- func MaxTransitionLabelWidth(f *fsm.FSM) int
- func MinLayerSpacing(upperMetrics, lowerMetrics []NodeMetrics, baseGap int) int
- func ParseJSON(data []byte) (*fsm.FSM, error)
- func ReadFSM(r io.ReaderAt, size int64) (*fsm.FSM, error)
- func ReadFSMBytes(data []byte) (*fsm.FSM, error)
- func ReadFSMFile(path string) (*fsm.FSM, error)
- func RecordsToFSM(records []Record, labels *Labels) (*fsm.FSM, error)
- func RectOverlap(a, b Rect) float64
- func RenderPNG(f *fsm.FSM, w io.Writer, opts PNGOptions) error
- func SelfLoopBounds(points []Point) (minX, minY, maxX, maxY float64)
- func SmartLayout(f *fsm.FSM, width, height int) map[string][2]int
- func SmartLayoutTUI(f *fsm.FSM, width, height int) map[string][2]int
- func SortedStates(states []string) []string
- func SplineLength(spline []Point) float64
- func SugiyamaLayout(f *fsm.FSM, width, height int) map[string][2]int
- func ToJSON(f *fsm.FSM, pretty bool) ([]byte, error)
- func UpdateBundleMachines(bundlePath string, updates map[string]BundleMachineData) error
- func WriteBundleFromData(bundlePath string, machines map[string]BundleMachineData) error
- func WriteFSM(w io.Writer, f *fsm.FSM, includeLabels bool) error
- func WriteFSMFile(path string, f *fsm.FSM, includeLabels bool) error
- func WriteFSMFileWithLayout(path string, f *fsm.FSM, includeLabels bool, positions map[string][2]int, ...) error
- func WriteFSMWithLayout(w io.Writer, f *fsm.FSM, includeLabels bool, positions map[string][2]int, ...) error
- type BundleMachineData
- type EdgeLayout
- type EditorMeta
- type Ellipse
- type FSMMeta
- type LabelPlacer
- type Labels
- type Layout
- func ParseLayout(text string) (*Layout, error)
- func ReadFSMBytesWithLayout(data []byte) (*fsm.FSM, *Layout, error)
- func ReadFSMFileWithLayout(path string) (*fsm.FSM, *Layout, error)
- func ReadFSMWithLayout(r io.ReaderAt, size int64) (*fsm.FSM, *Layout, error)
- func ReadMachineFromBundle(path string, machineName string) (*fsm.FSM, *Layout, error)
- func ReadMachineFromBundleReader(r io.ReaderAt, size int64, machineName string) (*fsm.FSM, *Layout, error)
- type LayoutAlgorithm
- type LayoutResult
- type LinkValidationResult
- type LoopSide
- type MachineInfo
- type NodeLayout
- type NodeMetrics
- type PNGOptions
- type Point
- func EvaluateSpline(spline []Point, t float64) Point
- func EvaluateSplineTangent(spline []Point, t float64) Point
- func FitSplineThroughBoxes(start, end Point, boxes []RoutingBox) []Point
- func RouteAroundObstacles(start, end Point, obstacles []Ellipse) []Point
- func SelfLoopControlPoints(state Ellipse, params SelfLoopParams, scale float64) []Point
- func SelfLoopLabelPosition(points []Point, side LoopSide, labelWidth, labelHeight, scale float64) Point
- func SplineMidpoint(spline []Point) Point
- type RankInfo
- type Record
- type Rect
- type RoutingBox
- type SVGOptions
- type SelfLoopParams
- type StateLayout
- type StateShape
- type VisEdge
- type VisibilityGraph
Constants ¶
const ( TypeDFATransition uint16 = 0x0000 TypeMealyTransition uint16 = 0x0001 TypeStateDecl uint16 = 0x0002 TypeNFAMulti uint16 = 0x0003 )
Record types
const ( StateFlagInitial uint16 = 0x0001 StateFlagAccepting uint16 = 0x0002 StateFlagLinked uint16 = 0x0004 )
State flags (used in Field2 of StateDecl records)
const ( InputAccept uint16 = 0xFFFE // Child machine accepted InputReject uint16 = 0xFFFD // Child machine rejected )
Reserved inputs for linked state results
const (
EpsilonInput uint16 = 0xFFFF
)
Special values
Variables ¶
This section is empty.
Functions ¶
func AutoLayout ¶
AutoLayout generates positions for FSM states. Returns map of state name to [x, y] coordinates.
func ComputeNodeMetrics ¶ added in v0.9.5
func ComputeNodeMetrics(f *fsm.FSM) map[string]NodeMetrics
ComputeNodeMetrics calculates the visual footprint of each state in an FSM. This accounts for:
- State label length (prefix[name]suffix)
- Self-loops (3 rows above + label extending right)
- Linked machine annotations (1 row below)
- Moore outputs (1 row below)
- Accepting/initial markers
func CreateBundle ¶ added in v0.9.5
CreateBundle combines multiple .fsm files into a single bundle. Each input file becomes a named machine (derived from filename).
func FormatRecord ¶
FormatRecord formats a record as "TYPE SSSS:IIII TTTT:OOOO".
func GenerateDOT ¶
GenerateDOT converts an FSM to Graphviz DOT format.
func GenerateLabels ¶
GenerateLabels creates labels.toml content.
func GenerateLayout ¶
GenerateLayout creates layout.toml content from state positions.
func GenerateSVGNative ¶
func GenerateSVGNative(f *fsm.FSM, opts SVGOptions) string
GenerateSVGNative renders FSM to SVG without external dependencies. Uses the built-in layout algorithms.
func LayoutQuality ¶
LayoutQuality returns a score for the current layout (lower is better). Considers edge crossings, edge length variance, and node distribution.
func MaxTransitionLabelWidth ¶ added in v0.9.5
MaxTransitionLabelWidth returns the length of the longest transition label in the FSM. For Mealy machines this includes "input/output".
func MinLayerSpacing ¶ added in v0.9.5
func MinLayerSpacing(upperMetrics, lowerMetrics []NodeMetrics, baseGap int) int
MinLayerSpacing calculates the minimum vertical spacing between two adjacent layers given the metrics of nodes in each layer. It ensures the bottom margin of the upper layer plus the top margin of the lower layer plus a base gap all fit.
func ReadFSMBytes ¶
ReadFSMBytes reads an FSM from bytes in .fsm format.
func ReadFSMFile ¶
ReadFSMFile reads an FSM from a .fsm file.
func RecordsToFSM ¶
RecordsToFSM converts hex records to an FSM.
func RectOverlap ¶ added in v0.8.1
RectOverlap returns the overlap area between two rectangles. Returns 0 if they don't overlap.
func SelfLoopBounds ¶ added in v0.8.1
SelfLoopBounds returns the bounding box of the self-loop (for canvas clipping check).
func SmartLayout ¶
SmartLayout chooses the best algorithm based on FSM structure. Prefers Sugiyama for most cases as it produces the cleanest layouts.
func SmartLayoutTUI ¶ added in v0.9.5
SmartLayoutTUI generates positions for FSM states optimised for the TUI editor.
The layout uses a cell-grid model:
- Sugiyama assigns layers (rows) and left-to-right ordering within rows.
- The canvas is divided into a grid whose cell size is derived from the widest state, the longest transition label, and self-loop/annotation margins.
- Each state occupies one or more columns depending on its connection degree: states with many arcs get extra blank columns for routing space.
- States are centred within their column span.
Returned positions are left-edge character-cell coordinates matching the TUI draw origin (state box starts at pos.X).
func SortedStates ¶
SortedStates returns states in a deterministic order.
func SplineLength ¶ added in v0.8.1
SplineLength approximates the length of a spline by sampling.
func SugiyamaLayout ¶
SugiyamaLayout implements a layered graph layout algorithm inspired by Sugiyama's method. It produces clean hierarchical layouts similar to Graphviz.
The algorithm has four phases: 1. Layer assignment - assign each node to a horizontal layer 2. Crossing minimisation - reorder nodes within layers to reduce edge crossings 3. Horizontal positioning - assign X coordinates to minimise edge length 4. Final coordinate assignment - convert to pixel coordinates
func UpdateBundleMachines ¶ added in v0.9.5
func UpdateBundleMachines(bundlePath string, updates map[string]BundleMachineData) error
UpdateBundleMachines updates specific machines in a bundle file. Only the machines in the 'updates' map are modified; others are preserved.
func WriteBundleFromData ¶ added in v0.9.5
func WriteBundleFromData(bundlePath string, machines map[string]BundleMachineData) error
WriteBundleFromData creates a new bundle file from in-memory machine data. Unlike UpdateBundleMachines, this does not require an existing file on disk. All machines in the 'machines' map are written to the new bundle.
func WriteFSMFile ¶
WriteFSMFile writes an FSM to a .fsm file.
Types ¶
type BundleMachineData ¶ added in v0.9.5
BundleMachineData holds FSM and layout data for a machine in a bundle
type EdgeLayout ¶ added in v0.8.1
type EdgeLayout struct {
From, To string
Waypoints []Point // Path through routing corridor
Boxes []RoutingBox // Constraint boxes (for spline fitting)
IsSelfLoop bool
IsBackEdge bool // Goes against hierarchy direction
IsFlatEdge bool // Same rank
}
EdgeLayout contains routing information for an edge.
type EditorMeta ¶
type EditorMeta struct {
CanvasOffsetX int `toml:"canvas_offset_x"`
CanvasOffsetY int `toml:"canvas_offset_y"`
}
EditorMeta contains editor-specific settings.
type FSMMeta ¶
type FSMMeta struct {
Version int `toml:"version"`
Type string `toml:"type"`
Name string `toml:"name"`
Description string `toml:"description"`
Vocabulary string `toml:"vocabulary"`
}
FSMMeta contains FSM metadata.
type LabelPlacer ¶ added in v0.8.1
type LabelPlacer struct {
// contains filtered or unexported fields
}
LabelPlacer manages label placement with collision avoidance.
func NewLabelPlacer ¶ added in v0.8.1
func NewLabelPlacer(states []Rect) *LabelPlacer
NewLabelPlacer creates a LabelPlacer with initial obstacles (states).
func (*LabelPlacer) PlaceLabel ¶ added in v0.8.1
func (lp *LabelPlacer) PlaceLabel(anchor Point, labelW, labelH, gap float64) Point
PlaceLabel finds the best position for a label near an anchor point. Returns the center position for the label.
func (*LabelPlacer) PlaceLabelOnCurve ¶ added in v0.8.1
func (lp *LabelPlacer) PlaceLabelOnCurve(curvePoint, tangent Point, labelW, labelH, offset float64) Point
PlaceLabelOnCurve places a label at a point on a Bézier curve, offset perpendicular to the curve tangent.
func (*LabelPlacer) PlaceLabelOnEdge ¶ added in v0.8.1
func (lp *LabelPlacer) PlaceLabelOnEdge(p1, p2 Point, labelW, labelH, gap float64) Point
PlaceLabelOnEdge places a label along an edge, trying the midpoint first, then sliding along the edge to find a clear spot.
type Labels ¶
type Labels struct {
FSM FSMMeta `toml:"fsm"`
States map[int]string `toml:"states"`
Inputs map[int]string `toml:"inputs"`
Outputs map[int]string `toml:"outputs"`
Machines map[string]string `toml:"machines"` // state name -> linked machine name
Nets map[string]string `toml:"nets"` // net name -> "U3.3Y, U7.2D"
}
Labels represents the labels.toml content.
func ParseLabels ¶
ParseLabels parses labels.toml content. Simple parser that doesn't require external TOML library.
type Layout ¶
type Layout struct {
Version int `toml:"version"`
Editor EditorMeta `toml:"editor"`
States map[string]StateLayout `toml:"states"`
}
Layout represents visual layout metadata for the editor.
func ParseLayout ¶
ParseLayout parses layout.toml content.
func ReadFSMBytesWithLayout ¶
ReadFSMBytesWithLayout reads an FSM and layout from bytes.
func ReadFSMFileWithLayout ¶
ReadFSMFileWithLayout reads an FSM and layout from a .fsm file.
func ReadFSMWithLayout ¶
ReadFSMWithLayout reads an FSM and layout from a reader.
func ReadMachineFromBundle ¶ added in v0.9.5
ReadMachineFromBundle reads a specific machine from a bundle by name. If name is empty, reads the default "machine.hex" or the first available.
type LayoutAlgorithm ¶
type LayoutAlgorithm int
LayoutAlgorithm represents a layout strategy.
const ( LayoutGrid LayoutAlgorithm = iota LayoutCircular LayoutHierarchical LayoutForceDirected LayoutSugiyama // Layered graph layout with crossing minimisation )
type LayoutResult ¶ added in v0.8.1
type LayoutResult struct {
// Node positions (center coordinates)
Nodes map[string]NodeLayout
// Edge routing information
Edges map[string]EdgeLayout // Key: "from->to"
// Rank (layer) information
Ranks []RankInfo
// Canvas dimensions used during layout
Width, Height int
}
LayoutResult contains complete layout information including edge routing.
func NewLayoutResult ¶ added in v0.8.1
func NewLayoutResult(width, height int) *LayoutResult
NewLayoutResult creates an empty LayoutResult.
func SugiyamaLayoutFull ¶ added in v0.8.1
func SugiyamaLayoutFull(f *fsm.FSM, width, height int) *LayoutResult
SugiyamaLayoutFull performs Sugiyama layout with virtual nodes and routing boxes. This extended version provides routing information for edges that span multiple layers.
func (*LayoutResult) GetEdgeWaypoints ¶ added in v0.8.1
func (lr *LayoutResult) GetEdgeWaypoints(from, to string) []Point
GetEdgeWaypoints returns the waypoints for an edge, or nil if not found.
func (*LayoutResult) GetNodePosition ¶ added in v0.8.1
func (lr *LayoutResult) GetNodePosition(name string) (float64, float64)
GetNodePosition returns the position of a node, or (0,0) if not found.
func (*LayoutResult) ToSimplePositions ¶ added in v0.8.1
func (lr *LayoutResult) ToSimplePositions() map[string][2]int
ToSimplePositions converts LayoutResult to the simple map format for backward compatibility.
type LinkValidationResult ¶ added in v0.9.5
LinkValidationResult contains the result of bundle link validation.
func ValidateBundleLinks ¶ added in v0.9.5
func ValidateBundleLinks(path string) (*LinkValidationResult, error)
ValidateBundleLinks validates all linked state references in a bundle. It checks that: - All linked machines exist in the bundle - Linked machines are DFAs (required for delegation) - No circular links exist - Linked states have accept/reject transitions defined
type LoopSide ¶ added in v0.8.1
type LoopSide int
LoopSide indicates which side of a state the self-loop extends to.
type MachineInfo ¶ added in v0.9.5
type MachineInfo struct {
Name string // filename without .hex extension
HexFile string // full filename (e.g., "main.hex" or "pedestrian.hex")
Type string // FSM type if labels available
Description string // description if labels available
StateCount int // number of states
TransCount int // number of transitions
}
MachineInfo contains basic information about a machine in a bundle.
func ListMachines ¶ added in v0.9.5
func ListMachines(path string) ([]MachineInfo, error)
ListMachines returns information about all machines in a .fsm bundle. A bundle can contain multiple .hex files. The traditional single-machine format uses "machine.hex"; bundles use named files like "main.hex", "child.hex".
func ListMachinesFromReader ¶ added in v0.9.5
func ListMachinesFromReader(r io.ReaderAt, size int64) ([]MachineInfo, error)
ListMachinesFromReader returns machine info from a reader.
type NodeLayout ¶ added in v0.8.1
type NodeLayout struct {
X, Y float64 // Center position
Width float64 // Full width
Height float64 // Full height
Rank int // Which layer (for hierarchical layouts)
Order int // Position within rank
Virtual bool // True for virtual nodes (not rendered)
EdgeID string // For virtual nodes: which edge this belongs to
}
NodeLayout contains position and size for a node.
type NodeMetrics ¶ added in v0.9.5
type NodeMetrics struct {
// Width is the total horizontal span of the state and its decorations.
Width int
// TopMargin is the number of rows needed above the anchor point.
// Self-loops draw 3 rows above the state.
TopMargin int
// BottomMargin is the number of rows needed below the anchor point.
// Linked targets and Moore outputs draw 1 row below.
BottomMargin int
// SelfLoopCount is the number of self-loop transitions on this state.
SelfLoopCount int
}
NodeMetrics holds the visual footprint of a state node in character cells. All measurements are in terminal character units.
type PNGOptions ¶
type PNGOptions struct {
Width int
Height int
Padding int
StateRadius int
FontSize int
LabelSize int
NodeSpacing float64
Title string
}
PNGOptions configures PNG rendering.
func DefaultPNGOptions ¶
func DefaultPNGOptions() PNGOptions
DefaultPNGOptions returns sensible defaults for PNG rendering.
type Point ¶ added in v0.8.1
type Point struct {
X, Y float64
}
Point represents a 2D coordinate.
func EvaluateSpline ¶ added in v0.8.1
EvaluateSpline computes the point on a spline at parameter t ∈ [0,1].
func EvaluateSplineTangent ¶ added in v0.8.1
EvaluateSplineTangent computes the tangent vector at parameter t.
func FitSplineThroughBoxes ¶ added in v0.8.1
func FitSplineThroughBoxes(start, end Point, boxes []RoutingBox) []Point
FitSplineThroughBoxes creates a smooth spline from start to end that stays within all routing boxes.
func RouteAroundObstacles ¶ added in v0.8.1
RouteAroundObstacles finds a path from start to end that avoids all obstacles.
func SelfLoopControlPoints ¶ added in v0.8.1
func SelfLoopControlPoints(state Ellipse, params SelfLoopParams, scale float64) []Point
SelfLoopControlPoints computes the 7 control points for a self-loop. Returns points P0-P6 forming two cubic Bézier segments:
Segment 1: P0, P1, P2, P3 (tail to apex) Segment 2: P3, P4, P5, P6 (apex to head)
func SelfLoopLabelPosition ¶ added in v0.8.1
func SelfLoopLabelPosition(points []Point, side LoopSide, labelWidth, labelHeight, scale float64) Point
SelfLoopLabelPosition returns the label anchor point for a self-loop.
func SplineMidpoint ¶ added in v0.8.1
SplineMidpoint returns the point at the middle of the spline (t=0.5).
type RankInfo ¶ added in v0.8.1
type RankInfo struct {
Y float64 // Vertical position
Height float64 // Height of rank
Nodes []string // Nodes in this rank (in order)
}
RankInfo contains information about a rank (layer).
type Record ¶
type Record struct {
Type uint16
Field1 uint16 // Source state or state ID
Field2 uint16 // Input or flags
Field3 uint16 // Target state or output
Field4 uint16 // Output/flags or continuation
}
Record represents a single hex record.
func FSMToRecords ¶
FSMToRecords converts an FSM to hex records.
func ParseRecord ¶
ParseRecord parses a record from "TYPE SSSS:IIII TTTT:OOOO" format.
type RoutingBox ¶ added in v0.8.1
type RoutingBox struct {
Left, Right float64 // Horizontal bounds
Top, Bottom float64 // Vertical bounds
}
RoutingBox defines the available space for an edge segment.
func (RoutingBox) Center ¶ added in v0.8.1
func (box RoutingBox) Center() Point
BoxCenter returns the center point of a routing box.
func (RoutingBox) Contains ¶ added in v0.8.1
func (box RoutingBox) Contains(p Point) bool
BoxContains checks if a point is inside a routing box.
func (RoutingBox) Height ¶ added in v0.8.1
func (box RoutingBox) Height() float64
BoxHeight returns the height of a routing box.
func (RoutingBox) Width ¶ added in v0.8.1
func (box RoutingBox) Width() float64
BoxWidth returns the width of a routing box.
type SVGOptions ¶
type SVGOptions struct {
Width int // canvas width in pixels
Height int // canvas height in pixels
Title string // diagram title
FontSize int // base font size for state labels
LabelSize int // font size for transition labels (0 = FontSize - 2)
TitleSize int // font size for title (0 = FontSize + 4)
StateRadius int // radius of state circles (or half-height for other shapes)
StateShape StateShape // shape of state nodes
Padding int // padding around edges
NodeSpacing float64 // multiplier for spacing between nodes (default 1.0)
}
SVGOptions controls native SVG rendering.
func DefaultSVGOptions ¶
func DefaultSVGOptions() SVGOptions
DefaultSVGOptions returns sensible defaults.
type SelfLoopParams ¶ added in v0.8.1
type SelfLoopParams struct {
Side LoopSide
Index int // 0-based index for multiple self-loops on same state
BaseOffset float64 // Base distance from state edge to loop apex
Spacing float64 // Additional offset per loop index
PortOffset float64 // Vertical offset of ports from center (as fraction of ry)
}
SelfLoopParams configures self-loop rendering.
func DefaultSelfLoopParams ¶ added in v0.8.1
func DefaultSelfLoopParams() SelfLoopParams
DefaultSelfLoopParams returns standard parameters.
type StateLayout ¶
StateLayout contains position for a single state.
type StateShape ¶
type StateShape int
StateShape defines the shape used for state nodes.
const ( ShapeCircle StateShape = iota // Default circle/ellipse ShapeRoundRect // Rounded rectangle ShapeRect // Rectangle ShapeEllipse // Ellipse (wider than circle) ShapeDiamond // Diamond shape )
type VisibilityGraph ¶ added in v0.8.1
VisibilityGraph represents the visibility graph for path planning.
func BuildVisibilityGraph ¶ added in v0.8.1
func BuildVisibilityGraph(obstacles []Ellipse, start, end Point) *VisibilityGraph
BuildVisibilityGraph constructs a visibility graph around obstacles. The graph includes tangent points on each obstacle plus the start and end points.
func (*VisibilityGraph) FindStartEndIndices ¶ added in v0.8.1
func (vg *VisibilityGraph) FindStartEndIndices(start, end Point) (int, int)
FindStartEndIndices returns the indices of start and end points in the visibility graph.
func (*VisibilityGraph) ShortestPath ¶ added in v0.8.1
func (vg *VisibilityGraph) ShortestPath(startIdx, endIdx int) []Point
ShortestPath finds the shortest path through the visibility graph using Dijkstra's algorithm.