@zerodep-dev/vue-tsx-pro-ui 0.3.3 → 0.4.1
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/charts/AreaChart.d.ts +1 -1
- package/dist/charts/AreaChart.js +6 -48
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +6 -49
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.d.ts +7 -2
- package/dist/charts/ChordChart.js +151 -60
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.d.ts +2 -1
- package/dist/charts/ComboChart.js +17 -93
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.d.ts +6 -1
- package/dist/charts/DonutChart.js +134 -51
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.d.ts +22 -10
- package/dist/charts/GraphChart.js +415 -175
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.d.ts +1 -29
- package/dist/charts/LineChart.js +6 -54
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.d.ts +21 -48
- package/dist/charts/MapChart.js +426 -353
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.d.ts +7 -2
- package/dist/charts/SankeyChart.js +164 -76
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.d.ts +7 -1
- package/dist/charts/ScatterChart.js +45 -30
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.d.ts +8 -1
- package/dist/charts/TimelineChart.js +141 -89
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.d.ts +6 -0
- package/dist/charts/TreemapChart.js +174 -58
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.d.ts +6 -7
- package/dist/charts/WaterfallChart.js +46 -51
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.d.ts +44 -35
- package/dist/charts/XYChart.js +493 -281
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/axis-ticks.d.ts +1 -1
- package/dist/charts/axis-ticks.js +4 -5
- package/dist/charts/axis-ticks.js.map +1 -1
- package/dist/charts/flow.d.ts +4 -11
- package/dist/charts/flow.js +4 -4
- package/dist/charts/flow.js.map +1 -1
- package/dist/charts/frame.d.ts +6 -30
- package/dist/charts/frame.js +6 -99
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/geo/projection.d.ts +45 -0
- package/dist/charts/geo/projection.js +201 -0
- package/dist/charts/geo/projection.js.map +1 -0
- package/dist/charts/geo/topojson.d.ts +50 -0
- package/dist/charts/geo/topojson.js +119 -0
- package/dist/charts/geo/topojson.js.map +1 -0
- package/dist/charts/index.d.ts +21 -9
- package/dist/charts/index.js +19 -8
- package/dist/charts/label-layout.js.map +1 -1
- package/dist/charts/layout/chord.d.ts +44 -0
- package/dist/charts/layout/chord.js +45 -0
- package/dist/charts/layout/chord.js.map +1 -0
- package/dist/charts/layout/graph.d.ts +49 -0
- package/dist/charts/layout/graph.js +268 -0
- package/dist/charts/layout/graph.js.map +1 -0
- package/dist/charts/layout/sankey.d.ts +79 -0
- package/dist/charts/layout/sankey.js +200 -0
- package/dist/charts/layout/sankey.js.map +1 -0
- package/dist/charts/layout/treemap.d.ts +46 -0
- package/dist/charts/layout/treemap.js +152 -0
- package/dist/charts/layout/treemap.js.map +1 -0
- package/dist/charts/marks.d.ts +79 -0
- package/dist/charts/marks.js +231 -0
- package/dist/charts/marks.js.map +1 -0
- package/dist/charts/plot.d.ts +107 -0
- package/dist/charts/plot.js +231 -0
- package/dist/charts/plot.js.map +1 -0
- package/dist/charts/scale.d.ts +29 -0
- package/dist/charts/scale.js +244 -0
- package/dist/charts/scale.js.map +1 -0
- package/dist/charts/shape.d.ts +46 -0
- package/dist/charts/shape.js +131 -0
- package/dist/charts/shape.js.map +1 -0
- package/dist/charts/stack.d.ts +17 -0
- package/dist/charts/stack.js +35 -0
- package/dist/charts/stack.js.map +1 -0
- package/dist/charts/text-size.d.ts +12 -0
- package/dist/charts/text-size.js +32 -0
- package/dist/charts/text-size.js.map +1 -0
- package/dist/config/createUi.d.ts +21 -2
- package/dist/config/createUi.js +18 -1
- package/dist/config/createUi.js.map +1 -1
- package/dist/config/theme/preference.js +2 -1
- package/dist/config/theme/preference.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/maps/china-provinces.js +35680 -0
- package/dist/maps/china-provinces.js.map +1 -0
- package/dist/maps/china.d.ts +13 -0
- package/dist/maps/china.js +8 -0
- package/dist/maps/china.js.map +1 -0
- package/dist/maps/index.d.ts +9 -0
- package/dist/maps/index.js +3 -0
- package/dist/maps/world-countries.js +22273 -0
- package/dist/maps/world-countries.js.map +1 -0
- package/dist/maps/world.d.ts +13 -0
- package/dist/maps/world.js +8 -0
- package/dist/maps/world.js.map +1 -0
- package/dist/overlay/detached.js.map +1 -1
- package/dist/utils/request/use-request.d.ts +25 -1
- package/dist/utils/request/use-request.js +35 -3
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +7 -8
- package/dist/charts/single.d.ts +0 -41
- package/dist/charts/single.js +0 -66
- package/dist/charts/single.js.map +0 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { presimplify, simplify } from "topojson-simplify";
|
|
2
|
+
//#region src/charts/geo/topojson.ts
|
|
3
|
+
/**
|
|
4
|
+
* TopoJSON:解出要素的多边形(经纬度)、按像素简化、算经纬度范围。地图(MapChart)用它,组件库的
|
|
5
|
+
* `@zerodep-dev/vue-tsx-pro-ui/maps` 带着中国省份与世界各国两份数据,也可以用自己的 TopoJSON。
|
|
6
|
+
*
|
|
7
|
+
* const features = topologyFeatures(chinaProvinces, 'provinces');
|
|
8
|
+
* features[0].id // 'CN-GS'
|
|
9
|
+
* features[0].polygons[0][0] // 第一个多边形的外环:[[经度, 纬度], …]
|
|
10
|
+
*
|
|
11
|
+
* TopoJSON 把相邻区域共用的边界存成一段段弧(arcs),多边形的环是弧的序号(负数 ~i 表示倒着走第 i 段);
|
|
12
|
+
* 有 transform 时弧的坐标是量化后逐点的差值。解码的写法同 topojson-client(Mike Bostock,ISC)。
|
|
13
|
+
*/
|
|
14
|
+
/** 解码过的弧:按拓扑缓存 */
|
|
15
|
+
var decodedArcs = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
/** 各段弧的绝对经纬度 */
|
|
17
|
+
function decodeArcs(topology) {
|
|
18
|
+
let arcs = decodedArcs.get(topology);
|
|
19
|
+
if (arcs) return arcs;
|
|
20
|
+
const { scale = [1, 1], translate = [0, 0] } = topology.transform ?? {};
|
|
21
|
+
const [sx = 1, sy = 1] = scale;
|
|
22
|
+
const [tx = 0, ty = 0] = translate;
|
|
23
|
+
arcs = topology.arcs.map((arc) => {
|
|
24
|
+
let x = 0;
|
|
25
|
+
let y = 0;
|
|
26
|
+
return arc.map(([px = 0, py = 0]) => {
|
|
27
|
+
if (!topology.transform) return [px, py];
|
|
28
|
+
x += px;
|
|
29
|
+
y += py;
|
|
30
|
+
return [x * sx + tx, y * sy + ty];
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
decodedArcs.set(topology, arcs);
|
|
34
|
+
return arcs;
|
|
35
|
+
}
|
|
36
|
+
/** 一个环:依次接上各段弧(后一段的第一个点就是前一段的最后一个点,去掉重复的) */
|
|
37
|
+
function ring(arcs, indices) {
|
|
38
|
+
const points = [];
|
|
39
|
+
for (const index of indices) {
|
|
40
|
+
const arc = index < 0 ? [...arcs[~index]].reverse() : arcs[index];
|
|
41
|
+
if (points.length) points.pop();
|
|
42
|
+
points.push(...arc);
|
|
43
|
+
}
|
|
44
|
+
return points;
|
|
45
|
+
}
|
|
46
|
+
/** 一个几何对象里的多边形 */
|
|
47
|
+
function polygonsOf(arcs, object) {
|
|
48
|
+
if (object.type === "Polygon") return [object.arcs.map((indices) => ring(arcs, indices))];
|
|
49
|
+
if (object.type === "MultiPolygon") return object.arcs.map((rings) => rings.map((indices) => ring(arcs, indices)));
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
/** 要素组 name 里的要素(GeometryCollection 展开;单个几何对象当作一个要素) */
|
|
53
|
+
function topologyFeatures(topology, name) {
|
|
54
|
+
const object = topology.objects[name];
|
|
55
|
+
if (!object) return [];
|
|
56
|
+
const arcs = decodeArcs(topology);
|
|
57
|
+
return (object.type === "GeometryCollection" ? object.geometries ?? [] : [object]).map((member) => ({
|
|
58
|
+
id: String(member.id ?? ""),
|
|
59
|
+
properties: member.properties ?? {},
|
|
60
|
+
polygons: polygonsOf(arcs, member)
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
/** 算好了各点权重的地理数据:同一份 TopoJSON 只算一次 */
|
|
64
|
+
var presimplified = /* @__PURE__ */ new WeakMap();
|
|
65
|
+
/** 简化过的地理数据:TopoJSON → 档位 → 结果 */
|
|
66
|
+
var simplified = /* @__PURE__ */ new WeakMap();
|
|
67
|
+
/**
|
|
68
|
+
* 简化地理数据:去掉与前后两点围成的三角形面积不到 minArea(坐标单位的平方,经纬度是平方度)的点
|
|
69
|
+
* (Visvalingam,topojson-simplify)。相邻区域共用的边界一起简化,不会出现缝隙;各段弧的两端总保留。
|
|
70
|
+
* minArea 按 2 的整数次幂分档缓存:绘图区大小变一点不重算。
|
|
71
|
+
*/
|
|
72
|
+
function simplifyTopology(topology, minArea) {
|
|
73
|
+
if (!(minArea > 0) || !Number.isFinite(minArea)) return topology;
|
|
74
|
+
const level = 2 ** Math.floor(Math.log2(minArea));
|
|
75
|
+
let byLevel = simplified.get(topology);
|
|
76
|
+
if (!byLevel) simplified.set(topology, byLevel = /* @__PURE__ */ new Map());
|
|
77
|
+
let result = byLevel.get(level);
|
|
78
|
+
if (!result) {
|
|
79
|
+
let weighted = presimplified.get(topology);
|
|
80
|
+
if (!weighted) presimplified.set(topology, weighted = presimplify(topology));
|
|
81
|
+
result = simplify(weighted, level);
|
|
82
|
+
byLevel.set(level, result);
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
/** TopoJSON 的经纬度范围 [西, 南, 东, 北]:有 bbox 用它,否则解出各段弧的坐标 */
|
|
87
|
+
function topologyBounds(topology) {
|
|
88
|
+
if (topology.bbox && topology.bbox.length >= 4) {
|
|
89
|
+
const [west = 0, south = 0, east = 0, north = 0] = topology.bbox;
|
|
90
|
+
return [
|
|
91
|
+
west,
|
|
92
|
+
south,
|
|
93
|
+
east,
|
|
94
|
+
north
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
let [west, south, east, north] = [
|
|
98
|
+
Infinity,
|
|
99
|
+
Infinity,
|
|
100
|
+
-Infinity,
|
|
101
|
+
-Infinity
|
|
102
|
+
];
|
|
103
|
+
for (const arc of decodeArcs(topology)) for (const [longitude, latitude] of arc) {
|
|
104
|
+
west = Math.min(west, longitude);
|
|
105
|
+
east = Math.max(east, longitude);
|
|
106
|
+
south = Math.min(south, latitude);
|
|
107
|
+
north = Math.max(north, latitude);
|
|
108
|
+
}
|
|
109
|
+
return [
|
|
110
|
+
west,
|
|
111
|
+
south,
|
|
112
|
+
east,
|
|
113
|
+
north
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { simplifyTopology, topologyBounds, topologyFeatures };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=topojson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topojson.js","names":[],"sources":["../../../src/charts/geo/topojson.ts"],"sourcesContent":["/**\n * TopoJSON:解出要素的多边形(经纬度)、按像素简化、算经纬度范围。地图(MapChart)用它,组件库的\n * `@zerodep-dev/vue-tsx-pro-ui/maps` 带着中国省份与世界各国两份数据,也可以用自己的 TopoJSON。\n *\n * const features = topologyFeatures(chinaProvinces, 'provinces');\n * features[0].id // 'CN-GS'\n * features[0].polygons[0][0] // 第一个多边形的外环:[[经度, 纬度], …]\n *\n * TopoJSON 把相邻区域共用的边界存成一段段弧(arcs),多边形的环是弧的序号(负数 ~i 表示倒着走第 i 段);\n * 有 transform 时弧的坐标是量化后逐点的差值。解码的写法同 topojson-client(Mike Bostock,ISC)。\n */\nimport { presimplify, simplify } from 'topojson-simplify';\n\n/** TopoJSON 里的一个几何对象:多边形、多个多边形,或一组几何对象 */\nexport interface MapTopologyObject {\n /** 类型:Polygon、MultiPolygon、GeometryCollection(其余类型不画) */\n type: string;\n /** 要素的 id:地图按它对应区域的数据 */\n id?: string | number;\n /** 要素的属性(名称等) */\n properties?: Readonly<Record<string, unknown>> | null;\n /** 多边形的环:弧的序号(Polygon 是两层数组,MultiPolygon 是三层) */\n arcs?: unknown;\n /** GeometryCollection 里的几何对象 */\n geometries?: readonly MapTopologyObject[];\n}\n\n/** TopoJSON 拓扑:地理数据 */\nexport interface MapTopology {\n /** 'Topology' */\n type: string;\n /** 各段弧:点的序列;有 transform 时是量化后逐点的差值 */\n arcs: readonly (readonly (readonly number[])[])[];\n /** 量化:坐标 = 整数 × scale + translate */\n transform?: { scale: readonly number[]; translate: readonly number[] };\n /** 经纬度范围 [西, 南, 东, 北] */\n bbox?: readonly number[];\n /** 要素组:名字 → 几何对象(通常是 GeometryCollection) */\n objects: Readonly<Record<string, MapTopologyObject>>;\n}\n\n/** 经纬度(度):[经度, 纬度] */\nexport type GeoPosition = readonly [number, number];\n\n/** 解出来的一个要素 */\nexport interface MapFeature {\n /** 要素的 id(转成字符串) */\n id: string;\n /** 属性 */\n properties: Readonly<Record<string, unknown>>;\n /** 多边形:每个多边形是几个环,第一个是外环,其余是洞 */\n polygons: GeoPosition[][][];\n}\n\n/** 解码过的弧:按拓扑缓存 */\nconst decodedArcs = new WeakMap<MapTopology, GeoPosition[][]>();\n\n/** 各段弧的绝对经纬度 */\nfunction decodeArcs(topology: MapTopology): GeoPosition[][] {\n let arcs = decodedArcs.get(topology);\n if (arcs) return arcs;\n const { scale = [1, 1], translate = [0, 0] } = topology.transform ?? {};\n const [sx = 1, sy = 1] = scale;\n const [tx = 0, ty = 0] = translate;\n arcs = topology.arcs.map((arc) => {\n let x = 0;\n let y = 0;\n return arc.map(([px = 0, py = 0]): GeoPosition => {\n if (!topology.transform) return [px, py];\n x += px;\n y += py;\n return [x * sx + tx, y * sy + ty];\n });\n });\n decodedArcs.set(topology, arcs);\n return arcs;\n}\n\n/** 一个环:依次接上各段弧(后一段的第一个点就是前一段的最后一个点,去掉重复的) */\nfunction ring(arcs: readonly GeoPosition[][], indices: readonly number[]): GeoPosition[] {\n const points: GeoPosition[] = [];\n for (const index of indices) {\n const arc = index < 0 ? [...arcs[~index]!].reverse() : arcs[index]!;\n if (points.length) points.pop();\n points.push(...arc);\n }\n return points;\n}\n\n/** 一个几何对象里的多边形 */\nfunction polygonsOf(arcs: readonly GeoPosition[][], object: MapTopologyObject): GeoPosition[][][] {\n if (object.type === 'Polygon')\n return [(object.arcs as number[][]).map((indices) => ring(arcs, indices))];\n if (object.type === 'MultiPolygon')\n return (object.arcs as number[][][]).map((rings) =>\n rings.map((indices) => ring(arcs, indices)),\n );\n return [];\n}\n\n/** 要素组 name 里的要素(GeometryCollection 展开;单个几何对象当作一个要素) */\nexport function topologyFeatures(topology: MapTopology, name: string): MapFeature[] {\n const object = topology.objects[name];\n if (!object) return [];\n const arcs = decodeArcs(topology);\n const members = object.type === 'GeometryCollection' ? (object.geometries ?? []) : [object];\n return members.map((member) => ({\n id: String(member.id ?? ''),\n properties: member.properties ?? {},\n polygons: polygonsOf(arcs, member),\n }));\n}\n\n/** 算好了各点权重的地理数据:同一份 TopoJSON 只算一次 */\nconst presimplified = new WeakMap<MapTopology, MapTopology>();\n\n/** 简化过的地理数据:TopoJSON → 档位 → 结果 */\nconst simplified = new WeakMap<MapTopology, Map<number, MapTopology>>();\n\n/**\n * 简化地理数据:去掉与前后两点围成的三角形面积不到 minArea(坐标单位的平方,经纬度是平方度)的点\n * (Visvalingam,topojson-simplify)。相邻区域共用的边界一起简化,不会出现缝隙;各段弧的两端总保留。\n * minArea 按 2 的整数次幂分档缓存:绘图区大小变一点不重算。\n */\nexport function simplifyTopology(topology: MapTopology, minArea: number): MapTopology {\n if (!(minArea > 0) || !Number.isFinite(minArea)) return topology;\n const level = 2 ** Math.floor(Math.log2(minArea));\n let byLevel = simplified.get(topology);\n if (!byLevel) simplified.set(topology, (byLevel = new Map()));\n let result = byLevel.get(level);\n if (!result) {\n // topojson-simplify 的类型(topojson-specification)比这里的宽松类型严:边界上转一下\n type Input = Parameters<typeof presimplify>[0];\n let weighted = presimplified.get(topology);\n if (!weighted)\n presimplified.set(\n topology,\n (weighted = presimplify(topology as unknown as Input) as unknown as MapTopology),\n );\n result = simplify(weighted as unknown as Input, level) as unknown as MapTopology;\n byLevel.set(level, result);\n }\n return result;\n}\n\n/** TopoJSON 的经纬度范围 [西, 南, 东, 北]:有 bbox 用它,否则解出各段弧的坐标 */\nexport function topologyBounds(topology: MapTopology): [number, number, number, number] {\n if (topology.bbox && topology.bbox.length >= 4) {\n const [west = 0, south = 0, east = 0, north = 0] = topology.bbox;\n return [west, south, east, north];\n }\n let [west, south, east, north] = [Infinity, Infinity, -Infinity, -Infinity];\n for (const arc of decodeArcs(topology))\n for (const [longitude, latitude] of arc) {\n west = Math.min(west, longitude);\n east = Math.max(east, longitude);\n south = Math.min(south, latitude);\n north = Math.max(north, latitude);\n }\n return [west, south, east, north];\n}\n"],"mappings":"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"}
|
package/dist/charts/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 图表(子路径 `@zerodep-dev/vue-tsx-pro-ui/charts
|
|
2
|
+
* 图表(子路径 `@zerodep-dev/vue-tsx-pro-ui/charts`):自己用 SVG 画,不依赖第三方图表库;比例尺、曲线、
|
|
3
|
+
* 堆叠与各种布局(桑基、弦、矩形树、网络、地图投影)都是本目录里的精简实现。
|
|
3
4
|
*
|
|
4
5
|
* import { BarChart, LineChart } from '@zerodep-dev/vue-tsx-pro-ui/charts';
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
+
* 主入口不导入图表:没用到图表的应用不打包这些代码。地图的地理数据在另一个子路径(`@zerodep-dev/vue-tsx-pro-ui/maps`)。
|
|
7
8
|
*/
|
|
8
9
|
export { AreaChart, type AreaChartProps } from './AreaChart';
|
|
9
10
|
export { BarChart, type BarChartOrientations, type BarChartProps } from './BarChart';
|
|
@@ -12,19 +13,30 @@ export { ComboChart, type ComboChartProps, type ComboSeries } from './ComboChart
|
|
|
12
13
|
export { DonutChart, donutChartStyles, type DonutChartProps } from './DonutChart';
|
|
13
14
|
export { FunnelChart, funnelChartDynamicStyles, funnelChartStyles, type FunnelChartProps, type FunnelStage, } from './FunnelChart';
|
|
14
15
|
export { GraphChart, graphChartStyles, type GraphChartGroup, type GraphChartLayouts, type GraphChartLink, type GraphChartNode, type GraphChartProps, type GraphChartShapes, } from './GraphChart';
|
|
15
|
-
export {
|
|
16
|
-
export { MapChart, mapChartStyles,
|
|
16
|
+
export { LineChart, type LineChartProps, type LineDots } from './LineChart';
|
|
17
|
+
export { MapChart, mapChartStyles, type MapArea, type MapChartProps, type MapLink, type MapPoint, type MapProjections, } from './MapChart';
|
|
17
18
|
export { RadarChart, radarChartStyles, type RadarChartProps } from './RadarChart';
|
|
18
19
|
export { SankeyChart, sankeyChartStyles, type SankeyAligns, type SankeyChartProps, } from './SankeyChart';
|
|
19
|
-
export { ScatterChart, type ScatterChartProps } from './ScatterChart';
|
|
20
|
-
export { TimelineChart, type TimelineChartItem, type TimelineChartMarker, type TimelineChartProps, type TimelineChartSeries, type TimelineTime, } from './TimelineChart';
|
|
20
|
+
export { ScatterChart, scatterChartStyles, type ScatterChartProps } from './ScatterChart';
|
|
21
|
+
export { TimelineChart, timelineChartStyles, type TimelineChartItem, type TimelineChartMarker, type TimelineChartProps, type TimelineChartSeries, type TimelineTime, } from './TimelineChart';
|
|
21
22
|
export { TreemapChart, treemapChartStyles, type TreemapChartProps } from './TreemapChart';
|
|
22
|
-
export { WaterfallChart, waterfallRows, type WaterfallChartProps, type WaterfallRow, type WaterfallStep, } from './WaterfallChart';
|
|
23
|
+
export { WaterfallChart, waterfallChartStyles, waterfallRows, type WaterfallChartProps, type WaterfallRow, type WaterfallStep, } from './WaterfallChart';
|
|
23
24
|
export { XYChart, xyChartStyles, type ChartAnnotation, type ChartBand, type ChartSecondaryAxis, type ChartSkeletons, type ChartTarget, type XYChartContext, type XYChartKind, type XYChartProps, } from './XYChart';
|
|
24
25
|
export { alignedTicks, niceStep, type AlignedAxes, type AxisTicks } from './axis-ticks';
|
|
25
26
|
export { exportChartData, exportChartImage, serializeChartSvg } from './export';
|
|
26
27
|
export { flowData, resolveFlowNodes, type FlowLink, type FlowNode, type ResolvedFlowNode, } from './flow';
|
|
27
|
-
export {
|
|
28
|
+
export { ChartFrame, chartStyles, chartTooltip, useChartValueText, type ChartFrameParts, type ChartFrameProps, type ChartLegendItem, type ChartTable, type ChartTooltipItem, } from './frame';
|
|
29
|
+
export { createProjection, type MapGeoProjection, type MapProjectionName, type ProjectionOptions, type Projector, } from './geo/projection';
|
|
30
|
+
export { simplifyTopology, topologyBounds, topologyFeatures, type GeoPosition, type MapFeature, type MapTopology, type MapTopologyObject, } from './geo/topojson';
|
|
28
31
|
export { placeLabels, rectsOverlap, type LabelAnchor, type LabelLayoutOptions, type LabelPlacement, type LabelRect, } from './label-layout';
|
|
32
|
+
export { chordLayout, type ChordGroup, type ChordInputLink, type ChordLink } from './layout/chord';
|
|
33
|
+
export { graphLayout, type GraphLayoutInput, type GraphLayoutName, type GraphLayoutOptions, } from './layout/graph';
|
|
34
|
+
export { sankeyLayout, type SankeyAlign, type SankeyInputLink, type SankeyLayoutLink, type SankeyLayoutNode, type SankeyLayoutOptions, } from './layout/sankey';
|
|
35
|
+
export { groupTree, treemapLayout, type TreemapLayoutOptions, type TreemapNode, } from './layout/treemap';
|
|
36
|
+
export { chartAreas, chartBar, chartBars, chartDots, chartLines, chartMarkStyles, type ChartAreaOptions, type ChartBarOptions, type ChartLineOptions, } from './marks';
|
|
37
|
+
export { chartAxis, chartSvgStyles, ChartTooltipBox, chartTooltipStyles, pointerIn, usePlotArea, usePlotSeen, type AxisTick, type ChartAxisOptions, type ChartTip, type PlotArea, type PlotRect, } from './plot';
|
|
38
|
+
export { linearScale, linearTicks, niceDomain, tickIncrement, timeTicks, type LinearScale, } from './scale';
|
|
29
39
|
export { resolveColor, resolveSeries, seriesNumber, seriesValue, type ChartColor, type ChartPaletteColor, type ChartSeries, type ChartToneColor, type NumericKey, type ResolvedSeries, } from './series';
|
|
30
|
-
export {
|
|
40
|
+
export { arcPath, areaPath, barPath, horizontalLinkPath, linePath, polarPoint, ribbonPath, type ArcShape, type BarEnd, type ChartPoint, type LineCurves, } from './shape';
|
|
41
|
+
export { stackValues, type StackSegment } from './stack';
|
|
42
|
+
export { estimateTextWidth, truncateText } from './text-size';
|
package/dist/charts/index.js
CHANGED
|
@@ -1,24 +1,35 @@
|
|
|
1
|
+
import { ChartTooltipBox, chartAxis, chartSvgStyles, chartTooltipStyles, pointerIn, usePlotArea, usePlotSeen } from "./plot.js";
|
|
2
|
+
import { arcPath, areaPath, barPath, horizontalLinkPath, linePath, polarPoint, ribbonPath } from "./shape.js";
|
|
3
|
+
import { stackValues } from "./stack.js";
|
|
4
|
+
import { chartAreas, chartBar, chartBars, chartDots, chartLines, chartMarkStyles } from "./marks.js";
|
|
5
|
+
import { linearScale, linearTicks, niceDomain, tickIncrement, timeTicks } from "./scale.js";
|
|
1
6
|
import { alignedTicks, niceStep } from "./axis-ticks.js";
|
|
2
7
|
import { exportChartData, exportChartImage, serializeChartSvg } from "./export.js";
|
|
3
|
-
import { ChartFrame,
|
|
8
|
+
import { ChartFrame, chartStyles, chartTooltip, useChartValueText } from "./frame.js";
|
|
4
9
|
import { resolveColor, resolveSeries, seriesNumber, seriesValue } from "./series.js";
|
|
10
|
+
import { estimateTextWidth, truncateText } from "./text-size.js";
|
|
5
11
|
import { XYChart, xyChartStyles } from "./XYChart.js";
|
|
6
|
-
import { CURVES, LineChart } from "./LineChart.js";
|
|
7
12
|
import { AreaChart } from "./AreaChart.js";
|
|
8
13
|
import { BarChart } from "./BarChart.js";
|
|
9
14
|
import { flowData, resolveFlowNodes } from "./flow.js";
|
|
10
|
-
import {
|
|
15
|
+
import { chordLayout } from "./layout/chord.js";
|
|
11
16
|
import { ChordChart, chordChartStyles } from "./ChordChart.js";
|
|
12
17
|
import { ComboChart } from "./ComboChart.js";
|
|
13
18
|
import { DonutChart, donutChartStyles } from "./DonutChart.js";
|
|
14
19
|
import { FunnelChart, funnelChartDynamicStyles, funnelChartStyles } from "./FunnelChart.js";
|
|
20
|
+
import { graphLayout } from "./layout/graph.js";
|
|
15
21
|
import { GraphChart, graphChartStyles } from "./GraphChart.js";
|
|
22
|
+
import { LineChart } from "./LineChart.js";
|
|
23
|
+
import { createProjection } from "./geo/projection.js";
|
|
24
|
+
import { simplifyTopology, topologyBounds, topologyFeatures } from "./geo/topojson.js";
|
|
16
25
|
import { placeLabels, rectsOverlap } from "./label-layout.js";
|
|
17
|
-
import { MapChart, mapChartStyles
|
|
26
|
+
import { MapChart, mapChartStyles } from "./MapChart.js";
|
|
18
27
|
import { RadarChart, radarChartStyles } from "./RadarChart.js";
|
|
28
|
+
import { sankeyLayout } from "./layout/sankey.js";
|
|
19
29
|
import { SankeyChart, sankeyChartStyles } from "./SankeyChart.js";
|
|
20
|
-
import { ScatterChart } from "./ScatterChart.js";
|
|
21
|
-
import { TimelineChart } from "./TimelineChart.js";
|
|
30
|
+
import { ScatterChart, scatterChartStyles } from "./ScatterChart.js";
|
|
31
|
+
import { TimelineChart, timelineChartStyles } from "./TimelineChart.js";
|
|
32
|
+
import { groupTree, treemapLayout } from "./layout/treemap.js";
|
|
22
33
|
import { TreemapChart, treemapChartStyles } from "./TreemapChart.js";
|
|
23
|
-
import { WaterfallChart, waterfallRows } from "./WaterfallChart.js";
|
|
24
|
-
export { AreaChart, BarChart,
|
|
34
|
+
import { WaterfallChart, waterfallChartStyles, waterfallRows } from "./WaterfallChart.js";
|
|
35
|
+
export { AreaChart, BarChart, ChartFrame, ChartTooltipBox, ChordChart, ComboChart, DonutChart, FunnelChart, GraphChart, LineChart, MapChart, RadarChart, SankeyChart, ScatterChart, TimelineChart, TreemapChart, WaterfallChart, XYChart, alignedTicks, arcPath, areaPath, barPath, chartAreas, chartAxis, chartBar, chartBars, chartDots, chartLines, chartMarkStyles, chartStyles, chartSvgStyles, chartTooltip, chartTooltipStyles, chordChartStyles, chordLayout, createProjection, donutChartStyles, estimateTextWidth, exportChartData, exportChartImage, flowData, funnelChartDynamicStyles, funnelChartStyles, graphChartStyles, graphLayout, groupTree, horizontalLinkPath, linePath, linearScale, linearTicks, mapChartStyles, niceDomain, niceStep, placeLabels, pointerIn, polarPoint, radarChartStyles, rectsOverlap, resolveColor, resolveFlowNodes, resolveSeries, ribbonPath, sankeyChartStyles, sankeyLayout, scatterChartStyles, serializeChartSvg, seriesNumber, seriesValue, simplifyTopology, stackValues, tickIncrement, timeTicks, timelineChartStyles, topologyBounds, topologyFeatures, treemapChartStyles, treemapLayout, truncateText, useChartValueText, usePlotArea, usePlotSeen, waterfallChartStyles, waterfallRows, xyChartStyles };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label-layout.js","names":[],"sources":["../../src/charts/label-layout.ts"],"sourcesContent":["/**\n * 点标注的避让:给每个锚点(地图上的地点)找一个不压住别的标签、标记的位置。\n * 参照制图学的点标注(Imhof 的方位优先级)与 ECharts labelLayout 的 moveOverlap、Mapbox 的 variable anchor:\n * 按优先级贪心放置,先试紧挨着标记的 8 个方位,都放不下时一圈圈往外退(默认两圈)、画引线连回标记,仍放不下就不显示。\n * 坐标都是屏幕像素。\n *\n * const placements = placeLabels(anchors, { bounds: { x: 0, y: 0, width, height }, soft: areaLabelBoxes });\n * placements[i] // null 时不显示;否则 box 是标签的矩形,leader 时画引线\n */\n\n/** 矩形:左上角与宽高 */\nexport interface LabelRect {\n /** 左 */\n x: number;\n /** 上 */\n y: number;\n /** 宽 */\n width: number;\n /** 高 */\n height: number;\n}\n\n/** 要标注的点 */\nexport interface LabelAnchor {\n /** 中心横坐标 */\n x: number;\n /** 中心纵坐标 */\n y: number;\n /** 标记的半径:标签放在它外面 */\n radius: number;\n /** 标签的宽 */\n width: number;\n /** 标签的高 */\n height: number;\n /** 优先级:大的先放(地图按数值),默认 0;相同时按原来的顺序 */\n priority?: number;\n}\n\n/** 一个标签放在哪 */\nexport interface LabelPlacement {\n /** 标签的矩形 */\n box: LabelRect;\n /** 退到了外一圈:画一条引线连回标记 */\n leader: boolean;\n}\n\n/** placeLabels 的选项 */\nexport interface LabelLayoutOptions {\n /** 标签要落在这个范围里(绘图区);不给时不限 */\n bounds?: LabelRect;\n /** 尽量不压住的矩形(地图上的区域名):都避不开时可以压住 */\n soft?: readonly LabelRect[];\n /** 标签与标记的间距,默认 3 */\n gap?: number;\n /** 往外退时每圈远多少,默认 16 */\n ring?: number;\n /** 往外退几圈,默认 2 */\n rings?: number;\n}\n\n//
|
|
1
|
+
{"version":3,"file":"label-layout.js","names":[],"sources":["../../src/charts/label-layout.ts"],"sourcesContent":["/**\n * 点标注的避让:给每个锚点(地图上的地点)找一个不压住别的标签、标记的位置。\n * 参照制图学的点标注(Imhof 的方位优先级)与 ECharts labelLayout 的 moveOverlap、Mapbox 的 variable anchor:\n * 按优先级贪心放置,先试紧挨着标记的 8 个方位,都放不下时一圈圈往外退(默认两圈)、画引线连回标记,仍放不下就不显示。\n * 坐标都是屏幕像素。\n *\n * const placements = placeLabels(anchors, { bounds: { x: 0, y: 0, width, height }, soft: areaLabelBoxes });\n * placements[i] // null 时不显示;否则 box 是标签的矩形,leader 时画引线\n */\n\n/** 矩形:左上角与宽高 */\nexport interface LabelRect {\n /** 左 */\n x: number;\n /** 上 */\n y: number;\n /** 宽 */\n width: number;\n /** 高 */\n height: number;\n}\n\n/** 要标注的点 */\nexport interface LabelAnchor {\n /** 中心横坐标 */\n x: number;\n /** 中心纵坐标 */\n y: number;\n /** 标记的半径:标签放在它外面 */\n radius: number;\n /** 标签的宽 */\n width: number;\n /** 标签的高 */\n height: number;\n /** 优先级:大的先放(地图按数值),默认 0;相同时按原来的顺序 */\n priority?: number;\n}\n\n/** 一个标签放在哪 */\nexport interface LabelPlacement {\n /** 标签的矩形 */\n box: LabelRect;\n /** 退到了外一圈:画一条引线连回标记 */\n leader: boolean;\n}\n\n/** placeLabels 的选项 */\nexport interface LabelLayoutOptions {\n /** 标签要落在这个范围里(绘图区);不给时不限 */\n bounds?: LabelRect;\n /** 尽量不压住的矩形(地图上的区域名):都避不开时可以压住 */\n soft?: readonly LabelRect[];\n /** 标签与标记的间距,默认 3 */\n gap?: number;\n /** 往外退时每圈远多少,默认 16 */\n ring?: number;\n /** 往外退几圈,默认 2 */\n rings?: number;\n}\n\n// 方位的优先级:下(地图上地名常见的位置)、上、右、左,然后四个斜角\nconst directions: readonly (readonly [number, number])[] = [\n [0, 1],\n [0, -1],\n [1, 0],\n [-1, 0],\n [1, 1],\n [1, -1],\n [-1, 1],\n [-1, -1],\n];\n\n/** 两个矩形相交(边贴边不算) */\nexport function rectsOverlap(a: LabelRect, b: LabelRect): boolean {\n return a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height;\n}\n\nconst inside = (box: LabelRect, bounds: LabelRect) =>\n box.x >= bounds.x &&\n box.y >= bounds.y &&\n box.x + box.width <= bounds.x + bounds.width &&\n box.y + box.height <= bounds.y + bounds.height;\n\n/** 锚点在某个方位、某个距离上的标签矩形:标签靠向标记的那条边(或那个角)离标记 distance */\nfunction boxAt(anchor: LabelAnchor, [dx, dy]: readonly [number, number], distance: number) {\n // 斜角按 45° 摆,两个方向各走 distance / √2\n const step = dx && dy ? distance * Math.SQRT1_2 : distance;\n const x = anchor.x + dx * step - (dx === 1 ? 0 : dx === -1 ? anchor.width : anchor.width / 2);\n const y = anchor.y + dy * step - (dy === 1 ? 0 : dy === -1 ? anchor.height : anchor.height / 2);\n return { x, y, width: anchor.width, height: anchor.height };\n}\n\n/** 给每个锚点找位置(见文件开头);返回与 anchors 一一对应 */\nexport function placeLabels(\n anchors: readonly LabelAnchor[],\n { bounds, soft = [], gap = 3, ring = 16, rings = 2 }: LabelLayoutOptions = {},\n): (LabelPlacement | null)[] {\n const markers = anchors.map((anchor) => ({\n x: anchor.x - anchor.radius,\n y: anchor.y - anchor.radius,\n width: anchor.radius * 2,\n height: anchor.radius * 2,\n }));\n const placed: LabelRect[] = [];\n const result: (LabelPlacement | null)[] = anchors.map(() => null);\n const order = anchors\n .map((anchor, index) => ({ index, priority: anchor.priority ?? 0 }))\n .sort((a, b) => b.priority - a.priority || a.index - b.index);\n\n const free = (box: LabelRect) =>\n (!bounds || inside(box, bounds)) &&\n !markers.some((marker) => rectsOverlap(box, marker)) &&\n !placed.some((other) => rectsOverlap(box, other));\n\n for (const { index } of order) {\n const anchor = anchors[index]!;\n // 紧挨着一圈,再一圈圈往外;每圈先找不压区域名的,再退而求其次\n for (let level = 0; level <= rings; level++) {\n const distance = anchor.radius + gap + ring * level;\n const leader = level > 0;\n const candidates = directions.map((direction) => boxAt(anchor, direction, distance));\n const box =\n candidates.find((item) => free(item) && !soft.some((rect) => rectsOverlap(item, rect))) ??\n candidates.find(free);\n if (box) {\n placed.push(box);\n result[index] = { box, leader };\n break;\n }\n }\n }\n return result;\n}\n"],"mappings":";AA6DA,IAAM,aAAqD;CACzD,CAAC,GAAG,CAAC;CACL,CAAC,GAAG,EAAE;CACN,CAAC,GAAG,CAAC;CACL,CAAC,IAAI,CAAC;CACN,CAAC,GAAG,CAAC;CACL,CAAC,GAAG,EAAE;CACN,CAAC,IAAI,CAAC;CACN,CAAC,IAAI,EAAE;AACT;;AAGA,SAAgB,aAAa,GAAc,GAAuB;CAChE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE;AAC7F;AAEA,IAAM,UAAU,KAAgB,WAC9B,IAAI,KAAK,OAAO,KAChB,IAAI,KAAK,OAAO,KAChB,IAAI,IAAI,IAAI,SAAS,OAAO,IAAI,OAAO,SACvC,IAAI,IAAI,IAAI,UAAU,OAAO,IAAI,OAAO;;AAG1C,SAAS,MAAM,QAAqB,CAAC,IAAI,KAAgC,UAAkB;CAEzF,MAAM,OAAO,MAAM,KAAK,WAAW,KAAK,UAAU;CAGlD,OAAO;EAAE,GAFC,OAAO,IAAI,KAAK,QAAQ,OAAO,IAAI,IAAI,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ;EAE/E,GADF,OAAO,IAAI,KAAK,QAAQ,OAAO,IAAI,IAAI,OAAO,KAAK,OAAO,SAAS,OAAO,SAAS;EAC9E,OAAO,OAAO;EAAO,QAAQ,OAAO;CAAO;AAC5D;;AAGA,SAAgB,YACd,SACA,EAAE,QAAQ,OAAO,CAAC,GAAG,MAAM,GAAG,OAAO,IAAI,QAAQ,MAA0B,CAAC,GACjD;CAC3B,MAAM,UAAU,QAAQ,KAAK,YAAY;EACvC,GAAG,OAAO,IAAI,OAAO;EACrB,GAAG,OAAO,IAAI,OAAO;EACrB,OAAO,OAAO,SAAS;EACvB,QAAQ,OAAO,SAAS;CAC1B,EAAE;CACF,MAAM,SAAsB,CAAC;CAC7B,MAAM,SAAoC,QAAQ,UAAU,IAAI;CAChE,MAAM,QAAQ,QACX,KAAK,QAAQ,WAAW;EAAE;EAAO,UAAU,OAAO,YAAY;CAAE,EAAE,CAAC,CACnE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK;CAE9D,MAAM,QAAQ,SACX,CAAC,UAAU,OAAO,KAAK,MAAM,MAC9B,CAAC,QAAQ,MAAM,WAAW,aAAa,KAAK,MAAM,CAAC,KACnD,CAAC,OAAO,MAAM,UAAU,aAAa,KAAK,KAAK,CAAC;CAElD,KAAK,MAAM,EAAE,WAAW,OAAO;EAC7B,MAAM,SAAS,QAAQ;EAEvB,KAAK,IAAI,QAAQ,GAAG,SAAS,OAAO,SAAS;GAC3C,MAAM,WAAW,OAAO,SAAS,MAAM,OAAO;GAC9C,MAAM,SAAS,QAAQ;GACvB,MAAM,aAAa,WAAW,KAAK,cAAc,MAAM,QAAQ,WAAW,QAAQ,CAAC;GACnF,MAAM,MACJ,WAAW,MAAM,SAAS,KAAK,IAAI,KAAK,CAAC,KAAK,MAAM,SAAS,aAAa,MAAM,IAAI,CAAC,CAAC,KACtF,WAAW,KAAK,IAAI;GACtB,IAAI,KAAK;IACP,OAAO,KAAK,GAAG;IACf,OAAO,SAAS;KAAE;KAAK;IAAO;IAC9B;GACF;EACF;CACF;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 弦图的布局:节点排成一圈,每个节点一段弧(长短按流入加流出的总量),每条流向一根弦,
|
|
3
|
+
* 起点一端在起点节点的弧上、终点一端在终点节点的弧上,宽度都按流量(d3-chord 的 chordDirected,ISC)。
|
|
4
|
+
* 角度从 12 点钟方向顺时针(弧度),与 arcPath、ribbonPath 一致。
|
|
5
|
+
*
|
|
6
|
+
* const { groups, chords } = chordLayout(3, [{ source: 0, target: 1, value: 5 }], 0.04);
|
|
7
|
+
* groups[0].start、end;chords[0].source([起, 止])、target
|
|
8
|
+
*/
|
|
9
|
+
/** 一条流向:起点、终点是节点的序号 */
|
|
10
|
+
export interface ChordInputLink {
|
|
11
|
+
/** 起点节点的序号 */
|
|
12
|
+
source: number;
|
|
13
|
+
/** 终点节点的序号 */
|
|
14
|
+
target: number;
|
|
15
|
+
/** 流量,大于 0 */
|
|
16
|
+
value: number;
|
|
17
|
+
}
|
|
18
|
+
/** 一个节点的弧 */
|
|
19
|
+
export interface ChordGroup {
|
|
20
|
+
/** 节点的序号 */
|
|
21
|
+
index: number;
|
|
22
|
+
/** 起始角 */
|
|
23
|
+
start: number;
|
|
24
|
+
/** 结束角 */
|
|
25
|
+
end: number;
|
|
26
|
+
/** 流入加流出的总量 */
|
|
27
|
+
value: number;
|
|
28
|
+
}
|
|
29
|
+
/** 一根弦 */
|
|
30
|
+
export interface ChordLink {
|
|
31
|
+
/** 在输入里的位置 */
|
|
32
|
+
index: number;
|
|
33
|
+
/** 起点节点上的一段 [起始角, 结束角] */
|
|
34
|
+
source: [number, number];
|
|
35
|
+
/** 终点节点上的一段 */
|
|
36
|
+
target: [number, number];
|
|
37
|
+
/** 流量 */
|
|
38
|
+
value: number;
|
|
39
|
+
}
|
|
40
|
+
/** 弦图的布局;count 是节点数,padAngle 是相邻两段弧之间的空隙(弧度) */
|
|
41
|
+
export declare function chordLayout(count: number, links: readonly ChordInputLink[], padAngle: number): {
|
|
42
|
+
groups: ChordGroup[];
|
|
43
|
+
chords: ChordLink[];
|
|
44
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/charts/layout/chord.ts
|
|
2
|
+
/** 弦图的布局;count 是节点数,padAngle 是相邻两段弧之间的空隙(弧度) */
|
|
3
|
+
function chordLayout(count, links, padAngle) {
|
|
4
|
+
const totals = Array.from({ length: count }, () => 0);
|
|
5
|
+
for (const link of links) {
|
|
6
|
+
totals[link.source] += link.value;
|
|
7
|
+
totals[link.target] += link.value;
|
|
8
|
+
}
|
|
9
|
+
const sum = totals.reduce((a, b) => a + b, 0);
|
|
10
|
+
const k = sum > 0 ? Math.max(0, Math.PI * 2 - padAngle * count) / sum : 0;
|
|
11
|
+
const chords = links.map((link, index) => ({
|
|
12
|
+
index,
|
|
13
|
+
source: [0, 0],
|
|
14
|
+
target: [0, 0],
|
|
15
|
+
value: link.value
|
|
16
|
+
}));
|
|
17
|
+
const groups = [];
|
|
18
|
+
let angle = 0;
|
|
19
|
+
for (let node = 0; node < count; node += 1) {
|
|
20
|
+
const start = angle;
|
|
21
|
+
for (let other = count - 1; other >= 0; other -= 1) links.forEach((link, index) => {
|
|
22
|
+
if (link.target !== node || link.source !== other) return;
|
|
23
|
+
chords[index].target = [angle, angle += link.value * k];
|
|
24
|
+
});
|
|
25
|
+
for (let other = 0; other < count; other += 1) links.forEach((link, index) => {
|
|
26
|
+
if (link.source !== node || link.target !== other) return;
|
|
27
|
+
chords[index].source = [angle, angle += link.value * k];
|
|
28
|
+
});
|
|
29
|
+
groups.push({
|
|
30
|
+
index: node,
|
|
31
|
+
start,
|
|
32
|
+
end: angle,
|
|
33
|
+
value: totals[node]
|
|
34
|
+
});
|
|
35
|
+
angle += padAngle;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
groups,
|
|
39
|
+
chords
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { chordLayout };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=chord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chord.js","names":[],"sources":["../../../src/charts/layout/chord.ts"],"sourcesContent":["/**\n * 弦图的布局:节点排成一圈,每个节点一段弧(长短按流入加流出的总量),每条流向一根弦,\n * 起点一端在起点节点的弧上、终点一端在终点节点的弧上,宽度都按流量(d3-chord 的 chordDirected,ISC)。\n * 角度从 12 点钟方向顺时针(弧度),与 arcPath、ribbonPath 一致。\n *\n * const { groups, chords } = chordLayout(3, [{ source: 0, target: 1, value: 5 }], 0.04);\n * groups[0].start、end;chords[0].source([起, 止])、target\n */\n\n/** 一条流向:起点、终点是节点的序号 */\nexport interface ChordInputLink {\n /** 起点节点的序号 */\n source: number;\n /** 终点节点的序号 */\n target: number;\n /** 流量,大于 0 */\n value: number;\n}\n\n/** 一个节点的弧 */\nexport interface ChordGroup {\n /** 节点的序号 */\n index: number;\n /** 起始角 */\n start: number;\n /** 结束角 */\n end: number;\n /** 流入加流出的总量 */\n value: number;\n}\n\n/** 一根弦 */\nexport interface ChordLink {\n /** 在输入里的位置 */\n index: number;\n /** 起点节点上的一段 [起始角, 结束角] */\n source: [number, number];\n /** 终点节点上的一段 */\n target: [number, number];\n /** 流量 */\n value: number;\n}\n\n/** 弦图的布局;count 是节点数,padAngle 是相邻两段弧之间的空隙(弧度) */\nexport function chordLayout(\n count: number,\n links: readonly ChordInputLink[],\n padAngle: number,\n): { groups: ChordGroup[]; chords: ChordLink[] } {\n const totals = Array.from({ length: count }, () => 0);\n for (const link of links) {\n totals[link.source]! += link.value;\n totals[link.target]! += link.value;\n }\n const sum = totals.reduce((a, b) => a + b, 0);\n const k = sum > 0 ? Math.max(0, Math.PI * 2 - padAngle * count) / sum : 0;\n const chords: ChordLink[] = links.map((link, index) => ({\n index,\n source: [0, 0],\n target: [0, 0],\n value: link.value,\n }));\n const groups: ChordGroup[] = [];\n let angle = 0;\n for (let node = 0; node < count; node += 1) {\n const start = angle;\n // 先排流入(按起点从后往前),再排流出(按终点从前往后):相邻节点之间的弦不交叉\n for (let other = count - 1; other >= 0; other -= 1)\n links.forEach((link, index) => {\n if (link.target !== node || link.source !== other) return;\n chords[index]!.target = [angle, (angle += link.value * k)];\n });\n for (let other = 0; other < count; other += 1)\n links.forEach((link, index) => {\n if (link.source !== node || link.target !== other) return;\n chords[index]!.source = [angle, (angle += link.value * k)];\n });\n groups.push({ index: node, start, end: angle, value: totals[node]! });\n angle += padAngle;\n }\n return { groups, chords };\n}\n"],"mappings":";;AA4CA,SAAgB,YACd,OACA,OACA,UAC+C;CAC/C,MAAM,SAAS,MAAM,KAAK,EAAE,QAAQ,MAAM,SAAS,CAAC;CACpD,KAAK,MAAM,QAAQ,OAAO;EACxB,OAAO,KAAK,WAAY,KAAK;EAC7B,OAAO,KAAK,WAAY,KAAK;CAC/B;CACA,MAAM,MAAM,OAAO,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC;CAC5C,MAAM,IAAI,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,WAAW,KAAK,IAAI,MAAM;CACxE,MAAM,SAAsB,MAAM,KAAK,MAAM,WAAW;EACtD;EACA,QAAQ,CAAC,GAAG,CAAC;EACb,QAAQ,CAAC,GAAG,CAAC;EACb,OAAO,KAAK;CACd,EAAE;CACF,MAAM,SAAuB,CAAC;CAC9B,IAAI,QAAQ;CACZ,KAAK,IAAI,OAAO,GAAG,OAAO,OAAO,QAAQ,GAAG;EAC1C,MAAM,QAAQ;EAEd,KAAK,IAAI,QAAQ,QAAQ,GAAG,SAAS,GAAG,SAAS,GAC/C,MAAM,SAAS,MAAM,UAAU;GAC7B,IAAI,KAAK,WAAW,QAAQ,KAAK,WAAW,OAAO;GACnD,OAAO,MAAM,CAAE,SAAS,CAAC,OAAQ,SAAS,KAAK,QAAQ,CAAE;EAC3D,CAAC;EACH,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,SAAS,GAC1C,MAAM,SAAS,MAAM,UAAU;GAC7B,IAAI,KAAK,WAAW,QAAQ,KAAK,WAAW,OAAO;GACnD,OAAO,MAAM,CAAE,SAAS,CAAC,OAAQ,SAAS,KAAK,QAAQ,CAAE;EAC3D,CAAC;EACH,OAAO,KAAK;GAAE,OAAO;GAAM;GAAO,KAAK;GAAO,OAAO,OAAO;EAAO,CAAC;EACpE,SAAS;CACX;CACA,OAAO;EAAE;EAAQ;CAAO;AAC1B"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 网络图的布局:给每个节点算出坐标(单位是 px,原点任意,画的时候再整体居中、按绘图区缩放)。
|
|
3
|
+
*
|
|
4
|
+
* - force:力导向(d3-force 的做法:连线像弹簧、节点互相排斥、往中心拉、不重叠),从固定的初始位置迭代 300 步,结果是确定的;
|
|
5
|
+
* - layered:分层(Sugiyama 的简化版:去环、按最长路径分层、重心法几轮减少交叉、向邻居的平均位置靠拢),direction 定方向;
|
|
6
|
+
* - circular:排成一圈;concentric:连得多的在中间,一圈圈往外;parallel:按组分列,列里按连接排。
|
|
7
|
+
* 有组时 layered、circular、parallel 让同组的节点挨在一起(组的先后按 groups)。
|
|
8
|
+
*
|
|
9
|
+
* const positions = graphLayout(nodes, links, { layout: 'layered', direction: 'LR', groups: ['a', 'b'] });
|
|
10
|
+
* positions.get('n1') // { x, y }
|
|
11
|
+
*/
|
|
12
|
+
/** 布局 */
|
|
13
|
+
export type GraphLayoutName = 'force' | 'layered' | 'circular' | 'concentric' | 'parallel';
|
|
14
|
+
/** 布局的输入:一个节点与它占的地方(节点加名字的外框) */
|
|
15
|
+
export interface GraphLayoutInput {
|
|
16
|
+
/** id */
|
|
17
|
+
id: string;
|
|
18
|
+
/** 所在的组 */
|
|
19
|
+
group?: string;
|
|
20
|
+
/** 占的宽 */
|
|
21
|
+
width: number;
|
|
22
|
+
/** 占的高 */
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
/** graphLayout 的选项 */
|
|
26
|
+
export interface GraphLayoutOptions {
|
|
27
|
+
/** 布局 */
|
|
28
|
+
layout: GraphLayoutName;
|
|
29
|
+
/** 分层、分列的方向:LR 从左到右,TB 从上到下 */
|
|
30
|
+
direction?: 'LR' | 'TB';
|
|
31
|
+
/** 组的先后 */
|
|
32
|
+
groups?: readonly string[];
|
|
33
|
+
/** 力导向的连线长度,默认 90 */
|
|
34
|
+
linkDistance?: number;
|
|
35
|
+
/** 力导向的排斥强度(负数),默认 -600 */
|
|
36
|
+
charge?: number;
|
|
37
|
+
}
|
|
38
|
+
/** 一个点 */
|
|
39
|
+
interface Position {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
}
|
|
43
|
+
type Links = readonly {
|
|
44
|
+
source: string;
|
|
45
|
+
target: string;
|
|
46
|
+
}[];
|
|
47
|
+
/** 网络图的布局(见文件开头) */
|
|
48
|
+
export declare function graphLayout(nodes: readonly GraphLayoutInput[], links: Links, { layout, direction, groups, linkDistance, charge }: GraphLayoutOptions): Map<string, Position>;
|
|
49
|
+
export {};
|