import { isEqual, pick } from 'lodash'; import { defaults } from '../types/util'; export interface ShapeStyle { type?: 'circle' | 'rectangle' fill?: string stroke?: string strokeDash?: number[] strokeWidth?: number } export interface CircleStyle extends ShapeStyle { type: 'circle' radius?: number } export interface RectangleStyle extends ShapeStyle { type: 'rectangle' width?: number height?: number } /** * Lazily apply shape attributes to another shape style * @param source * @param target */ function applyShapeAttributes (target: ShapeStyle, source: ShapeStyle): void { if (isCircleStyle(target) && isCircleStyle(source)) { defaults(target, source, ['type', 'radius']); return; } if (isRectangleStyle(target) && isRectangleStyle(source)) { defaults(target, source, ['type', 'width', 'height']); return; } if (!target.type) { if (isCircleStyle(source)) { defaults(target, source, ['type', 'radius']); return; } if (isRectangleStyle(source)) { defaults(target, source, ['type', 'width', 'height']); return; } } } /** * Lazily apply shape style attributes to another shape style. * @param target * @param source */ function applyShapeStyleAttributes (target: ShapeStyle, source: ShapeStyle): void { defaults(target, source, ['fill', 'stroke', 'strokeDash', 'strokeWidth']); } /** * Create a clone of a shape style * @param style */ function cloneShapeStyle (style: ShapeStyle): ShapeStyle { let clone = {}; if (isCircleStyle(style)) clone = _cloneCircleStyle(style); if (isRectangleStyle(style)) clone = _cloneRectangleStyle(style); applyShapeStyleAttributes(clone, style); return clone; } function convertToShape (style: ShapeStyle, type: 'circle' | 'rectangle' | null): ShapeStyle { const next = {}; applyShapeStyleAttributes(next, style); if (type === 'circle') { (next as CircleStyle).type = 'circle'; (next as CircleStyle).radius = isRectangleStyle(style) ? Math.min(style.width || 35, style.height || 35) : 35 } if (type === 'rectangle') { (next as RectangleStyle).type = 'rectangle'; (next as RectangleStyle).width = isCircleStyle(style) ? (style.radius || 40) * 2 : 80; (next as RectangleStyle).height = isCircleStyle(style) ? style.radius : 40; } return next; } /** * Returns true if shape styles are equivalent, false otherwise * @param a * @param b */ function equivalentShapeStyles (a: ShapeStyle, b: ShapeStyle): boolean { return isEqual(a, b); } /** * Returns true if style is a CircleStyle, false otherwise * @param style */ function isCircleStyle (style: ShapeStyle): style is CircleStyle { return style.type === 'circle'; } /** * Returns true is style is a RectangleStyle, false otherwise * @param style */ function isRectangleStyle (style: ShapeStyle): style is RectangleStyle { return style.type === 'rectangle'; } /** * Clone a circle style (only attributes specific to circles) * @param style */ function _cloneCircleStyle (style: CircleStyle): CircleStyle { return pick(style, ['type', 'radius']); } /** * Clone a rectangle style (only attributes specific to rectangles) * @param style */ function _cloneRectangleStyle (style: RectangleStyle): RectangleStyle { return pick(style, ['type', 'width', 'height']); } export { applyShapeAttributes, applyShapeStyleAttributes, cloneShapeStyle, convertToShape, equivalentShapeStyles, isCircleStyle, isRectangleStyle }