diff --git a/apps/dsv/package.json b/apps/dsv/package.json index 1efc00f..6560f26 100644 --- a/apps/dsv/package.json +++ b/apps/dsv/package.json @@ -12,6 +12,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "parser": "workspace:*", + "data-structure": "workspace:*", + "schema": "workspace:*", "process": "^0.11.10", "assert": "^2.1.0", "path-browserify": "^1.0.1", diff --git a/apps/dsv/src/App.tsx b/apps/dsv/src/App.tsx index 3c607b1..0a044ee 100644 --- a/apps/dsv/src/App.tsx +++ b/apps/dsv/src/App.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { run } from 'parser'; import { Chart } from './Chart'; +import { StructureType } from 'schema'; function App() { const [code, setCode] = useState(` @@ -76,844 +77,18 @@ console.log(JSON.parse(JSON.stringify(arr3))) = (props) => { const { schema } = props; @@ -18,64 +20,31 @@ export const Chart: FC = (props) => { return; } - let top = 10; - let left = 10; - const characters = schema.structures.map( - (structure: any, index: number) => { - return { - type: 'VChart', - id: structure.id, - zIndex: 1, - position: { - top: top + 200 * index, - left: left, - width: 580, - height: 190, - }, - options: { - // 图表的背景板配置 - panel: { - fill: '#ffffff', - shadowColor: 'rgba(0, 0, 0, 0.05)', - shadowBlur: 10, - shadowOffsetX: 4, - shadowOffsetY: 4, - cornerRadius: 8, - }, - spec: { - data: [ - { - id: 'barData', - values: structure.array.map((y, x) => { - return { - x: y, - y: 1, - }; - }), - }, - ], - axes: [ - { - orient: 'bottom', - type: 'band', - }, - { - orient: 'bottom', - type: 'linear', - visible: false, - }, - ], - type: 'bar', - xField: 'x', - yField: 'y', - }, - }, - }; + const interval = 1000; + const arrayBar = new ArrayBar([...schema.structures[0].array], { + id: schema.structures[0].id, + interval: interval, + structure: { + position: { + top: 10, + left: 10, + width: 580, + height: 300, + }, }, - ); + }); + + schema.actions.forEach((action) => { + if (action.structureId === arrayBar.id) { + if (action.type === 'set') { + arrayBar.set(action.args[0], action.args[1]); + } + } + }); + // 生成一个DSL,该DSL只包含一个VChart元素 const dsl = { - characters: characters, + characters: [arrayBar.structure], // 图表的具体动画编排 acts: [ // 幕数组,一个故事可以包含多个幕,幕与幕之间是有先后顺序的串联结构 @@ -87,40 +56,15 @@ export const Chart: FC = (props) => { id: 'scene0', // 场景中包含的动作数组,动作中描述了一个或多个character的具体行为,一个场景中可以包含多个动作,动作之间是并行执行的 actions: [ - { - characterId: 'array-fad29a', - characterActions: [ - { - action: 'appear', - payload: { - animation: { - duration: 1000, - }, - }, - }, - ], - }, - { - characterId: 'array-7d8dc9', - characterActions: [ - { - action: 'appear', - payload: { - animation: { - duration: 1000, - }, - }, - }, - ], - }, { characterId: 'array-94718b', characterActions: [ { action: 'appear', + startTime: 0, payload: { animation: { - duration: 1000, + duration: interval, }, }, }, @@ -128,11 +72,17 @@ export const Chart: FC = (props) => { }, ], }, + { + id: 'scene1', + actions: arrayBar.actions, + }, ], }, ], }; + console.log(dsl); + const story = new VStory.Story(dsl, { dom: ref.current, background: 'pink', diff --git a/docs/CodeStructure.md b/docs/CodeStructure.md new file mode 100644 index 0000000..bb8ae20 --- /dev/null +++ b/docs/CodeStructure.md @@ -0,0 +1,13 @@ +. +├── apps +│   └── dsv +├── docs +│   └── Structure.md +├── packages +│   ├── parser 用户代码解析器 +│   └── schema 过程的统一描述结构: 算法执行过程 +├── package.json +├── pnpm-lock.yaml +└── pnpm-workspace.yaml + +7 directories, 4 files diff --git a/docs/LogicStructure.md b/docs/LogicStructure.md new file mode 100644 index 0000000..8c3acca --- /dev/null +++ b/docs/LogicStructure.md @@ -0,0 +1,24 @@ +# 逻辑结构 + +Step1: 用户输入代码 + +Step2: 解析代码, + +Step3: 转译代码 + +Step4: 执行代码, 生成schema + +Step5: 基于schema, 模拟执行DataStructure, 生成DSL + +Step6: 渲染算法可视化 + +## 逻辑顺序 + +Code => Schema => DataStructure => VStory => 可视化 + +## 简要描述 + +Code: 用户输入的代码 +Schema: 算法执行过程统一描述语言, 支持多种语言生成同一个JSON描述的Schema +DataStructure: 数据结构, 接收一个Schema, 输出一个DSL; 允许脱离Schema, 直接调用DataStructure生成DSL +VStory: 可视化故事 diff --git a/docs/Structure.md b/docs/Structure.md deleted file mode 100644 index fe7a133..0000000 --- a/docs/Structure.md +++ /dev/null @@ -1,13 +0,0 @@ -# Structure - -## 1、准备冒泡排序代码 - -## 2、创建Babel处理代码 - -### 2.1、插件捕获数组操作 - -### 2.2、创建代理数组 - -## 3、运行新代码, 获取Schema - -## 4、模拟执行Schema diff --git a/package.json b/package.json index 310955c..f0c85ec 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "dev": "concurrently \"pnpm --filter dsv dev\" \"pnpm --filter=schema dev\" \"pnpm --filter=parser dev\"", + "dev": "concurrently \"pnpm --filter dsv dev\" \"pnpm --filter=./packages/* dev\" ", "build": "pnpm -r build", "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/packages/data-structure/package.json b/packages/data-structure/package.json new file mode 100644 index 0000000..993e48a --- /dev/null +++ b/packages/data-structure/package.json @@ -0,0 +1,25 @@ +{ + "name": "data-structure", + "version": "0.0.0", + "description": "", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "dev": "tsc --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@visactor/vstory": "0.0.23", + "@babel/core": "^7.26.7", + "@babel/types": "^7.26.7", + "schema": "workspace:*" + }, + "devDependencies": { + "typescript": "^5.0.0", + "@types/babel__core": "^7.0.0", + "@types/node": "^20.0.0" + } +} diff --git a/packages/data-structure/src/algorithm/algorithm.ts b/packages/data-structure/src/algorithm/algorithm.ts new file mode 100644 index 0000000..efd691b --- /dev/null +++ b/packages/data-structure/src/algorithm/algorithm.ts @@ -0,0 +1,9 @@ +import { IStoryDSL } from "@visactor/vstory"; +import type { SchemaAction } from "schema"; + +export class Algorithm { + private acts: IStoryDSL["acts"] = []; + private characters: IStoryDSL["characters"] = []; + + constructor(private array: number[]) {} +} diff --git a/packages/data-structure/src/algorithm/index.ts b/packages/data-structure/src/algorithm/index.ts new file mode 100644 index 0000000..6d91e1b --- /dev/null +++ b/packages/data-structure/src/algorithm/index.ts @@ -0,0 +1 @@ +export { Algorithm } from "./algorithm"; diff --git a/packages/data-structure/src/data-structures/array-bar/array-bar.ts b/packages/data-structure/src/data-structures/array-bar/array-bar.ts new file mode 100644 index 0000000..f900293 --- /dev/null +++ b/packages/data-structure/src/data-structures/array-bar/array-bar.ts @@ -0,0 +1,185 @@ +import { + IActions, + IActionSpec, + ICharacterConfig, + IStoryDSL, +} from "@visactor/vstory"; + +interface ArrayBarOptions { + id: string; + interval?: number; + structure?: Partial; +} + +export class ArrayBar { + private _data: T[]; + private _id: string; + private _dataId: string; + private _interval: number; + + private _actions: IActions[] = []; + private _structure: ICharacterConfig; + + constructor(data: T[], options: ArrayBarOptions) { + this._data = data; + + const { id, interval = 1000 } = options; + this._id = id; + this._dataId = `array-bar-${id}`; + this._interval = interval; + + this._structure = Object.assign( + {}, + { + id: id, + type: "VChart", + zIndex: 1, + position: { + top: 200, + left: 200, + width: 580, + height: 190, + }, + options: { + // 图表的背景板配置 + panel: { + fill: "#ffffff", + shadowColor: "rgba(0, 0, 0, 0.05)", + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8, + }, + spec: { + data: [ + { + id: this._dataId, + values: this._data.map((value, index) => { + return { + key: `${value}-${index}`, + x: `${value}`, + y: value, + }; + }), + }, + ], + dataKey: "key", + axes: [ + { + orient: "bottom", + type: "band", + }, + { + orient: "bottom", + type: "linear", + visible: false, + }, + ], + type: "bar", + xField: "x", + yField: "y", + }, + }, + }, + options.structure + ); + } + + set(index: number, value: T) { + const id = this._id; + const dataId = this._dataId; + const interval = this._interval; + const length = this._actions.length; + const data = this._data; + + this._data[index] = value; + + const action = { + action: "update", + startTime: interval * (length + 1), + payload: { + id: dataId, + animation: { + duration: interval, + }, + values: data.map((value) => { + return { + x: `${value}`, + y: value, + }; + }), + }, + } as IActionSpec; + + this._actions.push({ + characterId: id, + characterActions: [action], + }); + + return this; + } + + concat(array: T[]) { + this._data = this._data.concat(array); + const id = this._id; + const dataId = this._dataId; + const interval = this._interval; + const length = this._actions.length; + + const action = { + action: "update", + startTime: interval * length, + payload: { + id: dataId, + values: this._data.map((value, index) => { + return { + x: `${value}`, + y: value, + }; + }), + }, + } as IActionSpec; + + this._actions.push({ + characterId: id, + characterActions: [action], + }); + return this; + } + + get(index: number) { + const value = this._data[index]; + const action = { + action: "highlight", + startTime: this._interval * this._actions.length, + payload: { + id: this._dataId, + value: value, + animation: { + duration: 1000, + easing: "linear", + }, + style: { + fill: "red", + }, + }, + } as IActionSpec; + + this._actions.push({ + characterId: this._id, + characterActions: [action], + }); + } + + get actions() { + return this._actions; + } + + get structure() { + return this._structure; + } + + get id() { + return this._id; + } +} diff --git a/packages/data-structure/src/data-structures/array-bar/index.ts b/packages/data-structure/src/data-structures/array-bar/index.ts new file mode 100644 index 0000000..a0ce54e --- /dev/null +++ b/packages/data-structure/src/data-structures/array-bar/index.ts @@ -0,0 +1 @@ +export { ArrayBar } from "./array-bar"; diff --git a/packages/data-structure/src/data-structures/index.ts b/packages/data-structure/src/data-structures/index.ts new file mode 100644 index 0000000..a0ce54e --- /dev/null +++ b/packages/data-structure/src/data-structures/index.ts @@ -0,0 +1 @@ +export { ArrayBar } from "./array-bar"; diff --git a/packages/data-structure/src/index.ts b/packages/data-structure/src/index.ts new file mode 100644 index 0000000..e8ca83b --- /dev/null +++ b/packages/data-structure/src/index.ts @@ -0,0 +1,2 @@ +export { Algorithm } from "./algorithm"; +export { ArrayBar } from "./data-structures"; diff --git a/packages/data-structure/tsconfig.json b/packages/data-structure/tsconfig.json new file mode 100644 index 0000000..b0f9d90 --- /dev/null +++ b/packages/data-structure/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "CommonJS", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/parser/src/data-structures/array/array-proxy.ts b/packages/parser/src/data-structures/array/array-proxy.ts index 558ff8e..68c80fc 100644 --- a/packages/parser/src/data-structures/array/array-proxy.ts +++ b/packages/parser/src/data-structures/array/array-proxy.ts @@ -16,13 +16,14 @@ export class ArrayProxy { const { snapshotSchema, schema, SchemaBuilder, uuid } = options; this.structureId = uuid("array"); this.options = options; + debugger snapshotSchema( new SchemaBuilder() .from(schema) .addStructure({ id: this.structureId, type: StructureType.Array, - array, + array: [...array], }) .build() ); @@ -44,11 +45,13 @@ export class ArrayProxy { return this._applyHandler(fn, target, args, method); }; } - return this._getHandler(target, prop); + return this._getHandler(target, prop as unknown as number); }, - set: (target, prop, value) => this._setHandler(target, prop, value), + set: (target, prop, value) => + this._setHandler(target, prop as unknown as number, value), }); - return proxy; + + return proxy as unknown as ArrayProxy; } _getHandler(target: any[], prop: number) { diff --git a/packages/schema/src/builder/index.ts b/packages/schema/src/builder/index.ts index 7803f6a..c49cfcc 100644 --- a/packages/schema/src/builder/index.ts +++ b/packages/schema/src/builder/index.ts @@ -1,7 +1,7 @@ -import { Action, Schema, Structure } from "../types"; +import { SchemaAction, Schema, Structure } from "../types"; export class SchemaBuilder { - private actions: Action[] = []; + private actions: SchemaAction[] = []; private structures: Structure[] = []; constructor() {} @@ -23,7 +23,7 @@ export class SchemaBuilder { return this; } - addAction(action: Action) { + addAction(action: SchemaAction) { this.actions.push(action); return this; } diff --git a/packages/schema/src/types/index.ts b/packages/schema/src/types/index.ts index 97b9f59..927e094 100644 --- a/packages/schema/src/types/index.ts +++ b/packages/schema/src/types/index.ts @@ -1,6 +1 @@ -export { - Schema, - Structure, - Action, - StructureType, -} from "./schema"; +export { Schema, Structure, SchemaAction, StructureType } from "./schema"; diff --git a/packages/schema/src/types/schema.ts b/packages/schema/src/types/schema.ts index b5ae0b6..d8ac800 100644 --- a/packages/schema/src/types/schema.ts +++ b/packages/schema/src/types/schema.ts @@ -9,7 +9,7 @@ export interface Schema { /** * @description The actions in the schema */ - actions: Action[]; + actions: SchemaAction[]; } export interface Structure { @@ -22,7 +22,7 @@ export enum StructureType { Array = "array", } -export interface Action { +export interface SchemaAction { structureId: string; name: string; type: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 204402d..dd92281 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: buffer: specifier: ^6.0.3 version: 6.0.3 + data-structure: + specifier: workspace:* + version: link:../../packages/data-structure parser: specifier: workspace:* version: link:../../packages/parser @@ -38,6 +41,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.3.1(react@18.3.1) + schema: + specifier: workspace:* + version: link:../../packages/schema util: specifier: ^0.12.5 version: 0.12.5 @@ -73,6 +79,31 @@ importers: specifier: ^5.7.2 version: 5.7.3 + packages/data-structure: + dependencies: + '@babel/core': + specifier: ^7.26.7 + version: 7.26.7 + '@babel/types': + specifier: ^7.26.7 + version: 7.26.7 + '@visactor/vstory': + specifier: 0.0.23 + version: 0.0.23 + schema: + specifier: workspace:* + version: link:../schema + devDependencies: + '@types/babel__core': + specifier: ^7.0.0 + version: 7.20.5 + '@types/node': + specifier: ^20.0.0 + version: 20.17.17 + typescript: + specifier: ^5.0.0 + version: 5.7.3 + packages/parser: dependencies: '@babel/core':