Ilya Ashikhmin 372c2c25c4 feat(domain): add relations, properties, catalogue, symbols and flow model
Pure, framework-free core: port media + color schemes with compatibility
rules, typed/grouped property model with coercion and validation,
catalogue store with physics seeds, SVG symbol registry with port layouts,
and a mass-conserving mock flow solver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 23:13:45 +02:00

138 lines
4.8 KiB
TypeScript

/**
* Reusable property-schema fragments for nodes and edges. Each symbol composes
* a base (Identity + Presentation) with its own Physics group.
*/
import type { PropertyGroup, PropertySchema, PropertyDef } from './properties'
const yesNo: PropertyDef['options'] = [
{ value: 'yes', label: 'Show' },
{ value: 'no', label: 'Hide' }
]
export function identityGroup(defaultTitle: string): PropertyGroup {
return {
id: 'identity',
label: 'Identity',
defs: [
{ key: 'title', label: 'Title', type: 'string', group: 'identity', default: defaultTitle },
{ key: 'tag', label: 'Tag / ID', type: 'string', group: 'identity', default: '' },
{
key: 'notes',
label: 'Notes',
type: 'string',
group: 'identity',
default: ''
}
]
}
}
export function nodePresentationGroup(color: string): PropertyGroup {
return {
id: 'presentation',
label: 'Presentation',
defs: [
{ key: 'color', label: 'Accent color', type: 'color', group: 'presentation', default: color },
{
key: 'rotation',
label: 'Rotation',
type: 'int',
group: 'presentation',
unit: '°',
min: 0,
max: 270,
step: 90,
default: 0
},
{ key: 'width', label: 'Width', type: 'int', group: 'presentation', unit: 'px', min: 24, max: 240, default: 72 },
{ key: 'height', label: 'Height', type: 'int', group: 'presentation', unit: 'px', min: 24, max: 240, default: 72 },
{ key: 'showLabel', label: 'Label', type: 'enum', group: 'presentation', options: yesNo, default: 'yes' }
]
}
}
export function physicsGroup(defs: PropertyDef[]): PropertyGroup {
return { id: 'physics', label: 'Physics', defs: defs.map((d) => ({ ...d, group: 'physics' })) }
}
/** Build a node schema from a physics-group definition list. */
export function nodeSchema(
defaultTitle: string,
color: string,
physics: PropertyDef[]
): PropertySchema {
return [identityGroup(defaultTitle), nodePresentationGroup(color), physicsGroup(physics)]
}
// ---- Edge schema ----------------------------------------------------------
export const MARKER_OPTIONS: PropertyDef['options'] = [
{ value: 'none', label: 'None' },
{ value: 'arrow', label: 'Arrow' },
{ value: 'openArrow', label: 'Open arrow' },
{ value: 'circle', label: 'Circle' },
{ value: 'diamond', label: 'Diamond' },
{ value: 'bar', label: 'Bar' }
]
export const LINE_STYLE_OPTIONS: PropertyDef['options'] = [
{ value: 'solid', label: 'Solid' },
{ value: 'dashed', label: 'Dashed' },
{ value: 'dotted', label: 'Dotted' }
]
export function edgeSchema(): PropertySchema {
return [
{
id: 'identity',
label: 'Identity',
defs: [
{ key: 'title', label: 'Title', type: 'string', group: 'identity', default: '' },
{ key: 'tag', label: 'Tag / ID', type: 'string', group: 'identity', default: '' }
]
},
{
id: 'presentation',
label: 'Presentation',
defs: [
{ key: 'color', label: 'Line color', type: 'color', group: 'presentation', default: '#2f7fe0' },
{ key: 'lineStyle', label: 'Line style', type: 'enum', group: 'presentation', options: LINE_STYLE_OPTIONS, default: 'solid' },
{ key: 'strokeWidth', label: 'Width', type: 'int', group: 'presentation', unit: 'px', min: 1, max: 12, default: 3 },
{ key: 'tailMarker', label: 'Tail marker', type: 'enum', group: 'presentation', options: MARKER_OPTIONS, default: 'none' },
{ key: 'headMarker', label: 'Head marker', type: 'enum', group: 'presentation', options: MARKER_OPTIONS, default: 'arrow' }
]
},
{
id: 'physics',
label: 'Physics',
defs: [
{ key: 'length', label: 'Length', type: 'float', group: 'physics', unit: 'm', min: 0, step: 0.1, default: 10 },
{ key: 'diameter', label: 'Diameter', type: 'float', group: 'physics', unit: 'mm', min: 1, step: 1, default: 100 },
{ key: 'material', label: 'Material', type: 'catalogueItem', group: 'physics', catalogue: 'pipeMaterial', default: 'steel' },
{ key: 'roughness', label: 'Roughness', type: 'float', group: 'physics', unit: 'mm', min: 0, step: 0.001, default: 0.045 },
{
key: 'flowType',
label: 'Flow type',
type: 'enum',
group: 'physics',
options: [
{ value: 'auto', label: 'Auto' },
{ value: 'laminar', label: 'Laminar' },
{ value: 'turbulent', label: 'Turbulent' }
],
default: 'auto'
},
{ key: 'designFlow', label: 'Design flow', type: 'float', group: 'physics', unit: 'm³/h', min: 0, step: 0.1, default: 0 },
{
key: 'tags',
label: 'Zone tags',
type: 'listOfValues',
group: 'physics',
itemType: 'string',
default: []
}
]
}
]
}