Compare commits

...

2 Commits

Author SHA1 Message Date
veypi
042333ed4b eda演示完成 2021-01-11 11:50:18 +08:00
veypi
215cb69313 init 2021-01-09 11:49:23 +08:00
31 changed files with 9887 additions and 1222 deletions

3
eda/edaf/.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

5
eda/edaf/.editorconfig Normal file
View File

@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

35
eda/edaf/.eslintrc.js Normal file
View File

@ -0,0 +1,35 @@
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'@vue/standard',
'@vue/typescript/recommended'
],
parserOptions: {
ecmaVersion: 2020
},
rules: {
'space-before-function-paren': 'off',
'no-callback-literal': 0,
'arrow-parens': 0,
// allow async-await
'no-unused-vars': 0,
'generator-star-spacing': 'off',
'spaced-comment': 0,
'object-curly-spacing': 0,
'@typescript-eslint/no-empty-function': 0,
'@typescript-eslint/no-this-alias': 0,
'@typescript-eslint/no-var-requires': 0,
'prefer-const': 0,
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
camelcase: 0,
// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-undef-init': 0,
'no-useless-call': 0,
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}

26
eda/edaf/.gitignore vendored
View File

@ -1,5 +1,23 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
eda/edaf/README.md Normal file
View File

@ -0,0 +1,24 @@
# edaf
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
eda/edaf/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@ -1,13 +0,0 @@
<!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.js"></script>
</body>
</html>

View File

