-
-
Notifications
You must be signed in to change notification settings - Fork 899
Expand file tree
/
Copy pathdraw-features.ts
More file actions
107 lines (89 loc) · 3.28 KB
/
draw-features.ts
File metadata and controls
107 lines (89 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { curveBasisClosed, line, select } from "d3";
import type { PackedGraphFeature } from "../modules/features";
import { clipPoly, round } from "../utils";
declare global {
var drawFeatures: () => void;
var simplify: (
points: [number, number][],
tolerance: number,
highestQuality?: boolean,
) => [number, number][];
var getFeaturePath: (feature: PackedGraphFeature) => string;
}
interface FeaturesHtml {
paths: string[];
landMask: string[];
waterMask: string[];
coastline: { [key: string]: string[] };
lakes: { [key: string]: string[] };
}
const featuresRenderer = (): void => {
TIME && console.time("drawFeatures");
const html: FeaturesHtml = {
paths: [],
landMask: [],
waterMask: ['<rect x="0" y="0" width="100%" height="100%" fill="white" />'],
coastline: {},
lakes: {},
};
for (const feature of pack.features) {
if (!feature || feature.type === "ocean") continue;
html.paths.push(
`<path d="${featurePathRenderer(feature)}" id="feature_${feature.i}" data-f="${feature.i}"></path>`,
);
if (feature.type === "lake") {
html.landMask.push(
`<use href="#feature_${feature.i}" data-f="${feature.i}" fill="black"></use>`,
);
html.waterMask.push(
`<use href="#feature_${feature.i}" data-f="${feature.i}" fill="white"></use>`,
);
const lakeGroup = feature.group || "freshwater";
if (!html.lakes[lakeGroup]) html.lakes[lakeGroup] = [];
html.lakes[lakeGroup].push(
`<use href="#feature_${feature.i}" data-f="${feature.i}"></use>`,
);
} else {
html.landMask.push(
`<use href="#feature_${feature.i}" data-f="${feature.i}" fill="white"></use>`,
);
html.waterMask.push(
`<use href="#feature_${feature.i}" data-f="${feature.i}" fill="black"></use>`,
);
const coastlineGroup =
feature.group === "lake_island" ? "lake_island" : "sea_island";
if (!html.coastline[coastlineGroup]) html.coastline[coastlineGroup] = [];
html.coastline[coastlineGroup].push(
`<use href="#feature_${feature.i}" data-f="${feature.i}"></use>`,
);
}
}
defs.select("#featurePaths").html(html.paths.join(""));
defs.select("#land").html(html.landMask.join(""));
defs.select("#water").html(html.waterMask.join(""));
coastline.selectAll<SVGGElement, unknown>("g").each(function () {
const paths = html.coastline[this.id] || [];
select(this).html(paths.join(""));
});
lakes.selectAll<SVGGElement, unknown>("g").each(function () {
const paths = html.lakes[this.id] || [];
select(this).html(paths.join(""));
});
TIME && console.timeEnd("drawFeatures");
};
function featurePathRenderer(feature: PackedGraphFeature): string {
const points: [number, number][] = feature.vertices.map(
(vertex: number) => pack.vertices.p[vertex],
);
if (points.some((point) => point === undefined)) {
ERROR && console.error("Undefined point in getFeaturePath");
return "";
}
const simplifiedPoints = simplify(points, 0.3);
const clippedPoints = clipPoly(simplifiedPoints, graphWidth, graphHeight);
const lineGen = line().curve(curveBasisClosed);
const path = `${round(lineGen(clippedPoints) || "")}Z`;
return path;
}
window.drawFeatures = featuresRenderer;
window.getFeaturePath = featurePathRenderer;