fsmfile

package
v0.9.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 3, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package fsmfile handles the .fsm file format (zip with machine.hex and labels.toml).

Index

Constants

View Source
const (
	TypeDFATransition   uint16 = 0x0000
	TypeMealyTransition uint16 = 0x0001
	TypeStateDecl       uint16 = 0x0002
	TypeNFAMulti        uint16 = 0x0003
)

Record types

View Source
const (
	StateFlagInitial   uint16 = 0x0001
	StateFlagAccepting uint16 = 0x0002
	StateFlagLinked    uint16 = 0x0004
)

State flags (used in Field2 of StateDecl records)

View Source
const (
	InputAccept uint16 = 0xFFFE // Child machine accepted
	InputReject uint16 = 0xFFFD // Child machine rejected
)

Reserved inputs for linked state results

View Source
const (
	EpsilonInput uint16 = 0xFFFF
)

Special values

Variables

This section is empty.

Functions

func AutoLayout

func AutoLayout(f *fsm.FSM, algorithm LayoutAlgorithm, width, height int) map[string][2]int

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

func CreateBundle(inputs []string, outputPath string) error

CreateBundle combines multiple .fsm files into a single bundle. Each input file becomes a named machine (derived from filename).

func FormatHex

func FormatHex(records []Record, width int) string

FormatHex formats records as text.

func FormatRecord

func FormatRecord(r Record) string

FormatRecord formats a record as "TYPE SSSS:IIII TTTT:OOOO".

func GenerateDOT

func GenerateDOT(f *fsm.FSM, title string) string

GenerateDOT converts an FSM to Graphviz DOT format.

func GenerateLabels

func GenerateLabels(f *fsm.FSM, states, inputs, outputs map[int]string) string

GenerateLabels creates labels.toml content.

func GenerateLayout

func GenerateLayout(positions map[string][2]int, offsetX, offsetY int) string

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 IsBundle added in v0.9.5

func IsBundle(path string) (bool, error)

IsBundle returns true if the .fsm file contains multiple machines.

func LayoutQuality

func LayoutQuality(f *fsm.FSM, positions map[string][2]int) float64

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

func MaxTransitionLabelWidth(f *fsm.FSM) int

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 ParseJSON

func ParseJSON(data []byte) (*fsm.FSM, error)

ParseJSON parses an FSM from JSON.

func ReadFSM

func ReadFSM(r io.ReaderAt, size int64) (*fsm.FSM, error)

ReadFSM reads an FSM from a reader containing .fsm format.

func ReadFSMBytes

func ReadFSMBytes(data []byte) (*fsm.FSM, error)

ReadFSMBytes reads an FSM from bytes in .fsm format.

func ReadFSMFile

func ReadFSMFile(path string) (*fsm.FSM, error)

ReadFSMFile reads an FSM from a .fsm file.

func RecordsToFSM

func RecordsToFSM(records []Record, labels *Labels) (*fsm.FSM, error)

RecordsToFSM converts hex records to an FSM.

func RectOverlap added in v0.8.1

func RectOverlap(a, b Rect) float64

RectOverlap returns the overlap area between two rectangles. Returns 0 if they don't overlap.

func RenderPNG

func RenderPNG(f *fsm.FSM, w io.Writer, opts PNGOptions) error

RenderPNG renders an FSM to PNG format. Uses 4x supersampling for smoother output.

func SelfLoopBounds added in v0.8.1

func SelfLoopBounds(points []Point) (minX, minY, maxX, maxY float64)

SelfLoopBounds returns the bounding box of the self-loop (for canvas clipping check).

func SmartLayout

func SmartLayout(f *fsm.FSM, width, height int) map[string][2]int

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

func SmartLayoutTUI(f *fsm.FSM, width, height int) map[string][2]int

SmartLayoutTUI generates positions for FSM states optimised for the TUI editor.

The layout uses a cell-grid model:

  1. Sugiyama assigns layers (rows) and left-to-right ordering within rows.
  2. 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.
  3. Each state occupies one or more columns depending on its connection degree: states with many arcs get extra blank columns for routing space.
  4. 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

func SortedStates(states []string) []string

SortedStates returns states in a deterministic order.

func SplineLength added in v0.8.1

func SplineLength(spline []Point) float64

SplineLength approximates the length of a spline by sampling.

func SugiyamaLayout

func SugiyamaLayout(f *fsm.FSM, width, height int) map[string][2]int

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 ToJSON

func ToJSON(f *fsm.FSM, pretty bool) ([]byte, error)

ToJSON converts an FSM to JSON.

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 WriteFSM