@ -1,15 +1,46 @@
{
"name": "edaf",
"version": "0.0.0",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"vue": "^3.0.4"
"@antv/g6": "^4.1.1",
"core-js": "^3.6.5",
"d3-force": "^2.1.1",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vuetify": "^2.4.2",
"vuex": "^3.4.0"
},
"devDependencies": {
"vite": "^1.0.0-rc.13",
"@vue/compiler-sfc": "^3.0.4"
"@types/d3-force": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/eslint-config-typescript": "^5.0.2",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.2.2",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"sass": "^1.19.0",
"sass-loader": "^8.0.0",
"typescript": "~3.9.3",
"vue-cli-plugin-vuetify": "~2.0.9",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.3.0"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -1,15 +1,270 @@
<style>
.info-div {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
}
</style>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3.0 + Vite" />
<v-app>
<v-app-bar
app
color="primary"
dark
dense
>
<v-app-bar-nav-icon>
<v-avatar size="20">
<img
src="/favicon.ico"
alt="veypi"
>
</v-avatar>
</v-app-bar-nav-icon>
<v-toolbar-title>
{{ $store.state.algorithms[$store.state.algorithm].text }}
</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
<v-icon class="mr-5" @click="info=!info">mdi-information</v-icon>
<v-icon class="mr-5" @click="sync">mdi-autorenew</v-icon>
<v-btn color="warning" class="mr-5" v-if="$store.state.algorithm===0" @click="$refs.core.lay2()">计算</v-btn>
<v-icon @click="dialog=!dialog">mdi-cog</v-icon>
</v-app-bar>
<v-main>
<v-system-bar>
<v-row class="text-center">
<v-col>边数: {{ $store.state.edgesNum }}</v-col>
<v-col>节点数: {{ $store.state.nodesNum }}</v-col>
</v-row>
</v-system-bar>
<router-view ref="core" style="height: 100%;width: 100%"></router-view>
</v-main>
<div class="info-div" v-if="info">
<v-data-table
class="mx-auto"
:headers="headers"
:items="nodeList"
:search="search"
disable-sort
dense
height="300px"
style="background: rgba(0,0,0,0.2);"
>
<template v-slot:item.x="{ item }">
{{ item.x.toFixed(2) }}
</template>
<template v-slot:item.y="{ item }">
{{ item.y.toFixed(2) }}
</template>
<template v-slot:item.act="{ item }">
<v-btn v-if="item.color === freeC " color="warning" @click="lock(item)">固定</v-btn>
<v-btn v-else color="primary" @click="unlock(item)">解除</v-btn>
</template>
</v-data-table>
</div>
<v-dialog
v-model="dialog"
transition="dialog-bottom-transition"
max-width="600"
>
<v-card>
<v-system-bar
color="success"
dark
>
设置
</v-system-bar>
<v-card-text class="mt-10">
<v-form>
<v-select v-model="$store.state.algorithm" :items="$store.state.algorithms" label="算法"></v-select>
<v-slider
label="节点数量"
class="mt-10"
v-model="$store.state.nodesNum"
thumb-label="always"
:max="$store.state.algorithm ? 100: 10"
:min="5"
></v-slider>
<v-slider
:max="$store.state.nodesNum * 5"
:min="$store.state.nodesNum * (1 + $store.state.algorithm) - 1" label="边数量" class="mt-10"
v-model="$store.state.edgesNum" thumb-label="always"></v-slider>
</v-form>
</v-card-text>
</v-card>
</v-dialog>
</v-app>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
<script lang="ts">
import Vue from 'vue'
export default {
name: 'App',
components: {
HelloWorld
function uniform2NormalDistribution() {
let sum = 0.0
for (let i = 0; i < 12; i++) {
sum = sum + Math.random(1)
}
return sum - 6.0
}
function getNumberInNormalDistribution(mean, stdDev) {
return mean + (uniform2NormalDistribution() * stdDev)
}
export default Vue.extend({
name: 'App',
components: {},
data: () => ({
freeC: '#5cf43d',
search: '',
headers: [
{
text: '节点id',
align: 'start',
width: '60px',
value: 'id'
},
{text: 'x', value: 'x', width: 60},
{text: 'y', value: 'y', width: 60},
{text: 'act', value: 'act'}
],
dialog: false,
info: false
//
}),
computed: {
nodeList() {
return Object.keys(this.$store.state.nodes).map(v => {
return this.$store.state.nodes[v]
})
}
},
methods: {
lock(n) {
n.color = 'red'
n.fx = n.x
n.fy = n.y
const graph = this.$refs.core.graph
graph.updateItem(n.id, {color: n.color})
},
unlock(n) {
n.color = this.freeC
n.fx = null
n.fy = null
this.$refs.core.graph.updateItem(n.id, {color: n.color})
},
sync() {
const edges = {}
const nodes = {}
for (let i = 0; i < this.$store.state.nodesNum; i++) {
nodes['n' + i] = {
label: 'n' + i,
id: 'n' + i,
//
color: this.freeC,
x: i * Math.random() * 100,
y: i * Math.random() * 100,
size: Math.floor(Math.random() * 20) + 5
}
}
// nodes.n0.color = 'red'
// nodes.n0.fx = 100
// nodes.n0.fy = 175
// nodes.n0.x = 100
// nodes.n0.y = 175
// nodes.n1.color = 'red'
// nodes.n1.fx = 200
// nodes.n1.fy = 225
// nodes.n1.x = 200
// nodes.n1.y = 225
// edges['0-2'] = {
// id: '0-2',
// source: 'n0',
// target: 'n2',
// value: 1
// }
// edges['2-3'] = {
// id: '2-3',
// source: 'n2',
// target: 'n3',
// value: 1
// }
// edges['3-4'] = {
// id: '3-4',
// source: 'n3',
// target: 'n4',
// value: 1
// }
// edges['4-1'] = {
// id: '4-1',
// source: 'n4',
// target: 'n1',
// value: 1
// }
let iter = 1
for (let i = 0; i < this.$store.state.nodesNum; i++) {
if (Object.keys(edges).length >= this.$store.state.edgesNum) {
this.$store.state.edges = edges
this.$store.state.nodes = nodes
this.$refs.core.loaddata()
return
}
let j = i
while (i === j) {
j = Math.floor(Math.random() * this.$store.state.nodesNum)
}
let ni = i
if (i > j) {
ni = j
j = i
}
const v = Math.abs(Math.floor(getNumberInNormalDistribution(4, 20)))
edges[ni + '-' + j] = {
id: ni + '-' + j,
source: 'n' + ni,
target: 'n' + j,
value: v
}
}
while (iter < 10) {
iter = iter + 1
for (let i = 0; i < this.$store.state.nodesNum; i++) {
for (let j = i + 1; j < this.$store.state.nodesNum; j++) {
// const v = Math.floor(Math.random() * 10)
const v = Math.floor(getNumberInNormalDistribution(4, 20))
if (v > 0) {
const id = i + '-' + j
edges[id] = {
id: id,
source: 'n' + i,
target: 'n' + j,
value: v
// label: v
}
if (Object.keys(edges).length >= this.$store.state.edgesNum) {
this.$store.state.edges = edges
this.$store.state.nodes = nodes
this.$refs.core.loaddata()
return
}
}
}
}
}
}
},
mounted() {
this.sync()
}
})
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,19 +1,153 @@
<template>
<h1>{{ msg }}</h1>
<button @click="count++">count is: {{ count }}</button>
<p>Edit <code>components/HelloWorld.vue</code> to test hot module replacement.</p>
<v-container>
<v-row class="text-center">
<v-col cols="12">
<v-img
:src="require('../assets/logo.svg')"
class="my-3"
contain
height="200"
/>
</v-col>
<v-col class="mb-4">
<h1 class="display-2 font-weight-bold mb-3">
Welcome to Vuetify
</h1>
<p class="subheading font-weight-regular">
For help and collaboration with other Vuetify developers,
<br>please join our online
<a
href="https://community.vuetifyjs.com"
target="_blank"
>Discord Community</a>
</p>
</v-col>
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
What's next?
</h2>
<v-row justify="center">
<a
v-for="(next, i) in whatsNext"
:key="i"
:href="next.href"
class="subheading mx-3"
target="_blank"
>
{{ next.text }}
</a>
</v-row>
</v-col>
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
Important Links
</h2>
<v-row justify="center">
<a
v-for="(link, i) in importantLinks"
:key="i"
:href="link.href"
class="subheading mx-3"
target="_blank"
>
{{ link.text }}
</a>
</v-row>
</v-col>
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
Ecosystem
</h2>
<v-row justify="center">
<a
v-for="(eco, i) in ecosystem"
:key="i"
:href="eco.href"
class="subheading mx-3"
target="_blank"
>
{{ eco.text }}
</a>
</v-row>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
count: 0
}
}
}
data: () => ({
ecosystem: [
{
text: 'vuetify-loader',
href: 'https://github.com/vuetifyjs/vuetify-loader'
},
{
text: 'github',
href: 'https://github.com/vuetifyjs/vuetify'
},
{
text: 'awesome-vuetify',
href: 'https://github.com/vuetifyjs/awesome-vuetify'
}
],
importantLinks: [
{
text: 'Documentation',
href: 'https://vuetifyjs.com'
},
{
text: 'Chat',
href: 'https://community.vuetifyjs.com'
},
{
text: 'Made with Vuetify',
href: 'https://madewithvuejs.com/vuetify'
},
{
text: 'Twitter',
href: 'https://twitter.com/vuetifyjs'
},
{
text: 'Articles',
href: 'https://medium.com/vuetify'
}
],
whatsNext: [
{
text: 'Explore components',
href: 'https://vuetifyjs.com/components/api-explorer'
},
{
text: 'Select a layout',
href: 'https://vuetifyjs.com/getting-started/pre-made-layouts'
},
{
text: 'Frequently Asked Questions',
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions'
}
]
})
})
</script>

