Commit 4e29d85f by Onlynagesha

init

parents
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
# c-w-im-visualization-ts
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
/// <reference types="vite/client" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "c-w-im-visualization-ts",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force"
},
"dependencies": {
"@antv/g2": "^5.1.20",
"d3": "latest",
"netv": "latest",
"vue": "^3.4.21"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/d3": "^7.4.3",
"@types/node": "^20.12.5",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.5.1",
"npm-run-all2": "^6.1.2",
"typescript": "~5.4.0",
"vite": "^5.2.8",
"vue-tsc": "^2.0.11"
}
}
This diff is collapsed. Click to expand it.
{
"fps": 60,
"nAnimatingNodesPerLink": 10,
"animatingNodeMaxSpeed": 1.25,
"nMovingFrames": 45,
"nWaitingFrames": 15,
"styles": {
"nodes": {
"seed": {
"shape": "circle",
"r": 7,
"fill": {
"r": 0.88,
"g": 0.222,
"b": 0.165,
"a": 0.9
},
"strokeWidth": 0
},
"visited": {
"shape": "circle",
"r": 6,
"fill": {
"r": 0.898,
"g": 0.607,
"b": 0.322,
"a": 0.9
},
"strokeWidth": 0
},
"animating": {
"shape": "circle",
"r": 3,
"fill": {
"r": 0.898,
"g": 0.749,
"b": 0.369,
"a": 0.75
},
"strokeWidth": 0
},
"default": {
"shape": "circle",
"r": 5,
"fill": {
"r": 0,
"g": 0.422,
"b": 0.622,
"a": 0.5
},
"strokeWidth": 0
}
},
"links": {
"animating": {
"strokeColor": {
"r": 0.75,
"g": 0.5,
"b": 0.25,
"a": 0.5
},
"strokeWidth": 2
},
"default": {
"strokeColor": {
"r": 0,
"g": 0,
"b": 0,
"a": 0.1
},
"strokeWidth": 2
}
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<template>
<!-- 3 canvas objects overlapped -->
<div ref="canvas">
<text class="title" :style="titleStyle">{{ title }}</text>
<div ref="graphCanvas" class="graph-canvas"></div>
<div ref="animatingCanvas" class="animating-canvas"></div>
</div>
</template>
<script lang="ts">
import NetV from 'netv/src';
import { defineComponent } from 'vue';
import {
createEmptyCanvas,
drawInitialGraphCanvas,
getProceedAnimationFrameFn,
initialCoarseningAnimationStates,
nodeLinkLimitOfCanvases,
} from '@/scripts/draw_canvas';
import type {
AnimationConfig,
DisplayAnimationStates,
DisplayData
} from '@/scripts/data_types';
interface CanvasData {
graphCanvas: NetV | null,
animatingCanvas: NetV | null,
intervalID: number | null,
states: DisplayAnimationStates | null,
}
export default defineComponent({
data(): CanvasData {
return {
graphCanvas: null,
animatingCanvas: null,
intervalID: null,
states: null
};
},
props: {
data: {
type: Object,
required: true
},
config: {
type: Object,
required: true
},
title: {
type: String,
default: "<No Name>"
},
titleStyle: {
type: Object,
default: { fontSize: '12px' }
},
algorithmKey: {
type: String,
default: 'cwim'
},
proceedingHandle: {
type: Function,
default: null
}
},
methods: {
proceed() {
if (this.states) {
this.states.manualStarts = true;
}
},
autoProceed(flag = true) {
if (this.states) {
this.states.autoStarts = flag;
}
},
initCanvas() {
const config = (this.config as AnimationConfig);
const limit = nodeLinkLimitOfCanvases(this.data as DisplayData, this.config as AnimationConfig);
// (1) Graph canvas: bottommost
const graphCanvas = createEmptyCanvas((this.$refs.graphCanvas as HTMLDivElement), limit.graphCanvas);
drawInitialGraphCanvas(graphCanvas, this.data as DisplayData, config);
this.graphCanvas = graphCanvas;
// (2) Animating canvas: in the middle, animation effects to display the simulation process
this.animatingCanvas = createEmptyCanvas((this.$refs.animatingCanvas as HTMLDivElement), limit.animatingCanvas);
// Registers loop
this.states = initialCoarseningAnimationStates(config);
const proceedingFn = getProceedAnimationFrameFn(this.algorithmKey);
const proceedingLoopFn = () => proceedingFn(
this.graphCanvas,
this.animatingCanvas,
this.data as DisplayData,
this.states as DisplayAnimationStates
);
if (this.proceedingHandle) {
this.proceedingHandle(proceedingLoopFn);
} else {
this.intervalID = setInterval(proceedingLoopFn, 1000.0 / (config).fps);
}
},
deinitCanvas() {
if (this.intervalID) {
clearInterval(this.intervalID);
}
for (let canvas of [this.graphCanvas, this.animatingCanvas]) {
if (canvas) {
(canvas as NetV).dispose();
}
}
},
},
mounted() {
this.initCanvas();
},
unmounted() {
this.deinitCanvas();
},
watch: {
data() {
this.deinitCanvas();
this.states = initialCoarseningAnimationStates(this.config as AnimationConfig);
this.initCanvas();
}
}
});
</script>
<style scoped>
.title {
position: absolute;
margin: 5px;
z-index: 0;
}
.graph-canvas {
width: inherit;
height: inherit;
position: absolute;
z-index: 1;
}
.animating-canvas {
width: inherit;
height: inherit;
position: absolute;
z-index: 2;
}
</style>
<template>
<div>
<div ref="timeUsageChart" class="chart"></div>
<div ref="influenceChart" class="chart"></div>
</div>
</template>
<script lang="ts">
import { Chart } from '@antv/g2';
import { defineComponent } from 'vue';
import type { ChartsData, ChartsDataItemKey, ChartsDataKey } from '@/scripts/data_types';
interface ChartsComponentData {
data: ChartsData,
timeUsageChart: Chart | null,
influenceChart: Chart | null
}
export default defineComponent({
data(): ChartsComponentData {
return {
data: {
nNodes: { cwim: 0, contrast: 0 },
timeUsage: { cwim: 0, contrast: 0 },
influence: { cwim: 0, contrast: 0 }
},
timeUsageChart: null,
influenceChart: null
};
},
methods: {
toChartData(key: ChartsDataKey): {x: string, y: number}[] {
const res = [
{ x: 'main', y: this.data[key].cwim },
{ x: 'contrast', y: this.data[key].contrast }
];
return res;
},
updateData(key: ChartsDataKey, group: ChartsDataItemKey, value: number) {
this.data[key][group] = value;
},
reRender(key?: ChartsDataKey) {
if (key === 'timeUsage' || key === undefined || key === null) {
this.timeUsageChart?.changeData(this.toChartData('timeUsage'));
}
if (key === 'influence' || key === undefined || key === null) {
this.influenceChart?.changeData(this.toChartData('influence'));
}
}
},
mounted() {
const timeUsageChart = new Chart({
container: this.$refs.timeUsageChart as HTMLDivElement,
autoFit: true,
axis: {
y: { title: "影响最大化算法耗时(越短越好)", tickCount: 5 },
x: { title: '' }
}
});
timeUsageChart.interval()
.coordinate({ transform: [{ type: 'transpose' }] })
.data(this.toChartData('timeUsage'))
.encode('x', 'x')
.encode('y', 'y')
.style('fill', '#ff4444')
this.timeUsageChart = timeUsageChart;
const influenceChart = new Chart({
container: this.$refs.influenceChart as HTMLDivElement,
autoFit: true,
axis: {
y: { title: "信息传播节点个数(越多越好)", tickCount: 5 },
x: { title: '' }
}
});
influenceChart.interval()
.coordinate({ transform: [{ type: 'transpose' }] })
.data(this.toChartData('influence'))
.encode('x', 'x')
.encode('y', 'y');
this.influenceChart = influenceChart;
for (let chart of [timeUsageChart, influenceChart]) {
chart.render();
}
}
});
</script>
<style scoped>
.chart {
float: left;
width: inherit;
height: 50%;
}
</style>
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
import type { DisplayData, DisplayJSONData, DisplayNodeData, IMAlgorithmKey } from "./data_types";
import * as d3 from 'd3';
export function parseCoarseningData(obj: DisplayJSONData): DisplayData {
// With preprocessing
const res: DisplayData = {
...obj,
graphs: obj.graphs.map((G) => {
const nodeMap = new Map<string, DisplayNodeData>()
G.nodes.forEach((node) => {
nodeMap.set(node.id, node);
});
return { nodes: nodeMap, links: G.links };
}),
seeds: {
cwim: obj.seeds.cwim.map((seeds) => new Set(seeds)),
contrast: new Set(obj.seeds.contrast),
},
};
return res;
}
function nSeeds(seeds: Array<string> | Set<string>) {
if (seeds instanceof Array) {
return seeds.length;
} else if (seeds instanceof Set) {
return seeds.size;
} else {
throw TypeError("Invalid seeds data type.");
}
}
export function nTotalVisitedNodes(data: DisplayJSONData | DisplayData, key: IMAlgorithmKey) {
const simData = data.simulation[key];
const nVisited = d3.sum(simData.map((level) => level.length));
if (key === 'cwim') {
const seeds = data.seeds.cwim[0];
return nVisited + nSeeds(seeds);
} else if (key === 'contrast') {
const seeds = data.seeds.contrast;
return nVisited + nSeeds(seeds);
} else {
throw RangeError(`Invalid algorithm key '${key}'.`);
}
}
import type NetV from "netv/src"
import type { LinkData, LinkStyle, NodeData, NodeStyle } from "netv/src/interfaces"
export interface DisplayNodeData extends NodeData {
/** (Required) x-coordinate in range [0, 1]. */
x: number,
/** (Required) y-coordinate in range [0, 1]. */
y: number,
/**
* Node ID in the next level to which current node is coarsened.
* Not present in the final level.
*/
to?: string,
}
export interface DisplayLevelJSONData {
/** All the nodes in the current level. */
nodes: Array<DisplayNodeData>,
/** All the links in the current level. */
links: Array<LinkData>
}
export interface DisplayLevelData {
/** All the nodes in the current level. Finds node by ID. */
nodes: Map<string, DisplayNodeData>,
/** All the links in the current level. */
links: Array<LinkData>
}
export type IMAlgorithmKey = 'cwim' | 'contrast';
interface DisplayDataBase<
LevelData extends DisplayLevelJSONData | DisplayLevelData,
SeedContainer extends Array<string> | Set<string>,
> {
/** Graphs in each level. */
graphs: Array<LevelData>,
/** Seed nodes from different algorithms. */
seeds: {
/**
* Seed nodes obtained by C-w-IM algorithm in each level.
* It shall be guaranteed in prior that each seeds in the (i + 1)-th layer
* is coarsened from some seed in the i-th layer.
*/
cwim: Array<SeedContainer>,
/** Seeds nodes obtained by the contrast IM algorithm. */
contrast: SeedContainer,
},
/**
* Process of information propagation from seeds by discrete steps.
* simulation[t] represents propagation actions (u -> v) in the t-th iteration.
*/
simulation: {
cwim: Array<Array<LinkData>>,
contrast: Array<Array<LinkData>>,
},
/** Time usage of different algorithms */
timeUsed: {
cwim: number,
contrast: number,
},
/**
* Number of nodes in the original graph (other than the displayed).
* Due to the limitation of rendering performance, the graph size displayed is much smaller than the actual.
*/
nNodesRaw: number,
/**
* Number of nodes in the original graph (other than the displayed).
* Due to the limitation of rendering performance, the graph size displayed is much smaller than the actual.
*/
nLinksRaw: number,
}
/** Display data obtained from JSON input. */
export interface DisplayJSONData
extends DisplayDataBase<DisplayLevelJSONData, Array<string>> {}
/** Display data after preprocessing. */
export interface DisplayData
extends DisplayDataBase<DisplayLevelData, Set<string>> {}
export interface AnimationConfig {
/** Maximum FPS of the animation. */
fps: number,
/**
* # of animating nodes per link during simulation, integer value in range [1, +inf). Denoted as k.
* Let u -> v be a link with information propagation,
* there will be k nodes moving from u to v with differing speed in the animation.
*/
nAnimatingNodesPerLink: number,
/**
* Maximum speed of animating nodes, value in range [1, +inf). Denoted as lambda.
* For each link u -> v during animation, the i-th animation node (i = 1 ... k, k >= 2) requires
* Fm / Speed(i) frames to reach the destination v, where Speed(i) = (i - 1) / (k - 1) * (lambda - 1) + 1.
* If k == 1, then Speed(0) = 1.0.
*/
animatingNodeMaxSpeed: number,
/** # of frames for moving animation in each section, denoted as Fm. */
nMovingFrames: number,
/** # of waiting frames between two moving sections. Denoted as Fw. */
nWaitingFrames: number,
styles: {
nodes: {
/** Style of seed nodes. */
seed: NodeStyle,
/** Style of the visited nodes. */
visited: NodeStyle,
/** Style of the animation nodes. */
animating: NodeStyle,
/** Style of all other nodes. */
default?: NodeStyle,
},
links: {
/** Style of the animating links. */
animating: LinkStyle,
/** Style of all the other links. */
default: LinkStyle,
},
},
}
export type AnimationPhase =
'INITIAL' | 'WAITING' | 'COARSENING' | 'SHOWING_SEEDS' | 'EXPANDING' | 'SIMULATING';
export interface DisplayAnimationStates {
/** See above */
config: AnimationConfig,
/** Whether auto play is enabled. */
autoStarts: boolean,
/** Whether manual play is triggered for the next section. */
manualStarts: boolean,
/** Current phase of animation. */
phase: AnimationPhase,
/**
* For coarsening and expanding phase, step = current graph level.
* For simulating phase, step = current iteration index.
*/
step: number,
/** Used in simulating phase. # of iterations whose destination nodes are displayed as visited. */
nVisitedSteps: number,
/** Frame index of current animation section. */
frame: number,
}
export interface ChartsDataItem {
cwim: number,
contrast: number
}
export interface ChartsData {
nNodes: ChartsDataItem,
timeUsage: ChartsDataItem,
influence: ChartsDataItem
}
export type ChartsDataItemKey = keyof ChartsDataItem;
export type ChartsDataKey = keyof ChartsData;
\ No newline at end of file
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment