@istar-ts/core 0.15.0-beta.0 → 0.15.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +142 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +141 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -625,6 +625,34 @@ export declare const ACTOR_PADDING = 10;
|
|
|
625
625
|
* what it actually draws.
|
|
626
626
|
*/
|
|
627
627
|
export declare function actorBounds<EK extends string, LK extends string>(model: IstarModel<EK, LK>, actorId: string, metamodel?: Metamodel<EK, LK>, defaultSizeOf?: (kind: EK) => Size): Box;
|
|
628
|
+
/** Space kept between a suggested box and any existing element (`suggestPosition`). */
|
|
629
|
+
export declare const SUGGEST_MARGIN = 20;
|
|
630
|
+
export interface SuggestPositionOptions<EK extends string> {
|
|
631
|
+
/** The kind of element being placed; its metamodel size is what must not overlap. */
|
|
632
|
+
readonly kind: EK;
|
|
633
|
+
/** Place inside this actor's boundary (`actorBounds`), growing it if full. */
|
|
634
|
+
readonly containerId?: string;
|
|
635
|
+
/** Prefer a spot adjacent to this element's id, or to this point. */
|
|
636
|
+
readonly near?: string | Point;
|
|
637
|
+
readonly metamodel?: Metamodel<EK, string>;
|
|
638
|
+
/** See `actorBounds`' `defaultSizeOf`. */
|
|
639
|
+
readonly defaultSizeOf?: (kind: EK) => Size;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* A free spot for a new element of `kind`, overlapping no existing element (sizes from the
|
|
643
|
+
* metamodel, or `defaultSizeOf` for a host with its own size overrides, as in `actorBounds`).
|
|
644
|
+
*
|
|
645
|
+
* Scans a deterministic, fixed-order set of candidates — rings around `near` when given, else
|
|
646
|
+
* the container's interior, else rings around the origin — and returns the first whose padded
|
|
647
|
+
* box (`SUGGEST_MARGIN`) overlaps nothing; same input always yields the same output. However
|
|
648
|
+
* dense the model, the result never overlaps: once every ring candidate is taken, it falls back
|
|
649
|
+
* to a point past the right edge of every obstacle (`guaranteedFreePoint`).
|
|
650
|
+
*
|
|
651
|
+
* Inside `containerId`, the candidate stays within the actor's current boundary (`actorBounds`)
|
|
652
|
+
* when there is room, or goes just past its last child when the boundary is full — the boundary
|
|
653
|
+
* grows from there, same as any other inner element (`actorBounds`).
|
|
654
|
+
*/
|
|
655
|
+
export declare function suggestPosition<EK extends string, LK extends string>(model: IstarModel<EK, LK>, options: SuggestPositionOptions<EK>): Point;
|
|
628
656
|
//#endregion
|
|
629
657
|
//#region src/file-metamodel.d.ts
|
|
630
658
|
/** The top-level key of the block in piStar JSON. */
|
package/dist/index.d.ts
CHANGED
|
@@ -625,6 +625,34 @@ export declare const ACTOR_PADDING = 10;
|
|
|
625
625
|
* what it actually draws.
|
|
626
626
|
*/
|
|
627
627
|
export declare function actorBounds<EK extends string, LK extends string>(model: IstarModel<EK, LK>, actorId: string, metamodel?: Metamodel<EK, LK>, defaultSizeOf?: (kind: EK) => Size): Box;
|
|
628
|
+
/** Space kept between a suggested box and any existing element (`suggestPosition`). */
|
|
629
|
+
export declare const SUGGEST_MARGIN = 20;
|
|
630
|
+
export interface SuggestPositionOptions<EK extends string> {
|
|
631
|
+
/** The kind of element being placed; its metamodel size is what must not overlap. */
|
|
632
|
+
readonly kind: EK;
|
|
633
|
+
/** Place inside this actor's boundary (`actorBounds`), growing it if full. */
|
|
634
|
+
readonly containerId?: string;
|
|
635
|
+
/** Prefer a spot adjacent to this element's id, or to this point. */
|
|
636
|
+
readonly near?: string | Point;
|
|
637
|
+
readonly metamodel?: Metamodel<EK, string>;
|
|
638
|
+
/** See `actorBounds`' `defaultSizeOf`. */
|
|
639
|
+
readonly defaultSizeOf?: (kind: EK) => Size;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* A free spot for a new element of `kind`, overlapping no existing element (sizes from the
|
|
643
|
+
* metamodel, or `defaultSizeOf` for a host with its own size overrides, as in `actorBounds`).
|
|
644
|
+
*
|
|
645
|
+
* Scans a deterministic, fixed-order set of candidates — rings around `near` when given, else
|
|
646
|
+
* the container's interior, else rings around the origin — and returns the first whose padded
|
|
647
|
+
* box (`SUGGEST_MARGIN`) overlaps nothing; same input always yields the same output. However
|
|
648
|
+
* dense the model, the result never overlaps: once every ring candidate is taken, it falls back
|
|
649
|
+
* to a point past the right edge of every obstacle (`guaranteedFreePoint`).
|
|
650
|
+
*
|
|
651
|
+
* Inside `containerId`, the candidate stays within the actor's current boundary (`actorBounds`)
|
|
652
|
+
* when there is room, or goes just past its last child when the boundary is full — the boundary
|
|
653
|
+
* grows from there, same as any other inner element (`actorBounds`).
|
|
654
|
+
*/
|
|
655
|
+
export declare function suggestPosition<EK extends string, LK extends string>(model: IstarModel<EK, LK>, options: SuggestPositionOptions<EK>): Point;
|
|
628
656
|
//#endregion
|
|
629
657
|
//#region src/file-metamodel.d.ts
|
|
630
658
|
/** The top-level key of the block in piStar JSON. */
|
package/dist/index.js
CHANGED
|
@@ -811,6 +811,146 @@ function actorBounds(model, actorId, metamodel = metamodelOf(model), defaultSize
|
|
|
811
811
|
height: maxY - minY
|
|
812
812
|
};
|
|
813
813
|
}
|
|
814
|
+
/** Space kept between a suggested box and any existing element (`suggestPosition`). */
|
|
815
|
+
const SUGGEST_MARGIN = 20;
|
|
816
|
+
/** Step between candidate positions on the grid `suggestPosition` scans. */
|
|
817
|
+
const SUGGEST_STEP = 40;
|
|
818
|
+
/** How many rings out `suggestPosition` scans before falling back to `guaranteedFreePoint`. */
|
|
819
|
+
const SUGGEST_MAX_RING = 25;
|
|
820
|
+
function boxesOverlap(a, b) {
|
|
821
|
+
return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
|
|
822
|
+
}
|
|
823
|
+
function padded(box, margin) {
|
|
824
|
+
return {
|
|
825
|
+
x: box.x - margin,
|
|
826
|
+
y: box.y - margin,
|
|
827
|
+
width: box.width + margin * 2,
|
|
828
|
+
height: box.height + margin * 2
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function firstFreeCandidate(candidates, size, obstacles, fallback) {
|
|
832
|
+
for (const point of candidates) {
|
|
833
|
+
const box = {
|
|
834
|
+
x: point.x,
|
|
835
|
+
y: point.y,
|
|
836
|
+
width: size.width,
|
|
837
|
+
height: size.height
|
|
838
|
+
};
|
|
839
|
+
if (!obstacles.some((obstacle) => boxesOverlap(padded(box, 20), obstacle))) return point;
|
|
840
|
+
}
|
|
841
|
+
return fallback;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* A point guaranteed to overlap no obstacle, however dense the model: past the right edge of
|
|
845
|
+
* every obstacle's box, so no obstacle can reach it regardless of its y (`suggestPosition`'s
|
|
846
|
+
* last-resort fallback, once every ring candidate is taken).
|
|
847
|
+
*/
|
|
848
|
+
function guaranteedFreePoint(obstacles, referenceY) {
|
|
849
|
+
if (obstacles.length === 0) return {
|
|
850
|
+
x: 0,
|
|
851
|
+
y: referenceY
|
|
852
|
+
};
|
|
853
|
+
return {
|
|
854
|
+
x: Math.max(...obstacles.map((box) => box.x + box.width)) + 20,
|
|
855
|
+
y: referenceY
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
/** Candidate positions in expanding square rings around `center`, in a fixed scan order. */
|
|
859
|
+
function ringCandidates(center, maxRing) {
|
|
860
|
+
const points = [center];
|
|
861
|
+
for (let ring = 1; ring <= maxRing; ring++) {
|
|
862
|
+
const offset = ring * SUGGEST_STEP;
|
|
863
|
+
for (let dx = -offset; dx <= offset; dx += SUGGEST_STEP) {
|
|
864
|
+
points.push({
|
|
865
|
+
x: center.x + dx,
|
|
866
|
+
y: center.y - offset
|
|
867
|
+
});
|
|
868
|
+
points.push({
|
|
869
|
+
x: center.x + dx,
|
|
870
|
+
y: center.y + offset
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
for (let dy = -offset + SUGGEST_STEP; dy <= offset - SUGGEST_STEP; dy += SUGGEST_STEP) {
|
|
874
|
+
points.push({
|
|
875
|
+
x: center.x - offset,
|
|
876
|
+
y: center.y + dy
|
|
877
|
+
});
|
|
878
|
+
points.push({
|
|
879
|
+
x: center.x + offset,
|
|
880
|
+
y: center.y + dy
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return points;
|
|
885
|
+
}
|
|
886
|
+
function modelBoxes(model, defaultSizeOf) {
|
|
887
|
+
const boxes = [];
|
|
888
|
+
for (const element of model.elements.values()) {
|
|
889
|
+
const base = defaultSizeOf(element.kind);
|
|
890
|
+
const width = element.display?.width ?? base.width;
|
|
891
|
+
const height = element.display?.height ?? base.height;
|
|
892
|
+
boxes.push({
|
|
893
|
+
x: element.x,
|
|
894
|
+
y: element.y,
|
|
895
|
+
width,
|
|
896
|
+
height
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
return boxes;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* A free spot for a new element of `kind`, overlapping no existing element (sizes from the
|
|
903
|
+
* metamodel, or `defaultSizeOf` for a host with its own size overrides, as in `actorBounds`).
|
|
904
|
+
*
|
|
905
|
+
* Scans a deterministic, fixed-order set of candidates — rings around `near` when given, else
|
|
906
|
+
* the container's interior, else rings around the origin — and returns the first whose padded
|
|
907
|
+
* box (`SUGGEST_MARGIN`) overlaps nothing; same input always yields the same output. However
|
|
908
|
+
* dense the model, the result never overlaps: once every ring candidate is taken, it falls back
|
|
909
|
+
* to a point past the right edge of every obstacle (`guaranteedFreePoint`).
|
|
910
|
+
*
|
|
911
|
+
* Inside `containerId`, the candidate stays within the actor's current boundary (`actorBounds`)
|
|
912
|
+
* when there is room, or goes just past its last child when the boundary is full — the boundary
|
|
913
|
+
* grows from there, same as any other inner element (`actorBounds`).
|
|
914
|
+
*/
|
|
915
|
+
function suggestPosition(model, options) {
|
|
916
|
+
const metamodel = options.metamodel ?? metamodelOf(model);
|
|
917
|
+
const defaultSizeOf = options.defaultSizeOf ?? ((kind) => metamodelSizeOf(metamodel, kind));
|
|
918
|
+
const size = defaultSizeOf(options.kind);
|
|
919
|
+
const obstacles = modelBoxes(model, defaultSizeOf);
|
|
920
|
+
const near = typeof options.near === "string" ? model.elements.get(options.near) : options.near;
|
|
921
|
+
const nearPoint = near ? "kind" in near ? {
|
|
922
|
+
x: near.x,
|
|
923
|
+
y: near.y
|
|
924
|
+
} : near : void 0;
|
|
925
|
+
if (options.containerId) {
|
|
926
|
+
const bounds = actorBounds(model, options.containerId, metamodel, defaultSizeOf);
|
|
927
|
+
const candidates = ringCandidates(nearPoint ?? {
|
|
928
|
+
x: bounds.x + 10,
|
|
929
|
+
y: bounds.y + 10
|
|
930
|
+
}, SUGGEST_MAX_RING).filter((point) => point.x >= bounds.x + 10 && point.y >= bounds.y + 10 && point.x + size.width <= bounds.x + bounds.width - 10 && point.y + size.height <= bounds.y + bounds.height - 10);
|
|
931
|
+
if (candidates.length > 0) {
|
|
932
|
+
const sentinel = {
|
|
933
|
+
x: NaN,
|
|
934
|
+
y: NaN
|
|
935
|
+
};
|
|
936
|
+
const found = firstFreeCandidate(candidates, size, obstacles, sentinel);
|
|
937
|
+
if (found !== sentinel) return found;
|
|
938
|
+
}
|
|
939
|
+
const pastLastChild = {
|
|
940
|
+
x: bounds.x + bounds.width + 10,
|
|
941
|
+
y: bounds.y + 10
|
|
942
|
+
};
|
|
943
|
+
return firstFreeCandidate(ringCandidates(pastLastChild, SUGGEST_MAX_RING), size, obstacles, guaranteedFreePoint(obstacles, pastLastChild.y));
|
|
944
|
+
}
|
|
945
|
+
const start = nearPoint ?? (obstacles.length > 0 ? {
|
|
946
|
+
x: Math.max(...obstacles.map((box) => box.x + box.width)) + 20,
|
|
947
|
+
y: 0
|
|
948
|
+
} : {
|
|
949
|
+
x: 0,
|
|
950
|
+
y: 0
|
|
951
|
+
});
|
|
952
|
+
return firstFreeCandidate(ringCandidates(start, SUGGEST_MAX_RING), size, obstacles, guaranteedFreePoint(obstacles, start.y));
|
|
953
|
+
}
|
|
814
954
|
//#endregion
|
|
815
955
|
//#region src/constraints.ts
|
|
816
956
|
const OK = { ok: true };
|
|
@@ -3152,6 +3292,6 @@ function buildDefaultPalette() {
|
|
|
3152
3292
|
/** The palette data of iStar 2.0, as piStar's toolbar presents it. */
|
|
3153
3293
|
const DEFAULT_PALETTE = buildDefaultPalette();
|
|
3154
3294
|
//#endregion
|
|
3155
|
-
export { ACTOR_KINDS, ACTOR_LINK_KINDS, ACTOR_PADDING, CONTRIBUTION_LABELS, DEFAULT_DIAGRAM, DEFAULT_ELEMENT_SIZE, DEFAULT_PALETTE, DEPENDENCY_LINK_KINDS, DIAGNOSTIC_SEVERITIES, DIAGRAM_LAYOUT_KEY, DSM3_FILE_KEYS, DSM3_TOOL, ELEMENT_KINDS, FILE_METAMODEL_KEY, HUB_ENTRY_KEY, ISTAR_2_0, ISTAR_2_0_DECLARATION, ISTAR_PREFIX, ISTAR_VERSION, LINK_KINDS, LINK_KIND_INFO, LINK_SELECTOR, METAMODEL_VERSION, MetamodelError, ModelOperationError, NODE_KINDS, NODE_KIND_INFO, NODE_LINK_KINDS, PISTAR_TOOL, PistarParseError, PistarWriteError, actorBounds, addDependency, addElement, addLeg, boolean, canLink, childrenOf, compareSeverity, connect, connectResultToDiagnostic, createDiagnosticsStore, createEmptyModel, createModelStore, defaultHubPosition, defaultIdGenerator, defineMetamodel, defineMetamodelExtension, defineProperties, dependencyEnds, dependencyLinksOf, detectFramework, detectPrefix, disconnect, effectiveElementKind, effectiveLinkKind, elementBehavesLike, elementCenter, elementKindDefinition, enumOf, extendMetamodel, fileMetamodelExtension, fileMetamodelOf, fromLspDiagnostic, fromLspDiagnostics, fromNodeIdDiagnostic, fromNodeIdDiagnostics, getSourceLayout, groupDiagnostics, hostMetamodelOf, hubEntryLayoutKey, hubLinkOf, hubOfLink, inheritMetamodel, inheritSourceLayout, isActor, isActorIn, isActorKind, isActorKindIn, isActorLinkKind, isContributionLabel, isDependencyLinkKind, isElementKind, isHubElement, isLinkKind, isLinkRef, isNode, isNodeIn, isNodeKind, isNodeKindIn, isNodeLinkKind, linkBehavesLike, linkKindDefinition, linkTargetOf, linksOf, mergeDiagnostics, metamodelOf, moveElement, nestElement, number, parsePistar, prop, reference, removeElement, removeElements, removeLeg, setHostMetamodel, shortKindName, string, targetsLinks, toPistar, toPistarObject, updateDiagram, updateElement, updateLink, validateFileMetamodel, validateModel, validateModelProperties, withFileMetamodel, withMetamodel, worstDiagnosticSeverity };
|
|
3295
|
+
export { ACTOR_KINDS, ACTOR_LINK_KINDS, ACTOR_PADDING, CONTRIBUTION_LABELS, DEFAULT_DIAGRAM, DEFAULT_ELEMENT_SIZE, DEFAULT_PALETTE, DEPENDENCY_LINK_KINDS, DIAGNOSTIC_SEVERITIES, DIAGRAM_LAYOUT_KEY, DSM3_FILE_KEYS, DSM3_TOOL, ELEMENT_KINDS, FILE_METAMODEL_KEY, HUB_ENTRY_KEY, ISTAR_2_0, ISTAR_2_0_DECLARATION, ISTAR_PREFIX, ISTAR_VERSION, LINK_KINDS, LINK_KIND_INFO, LINK_SELECTOR, METAMODEL_VERSION, MetamodelError, ModelOperationError, NODE_KINDS, NODE_KIND_INFO, NODE_LINK_KINDS, PISTAR_TOOL, PistarParseError, PistarWriteError, SUGGEST_MARGIN, actorBounds, addDependency, addElement, addLeg, boolean, canLink, childrenOf, compareSeverity, connect, connectResultToDiagnostic, createDiagnosticsStore, createEmptyModel, createModelStore, defaultHubPosition, defaultIdGenerator, defineMetamodel, defineMetamodelExtension, defineProperties, dependencyEnds, dependencyLinksOf, detectFramework, detectPrefix, disconnect, effectiveElementKind, effectiveLinkKind, elementBehavesLike, elementCenter, elementKindDefinition, enumOf, extendMetamodel, fileMetamodelExtension, fileMetamodelOf, fromLspDiagnostic, fromLspDiagnostics, fromNodeIdDiagnostic, fromNodeIdDiagnostics, getSourceLayout, groupDiagnostics, hostMetamodelOf, hubEntryLayoutKey, hubLinkOf, hubOfLink, inheritMetamodel, inheritSourceLayout, isActor, isActorIn, isActorKind, isActorKindIn, isActorLinkKind, isContributionLabel, isDependencyLinkKind, isElementKind, isHubElement, isLinkKind, isLinkRef, isNode, isNodeIn, isNodeKind, isNodeKindIn, isNodeLinkKind, linkBehavesLike, linkKindDefinition, linkTargetOf, linksOf, mergeDiagnostics, metamodelOf, moveElement, nestElement, number, parsePistar, prop, reference, removeElement, removeElements, removeLeg, setHostMetamodel, shortKindName, string, suggestPosition, targetsLinks, toPistar, toPistarObject, updateDiagram, updateElement, updateLink, validateFileMetamodel, validateModel, validateModelProperties, withFileMetamodel, withMetamodel, worstDiagnosticSeverity };
|
|
3156
3296
|
|
|
3157
3297
|
//# sourceMappingURL=index.js.map
|