189
eda/edaf/src/g6/force.ts Normal file
View File

@ -0,0 +1,189 @@
const d3Force = require('d3-force')
const forceLayout = {
tick: () => {
},
center: [0, 0],
nodeStrength: null,
edgeStrength: null,
preventOverlap: false,
nodeSize: undefined,
nodeSpacing: undefined,
linkDistance: 50,
forceSimulation: null,
alphaDecay: 0.028,
alphaMin: 0.001,
alpha: 0.3,
collideStrength: 1,
onLayoutEnd: function () {
},
getDefaultCfg() {
return {
center: [0, 0],
nodeStrength: null,
edgeStrength: null,
preventOverlap: false,
nodeSize: undefined,
nodeSpacing: undefined,
linkDistance: 50,
forceSimulation: null,
alphaDecay: 0.028,
alphaMin: 0.001,
alpha: 0.3,
collideStrength: 1,
onLayoutEnd: function () {
}
}
},
/**
*
* @param {Object} data
*/
init(data) {
console.log('init')
const self = this
self.nodes = data.nodes || []
const edges = data.edges || []
self.edges = edges.map(function (edge) {
const res = {}
const expectKeys = ['targetNode', 'sourceNode', 'startPoint', 'endPoint']
Object.keys(edge).forEach(function (key) {
if (!(expectKeys.indexOf(key) > -1)) {
res[key] = edge[key]
}
})
return res
})
self.ticking = false
},
/**
*
*/
execute(reloadData: any) {
console.log('execute')
const self = this
const nodes = self.nodes
const edges = self.edges
// 如果正在布局,忽略布局请求
if (self.ticking) {
return
}
let simulation = self.forceSimulation
const alphaMin = self.alphaMin
const alphaDecay = self.alphaDecay
const alpha = self.alpha
if (!simulation) {
try {
// 定义节点的力
const nodeForce = d3Force.forceManyBody()
if (self.nodeStrength) {
nodeForce.strength(self.nodeStrength)
}
simulation = d3Force.forceSimulation().nodes(nodes)
simulation
.force('center', d3Force.forceCenter(self.center[0], self.center[1]))
.force('charge', nodeForce)
.alpha(alpha)
.alphaDecay(alphaDecay)
.alphaMin(alphaMin)
if (self.preventOverlap) {
self.overlapProcess(simulation)
}
// 如果有边,定义边的力
if (edges) {
// d3 的 forceLayout 会重新生成边的数据模型,为了避免污染源数据
const edgeForce = d3Force
.forceLink()
.id(function (d) {
return d.id
})
.links(edges)
if (self.edgeStrength) {
edgeForce.strength(self.edgeStrength)
}
if (self.linkDistance) {
edgeForce.distance(self.linkDistance)
}
self.edgeForce = edgeForce
simulation.force('link', edgeForce)
}
simulation
.on('tick', function () {
self.tick()
})
.on('end', function () {
self.ticking = false
if (self.onLayoutEnd) {
self.onLayoutEnd()
}
})
self.ticking = true
self.forceSimulation = simulation
self.ticking = true
} catch (e) {
self.ticking = false
console.warn(e)
}
} else {
if (reloadData) {
simulation.nodes(nodes)
if (edges && self.edgeForce) {
self.edgeForce.links(edges)
} else if (edges && !self.edgeForce) {
// d3 的 forceLayout 会重新生成边的数据模型,为了避免污染源数据
const edgeForce = d3Force
.forceLink()
.id(function (d) {
return d.id
})
.links(edges)
if (self.edgeStrength) {
edgeForce.strength(self.edgeStrength)
}
if (self.linkDistance) {
edgeForce.distance(self.linkDistance)
}
self.edgeForce = edgeForce
simulation.force('link', edgeForce)
}
}
if (self.preventOverlap) {
self.overlapProcess(simulation)
}
simulation.alpha(alpha).restart()
this.ticking = true
}
// TODO
},
/**
*
* @param {Object} data
*/
layout(data) {
console.log('layout')
this.init(data)
this.execute()
},
/**
*
* @param {Object} cfg
*/
updateCfg(cfg) {
console.log('update cfg')
const self = this
if (self.ticking) {
self.forceSimulation.stop()
self.ticking = false
}
self.forceSimulation = null
Object.assign(self, cfg)
},
/**
*
*/
destroy() {
console.log('destroy')
}
}
export default forceLayout

