@glossarist/concept-browser 0.7.83 → 0.7.84
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/package.json +1 -1
- package/src/components/RelationSphere.vue +129 -53
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.84",
|
|
4
4
|
"description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -630,8 +630,10 @@ function drawEdges(cx: number, cy: number) {
|
|
|
630
630
|
svg.setAttribute('height', String(h));
|
|
631
631
|
svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
|
|
632
632
|
svg.setAttribute('preserveAspectRatio', 'none');
|
|
633
|
-
/* Remove only path + text + rect (edges), keep defs (markers)
|
|
634
|
-
|
|
633
|
+
/* Remove only path + text + rect (edges), keep defs (markers).
|
|
634
|
+
Also remove <line> elements (rake-bundle segments) and <circle>
|
|
635
|
+
(rake junction markers). */
|
|
636
|
+
svg.querySelectorAll('path:not(defs path), line.rake-seg, circle.rake-junction, text.edge-label, rect.edge-label-bg').forEach(el => el.remove());
|
|
635
637
|
if (!svg.querySelector('defs')) ensureMarkers(svg);
|
|
636
638
|
|
|
637
639
|
/* Compute visible node positions */
|
|
@@ -750,19 +752,36 @@ function drawEdges(cx: number, cy: number) {
|
|
|
750
752
|
}
|
|
751
753
|
|
|
752
754
|
/**
|
|
753
|
-
* Render PartitiveRelations as ISO 704 rake bundles in the sphere.
|
|
755
|
+
* Render PartitiveRelations as ISO 704 rake/bracket bundles in the sphere.
|
|
754
756
|
*
|
|
755
|
-
*
|
|
756
|
-
* so we use a bundle of N curves fanning out from the comprehensive,
|
|
757
|
-
* all sharing visual styling. The bundle communicates "these N edges
|
|
758
|
-
* are ONE relation", not N independent pairwise links.
|
|
757
|
+
* Each relation renders as a real bracket:
|
|
759
758
|
*
|
|
760
|
-
*
|
|
761
|
-
*
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
759
|
+
* [comprehensive]
|
|
760
|
+
* |
|
|
761
|
+
* | ← single stem (vertical/radial)
|
|
762
|
+
* |
|
|
763
|
+
* +---•---+ ← spine midpoint (junction)
|
|
764
|
+
* | | |
|
|
765
|
+
* | | | ← N branches from spine midpoint
|
|
766
|
+
* [p1] [p2] [p3] (one per partitive)
|
|
767
|
+
*
|
|
768
|
+
* In 3D the bracket's "vertical" direction is along the line from
|
|
769
|
+
* comprehensive to the centroid of partitives. The branches fan out
|
|
770
|
+
* from the spine midpoint to each partitive. Right angles are not
|
|
771
|
+
* enforceable in a force-directed layout; instead we use a single
|
|
772
|
+
* shared junction point so the eye reads "ONE relation, N branches".
|
|
773
|
+
*
|
|
774
|
+
* ISO 704 line conventions applied (each segment: stem + spine + branches):
|
|
775
|
+
* - default : single solid line
|
|
776
|
+
* - plurality.isShared && !isUncertain : two parallel solid lines
|
|
777
|
+
* (close-set; offset ±DOUBLE_GAP/2)
|
|
778
|
+
* - plurality.isShared && isUncertain : one solid + one dashed line
|
|
779
|
+
*
|
|
780
|
+
* Per-member certainty:
|
|
781
|
+
* - confirmed : branch is solid (with the plurality style above)
|
|
782
|
+
* - possible : branch is dashed + reduced opacity (overrides plurality
|
|
783
|
+
* on that branch only; the stem + spine keep the relation
|
|
784
|
+
* style)
|
|
766
785
|
*
|
|
767
786
|
* A diamond marker at the comprehensive end signals the hyperedge origin.
|
|
768
787
|
*/
|
|
@@ -772,6 +791,10 @@ function drawRakeBundles(
|
|
|
772
791
|
_cx: number,
|
|
773
792
|
_cy: number,
|
|
774
793
|
) {
|
|
794
|
+
const color = uiStore.isDark ? '#2dd4bf' : '#0d9488';
|
|
795
|
+
const DOUBLE_GAP = 4;
|
|
796
|
+
const DASH = '4 3';
|
|
797
|
+
|
|
775
798
|
for (const rel of props.partitiveRelations ?? []) {
|
|
776
799
|
const compParsed = UriRouter.parseUri(rel.comprehensive);
|
|
777
800
|
if (!compParsed) continue;
|
|
@@ -791,58 +814,111 @@ function drawRakeBundles(
|
|
|
791
814
|
|
|
792
815
|
if (memberPositions.length === 0) continue;
|
|
793
816
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
const
|
|
817
|
+
// Spine midpoint = centroid of comprehensive + all partitives.
|
|
818
|
+
// This is the junction where the stem meets the branches.
|
|
819
|
+
const cx_part = (compPos.x + memberPositions.reduce((s, m) => s + m.pos.x, 0)) / (memberPositions.length + 1);
|
|
820
|
+
const cy_part = (compPos.y + memberPositions.reduce((s, m) => s + m.pos.y, 0)) / (memberPositions.length + 1);
|
|
821
|
+
|
|
797
822
|
const isShared = !!rel.plurality?.isShared;
|
|
798
823
|
const isUncertain = !!rel.plurality?.isUncertain;
|
|
799
|
-
const drawDouble = isShared;
|
|
800
|
-
const secondDashed = isUncertain;
|
|
801
824
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
825
|
+
/**
|
|
826
|
+
* Draw a single straight line segment with optional parallel offset
|
|
827
|
+
* and dash style. Used for both stem and branches.
|
|
828
|
+
*/
|
|
829
|
+
const drawSegment = (
|
|
830
|
+
ax: number, ay: number, bx: number, by: number,
|
|
831
|
+
opts: { offset?: number; dashed?: boolean; opacity?: number } = {},
|
|
832
|
+
) => {
|
|
833
|
+
const offset = opts.offset ?? 0;
|
|
834
|
+
const dashed = opts.dashed ?? false;
|
|
835
|
+
const opacity = opts.opacity ?? 0.85;
|
|
836
|
+
if (offset === 0) {
|
|
837
|
+
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
838
|
+
line.setAttribute('class', 'rake-seg');
|
|
839
|
+
line.setAttribute('x1', String(ax));
|
|
840
|
+
line.setAttribute('y1', String(ay));
|
|
841
|
+
line.setAttribute('x2', String(bx));
|
|
842
|
+
line.setAttribute('y2', String(by));
|
|
843
|
+
line.setAttribute('stroke', color);
|
|
844
|
+
line.setAttribute('stroke-width', '1.6');
|
|
845
|
+
line.setAttribute('opacity', String(opacity));
|
|
846
|
+
line.setAttribute('stroke-linecap', 'round');
|
|
847
|
+
if (dashed) line.setAttribute('stroke-dasharray', DASH);
|
|
848
|
+
svg.appendChild(line);
|
|
849
|
+
} else {
|
|
850
|
+
// Compute perpendicular unit vector for parallel offset
|
|
851
|
+
const dx = bx - ax, dy = by - ay;
|
|
852
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
853
|
+
const px = -dy / len, py = dx / len;
|
|
854
|
+
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
855
|
+
line.setAttribute('class', 'rake-seg');
|
|
856
|
+
line.setAttribute('x1', String(ax + px * offset));
|
|
857
|
+
line.setAttribute('y1', String(ay + py * offset));
|
|
858
|
+
line.setAttribute('x2', String(bx + px * offset));
|
|
859
|
+
line.setAttribute('y2', String(by + py * offset));
|
|
860
|
+
line.setAttribute('stroke', color);
|
|
861
|
+
line.setAttribute('stroke-width', '1.6');
|
|
862
|
+
line.setAttribute('opacity', String(opacity));
|
|
863
|
+
line.setAttribute('stroke-linecap', 'round');
|
|
864
|
+
if (dashed) line.setAttribute('stroke-dasharray', DASH);
|
|
865
|
+
svg.appendChild(line);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
831
868
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
869
|
+
/**
|
|
870
|
+
* Draw a segment with the relation's plurality style:
|
|
871
|
+
* single solid (default), double solid (isShared),
|
|
872
|
+
* or solid + dashed (isShared + isUncertain).
|
|
873
|
+
*/
|
|
874
|
+
const drawSegmentWithPlurality = (
|
|
875
|
+
ax: number, ay: number, bx: number, by: number,
|
|
876
|
+
opts: { opacity?: number; perMemberPossible?: boolean } = {},
|
|
877
|
+
) => {
|
|
878
|
+
const opacity = opts.opacity ?? 0.85;
|
|
879
|
+
if (!isShared) {
|
|
880
|
+
drawSegment(ax, ay, bx, by, { opacity, dashed: opts.perMemberPossible });
|
|
881
|
+
} else {
|
|
882
|
+
// Close-set double: two parallel lines offset ±DOUBLE_GAP/2
|
|
883
|
+
drawSegment(ax, ay, bx, by, { offset: -DOUBLE_GAP / 2, opacity });
|
|
884
|
+
drawSegment(ax, ay, bx, by, {
|
|
885
|
+
offset: DOUBLE_GAP / 2,
|
|
886
|
+
opacity,
|
|
887
|
+
dashed: isUncertain || opts.perMemberPossible,
|
|
888
|
+
});
|
|
836
889
|
}
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
// 1. Stem: comprehensive → spine midpoint
|
|
893
|
+
drawSegmentWithPlurality(compPos.x, compPos.y, cx_part, cy_part);
|
|
894
|
+
|
|
895
|
+
// 2. Branches: spine midpoint → each partitive
|
|
896
|
+
for (const { member, pos: pPos } of memberPositions) {
|
|
897
|
+
drawSegmentWithPlurality(cx_part, cy_part, pPos.x, pPos.y, {
|
|
898
|
+
opacity: member.certainty === 'possible' ? 0.45 : 0.85,
|
|
899
|
+
perMemberPossible: member.certainty === 'possible',
|
|
900
|
+
});
|
|
837
901
|
}
|
|
838
902
|
|
|
839
|
-
|
|
903
|
+
// 3. Junction marker at spine midpoint (small filled circle)
|
|
904
|
+
const junction = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
905
|
+
junction.setAttribute('class', 'rake-junction');
|
|
906
|
+
junction.setAttribute('cx', String(cx_part));
|
|
907
|
+
junction.setAttribute('cy', String(cy_part));
|
|
908
|
+
junction.setAttribute('r', '3');
|
|
909
|
+
junction.setAttribute('fill', color);
|
|
910
|
+
junction.setAttribute('opacity', '0.85');
|
|
911
|
+
junction.setAttribute('stroke', uiStore.isDark ? '#0a1f1c' : '#ffffff');
|
|
912
|
+
junction.setAttribute('stroke-width', '1');
|
|
913
|
+
svg.appendChild(junction);
|
|
914
|
+
|
|
915
|
+
// 4. Diamond marker at comprehensive end (hyperedge origin signal)
|
|
840
916
|
const diamond = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
841
917
|
const dsize = 5;
|
|
842
918
|
diamond.setAttribute('d',
|
|
843
919
|
`M ${compPos.x} ${compPos.y - dsize} L ${compPos.x + dsize} ${compPos.y} L ${compPos.x} ${compPos.y + dsize} L ${compPos.x - dsize} ${compPos.y} Z`);
|
|
844
920
|
diamond.setAttribute('fill', color);
|
|
845
|
-
diamond.setAttribute('opacity', '0.
|
|
921
|
+
diamond.setAttribute('opacity', '0.9');
|
|
846
922
|
diamond.setAttribute('stroke', uiStore.isDark ? '#0a1f1c' : '#ffffff');
|
|
847
923
|
diamond.setAttribute('stroke-width', '1');
|
|
848
924
|
svg.appendChild(diamond);
|