1import Graph from 'graphology';
2import * as colors from './colors';
4const example = '[0[3216291:t;9219219;4608:s[156546;1612196;219621:~;321965149:4608;]1280[4289594624;4278191360;426672256;4260168960;4260824320;4246668544;4246144256;4253746432;4235396352;4230415616;4225697024;4227007744;4219667712;4222616832;4224648448;4214490368;4215997696;4219143424;4237296896;]]]'
6export interface ChartPoint {
8 dominion: ('l1' | 'l2' | 'spawn'),
11 children?: ChartPoint[],
12 sponsees?: ChartPoint[],
15function parseMetaString(metaString: string): ChartPoint {
16 let idString, subMetaString = '';
17 const colIndex = metaString.indexOf(':');
18 if (colIndex === -1) {
19 idString = metaString;
21 idString = metaString.slice(0, colIndex);
22 subMetaString = metaString.slice(colIndex + 1);
24 const meta: ChartPoint = {
29 const firstChar = subMetaString[0];
30 if (firstChar === 't') {
32 subMetaString = subMetaString.slice(1);
33 } else if (firstChar === 's') {
34 meta.dominion = 'spawn';
35 subMetaString = subMetaString.slice(1);
38 if (subMetaString[0] === '~') {
41 let [sponsor, parent] = subMetaString.slice(1).split('!');
42 meta.sponsor = '~' + sponsor;
43 meta.parent = '~' + parent;
50function extractMeta(data: string): [ChartPoint, string] {
51 const index = data.search(/[[;]/);
53 throw new Error(`No point meta present in ${data}`);
55 const metaString = data.slice(0, index);
56 data = data.slice(index);
57 return [parseMetaString(metaString), data];
60function extractChildren(data: string, sup: string = undefined): [ChartPoint[], ChartPoint[], string] {
61 if (data[0] === ';') return [[], [], data.slice(1)];
62 if (data[0] !== '[') throw new Error('Invalid children in data: ' + data.slice(0, 20));
69 while (!endFound && data.length && counter < 20000) {
70 if (data[0] === ']') {
75 [sub, data] = extractChartPoint(data);
76 if (!sub.parent || sub.parent === sup)
78 if (sub.sponsor === undefined || sub.sponsor === sup)
83 return [children, sponsees, data];
86function extractChartPoint(data: string): [ChartPoint, string] {
87 let point, children, sponsees;
88 [point, data] = extractMeta(data);
89 [children, sponsees, data] = extractChildren(data, point.patp);
90 point.children = children;
91 point.sponsees = sponsees;
95function linkChartData(points: ChartPoint[], store: any): ChartPoint[] {
96 if (!points) return points;
97 return points.map(point => {
98 if (!store[point.patp]) return point;
99 if (!point.children?.length) {
100 point.children = linkChartData(store[point.patp].children, store);
102 if (!point.sponsees?.length) {
103 point.sponsees = linkChartData(store[point.patp].sponsees, store);
109function _parseChartData(str: string): ChartPoint[] {
111 let [baseChildren, _] = extractChildren(str);
113 baseChildren.forEach(child => {
114 store[child.patp] = {
115 children: child.children,
116 sponsees: child.sponsees,
120 return linkChartData(store['~base'].children, store);
123export function parseChartData(data: string): ChartPoint[] {
124 let start = Date.now();
125 let parsed = _parseChartData(data);
126 // console.log(`finished parsing chart data to js object in ${Date.now() - start} ms`);
130const circle = 2 * Math.PI;
131function chordLength(radius, radians) {
132 return 2 * radius * Math.sin(radians / 2);
134function distancePolar(r1, th1, r2, th2) {
135 return Math.sqrt((r1 * r1) + (r2 * r2) - (2 * r1 * r2 * Math.cos(th2 - th1)));
137export function arrangePointsInSpirals(
140 maxDistance, minDistance,
141 startX = 0, startY = 0,
142 maxArms = 6, maxPerArm = 45,
143 makeSingleArmCircle = false,
144 }): { coords: any[], numArms: number, minDistanceBetween: number } {
145 const numSmallEnoughArms = Math.ceil(numPoints / maxPerArm);
146 let eRootNumArms = Math.max(1, Math.floor(Math.pow(numPoints, 1 / Math.E)));
147 const numArms = Math.max(numSmallEnoughArms, eRootNumArms);
148 const singleArmCircle = makeSingleArmCircle && numArms === 1;
149 const numPerArm = numPoints / numArms;
150 let armThetaRange = numPerArm * Math.min(Math.PI / 12, 0.25 * circle / numArms);
151 if (singleArmCircle) {
152 minDistance = maxDistance;
153 armThetaRange = circle;
155 const distanceRange = maxDistance - minDistance;
156 const pointWeight = 1 / numPerArm;
157 let minDistanceBetween = distancePolar(minDistance, 0,
158 minDistance + distanceRange * pointWeight, armThetaRange * pointWeight);
159 if (numPoints <= 3) minDistanceBetween = maxDistance * 0.75;
162 let indexInArm = null;
163 for (let i = 0; i < numPoints; i++) {
164 const overallProgress = i / numPoints;
165 const arm = Math.floor(numArms * overallProgress);
166 if (lastArm === arm) {
171 const interArmProgress = arm / numArms;
172 const beginningOfNextArm = Math.floor(numPerArm * (arm + 1));
173 const numInThisArm = beginningOfNextArm - (i - indexInArm);
174 const intraArmProgress = indexInArm / numInThisArm;
175 const theta = circle * interArmProgress + armThetaRange * intraArmProgress;
176 let line = (minDistance + distanceRange * intraArmProgress);
177 let x = 0 + startX + line * Math.cos(theta);
178 let y = 0 + startY + line * Math.sin(theta);
181 arm: singleArmCircle ? i : arm,
192export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: boolean, callback?): Graph {
193 if (!(chartData instanceof Array)) return;
200 let subsKey = bySponsor ? 'sponsees' : 'children';
201 let sponsorEdges = [];
210 chartData.forEach((galaxy, gi) => {
211 offsetX = 0 + (a + b * theta) * Math.cos(theta);
212 offsetY = 0 + (a + b * theta) * Math.sin(theta);
214 theta = 0.15 * (gi+1) * (1 - .15 * (gi / 256));
215 const gPatp = galaxy.patp;
216 const itsYou = gPatp === '~' + window.ship;
220 color: itsYou ? colors.YOU : colors.WHITE,
226 const stars = galaxy[subsKey] || [];
227 const numStars = stars.length;
228 const maxStarDistance = 60000 * 128;
229 const minStarDistance = maxStarDistance / 4;
230 const starSize = 1.2 / Math.pow(numStars, 1 / 7);
233 numArms: numStarArms,
234 minDistanceBetween: minDistanceBetweenStars,
235 } = arrangePointsInSpirals(numStars,
237 maxDistance: maxStarDistance,
238 minDistance: minStarDistance,
241 makeSingleArmCircle: true,
243 let lastStarArm = null;
244 let lastStarPatp = null;
245 stars.forEach((star, si) => {
246 const sPatp = star.patp;
248 const [starX, starY] = starCoords[si].coords;
249 const arm = starCoords[si].arm;
251 const itsYou = sPatp === '~' + window.ship;
253 g.setNodeAttribute(gPatp, 'color', colors.YOUR_GAL);
259 color: itsYou ? colors.YOU : colors.GOLDT2,
264 if (lastStarArm == arm) {
265 g.addEdge(sPatp, lastStarPatp, {
271 g.addEdge(sPatp, gPatp, {
278 lastStarPatp = sPatp;
280 sponsorEdges.push([sPatp, star.sponsor, {
287 const planets = star[subsKey] || [];
288 const numPlanets = planets.length;
289 // const maxPlanetDistance = starSize * 1.6 * 4000 * 64;
290 const maxPlanetDistance = minDistanceBetweenStars * 0.45;
291 const minPlanetDistance = maxPlanetDistance / 4;
293 coords: planetCoords,
294 numArms: numPlanetArms,
295 minDistanceBetween: minDistanceBetweenPlanets,
296 } = arrangePointsInSpirals(numPlanets,
298 maxDistance: maxPlanetDistance,
299 minDistance: minPlanetDistance,
303 makeSingleArmCircle: true,
305 let lastPlanetArm = null;
306 let lastPlanetPatp = null;
307 planets.forEach((planet, pi) => {
308 const pPatp = planet.patp;
309 const [planetX, planetY] = planetCoords[pi].coords;
310 const arm = planetCoords[pi].arm;
311 const planetSize = Math.sqrt(Math.min(minPlanetDistance, minDistanceBetweenPlanets)) / 1300;
312 const planetEdgeSize = planetSize / 3;
313 const itsYou = pPatp === '~' + window.ship;
315 g.setNodeAttribute(sPatp, 'color', colors.YOUR_STAR);
316 g.setNodeAttribute(gPatp, 'color', colors.YOUR_GAL);
322 color: itsYou ? colors.YOU : (planet.dominion == 'l2' ? colors.GREEN : colors.BLUE),
327 if (lastPlanetArm == arm) {
328 g.addEdge(pPatp, lastPlanetPatp, {
330 size: planetEdgeSize,
334 if (numPlanetArms == 1) {
335 g.addEdge(pPatp, sPatp, {
337 size: planetEdgeSize,
342 if (planet.sponsor) {
343 sponsorEdges.push([pPatp, planet.sponsor, {
345 size: planetEdgeSize,
350 lastPlanetPatp = pPatp;
352 let msg = `Couldn't add ${pPatp} to the graph`;
353 console.error(msg, e);
354 alertMe(msg + '\n' + e.message);
358 let msg = `Couldn't add ${sPatp} to the graph`;
359 console.error(msg, e);
360 alertMe(msg + '\n' + e.message);
364 sponsorEdges.forEach(([point, sponsor, attrs]) => {
365 // g.addEdge(point, sponsor, attrs);
368 setTimeout(callback, 0);
373function alertMe(msg: string) {
374 alert('Encountered an error, please tell ~midlev-mindyr: ' + msg);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list