6
eda/edaf/src/g6/index.ts Normal file
View File

@ -0,0 +1,6 @@
import G6 from '@antv/g6'
import forceLayout from '@/g6/force'
G6.registerLayout('forceLayout', forceLayout)
export default G6

View File

@ -1,8 +0,0 @@
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}

133
eda/edaf/src/libs/index.js Normal file
View File

@ -0,0 +1,133 @@
function multiply(a, b) {
// 相乘约束
if (a[0].length !== b.length) {
throw new Error()
}
let m = a.length
let p = a[0].length
let n = b[0].length
// 初始化 m*n 全 0 二维数组
let c = new Array(m).fill(0).map(arr => new Array(n).fill(0))
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
for (let k = 0; k < p; k++) {
c[i][j] += a[i][k] * b[k][j]
}
}
}
return c
}
function det(square) {
// 方阵约束
if (square.length !== square[0].length) {
throw new Error()
}
// 方阵阶数
let n = square.length
let result = 0
if (n > 3) {
// n 阶
for (let column = 0; column < n; column++) {
// 去掉第 0 行第 column 列的矩阵
let matrix = new Array(n - 1).fill(0).map(arr => new Array(n - 1).fill(0))
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - 1; j++) {
if (j < column) {
matrix[i][j] = square[i + 1][j]
} else {
matrix[i][j] = square[i + 1][j + 1]
}
}
}
result += square[0][column] * Math.pow(-1, 0 + column) * det(matrix)
}
} else if (n === 3) {
// 3 阶
result = square[0][0] * square[1][1] * square[2][2] +
square[0][1] * square[1][2] * square[2][0] +
square[0][2] * square[1][0] * square[2][1] -
square[0][2] * square[1][1] * square[2][0] -
square[0][1] * square[1][0] * square[2][2] -
square[0][0] * square[1][2] * square[2][1]
} else if (n === 2) {
// 2 阶
result = square[0][0] * square[1][1] - square[0][1] * square[1][0]
} else if (n === 1) {
// 1 阶
result = square[0][0]
}
return result
}
function transpose(matrix) {
let result = new Array(matrix.length).fill(0).map(arr => new Array(matrix[0].length).fill(0))
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result[0].length; j++) {
result[i][j] = matrix[j][i]
}
}
return result
}
function adjoint(square) {
// 方阵约束
if (square[0].length !== square.length) {
throw new Error()
}
let n = square.length
let result = new Array(n).fill(0).map(arr => new Array(n).fill(0))
for (let row = 0; row < n; row++) {
for (let column = 0; column < n; column++) {
// 去掉第 row 行第 column 列的矩阵
let matrix = []
for (let i = 0; i < square.length; i++) {
if (i !== row) {
let arr = []
for (let j = 0; j < square.length; j++) {
if (j !== column) {
arr.push(square[i][j])
}
}
matrix.push(arr)
}
}
result[row][column] = Math.pow(-1, row + column) * det(matrix)
}
}
return transpose(result)
}
function inv(square) {
if (square[0].length !== square.length) {
throw new Error()
}
let detValue = det(square)
let result = adjoint(square)
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result.length; j++) {
result[i][j] /= detValue
}
}
return result
}
function copyM(m, xs, xe, ys, ye) {
let n = []
for (let i = xs; i < xe; i++) {
n.push([])
for (let j = ys; j < ye; j++) {
n[i - xs].push(m[i][j])
}
}
return n
}
export { inv, multiply, copyM }

