@ichigo_san/graphing
Version:
A lightweight UML-style diagram editor built with React Flow and Tailwind CSS
310 lines (286 loc) • 9.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DEFAULT_JETTY_CONFIG = void 0;
exports.calculateAdaptiveJettySize = calculateAdaptiveJettySize;
exports.createJettyConfig = createJettyConfig;
exports.default = void 0;
exports.getJettyOffset = getJettyOffset;
exports.hasJettyOffset = hasJettyOffset;
exports.jetty = jetty;
exports.jettyEdgeEndpoints = jettyEdgeEndpoints;
exports.jettyPoints = jettyPoints;
exports.jettyWithSize = jettyWithSize;
exports.unjetty = unjetty;
exports.unjettyPoints = unjettyPoints;
exports.validateJettyConfig = validateJettyConfig;
var _edgeTypes = require("./edgeTypes.js");
var _perimeterIntersection = require("./perimeterIntersection.js");
/**
* Edge & Arrow System - Jetty System
* Module B3: jetty(point, side, size=20) outward offset; themeable
*/
// ============================================================================
// JETTY CONFIGURATION
// ============================================================================
/**
* @typedef {Object} JettyConfig
* @property {number} size - Jetty size in pixels
* @property {boolean} enabled - Whether jetty is enabled
* @property {number} [minSize] - Minimum jetty size
* @property {number} [maxSize] - Maximum jetty size
*/
/**
* Default jetty configuration
*/
const DEFAULT_JETTY_CONFIG = exports.DEFAULT_JETTY_CONFIG = {
size: _edgeTypes.EdgeConfig.JETTY_SIZE,
// 20px default
enabled: true,
minSize: 5,
maxSize: 50
};
// ============================================================================
// JETTY CALCULATION
// ============================================================================
/**
* Apply jetty offset to a point along a side
* @param {Point} point - Base point
* @param {Side} side - Side to offset along
* @param {JettyConfig} [config] - Jetty configuration
* @returns {Point} Point with jetty offset applied
*/
function jetty(point, side, config = DEFAULT_JETTY_CONFIG) {
if (!point || !side || !config.enabled) {
return point;
}
const size = clampJettySize(config.size, config);
const normal = (0, _perimeterIntersection.getNormalFromSide)(side);
return {
x: point.x + normal.x * size,
y: point.y + normal.y * size
};
}
/**
* Apply jetty offset with custom size
* @param {Point} point - Base point
* @param {Side} side - Side to offset along
* @param {number} size - Jetty size in pixels
* @returns {Point} Point with jetty offset applied
*/
function jettyWithSize(point, side, size) {
if (!point || !side || size <= 0) {
return point;
}
const normal = (0, _perimeterIntersection.getNormalFromSide)(side);
return {
x: point.x + normal.x * size,
y: point.y + normal.y * size
};
}
/**
* Remove jetty offset from a point
* @param {Point} point - Point with jetty offset
* @param {Side} side - Side the offset was applied along
* @param {JettyConfig} [config] - Jetty configuration
* @returns {Point} Point with jetty offset removed
*/
function unjetty(point, side, config = DEFAULT_JETTY_CONFIG) {
if (!point || !side || !config.enabled) {
return point;
}
const size = clampJettySize(config.size, config);
const normal = (0, _perimeterIntersection.getNormalFromSide)(side);
return {
x: point.x - normal.x * size,
y: point.y - normal.y * size
};
}
// ============================================================================
// ARRAY JETTY OPERATIONS
// ============================================================================
/**
* Apply jetty to an array of points
* @param {Point[]} points - Array of points
* @param {Side[]} sides - Array of sides (must match points length)
* @param {JettyConfig} [config] - Jetty configuration
* @returns {Point[]} Array of points with jetty applied
*/
function jettyPoints(points, sides, config = DEFAULT_JETTY_CONFIG) {
if (!Array.isArray(points) || !Array.isArray(sides) || points.length !== sides.length) {
return points;
}
return points.map((point, index) => jetty(point, sides[index], config));
}
/**
* Remove jetty from an array of points
* @param {Point[]} points - Array of points with jetty
* @param {Side[]} sides - Array of sides (must match points length)
* @param {JettyConfig} [config] - Jetty configuration
* @returns {Point[]} Array of points with jetty removed
*/
function unjettyPoints(points, sides, config = DEFAULT_JETTY_CONFIG) {
if (!Array.isArray(points) || !Array.isArray(sides) || points.length !== sides.length) {
return points;
}
return points.map((point, index) => unjetty(point, sides[index], config));
}
// ============================================================================
// JETTY FOR EDGE ENDPOINTS
// ============================================================================
/**
* Apply jetty to edge endpoints
* @param {Point} sourcePoint - Source anchor point
* @param {Side} sourceSide - Source side
* @param {Point} targetPoint - Target anchor point
* @param {Side} targetSide - Target side
* @param {JettyConfig} [config] - Jetty configuration
* @returns {Object} {sourceJetty, targetJetty} - Points with jetty applied
*/
function jettyEdgeEndpoints(sourcePoint, sourceSide, targetPoint, targetSide, config = DEFAULT_JETTY_CONFIG) {
if (!sourcePoint || !targetPoint || !sourceSide || !targetSide) {
return {
sourceJetty: sourcePoint,
targetJetty: targetPoint
};
}
return {
sourceJetty: jetty(sourcePoint, sourceSide, config),
targetJetty: jetty(targetPoint, targetSide, config)
};
}
/**
* Calculate jetty size based on edge length and configuration
* @param {Point} sourcePoint - Source point
* @param {Point} targetPoint - Target point
* @param {JettyConfig} [config] - Jetty configuration
* @returns {number} Adaptive jetty size
*/
function calculateAdaptiveJettySize(sourcePoint, targetPoint, config = DEFAULT_JETTY_CONFIG) {
if (!sourcePoint || !targetPoint) {
return config.size;
}
const distance = Math.sqrt(Math.pow(targetPoint.x - sourcePoint.x, 2) + Math.pow(targetPoint.y - sourcePoint.y, 2));
// Scale jetty size based on edge length
const baseSize = config.size;
const minSize = config.minSize || 5;
const maxSize = config.maxSize || 50;
// For very short edges, use smaller jetty
if (distance < 100) {
return Math.max(minSize, baseSize * 0.5);
}
// For very long edges, use larger jetty
if (distance > 500) {
return Math.min(maxSize, baseSize * 1.5);
}
return baseSize;
}
// ============================================================================
// JETTY VALIDATION AND UTILITIES
// ============================================================================
/**
* Clamp jetty size to valid range
* @param {number} size - Jetty size
* @param {JettyConfig} config - Jetty configuration
* @returns {number} Clamped jetty size
*/
function clampJettySize(size, config) {
const minSize = config.minSize || 5;
const maxSize = config.maxSize || 50;
return Math.max(minSize, Math.min(maxSize, size));
}
/**
* Create jetty configuration from options
* @param {Object} options - Jetty options
* @returns {JettyConfig} Jetty configuration
*/
function createJettyConfig(options = {}) {
return {
...DEFAULT_JETTY_CONFIG,
...options
};
}
/**
* Validate jetty configuration
* @param {JettyConfig} config - Jetty configuration to validate
* @returns {boolean} True if configuration is valid
*/
function validateJettyConfig(config) {
if (!config || typeof config !== 'object') {
return false;
}
if (typeof config.size !== 'number' || config.size < 0) {
return false;
}
if (typeof config.enabled !== 'boolean') {
return false;
}
if (config.minSize !== undefined && (typeof config.minSize !== 'number' || config.minSize < 0)) {
return false;
}
if (config.maxSize !== undefined && (typeof config.maxSize !== 'number' || config.maxSize < 0)) {
return false;
}
if (config.minSize !== undefined && config.maxSize !== undefined && config.minSize > config.maxSize) {
return false;
}
return true;
}
/**
* Check if a point has jetty offset applied
* @param {Point} point - Point to check
* @param {Point} basePoint - Base point (without jetty)
* @param {Side} side - Side jetty was applied along
* @param {JettyConfig} [config] - Jetty configuration
* @returns {boolean} True if point has jetty offset
*/
function hasJettyOffset(point, basePoint, side, config = DEFAULT_JETTY_CONFIG) {
if (!point || !basePoint || !side) {
return false;
}
const expectedJetty = jetty(basePoint, side, config);
const tolerance = 1; // 1px tolerance
const dx = Math.abs(point.x - expectedJetty.x);
const dy = Math.abs(point.y - expectedJetty.y);
return dx <= tolerance && dy <= tolerance;
}
/**
* Get jetty offset vector for a side
* @param {Side} side - Side to get offset for
* @param {JettyConfig} [config] - Jetty configuration
* @returns {Point} Offset vector
*/
function getJettyOffset(side, config = DEFAULT_JETTY_CONFIG) {
if (!side || !config.enabled) {
return {
x: 0,
y: 0
};
}
const size = clampJettySize(config.size, config);
const normal = (0, _perimeterIntersection.getNormalFromSide)(side);
return {
x: normal.x * size,
y: normal.y * size
};
}
var _default = exports.default = {
// Configuration
DEFAULT_JETTY_CONFIG,
createJettyConfig,
validateJettyConfig,
// Basic jetty operations
jetty,
jettyWithSize,
unjetty,
// Array operations
jettyPoints,
unjettyPoints,
// Edge operations
jettyEdgeEndpoints,
calculateAdaptiveJettySize,
// Utilities
hasJettyOffset,
getJettyOffset
};