@ustutt/grapheditor-webcomponent
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -13,2 +13,28 @@ # Changelog | ||
## [0.3.0] - 2019-11-22 | ||
### Added | ||
- Added `Edge.markerStart` | ||
- Added `data-line-attachement-point` to marker templates to specify where an edge attaches to the marker if the marker is an end marker | ||
- Added dynamic templates for Nodes, Markers,TextComponents and LinkHandles | ||
- Added dynamic template registry | ||
- Added static template registry | ||
- Consolidated positioning for all objects placed along an edge (markers and text components) | ||
- Added rotation to text components | ||
- Added rotation to link handles | ||
- Added `EdgePathGenerator` and `EdgePathGeneratorRegistry` | ||
### Deprecated | ||
- Deprecated `Marker.rotate`. Use `Marker.absoluteRotation` and `Marker.relativeRotation` instead. | ||
### Incompatible changes | ||
- Removed `Marker.lineOffset`. Use `data-line-attachement-point` in marker template instead. | ||
- Removed `TextComponent.class`. Use custom dynamic templates instead. | ||
- All text components are wrapped in a `<g>` element. This will break some css styles! | ||
## [0.2.0] - 2019-10-27 | ||
@@ -15,0 +41,0 @@ |
@@ -13,2 +13,28 @@ # Changelog | ||
## [0.3.0] - 2019-11-22 | ||
### Added | ||
- Added `Edge.markerStart` | ||
- Added `data-line-attachement-point` to marker templates to specify where an edge attaches to the marker if the marker is an end marker | ||
- Added dynamic templates for Nodes, Markers,TextComponents and LinkHandles | ||
- Added dynamic template registry | ||
- Added static template registry | ||
- Consolidated positioning for all objects placed along an edge (markers and text components) | ||
- Added rotation to text components | ||
- Added rotation to link handles | ||
- Added `EdgePathGenerator` and `EdgePathGeneratorRegistry` | ||
### Deprecated | ||
- Deprecated `Marker.rotate`. Use `Marker.absoluteRotation` and `Marker.relativeRotation` instead. | ||
### Incompatible changes | ||
- Removed `Marker.lineOffset`. Use `data-line-attachement-point` in marker template instead. | ||
- Removed `TextComponent.class`. Use custom dynamic templates instead. | ||
- All text components are wrapped in a `<g>` element. This will break some css styles! | ||
## [0.2.0] - 2019-10-27 | ||
@@ -15,0 +41,0 @@ |
import { LinkHandle } from './link-handle'; | ||
import { Marker } from './marker'; | ||
import { RotationData } from './rotation-vector'; | ||
/** | ||
@@ -11,7 +12,23 @@ * A single point. | ||
/** | ||
* Interface describing the position of a marker, text component or link handle placed along an edge path. | ||
*/ | ||
export interface PathPositionRotationAndScale extends RotationData { | ||
/** The relative position of the marker on the edge (between 0 and 1). (Default `0`=='start') */ | ||
positionOnLine?: number | 'start' | 'end'; | ||
/** A factor to scale the marker. */ | ||
scale?: number; | ||
/** If true the scaling factor is applied relative to the stroke width. */ | ||
scaleRelative?: boolean; | ||
/** If true the relative rotation is applied as if the path always goes from left to right. */ | ||
ignorePathDirectionForRotation?: boolean; | ||
} | ||
/** | ||
* Normalize the positionOnLine argument to a number. (Default: `0`) | ||
* @param positionOnLine | ||
*/ | ||
export declare function normalizePositionOnLine(positionOnLine: number | 'start' | 'end'): number; | ||
/** | ||
* Interface for text components that are part of an edge. | ||
*/ | ||
export interface TextComponent { | ||
/** The relative position of the marker on the edge (between 0 and 1). */ | ||
positionOnLine: number; | ||
export interface TextComponent extends PathPositionRotationAndScale { | ||
/** The actual text content. */ | ||
@@ -27,4 +44,2 @@ value?: string; | ||
height?: number; | ||
/** The complete class attribute. */ | ||
class?: string; | ||
/** The padding is used to avoid collisions. */ | ||
@@ -36,2 +51,4 @@ padding?: number; | ||
offsetY?: number; | ||
/** The template to use for this text component. (Default: `'default-textcomponent'`) */ | ||
template?: string; | ||
} | ||
@@ -42,2 +59,8 @@ /** | ||
export interface Edge { | ||
/** | ||
* An optional explicit edge id. | ||
* | ||
* The edge id is normally computed by the `edgeId` function. | ||
* If this attribute is set it gets returned by `edgeId` instead of the computed id. | ||
*/ | ||
id?: number | string; | ||
@@ -48,8 +71,22 @@ /** The id of the source node of this edge. */ | ||
target: number | string; | ||
/** | ||
* The link handle of the source node the edge is attached to. | ||
* | ||
* This attribute is set automatically by the grapheditor. | ||
*/ | ||
sourceHandle?: LinkHandle; | ||
/** | ||
* The link handle of the target node the edge is attached to. | ||
* | ||
* This attribute is set automatically by the grapheditor. | ||
*/ | ||
targetHandle?: LinkHandle; | ||
/** Edge type. Can be used for styling. */ | ||
type?: any; | ||
/** The id of the path generator used for this edge. */ | ||
pathType?: string; | ||
/** List of markers to draw for this edge. */ | ||
markers?: Marker[]; | ||
/** Markers to draw at the start of this edge. */ | ||
markerStart?: Marker; | ||
/** Markers to draw at the end of this edge. */ | ||
@@ -65,2 +102,7 @@ markerEnd?: Marker; | ||
export interface DraggedEdge extends Edge { | ||
/** | ||
* Explicit id of the dragged edge. | ||
* | ||
* A dragged edge may have no current target and must to specify an explicit id! | ||
*/ | ||
id: string; | ||
@@ -67,0 +109,0 @@ /** If edge was created from an existing edge this is the id of the existing edge. */ |
@@ -18,2 +18,18 @@ /* | ||
/** | ||
* Normalize the positionOnLine argument to a number. (Default: `0`) | ||
* @param positionOnLine | ||
*/ | ||
export function normalizePositionOnLine(positionOnLine) { | ||
if (positionOnLine == null || positionOnLine === 'start') { | ||
return 0; | ||
} | ||
if (positionOnLine === 'end') { | ||
return 1; | ||
} | ||
if (isNaN(positionOnLine)) { | ||
return 0; | ||
} | ||
return positionOnLine; | ||
} | ||
/** | ||
* Return edge id if set or calculate a new id from target and source. | ||
@@ -20,0 +36,0 @@ * |
@@ -5,2 +5,4 @@ import { Selection } from 'd3-selection'; | ||
import { LinkHandle } from './link-handle'; | ||
import { RotationVector } from './rotation-vector'; | ||
import { StaticTemplateRegistry, DynymicTemplateRegistry, EdgePathGeneratorRegistry } from './templating'; | ||
/** | ||
@@ -23,3 +25,2 @@ * An enum describing the source of the event. | ||
private zoomActive; | ||
private edgeGenerator; | ||
private contentMinHeight; | ||
@@ -37,8 +38,31 @@ private contentMaxHeight; | ||
private _zoomMode; | ||
private templateCache; | ||
/** | ||
* The static template registry. | ||
* | ||
* The templates will be automatically loaded when the svg changes or `updateTemplates` gets called. | ||
*/ | ||
staticTemplateRegistry: StaticTemplateRegistry; | ||
/** | ||
* The dynamic template registry of this graph. | ||
* | ||
* The dynamic template registry does not get cleared automatically when the other | ||
* templates get updated! | ||
*/ | ||
dynamicTemplateRegistry: DynymicTemplateRegistry; | ||
/** | ||
* The edge path generator registry of this graph. | ||
* | ||
* The registry does not get cleared automatically when the other | ||
* templates get updated! | ||
*/ | ||
edgePathGeneratorRegistry: EdgePathGeneratorRegistry; | ||
private defaultEdgePathGenerator; | ||
/** | ||
* The object cache responsible for fast access of nodes and edges. | ||
*/ | ||
private objectCache; | ||
private interactionStateData; | ||
/** Private property to determine if the graph can be drawn. */ | ||
private readonly initialized; | ||
private readonly isInteractive; | ||
private get initialized(); | ||
private get isInteractive(); | ||
/** | ||
@@ -83,26 +107,31 @@ * Callback when a new dragged edge is created. | ||
}; | ||
get classes(): string[]; | ||
/** | ||
* The list of css classes used for dynamic css classes together with `setNodeClass` or `setEdgeClass`. | ||
*/ | ||
classes: string[]; | ||
* The list of css classes used for dynamic css classes together with `setNodeClass` or `setEdgeClass`. | ||
*/ | ||
set classes(classes: string[]); | ||
get nodeList(): Node[]; | ||
/** | ||
* The list of nodes. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
nodeList: Node[]; | ||
* The list of nodes. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
set nodeList(nodes: Node[]); | ||
get edgeList(): Edge[]; | ||
/** | ||
* The list of edges. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
edgeList: Edge[]; | ||
* The list of edges. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
set edgeList(edges: Edge[]); | ||
get mode(): string; | ||
/** | ||
* The interaction mode of the grapheditor. | ||
*/ | ||
mode: string; | ||
* The interaction mode of the grapheditor. | ||
*/ | ||
set mode(mode: string); | ||
get zoomMode(): string; | ||
/** | ||
* The zoom mode of the grapheditor. | ||
*/ | ||
zoomMode: string; | ||
* The zoom mode of the grapheditor. | ||
*/ | ||
set zoomMode(mode: string); | ||
constructor(); | ||
@@ -113,3 +142,3 @@ connectedCallback(): void; | ||
*/ | ||
static readonly observedAttributes: string[]; | ||
static get observedAttributes(): string[]; | ||
/** | ||
@@ -219,3 +248,3 @@ * Callback when an attribute changed in html dom. | ||
*/ | ||
private initialize; | ||
initialize(svg: any): void; | ||
/** | ||
@@ -269,5 +298,22 @@ * Calculate and store the size of the svg. | ||
* @param templateType the template type to use | ||
* @param dynamic `true` iff the template is a dynamic template (default: `false`) | ||
*/ | ||
private updateContentTemplate; | ||
/** | ||
* Update the static content template of a `SVGGElement` to the new template id. | ||
* | ||
* If the `SVGGElement` already uses the template the content is not touched. | ||
* | ||
* @param element the lement to update the content | ||
* @param templateId the new template ID | ||
* @param templateType the template type to use | ||
*/ | ||
private updateStaticContentTemplate; | ||
/** | ||
* Get a single node selection with bound datum. | ||
* | ||
* @param nodeId the id of the node to select | ||
*/ | ||
private getSingleNodeSelection; | ||
/** | ||
* Update existing nodes. | ||
@@ -285,11 +331,13 @@ * | ||
/** | ||
* Update non text elements of existing nodes. | ||
* Update non text elements of existing nodes or edges. | ||
* | ||
* @param nodeSelection d3 selection of nodes to update with bound data | ||
* @param groupSelection d3 selection of nodes or edges to update with bound data | ||
*/ | ||
private updateNodeDynamicProperties; | ||
private updateDynamicProperties; | ||
/** | ||
* Recursively retrieve an attribute. | ||
* | ||
* This only supports '.' access of attributes. | ||
* The attribute path is a string split at the '.' character. | ||
* The attribute path is processed recursively by applying `obj = obj[attr[0]]`. | ||
* If a path segment is '()' then `obj = obj()` is applied instead. | ||
* | ||
@@ -299,3 +347,3 @@ * @param obj the object to get the attribute from | ||
*/ | ||
private recursiveAttributeGet; | ||
recursiveAttributeGet(obj: any, attr: string): any; | ||
/** | ||
@@ -351,2 +399,16 @@ * Update node classes. | ||
/** | ||
* Calculate the attachement vector for a marker. | ||
* | ||
* @param startingAngle the line angle for the marker | ||
* @param marker the selection of a single marker | ||
* @param strokeWidth the current stroke width | ||
*/ | ||
private calculateLineAttachementVector; | ||
/** | ||
* Calculate the link handles for each edge and store them into the edge. | ||
* | ||
* @param edgeSelection d3 selection of edges to update with bound data | ||
*/ | ||
private updateEdgeLinkHandles; | ||
/** | ||
* Update existing edge path. | ||
@@ -361,6 +423,7 @@ * | ||
* @param markerSelection d3 selection | ||
* @param edge the edge datum this marker belongs to | ||
*/ | ||
private updateMarker; | ||
/** | ||
* Update edge-end marker. | ||
* Update edge-end and edge-start marker. | ||
* | ||
@@ -370,9 +433,57 @@ * @param edgeGroupSelection d3 selection of single edge group | ||
*/ | ||
private updateEndMarkers; | ||
/** | ||
* Update a specific edge end marker (either start or end marker). | ||
* | ||
* @param edgeGroupSelection d3 selection of single edge group | ||
* @param marker the special end marker | ||
* @param markerClass the css class to select for | ||
* @param edge the edge datum this marker belongs to | ||
*/ | ||
private updateEndMarker; | ||
/** | ||
* Update position of edge-end marker. | ||
* Calculate a normal vector pointing in the direction of the path at the positonOnLine. | ||
* | ||
* @param path the path object | ||
* @param positionOnLine the relative position on the path (between 0 and 1) | ||
* @param point the point at positionOnLine (will be calculated if not supplied) | ||
* @param length the length of the path (will be calculated if not supplied) | ||
*/ | ||
calculatePathNormalAtPosition(path: SVGPathElement, positionOnLine: number, point?: DOMPoint, length?: number): RotationVector; | ||
/** | ||
* Calculate the transformation attribute for a path object placed on an edge. | ||
* | ||
* @param point the path object position position | ||
* @param pathObject the path object to place | ||
* @param strokeWidth the stroke width of the edge | ||
* @param normal the normal vector of the edge at the path object position | ||
*/ | ||
private calculatePathObjectTransformation; | ||
/** | ||
* Calculate the rotation vector from rotation data and a normal vector. | ||
* | ||
* @param rotationData the rotation data object | ||
* @param normal the normal vector used for relative rotation | ||
* @param ignorePathDirectionForRotation iff true the normal rotation is limited to half a circle (useful for text components) | ||
*/ | ||
private calculateRotationTransformationAngle; | ||
/** | ||
* Update positions of edge-end and edge-start marker. | ||
* | ||
* @param edgeGroupSelection d3 selection of single edge group | ||
* @param d edge datum | ||
*/ | ||
private updateEndMarkerPositions; | ||
/** | ||
* Update a single end marker position (either start or end marker). | ||
* | ||
* @param path the path selection | ||
* @param length the path length | ||
* @param positionOnLine positionOnLine at the marker | ||
* @param marker the marker | ||
* @param handle the link handle at the path end of the marker; can be `null` | ||
* @param markerClass the class of the marker | ||
* @param strokeWidth the edge stroke width | ||
* @param edgeGroupSelection d3 selection of a single edge group | ||
*/ | ||
private updateEndMarkerPosition; | ||
@@ -386,2 +497,19 @@ /** | ||
/** | ||
* Apply a transformation to a bbox. | ||
* | ||
* @param bbox the bbox to transform | ||
* @param transformation the transformation matrix | ||
*/ | ||
transformBBox(bbox: { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
}, transformation: DOMMatrix): { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
}; | ||
/** | ||
* Update all edge text positions in a edge group. | ||
@@ -388,0 +516,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { RotationVector } from './rotation-vector'; | ||
import { RotationVector, RotationData } from './rotation-vector'; | ||
import { Point } from './edge'; | ||
@@ -6,3 +6,3 @@ /** | ||
*/ | ||
export interface LinkHandle { | ||
export interface LinkHandle extends RotationData { | ||
/** Normally the index of the LinkHandle. Unique for each template. */ | ||
@@ -20,2 +20,6 @@ id: number; | ||
normal?: RotationVector; | ||
/** The template to use for the link handle. */ | ||
template?: string; | ||
/** True iff the link handle uses a dynamic template. */ | ||
isDynamicTemplate?: boolean; | ||
} | ||
@@ -27,3 +31,3 @@ /** | ||
*/ | ||
export declare function calculateNormal(handle: LinkHandle): void; | ||
export declare function calculateLinkHandleNormal(handle: LinkHandle): void; | ||
/** | ||
@@ -30,0 +34,0 @@ * Generate link handles list for a rectangle. |
@@ -23,3 +23,3 @@ /* | ||
*/ | ||
export function calculateNormal(handle) { | ||
export function calculateLinkHandleNormal(handle) { | ||
const x = handle.normal != null ? handle.normal.dx : handle.x; | ||
@@ -68,3 +68,3 @@ const y = handle.normal != null ? handle.normal.dy : handle.y; | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -113,3 +113,3 @@ } | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -143,3 +143,3 @@ } | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -175,5 +175,5 @@ } | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
} | ||
//# sourceMappingURL=link-handle.js.map |
import { RotationVector } from './rotation-vector'; | ||
interface RotationData { | ||
/** Absolute rotation via direction vector. */ | ||
normal?: RotationVector; | ||
/** Relative angle in degree. */ | ||
relativeAngle: number; | ||
} | ||
export interface Marker { | ||
import { PathPositionRotationAndScale, Point } from './edge'; | ||
/** | ||
* Interface describing an edge marker. | ||
*/ | ||
export interface Marker extends PathPositionRotationAndScale { | ||
/** the marker template id to use for this marker. */ | ||
template: string; | ||
/** The relative position of the marker on the edge (between 0 and 1). */ | ||
positionOnLine: number | string; | ||
/** The length used for end/start markers to offset the line position. */ | ||
lineOffset?: number; | ||
/** True iff the link handle uses a dynamic template. */ | ||
isDynamicTemplate?: boolean; | ||
/** A factor to scale the marker. */ | ||
@@ -19,7 +15,39 @@ scale?: number; | ||
scaleRelative?: boolean; | ||
/** Rotation information for the marker. */ | ||
rotate?: RotationData; | ||
/** @deprecated Rotation information for the marker. (Use `absoluteRotation` and `relativeRotation attributes instead!) */ | ||
rotate?: { | ||
/** Absolute rotation via direction vector. */ | ||
normal?: RotationVector; | ||
/** Relative angle in degree. */ | ||
relativeAngle: number; | ||
}; | ||
/** A key used in a click event when the marker was clicked. */ | ||
clickEventKey?: string; | ||
} | ||
export {}; | ||
/** | ||
* Helper class to calculate where the edge attaches to an end marker. | ||
*/ | ||
export declare class LineAttachementInfo { | ||
private isDirectional; | ||
private lineAttachementAngle; | ||
private attachementOffset; | ||
/** | ||
* Create a new line attachement info object. | ||
* | ||
* The attachement point can either be an offset from 0,0 or a point relative to 0,0 of the template. | ||
* To specify an offset use a single number or a string containing exactly one number. | ||
* To specify a point use a string with two numbers seperated by a space or a point object. | ||
* The line attachement point must not include any transformations applied to the marker when it is rendered. | ||
* | ||
* If only an offset was specified the attachement info is not directional. | ||
* | ||
* @param lineAttachementPoint the attachement point relative to 0,0 in the template | ||
*/ | ||
constructor(lineAttachementPoint: string | number | Point); | ||
/** | ||
* Return a rotation vector pointing at the translated line attachement point. | ||
* | ||
* @param angle the angle the marker is currently rotated | ||
* @param scale the current scale of the marker | ||
*/ | ||
getRotationVector(angle: number, scale?: number): RotationVector; | ||
} |
@@ -17,2 +17,83 @@ /* | ||
*/ | ||
import { angleToVector, calculateAngle, calculateLength } from './rotation-vector'; | ||
/** | ||
* Helper class to calculate where the edge attaches to an end marker. | ||
*/ | ||
export class LineAttachementInfo { | ||
/** | ||
* Create a new line attachement info object. | ||
* | ||
* The attachement point can either be an offset from 0,0 or a point relative to 0,0 of the template. | ||
* To specify an offset use a single number or a string containing exactly one number. | ||
* To specify a point use a string with two numbers seperated by a space or a point object. | ||
* The line attachement point must not include any transformations applied to the marker when it is rendered. | ||
* | ||
* If only an offset was specified the attachement info is not directional. | ||
* | ||
* @param lineAttachementPoint the attachement point relative to 0,0 in the template | ||
*/ | ||
constructor(lineAttachementPoint) { | ||
if (lineAttachementPoint == null) { | ||
lineAttachementPoint = 0; | ||
} | ||
if (typeof lineAttachementPoint === 'number') { | ||
this.attachementOffset = lineAttachementPoint; | ||
this.lineAttachementAngle = 0; | ||
this.isDirectional = false; | ||
} | ||
else if (typeof lineAttachementPoint === 'string') { | ||
const coords = lineAttachementPoint.toString().split(' '); | ||
if (coords.length === 1) { | ||
this.attachementOffset = parseFloat(coords[0]); | ||
this.lineAttachementAngle = 0; | ||
this.isDirectional = false; | ||
} | ||
else if (coords.length === 2) { | ||
const normal = { dx: parseFloat(coords[0]), dy: parseFloat(coords[1]) }; | ||
this.attachementOffset = calculateLength(normal); | ||
this.lineAttachementAngle = calculateAngle(normal); | ||
this.isDirectional = true; | ||
} | ||
else { | ||
console.warn('lineAttachementPoint must be one or two numbers seperated by a space!'); | ||
} | ||
} | ||
else { | ||
const normal = { dx: lineAttachementPoint.x, dy: lineAttachementPoint.y }; | ||
this.attachementOffset = calculateLength(normal); | ||
this.lineAttachementAngle = calculateAngle(normal); | ||
this.isDirectional = true; | ||
} | ||
if (this.attachementOffset == null || isNaN(this.attachementOffset)) { | ||
console.warn('Could not parse attachement offset! Using 0 instead.'); | ||
this.attachementOffset = 0; | ||
} | ||
if (this.lineAttachementAngle == null || isNaN(this.lineAttachementAngle)) { | ||
console.warn('Could not parse attachement angle! Using 0 instead.'); | ||
this.lineAttachementAngle = 0; | ||
this.isDirectional = false; | ||
} | ||
} | ||
/** | ||
* Return a rotation vector pointing at the translated line attachement point. | ||
* | ||
* @param angle the angle the marker is currently rotated | ||
* @param scale the current scale of the marker | ||
*/ | ||
getRotationVector(angle, scale) { | ||
let attachementAngle; | ||
if (this.isDirectional) { | ||
attachementAngle = angle + this.lineAttachementAngle; | ||
} | ||
else { | ||
// rotate attachement angle | ||
attachementAngle = angle + 180; | ||
} | ||
let offset = this.attachementOffset; | ||
if (scale != null || scale === 0) { | ||
offset = this.attachementOffset * scale; | ||
} | ||
return angleToVector(attachementAngle, offset); | ||
} | ||
} | ||
//# sourceMappingURL=marker.js.map |
@@ -13,3 +13,5 @@ /** | ||
type?: any; | ||
/** The id of the dynamic node template to use for this node. */ | ||
dynamicTemplate?: string; | ||
[prop: string]: any; | ||
} |
import { Node } from './node'; | ||
import { Edge, DraggedEdge, Point } from './edge'; | ||
import { LinkHandle } from './link-handle'; | ||
import { TemplateCache } from './templating'; | ||
import { Edge } from './edge'; | ||
export declare class GraphObjectCache { | ||
private templateCache; | ||
private nodes; | ||
@@ -12,3 +9,3 @@ private nodeBBoxes; | ||
private edgesByTarget; | ||
constructor(templateCache: TemplateCache); | ||
constructor(); | ||
updateNodeCache(nodes: Node[]): void; | ||
@@ -22,18 +19,2 @@ updateEdgeCache(edges: Edge[]): void; | ||
getEdgesBySource(sourceId: number | string): Set<Edge>; | ||
getEdgeLinkHandles(edge: Edge | DraggedEdge, _calculateHandlesToUse?: (edge: Edge | DraggedEdge, sourceHandles: LinkHandle[], source: Node, targetHandles: LinkHandle[], target: Node | Point) => { | ||
sourceHandles: LinkHandle[]; | ||
targetHandles: LinkHandle[]; | ||
}): { | ||
sourceHandle: LinkHandle; | ||
sourceCoordinates: { | ||
x: number; | ||
y: number; | ||
}; | ||
targetHandle: LinkHandle; | ||
targetCoordinates: { | ||
x: number; | ||
y: number; | ||
}; | ||
}; | ||
private calculateNearestHandles; | ||
} |
@@ -18,6 +18,4 @@ /* | ||
import { edgeId } from './edge'; | ||
import { calculateNormal } from './link-handle'; | ||
export class GraphObjectCache { | ||
constructor(templateCache) { | ||
this.templateCache = templateCache; | ||
constructor() { | ||
this.nodes = new Map(); | ||
@@ -84,80 +82,3 @@ this.nodeBBoxes = new Map(); | ||
} | ||
// tslint:disable-next-line:max-line-length | ||
getEdgeLinkHandles(edge, _calculateHandlesToUse) { | ||
let source = this.getNode(edge.source); | ||
let sourceHandles = edge.sourceHandle != null ? [edge.sourceHandle] : [{ id: 0, x: 0, y: 0, normal: { dx: 0, dy: 0 } }]; | ||
if (source != null) { | ||
sourceHandles = this.templateCache.getNodeTemplateLinkHandles(source.type); | ||
} | ||
else { | ||
console.warn('Attempting to render Edge without valid source.', edge); | ||
source = { id: 'UNDEFINED', x: 0, y: 0 }; | ||
} | ||
let target = edge.target != null ? this.getNode(edge.target) : null; | ||
let targetHandles = edge.targetHandle != null ? [edge.targetHandle] : [{ id: 0, x: 0, y: 0, normal: { dx: 0, dy: 0 } }]; | ||
if (target != null) { | ||
targetHandles = this.templateCache.getNodeTemplateLinkHandles(target.type); | ||
} | ||
else { | ||
if (edge.currentTarget != null) { | ||
target = edge.currentTarget; | ||
} | ||
else { | ||
console.warn('Attempting to render Edge without valid target.', edge); | ||
target = { id: 'UNDEFINED', x: 1, y: 1 }; | ||
} | ||
} | ||
if (_calculateHandlesToUse != null) { | ||
// replace template link handle lists with user calculated lists | ||
const calculatedHandles = _calculateHandlesToUse(edge, sourceHandles, source, targetHandles, target); | ||
if (calculatedHandles != null && calculatedHandles.sourceHandles != null && calculatedHandles.sourceHandles.length > 0) { | ||
sourceHandles = calculatedHandles.sourceHandles; | ||
} | ||
if (calculatedHandles != null && calculatedHandles.targetHandles != null && calculatedHandles.targetHandles.length > 0) { | ||
targetHandles = calculatedHandles.targetHandles; | ||
} | ||
} | ||
const result = this.calculateNearestHandles(sourceHandles, source, targetHandles, target); | ||
return { | ||
sourceHandle: result.sourceHandle, | ||
sourceCoordinates: { x: (source.x + result.sourceHandle.x), y: (source.y + result.sourceHandle.y) }, | ||
targetHandle: result.targetHandle, | ||
targetCoordinates: { x: (target.x + result.targetHandle.x), y: (target.y + result.targetHandle.y) }, | ||
}; | ||
} | ||
calculateNearestHandles(sourceHandles, source, targetHandles, target) { | ||
let currentSourceHandle = { id: 0, x: 0, y: 0, normal: { dx: 1, dy: 1 } }; | ||
if (sourceHandles != null && sourceHandles.length > 0) { | ||
currentSourceHandle = sourceHandles[0]; | ||
} | ||
else { | ||
calculateNormal(currentSourceHandle); | ||
} | ||
let currentTargetHandle = { id: 0, x: 0, y: 0, normal: { dx: 1, dy: 1 } }; | ||
if (targetHandles != null && targetHandles.length > 0) { | ||
currentTargetHandle = targetHandles[0]; | ||
} | ||
else { | ||
calculateNormal(currentTargetHandle); | ||
} | ||
let currentDist = Math.pow((source.x + currentSourceHandle.x) - target.x, 2) + | ||
Math.pow((source.y + currentSourceHandle.y) - target.y, 2); | ||
targetHandles.forEach((targetHandle) => { | ||
for (let i = 0; i < sourceHandles.length; i++) { | ||
const handle = sourceHandles[i]; | ||
const dist = Math.pow((source.x + handle.x) - (target.x + targetHandle.x), 2) + | ||
Math.pow((source.y + handle.y) - (target.y + targetHandle.y), 2); | ||
if (dist <= currentDist) { | ||
currentSourceHandle = handle; | ||
currentTargetHandle = targetHandle; | ||
currentDist = dist; | ||
} | ||
} | ||
}); | ||
return { | ||
sourceHandle: currentSourceHandle, | ||
targetHandle: currentTargetHandle, | ||
}; | ||
} | ||
} | ||
//# sourceMappingURL=object-cache.js.map |
@@ -6,2 +6,21 @@ export interface RotationVector { | ||
/** | ||
* Interface holding rotation information of a graph object. | ||
*/ | ||
export interface RotationData { | ||
/** | ||
* Absolute rotation angle in degree. | ||
* | ||
* Absolute rotation overwrites any relative rotation. | ||
*/ | ||
absoluteRotation?: number; | ||
/** Relative rotation angle in degree. */ | ||
relativeRotation?: number; | ||
} | ||
/** | ||
* Calculate the absolute length of the given vector. | ||
* | ||
* @param vector vector to calculate length of | ||
*/ | ||
export declare function calculateLength(vector: RotationVector): number; | ||
/** | ||
* Normalize an existing vector to length 1. | ||
@@ -18,1 +37,8 @@ * | ||
export declare function calculateAngle(vector: RotationVector): number; | ||
/** | ||
* Calculate a rotation vector from an angle in degree and a length. | ||
* | ||
* @param angle in degree | ||
* @param length length of the vector (defaults to 1) | ||
*/ | ||
export declare function angleToVector(angle: number, length?: number): RotationVector; |
@@ -18,2 +18,12 @@ /* | ||
/** | ||
* Calculate the absolute length of the given vector. | ||
* | ||
* @param vector vector to calculate length of | ||
*/ | ||
export function calculateLength(vector) { | ||
const x = vector.dx; | ||
const y = vector.dy; | ||
return Math.sqrt(x * x + y * y); | ||
} | ||
/** | ||
* Normalize an existing vector to length 1. | ||
@@ -45,2 +55,21 @@ * | ||
} | ||
/** | ||
* Calculate a rotation vector from an angle in degree and a length. | ||
* | ||
* @param angle in degree | ||
* @param length length of the vector (defaults to 1) | ||
*/ | ||
export function angleToVector(angle, length) { | ||
if (length == null || isNaN(length)) { | ||
length = 1; | ||
} | ||
if (angle == null || isNaN(angle)) { | ||
angle = 0; | ||
} | ||
angle = (angle * Math.PI) / 180; | ||
return { | ||
dx: Math.cos(angle) * length, | ||
dy: Math.sin(angle) * length, | ||
}; | ||
} | ||
//# sourceMappingURL=rotation-vector.js.map |
import { LinkHandle } from './link-handle'; | ||
import { Selection } from 'd3-selection'; | ||
export declare class TemplateCache { | ||
import { TextComponent } from './edge'; | ||
import { LineAttachementInfo, Marker } from './marker'; | ||
import { DynamicTemplate } from './dynamic-templates/dynamic-template'; | ||
import { Node } from './node'; | ||
import { EdgePathGenerator } from './dynamic-templates/edge-path-generators'; | ||
/** | ||
* Registry for edge path generators. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export declare class EdgePathGeneratorRegistry { | ||
private pathGenerators; | ||
constructor(); | ||
/** | ||
* Clears all path generators (including the default path generator). | ||
*/ | ||
clearAllPathGenerators(): void; | ||
/** | ||
* Add a new path generator to the registry. | ||
* | ||
* @param pathGeneratorId the key to register the path generator with | ||
* @param pathGenerator the path generator to register (`null` will remove the path generator with `pathGeneratorId`) | ||
*/ | ||
addEdgePathGenerator(pathGeneratorId: string, pathGenerator: EdgePathGenerator): void; | ||
/** | ||
* Remove a registered path generator. | ||
* | ||
* @param pathGeneratorId the id to remove | ||
*/ | ||
removePathGenerator(pathGeneratorId: string): void; | ||
/** | ||
* Get the edge path generator. | ||
* | ||
* If the id was not found the id 'default' will be used instead. | ||
* @param pathGeneratorId the id to retrieve | ||
*/ | ||
getEdgePathGenerator(pathGeneratorId: string): EdgePathGenerator; | ||
} | ||
/** | ||
* Template registry for static templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export declare class StaticTemplateRegistry { | ||
private nodeTemplates; | ||
private nodeTemplateLinkHandles; | ||
private markerTemplates; | ||
private markerTemplateLineAttachements; | ||
private templateBBoxes; | ||
@@ -12,11 +56,93 @@ constructor(); | ||
* | ||
* This method searches for templates in the first `<defs>` element of the given svg. | ||
* | ||
* @param svg the svg to search for templates | ||
*/ | ||
updateTemplateCache(svg: Selection<SVGSVGElement, unknown, any, unknown>): void; | ||
/** | ||
* Get the bounding box of a static template (without link handles!). | ||
* | ||
* @param id the template id | ||
*/ | ||
getTemplateBBox(id: string): DOMRect; | ||
/** | ||
* Get the template id for the given node type. | ||
* | ||
* If the type is null or has no registered template the id `'default'` is returned instead. | ||
* | ||
* @param nodeType the type of the node | ||
*/ | ||
getNodeTemplateId(nodeType: string): string; | ||
/** | ||
* Get the static template for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplate(id: string): Selection<SVGGElement, unknown, any, unknown>; | ||
/** | ||
* Get the link handles for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplateLinkHandles(id: string): LinkHandle[]; | ||
/** | ||
* Get the template id for the given marker type. | ||
* | ||
* If the type is null or has no registered template the id `'default-marker'` is returned instead. | ||
* | ||
* @param nodeType the type of the marker | ||
*/ | ||
getMarkerTemplateId(markerType: string): string; | ||
/** | ||
* Get the static template for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerTemplate(markerType: string): Selection<SVGGElement, unknown, any, unknown>; | ||
/** | ||
* Get the line attachement point for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerAttachementPointInfo(markerType: string): LineAttachementInfo; | ||
} | ||
/** | ||
* Template registry for dynamic templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export declare class DynymicTemplateRegistry { | ||
private templates; | ||
constructor(); | ||
/** | ||
* Clears all dynamic templates (including any default templates). | ||
*/ | ||
clearAllTemplates(): void; | ||
/** | ||
* Add a new dynamic template to the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the id of the new template | ||
* @param template the new dynamic template (`null` will remove the template with `templateId`) | ||
*/ | ||
addDynamicTemplate(templateId: string, template: DynamicTemplate<Node | Marker | LinkHandle | TextComponent>): void; | ||
/** | ||
* Get a dynamic template from the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the template id | ||
*/ | ||
getDynamicTemplate<T extends DynamicTemplate<Node | Marker | LinkHandle | TextComponent>>(templateId: string): T; | ||
/** | ||
* Remove a dynamic template from the registry. | ||
* | ||
* @param templateId the template id | ||
*/ | ||
removeDynamicTemplate(templateId: string): void; | ||
} |
@@ -17,9 +17,68 @@ /* | ||
*/ | ||
import { calculateNormal, handlesForCircle, handlesForRectangle, handlesForPolygon, handlesForPath } from './link-handle'; | ||
import { calculateLinkHandleNormal, handlesForCircle, handlesForRectangle, handlesForPolygon, handlesForPath } from './link-handle'; | ||
import { select } from 'd3-selection'; | ||
export class TemplateCache { | ||
import { LineAttachementInfo } from './marker'; | ||
/** | ||
* Registry for edge path generators. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export class EdgePathGeneratorRegistry { | ||
constructor() { | ||
this.pathGenerators = new Map(); | ||
} | ||
/** | ||
* Clears all path generators (including the default path generator). | ||
*/ | ||
clearAllPathGenerators() { | ||
this.pathGenerators = new Map(); | ||
} | ||
/** | ||
* Add a new path generator to the registry. | ||
* | ||
* @param pathGeneratorId the key to register the path generator with | ||
* @param pathGenerator the path generator to register (`null` will remove the path generator with `pathGeneratorId`) | ||
*/ | ||
addEdgePathGenerator(pathGeneratorId, pathGenerator) { | ||
if (pathGenerator == null) { | ||
this.removePathGenerator(pathGeneratorId); | ||
return; | ||
} | ||
if (this.pathGenerators.has(pathGeneratorId)) { | ||
console.warn(`Path generator id ${pathGeneratorId} was already in use!`); | ||
} | ||
this.pathGenerators.set(pathGeneratorId, pathGenerator); | ||
} | ||
/** | ||
* Remove a registered path generator. | ||
* | ||
* @param pathGeneratorId the id to remove | ||
*/ | ||
removePathGenerator(pathGeneratorId) { | ||
this.pathGenerators.delete(pathGeneratorId); | ||
} | ||
/** | ||
* Get the edge path generator. | ||
* | ||
* If the id was not found the id 'default' will be used instead. | ||
* @param pathGeneratorId the id to retrieve | ||
*/ | ||
getEdgePathGenerator(pathGeneratorId) { | ||
if (pathGeneratorId == null || !this.pathGenerators.has(pathGeneratorId)) { | ||
return this.pathGenerators.get('default'); | ||
} | ||
return this.pathGenerators.get(pathGeneratorId); | ||
} | ||
} | ||
/** | ||
* Template registry for static templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export class StaticTemplateRegistry { | ||
constructor() { | ||
this.nodeTemplates = new Map(); | ||
this.nodeTemplateLinkHandles = new Map(); | ||
this.markerTemplates = new Map(); | ||
this.markerTemplateLineAttachements = new Map(); | ||
this.templateBBoxes = new Map(); | ||
@@ -30,2 +89,4 @@ } | ||
* | ||
* This method searches for templates in the first `<defs>` element of the given svg. | ||
* | ||
* @param svg the svg to search for templates | ||
@@ -37,2 +98,3 @@ */ | ||
const markerTemplates = new Map(); | ||
const markerTemplateLineAttachements = new Map(); | ||
const templateBBoxes = new Map(); | ||
@@ -77,2 +139,4 @@ const templates = svg.select('defs').selectAll('g[data-template-type]'); | ||
markerTemplates.set(id, template); | ||
const attachementInfo = new LineAttachementInfo(template.attr('data-line-attachement-point')); | ||
markerTemplateLineAttachements.set(id, attachementInfo); | ||
} | ||
@@ -88,6 +152,19 @@ // cleanup temp | ||
this.markerTemplates = markerTemplates; | ||
this.markerTemplateLineAttachements = markerTemplateLineAttachements; | ||
} | ||
/** | ||
* Get the bounding box of a static template (without link handles!). | ||
* | ||
* @param id the template id | ||
*/ | ||
getTemplateBBox(id) { | ||
return this.templateBBoxes.get(id); | ||
} | ||
/** | ||
* Get the template id for the given node type. | ||
* | ||
* If the type is null or has no registered template the id `'default'` is returned instead. | ||
* | ||
* @param nodeType the type of the node | ||
*/ | ||
getNodeTemplateId(nodeType) { | ||
@@ -101,5 +178,17 @@ if (nodeType == null || !this.nodeTemplates.has(nodeType)) { | ||
} | ||
/** | ||
* Get the static template for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplate(id) { | ||
return this.nodeTemplates.get(this.getNodeTemplateId(id)); | ||
} | ||
/** | ||
* Get the link handles for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplateLinkHandles(id) { | ||
@@ -112,2 +201,9 @@ const handles = this.nodeTemplateLinkHandles.get(this.getNodeTemplateId(id)); | ||
} | ||
/** | ||
* Get the template id for the given marker type. | ||
* | ||
* If the type is null or has no registered template the id `'default-marker'` is returned instead. | ||
* | ||
* @param nodeType the type of the marker | ||
*/ | ||
getMarkerTemplateId(markerType) { | ||
@@ -121,9 +217,72 @@ if (markerType == null || !this.markerTemplates.has(markerType)) { | ||
} | ||
/** | ||
* Get the static template for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerTemplate(markerType) { | ||
if (markerType == null || markerType === '') { | ||
console.warn('Cannot retrieve marker template for type ' + markerType + '!'); | ||
return this.markerTemplates.get(this.getMarkerTemplateId(markerType)); | ||
} | ||
/** | ||
* Get the line attachement point for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerAttachementPointInfo(markerType) { | ||
return this.markerTemplateLineAttachements.get(this.getMarkerTemplateId(markerType)); | ||
} | ||
} | ||
/** | ||
* Template registry for dynamic templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export class DynymicTemplateRegistry { | ||
constructor() { | ||
this.templates = new Map(); | ||
} | ||
/** | ||
* Clears all dynamic templates (including any default templates). | ||
*/ | ||
clearAllTemplates() { | ||
this.templates = new Map(); | ||
} | ||
/** | ||
* Add a new dynamic template to the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the id of the new template | ||
* @param template the new dynamic template (`null` will remove the template with `templateId`) | ||
*/ | ||
addDynamicTemplate(templateId, template) { | ||
if (template == null) { | ||
this.removeDynamicTemplate(templateId); | ||
return; | ||
} | ||
return this.markerTemplates.get(markerType); | ||
if (this.templates.has(templateId)) { | ||
console.warn(`Template id ${templateId} was already in use!`); | ||
} | ||
this.templates.set(templateId, template); | ||
} | ||
/** | ||
* Get a dynamic template from the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the template id | ||
*/ | ||
getDynamicTemplate(templateId) { | ||
return this.templates.get(templateId); | ||
} | ||
/** | ||
* Remove a dynamic template from the registry. | ||
* | ||
* @param templateId the template id | ||
*/ | ||
removeDynamicTemplate(templateId) { | ||
this.templates.delete(templateId); | ||
} | ||
} | ||
@@ -189,3 +348,3 @@ /** | ||
linkHandles.forEach((element, index) => element.id = index); | ||
linkHandles.forEach(calculateNormal); | ||
linkHandles.forEach(calculateLinkHandleNormal); | ||
return linkHandles; | ||
@@ -192,0 +351,0 @@ } |
@@ -36,4 +36,10 @@ /* | ||
const text = select(element); | ||
const x = parseFloat(text.attr('x')); | ||
const y = parseFloat(text.attr('y')); | ||
let x = parseFloat(text.attr('x')); | ||
if (isNaN(x)) { | ||
x = 0; | ||
} | ||
let y = parseFloat(text.attr('y')); | ||
if (isNaN(y)) { | ||
y = 0; | ||
} | ||
let width = parseFloat(text.attr('width')); | ||
@@ -40,0 +46,0 @@ if (isNaN(width)) { |
import { LinkHandle } from './link-handle'; | ||
import { Marker } from './marker'; | ||
import { RotationData } from './rotation-vector'; | ||
/** | ||
@@ -11,7 +12,23 @@ * A single point. | ||
/** | ||
* Interface describing the position of a marker, text component or link handle placed along an edge path. | ||
*/ | ||
export interface PathPositionRotationAndScale extends RotationData { | ||
/** The relative position of the marker on the edge (between 0 and 1). (Default `0`=='start') */ | ||
positionOnLine?: number | 'start' | 'end'; | ||
/** A factor to scale the marker. */ | ||
scale?: number; | ||
/** If true the scaling factor is applied relative to the stroke width. */ | ||
scaleRelative?: boolean; | ||
/** If true the relative rotation is applied as if the path always goes from left to right. */ | ||
ignorePathDirectionForRotation?: boolean; | ||
} | ||
/** | ||
* Normalize the positionOnLine argument to a number. (Default: `0`) | ||
* @param positionOnLine | ||
*/ | ||
export declare function normalizePositionOnLine(positionOnLine: number | 'start' | 'end'): number; | ||
/** | ||
* Interface for text components that are part of an edge. | ||
*/ | ||
export interface TextComponent { | ||
/** The relative position of the marker on the edge (between 0 and 1). */ | ||
positionOnLine: number; | ||
export interface TextComponent extends PathPositionRotationAndScale { | ||
/** The actual text content. */ | ||
@@ -27,4 +44,2 @@ value?: string; | ||
height?: number; | ||
/** The complete class attribute. */ | ||
class?: string; | ||
/** The padding is used to avoid collisions. */ | ||
@@ -36,2 +51,4 @@ padding?: number; | ||
offsetY?: number; | ||
/** The template to use for this text component. (Default: `'default-textcomponent'`) */ | ||
template?: string; | ||
} | ||
@@ -42,2 +59,8 @@ /** | ||
export interface Edge { | ||
/** | ||
* An optional explicit edge id. | ||
* | ||
* The edge id is normally computed by the `edgeId` function. | ||
* If this attribute is set it gets returned by `edgeId` instead of the computed id. | ||
*/ | ||
id?: number | string; | ||
@@ -48,8 +71,22 @@ /** The id of the source node of this edge. */ | ||
target: number | string; | ||
/** | ||
* The link handle of the source node the edge is attached to. | ||
* | ||
* This attribute is set automatically by the grapheditor. | ||
*/ | ||
sourceHandle?: LinkHandle; | ||
/** | ||
* The link handle of the target node the edge is attached to. | ||
* | ||
* This attribute is set automatically by the grapheditor. | ||
*/ | ||
targetHandle?: LinkHandle; | ||
/** Edge type. Can be used for styling. */ | ||
type?: any; | ||
/** The id of the path generator used for this edge. */ | ||
pathType?: string; | ||
/** List of markers to draw for this edge. */ | ||
markers?: Marker[]; | ||
/** Markers to draw at the start of this edge. */ | ||
markerStart?: Marker; | ||
/** Markers to draw at the end of this edge. */ | ||
@@ -65,2 +102,7 @@ markerEnd?: Marker; | ||
export interface DraggedEdge extends Edge { | ||
/** | ||
* Explicit id of the dragged edge. | ||
* | ||
* A dragged edge may have no current target and must to specify an explicit id! | ||
*/ | ||
id: string; | ||
@@ -67,0 +109,0 @@ /** If edge was created from an existing edge this is the id of the existing edge. */ |
@@ -20,2 +20,19 @@ "use strict"; | ||
/** | ||
* Normalize the positionOnLine argument to a number. (Default: `0`) | ||
* @param positionOnLine | ||
*/ | ||
function normalizePositionOnLine(positionOnLine) { | ||
if (positionOnLine == null || positionOnLine === 'start') { | ||
return 0; | ||
} | ||
if (positionOnLine === 'end') { | ||
return 1; | ||
} | ||
if (isNaN(positionOnLine)) { | ||
return 0; | ||
} | ||
return positionOnLine; | ||
} | ||
exports.normalizePositionOnLine = normalizePositionOnLine; | ||
/** | ||
* Return edge id if set or calculate a new id from target and source. | ||
@@ -22,0 +39,0 @@ * |
@@ -5,2 +5,4 @@ import { Selection } from 'd3-selection'; | ||
import { LinkHandle } from './link-handle'; | ||
import { RotationVector } from './rotation-vector'; | ||
import { StaticTemplateRegistry, DynymicTemplateRegistry, EdgePathGeneratorRegistry } from './templating'; | ||
/** | ||
@@ -23,3 +25,2 @@ * An enum describing the source of the event. | ||
private zoomActive; | ||
private edgeGenerator; | ||
private contentMinHeight; | ||
@@ -37,8 +38,31 @@ private contentMaxHeight; | ||
private _zoomMode; | ||
private templateCache; | ||
/** | ||
* The static template registry. | ||
* | ||
* The templates will be automatically loaded when the svg changes or `updateTemplates` gets called. | ||
*/ | ||
staticTemplateRegistry: StaticTemplateRegistry; | ||
/** | ||
* The dynamic template registry of this graph. | ||
* | ||
* The dynamic template registry does not get cleared automatically when the other | ||
* templates get updated! | ||
*/ | ||
dynamicTemplateRegistry: DynymicTemplateRegistry; | ||
/** | ||
* The edge path generator registry of this graph. | ||
* | ||
* The registry does not get cleared automatically when the other | ||
* templates get updated! | ||
*/ | ||
edgePathGeneratorRegistry: EdgePathGeneratorRegistry; | ||
private defaultEdgePathGenerator; | ||
/** | ||
* The object cache responsible for fast access of nodes and edges. | ||
*/ | ||
private objectCache; | ||
private interactionStateData; | ||
/** Private property to determine if the graph can be drawn. */ | ||
private readonly initialized; | ||
private readonly isInteractive; | ||
private get initialized(); | ||
private get isInteractive(); | ||
/** | ||
@@ -83,26 +107,31 @@ * Callback when a new dragged edge is created. | ||
}; | ||
get classes(): string[]; | ||
/** | ||
* The list of css classes used for dynamic css classes together with `setNodeClass` or `setEdgeClass`. | ||
*/ | ||
classes: string[]; | ||
* The list of css classes used for dynamic css classes together with `setNodeClass` or `setEdgeClass`. | ||
*/ | ||
set classes(classes: string[]); | ||
get nodeList(): Node[]; | ||
/** | ||
* The list of nodes. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
nodeList: Node[]; | ||
* The list of nodes. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
set nodeList(nodes: Node[]); | ||
get edgeList(): Edge[]; | ||
/** | ||
* The list of edges. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
edgeList: Edge[]; | ||
* The list of edges. | ||
* | ||
* This list should **not** be altered without updating the cache! | ||
*/ | ||
set edgeList(edges: Edge[]); | ||
get mode(): string; | ||
/** | ||
* The interaction mode of the grapheditor. | ||
*/ | ||
mode: string; | ||
* The interaction mode of the grapheditor. | ||
*/ | ||
set mode(mode: string); | ||
get zoomMode(): string; | ||
/** | ||
* The zoom mode of the grapheditor. | ||
*/ | ||
zoomMode: string; | ||
* The zoom mode of the grapheditor. | ||
*/ | ||
set zoomMode(mode: string); | ||
constructor(); | ||
@@ -113,3 +142,3 @@ connectedCallback(): void; | ||
*/ | ||
static readonly observedAttributes: string[]; | ||
static get observedAttributes(): string[]; | ||
/** | ||
@@ -219,3 +248,3 @@ * Callback when an attribute changed in html dom. | ||
*/ | ||
private initialize; | ||
initialize(svg: any): void; | ||
/** | ||
@@ -269,5 +298,22 @@ * Calculate and store the size of the svg. | ||
* @param templateType the template type to use | ||
* @param dynamic `true` iff the template is a dynamic template (default: `false`) | ||
*/ | ||
private updateContentTemplate; | ||
/** | ||
* Update the static content template of a `SVGGElement` to the new template id. | ||
* | ||
* If the `SVGGElement` already uses the template the content is not touched. | ||
* | ||
* @param element the lement to update the content | ||
* @param templateId the new template ID | ||
* @param templateType the template type to use | ||
*/ | ||
private updateStaticContentTemplate; | ||
/** | ||
* Get a single node selection with bound datum. | ||
* | ||
* @param nodeId the id of the node to select | ||
*/ | ||
private getSingleNodeSelection; | ||
/** | ||
* Update existing nodes. | ||
@@ -285,11 +331,13 @@ * | ||
/** | ||
* Update non text elements of existing nodes. | ||
* Update non text elements of existing nodes or edges. | ||
* | ||
* @param nodeSelection d3 selection of nodes to update with bound data | ||
* @param groupSelection d3 selection of nodes or edges to update with bound data | ||
*/ | ||
private updateNodeDynamicProperties; | ||
private updateDynamicProperties; | ||
/** | ||
* Recursively retrieve an attribute. | ||
* | ||
* This only supports '.' access of attributes. | ||
* The attribute path is a string split at the '.' character. | ||
* The attribute path is processed recursively by applying `obj = obj[attr[0]]`. | ||
* If a path segment is '()' then `obj = obj()` is applied instead. | ||
* | ||
@@ -299,3 +347,3 @@ * @param obj the object to get the attribute from | ||
*/ | ||
private recursiveAttributeGet; | ||
recursiveAttributeGet(obj: any, attr: string): any; | ||
/** | ||
@@ -351,2 +399,16 @@ * Update node classes. | ||
/** | ||
* Calculate the attachement vector for a marker. | ||
* | ||
* @param startingAngle the line angle for the marker | ||
* @param marker the selection of a single marker | ||
* @param strokeWidth the current stroke width | ||
*/ | ||
private calculateLineAttachementVector; | ||
/** | ||
* Calculate the link handles for each edge and store them into the edge. | ||
* | ||
* @param edgeSelection d3 selection of edges to update with bound data | ||
*/ | ||
private updateEdgeLinkHandles; | ||
/** | ||
* Update existing edge path. | ||
@@ -361,6 +423,7 @@ * | ||
* @param markerSelection d3 selection | ||
* @param edge the edge datum this marker belongs to | ||
*/ | ||
private updateMarker; | ||
/** | ||
* Update edge-end marker. | ||
* Update edge-end and edge-start marker. | ||
* | ||
@@ -370,9 +433,57 @@ * @param edgeGroupSelection d3 selection of single edge group | ||
*/ | ||
private updateEndMarkers; | ||
/** | ||
* Update a specific edge end marker (either start or end marker). | ||
* | ||
* @param edgeGroupSelection d3 selection of single edge group | ||
* @param marker the special end marker | ||
* @param markerClass the css class to select for | ||
* @param edge the edge datum this marker belongs to | ||
*/ | ||
private updateEndMarker; | ||
/** | ||
* Update position of edge-end marker. | ||
* Calculate a normal vector pointing in the direction of the path at the positonOnLine. | ||
* | ||
* @param path the path object | ||
* @param positionOnLine the relative position on the path (between 0 and 1) | ||
* @param point the point at positionOnLine (will be calculated if not supplied) | ||
* @param length the length of the path (will be calculated if not supplied) | ||
*/ | ||
calculatePathNormalAtPosition(path: SVGPathElement, positionOnLine: number, point?: DOMPoint, length?: number): RotationVector; | ||
/** | ||
* Calculate the transformation attribute for a path object placed on an edge. | ||
* | ||
* @param point the path object position position | ||
* @param pathObject the path object to place | ||
* @param strokeWidth the stroke width of the edge | ||
* @param normal the normal vector of the edge at the path object position | ||
*/ | ||
private calculatePathObjectTransformation; | ||
/** | ||
* Calculate the rotation vector from rotation data and a normal vector. | ||
* | ||
* @param rotationData the rotation data object | ||
* @param normal the normal vector used for relative rotation | ||
* @param ignorePathDirectionForRotation iff true the normal rotation is limited to half a circle (useful for text components) | ||
*/ | ||
private calculateRotationTransformationAngle; | ||
/** | ||
* Update positions of edge-end and edge-start marker. | ||
* | ||
* @param edgeGroupSelection d3 selection of single edge group | ||
* @param d edge datum | ||
*/ | ||
private updateEndMarkerPositions; | ||
/** | ||
* Update a single end marker position (either start or end marker). | ||
* | ||
* @param path the path selection | ||
* @param length the path length | ||
* @param positionOnLine positionOnLine at the marker | ||
* @param marker the marker | ||
* @param handle the link handle at the path end of the marker; can be `null` | ||
* @param markerClass the class of the marker | ||
* @param strokeWidth the edge stroke width | ||
* @param edgeGroupSelection d3 selection of a single edge group | ||
*/ | ||
private updateEndMarkerPosition; | ||
@@ -386,2 +497,19 @@ /** | ||
/** | ||
* Apply a transformation to a bbox. | ||
* | ||
* @param bbox the bbox to transform | ||
* @param transformation the transformation matrix | ||
*/ | ||
transformBBox(bbox: { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
}, transformation: DOMMatrix): { | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
}; | ||
/** | ||
* Update all edge text positions in a edge group. | ||
@@ -388,0 +516,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { RotationVector } from './rotation-vector'; | ||
import { RotationVector, RotationData } from './rotation-vector'; | ||
import { Point } from './edge'; | ||
@@ -6,3 +6,3 @@ /** | ||
*/ | ||
export interface LinkHandle { | ||
export interface LinkHandle extends RotationData { | ||
/** Normally the index of the LinkHandle. Unique for each template. */ | ||
@@ -20,2 +20,6 @@ id: number; | ||
normal?: RotationVector; | ||
/** The template to use for the link handle. */ | ||
template?: string; | ||
/** True iff the link handle uses a dynamic template. */ | ||
isDynamicTemplate?: boolean; | ||
} | ||
@@ -27,3 +31,3 @@ /** | ||
*/ | ||
export declare function calculateNormal(handle: LinkHandle): void; | ||
export declare function calculateLinkHandleNormal(handle: LinkHandle): void; | ||
/** | ||
@@ -30,0 +34,0 @@ * Generate link handles list for a rectangle. |
@@ -25,3 +25,3 @@ "use strict"; | ||
*/ | ||
function calculateNormal(handle) { | ||
function calculateLinkHandleNormal(handle) { | ||
const x = handle.normal != null ? handle.normal.dx : handle.x; | ||
@@ -34,3 +34,3 @@ const y = handle.normal != null ? handle.normal.dy : handle.y; | ||
} | ||
exports.calculateNormal = calculateNormal; | ||
exports.calculateLinkHandleNormal = calculateLinkHandleNormal; | ||
/** | ||
@@ -72,3 +72,3 @@ * Generate link handles list for a rectangle. | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -118,3 +118,3 @@ } | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -149,3 +149,3 @@ } | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -182,3 +182,3 @@ } | ||
handles.forEach((element, index) => { element.id = index; }); | ||
handles.forEach(calculateNormal); | ||
handles.forEach(calculateLinkHandleNormal); | ||
return handles; | ||
@@ -185,0 +185,0 @@ } |
import { RotationVector } from './rotation-vector'; | ||
interface RotationData { | ||
/** Absolute rotation via direction vector. */ | ||
normal?: RotationVector; | ||
/** Relative angle in degree. */ | ||
relativeAngle: number; | ||
} | ||
export interface Marker { | ||
import { PathPositionRotationAndScale, Point } from './edge'; | ||
/** | ||
* Interface describing an edge marker. | ||
*/ | ||
export interface Marker extends PathPositionRotationAndScale { | ||
/** the marker template id to use for this marker. */ | ||
template: string; | ||
/** The relative position of the marker on the edge (between 0 and 1). */ | ||
positionOnLine: number | string; | ||
/** The length used for end/start markers to offset the line position. */ | ||
lineOffset?: number; | ||
/** True iff the link handle uses a dynamic template. */ | ||
isDynamicTemplate?: boolean; | ||
/** A factor to scale the marker. */ | ||
@@ -19,7 +15,39 @@ scale?: number; | ||
scaleRelative?: boolean; | ||
/** Rotation information for the marker. */ | ||
rotate?: RotationData; | ||
/** @deprecated Rotation information for the marker. (Use `absoluteRotation` and `relativeRotation attributes instead!) */ | ||
rotate?: { | ||
/** Absolute rotation via direction vector. */ | ||
normal?: RotationVector; | ||
/** Relative angle in degree. */ | ||
relativeAngle: number; | ||
}; | ||
/** A key used in a click event when the marker was clicked. */ | ||
clickEventKey?: string; | ||
} | ||
export {}; | ||
/** | ||
* Helper class to calculate where the edge attaches to an end marker. | ||
*/ | ||
export declare class LineAttachementInfo { | ||
private isDirectional; | ||
private lineAttachementAngle; | ||
private attachementOffset; | ||
/** | ||
* Create a new line attachement info object. | ||
* | ||
* The attachement point can either be an offset from 0,0 or a point relative to 0,0 of the template. | ||
* To specify an offset use a single number or a string containing exactly one number. | ||
* To specify a point use a string with two numbers seperated by a space or a point object. | ||
* The line attachement point must not include any transformations applied to the marker when it is rendered. | ||
* | ||
* If only an offset was specified the attachement info is not directional. | ||
* | ||
* @param lineAttachementPoint the attachement point relative to 0,0 in the template | ||
*/ | ||
constructor(lineAttachementPoint: string | number | Point); | ||
/** | ||
* Return a rotation vector pointing at the translated line attachement point. | ||
* | ||
* @param angle the angle the marker is currently rotated | ||
* @param scale the current scale of the marker | ||
*/ | ||
getRotationVector(angle: number, scale?: number): RotationVector; | ||
} |
@@ -19,2 +19,84 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const rotation_vector_1 = require("./rotation-vector"); | ||
/** | ||
* Helper class to calculate where the edge attaches to an end marker. | ||
*/ | ||
class LineAttachementInfo { | ||
/** | ||
* Create a new line attachement info object. | ||
* | ||
* The attachement point can either be an offset from 0,0 or a point relative to 0,0 of the template. | ||
* To specify an offset use a single number or a string containing exactly one number. | ||
* To specify a point use a string with two numbers seperated by a space or a point object. | ||
* The line attachement point must not include any transformations applied to the marker when it is rendered. | ||
* | ||
* If only an offset was specified the attachement info is not directional. | ||
* | ||
* @param lineAttachementPoint the attachement point relative to 0,0 in the template | ||
*/ | ||
constructor(lineAttachementPoint) { | ||
if (lineAttachementPoint == null) { | ||
lineAttachementPoint = 0; | ||
} | ||
if (typeof lineAttachementPoint === 'number') { | ||
this.attachementOffset = lineAttachementPoint; | ||
this.lineAttachementAngle = 0; | ||
this.isDirectional = false; | ||
} | ||
else if (typeof lineAttachementPoint === 'string') { | ||
const coords = lineAttachementPoint.toString().split(' '); | ||
if (coords.length === 1) { | ||
this.attachementOffset = parseFloat(coords[0]); | ||
this.lineAttachementAngle = 0; | ||
this.isDirectional = false; | ||
} | ||
else if (coords.length === 2) { | ||
const normal = { dx: parseFloat(coords[0]), dy: parseFloat(coords[1]) }; | ||
this.attachementOffset = rotation_vector_1.calculateLength(normal); | ||
this.lineAttachementAngle = rotation_vector_1.calculateAngle(normal); | ||
this.isDirectional = true; | ||
} | ||
else { | ||
console.warn('lineAttachementPoint must be one or two numbers seperated by a space!'); | ||
} | ||
} | ||
else { | ||
const normal = { dx: lineAttachementPoint.x, dy: lineAttachementPoint.y }; | ||
this.attachementOffset = rotation_vector_1.calculateLength(normal); | ||
this.lineAttachementAngle = rotation_vector_1.calculateAngle(normal); | ||
this.isDirectional = true; | ||
} | ||
if (this.attachementOffset == null || isNaN(this.attachementOffset)) { | ||
console.warn('Could not parse attachement offset! Using 0 instead.'); | ||
this.attachementOffset = 0; | ||
} | ||
if (this.lineAttachementAngle == null || isNaN(this.lineAttachementAngle)) { | ||
console.warn('Could not parse attachement angle! Using 0 instead.'); | ||
this.lineAttachementAngle = 0; | ||
this.isDirectional = false; | ||
} | ||
} | ||
/** | ||
* Return a rotation vector pointing at the translated line attachement point. | ||
* | ||
* @param angle the angle the marker is currently rotated | ||
* @param scale the current scale of the marker | ||
*/ | ||
getRotationVector(angle, scale) { | ||
let attachementAngle; | ||
if (this.isDirectional) { | ||
attachementAngle = angle + this.lineAttachementAngle; | ||
} | ||
else { | ||
// rotate attachement angle | ||
attachementAngle = angle + 180; | ||
} | ||
let offset = this.attachementOffset; | ||
if (scale != null || scale === 0) { | ||
offset = this.attachementOffset * scale; | ||
} | ||
return rotation_vector_1.angleToVector(attachementAngle, offset); | ||
} | ||
} | ||
exports.LineAttachementInfo = LineAttachementInfo; | ||
//# sourceMappingURL=marker.js.map |
@@ -13,3 +13,5 @@ /** | ||
type?: any; | ||
/** The id of the dynamic node template to use for this node. */ | ||
dynamicTemplate?: string; | ||
[prop: string]: any; | ||
} |
import { Node } from './node'; | ||
import { Edge, DraggedEdge, Point } from './edge'; | ||
import { LinkHandle } from './link-handle'; | ||
import { TemplateCache } from './templating'; | ||
import { Edge } from './edge'; | ||
export declare class GraphObjectCache { | ||
private templateCache; | ||
private nodes; | ||
@@ -12,3 +9,3 @@ private nodeBBoxes; | ||
private edgesByTarget; | ||
constructor(templateCache: TemplateCache); | ||
constructor(); | ||
updateNodeCache(nodes: Node[]): void; | ||
@@ -22,18 +19,2 @@ updateEdgeCache(edges: Edge[]): void; | ||
getEdgesBySource(sourceId: number | string): Set<Edge>; | ||
getEdgeLinkHandles(edge: Edge | DraggedEdge, _calculateHandlesToUse?: (edge: Edge | DraggedEdge, sourceHandles: LinkHandle[], source: Node, targetHandles: LinkHandle[], target: Node | Point) => { | ||
sourceHandles: LinkHandle[]; | ||
targetHandles: LinkHandle[]; | ||
}): { | ||
sourceHandle: LinkHandle; | ||
sourceCoordinates: { | ||
x: number; | ||
y: number; | ||
}; | ||
targetHandle: LinkHandle; | ||
targetCoordinates: { | ||
x: number; | ||
y: number; | ||
}; | ||
}; | ||
private calculateNearestHandles; | ||
} |
@@ -20,6 +20,4 @@ "use strict"; | ||
const edge_1 = require("./edge"); | ||
const link_handle_1 = require("./link-handle"); | ||
class GraphObjectCache { | ||
constructor(templateCache) { | ||
this.templateCache = templateCache; | ||
constructor() { | ||
this.nodes = new Map(); | ||
@@ -86,81 +84,4 @@ this.nodeBBoxes = new Map(); | ||
} | ||
// tslint:disable-next-line:max-line-length | ||
getEdgeLinkHandles(edge, _calculateHandlesToUse) { | ||
let source = this.getNode(edge.source); | ||
let sourceHandles = edge.sourceHandle != null ? [edge.sourceHandle] : [{ id: 0, x: 0, y: 0, normal: { dx: 0, dy: 0 } }]; | ||
if (source != null) { | ||
sourceHandles = this.templateCache.getNodeTemplateLinkHandles(source.type); | ||
} | ||
else { | ||
console.warn('Attempting to render Edge without valid source.', edge); | ||
source = { id: 'UNDEFINED', x: 0, y: 0 }; | ||
} | ||
let target = edge.target != null ? this.getNode(edge.target) : null; | ||
let targetHandles = edge.targetHandle != null ? [edge.targetHandle] : [{ id: 0, x: 0, y: 0, normal: { dx: 0, dy: 0 } }]; | ||
if (target != null) { | ||
targetHandles = this.templateCache.getNodeTemplateLinkHandles(target.type); | ||
} | ||
else { | ||
if (edge.currentTarget != null) { | ||
target = edge.currentTarget; | ||
} | ||
else { | ||
console.warn('Attempting to render Edge without valid target.', edge); | ||
target = { id: 'UNDEFINED', x: 1, y: 1 }; | ||
} | ||
} | ||
if (_calculateHandlesToUse != null) { | ||
// replace template link handle lists with user calculated lists | ||
const calculatedHandles = _calculateHandlesToUse(edge, sourceHandles, source, targetHandles, target); | ||
if (calculatedHandles != null && calculatedHandles.sourceHandles != null && calculatedHandles.sourceHandles.length > 0) { | ||
sourceHandles = calculatedHandles.sourceHandles; | ||
} | ||
if (calculatedHandles != null && calculatedHandles.targetHandles != null && calculatedHandles.targetHandles.length > 0) { | ||
targetHandles = calculatedHandles.targetHandles; | ||
} | ||
} | ||
const result = this.calculateNearestHandles(sourceHandles, source, targetHandles, target); | ||
return { | ||
sourceHandle: result.sourceHandle, | ||
sourceCoordinates: { x: (source.x + result.sourceHandle.x), y: (source.y + result.sourceHandle.y) }, | ||
targetHandle: result.targetHandle, | ||
targetCoordinates: { x: (target.x + result.targetHandle.x), y: (target.y + result.targetHandle.y) }, | ||
}; | ||
} | ||
calculateNearestHandles(sourceHandles, source, targetHandles, target) { | ||
let currentSourceHandle = { id: 0, x: 0, y: 0, normal: { dx: 1, dy: 1 } }; | ||
if (sourceHandles != null && sourceHandles.length > 0) { | ||
currentSourceHandle = sourceHandles[0]; | ||
} | ||
else { | ||
link_handle_1.calculateNormal(currentSourceHandle); | ||
} | ||
let currentTargetHandle = { id: 0, x: 0, y: 0, normal: { dx: 1, dy: 1 } }; | ||
if (targetHandles != null && targetHandles.length > 0) { | ||
currentTargetHandle = targetHandles[0]; | ||
} | ||
else { | ||
link_handle_1.calculateNormal(currentTargetHandle); | ||
} | ||
let currentDist = Math.pow((source.x + currentSourceHandle.x) - target.x, 2) + | ||
Math.pow((source.y + currentSourceHandle.y) - target.y, 2); | ||
targetHandles.forEach((targetHandle) => { | ||
for (let i = 0; i < sourceHandles.length; i++) { | ||
const handle = sourceHandles[i]; | ||
const dist = Math.pow((source.x + handle.x) - (target.x + targetHandle.x), 2) + | ||
Math.pow((source.y + handle.y) - (target.y + targetHandle.y), 2); | ||
if (dist <= currentDist) { | ||
currentSourceHandle = handle; | ||
currentTargetHandle = targetHandle; | ||
currentDist = dist; | ||
} | ||
} | ||
}); | ||
return { | ||
sourceHandle: currentSourceHandle, | ||
targetHandle: currentTargetHandle, | ||
}; | ||
} | ||
} | ||
exports.GraphObjectCache = GraphObjectCache; | ||
//# sourceMappingURL=object-cache.js.map |
@@ -6,2 +6,21 @@ export interface RotationVector { | ||
/** | ||
* Interface holding rotation information of a graph object. | ||
*/ | ||
export interface RotationData { | ||
/** | ||
* Absolute rotation angle in degree. | ||
* | ||
* Absolute rotation overwrites any relative rotation. | ||
*/ | ||
absoluteRotation?: number; | ||
/** Relative rotation angle in degree. */ | ||
relativeRotation?: number; | ||
} | ||
/** | ||
* Calculate the absolute length of the given vector. | ||
* | ||
* @param vector vector to calculate length of | ||
*/ | ||
export declare function calculateLength(vector: RotationVector): number; | ||
/** | ||
* Normalize an existing vector to length 1. | ||
@@ -18,1 +37,8 @@ * | ||
export declare function calculateAngle(vector: RotationVector): number; | ||
/** | ||
* Calculate a rotation vector from an angle in degree and a length. | ||
* | ||
* @param angle in degree | ||
* @param length length of the vector (defaults to 1) | ||
*/ | ||
export declare function angleToVector(angle: number, length?: number): RotationVector; |
@@ -20,2 +20,13 @@ "use strict"; | ||
/** | ||
* Calculate the absolute length of the given vector. | ||
* | ||
* @param vector vector to calculate length of | ||
*/ | ||
function calculateLength(vector) { | ||
const x = vector.dx; | ||
const y = vector.dy; | ||
return Math.sqrt(x * x + y * y); | ||
} | ||
exports.calculateLength = calculateLength; | ||
/** | ||
* Normalize an existing vector to length 1. | ||
@@ -49,2 +60,22 @@ * | ||
exports.calculateAngle = calculateAngle; | ||
/** | ||
* Calculate a rotation vector from an angle in degree and a length. | ||
* | ||
* @param angle in degree | ||
* @param length length of the vector (defaults to 1) | ||
*/ | ||
function angleToVector(angle, length) { | ||
if (length == null || isNaN(length)) { | ||
length = 1; | ||
} | ||
if (angle == null || isNaN(angle)) { | ||
angle = 0; | ||
} | ||
angle = (angle * Math.PI) / 180; | ||
return { | ||
dx: Math.cos(angle) * length, | ||
dy: Math.sin(angle) * length, | ||
}; | ||
} | ||
exports.angleToVector = angleToVector; | ||
//# sourceMappingURL=rotation-vector.js.map |
import { LinkHandle } from './link-handle'; | ||
import { Selection } from 'd3-selection'; | ||
export declare class TemplateCache { | ||
import { TextComponent } from './edge'; | ||
import { LineAttachementInfo, Marker } from './marker'; | ||
import { DynamicTemplate } from './dynamic-templates/dynamic-template'; | ||
import { Node } from './node'; | ||
import { EdgePathGenerator } from './dynamic-templates/edge-path-generators'; | ||
/** | ||
* Registry for edge path generators. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export declare class EdgePathGeneratorRegistry { | ||
private pathGenerators; | ||
constructor(); | ||
/** | ||
* Clears all path generators (including the default path generator). | ||
*/ | ||
clearAllPathGenerators(): void; | ||
/** | ||
* Add a new path generator to the registry. | ||
* | ||
* @param pathGeneratorId the key to register the path generator with | ||
* @param pathGenerator the path generator to register (`null` will remove the path generator with `pathGeneratorId`) | ||
*/ | ||
addEdgePathGenerator(pathGeneratorId: string, pathGenerator: EdgePathGenerator): void; | ||
/** | ||
* Remove a registered path generator. | ||
* | ||
* @param pathGeneratorId the id to remove | ||
*/ | ||
removePathGenerator(pathGeneratorId: string): void; | ||
/** | ||
* Get the edge path generator. | ||
* | ||
* If the id was not found the id 'default' will be used instead. | ||
* @param pathGeneratorId the id to retrieve | ||
*/ | ||
getEdgePathGenerator(pathGeneratorId: string): EdgePathGenerator; | ||
} | ||
/** | ||
* Template registry for static templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export declare class StaticTemplateRegistry { | ||
private nodeTemplates; | ||
private nodeTemplateLinkHandles; | ||
private markerTemplates; | ||
private markerTemplateLineAttachements; | ||
private templateBBoxes; | ||
@@ -12,11 +56,93 @@ constructor(); | ||
* | ||
* This method searches for templates in the first `<defs>` element of the given svg. | ||
* | ||
* @param svg the svg to search for templates | ||
*/ | ||
updateTemplateCache(svg: Selection<SVGSVGElement, unknown, any, unknown>): void; | ||
/** | ||
* Get the bounding box of a static template (without link handles!). | ||
* | ||
* @param id the template id | ||
*/ | ||
getTemplateBBox(id: string): DOMRect; | ||
/** | ||
* Get the template id for the given node type. | ||
* | ||
* If the type is null or has no registered template the id `'default'` is returned instead. | ||
* | ||
* @param nodeType the type of the node | ||
*/ | ||
getNodeTemplateId(nodeType: string): string; | ||
/** | ||
* Get the static template for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplate(id: string): Selection<SVGGElement, unknown, any, unknown>; | ||
/** | ||
* Get the link handles for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplateLinkHandles(id: string): LinkHandle[]; | ||
/** | ||
* Get the template id for the given marker type. | ||
* | ||
* If the type is null or has no registered template the id `'default-marker'` is returned instead. | ||
* | ||
* @param nodeType the type of the marker | ||
*/ | ||
getMarkerTemplateId(markerType: string): string; | ||
/** | ||
* Get the static template for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerTemplate(markerType: string): Selection<SVGGElement, unknown, any, unknown>; | ||
/** | ||
* Get the line attachement point for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerAttachementPointInfo(markerType: string): LineAttachementInfo; | ||
} | ||
/** | ||
* Template registry for dynamic templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
export declare class DynymicTemplateRegistry { | ||
private templates; | ||
constructor(); | ||
/** | ||
* Clears all dynamic templates (including any default templates). | ||
*/ | ||
clearAllTemplates(): void; | ||
/** | ||
* Add a new dynamic template to the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the id of the new template | ||
* @param template the new dynamic template (`null` will remove the template with `templateId`) | ||
*/ | ||
addDynamicTemplate(templateId: string, template: DynamicTemplate<Node | Marker | LinkHandle | TextComponent>): void; | ||
/** | ||
* Get a dynamic template from the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the template id | ||
*/ | ||
getDynamicTemplate<T extends DynamicTemplate<Node | Marker | LinkHandle | TextComponent>>(templateId: string): T; | ||
/** | ||
* Remove a dynamic template from the registry. | ||
* | ||
* @param templateId the template id | ||
*/ | ||
removeDynamicTemplate(templateId: string): void; | ||
} |
@@ -21,7 +21,67 @@ "use strict"; | ||
const d3_selection_1 = require("d3-selection"); | ||
class TemplateCache { | ||
const marker_1 = require("./marker"); | ||
/** | ||
* Registry for edge path generators. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
class EdgePathGeneratorRegistry { | ||
constructor() { | ||
this.pathGenerators = new Map(); | ||
} | ||
/** | ||
* Clears all path generators (including the default path generator). | ||
*/ | ||
clearAllPathGenerators() { | ||
this.pathGenerators = new Map(); | ||
} | ||
/** | ||
* Add a new path generator to the registry. | ||
* | ||
* @param pathGeneratorId the key to register the path generator with | ||
* @param pathGenerator the path generator to register (`null` will remove the path generator with `pathGeneratorId`) | ||
*/ | ||
addEdgePathGenerator(pathGeneratorId, pathGenerator) { | ||
if (pathGenerator == null) { | ||
this.removePathGenerator(pathGeneratorId); | ||
return; | ||
} | ||
if (this.pathGenerators.has(pathGeneratorId)) { | ||
console.warn(`Path generator id ${pathGeneratorId} was already in use!`); | ||
} | ||
this.pathGenerators.set(pathGeneratorId, pathGenerator); | ||
} | ||
/** | ||
* Remove a registered path generator. | ||
* | ||
* @param pathGeneratorId the id to remove | ||
*/ | ||
removePathGenerator(pathGeneratorId) { | ||
this.pathGenerators.delete(pathGeneratorId); | ||
} | ||
/** | ||
* Get the edge path generator. | ||
* | ||
* If the id was not found the id 'default' will be used instead. | ||
* @param pathGeneratorId the id to retrieve | ||
*/ | ||
getEdgePathGenerator(pathGeneratorId) { | ||
if (pathGeneratorId == null || !this.pathGenerators.has(pathGeneratorId)) { | ||
return this.pathGenerators.get('default'); | ||
} | ||
return this.pathGenerators.get(pathGeneratorId); | ||
} | ||
} | ||
exports.EdgePathGeneratorRegistry = EdgePathGeneratorRegistry; | ||
/** | ||
* Template registry for static templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
class StaticTemplateRegistry { | ||
constructor() { | ||
this.nodeTemplates = new Map(); | ||
this.nodeTemplateLinkHandles = new Map(); | ||
this.markerTemplates = new Map(); | ||
this.markerTemplateLineAttachements = new Map(); | ||
this.templateBBoxes = new Map(); | ||
@@ -32,2 +92,4 @@ } | ||
* | ||
* This method searches for templates in the first `<defs>` element of the given svg. | ||
* | ||
* @param svg the svg to search for templates | ||
@@ -39,2 +101,3 @@ */ | ||
const markerTemplates = new Map(); | ||
const markerTemplateLineAttachements = new Map(); | ||
const templateBBoxes = new Map(); | ||
@@ -79,2 +142,4 @@ const templates = svg.select('defs').selectAll('g[data-template-type]'); | ||
markerTemplates.set(id, template); | ||
const attachementInfo = new marker_1.LineAttachementInfo(template.attr('data-line-attachement-point')); | ||
markerTemplateLineAttachements.set(id, attachementInfo); | ||
} | ||
@@ -90,6 +155,19 @@ // cleanup temp | ||
this.markerTemplates = markerTemplates; | ||
this.markerTemplateLineAttachements = markerTemplateLineAttachements; | ||
} | ||
/** | ||
* Get the bounding box of a static template (without link handles!). | ||
* | ||
* @param id the template id | ||
*/ | ||
getTemplateBBox(id) { | ||
return this.templateBBoxes.get(id); | ||
} | ||
/** | ||
* Get the template id for the given node type. | ||
* | ||
* If the type is null or has no registered template the id `'default'` is returned instead. | ||
* | ||
* @param nodeType the type of the node | ||
*/ | ||
getNodeTemplateId(nodeType) { | ||
@@ -103,5 +181,17 @@ if (nodeType == null || !this.nodeTemplates.has(nodeType)) { | ||
} | ||
/** | ||
* Get the static template for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplate(id) { | ||
return this.nodeTemplates.get(this.getNodeTemplateId(id)); | ||
} | ||
/** | ||
* Get the link handles for the given node type. | ||
* | ||
* This method uses `getNodeTemplateId`. | ||
* @param id the template id (normally the node type) | ||
*/ | ||
getNodeTemplateLinkHandles(id) { | ||
@@ -114,2 +204,9 @@ const handles = this.nodeTemplateLinkHandles.get(this.getNodeTemplateId(id)); | ||
} | ||
/** | ||
* Get the template id for the given marker type. | ||
* | ||
* If the type is null or has no registered template the id `'default-marker'` is returned instead. | ||
* | ||
* @param nodeType the type of the marker | ||
*/ | ||
getMarkerTemplateId(markerType) { | ||
@@ -123,11 +220,75 @@ if (markerType == null || !this.markerTemplates.has(markerType)) { | ||
} | ||
/** | ||
* Get the static template for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerTemplate(markerType) { | ||
if (markerType == null || markerType === '') { | ||
console.warn('Cannot retrieve marker template for type ' + markerType + '!'); | ||
return this.markerTemplates.get(this.getMarkerTemplateId(markerType)); | ||
} | ||
/** | ||
* Get the line attachement point for the given marker type. | ||
* | ||
* This method uses `getMarkerTemplateId`. | ||
* @param id the template id (normally the marker type) | ||
*/ | ||
getMarkerAttachementPointInfo(markerType) { | ||
return this.markerTemplateLineAttachements.get(this.getMarkerTemplateId(markerType)); | ||
} | ||
} | ||
exports.StaticTemplateRegistry = StaticTemplateRegistry; | ||
/** | ||
* Template registry for dynamic templates. | ||
* | ||
* The current instance can be retrieved from the GraphEditor object. | ||
*/ | ||
class DynymicTemplateRegistry { | ||
constructor() { | ||
this.templates = new Map(); | ||
} | ||
/** | ||
* Clears all dynamic templates (including any default templates). | ||
*/ | ||
clearAllTemplates() { | ||
this.templates = new Map(); | ||
} | ||
/** | ||
* Add a new dynamic template to the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the id of the new template | ||
* @param template the new dynamic template (`null` will remove the template with `templateId`) | ||
*/ | ||
addDynamicTemplate(templateId, template) { | ||
if (template == null) { | ||
this.removeDynamicTemplate(templateId); | ||
return; | ||
} | ||
return this.markerTemplates.get(markerType); | ||
if (this.templates.has(templateId)) { | ||
console.warn(`Template id ${templateId} was already in use!`); | ||
} | ||
this.templates.set(templateId, template); | ||
} | ||
/** | ||
* Get a dynamic template from the registry. | ||
* | ||
* The registry does not ensure type safety for templates on get! | ||
* | ||
* @param templateId the template id | ||
*/ | ||
getDynamicTemplate(templateId) { | ||
return this.templates.get(templateId); | ||
} | ||
/** | ||
* Remove a dynamic template from the registry. | ||
* | ||
* @param templateId the template id | ||
*/ | ||
removeDynamicTemplate(templateId) { | ||
this.templates.delete(templateId); | ||
} | ||
} | ||
exports.TemplateCache = TemplateCache; | ||
exports.DynymicTemplateRegistry = DynymicTemplateRegistry; | ||
/** | ||
@@ -192,3 +353,3 @@ * Calculate link handle positions for the given template. | ||
linkHandles.forEach((element, index) => element.id = index); | ||
linkHandles.forEach(link_handle_1.calculateNormal); | ||
linkHandles.forEach(link_handle_1.calculateLinkHandleNormal); | ||
return linkHandles; | ||
@@ -195,0 +356,0 @@ } |
@@ -38,4 +38,10 @@ "use strict"; | ||
const text = d3_selection_1.select(element); | ||
const x = parseFloat(text.attr('x')); | ||
const y = parseFloat(text.attr('y')); | ||
let x = parseFloat(text.attr('x')); | ||
if (isNaN(x)) { | ||
x = 0; | ||
} | ||
let y = parseFloat(text.attr('y')); | ||
if (isNaN(y)) { | ||
y = 0; | ||
} | ||
let width = parseFloat(text.attr('width')); | ||
@@ -42,0 +48,0 @@ if (isNaN(width)) { |
{ | ||
"name": "@ustutt/grapheditor-webcomponent", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Webcomponent for displaying and editing graphs.", | ||
@@ -40,5 +40,5 @@ "author": "Fabian Bühler", | ||
"dependencies": { | ||
"d3-drag": "^1.2.4", | ||
"d3-selection": "^1.4.0", | ||
"d3-shape": "^1.3.5", | ||
"d3-drag": "^1.2.5", | ||
"d3-selection": "^1.4.1", | ||
"d3-shape": "^1.3.7", | ||
"d3-zoom": "^1.8.3" | ||
@@ -51,7 +51,7 @@ }, | ||
"source-map-loader": "^0.2.4", | ||
"tslint": "^5.20.0", | ||
"typedoc": "^0.15.0", | ||
"typescript": "~3.6.4", | ||
"tslint": "^5.20.1", | ||
"@gerrit0/typedoc": "^0.15.10", | ||
"typescript": "~3.7.2", | ||
"webpack": "^4.41.2", | ||
"webpack-cli": "^3.3.9", | ||
"webpack-cli": "^3.3.10", | ||
"webpack-dev-server": "^3.9.0", | ||
@@ -58,0 +58,0 @@ "webpack-merge": "^4.2.2" |
@@ -13,3 +13,3 @@ # Grapheditor Webcomponent | ||
There is a [Quickstart](https://mico-grapheditor.readthedocs.io/en/latest/quickstart.html) available in the documentation and also a [Demo HTML](docs/example.html) for experimentation. | ||
There is a [Quickstart](https://mico-grapheditor.readthedocs.io/en/stable/quickstart.html) available in the documentation and also a [Demo HTML](docs/example.html) for experimentation. | ||
@@ -22,3 +22,4 @@ The library is packaged in three different formats on npm. | ||
The full documentation is available on [READTHEDOCS](https://mico-grapheditor.readthedocs.io). | ||
The full documentation is available on [READTHEDOCS (stable)](https://mico-grapheditor.readthedocs.io/en/stable). | ||
The documentation for the version in development can be found here: [READTHEDOCS (latest)](https://mico-grapheditor.readthedocs.io) | ||
@@ -25,0 +26,0 @@ --- |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1965767
126
10951
61
Updatedd3-drag@^1.2.5
Updatedd3-selection@^1.4.1
Updatedd3-shape@^1.3.7