View File

@ -1,5 +0,0 @@
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
createApp(App).mount('#app')

14
eda/edaf/src/main.ts Normal file
View File

@ -0,0 +1,14 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify'
Vue.config.productionTip = false
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')

View File

@ -0,0 +1,7 @@
import Vue from 'vue'
import Vuetify from 'vuetify/lib/framework'
Vue.use(Vuetify)
export default new Vuetify({
})

View File

@ -0,0 +1,27 @@
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
routes
})
export default router

13
eda/edaf/src/shims-tsx.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}

4
eda/edaf/src/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

4
eda/edaf/src/shims-vuetify.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module 'vuetify/lib/framework' {
import Vuetify from 'vuetify'
export default Vuetify
}

View File

@ -0,0 +1,21 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
algorithm: 1,
algorithms: [
{value: 0, text: '二次线长布局'},
{value: 1, text: '力矢量布局'}
],
nodes: [],
edges: {},
nodesNum: 5,
edgesNum: 4
},
mutations: {},
actions: {},
modules: {}
})

View File

@ -0,0 +1,5 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

173
eda/edaf/src/views/Home.vue Normal file
View File

@ -0,0 +1,173 @@
<template>
<div class="home" id="container">
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import G6 from './../g6'
import {multiply, inv, copyM} from '@/libs'
const modes = {
default: ['drag-canvas', 'zoom-canvas', 'brush-select']
}
function refreshDragedNodePosition(e: any) {
const model = e.item.get('model')
model.x = e.x
model.y = e.y
}
export default Vue.extend({
name: 'Home',
components: {},
data() {
return {
graph: G6.Graph,
cfg: {
container: 'container',
modes: modes,
defaultNode: {
size: 20
}
}
}
},
computed: {
mode() {
return this.$store.state.algorithm
},
nodes() {
return Object.keys(this.$store.state.nodes).map(v => {
return this.$store.state.nodes[v]
})
},
edges() {
return Object.keys(this.$store.state.edges).map(v => this.$store.state.edges[v])
}
},
watch: {
mode() {
this.graph.destroy()
this.init()
}
},
methods: {
lay2() {
const p = []
const l = []
let fnum = 0
this.nodes.forEach(v => {
l.push([])
this.nodes.forEach(() => {
l[l.length - 1].push(0)
})
if (v.color === 'red') {
fnum = fnum + 1
p.push([v.x, v.y])
}
})
if (fnum === 0) {
alert('请固定至少一个点')
return
}
this.nodes.forEach(v => {
if (v.color !== 'red') {
p.push([0, 0])
}
})
this.edges.forEach(v => {
const s = Number(v.source.slice(1))
const e = Number(v.target.slice(1))
l[s][e] = 1
l[e][s] = 1
})
const ones = [l.map(e => 1)]
const onesL = multiply(ones, l)
const B = multiply(l, p)
const A = []
for (let i = 0; i < p.length; i++) {
A.push([])
for (let j = 0; j < p.length; j++) {
A[i].push(0)
if (i === j) {
A[i][j] = onesL[0][i]
}
A[i][j] = A[i][j] - l[i][j]
}
}
const nA = copyM(A, fnum, A.length, fnum, A.length)
const nB = copyM(B, fnum, A.length, 0, 2)
const res = multiply(inv(nA), nB)
let index = 0
console.log(res)
this.nodes.forEach(v => {
if (v.color !== 'red') {
v.x = res[index][0]
v.y = res[index][1]
this.graph.updateItem(v.id, {x: v.x, y: v.y})
index = index + 1
}
})
},
init() {
const container = document.getElementById('container')
this.cfg.width = container.scrollWidth
this.cfg.height = container.scrollHeight || 500
this.cfg.layout = this.mode === 1 ? {
type: 'force',
center: [this.cfg.width / 2, this.cfg.height / 2],
preventOverlap: true,
onTick: (e) => {
},
onLayoutEnd: () => {
},
workerEnabled: false
} : undefined
const graph = new G6.Graph(this.cfg)
this.graph = graph
this.loaddata()
if (this.mode !== 1) {
graph.on('node:drag', e => {
if (e.item?.getModel().color === 'red') {
return
}
refreshDragedNodePosition(e)
graph.refresh()
})
return
}
const forceLayout = graph.get('layoutController').layoutMethod
const start = (e) => {
graph.layout()
refreshDragedNodePosition(e)
}
const move = (e) => {
forceLayout.execute()
refreshDragedNodePosition(e)
}
const end = (e) => {
// e.item.get('model').fx = null
// e.item.get('model').fy = null
}
graph.on('node:dragstart', start)
graph.on('node:drag', move)
graph.on('node:dragend', end)
graph.on('node:touchstart', start)
graph.on('node:touchmove', move)
graph.on('node:touchend', end)
},
loaddata() {
this.graph.data({
nodes: this.nodes,
edges: this.edges
})
this.graph.render()
// this.graph.fitView(20)
}
},
mounted() {
this.init()
}
})
</script>

39
eda/edaf/tsconfig.json Normal file
View File

@ -0,0 +1,39 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

5
eda/edaf/vue.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
transpileDependencies: [
'vuetify'
]
}

File diff suppressed because it is too large Load Diff