func WriteFSM(w io.Writer, f *fsm.FSM, includeLabels bool) error

WriteFSM writes an FSM to a writer in .fsm format.

func WriteFSMFile

func WriteFSMFile(path string, f *fsm.FSM, includeLabels bool) error

WriteFSMFile writes an FSM to a .fsm file.

func WriteFSMFileWithLayout

func WriteFSMFileWithLayout(path string, f *fsm.FSM, includeLabels bool, positions map[string][2]int, offsetX, offsetY int) error

WriteFSMFileWithLayout writes an FSM with layout metadata.

func WriteFSMWithLayout

func WriteFSMWithLayout(w io.Writer, f *fsm.FSM, includeLabels bool, positions map[string][2]int, offsetX, offsetY int) error

WriteFSMWithLayout writes an FSM with layout to a writer.

Types

type BundleMachineData added in v0.9.5

type BundleMachineData struct {
	FSM       *fsm.FSM
	Positions map[string][2]int
	OffsetX   int
	OffsetY   int
}

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 Ellipse added in v0.8.1

type Ellipse struct {
	CX, CY float64 // Center
	RX, RY float64 // Radii
}

Ellipse represents a state's bounding ellipse.

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

func ParseLabels(text string) (*Labels, error)

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

func ParseLayout(text string) (*Layout, error)

ParseLayout parses layout.toml content.

func ReadFSMBytesWithLayout

func ReadFSMBytesWithLayout(data []byte) (*fsm.FSM, *Layout, error)

ReadFSMBytesWithLayout reads an FSM and layout from bytes.

func ReadFSMFileWithLayout

func ReadFSMFileWithLayout(path string) (*fsm.FSM, *Layout, error)

ReadFSMFileWithLayout reads an FSM and layout from a .fsm file.

func ReadFSMWithLayout

func ReadFSMWithLayout(r io.ReaderAt, size int64) (*fsm.FSM, *Layout, error)

ReadFSMWithLayout reads an FSM and layout from a reader.

func ReadMachineFromBundle added in v0.9.5

func ReadMachineFromBundle(path string, machineName string) (*fsm.FSM, *Layout, error)

ReadMachineFromBundle reads a specific machine from a bundle by name. If name is empty, reads the default "machine.hex" or the first available.

func ReadMachineFromBundleReader added in v0.9.5

func ReadMachineFromBundleReader(r io.ReaderAt, size int64, machineName string) (*fsm.FSM, *Layout, error)

ReadMachineFromBundleReader reads a specific machine from a bundle.

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

type LinkValidationResult struct {
	Valid    bool
	Errors   []string
	Warnings []string
}

LinkValidationResult contains the result of bundle link validation.

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.

const (
	LoopRight  LoopSide = iota // Default: loop extends right
	LoopLeft                   // Loop extends left
	LoopTop                    // Loop extends above
	LoopBottom                 // Loop extends below
)

func ChooseSelfLoopSide added in v0.8.1

func ChooseSelfLoopSide(state Ellipse, canvasWidth, canvasHeight float64,
	occupiedSides map[LoopSide]bool) LoopSide

ChooseSelfLoopSide determines the best side for a self-loop based on available space and canvas boundaries.

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

func EvaluateSpline(spline []Point, t float64) Point

EvaluateSpline computes the point on a spline at parameter t ∈ [0,1].

func EvaluateSplineTangent added in v0.8.1

func EvaluateSplineTangent(spline []Point, t float64) Point

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

func RouteAroundObstacles(start, end Point, obstacles []Ellipse) []Point

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

func SplineMidpoint(spline []Point) Point

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

func FSMToRecords(f *fsm.FSM) ([]Record, map[int]string, map[int]string, map[int]string)

FSMToRecords converts an FSM to hex records.

func ParseHex

func ParseHex(text string) ([]Record, error)

ParseHex parses hex records from text.

func ParseRecord

func ParseRecord(s string) (Record, error)

ParseRecord parses a record from "TYPE SSSS:IIII TTTT:OOOO" format.

type Rect added in v0.8.1

type Rect struct {
	X, Y float64 // Center
	W, H float64 // Full width and height
}

Rect represents an axis-aligned rectangle.

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

type StateLayout struct {
	X int `toml:"x"`
	Y int `toml:"y"`
}

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 VisEdge added in v0.8.1

type VisEdge struct {
	To     int
	Weight float64
}

VisEdge represents an edge in the visibility graph.

type VisibilityGraph added in v0.8.1

type VisibilityGraph struct {
	Vertices []Point
	Adj      [][]VisEdge // Adjacency list
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL