PageSourceSearch

https://rxinfer.com/js/graph.js

js rxinfer.com collected 2026-10-03 22:11:27 UTC 23,027 bytes, 511 lines download raw bytes

1((function () {
2
3    // Javascript has no random choice function..........
4    function randomChoice(array) {
5        return array[Math.floor(array.length * Math.random())];
6    }
7
8    // This function register a 'shape' node, which is not really a node, but 
9    // a bezier curve attached to it (with some options to animate it)
10    function registerShapeNode() {
11
12        function drawBezierCurve(start, wide, height, middle, curve, center) {
13
14            // Move command
15            var startx = start.x;
16            var starty = start.y;
17
18            if (center) { startx = startx - wide / 2 }
19
20            M = `M ${startx} ${starty}`
21
22            // Curve command
23            var topx = startx + wide * middle;
24            var topy = starty - height;
25
26            var endx = startx + wide;
27            var endy = starty;
28
29            var ldiff = topx - startx;
30            var rdiff = startx + wide - topx;
31
32            // Left anchor point
33            var lanchorx = startx + ldiff * curve.left;
34            var lanchory = starty;
35
36            // Top left anchor point
37            var tlanchorx = topx - ldiff * curve.top;
38            var tlanchory = topy;
39
40            C = `C ${lanchorx} ${lanchory}, ${tlanchorx} ${tlanchory}, ${topx} ${topy}`
41
42            // Top right anchor point
43            var tranchorx = topx + rdiff * curve.top;
44            var tranchory = topy;
45
46            // Right anchor point
47            var ranchorx = endx - rdiff * curve.right;
48            var ranchory = endy;
49
50            S = `C ${tranchorx} ${tranchory}, ${ranchorx} ${ranchory}, ${endx} ${endy}`
51
52            // Finish the shape to the end
53            L = `L ${startx} ${starty}`
54
55            // I've spend more than 3 hours on this function
56            // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
57            return `${M} ${C} ${S} ${L}`
58        }
59
60        function generateRandomProperties() {
61            return {
62                base: { wide: 60, height: 40, middle: 0.5, curve: { left: 0.85, top: 0.20, right: 0.85, } },
63                wide: Math.random() * 40,
64                height: Math.random() * 40,
65                middle: Math.random() * 0.5,
66                curve: { left: 0.3, top: 0.1, right: 0.3 },
67                animation: randomChoice(['easeCubicInOut', 'easePolyInOut', 'easeQuadInOut'])
68            }
69        }
70
71        G6.registerNode('shape-node', {
72            drawShape: (cfg, group) => {
73                var color = cfg.color ? cfg.color : '#0aa8a7';
74                var props = generateRandomProperties()
75                var curve = {
76                    left: props.base.curve.left + props.curve.left / 2,
77                    top: props.base.curve.top - props.curve.top / 2,
78                    right: props.base.curve.right + props.curve.right / 2,
79                }
80
81                var path = group.addShape('path', {
82                    attrs: {
83                        fill: color,
84                        stroke: color,
85                        cursor: 'pointer',
86                        opacity: 0.0,
87                        path: drawBezierCurve({ x: 0, y: 0 }, props.base.wide, props.base.height + props.height, props.base.middle + props.middle * 1 / 2, curve, true),
88                    },
89                    name: 'path-shape',
90                });
91
92                var animationDelay = 1000 + Math.random() * 1000;
93
94                // In the beginning shapes are not visible
95                path.animate((ratio) => ({ opacity: 0.4 * ratio }), { repeat: false, duration: animationDelay, easing: props.animation })
96
97                path.animate((ratio) => {
98                    var bratio = Math.sin(ratio * Math.PI);
99                    var wide = props.base.wide + props.wide * bratio;
100                    var height = props.base.height + props.height * (1 - bratio);
101                    var middle = props.base.middle + props.middle * (1 / 2 - bratio);
102                    var cleft = props.base.curve.left + props.curve.left * (1 / 2 - bratio);
103                    var ctop = props.base.curve.top + props.curve.top * (bratio - 1 / 2);
104                    var cright = props.base.curve.right + props.curve.right * (1 / 2 - bratio);
105                    var curve = { left: cleft, top: ctop, right: cright };
106                    return {
107                        path: drawBezierCurve({ x: 0, y: 0 }, wide, height, middle, curve, true),
108                    }
109                }, { repeat: true, duration: 4000 + Math.random() * 1000, delay: animationDelay / 2, easing: props.animation })
110
111                return path
112            },
113            setState(name, value, item) {
114                var shape = item.get('keyShape');
115                if (name === 'hover') {
116                    shape.animate((ratio) => ({ opacity: 0.5 + 0.3 * (value ? ratio : (1 - ratio)) }), { repeat: false, duration: 300, easing: 'easeCubic' });
117                }
118            },
119        }, 'rect');
120    }
121
122    // This function register a simple fully transparent node, 
123    // that can be used to put labels or add dangling edges
124    function registerInvisibleNode() {
125        var invisibleNodeDefaultSize = 25;
126        G6.registerNode('invisible-node', {
127            options: { labelCfg: { style: { fill: '#0aa8a7' } } },
128            drawShape: (cfg, group) => {
129                var size = (cfg.size !== undefined) ? cfg.size : invisibleNodeDefaultSize;
130                return group.addShape('rect', {
131                    attrs: {
132                        x: -size / 2,
133                        y: -size / 2,
134                        width: size,
135                        height: size,
136                        fill: 'white',
137                        stroke: 'white',
138                        opacity: 0.0,
139                    },
140                    name: 'inode-box'
141                });
142            },
143            getAnchorPoints(cfg) {
144                return [[0.5, 0.5], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5], [0.5, 0.0]]
145            },
146        }, 'rect');
147    }
148
149    // This function register a simple generic factor node
150    function registerFactorNode() {
151        var factorNodeDefaultFillColor = '#f5f7f7';
152        var factorNodeDefaultStrokeColor = '#0aa8a7';
153        var factorNodeDefaultSize = 50;
154        var factorNodeDefaultShadowOffset = 10;
155        var factorNodeDefaultShadowBlur = 50;
156
157        G6.registerNode('factor-node', {
158            options: { labelCfg: { style: { opacity: 0.0, }, } },
159            drawShape: (cfg, group) => {
160                var size = (cfg.size !== undefined) ? cfg.size : factorNodeDefaultSize;
161                var shape = group.addShape('rect', {
162                    attrs: {
163                        x: -size / 2,
164                        y: -size / 2,
165                        width: size,
166                        height: size,
167                        fill: factorNodeDefaultFillColor,
168                        stroke: factorNodeDefaultStrokeColor,
169                        radius: 2,
170                        lineWidth: 1.5,
171                        cursor: 'pointer',
172                        opacity: 1.0,
173                        shadowColor: '#c2c2c2',
174                    },
175                    name: 'gnode-box',
176                });
177
178                group.addShape('text', {
179                    attrs: {
180                        y: 0,
181                        x: 0,
182                        textAlign: 'center',
183                        textBaseline: 'middle',
184                        lineHeight: 10,
185                        text: cfg.label,
186                        fill: factorNodeDefaultStrokeColor,
187                        fontSize: size / 2.5,
188                        fontWeight: 'bold'
189                    },
190                    name: 'gnode-type-text'
191                });
192
193                return shape
194            },
195            setState(name, value, item) {
196                var shape = item.get('keyShape');
197                var size = (shape.cfg.attrs.width !== undefined) ? shape.cfg.attrs.width : factorNodeDefaultSize;
198
199                if (name === 'hover') {
200                    shape.animate((r) => {
201                        var ratio = value ? r : (1 - r);
202                        var newsize = size + (size / 10) * ratio;
203                        return {
204                            height: newsize,
205                            width: newsize,
206                            x: -newsize / 2,
207                            y: -newsize / 2,
208                            shadowOffsetX: factorNodeDefaultShadowOffset * ratio,
209                            shadowOffsetY: factorNodeDefaultShadowOffset * ratio,
210                            shadowBlur: factorNodeDefaultShadowBlur * ratio,
211                        }
212                    }, { repeat: false, duration: 300, easing: 'easeCubic' });
213                }
214            },
215            getAnchorPoints(cfg) {
216                return [[0.5, 0.5], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5], [0.5, 0.0]]
217            },
218        }, 'rect');
219    }
220
221    // This function register a simple equality factor node
222    function registerEqualityNode() {
223        var equalityNodeDefaultFillColor = '#ffffff';
224        var equalityNodeDefaultSize = 20;
225        var equalityNodeDefaultStrokeColor = '#0aa8a7';
226        var equalityNodeDefaultLabelStrokeColor = '#0aa8a7';
227        G6.registerNode('equality-node', {
228            drawShape: (cfg, group) => {
229                var shape = group.addShape('rect', {
230                    attrs: {
231                        x: -equalityNodeDefaultSize / 2,
232                        y: -equalityNodeDefaultSize / 2,
233                        width: equalityNodeDefaultSize,
234                        height: equalityNodeDefaultSize,
235                        fill: equalityNodeDefaultFillColor,
236                        stroke: equalityNodeDefaultStrokeColor,
237                        radius: 3,
238                        lineWidth: 1.5,
239                        cursor: 'pointer',
240                        opacity: 0.7
241                    },
242                    name: 'gnode-box',
243                });
244
245                group.addShape('text', {
246                    attrs: {
247                        y: 0,
248                        x: 0,
249                        textAlign: 'center',
250                        textBaseline: 'middle',
251                        lineHeight: 10,
252                        text: "=",
253                        fill: equalityNodeDefaultLabelStrokeColor,
254                        fontSize: equalityNodeDefaultSize / 2.5,
255                        fontWeight: 'bold'
256                    },
257                    name: 'gnode-type-text'
258                });
259
260                return shape
261            },
262            setState(name, value, item) {
263                var shape = item.get('keyShape');
264                if (name === 'hover') {
265                    shape.animate((r) => {
266                        var ratio = value ? r : (1 - r);
267                        var newsize = equalityNodeDefaultSize + (equalityNodeDefaultSize / 10) * ratio;
268                        return { height: newsize, width: newsize, x: -newsize / 2, y: -newsize / 2 }
269                    }, { repeat: false, duration: 300, easing: 'easeCubic' });
270                }
271            },
272            getAnchorPoints(cfg) {
273                return [[0.5, 0.5], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5], [0.5, 0.0]]
274            },
275        }, 'rect');
276    }
277
278    function registerFactorEdge() {
279        var factorEdgeDefaultStrokeColor = '#0aa8a7';
280        var animationCircleFillColor = '#edf6f5';
281        var animationCircleStrokeColor = '#0aa8a7';
282        var animationCircleStrokeColors = ['#17a2b8', '#fd7e14', '#dc3545', '#6f42c1', '#007bff', '#28a745']
283        G6.registerEdge('factor-edge', {
284            options: { style: { stroke: factorEdgeDefaultStrokeColor, opacity: 0.7, lineWidth: 1.2 } },
285            afterDraw(cfg, group) {
286                var shape = group.get('children')[0];
287                var startPoint = shape.getPoint(0);
288                var stroke = animationCircleStrokeColor; // randomChoice(animationCircleStrokeColors);
289                var circle = group.addShape('circle', {
290                    attrs: {
291                        x: startPoint.x,
292                        y: startPoint.y,
293                        fill: animationCircleFillColor,
294                        stroke: stroke,
295                        lineWidth: 1.5,
296                        opacity: 0.7,
297                        r: 3,
298                    },
299                    name: 'circle-shape',
300                });
301
302                circle.animate((ratio) => {
303                    var tmpPoint = shape.getPoint(ratio);
304                    return { x: tmpPoint.x, y: tmpPoint.y, opacity: Math.sin(ratio * Math.PI), r: Math.sin(ratio * Math.PI) * 3 };
305                }, { repeat: true, duration: (500 + 1000 * Math.random()), });
306            },
307        }, 'line');
308    }
309
310    // Register custom nodes and edges
311    registerShapeNode();
312    registerInvisibleNode();
313    registerFactorNode();
314    registerEqualityNode();
315    registerFactorEdge();
316
317    var data = {
318        nodes: [
319            // Top shape nodes
320            { id: 'shape10', x: -110, y: 80, type: 'shape-node', color: '#17a2b8' },
321            // { id: 'shape11', x: -100, y: 70, type: 'invisible-node', label: 'x_t' },
322            { id: 'shape12', x: -90, y: 80, type: 'shape-node', color: '#fd7e14' },
323
324            { id: 'shape20', x: 90, y: 80, type: 'shape-node', color: '#dc3545' },
325            // { id: 'shape21', x: 100, y: 70, type: 'invisible-node', label: 'x_t' },
326            { id: 'shape22', x: 110, y: 80, type: 'shape-node', color: '#17a2b8' },
327
328            { id: 'shape30', x: 290, y: 80, type: 'shape-node', color: '#6f42c1' },
329            // { id: 'shape31', x: 300, y: 70, type: 'invisible-node', label: 'x_t' },
330            { id: 'shape32', x: 310, y: 80, type: 'shape-node', color: '#007bff' },
331
332            { id: 'shape40', x: 490, y: 80, type: 'shape-node', color: '#28a745' },
333            // { id: 'shape41', x: 500, y: 70, type: 'invisible-node', label: 'x_t' },
334            { id: 'shape42', x: 510, y: 80, type: 'shape-node', color: '#dc3545' },
335
336            // Bottom shape nodes
337            { id: 'shape50', x: -30, y: 270, type: 'shape-node', color: '#28a745' },
338            { id: 'shape51', x: 170, y: 270, type: 'shape-node', color: '#6f42c1' },
339            { id: 'shape52', x: 370, y: 270, type: 'shape-node', color: '#dc3545' },
340            { id: 'shape53', x: 570, y: 270, type: 'shape-node', color: '#007bff' },
341
342            // Top layer
343            { id: 'z-e00', x: -1000, y: 100, type: 'invisible-node' },
344            { id: 'z-e01', x: 1400, y: 100, type: 'invisible-node' },
345
346            { id: 'z-g0', x: -200, y: 100, type: 'factor-node', label: '𝒩' },
347            { id: 'z-g0-i', x: -200, y: 0, type: 'invisible-node' },
348
349            { id: 'z-g1', x: 0, y: 100, type: 'factor-node', label: '𝒩' },
350            { id: 'z-g1-i', x: 0, y: 0, type: 'invisible-node' },
351
352            { id: 'z-g2', x: 200, y: 100, type: 'factor-node', label: '𝒩' },
353            { id: 'z-g2-i', x: 200, y: 0, type: 'invisible-node' },
354
355            { id: 'z-g3', x: 400, y: 100, type: 'factor-node', label: '𝒩' },
356            { id: 'z-g3-i', x: 400, y: 0, type: 'invisible-node' },
357
358            { id: 'z-g4', x: 600, y: 100, type: 'factor-node', label: '𝒩' },
359            { id: 'z-g4-i', x: 600, y: 0, type: 'invisible-node' },
360
361            { id: 'z-e0', x: -100, y: 100, type: 'equality-node' },
362            { id: 'z-e1', x: 100, y: 100, type: 'equality-node' },
363            { id: 'z-e2', x: 300, y: 100, type: 'equality-node' },
364            { id: 'z-e3', x: 500, y: 100, type: 'equality-node' },
365
366            // Multiplication layer
367            { id: 'm-e00', x: -1000, y: 160, type: 'invisible-node' },
368            { id: 'm-e01', x: 1400, y: 160, type: 'invisible-node' },
369
370            { id: 'm-e0', x: -200, y: 160, type: 'equality-node' },
371            { id: 'm-e1', x: 30, y: 160, type: 'equality-node' },
372            { id: 'm-e2', x: 230, y: 160, type: 'equality-node' },
373            { id: 'm-e3', x: 430, y: 160, type: 'equality-node' },
374            { id: 'm-e4', x: 630, y: 160, type: 'equality-node' },
375
376            { id: 'm-g00', x: -1000, y: 290, type: 'invisible-node' },
377            { id: 'm-g01', x: 1400, y: 290, type: 'invisible-node' },
378            { id: 'm-g0', x: -200, y: 290, type: 'factor-node', label: '✕', size: 30 },
379            { id: 'm-g1', x: 30, y: 290, type: 'factor-node', label: '✕', size: 30 },
380            { id: 'm-g2', x: 230, y: 290, type: 'factor-node', label: '✕', size: 30 },
381            { id: 'm-g3', x: 430, y: 290, type: 'factor-node', label: '✕', size: 30 },
382            { id: 'm-g4', x: 630, y: 290, type: 'factor-node', label: '✕', size: 30 },
383
384            // Middle layer 
385            { id: 'x-g0', x: -100, y: 225, type: 'factor-node', label: 'f', size: 30 },
386            { id: 'x-g1', x: 100, y: 225, type: 'factor-node', label: 'f', size: 30 },
387            { id: 'x-g2', x: 300, y: 225, type: 'factor-node', label: 'f', size: 30 },
388            { id: 'x-g3', x: 500, y: 225, type: 'factor-node', label: 'f', size: 30 },
389
390            { id: 's-g0', x: -100, y: 290, type: 'factor-node', label: '𝒩' },
391            { id: 's-g1', x: 100, y: 290, type: 'factor-node', label: '𝒩' },
392            { id: 's-g2', x: 300, y: 290, type: 'factor-node', label: '𝒩' },
393            { id: 's-g3', x: 500, y: 290, type: 'factor-node', label: '𝒩' },
394
395            { id: 's-e0', x: -30, y: 290, type: 'equality-node' },
396            { id: 's-e1', x: 170, y: 290, type: 'equality-node' },
397            { id: 's-e2', x: 370, y: 290, type: 'equality-node' },
398            { id: 's-e3', x: 570, y: 290, type: 'equality-node' },
399
400            // Bottom layer
401            { id: 'b-i0', x: -30, y: 390, type: 'invisible-node' },
402            { id: 'b-i1', x: 170, y: 390, type: 'invisible-node' },
403            { id: 'b-i2', x: 370, y: 390, type: 'invisible-node' },
404            { id: 'b-i3', x: 570, y: 390, type: 'invisible-node' },
405
406
407        ],
408        edges: [
409
410
411            // Top layer
412            { source: 'z-e00', target: 'z-g0', type: 'factor-edge' },
413            { source: 'z-g4', target: 'z-e01', type: 'factor-edge' },
414            { source: 'z-g0-i', target: 'z-g0', type: 'factor-edge' },
415            { source: 'z-g0', target: 'z-e0', type: 'factor-edge' },
416            { source: 'z-e0', target: 'z-g1', type: 'factor-edge' },
417            { source: 'z-g1-i', target: 'z-g1', type: 'factor-edge' },
418            { source: 'z-g1', target: 'z-e1', type: 'factor-edge' },
419            { source: 'z-e1', target: 'z-g2', type: 'factor-edge' },
420            { source: 'z-g2-i', target: 'z-g2', type: 'factor-edge' },
421            { source: 'z-g2', target: 'z-e2', type: 'factor-edge' },
422            { source: 'z-e2', target: 'z-g3', type: 'factor-edge' },
423            { source: 'z-g3-i', target: 'z-g3', type: 'factor-edge' },
424            { source: 'z-g3', target: 'z-e3', type: 'factor-edge' },
425            { source: 'z-e3', target: 'z-g4', type: 'factor-edge' },
426            { source: 'z-g4-i', target: 'z-g4', type: 'factor-edge' },
427
428            // Multiplication layer
429            { source: 'm-e00', target: 'm-e0', type: 'factor-edge' },
430            { source: 'm-e4', target: 'm-e01', type: 'factor-edge' },
431            { source: 'm-e0', target: 'm-e1', type: 'factor-edge' },
432            { source: 'm-e1', target: 'm-e2', type: 'factor-edge' },
433            { source: 'm-e2', target: 'm-e3', type: 'factor-edge' },
434            { source: 'm-e3', target: 'm-e4', type: 'factor-edge' },
435            { source: 'm-e0', target: 'm-g0', type: 'factor-edge' },
436            { source: 'm-e1', target: 'm-g1', type: 'factor-edge' },
437            { source: 'm-e2', target: 'm-g2', type: 'factor-edge' },
438            { source: 'm-e3', target: 'm-g3', type: 'factor-edge' },
439            { source: 'm-e4', target: 'm-g4', type: 'factor-edge' },
440            { source: 'm-g00', target: 'm-g0', type: 'factor-edge' },
441            { source: 'm-g4', target: 'm-g01', type: 'factor-edge' },
442
443            { source: 'm-g0', target: 's-g0', type: 'factor-edge' },
444            { source: 's-g0', target: 's-e0', type: 'factor-edge' },
445            { source: 's-e0', target: 'm-g1', type: 'factor-edge' },
446
447            { source: 'm-g1', target: 's-g1', type: 'factor-edge' },
448            { source: 's-g1', target: 's-e1', type: 'factor-edge' },
449            { source: 's-e1', target: 'm-g2', type: 'factor-edge' },
450
451            { source: 'm-g2', target: 's-g2', type: 'factor-edge' },
452            { source: 's-g2', target: 's-e2', type: 'factor-edge' },
453            { source: 's-e2', target: 'm-g3', type: 'factor-edge' },
454
455            { source: 'm-g3', target: 's-g3', type: 'factor-edge' },
456            { source: 's-g3', target: 's-e3', type: 'factor-edge' },
457            { source: 's-e3', target: 'm-g4', type: 'factor-edge' },
458
459
460            // Middle layer
461            { source: 'z-e0', target: 'x-g0', type: 'factor-edge' },
462            { source: 'z-e1', target: 'x-g1', type: 'factor-edge' },
463            { source: 'z-e2', target: 'x-g2', type: 'factor-edge' },
464            { source: 'z-e3', target: 'x-g3', type: 'factor-edge' },
465
466            { source: 'x-g0', target: 's-g0', type: 'factor-edge' },
467            { source: 'x-g1', target: 's-g1', type: 'factor-edge' },
468            { source: 'x-g2', target: 's-g2', type: 'factor-edge' },
469            { source: 'x-g3', target: 's-g3', type: 'factor-edge' },
470
471            // Bottom layer
472            { source: 's-e0', target: 'b-i0', type: 'factor-edge' },
473            { source: 's-e1', target: 'b-i1', type: 'factor-edge' },
474            { source: 's-e2', target: 'b-i2', type: 'factor-edge' },
475            { source: 's-e3', target: 'b-i3', type: 'factor-edge' },
476        ],
477    };
478
479    var graph = new G6.Graph({ container: 'landing-graph', fitCenter: true });
480
481    window.addEventListener('resize', () => {
482        graph.changeSize(window.innerWidth, 600)
483        graph.fitCenter();
484        // On resize we zoom without animation, otherwise it looks weird
485        graph.zoom(1.25, { x: window.innerWidth / 2, y: 300 });
486    })
487
488    graph.on('node:mouseenter', (e) => graph.setItemState(e.item, 'hover', true));
489    graph.on('node:mouseleave', (e) => graph.setItemState(e.item, 'hover', false));
490
491    graph.data(data);
492    graph.render();
493
494    // Fade out the placeholder after graph is rendered
495    const placeholder = document.getElementById('landing-graph-placeholder');
496    if (placeholder) {
497        placeholder.style.transition = 'opacity 1s ease';
498        placeholder.style.opacity = '0';
499    }
500
501    // Fade in the graph after rendering
502    const graphElement = document.getElementById('landing-graph');
503    if (graphElement) {
504        graphElement.style.transition = 'opacity 1s ease';
505        graphElement.style.opacity = '1';
506    }
507
508    window.setTimeout(() => {
509        graph.zoom(1.5, { x: window.innerWidth / 2, y: 300 }, true, { duration: 2000, easing: 'easeCubic' });
510    }, 100)
511})())

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.