diff --git a/drumming/README.md b/drumming/README.md
new file mode 100644
index 0000000..065a003
--- /dev/null
+++ b/drumming/README.md
@@ -0,0 +1,21 @@
+# pointgaming
+### 1、项目说明
+总体思路:数独程序项目开发的总体思路如图1.1示意,拟实现头部区域,主体区域,右边的内容展示和操作区域等。
+ 图1.1 项目开发的总体思路图
+
+
+
+### 2、开发环境说明
+(1)系统版本:OpenHarmony 4.0 Release
+(2)SDK版本:API 10
+(3)Model类型:Stage
+(4)DevEco Studio版本:Deveco Studio 4.0 release
+(5)硬件环境:Unionpi Whale、RK3566开发板
+(6)预览器参数:(1)DeviceType:default;(2)Resolution:560-1280;(3)DPI:240;其余参数自定。
+### 3、项目主要函数调用关系图与项目输出界面演进图
+ 图3.1 项目主要函数调用关系图
+
+
+
+ 图3.2 项目输出界面演进图
+
\ No newline at end of file
diff --git a/drumming/drummer/.gitignore b/drumming/drummer/.gitignore
new file mode 100644
index 0000000..fbabf77
--- /dev/null
+++ b/drumming/drummer/.gitignore
@@ -0,0 +1,11 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
\ No newline at end of file
diff --git a/drumming/drummer/AppScope/app.json5 b/drumming/drummer/AppScope/app.json5
new file mode 100644
index 0000000..3c45fe7
--- /dev/null
+++ b/drumming/drummer/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "com.test.myapplication",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:app_icon",
+ "label": "$string:app_name"
+ }
+}
diff --git a/drumming/drummer/AppScope/resources/base/element/string.json b/drumming/drummer/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000..7c49b04
--- /dev/null
+++ b/drumming/drummer/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "drawpart3"
+ }
+ ]
+}
diff --git a/drumming/drummer/AppScope/resources/base/media/app_icon.png b/drumming/drummer/AppScope/resources/base/media/app_icon.png
new file mode 100644
index 0000000..cd45acc
Binary files /dev/null and b/drumming/drummer/AppScope/resources/base/media/app_icon.png differ
diff --git a/drumming/drummer/build-profile.json5 b/drumming/drummer/build-profile.json5
new file mode 100644
index 0000000..f1e6a10
--- /dev/null
+++ b/drumming/drummer/build-profile.json5
@@ -0,0 +1,49 @@
+{
+ "app": {
+ "signingConfigs": [
+ {
+ "name": "default",
+ "material": {
+ "certpath": "C:\\Users\\cyf20\\.ohos\\config\\openharmony\\auto_ohos_default_drawpart3_com.test.myapplication.cer",
+ "storePassword": "0000001B112EEEE42E9C65C70B8BE139E9746FD3A593BEC334955CFDC40C11FEB89314E23E845E58F31D6E",
+ "keyAlias": "debugKey",
+ "keyPassword": "0000001BACB92E1829CB96E1D19A1D0FD4AF75EC50D07DAACF62C3FB0B8D5EF8E82E944DC0FD8F397E8901",
+ "profile": "C:\\Users\\cyf20\\.ohos\\config\\openharmony\\auto_ohos_default_drawpart3_com.test.myapplication.p7b",
+ "signAlg": "SHA256withECDSA",
+ "storeFile": "C:\\Users\\cyf20\\.ohos\\config\\openharmony\\auto_ohos_default_drawpart3_com.test.myapplication.p12"
+ }
+ }
+ ],
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "compileSdkVersion": 10,
+ "compatibleSdkVersion": 10,
+ "runtimeOS": "OpenHarmony",
+ }
+ ],
+ "buildModeSet": [
+ {
+ "name": "debug",
+ },
+ {
+ "name": "release"
+ }
+ ]
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/.gitignore b/drumming/drummer/entry/.gitignore
new file mode 100644
index 0000000..e2713a2
--- /dev/null
+++ b/drumming/drummer/entry/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/drumming/drummer/entry/build-profile.json5 b/drumming/drummer/entry/build-profile.json5
new file mode 100644
index 0000000..197e83c
--- /dev/null
+++ b/drumming/drummer/entry/build-profile.json5
@@ -0,0 +1,31 @@
+{
+ "apiType": "stageMode",
+ "buildOption": {
+ "arkOptions": {
+ // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
+ }
+ },
+ "buildOptionSet": [
+ {
+ "name": "release",
+ "arkOptions": {
+ "obfuscation": {
+ "ruleOptions": {
+ "enable": true,
+ "files": [
+ "./obfuscation-rules.txt"
+ ]
+ }
+ }
+ }
+ },
+ ],
+ "targets": [
+ {
+ "name": "default"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/hvigorfile.ts b/drumming/drummer/entry/hvigorfile.ts
new file mode 100644
index 0000000..c6edcd9
--- /dev/null
+++ b/drumming/drummer/entry/hvigorfile.ts
@@ -0,0 +1,6 @@
+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/drumming/drummer/entry/obfuscation-rules.txt b/drumming/drummer/entry/obfuscation-rules.txt
new file mode 100644
index 0000000..985b2ae
--- /dev/null
+++ b/drumming/drummer/entry/obfuscation-rules.txt
@@ -0,0 +1,18 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
\ No newline at end of file
diff --git a/drumming/drummer/entry/oh-package.json5 b/drumming/drummer/entry/oh-package.json5
new file mode 100644
index 0000000..225946c
--- /dev/null
+++ b/drumming/drummer/entry/oh-package.json5
@@ -0,0 +1,10 @@
+{
+ "license": "",
+ "devDependencies": {},
+ "author": "",
+ "name": "entry",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "version": "1.0.0",
+ "dependencies": {}
+}
diff --git a/drumming/drummer/entry/src/main/ets/common/CommonConstant.ets b/drumming/drummer/entry/src/main/ets/common/CommonConstant.ets
new file mode 100644
index 0000000..bf8cfa3
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/CommonConstant.ets
@@ -0,0 +1,38 @@
+/**
+ * 字体大小(大)
+ */
+export const BIG_SIZE_FONT = 20
+/**
+ * 字体大小(小)
+ */
+export const SMALL_SIZE_FONT = 15
+
+//以da yu200屏幕为标准 imageShow组件的左上角和右下角的坐标
+//经过测试在whale RK3588上影响不大
+export const TOP_LEFT_X = 0
+export const TOP_LEFT_Y = 38
+export const BOTTOM_RIGHT_X = 681
+export const BOTTOM_RIGHT_Y = 373
+
+//定义绘制节点的大小(vp)
+export const DRAW_NODE_SIZE = 40
+
+let setting:RenderingContextSettings = new RenderingContextSettings(true)
+//绘制未选择/一般情况
+export const NORMAL_CONTEXT: CanvasRenderingContext2D = new CanvasRenderingContext2D(setting)
+//绘制选中
+export const SELECTED_CONTEXT: CanvasRenderingContext2D = new CanvasRenderingContext2D(setting)
+
+export const X_CENTER = (BOTTOM_RIGHT_X - TOP_LEFT_X) / 2 - TOP_LEFT_X
+export const Y_CENTER = (BOTTOM_RIGHT_Y - TOP_LEFT_Y) / 2 - TOP_LEFT_Y
+
+//TODO KEY 用户需要自行更改
+export const KEY = "14a9551d9cdbf6e2ce12768de807a5fe"
+
+/**
+ * 程序会默认使用这些预定的城市作为参数调用API以获取数据 然后将其写入文件
+ */
+//模拟的城市
+export const CITY = ["广州市","深圳市","珠海市","汕头市","佛山市","韶关市","湛江市","肇庆市","江门市","茂名市","惠州市","梅州市","汕尾市"]
+//,"汕头市","佛山市","韶关市","湛江市","肇庆市","江门市","茂名市","惠州市","梅州市","汕尾市"
+
diff --git a/drumming/drummer/entry/src/main/ets/common/customDialog/SetNodeCount.ets b/drumming/drummer/entry/src/main/ets/common/customDialog/SetNodeCount.ets
new file mode 100644
index 0000000..973f954
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/customDialog/SetNodeCount.ets
@@ -0,0 +1,56 @@
+import { cp } from '../../utils/algorithm/customPath'
+import { showToast } from '../../utils/functions/showToast'
+import Node from '../dataStruct/class/Node'
+
+@CustomDialog
+export struct setNodeCount{
+ controller:CustomDialogController
+ @State count:number = 0
+ @Link Nodes:Array
+ @Consume refresh:boolean
+ @Consume isStartToPath:boolean
+ @Consume nodePathId:Array
+ build(){
+ Column({space:5}){
+ Row(){
+ Text('设置个数:')
+ TextInput()
+ .layoutWeight(1)
+ .onChange((value)=>{
+ this.count = Number(value)
+ })
+ }.width('100%')
+ Row(){
+ Button('确定')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.isStartToPath = false
+ this.nodePathId = []
+ if(this.count <= 1 || this.count >= 14){
+ showToast("节点数只能位于2至13之间")
+ return
+ }
+ this.Nodes = cp.createNodeData(this.count)
+ this.refresh = !this.refresh
+ this.controller.close()
+ })
+ Button('取消')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.controller.close()
+ })
+ }.width('100%').justifyContent(FlexAlign.SpaceAround)
+ }.width('100%').padding(5)
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drumming/drummer/entry/src/main/ets/common/customDialog/SetNodeNameDialog.ets b/drumming/drummer/entry/src/main/ets/common/customDialog/SetNodeNameDialog.ets
new file mode 100644
index 0000000..02e8c87
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/customDialog/SetNodeNameDialog.ets
@@ -0,0 +1,33 @@
+import Node from '../dataStruct/class/Node'
+
+@CustomDialog
+export struct setNodeNameDialog{
+ controller:CustomDialogController
+ @ObjectLink node:Node
+ @State inputName:string = ""
+ build(){
+ Column({space:5}){
+ Row(){
+ Text('设置名称:')
+ TextInput()
+ .layoutWeight(1)
+ .onChange((value)=>{
+ this.inputName = value
+ })
+ }.width('100%')
+ Row(){
+ Button('确定')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.node.label = this.inputName
+ this.controller.close()
+ })
+ Button('取消')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.controller.close()
+ })
+ }.width('100%').justifyContent(FlexAlign.SpaceAround)
+ }.width('100%').padding(5)
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Data.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Data.ets
new file mode 100644
index 0000000..1e62267
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Data.ets
@@ -0,0 +1,16 @@
+import { RouteData } from './RouteData'
+
+export class Data{
+ status:string
+ info:string
+ count:string
+ route:RouteData
+
+ constructor(status: string, info: string, count: string, route: RouteData) {
+ this.status = status
+ this.info = info
+ this.count = count
+ this.route = route
+ }
+
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Edge.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Edge.ets
new file mode 100644
index 0000000..b78cc8a
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Edge.ets
@@ -0,0 +1,21 @@
+export default class Edge {
+ id: number; // “连接对象编号”:连接线唯一标识,通常是自然数编号
+ node1: number; // “节点对象1编号”
+ node2: number; // “节点对象2编号”:连接线两端节点的编号
+ visible: boolean; // “连接是否显示”:表示该连接是否存在
+ marked: boolean; // ”连接是否标记“:该连接是否被标记
+ label: string; // ”连接标签“:该连接的名字
+ distance: number ; // ”连接的距离/成本“
+ time: number; // ”连接的时间“
+
+ constructor(id: number, node1: number, node2: number, visible: boolean, marked: boolean, label: string, distance: number, time: number) {
+ this.id = id;
+ this.node1 = node1;
+ this.node2 = node2;
+ this.visible = visible;
+ this.marked = marked;
+ this.label = label;
+ this.distance = distance;
+ this.time = time;
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Node.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Node.ets
new file mode 100644
index 0000000..8aa7075
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/Node.ets
@@ -0,0 +1,16 @@
+@Observed
+export default class Node {
+ id: number; // “节点编号”:唯一标识一个节点,通常是自然数编号
+ label: string; // “节点标签”:节点名称(如城市名)
+ marked: boolean; // “节点是否标记”:节点是否被标记
+ coordinates: [number, number]; // “节点坐标”:节点坐标组
+ mustVisit: boolean; // “是否为必经节点”:表示在遍历时是否必经该节点,默认为必经节点
+
+ constructor(id: number, label: string, marked: boolean, coordinates: [number, number], mustVisit: boolean = true) {
+ this.id = id;
+ this.label = label;
+ this.marked = marked;
+ this.coordinates = coordinates;
+ this.mustVisit = mustVisit;
+ }
+}
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/class/PathData.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/PathData.ets
new file mode 100644
index 0000000..3aa76f2
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/PathData.ets
@@ -0,0 +1,28 @@
+import { StepData } from './StepData'
+
+export class PathData{
+ path:string
+ distance:string
+ duration:string
+ strategy:string
+ tolls:string
+ restriction:string
+ traffic_lights:string
+ toll_distance:string
+ steps:Array
+
+ constructor(path: string, distance: string, duration: string, strategy: string, tolls: string, restriction: string,
+ traffic_lights: string, toll_distance: string, steps: Array) {
+ this.path = path
+ this.distance = distance
+ this.duration = duration
+ this.strategy = strategy
+ this.tolls = tolls
+ this.restriction = restriction
+ this.traffic_lights = traffic_lights
+ this.toll_distance = toll_distance
+ this.steps = steps
+ }
+
+
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/class/RouteData.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/RouteData.ets
new file mode 100644
index 0000000..80b3524
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/RouteData.ets
@@ -0,0 +1,16 @@
+import { PathData } from './PathData'
+
+export class RouteData{
+ origin:string
+ destination:string
+ taxi_cost:string
+ paths:Array
+
+ constructor(origin: string, destination: string, taxi_cost: string, paths: Array) {
+ this.origin = origin
+ this.destination = destination
+ this.taxi_cost = taxi_cost
+ this.paths = paths
+ }
+
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/class/StepData.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/StepData.ets
new file mode 100644
index 0000000..084abff
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/class/StepData.ets
@@ -0,0 +1,8 @@
+export class StepData{
+ step:string
+
+ constructor(step: string) {
+ this.step = step
+ }
+
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/CityInfo.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/CityInfo.ets
new file mode 100644
index 0000000..8fd1694
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/CityInfo.ets
@@ -0,0 +1,6 @@
+interface urbanTransitionCost {
+ distance: number,
+ time: number
+}
+
+export type CityInfo = Record>;
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/Graph.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/Graph.ets
new file mode 100644
index 0000000..cc16c5d
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/Graph.ets
@@ -0,0 +1,6 @@
+export interface nodeInfo {
+ to: number,
+ weight: number
+}
+
+export type Graph = Record;
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/SolutionData.ets b/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/SolutionData.ets
new file mode 100644
index 0000000..31400eb
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/common/dataStruct/interface/SolutionData.ets
@@ -0,0 +1,6 @@
+export interface solutionData{
+ count?:number
+ time:string
+ path:string
+ otherPath:Array
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/component/Header.ets b/drumming/drummer/entry/src/main/ets/component/Header.ets
new file mode 100644
index 0000000..3762381
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/component/Header.ets
@@ -0,0 +1,194 @@
+import { SMALL_SIZE_FONT } from '../common/CommonConstant'
+import { setNodeCount } from '../common/customDialog/SetNodeCount'
+import Node from '../common/dataStruct/class/Node'
+import { solutionData } from '../common/dataStruct/interface/SolutionData'
+import { cp } from '../utils/algorithm/customPath'
+import { tsp } from '../utils/algorithm/TSP'
+import { showToast } from '../utils/functions/showToast'
+
+@Component
+export struct header {
+ @Link resTime:number
+ @Link resDistance:number
+
+ @Prop headerContent:Array
+ @Link curHeaderContentIndex:number
+
+ @Consume Nodes:Array
+ @Consume refresh: boolean;
+ @Consume curNodesIndex:number
+ @Consume isStartToPath:boolean
+ @Consume solutionData:solutionData
+
+ controller:CustomDialogController = new CustomDialogController({
+ builder:setNodeCount({Nodes:$Nodes})
+ })
+
+ @Builder operatorMenu(){
+ Menu(){
+ MenuItem({content:"随机生成一个TSP问题"})
+ .onClick(()=>{
+ tsp.createRandomTspProblem();
+ this.Nodes = cp.nodes;
+ this.curHeaderContentIndex = 0
+ this.isStartToPath = false
+ })
+ MenuItem({content:"重新更改节点位置"})
+ .onClick(()=>{
+ cp.regenerate();
+ this.refresh = !this.refresh;
+ this.curHeaderContentIndex = 0
+ this.isStartToPath = false
+ })
+ MenuItem({content:"自动求解最优路径-遍历"})
+ .onClick(()=>{
+
+ if (this.Nodes.length <= 10) {
+ let startNodeId = this.Nodes[this.curNodesIndex].id;
+ // let result: [number[][], number, number, number] = cp.solveTspInTraversal(startNodeId);
+ let result: [number[][], number, number, number] = cp.solveTsp(startNodeId, cp.traversalSearchToPath);
+ let NodeIdsPaths: number[][] = result[0].slice();
+ if (!NodeIdsPaths.length) return;
+ let displayPath: number[] = NodeIdsPaths[0]; // 要展示的最短路径
+ let enumPaths: number[][] = NodeIdsPaths.slice(1); // 其余的最短路径
+ cp.setDisplayPath(displayPath);
+ this.refresh = !this.refresh;
+ let distanceCost: number = result[1]; // 距离成本
+ let runtime: number = result[2];
+ let runtimeTag: string = ""; // 运行时间的字符串表示,已有单位
+ if (runtime == 0) {
+ runtimeTag = "<0.001s";
+ } else {
+ runtimeTag = runtime.toFixed(3) + "s";
+ }
+ let traversalNum: number = result[3]; // 遍历次数
+ cp.createGraph(this.Nodes, false);
+ let timeCost: number = cp.calculatePathCost(cp.graph, displayPath); // 时间成本
+ this.curHeaderContentIndex = 3
+
+ this.resTime = Number((timeCost / 60).toFixed(0))
+ this.resDistance = Number((distanceCost * 1e-3).toFixed(3))
+ this.solutionData.count = traversalNum
+ this.solutionData.time = runtimeTag
+ this.solutionData.path = ""
+ this.solutionData.otherPath = [""]
+ for (let index = 0; index < displayPath.length; index++) {
+ this.solutionData.path += (this.Nodes[displayPath[index]].label)
+ if (index != displayPath.length - 1) {
+ this.solutionData.path += "->";
+ }
+ }
+ for (let index = 0; index < enumPaths.length; index++) {
+ for (let i = 0; i < enumPaths[index].length; i++) {
+ this.solutionData.otherPath[index] += (this.Nodes[enumPaths[index][i]].label)
+ if (i != enumPaths[index].length - 1) {
+ this.solutionData.otherPath[index] += "->";
+ }
+ }
+ }
+
+ } else {
+ showToast("超过11个节点的遍历法求tsp问题会导致程序崩溃,不建议使用");
+ }
+ })
+ MenuItem({content:"自动求解优化路径-贪心"})
+ .onClick(()=>{
+
+ let startNodeId = this.Nodes[this.curNodesIndex].id;
+ let result: [number[][], number, number, number] = cp.solveTsp(startNodeId, cp.greedySearchToPath);
+ if (!result[0].length) return;
+ let displayPath: number[] = result[0][0].slice(); // 要展示的最短路径
+ cp.setDisplayPath(displayPath);
+ this.refresh = !this.refresh;
+ let distanceCost: number = result[1]; // 距离成本
+ let runtime: number = result[2];
+ let runtimeTag: string = ""; // 运行时间的字符串表示,已有单位
+ if (runtime == 0) {
+ runtimeTag = "<0.001s";
+ } else {
+ runtimeTag = runtime.toFixed(3) + "s";
+ }
+ cp.createGraph(this.Nodes, false);
+ let timeCost: number = cp.calculatePathCost(cp.graph, displayPath); // 时间成本
+ this.curHeaderContentIndex = 4
+
+ this.resTime = Number((timeCost / 60).toFixed(0))
+ this.resDistance = Number((distanceCost * 1e-3).toFixed(3))
+ this.solutionData.time = runtimeTag
+ this.solutionData.path = ""
+ for (let index = 0; index < displayPath.length; index++) {
+ this.solutionData.path += (this.Nodes[displayPath[index]].label)
+ if (index != displayPath.length - 1) {
+ this.solutionData.path += "->";
+ }
+ }
+ })
+ MenuItem({content:"检查是否存在路径"})
+ .onClick(()=>{
+
+ let id1:number|undefined = AppStorage.get("startNodeId")
+ let id2:number|undefined = AppStorage.get("endNodeId")
+ if(id1 == undefined || id2 == undefined){
+ showToast("未选择两个节点,请前往路径操作中去选择两个节点")
+ return
+ }
+ let node1Id: number = id1;
+ let node2Id: number = id2;
+ if (cp.graphCheckConnectivity(this.Nodes[node1Id], this.Nodes[node2Id])[0]) {
+ showToast(`节点${node1Id} (${this.Nodes[node1Id].label})和节点${node2Id} (${this.Nodes[node2Id].label})之间存在路径`);
+ } else {
+ showToast(`节点${node1Id} (${this.Nodes[node1Id].label})和节点${node2Id} (${this.Nodes[node2Id].label})之间不存在路径`);
+ }
+ })
+ }.radius(0)
+ }
+
+ build() {
+ Row({space:5}){
+
+ ForEach(this.headerContent,(item:string,index:number)=>{
+ if(index <= 2){
+ Text(item)
+ .fontSize(SMALL_SIZE_FONT)
+ .fontColor(this.curHeaderContentIndex == index?Color.Red:Color.Black)
+ .onClick(()=>{
+ this.curHeaderContentIndex = index
+ if(this.curHeaderContentIndex != 2){
+ this.isStartToPath = false
+ }
+ })
+ }
+ })
+
+ Blank()
+
+ Row({space:5}){
+ Text("节点数目 " + (this.Nodes.length))
+ .fontSize(SMALL_SIZE_FONT)
+ Button('修改')
+ .width(60).height(25)
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.controller.open()
+ })
+ }
+
+ Text("时间 " + this.resTime + " 分钟")
+ .fontSize(SMALL_SIZE_FONT)
+
+ Text("距离(成本) " + this.resDistance+ " 千米")
+ .fontSize(SMALL_SIZE_FONT)
+
+ Blank()
+
+ Text("TSP问题生成与自动求解")
+ .onClick(()=>{
+ this.isStartToPath = false
+ })
+ .fontSize(SMALL_SIZE_FONT)
+ .bindMenu(this.operatorMenu)
+
+ }.width('100%').height('100%').padding(5).backgroundColor('#F8F8F8')
+
+ }
+}
diff --git a/drumming/drummer/entry/src/main/ets/component/ImageShow.ets b/drumming/drummer/entry/src/main/ets/component/ImageShow.ets
new file mode 100644
index 0000000..700ec11
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/component/ImageShow.ets
@@ -0,0 +1,96 @@
+import { DRAW_NODE_SIZE, NORMAL_CONTEXT, SELECTED_CONTEXT } from '../common/CommonConstant'
+import Edge from '../common/dataStruct/class/Edge'
+import Node from '../common/dataStruct/class/Node'
+import { drawEdgeList } from '../utils/functions/drawEdge'
+import { drawNode, drawNodeList } from '../utils/functions/drawNode'
+import { showToast } from '../utils/functions/showToast'
+import { Logger } from '../utils/Logger'
+
+@Component
+export struct imageShow {
+ @Consume Nodes:Array
+ @Consume curNodesIndex:number
+ @Consume Edges:Array
+ @Consume refresh:boolean
+ @Consume isStartToPath:boolean
+ @Consume nodePathId:Array
+ @Link curEdgesIndex:number
+ @Prop loop:string = "100%"
+ build() {
+ Stack() {
+ Canvas(SELECTED_CONTEXT).width('100%').height(this.loop)
+ .onReady(()=>{
+ SELECTED_CONTEXT.beginPath()
+ SELECTED_CONTEXT.strokeStyle = 'yellow'
+ SELECTED_CONTEXT.font = '20vp'
+ drawNode(this.Nodes[this.curNodesIndex],SELECTED_CONTEXT)
+ })
+ Canvas(NORMAL_CONTEXT).width('100%').height(this.loop)
+ .onReady(()=>{
+ NORMAL_CONTEXT.beginPath()
+ NORMAL_CONTEXT.strokeStyle = 'green'
+ NORMAL_CONTEXT.fillStyle = 'green'
+ //绘制线
+ drawEdgeList(this.Edges,false)
+ // drawEdgeList(this.Edges,true)
+ NORMAL_CONTEXT.font = '15vp'
+ //绘制点
+ drawNodeList(this.Nodes.slice(0,this.curNodesIndex),NORMAL_CONTEXT)
+ if(this.curNodesIndex == this.Nodes.length - 1){
+ return
+ }
+ drawNodeList(this.Nodes.slice(this.curNodesIndex + 1,this.Nodes.length),NORMAL_CONTEXT)
+ })
+ .onClick((event:ClickEvent)=>{
+ for (let index = 0; index < this.Nodes.length; index++) {
+ //是否点到节点上
+ if(event.x >= this.Nodes[index].coordinates[0] && event.x <= this.Nodes[index].coordinates[0] + DRAW_NODE_SIZE &&
+ event.y >= this.Nodes[index].coordinates[1] && event.y <= this.Nodes[index].coordinates[1] + DRAW_NODE_SIZE){
+ //进入到标记模式
+ if (this.isStartToPath) {
+ let i = index
+ if(this.Nodes[i].marked){
+ showToast("不允许选择相同的节点")
+ }else{
+ this.curNodesIndex = index
+ this.Nodes[this.curNodesIndex].marked = true
+ this.nodePathId.push(index)
+ }
+ }else{
+ this.curNodesIndex = index
+ this.refresh = !this.refresh
+ }
+ return
+ }
+ }
+
+ for (let index = 0; index < this.Edges.length; index++) {
+ if(!this.Edges[index].visible){
+ continue
+ }
+ let x1 = this.Nodes[this.Edges[index].node1].coordinates[0] + DRAW_NODE_SIZE / 2
+ let y1 = this.Nodes[this.Edges[index].node1].coordinates[1] + DRAW_NODE_SIZE / 2
+ let x2 = this.Nodes[this.Edges[index].node2].coordinates[0] + DRAW_NODE_SIZE / 2
+ let y2 = this.Nodes[this.Edges[index].node2].coordinates[1] + DRAW_NODE_SIZE / 2
+
+ let A = y2 - y1
+ let B = x1 - x2
+ let C = x2*y1 - x1*y2
+ let t1 = A * event.x + B * event.y + C
+ if(t1 < 0){
+ t1 = -t1
+ }
+ let t2 = Math.sqrt((A * A + B * B))
+ let d = t1 / t2
+ Logger.i("index" + index + "label" + this.Nodes[this.Edges[this.curEdgesIndex].node1].label + this.Nodes[this.Edges[this.curEdgesIndex].node2].label )
+ if(d < 3){
+ this.curEdgesIndex = index
+ Logger.i("cur - index" + index)
+ return
+ }
+ }
+ })
+ }
+ .width('100%').height('100%')
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/component/OperateBar.ets b/drumming/drummer/entry/src/main/ets/component/OperateBar.ets
new file mode 100644
index 0000000..9c3d025
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/component/OperateBar.ets
@@ -0,0 +1,460 @@
+import { BIG_SIZE_FONT , SMALL_SIZE_FONT } from '../common/CommonConstant'
+import { setNodeNameDialog } from '../common/customDialog/SetNodeNameDialog'
+import Edge from '../common/dataStruct/class/Edge'
+import Node from '../common/dataStruct/class/Node'
+import { solutionData } from '../common/dataStruct/interface/SolutionData'
+import { cp } from '../utils/algorithm/customPath'
+import { showToast } from '../utils/functions/showToast'
+import { Logger } from '../utils/Logger'
+
+@Component
+export struct operateBar {
+ @Prop headerContent:Array
+ @Link curHeaderContentIndex:number
+
+ @Consume Nodes:Array
+ @Consume curNodesIndex:number
+
+ build() {
+ Column(){
+ Text(this.headerContent[this.curHeaderContentIndex])
+ .fontSize(BIG_SIZE_FONT)
+ if(this.curHeaderContentIndex == 0){
+ nodeOP({selectNode:this.Nodes[this.curNodesIndex]})
+ .width('100%').layoutWeight(1)
+ }
+ if(this.curHeaderContentIndex == 1){
+ linkOP()
+ .width('100%').layoutWeight(1)
+ }
+ if(this.curHeaderContentIndex == 2){
+ pathOP()
+ .width('100%').layoutWeight(1)
+ }
+ if(this.curHeaderContentIndex == 3){
+ solution_traverse()
+ }
+ if(this.curHeaderContentIndex == 4){
+ solution_greedy()
+ }
+ }.width('100%').height('100%').backgroundColor(Color.Pink)
+ }
+}
+
+@Component
+struct nodeOP {
+ @Consume Nodes:Array
+ @Consume curNodesIndex:number
+ @Consume refresh:boolean
+ @ObjectLink selectNode:Node
+ controller:CustomDialogController = new CustomDialogController({
+ builder:setNodeNameDialog({node:this.Nodes[this.curNodesIndex]})
+ })
+ build() {
+ Column({space:5}){
+ Text('当前共有' + this.Nodes.length + "个节点")
+ .fontSize(SMALL_SIZE_FONT)
+ Row({space:5}){
+ Text('当前节点ID:')
+ .fontSize(SMALL_SIZE_FONT)
+ Text(this.selectNode.id.toString())
+ .fontSize(SMALL_SIZE_FONT)
+ }.width('100%')
+ Row({space:5}){
+ Text('当前节点标签:')
+ .fontSize(SMALL_SIZE_FONT)
+ Text(this.selectNode.label)
+ .fontSize(SMALL_SIZE_FONT)
+ }.width('100%')
+ Button('设置标签')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(()=>{
+ this.controller.open()
+ })
+ Button('删除当前节点')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(()=>{
+ if (this.Nodes.length <= 2){
+ showToast("节点数不可少于2")
+ return
+ }
+ this.Nodes = cp.deleteNodeData(this.curNodesIndex)
+ })
+ Button('设置为非经过点')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(()=>{
+ let c = 0
+ for (let index = 0; index < this.Nodes.length; index++) {
+ if(!this.Nodes[index].mustVisit){
+ c++;
+ }
+ if(c == 2){
+ showToast("最多只能选择两个非经过点")
+ return
+ }
+ }
+ this.Nodes[this.curNodesIndex].mustVisit = false
+ this.refresh = !this.refresh
+ })
+ Button('设置为经过点')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(()=>{
+ this.Nodes[this.curNodesIndex].mustVisit = true
+ this.refresh = !this.refresh
+ })
+ Button('增加一个节点')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(()=>{
+ if (this.Nodes.length >= 13) {
+ showToast("节点数只能位于2至13之间")
+ return
+ }
+ this.Nodes = cp.addNodeData()
+ })
+ }.width('100%').height('100%')
+ }
+}
+
+@Component
+struct linkOP {
+ @Consume Nodes:Array
+ @Consume curNodesIndex:number
+ @Consume Edges:Array
+ @Consume curEdgesIndex:number
+
+ @State isOnClick:boolean = false
+ @State startNode:Node|undefined = undefined
+ @State endNode:Node|undefined = undefined
+
+ @Consume refresh:boolean
+
+ build() {
+ Scroll() {
+ Column({ space: 5 }) {
+ Button('建立为完全图')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(() => {
+ for (let index = 0; index < this.Edges.length; index++) {
+ this.Edges[index].visible = true
+ }
+ this.refresh = !this.refresh
+ })
+ Button('删除所有连接')
+ .type(ButtonType.Normal)
+ .fontSize(SMALL_SIZE_FONT)
+ .onClick(() => {
+ for (let index = 0; index < this.Edges.length; index++) {
+ this.Edges[index].visible = false
+ }
+ this.refresh = !this.refresh
+ })
+ Column({ space: 5 }) {
+ Text('当前选中的连接:')
+ .fontSize(SMALL_SIZE_FONT)
+ Text(`从 节点${this.Edges[this.curEdgesIndex].node1}:` + this.Nodes[this.Edges[this.curEdgesIndex].node1].label)
+ .fontSize(SMALL_SIZE_FONT)
+ Text(`到 节点${this.Edges[this.curEdgesIndex].node2}:` + this.Nodes[this.Edges[this.curEdgesIndex].node2].label)
+ .fontSize(SMALL_SIZE_FONT)
+ Text(`距离${(this.Edges[this.curEdgesIndex].distance * 1e-3).toFixed(3)}千米`)
+ .fontSize(SMALL_SIZE_FONT)
+ Text(`时间${(this.Edges[this.curEdgesIndex].time / 60).toFixed(0)}分钟`)
+ .fontSize(SMALL_SIZE_FONT)
+ }.width('100%')
+
+ Button(this.isOnClick ? "取消" : '选择两个节点进行连接')
+ .type(ButtonType.Normal)
+ .onClick(() => {
+ this.startNode = this.Nodes[this.curNodesIndex]
+ this.isOnClick = !this.isOnClick
+ })
+ Row({ space: 5 }) {
+ Text('当前节点:')
+ .fontSize(SMALL_SIZE_FONT)
+ Text(this.isOnClick ? this.startNode!.label : this.Nodes[this.curNodesIndex].label)
+ .fontSize(SMALL_SIZE_FONT)
+ }.width('100%')
+
+ if (this.isOnClick) {
+ Row({ space: 5 }) {
+ Text('要连接节点:')
+ .fontSize(SMALL_SIZE_FONT)
+ Text(this.Nodes[this.curNodesIndex].label)
+ .fontSize(SMALL_SIZE_FONT)
+ }.width('100%')
+
+ Button('确定')
+ .type(ButtonType.Normal)
+ .onClick(() => {
+ let startNodeId = this.startNode!.id
+ let endNodeId = this.Nodes[this.curNodesIndex].id
+ Logger.i(startNodeId + " " + endNodeId)
+ this.startNode = this.Nodes[this.curNodesIndex]
+ if (startNodeId == endNodeId) {
+ showToast("不允许连接同一个节点")
+ return
+ }
+ this.isOnClick = false
+ cp.connectTwoNodes(startNodeId, endNodeId);
+ this.refresh = !this.refresh;
+ })
+ }
+ }
+ }.width('100%').height('100%').padding({bottom:5})
+ }
+}
+
+@Component
+struct pathOP {
+ @Consume Nodes:Array
+ @Consume curNodesIndex:number
+ @Consume Edges:Array
+ @Consume refresh:boolean
+ @Consume nodePathId:Array
+ @Consume nodePath:string
+ @Consume isStartToPath:boolean
+ @Consume solutionData:solutionData
+ @State isSelectNode:boolean = false
+ @State selectIndex:number = 0
+ @State index:number = -1
+ @State startNode:Node|undefined = undefined
+ @State endNode:Node|undefined = undefined
+ @State displayShortPath: boolean = false
+
+
+
+ build() {
+ Scroll(){
+ Column({space:5}){
+ Button(this.isStartToPath?"结束标记":'标记路径')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ if(this.isSelectNode){
+ return
+ }
+ if(!this.isStartToPath){
+ this.nodePathId = []
+ } else {
+ if (!cp.checkPathLegitimate(this.nodePathId)) {
+ this.nodePathId.length = 0;
+ cp.clearPath();
+ }
+ }
+ this.isStartToPath = !this.isStartToPath
+
+ //先置空
+ for (let index = 0; index < this.Edges.length; index++) {
+ this.Edges[index].marked = false
+ }
+ for (let index = 0; index < this.Nodes.length; index++) {
+ this.Nodes[index].marked = false
+ }
+ this.refresh = !this.refresh
+ cp.setDisplayPath(this.nodePathId);
+ })
+ Text('用户指定路径:' + this.nodePath)
+ .fontSize(SMALL_SIZE_FONT)
+ .width("100%")
+ if(this.isStartToPath){
+ Row(){
+ Button('清除')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.nodePathId = []
+ cp.clearPath();
+ for (let index = 0; index < this.Nodes.length; index++) {
+ this.Nodes[index].marked = false
+ }
+ })
+ Button('撤销')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ cp.undoPath();
+ if(this.nodePathId.length == 0){
+ return
+ }
+ this.Nodes[this.nodePathId[this.nodePathId.length-1]].marked = false
+ this.nodePathId.pop()
+ return
+
+ })
+ }.width('100%').justifyContent(FlexAlign.SpaceEvenly)
+ Button('检查路径是否合法')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ cp.checkPathLegitimate(this.nodePathId);
+ })
+ }
+ Button('判断是否为最短路径')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ if(this.isStartToPath){
+ return
+ }
+ if(this.isSelectNode){
+ return
+ }
+ if(!this.nodePath.length){
+ return
+ }
+ if(this.Nodes.length <= 10){
+ if (cp.checkUserBest(cp.path, this.nodePathId[0])) {
+ showToast("当前路径是最短路径");
+ } else {
+ showToast("当前路径不是最短路径");
+ }
+ }else{
+ showToast("超过11个节点的遍历法求tsp问题会导致程序崩溃,不建议使用");
+ }
+
+ })
+ Button('判断是否为贪心路径')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ if(this.isStartToPath){
+ return
+ }
+ if(this.isSelectNode){
+ return
+ }
+ if(!this.nodePath.length){
+ return
+ }
+ if (cp.checkIsGreedy(cp.path, this.nodePathId[0])) {
+ showToast("当前路径是贪心路径");
+ } else {
+ setTimeout(() => {
+ showToast("当前路径不是贪心路径");
+ }, 2000);
+ }
+ })
+ Row(){
+ Button(this.isSelectNode?"取消":'选择两个节点')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ if(this.isStartToPath){
+ return
+ }
+ this.isSelectNode = !this.isSelectNode
+ this.selectIndex = this.curNodesIndex
+ this.startNode = this.Nodes[this.curNodesIndex]
+ this.endNode = this.Nodes[this.curNodesIndex]
+ this.index = -1
+ this.displayShortPath = false;
+ this.solutionData.path = "";
+ this.solutionData.count = 0;
+ this.Nodes.forEach(node => {
+ node.mustVisit = true;
+ })
+ this.refresh = !this.refresh;
+ })
+ if(this.isSelectNode){
+ Button("确定")
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ if(this.curNodesIndex == this.startNode!.id){
+ showToast("不能选择相同的节点")
+ return
+ }
+ if (!cp.graphCheckConnectivity(this.Nodes[this.curNodesIndex], this.Nodes[this.startNode!.id])[0]) {
+ showToast("节点间不可达,请重新选择节点");
+ return;
+ }
+ if(this.index != -1){
+ return
+ }
+ this.endNode = this.Nodes[this.curNodesIndex]
+ this.index = this.curNodesIndex
+ AppStorage.setOrCreate("startNodeId",this.startNode!.id)
+ AppStorage.setOrCreate("endNodeId",this.endNode!.id)
+ })
+ }
+ }
+ Text(`当前节点1ID:${this.isSelectNode?this.selectIndex + `(${this.Nodes[this.selectIndex].label})`:this.curNodesIndex + `(${this.Nodes[this.curNodesIndex].label})`}`)
+ .fontSize(SMALL_SIZE_FONT)
+ if(this.isSelectNode){
+ Text(`当前节点2ID:${this.index != -1?this.index + `(${this.Nodes[this.index].label})`:this.curNodesIndex + `(${this.Nodes[this.curNodesIndex].label})`}`)
+ .fontSize(SMALL_SIZE_FONT)
+ Button('求解最短路径')
+ .type(ButtonType.Normal)
+ .onClick(()=>{
+ this.displayShortPath = false;
+ this.solutionData.path = "";
+ this.solutionData.count = 0;
+ if(this.endNode != undefined){
+ if(this.endNode!.id != this.startNode!.id){
+ let shortPathInfo: [number[], number] = cp.shortestPathAlgorithm(this.startNode!.id, this.endNode!.id);
+ let shortPath: number[] = shortPathInfo[0];
+ this.solutionData.count = Number((shortPathInfo[1] * 1e-3).toFixed(3));
+ for (let index = 0; index < shortPath.length; index++) {
+ this.solutionData.path += (this.Nodes[shortPath[index]].label)
+ if (index != shortPath.length - 1) {
+ this.solutionData.path += "->";
+ }
+ }
+ if (this.solutionData.path != "") {
+ this.displayShortPath = true;
+ } else {
+ showToast("没有找到路径");
+ }
+ return;
+ }
+ }
+ showToast("请选择第二个节点,或点击确定")
+ })
+ if (this.displayShortPath) {
+ Text('最短路径长度:' + this.solutionData.count + "km")
+ .fontSize(SMALL_SIZE_FONT)
+ .width("100%")
+ Text('最短路径为:' + this.solutionData.path)
+ .fontSize(SMALL_SIZE_FONT)
+ .width("100%")
+ }
+ }
+ }
+ }.width('100%').height('100%').padding({bottom:5})
+ }
+}
+
+@Component
+struct solution_traverse {
+ @Consume solutionData:solutionData
+ build() {
+ Column({space:5}){
+ Text('遍历次数' + this.solutionData.count!)
+ .fontSize(SMALL_SIZE_FONT)
+ Text('求解花费时间' + this.solutionData.time)
+ .fontSize(SMALL_SIZE_FONT)
+ Text('最短路径:' + this.solutionData.path)
+ .fontSize(SMALL_SIZE_FONT)
+ .width("100%")
+ ForEach(this.solutionData.otherPath,(item:string,index:number)=>{
+ Row(){
+ Text('其他最短路径' + (index + 1) + ": " + item)
+ .fontSize(SMALL_SIZE_FONT)
+ }.width('100%')
+ })
+ }.width('100%').height('100%')
+ }
+}
+
+@Component
+struct solution_greedy{
+
+ @Consume solutionData:solutionData
+ build() {
+ Column({space:5}){
+ Text('求解花费时间' + this.solutionData.time)
+ .fontSize(SMALL_SIZE_FONT)
+ Text('最短路径:' + this.solutionData.path)
+ .fontSize(SMALL_SIZE_FONT)
+ .width('100%')
+ }.width('100%').height('100%')
+ }
+}
+
diff --git a/drumming/drummer/entry/src/main/ets/entryability/EntryAbility.ets b/drumming/drummer/entry/src/main/ets/entryability/EntryAbility.ets
new file mode 100644
index 0000000..6e45be4
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/entryability/EntryAbility.ets
@@ -0,0 +1,60 @@
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+import hilog from '@ohos.hilog';
+import UIAbility from '@ohos.app.ability.UIAbility';
+import Want from '@ohos.app.ability.Want';
+import window from '@ohos.window';
+import dataPreferences from '@ohos.data.preferences';
+
+export default class EntryAbility extends UIAbility {
+
+ async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
+ let dp = await dataPreferences.getPreferences(this.context,"web")
+ let loop = await dp.has("isLoad")
+ if(loop){
+ //读取文件操作
+ AppStorage.setOrCreate('isOpFile',true)
+ }else{
+ dp.put("isLoad",true,()=>{
+ dp.flush()
+ })
+ //调用api操作
+ AppStorage.setOrCreate('isOpFile',false)
+ }
+
+
+ }
+
+
+ onDestroy(): void {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage): void {
+ // Main window is created, set main page for this ability
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
+
+ windowStage.loadContent('pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy(): void {
+ // Main window is destroyed, release UI related resources
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
+ }
+
+ onForeground(): void {
+ // Ability has brought to foreground
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground(): void {
+ // Ability has back to background
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
+ }
+}
diff --git a/drumming/drummer/entry/src/main/ets/pages/Index.ets b/drumming/drummer/entry/src/main/ets/pages/Index.ets
new file mode 100644
index 0000000..35f292e
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/pages/Index.ets
@@ -0,0 +1,126 @@
+import { CityInfo } from '../common/dataStruct/interface/CityInfo';
+import { X_CENTER, Y_CENTER } from '../common/CommonConstant';
+import Edge from '../common/dataStruct/class/Edge';
+import Node from '../common/dataStruct/class/Node';
+import { solutionData } from '../common/dataStruct/interface/SolutionData';
+import { header } from '../component/Header';
+import { imageShow } from '../component/ImageShow';
+import { operateBar } from '../component/OperateBar';
+import { cp } from '../utils/algorithm/customPath';
+import { getRealData } from '../utils/functions/getRealData';
+import { Logger } from '../utils/Logger';
+import { createCityInfo } from '../utils/functions/createCityInfo';
+
+export let cityInfo: CityInfo = {};
+
+@Entry
+@Component
+struct Index {
+ @State headerContent:Array = ["节点操作","连接操作","路径操作","最短路径-遍历","最短路径-贪心"]
+ @State @Watch('refreshOption')curHeaderContentIndex:number = 0
+ refreshOption(){
+ for (let index = 0; index < this.Nodes.length; index++) {
+ this.Nodes[index].marked = false
+ }
+ this.nodePathId = []
+ }
+
+ @Provide @Watch('refreshNodes')Nodes:Array = [new Node(0, "noMean", false, [X_CENTER,Y_CENTER])];
+ @Provide curNodesIndex:number = 0
+
+ @Provide Edges:Array = []
+ @Provide curEdgesIndex:number = 0
+
+ @State resTime:number = 0
+ @State resDistance:number = 0
+
+ @Provide solutionData:solutionData = {
+ count:-1,
+ time:" ",
+ path:" ",
+ otherPath:[""]
+ }
+
+ //画板刷新
+ @Provide @Watch('refreshBoard')refresh:boolean = false
+ @State loop:string = "100%"
+
+ @Provide isStartToPath:boolean = false
+ @Provide nodePath:string = ""
+ @Provide @Watch('refreshNodePath')nodePathId:Array = []
+ refreshNodePath(){
+ this.nodePath = ''
+ for (let index = 0; index < this.nodePathId.length; index++) {
+ this.nodePath += (this.Nodes[this.nodePathId[index]].label + `(${this.nodePathId[index]})`);
+ if (index != this.nodePathId.length - 1) {
+ this.nodePath += "->";
+ }
+ }
+ if(this.nodePathId.length == 0){
+ this.curNodesIndex = 0
+ }else{
+ this.curNodesIndex = this.nodePathId[this.nodePathId.length-1]
+ }
+ this.refresh = !this.refresh
+ Logger.i(JSON.stringify(this.nodePathId))
+ }
+ refreshNodes(){
+ this.curNodesIndex = 0
+ //重新给id排序
+ for (let index = 0; index < this.Nodes.length; index++) {
+ this.Nodes[index].id = index
+ }
+
+ this.Edges = cp.createEdgeData(this.Nodes)
+
+ //涉及到节点数组的变化先更新节点数组再更新连接数组再更新画板
+ this.refresh = !this.refresh
+ }
+
+ refreshBoard(){
+ this.loop = this.loop == "100%"?"101%":"100%"
+ }
+
+ @State isShow:boolean = true
+ build() {
+ Stack(){
+ Column(){
+
+ header({headerContent:this.headerContent,curHeaderContentIndex:$curHeaderContentIndex,resTime:$resTime,resDistance:$resDistance})
+ .width('100%')
+ .layoutWeight(1)
+
+ Divider()
+ .width('100%')
+ .strokeWidth(1)
+ .color(Color.Orange)
+
+ Row(){
+
+ imageShow({loop:this.loop,curEdgesIndex:$curEdgesIndex})
+ .width('80%')
+ .height('100%')
+
+ operateBar({headerContent:this.headerContent,curHeaderContentIndex:$curHeaderContentIndex})
+ .width('20%')
+ .height('100%')
+
+ }.width('100%').height('90%')
+ }.width('100%').height('100%').opacity(this.isShow?0.425:1)
+ if(this.isShow) {
+ Column() {
+ Text('正在初始化数据...')
+ .fontSize(40)
+ .onAppear(async () => {
+ await createCityInfo().then((info) => {
+ cityInfo = info;
+ })
+ this.Nodes = cp.createNodeData(3);
+ this.Edges = cp.createEdgeData(this.Nodes);
+ this.isShow = false;
+ })
+ }.width('100%').height('100%').justifyContent(FlexAlign.Center)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/Logger.ets b/drumming/drummer/entry/src/main/ets/utils/Logger.ets
new file mode 100644
index 0000000..2339ef5
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/Logger.ets
@@ -0,0 +1,22 @@
+import hilog from '@ohos.hilog';
+
+const DOMAIN = 0
+const APP_TAG = 'TSPTestApp'
+
+export class Logger {
+ public static d(message: string) {
+ hilog.debug(DOMAIN, APP_TAG, message)
+ }
+
+ public static i(message: string) {
+ hilog.info(DOMAIN, APP_TAG, message)
+ }
+
+ public static e(message: string) {
+ hilog.error(DOMAIN, APP_TAG, message)
+ }
+
+ public static w(message: string) {
+ hilog.warn(DOMAIN, APP_TAG, message)
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/algorithm/TSP.ets b/drumming/drummer/entry/src/main/ets/utils/algorithm/TSP.ets
new file mode 100644
index 0000000..1faebee
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/algorithm/TSP.ets
@@ -0,0 +1,48 @@
+import { cp } from "./customPath";
+import Node from '../../common/dataStruct/class/Node';
+import Edge from '../../common/dataStruct/class/Edge';
+
+export class TSP {
+ path: Edge[]; // 从连接构造的路径
+ nodes: Node[]; // 节点对象列表
+ edges: Edge[]; // 连接列表
+
+ constructor() {
+ this.path = [];
+ this.nodes = [];
+ this.edges = [];
+ }
+
+ /**
+ * 创建节点数组和全连接数组
+ * @param nodeNum 节点数量
+ */
+ createNodesAndEdges(nodeNum: number) {
+ this.path = [];
+ this.nodes = cp.createNodeData(nodeNum);
+ this.edges = cp.createEdgeData(this.nodes);
+ }
+
+ /**
+ * 创建随机 tsp 问题
+ */
+ createRandomTspProblem() {
+ // 随机4到7个节点
+ const nodeNum: number = Math.floor(Math.random() * (7 - 4 + 1)) + 4;
+ this.createNodesAndEdges(nodeNum);
+ }
+
+ /**
+ * 创建随机路径
+ * @param nodeNum 节点数量
+ */
+ createRandomPath(nodeNum: number) {
+ this.createNodesAndEdges(nodeNum);
+ let connOrder: number[] = Array.from({length: nodeNum}, (_: number, i: number) => i);
+ connOrder.sort(() => Math.random() - 0.5);
+ this.path = cp.setDisplayPath(connOrder)[0];
+ }
+
+}
+
+export let tsp: TSP = new TSP();
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/algorithm/customPath.ets b/drumming/drummer/entry/src/main/ets/utils/algorithm/customPath.ets
new file mode 100644
index 0000000..09ab82a
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/algorithm/customPath.ets
@@ -0,0 +1,574 @@
+import { showToast } from "../functions/showToast"
+import Node from '../../common/dataStruct/class/Node'
+import Edge from '../../common/dataStruct/class/Edge'
+import { BOTTOM_RIGHT_X, BOTTOM_RIGHT_Y, DRAW_NODE_SIZE, TOP_LEFT_X, TOP_LEFT_Y, CITY } from '../../common/CommonConstant';
+import { cityInfo } from '../../pages/Index';
+import { Graph, nodeInfo } from '../../common/dataStruct/interface/Graph';
+import { deepCopyGraph } from '../functions/deepCopyGraph';
+import { degreesToRadians } from '../functions/degreesToRadians';
+
+let cityName: string[] = CITY.slice();
+let usedCity: string[] = [];
+let surplusCity: string[] = [];
+
+export class CustomPath {
+ path: Edge[]; // 从连接构造的路径
+ pathDistanceCount: number; // 路径距离成本
+ pathTimeCount: number; // 路径时间成本
+ elapsedTime: number; // 运行时间
+ graph: Graph; // 邻接图
+ nodes: Node[]; // 节点对象列表
+ edges: Edge[]; // 连接列表
+ circles: number[][]; // 形成环路径
+ numberOfPasses: number; // 遍历次数
+
+ constructor(path: Edge[], pathDistanceCount: number, pathTimeCount: number, elapsedTime: number, graph: Graph, nodes: Node[], edges: Edge[]) {
+ this.path = path;
+ this.pathDistanceCount = pathDistanceCount;
+ this.pathTimeCount = pathTimeCount;
+ this.elapsedTime = elapsedTime;
+ this.graph = graph;
+ this.nodes = nodes;
+ this.edges = edges;
+ this.circles = [];
+ this.numberOfPasses = 0;
+ }
+
+ createNodeData(data: number): Node[] {
+ let num: number = data;
+ let ang = 360 / num;
+ let R = 140;
+ this.nodes = [];
+ cityName.sort(() => Math.random() - 0.5);
+ for (let i = 0; i < num; i++) {
+ let origin: [number, number] = [(BOTTOM_RIGHT_X - TOP_LEFT_X - DRAW_NODE_SIZE) / 2, (BOTTOM_RIGHT_Y - TOP_LEFT_Y - DRAW_NODE_SIZE) / 2];
+ let rad = degreesToRadians(i * ang);
+ const x = origin[0] + Math.cos(rad) * R | 0;
+ const y = origin[1] + Math.sin(rad) * R | 0;
+ this.nodes.push(new Node(i, cityName[i], false, [x, y], true));
+ }
+ usedCity = cityName.slice(0, num);
+ surplusCity = cityName.slice(num);
+ return this.nodes;
+ }
+
+ addNodeData(): Node[] {
+ let num: number = this.nodes.length + 1;
+ let ang = 360 / num;
+ let R = 140;
+ let origin: [number, number] = [(BOTTOM_RIGHT_X - TOP_LEFT_X - DRAW_NODE_SIZE) / 2, (BOTTOM_RIGHT_Y - TOP_LEFT_Y - DRAW_NODE_SIZE) / 2];
+ surplusCity.sort(() => Math.random() - 0.5);
+ this.nodes = this.nodes.map(node => {
+ let rad = degreesToRadians(node.id * ang);
+ const x = origin[0] + Math.cos(rad) * R | 0;
+ const y = origin[1] + Math.sin(rad) * R | 0;
+ node.coordinates = [x, y];
+ return node;
+ });
+ let rad = degreesToRadians((num - 1) * ang);
+ const x = origin[0] + Math.cos(rad) * R | 0;
+ const y = origin[1] + Math.sin(rad) * R | 0;
+ this.nodes.push(new Node(this.nodes.length, surplusCity[0] , false, [x, y], true));
+ usedCity = usedCity.concat(surplusCity[0]);
+ surplusCity = surplusCity.slice(1);
+ this.createEdgeData(this.nodes);
+ return this.nodes;
+ }
+
+ deleteNodeData(currentNodeId: number): Node[] {
+ // surplusCity = usedCity.slice(-1).concat(surplusCity);
+ // usedCity = usedCity.slice(0, -1);
+ // this.nodes.pop();
+ usedCity = usedCity.filter((city) => city != this.nodes[currentNodeId].label);
+ surplusCity = [this.nodes[currentNodeId].label].concat(surplusCity);
+ this.nodes = this.nodes.filter(node => node.id != currentNodeId);
+ this.nodes = this.nodes.map((node, index) => {
+ node.id = index;
+ return node;
+ });
+ this.createEdgeData(this.nodes);
+ return this.nodes;
+ }
+
+ regenerate() {
+ this.nodes = this.nodes.map(node => {
+ let x = Math.random()*(BOTTOM_RIGHT_X - TOP_LEFT_X - DRAW_NODE_SIZE);
+ let y = Math.random()*(BOTTOM_RIGHT_Y - TOP_LEFT_Y - DRAW_NODE_SIZE);
+ node.coordinates = [x, y];
+ return node;
+ })
+ }
+
+ createEdgeData(nodes: Node[]): Edge[] {
+ this.edges = [];
+ for (let i = 0; i < nodes.length; i++) {
+ for (let j = i + 1; j < nodes.length; j++) {
+ const connectionId = this.edges.length + 1;
+ const node1Id = nodes[i].id;
+ const node2Id = nodes[j].id;
+ const visible = true;
+ const marked = false;
+ const label = `Connection-${connectionId}`;
+ // const distance = Math.floor(Math.random() * (1000 - 10 + 1)) + 10; // 生成10到1000的随机整数 --你需要设置实际的距离值
+ // const time = Math.floor(Math.random() * (720 - 60 + 1)) + 60; // 生成60到720的随机整数 --你需要设置实际的时间值
+ const distance = cityInfo[nodes[i].label][nodes[j].label].distance;
+ const time = cityInfo[nodes[i].label][nodes[j].label].time;
+ // console.log(`${nodes[i].label}->${nodes[j].label}: 距离为${distance},时间为${time}`);
+ this.edges.push(new Edge(connectionId, node1Id, node2Id, visible, marked, label, distance, time));
+ }
+ }
+ return this.edges;
+ }
+
+ connectTwoNodes(node1Id: number, node2Id: number) {
+ let nodeId: [number, number] = [node1Id, node2Id];
+ for (const edge of this.edges) {
+ if (nodeId.includes(edge.node1) && nodeId.includes(edge.node2)) {
+ if (!edge.visible) edge.visible = true;
+ else showToast("当前两节点已连接");
+ }
+ }
+ }
+
+ setDisplayPath(nodeIds: number[]): [Edge[], number, number] {
+ this.path = []; // 置空操作
+ this.pathDistanceCount = 0;
+ this.pathTimeCount = 0;
+ if (nodeIds.length) {
+ for (let i = 0; i < nodeIds.length; i++) {
+ let id1: number = 0;
+ let id2: number = 0;
+ if (i < nodeIds.length - 1) {
+ id1 = nodeIds[i];
+ id2 = nodeIds[i + 1];
+ } else {
+ if (nodeIds[0] == nodeIds[-1]) {
+ break;
+ } else {
+ id1 = nodeIds[i];
+ id2 = nodeIds[0];
+ }
+ }
+ let nodeId: [number, number] = [id1, id2]; // 用于寻找两节点的连接对象
+ for (const edge of this.edges) {
+ if (nodeId.includes(edge.node1) && nodeId.includes(edge.node2)) {
+ edge.marked = true;
+ this.path.push(edge);
+ this.pathDistanceCount += edge.distance;
+ this.pathTimeCount += edge.time;
+ }
+ }
+ }
+ }
+ return [this.path, this.pathDistanceCount, this.pathTimeCount];
+ }
+
+ clearPath() {
+ this.path = [];
+ this.pathDistanceCount = 0;
+ this.pathTimeCount = 0;
+ this.edges.forEach(edge => {
+ edge.marked = false; // 全部连接清除标记
+ })
+ }
+
+ undoPath() {
+ if (this.path.length) {
+ this.path[-1].marked = false; // 路径末尾连接清除标记
+ this.path.pop();
+ }
+ }
+
+ checkPathLegitimate(paths: number[]): boolean {
+ for (let i = 0; i < paths.length; i++) {
+ let node1Id: number = 0;
+ let node2Id: number = 0;
+ if (i < paths.length - 1) {
+ node1Id = paths[i];
+ node2Id = paths[i + 1];
+ } else {
+ node1Id = paths[i];
+ node2Id = paths[0];
+ }
+ if (node1Id == node2Id) {
+ return false;
+ }
+ let node1: Node = this.nodes[node1Id];
+ let node2: Node = this.nodes[node2Id];
+ if (!this.graphCheckConnectivity(node1, node2)[0]) {
+ showToast(`在节点${node1.label}与节点${node2.label}之间没有连接, 路径不合法, 请重新选择路径`);
+ return false;
+ }
+ }
+ let nonessentialNode: Node[] = this.nodes.filter(node => node.mustVisit == false);
+ let nonessentialNum: number = nonessentialNode.length;
+ let tempPaths: number[] = paths.filter(nodeId => { // 路径中所有必经节点构成数组
+ for (let i = 0; i < nonessentialNum; i++) {
+ if (nodeId == nonessentialNode[i].id) {
+ return false;
+ }
+ }
+ return true;
+ });
+ if (tempPaths.length != this.nodes.length - nonessentialNum) {
+ showToast(`所选路径未经所有必经节点城市, 路径不合法, 请重新选择路径`);
+ return false;
+ }
+ showToast("所选路径合法");
+ return true;
+ }
+
+ createGraph(nodes: Node[], isDistance: boolean = true): Graph {
+ this.graph = {};
+ nodes.forEach(node => {
+ if (node.mustVisit == true) {
+ this.graph[node.id] = [];
+ }
+ })
+ nodes.forEach(node => {
+ if (node.mustVisit == true) {
+ this.edges.forEach(edge => {
+ if (edge.visible) {
+ let nodeIds: [number, number] = [edge.node1, edge.node2];
+ if (nodeIds.includes(node.id) && nodes[nodeIds[0]].mustVisit == true && nodes[nodeIds[1]].mustVisit == true) {
+ let nodeId: number = nodeIds.find(id => id !== node.id) as number;
+ let weight: number = isDistance ? edge.distance : edge.time;
+ this.graph[node.id].push({ to: nodeId, weight: weight });
+ }
+ }
+ })
+ }
+ })
+ return this.graph;
+ }
+
+ dfs(nodeId: number, visited: Set, path: number[], startNeighbors: Set) {
+ try {
+ visited.add(nodeId);
+ path.push(nodeId);
+ if (path.length == Object.keys(this.graph).length && startNeighbors.has(path[path.length - 1])) {
+ this.circles.push(path.slice());
+ return;
+ }
+ for (const adjacentNodeInfo of this.graph[nodeId]) {
+ const neighbor: number = adjacentNodeInfo.to;
+ if (!visited.has(neighbor)) {
+ this.dfs(neighbor, visited, path, startNeighbors);
+ visited.delete(neighbor);
+ path.pop();
+ }
+ }
+ } catch {
+ console.log(this.circles.length.toString());
+ this.circles.length = 0;
+ return;
+ }
+ }
+
+ findCompleteCycles(graph: Graph, startNodeId: number = 0): number[][] {
+ this.circles = [];
+ let visited: Set = new Set();
+ let startNeighborsArr: number[] = graph[startNodeId].map(nodeInfo => nodeInfo.to);
+ let startNeighbors: Set = new Set(startNeighborsArr);
+ this.dfs(startNodeId, visited, [], startNeighbors);
+ return this.circles;
+ }
+
+ calculatePathCost(graph: Graph, path: number[]) {
+ let costValue = 0;
+ for (let i = 0; i < path.length - 1; i++) {
+ let currentNodeId: number = path[i];
+ let nextNodeId: number = path[i + 1];
+ if (!(graph[currentNodeId] && graph[currentNodeId].length === 0)) {
+ for (const adjacentNodeInfo of graph[currentNodeId]) {
+ if (adjacentNodeInfo.to == nextNodeId) {
+ costValue += adjacentNodeInfo.weight;
+ break;
+ }
+ }
+ } else break;
+ }
+ return costValue;
+ }
+
+ traversalSearchToPath(cp: CustomPath, graph: Graph, startNodeId: number = 0, _mustVisitNode?: number[]): [number[][], number, number, number] {
+ let startTime: number = Date.now();
+ let allPaths: number[][] = cp.findCompleteCycles(graph, startNodeId);
+ let numOfPasses = allPaths.length;
+ if (allPaths.length == 0) {
+ try {
+ throw new Error("当前图无法获取完整路径,请检查节点连接是否完整");
+ } catch {
+ showToast("当前图无法获取完整路径,请检查节点连接是否完整");
+ return [[], 0, 0, 0];
+ }
+ }
+ let minPathList: number[][] = [];
+ let minValue: number = Number.POSITIVE_INFINITY;
+ for (const permutation of allPaths) {
+ let currentPath = permutation.concat([startNodeId]);
+ let currentCostValue = cp.calculatePathCost(graph, currentPath);
+ if (currentCostValue < minValue) {
+ minValue = currentCostValue;
+ minPathList = [];
+ minPathList.push(currentPath.slice());
+ } else if (currentCostValue == minValue) {
+ minPathList.push(currentPath.slice());
+ }
+ }
+ let endTime: number = Date.now();
+ cp.elapsedTime = (endTime - startTime) * 1e-3; // 转换为秒
+ return [minPathList, minValue, cp.elapsedTime, numOfPasses];
+ }
+
+ greedySearchToPath(cp: CustomPath, graph: Graph, startNodeId: number = 0, _mustVisitNode?: number[]): [number[][], number, number, number] {
+ let startTime: number = Date.now();
+ let startNeighborsArr: number[] = graph[startNodeId].map(nodeInfo => nodeInfo.to);
+ let startNeighbors: Set = new Set(startNeighborsArr);
+ let remainingNodes: Set = new Set(Object.keys(graph).map(i => Number(i)));
+ remainingNodes.delete(startNodeId);
+ let currentNodeId = startNodeId;
+ let path: number[] = [startNodeId];
+ while (remainingNodes.size > 0) {
+ if (graph[currentNodeId].length === 0) break;
+ let nextNodeId: number = Number.NaN;
+ let minValue: number = Number.POSITIVE_INFINITY;
+ for (const adjacentNodeInfo of graph[currentNodeId]) {
+ if (adjacentNodeInfo.weight < minValue && !path.includes(adjacentNodeInfo.to)) {
+ minValue = adjacentNodeInfo.weight;
+ nextNodeId = adjacentNodeInfo.to;
+ }
+ }
+ if (Number.isNaN(nextNodeId)) {
+ try {
+ throw new Error(`当前id为${currentNodeId}的节点无直接路径到id为${Array.from(remainingNodes)[0]}的节点,贪心算法求解失败`);
+ } catch {
+ showToast(`当前id为${currentNodeId}的节点无直接路径到id为${Array.from(remainingNodes)[0]}的节点,贪心算法求解失败`);
+ return [[], 0, 0, 1];
+ }
+ }
+ path.push(nextNodeId);
+ remainingNodes.delete(nextNodeId);
+ currentNodeId = nextNodeId;
+ }
+ if (startNeighbors.has(path[path.length - 1])) {
+ path.push(startNodeId);
+ } else {
+ try {
+ throw new Error(`当前id为${path[path.length - 1]}的节点无直接路径到id为${startNodeId}的节点,贪心算法求解失败`);
+ } catch {
+ showToast(`当前id为${path[path.length - 1]}的节点无直接路径到id为${startNodeId}的节点,贪心算法求解失败`);
+ return [[], 0, 0, 1];
+ }
+ }
+ let endTime: number = Date.now();
+ cp.elapsedTime = (endTime - startTime) * 1e-3; // 转换为秒
+ return [[path], cp.calculatePathCost(graph, path), cp.elapsedTime, 1];
+ }
+
+ solveTsp(startNodeId: number = 0, func: (cp: CustomPath, graph: Graph, startNodeId: number) => [number[][], number, number, number]): [number[][], number, number, number] {
+ let startTime: number = Date.now();
+ let shortestDistance: number[] = [];
+ let pathList: number[][][] = [];
+ let remainGraph: Graph[] = [];
+ let nonessentialNode: Node[] = this.nodes.filter(node => node.mustVisit == false);
+ let nonessentialNum: number = nonessentialNode.length;
+ this.numberOfPasses = 0;
+
+ for (let i = 0; i < nonessentialNum; i++) {
+ if (startNodeId == nonessentialNode[i].id) {
+ try {
+ throw new Error("起始节点不能为非必经节点");
+ } catch {
+ showToast("起始节点不能为非必经节点");
+ return [[], 0, 0, 0];
+ }
+ }
+ }
+
+ let graph: Graph = this.createGraph(this.nodes);
+ let res: [number[][], number, number, number] = func(this, graph, startNodeId);
+ if (!res[0].length) return [[], 0, 0, 0];
+ pathList.push(res[0].slice());
+ shortestDistance.push(res[1]);
+ this.numberOfPasses += res[3];
+ remainGraph.push(deepCopyGraph(graph));
+ for (let i = 0; i < nonessentialNum; i++) {
+ this.nodes[nonessentialNode[i].id].mustVisit = true;
+ for (let j = i + 1; j < nonessentialNum; j++) {
+ this.nodes[nonessentialNode[j].id].mustVisit = true;
+ graph = this.createGraph(this.nodes);
+ res = func(this, graph, startNodeId);
+ if (!res[0].length) return [[], 0, 0, 0];
+ pathList.push(res[0].slice());
+ shortestDistance.push(res[1]);
+ this.numberOfPasses += res[3];
+ remainGraph.push(deepCopyGraph(graph));
+ this.nodes[nonessentialNode[j].id].mustVisit = false;
+ }
+ graph = this.createGraph(this.nodes);
+ res = func(this, graph, startNodeId);
+ if (!res[0].length) return [[], 0, 0, 0];
+ pathList.push(res[0].slice());
+ shortestDistance.push(res[1]);
+ this.numberOfPasses += res[3];
+ remainGraph.push(deepCopyGraph(graph));
+ this.nodes[nonessentialNode[i].id].mustVisit = false;
+ }
+ let index: number = shortestDistance.reduce((minIndex, currentValue, currentIndex, array) => {
+ return currentValue < array[minIndex] ? currentIndex : minIndex;
+ }, 0);
+ this.graph = deepCopyGraph(remainGraph[index]);
+ let endTime: number = Date.now();
+ this.elapsedTime = (endTime - startTime) * 1e-3; // 转换为秒
+ return [pathList[index], shortestDistance[index], this.elapsedTime, this.numberOfPasses];
+ }
+
+ graphCheckConnectivity(node1: Node, node2: Node): [boolean, number, number] {
+ if (this.nodes && this.edges && this.nodes.length != 0 && this.edges.length != 0) {
+ let find: number[] = [];
+ for (const node of this.nodes) {
+ find.push(node.id);
+ }
+ for (const edge of this.edges) {
+ if (edge.visible) {
+ let x: number = edge.node1;
+ let y: number = edge.node2;
+ while (x != find[x]) x = find[x];
+ while (y != find[y]) y = find[y];
+ if (x < y) find[y] = x;
+ else find[x] = y;
+ }
+ }
+ let x: number = node1.id;
+ let y: number = node2.id;
+ while (x != find[x]) x = find[x];
+ while (y != find[y]) y = find[y];
+ if (find[x] == find[y]) return [true, x, y];
+ else return [false, x, y];
+ } else {
+ try {
+ throw new Error("当前图无节点或无边");
+ } catch {
+ showToast("当前图无节点或无边");
+ return [false, -1, -1];
+ }
+ }
+ }
+
+ specificPathCost(path: Edge[], isDistance: boolean = true): number {
+ let minValue: number = 0;
+ if (!isDistance) {
+ for (const pathEle of path) {
+ minValue += pathEle.time;
+ }
+ return minValue;
+ } else {
+ for (const pathEle of path) {
+ minValue += pathEle.distance;
+ }
+ return minValue;
+ }
+ }
+
+ checkUserBest(path: Edge[], startNodeId: number = 0, isDistance: boolean = true): boolean {
+ let revalue: [number[][], number, number, number] = this.solveTsp(startNodeId, this.traversalSearchToPath);
+ let minPath: number[] = revalue[0][0];
+ if (path.length + 1 != minPath.length) return false;
+ let minValue: number = revalue[1];
+ let userMinValue = this.specificPathCost(path, isDistance);
+ if (minValue == userMinValue) return true;
+ else return false;
+ }
+
+ NodeIdsPathToEdgesPath(path: number[]): Edge[] {
+ let paths: Edge[] = [];
+ let len: number = path.length;
+ for (let i = 0; i < len - 1; i++) {
+ for (const edge of this.edges) {
+ if ((edge.node1 == path[i] && edge.node2 == path[i + 1]) || (edge.node1 == path[i + 1] && edge.node2 == path[i])) {
+ paths.push(edge);
+ break;
+ }
+ }
+ }
+ return paths;
+ }
+
+ EdgesPathToNodeIdsPath(path: Edge[], startNodeId: number = 0): number[] {
+ let paths: number[] = [startNodeId];
+ let nextNodeId: number = paths[0];
+ for (const pathEle of path) {
+ let nodeIds: number[] = [pathEle.node1, pathEle.node2];
+ if (nodeIds.includes(nextNodeId)) {
+ nextNodeId = nodeIds.find(id => id != nextNodeId) as number;
+ paths.push(nextNodeId);
+ }
+ }
+ return paths;
+ }
+
+ checkIsGreedy(path: Edge[], startNodeId: number = 0) {
+ let revalue: [number[][], number, number, number] = this.solveTsp(startNodeId, this.greedySearchToPath);
+ let greedyPath: number[] = revalue[0][0];
+ if (path.length + 1 != greedyPath.length) return false;
+ let userPath: number[] = this.EdgesPathToNodeIdsPath(path, startNodeId);
+ let len: number = userPath.length;
+ for (let i = 0; i < len; i++) {
+ if (userPath[i] != greedyPath[i]) {
+ try {
+ throw new Error(`正确的贪心节点顺序为${greedyPath.slice(0, -1)}\n你的第${i + 1}个节点${this.nodes[userPath[i]].label}不符合贪心规则。`);
+ } catch {
+ showToast(`正确的贪心节点顺序为${greedyPath.slice(0, -1)}\n你的第${i + 1}个节点${this.nodes[userPath[i]].label}不符合贪心规则。`);
+ return false;
+ }
+ };
+ }
+ return true;
+ }
+
+ shortestPathAlgorithm(startNodeId: number, endNodeId: number, isDistance: boolean = true): [number[], number] {
+ this.graph = this.createGraph(this.nodes, isDistance);
+ let distances: Record = {}; // 记录从起始节点到每个节点的最短距离
+ let previous: Record = {}; // 记录最短路径中每个节点的前驱节点
+ let visited: Set = new Set(); // 存储已经处理过的节点
+ let priorityQueue: Array = []; // 优先队列,用于处理待访问的节点,按照距离排序
+
+ for (const nodeId of Object.keys(this.graph)) {
+ distances[nodeId] = Number.POSITIVE_INFINITY;
+ previous[nodeId] = -1;
+ }
+
+ distances[startNodeId] = 0;
+ priorityQueue.push({ to: startNodeId, weight: 0 });
+ while (priorityQueue.length > 0) {
+ priorityQueue.sort((a, b) => a.weight - b.weight); // 维护优先队列,保证每次队列中距离最小的节点在最前面
+ let currentNodeId = priorityQueue.shift()!.to;
+
+ if (currentNodeId === endNodeId) break;
+ if (visited.has(currentNodeId)) continue;
+ visited.add(currentNodeId);
+
+ for (const neighbor of this.graph[currentNodeId]) {
+ const neighborNodeId: number = neighbor.to;
+ const weight: number = neighbor.weight;
+ let newDist: number = distances[currentNodeId] + weight;
+
+ if (newDist < distances[neighborNodeId]) {
+ distances[neighborNodeId] = newDist;
+ previous[neighborNodeId] = currentNodeId;
+ priorityQueue.push({ to: neighborNodeId, weight: newDist });
+ }
+ }
+ }
+ let path: number[] = [];
+ let currentNodeId: number = endNodeId;
+ while (currentNodeId != -1) {
+ path.unshift(currentNodeId);
+ currentNodeId = previous[currentNodeId];
+ }
+ return [path, distances[endNodeId]];
+ }
+}
+
+export let cp: CustomPath = new CustomPath([], 0, 0, 0, {}, [], []);
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/createCityInfo.ets b/drumming/drummer/entry/src/main/ets/utils/functions/createCityInfo.ets
new file mode 100644
index 0000000..5b519b7
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/createCityInfo.ets
@@ -0,0 +1,110 @@
+import { CITY } from '../../common/CommonConstant';
+import { CityInfo } from '../../common/dataStruct/interface/CityInfo';
+import { Logger } from '../Logger';
+import { getRealData } from './getRealData';
+
+export async function createCityInfo(): Promise {
+ //TODO 可注释掉第87、88行 再将第6行至85行注释打开 即可在指定的预览器参数下模拟程序
+ // let res: [string, string, number, number][] = [
+ // ["广州市", "深圳市", 123012, 10838],
+ // ["广州市", "珠海市", 118916, 9380],
+ // ["广州市", "汕头市", 408198, 20355],
+ // ["广州市", "佛山市", 24721, 4042],
+ // ["广州市", "韶关市", 214580, 10298],
+ // ["广州市", "湛江市", 412296, 17672],
+ // ["广州市", "肇庆市", 95526, 6740],
+ // ["广州市", "江门市", 79764, 6810],
+ // ["广州市", "茂名市", 330124, 16845],
+ // ["广州市", "惠州市", 132169, 10257],
+ // ["广州市", "梅州市", 375039, 23471],
+ // ["广州市", "汕尾市", 250695, 22951],
+ // ["深圳市", "珠海市", 96524, 6856],
+ // ["深圳市", "汕头市", 327090, 16705],
+ // ["深圳市", "佛山市", 131396, 7894],
+ // ["深圳市", "韶关市", 314989, 14934],
+ // ["深圳市", "湛江市", 458270, 17171],
+ // ["深圳市", "肇庆市", 207397, 12600],
+ // ["深圳市", "江门市", 117755, 7185],
+ // ["深圳市", "茂名市", 379777, 14772],
+ // ["深圳市", "惠州市", 85900, 8126],
+ // ["深圳市", "梅州市", 333237, 19392],
+ // ["深圳市", "汕尾市", 160776, 10851],
+ // ["珠海市", "汕头市", 414804, 20789],
+ // ["珠海市", "佛山市", 115634, 8714],
+ // ["珠海市", "韶关市", 330794, 18384],
+ // ["珠海市", "湛江市", 399887, 17975],
+ // ["珠海市", "肇庆市", 170341, 10310],
+ // ["珠海市", "江门市", 74683, 6582],
+ // ["珠海市", "茂名市", 321394, 15731],
+ // ["珠海市", "惠州市", 166006, 16927],
+ // ["珠海市", "梅州市", 418175, 22730],
+ // ["珠海市", "汕尾市", 248490, 14697],
+ // ["汕头市", "佛山市", 428134, 21849],
+ // ["汕头市", "韶关市", 439125, 28337],
+ // ["汕头市", "湛江市", 777628, 31482],
+ // ["汕头市", "肇庆市", 503503, 19173],
+ // ["汕头市", "江门市", 437113, 21891],
+ // ["汕头市", "茂名市", 699135, 28654],
+ // ["汕头市", "惠州市", 281852, 13534],
+ // ["汕头市", "梅州市", 150580, 10931],
+ // ["汕头市", "汕尾市", 178082, 9358],
+ // ["佛山市", "韶关市", 236498, 14079],
+ // ["佛山市", "湛江市", 387931, 16579],
+ // ["佛山市", "肇庆市", 83082, 6325],
+ // ["佛山市", "江门市", 57582, 5669],
+ // ["佛山市", "茂名市", 306652, 14224],
+ // ["佛山市", "惠州市", 152376, 11891],
+ // ["佛山市", "梅州市", 395246, 25040],
+ // ["佛山市", "汕尾市", 270551, 21935],
+ // ["韶关市", "湛江市", 597469, 29973],
+ // ["韶关市", "肇庆市", 266051, 13269],
+ // ["韶关市", "江门市", 297901, 15143],
+ // ["韶关市", "茂名市", 512263, 25970],
+ // ["韶关市", "惠州市", 241728, 12976],
+ // ["韶关市", "梅州市", 351402, 17854],
+ // ["韶关市", "汕尾市", 353072, 17957],
+ // ["湛江市", "肇庆市", 337242, 17455],
+ // ["湛江市", "江门市", 352004, 13773],
+ // ["湛江市", "茂名市", 88410, 5807],
+ // ["湛江市", "惠州市", 525222, 21359],
+ // ["湛江市", "梅州市", 772108, 35020],
+ // ["湛江市", "汕尾市", 611969, 26177],
+ // ["肇庆市", "江门市", 96870, 6183],
+ // ["肇庆市", "茂名市", 250335, 13916],
+ // ["肇庆市", "惠州市", 227448, 15215],
+ // ["肇庆市", "梅州市", 470318, 28141],
+ // ["肇庆市", "汕尾市", 347288, 23119],
+ // ["江门市", "茂名市", 273884, 11126],
+ // ["江门市", "惠州市", 175884, 14701],
+ // ["江门市", "梅州市", 425305, 22731],
+ // ["江门市", "汕尾市", 271930, 18071],
+ // ["茂名市", "惠州市", 446878, 18932],
+ // ["茂名市", "梅州市", 693764, 33390],
+ // ["茂名市", "汕尾市", 533625, 25063],
+ // ["惠州市", "梅州市", 253720, 11521],
+ // ["惠州市", "汕尾市", 121252, 8521],
+ // ["梅州市", "汕尾市", 216441, 11933]
+ // ]
+
+ let res: [string, string, number, number][] = await getRealData();
+ Logger.i(JSON.stringify(res));
+
+ let cityName: string[] = CITY.slice();
+ let cityInfo: CityInfo = {};
+ cityName.forEach((city) => {
+ cityInfo[city] = {};
+ })
+ res.forEach((urbanTransitionInfo) => {
+ cityInfo[urbanTransitionInfo[0]][urbanTransitionInfo[1]] = { distance: urbanTransitionInfo[2], time: urbanTransitionInfo[3] };
+ if (!cityInfo[urbanTransitionInfo[1]][urbanTransitionInfo[0]]) cityInfo[urbanTransitionInfo[1]][urbanTransitionInfo[0]] = { distance: urbanTransitionInfo[2], time: urbanTransitionInfo[3] };
+ })
+
+ // console.log(Object.keys(cityInfo).toString())
+ // for (const i of Object.keys(cityInfo)) {
+ // console.log(Object.keys(cityInfo[i]).toString())
+ // for (const j of Object.keys(cityInfo[i])) {
+ // console.log(`${i}->${j}: 距离为${cityInfo[i][j].distance},时间为${cityInfo[i][j].time}`);
+ // }
+ // }
+ return cityInfo;
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/deepCopyGraph.ets b/drumming/drummer/entry/src/main/ets/utils/functions/deepCopyGraph.ets
new file mode 100644
index 0000000..a6b1549
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/deepCopyGraph.ets
@@ -0,0 +1,17 @@
+import { Graph, nodeInfo } from '../../common/dataStruct/interface/Graph';
+
+export function deepCopyGraph(graph: Graph): Graph {
+ const newGraph: Graph = {};
+
+ for (const nodeId of Object.keys(graph)) {
+ // 深拷贝每个 nodeInfo 数组
+ newGraph[nodeId] = graph[nodeId].map((node: nodeInfo) => {
+ let nodeMessage: nodeInfo ={
+ to: node.to,
+ weight: node.weight
+ };
+ return nodeMessage;
+ });
+ }
+ return newGraph;
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/degreesToRadians.ets b/drumming/drummer/entry/src/main/ets/utils/functions/degreesToRadians.ets
new file mode 100644
index 0000000..c8c0a94
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/degreesToRadians.ets
@@ -0,0 +1,3 @@
+export function degreesToRadians(degrees: number): number {
+ return degrees * Math.PI / 180;
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/drawEdge.ets b/drumming/drummer/entry/src/main/ets/utils/functions/drawEdge.ets
new file mode 100644
index 0000000..ccd20d2
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/drawEdge.ets
@@ -0,0 +1,33 @@
+import { DRAW_NODE_SIZE, NORMAL_CONTEXT, SELECTED_CONTEXT } from '../../common/CommonConstant'
+import Edge from '../../common/dataStruct/class/Edge'
+import { cp } from '../algorithm/customPath'
+import { Logger } from '../Logger'
+
+export let drawEdge = (edge:Edge,isSelectedPath:boolean)=>{
+ let context = isSelectedPath?SELECTED_CONTEXT:NORMAL_CONTEXT
+
+ if(isSelectedPath){
+ if(edge.marked){
+ context.moveTo(cp.nodes[edge.node1].coordinates[0] + DRAW_NODE_SIZE/2,cp.nodes[edge.node1].coordinates[1] + DRAW_NODE_SIZE/2)
+ context.lineTo(cp.nodes[edge.node2].coordinates[0] + DRAW_NODE_SIZE/2,cp.nodes[edge.node2].coordinates[1] + DRAW_NODE_SIZE/2)
+ context.stroke()
+ }
+ }else{
+ if(edge.visible){
+ context.moveTo(cp.nodes[edge.node1].coordinates[0] + DRAW_NODE_SIZE/2,cp.nodes[edge.node1].coordinates[1] + DRAW_NODE_SIZE/2)
+ context.lineTo(cp.nodes[edge.node2].coordinates[0] + DRAW_NODE_SIZE/2,cp.nodes[edge.node2].coordinates[1] + DRAW_NODE_SIZE/2)
+ context.stroke()
+ }
+ }
+
+ context.stroke()
+}
+export let drawEdgeList = (edgeList:Array,isSelectedPath:boolean)=>{
+ if(edgeList.length == 0){
+ return
+ }
+ for(let index = 0;index < edgeList.length;index++){
+ drawEdge(edgeList[index],isSelectedPath)
+ }
+}
+
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/drawNode.ets b/drumming/drummer/entry/src/main/ets/utils/functions/drawNode.ets
new file mode 100644
index 0000000..f185e2b
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/drawNode.ets
@@ -0,0 +1,40 @@
+import Node from '../../common/dataStruct/class/Node'
+import { BOTTOM_RIGHT_X, BOTTOM_RIGHT_Y, DRAW_NODE_SIZE, TOP_LEFT_X, TOP_LEFT_Y,
+ X_CENTER,
+ Y_CENTER
+} from '../../common/CommonConstant'
+
+export let drawNode = (node:Node,context:CanvasRenderingContext2D)=>{
+
+
+ if(!node.mustVisit){
+ context.fillRect(node.coordinates[0],node.coordinates[1],DRAW_NODE_SIZE,DRAW_NODE_SIZE)
+ }else{
+ context.strokeRect(node.coordinates[0],node.coordinates[1],DRAW_NODE_SIZE,DRAW_NODE_SIZE)
+ }
+ let bias = getTextBiasValue(node)
+ context.fillText(node.label,node.coordinates[0] + DRAW_NODE_SIZE/2 + bias[0],node.coordinates[1] + DRAW_NODE_SIZE/2 + bias[1])
+
+ context.stroke()
+}
+
+export let drawNodeList = (nodeList:Array,context:CanvasRenderingContext2D)=>{
+ if(nodeList.length == 0){
+ return
+ }
+ for (let index = 0; index < nodeList.length; index++) {
+ drawNode(nodeList[index],context)
+ }
+}
+
+
+let getTextBiasValue = (node:Node)=>{
+ let bias:[number,number] = [0,0]
+ if(node.coordinates[0] < X_CENTER){
+ bias[0] = -40
+ }
+ if(node.coordinates[1] > Y_CENTER){
+ bias[1] = 25
+ }
+ return bias
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/getRealData.ets b/drumming/drummer/entry/src/main/ets/utils/functions/getRealData.ets
new file mode 100644
index 0000000..8c13983
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/getRealData.ets
@@ -0,0 +1,147 @@
+import { CITY, KEY } from '../../common/CommonConstant'
+import http from '@ohos.net.http'
+import { Logger } from '../Logger'
+import fs from '@ohos.file.fs';
+import picker from '@ohos.file.picker';
+import { BusinessError } from '@ohos.base';
+import process from '@ohos.process';
+
+import { showToast } from './showToast';
+import { Data } from '../../common/dataStruct/class/Data';
+
+export let getRealData = async ()=>{
+ let res:Array<[string,string,number,number]> = []
+ let loop:boolean = AppStorage.get("isOpFile") as boolean
+ if(loop){
+ res = await getRealDataFromFile()
+ }else{
+ let str = ""
+ for(let i = 0;i < CITY.length;i++){
+ for(let j = i + 1;j < CITY.length;j++){
+ let tmp = await getRealDataFromMap(CITY[i],CITY[j])
+ res.push(tmp)
+ str += `${tmp[0]} ${tmp[1]} ${tmp[2]} ${tmp[3]}`
+ str += '\n'
+
+ }
+ }
+ await writeDocument(str)
+ }
+ return res;
+}
+
+
+/**
+ * 从地图获取真实数据
+ */
+let getRealDataFromMap = async (cityName1:string,cityName2:string)=>{
+
+ let res:[string,string,number,number] = ["","",0,0]
+ //获取经纬度
+ let getLocation1_url = `https://restapi.amap.com/v3/geocode/geo?address=${cityName1}&key=${KEY}`
+ let getLocation2_url = `https://restapi.amap.com/v3/geocode/geo?address=${cityName2}&key=${KEY}`
+ let loc1 = await getLoc(getLocation1_url)
+ let loc2 = await getLoc(getLocation2_url)
+ //获取最短路径
+ let getPath_url = `https://restapi.amap.com/v3/direction/driving?origin=${loc1}&destination=${loc2}&strategy=2&key=${KEY}`
+ let path = await getPath(getPath_url)
+
+ res[0] = cityName1
+ res[1] = cityName2
+ res[2] = path[0]
+ res[3] = path[1]
+ Logger.i(JSON.stringify(res))
+ return res
+}
+
+/**
+ * 从文件获取真实数据
+ */
+let getRealDataFromFile = async ()=>{
+ let dataList:Array<[string,string,number,number]> = []
+ //读取文件
+ let res = await readDocument(dataList)
+ if(!res){
+ setTimeout(()=>{
+ showToast("数据初始化异常,将自动关闭程序")
+ setTimeout(()=>{
+ let pro = new process.ProcessManager();
+ pro.exit(0);
+ },2000)
+ },2000)
+ }
+ return dataList
+}
+
+let getLoc = async (url:string)=>{
+ try {
+ let h = http.createHttp()
+ let res = await h.request(url)
+
+ //JSON
+ let json = res.result as string
+ let i = json.indexOf("location")
+ let loc = json.slice(i + 11,i+31)
+ return loc;
+ } catch (e) {
+ Logger.e(JSON.stringify(e))
+ return ""
+ }
+}
+
+let getPath = async (url:string)=>{
+ let h = http.createHttp()
+ let res = await h.request(url)
+ //JSON
+ let json = res.result as string
+ let obj:Data = JSON.parse(json)
+ let loc:[number,number] = [Number(obj.route.paths[0].distance),Number(obj.route.paths[0].duration)]
+ return loc;
+}
+
+let writeDocument = async (str:string)=>{
+ let documentPicker = new picker.DocumentViewPicker();
+ let uris:Array = []
+ let DocumentSaveOptions = new picker.DocumentSaveOptions();
+ DocumentSaveOptions.newFileNames = ['cityData.txt']
+ uris = await documentPicker.save(DocumentSaveOptions)
+ let uri = uris[0]
+ let file = fs.openSync(uri, fs.OpenMode.WRITE_ONLY);
+ fs.writeSync(file.fd,"THIS IS A TAG\n")
+ let writeStr = str
+ fs.writeSync(file.fd, writeStr);
+ fs.closeSync(file);
+}
+
+let readDocument = async (dataList:Array<[string,string,number,number]>)=>{
+ let documentPicker = new picker.DocumentViewPicker()
+ let DocumentSelectOptions = new picker.DocumentSelectOptions();
+ let DocumentSelectResult: Array = await documentPicker.select(DocumentSelectOptions)
+ if(DocumentSelectResult.length == 0){
+ showToast("您未选择任何文件!将无法获取到数据")
+ return false
+ }
+ let uri = DocumentSelectResult[0]
+ let file = fs.openSync(uri,fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
+ //已经获取到选择的文件 进行读取操作
+ let arrayBuffer = new ArrayBuffer(8)
+ let size = 0
+ let dSize = 0
+ let str = fs.readTextSync(file.path)
+ //解析路径
+ fs.closeSync(file)
+
+ //解析文件样本点
+ let arr = str.split('\n')
+ if(arr[0] != "THIS IS A TAG"){
+ showToast("读取文件失败,请确认文件是否选择正确或是否损坏")
+ return false
+ }
+ for (let index = 1; index < arr.length - 1; index++) {
+ let res = arr[index].split(' ')
+
+ let item:[string,string,number,number] = [res[0],res[1],Number(res[2]),Number(res[3])]
+ dataList.push(item)
+ }
+ return true
+}
diff --git a/drumming/drummer/entry/src/main/ets/utils/functions/showToast.ets b/drumming/drummer/entry/src/main/ets/utils/functions/showToast.ets
new file mode 100644
index 0000000..e41cc5b
--- /dev/null
+++ b/drumming/drummer/entry/src/main/ets/utils/functions/showToast.ets
@@ -0,0 +1,8 @@
+import promptAction from '@ohos.promptAction'
+
+export let showToast = (message:string,duration:number = 2000)=>{
+ promptAction.showToast({
+ message:message,
+ duration:duration
+ })
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/module.json5 b/drumming/drummer/entry/src/main/module.json5
new file mode 100644
index 0000000..a4ce4f2
--- /dev/null
+++ b/drumming/drummer/entry/src/main/module.json5
@@ -0,0 +1,49 @@
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.INTERNET"
+ },
+ {
+ "name": "ohos.permission.FILE_ACCESS_MANAGER"
+ },
+ {
+ "name": "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED"
+ }
+ ],
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ "abilities": [
+ {
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ets",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:startIcon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
+ "orientation": "landscape",
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/resources/base/element/color.json b/drumming/drummer/entry/src/main/resources/base/element/color.json
new file mode 100644
index 0000000..3c71296
--- /dev/null
+++ b/drumming/drummer/entry/src/main/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/resources/base/element/string.json b/drumming/drummer/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000..f945955
--- /dev/null
+++ b/drumming/drummer/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/resources/base/media/icon.png b/drumming/drummer/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 0000000..cd45acc
Binary files /dev/null and b/drumming/drummer/entry/src/main/resources/base/media/icon.png differ
diff --git a/drumming/drummer/entry/src/main/resources/base/media/startIcon.png b/drumming/drummer/entry/src/main/resources/base/media/startIcon.png
new file mode 100644
index 0000000..366f764
Binary files /dev/null and b/drumming/drummer/entry/src/main/resources/base/media/startIcon.png differ
diff --git a/drumming/drummer/entry/src/main/resources/base/profile/main_pages.json b/drumming/drummer/entry/src/main/resources/base/profile/main_pages.json
new file mode 100644
index 0000000..55c3f00
--- /dev/null
+++ b/drumming/drummer/entry/src/main/resources/base/profile/main_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "pages/Index"
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/resources/en_US/element/string.json b/drumming/drummer/entry/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000..f945955
--- /dev/null
+++ b/drumming/drummer/entry/src/main/resources/en_US/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/main/resources/zh_CN/element/string.json b/drumming/drummer/entry/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000..597ecf9
--- /dev/null
+++ b/drumming/drummer/entry/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "模块描述"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/ets/test/Ability.test.ets b/drumming/drummer/entry/src/ohosTest/ets/test/Ability.test.ets
new file mode 100644
index 0000000..8aa3749
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/ets/test/Ability.test.ets
@@ -0,0 +1,35 @@
+import hilog from '@ohos.hilog';
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function abilityTest() {
+ describe('ActsAbilityTest', () => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ })
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ })
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ })
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ })
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
+ let a = 'abc';
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
+ })
+ })
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/ets/test/List.test.ets b/drumming/drummer/entry/src/ohosTest/ets/test/List.test.ets
new file mode 100644
index 0000000..794c7dc
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/ets/test/List.test.ets
@@ -0,0 +1,5 @@
+import abilityTest from './Ability.test';
+
+export default function testsuite() {
+ abilityTest();
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/ets/testability/TestAbility.ets b/drumming/drummer/entry/src/ohosTest/ets/testability/TestAbility.ets
new file mode 100644
index 0000000..9484761
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/ets/testability/TestAbility.ets
@@ -0,0 +1,50 @@
+import UIAbility from '@ohos.app.ability.UIAbility';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import hilog from '@ohos.hilog';
+import { Hypium } from '@ohos/hypium';
+import testsuite from '../test/List.test';
+import window from '@ohos.window';
+import Want from '@ohos.app.ability.Want';
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+
+export default class TestAbility extends UIAbility {
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
+ let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator;
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs;
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments();
+ hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
+ Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite);
+ }
+
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
+ windowStage.loadContent('testability/pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
+ JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
+ }
+
+ onForeground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
+ }
+
+ onBackground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/ets/testability/pages/Index.ets b/drumming/drummer/entry/src/ohosTest/ets/testability/pages/Index.ets
new file mode 100644
index 0000000..423b427
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/ets/testability/pages/Index.ets
@@ -0,0 +1,17 @@
+@Entry
+@Component
+struct Index {
+ @State message: string = 'Hello World';
+
+ build() {
+ Row() {
+ Column() {
+ Text(this.message)
+ .fontSize(50)
+ .fontWeight(FontWeight.Bold)
+ }
+ .width('100%')
+ }
+ .height('100%')
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets b/drumming/drummer/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
new file mode 100644
index 0000000..917d27a
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
@@ -0,0 +1,47 @@
+import hilog from '@ohos.hilog';
+import TestRunner from '@ohos.application.testRunner';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import Want from '@ohos.app.ability.Want';
+
+let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator | undefined = undefined
+let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs | undefined = undefined
+
+async function onAbilityCreateCallback() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
+}
+
+async function addAbilityMonitorCallback(err : Error) {
+ hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
+}
+
+export default class OpenHarmonyTestRunner implements TestRunner {
+ constructor() {
+ }
+
+ onPrepare() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
+ }
+
+ async onRun() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
+ const bundleName = abilityDelegatorArguments.bundleName;
+ const testAbilityName = 'TestAbility';
+ let lMonitor: AbilityDelegatorRegistry.AbilityMonitor = {
+ abilityName: testAbilityName,
+ onAbilityCreate: onAbilityCreateCallback,
+ };
+ abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
+ const want: Want = {
+ bundleName: bundleName,
+ abilityName: testAbilityName
+ };
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ abilityDelegator.startAbility(want, (err, data) => {
+ hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
+ hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? '');
+ })
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/module.json5 b/drumming/drummer/entry/src/ohosTest/module.json5
new file mode 100644
index 0000000..4fc9701
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/module.json5
@@ -0,0 +1,37 @@
+{
+ "module": {
+ "name": "entry_test",
+ "type": "feature",
+ "description": "$string:module_test_desc",
+ "mainElement": "TestAbility",
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:test_pages",
+ "abilities": [
+ {
+ "name": "TestAbility",
+ "srcEntry": "./ets/testability/TestAbility.ets",
+ "description": "$string:TestAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:TestAbility_label",
+ "exported": true,
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "skills": [
+ {
+ "actions": [
+ "action.system.home"
+ ],
+ "entities": [
+ "entity.system.home"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/drumming/drummer/entry/src/ohosTest/resources/base/element/color.json b/drumming/drummer/entry/src/ohosTest/resources/base/element/color.json
new file mode 100644
index 0000000..3c71296
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/resources/base/element/string.json b/drumming/drummer/entry/src/ohosTest/resources/base/element/string.json
new file mode 100644
index 0000000..65d8fa5
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_test_desc",
+ "value": "test ability description"
+ },
+ {
+ "name": "TestAbility_desc",
+ "value": "the test ability"
+ },
+ {
+ "name": "TestAbility_label",
+ "value": "test label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/ohosTest/resources/base/media/icon.png b/drumming/drummer/entry/src/ohosTest/resources/base/media/icon.png
new file mode 100644
index 0000000..ce307a8
Binary files /dev/null and b/drumming/drummer/entry/src/ohosTest/resources/base/media/icon.png differ
diff --git a/drumming/drummer/entry/src/ohosTest/resources/base/profile/test_pages.json b/drumming/drummer/entry/src/ohosTest/resources/base/profile/test_pages.json
new file mode 100644
index 0000000..b7e7343
--- /dev/null
+++ b/drumming/drummer/entry/src/ohosTest/resources/base/profile/test_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "testability/pages/Index"
+ ]
+}
diff --git a/drumming/drummer/entry/src/test/List.test.ets b/drumming/drummer/entry/src/test/List.test.ets
new file mode 100644
index 0000000..bb5b5c3
--- /dev/null
+++ b/drumming/drummer/entry/src/test/List.test.ets
@@ -0,0 +1,5 @@
+import localUnitTest from './LocalUnit.test';
+
+export default function testsuite() {
+ localUnitTest();
+}
\ No newline at end of file
diff --git a/drumming/drummer/entry/src/test/LocalUnit.test.ets b/drumming/drummer/entry/src/test/LocalUnit.test.ets
new file mode 100644
index 0000000..ed22d4d
--- /dev/null
+++ b/drumming/drummer/entry/src/test/LocalUnit.test.ets
@@ -0,0 +1,33 @@
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function localUnitTest() {
+ describe('localUnitTest',() => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ let a = 'abc';
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
+ });
+ });
+}
\ No newline at end of file
diff --git a/drumming/drummer/hvigor/hvigor-config.json5 b/drumming/drummer/hvigor/hvigor-config.json5
new file mode 100644
index 0000000..1a47490
--- /dev/null
+++ b/drumming/drummer/hvigor/hvigor-config.json5
@@ -0,0 +1,18 @@
+{
+ "hvigorVersion": "3.0.9",
+ "dependencies": {
+ "@ohos/hvigor-ohos-plugin": "3.0.9"
+ },
+ "execution": {
+ // "daemon": true, /* Enable daemon compilation. Default: true */
+ // "incremental": true, /* Enable incremental compilation. Default: true */
+ // "parallel": true, /* Enable parallel compilation. Default: true */
+ // "typeCheck": false, /* Enable typeCheck. Default: false */
+ },
+ "logging": {
+ // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
+ },
+ "debugging": {
+ // "stacktrace": false /* Disable stacktrace compilation. Default: false */
+ }
+}
diff --git a/drumming/drummer/hvigor/hvigor-wrapper.js b/drumming/drummer/hvigor/hvigor-wrapper.js
new file mode 100644
index 0000000..372eae8
--- /dev/null
+++ b/drumming/drummer/hvigor/hvigor-wrapper.js
@@ -0,0 +1 @@
+"use strict";var u=require("path"),D=require("os"),e=require("fs"),t=require("crypto"),r=require("child_process"),n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},i={},C={},F=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(C,"__esModule",{value:!0}),C.maxPathLength=C.isMac=C.isLinux=C.isWindows=void 0;const E=F(D),A="Windows_NT",o="Darwin";function a(){return E.default.type()===A}function c(){return E.default.type()===o}C.isWindows=a,C.isLinux=function(){return"Linux"===E.default.type()},C.isMac=c,C.maxPathLength=function(){return c()?1016:a()?259:4095},function(e){var t=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),r=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),i=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&t(D,u,e);return r(D,u),D};Object.defineProperty(e,"__esModule",{value:!0}),e.WORK_SPACE=e.HVIGOR_PROJECT_WRAPPER_HOME=e.HVIGOR_PROJECT_ROOT_DIR=e.HVIGOR_PROJECT_CACHES_HOME=e.HVIGOR_PNPM_STORE_PATH=e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=e.PROJECT_CACHES=e.HVIGOR_WRAPPER_TOOLS_HOME=e.HVIGOR_USER_HOME=e.DEFAULT_PACKAGE_JSON=e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME=e.PNPM=e.HVIGOR=e.NPM_TOOL=e.PNPM_TOOL=e.HVIGOR_ENGINE_PACKAGE_NAME=void 0;const F=i(D),E=i(u),A=C;e.HVIGOR_ENGINE_PACKAGE_NAME="@ohos/hvigor",e.PNPM_TOOL=(0,A.isWindows)()?"pnpm.cmd":"pnpm",e.NPM_TOOL=(0,A.isWindows)()?"npm.cmd":"npm",e.HVIGOR="hvigor",e.PNPM="pnpm",e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME="hvigor-config.json5",e.DEFAULT_PACKAGE_JSON="package.json",e.HVIGOR_USER_HOME=E.resolve(F.homedir(),".hvigor"),e.HVIGOR_WRAPPER_TOOLS_HOME=E.resolve(e.HVIGOR_USER_HOME,"wrapper","tools"),e.PROJECT_CACHES="project_caches",e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=E.resolve(e.HVIGOR_WRAPPER_TOOLS_HOME,"node_modules",".bin",e.PNPM_TOOL),e.HVIGOR_PNPM_STORE_PATH=E.resolve(e.HVIGOR_USER_HOME,"caches"),e.HVIGOR_PROJECT_CACHES_HOME=E.resolve(e.HVIGOR_USER_HOME,e.PROJECT_CACHES),e.HVIGOR_PROJECT_ROOT_DIR=process.cwd(),e.HVIGOR_PROJECT_WRAPPER_HOME=E.resolve(e.HVIGOR_PROJECT_ROOT_DIR,e.HVIGOR),e.WORK_SPACE="workspace"}(i);var s={},l={};Object.defineProperty(l,"__esModule",{value:!0}),l.logInfoPrintConsole=l.logErrorAndExit=void 0,l.logErrorAndExit=function(u){u instanceof Error?console.error(u.message):console.error(u),process.exit(-1)},l.logInfoPrintConsole=function(u){console.log(u)};var B=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),d=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),f=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&B(D,u,e);return d(D,u),D};Object.defineProperty(s,"__esModule",{value:!0});var _=s.executeBuild=void 0;const p=f(e),O=f(u),h=l;_=s.executeBuild=function(u){const D=O.resolve(u,"node_modules","@ohos","hvigor","bin","hvigor.js");try{const u=p.realpathSync(D);require(u)}catch(e){(0,h.logErrorAndExit)(`Error: ENOENT: no such file ${D},delete ${u} and retry.`)}};var P={},v={};!function(u){var D=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(u,"__esModule",{value:!0}),u.hashFile=u.hash=u.createHash=void 0;const r=D(t),i=D(e);u.createHash=(u="MD5")=>r.default.createHash(u);u.hash=(D,e)=>(0,u.createHash)(e).update(D).digest("hex");u.hashFile=(D,e)=>{if(i.default.existsSync(D))return(0,u.hash)(i.default.readFileSync(D,"utf-8"),e)}}(v);var g={},m={},R={};Object.defineProperty(R,"__esModule",{value:!0}),R.Unicode=void 0;class y{}R.Unicode=y,y.SPACE_SEPARATOR=/[\u1680\u2000-\u200A\u202F\u205F\u3000]/,y.ID_START=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/,y.ID_CONTINUE=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/,Object.defineProperty(m,"__esModule",{value:!0}),m.JudgeUtil=void 0;const I=R;m.JudgeUtil=class{static isIgnoreChar(u){return"string"==typeof u&&("\t"===u||"\v"===u||"\f"===u||" "===u||" "===u||"\ufeff"===u||"\n"===u||"\r"===u||"\u2028"===u||"\u2029"===u)}static isSpaceSeparator(u){return"string"==typeof u&&I.Unicode.SPACE_SEPARATOR.test(u)}static isIdStartChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||"$"===u||"_"===u||I.Unicode.ID_START.test(u))}static isIdContinueChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||u>="0"&&u<="9"||"$"===u||"_"===u||""===u||""===u||I.Unicode.ID_CONTINUE.test(u))}static isDigitWithoutZero(u){return/[1-9]/.test(u)}static isDigit(u){return"string"==typeof u&&/[0-9]/.test(u)}static isHexDigit(u){return"string"==typeof u&&/[0-9A-Fa-f]/.test(u)}};var N=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(g,"__esModule",{value:!0}),g.parseJsonText=g.parseJsonFile=void 0;const b=N(e),S=N(D),w=N(u),H=m;var x;!function(u){u[u.Char=0]="Char",u[u.EOF=1]="EOF",u[u.Identifier=2]="Identifier"}(x||(x={}));let M,T,V,G,j,J,W="start",U=[],L=0,$=1,k=0,K=!1,z="default",q="'",Z=1;function X(u,D=!1){T=String(u),W="start",U=[],L=0,$=1,k=0,G=void 0,K=D;do{M=Q(),nu[W]()}while("eof"!==M.type);return G}function Q(){for(z="default",j="",q="'",Z=1;;){J=Y();const u=Du[z]();if(u)return u}}function Y(){if(T[L])return String.fromCodePoint(T.codePointAt(L))}function uu(){const u=Y();return"\n"===u?($++,k=0):u?k+=u.length:k++,u&&(L+=u.length),u}g.parseJsonFile=function(u,D=!1,e="utf-8"){const t=b.default.readFileSync(w.default.resolve(u),{encoding:e});try{return X(t,D)}catch(D){if(D instanceof SyntaxError){const e=D.message.split("at");if(2===e.length)throw new Error(`${e[0].trim()}${S.default.EOL}\t at ${u}:${e[1].trim()}`)}throw new Error(`${u} is not in valid JSON/JSON5 format.`)}},g.parseJsonText=X;const Du={default(){switch(J){case"/":return uu(),void(z="comment");case void 0:return uu(),eu("eof")}if(!H.JudgeUtil.isIgnoreChar(J)&&!H.JudgeUtil.isSpaceSeparator(J))return Du[W]();uu()},start(){z="value"},beforePropertyName(){switch(J){case"$":case"_":return j=uu(),void(z="identifierName");case"\\":return uu(),void(z="identifierNameStartEscape");case"}":return eu("punctuator",uu());case'"':case"'":return q=J,uu(),void(z="string")}if(H.JudgeUtil.isIdStartChar(J))return j+=uu(),void(z="identifierName");throw Eu(x.Char,uu())},afterPropertyName(){if(":"===J)return eu("punctuator",uu());throw Eu(x.Char,uu())},beforePropertyValue(){z="value"},afterPropertyValue(){switch(J){case",":case"}":return eu("punctuator",uu())}throw Eu(x.Char,uu())},beforeArrayValue(){if("]"===J)return eu("punctuator",uu());z="value"},afterArrayValue(){switch(J){case",":case"]":return eu("punctuator",uu())}throw Eu(x.Char,uu())},end(){throw Eu(x.Char,uu())},comment(){switch(J){case"*":return uu(),void(z="multiLineComment");case"/":return uu(),void(z="singleLineComment")}throw Eu(x.Char,uu())},multiLineComment(){switch(J){case"*":return uu(),void(z="multiLineCommentAsterisk");case void 0:throw Eu(x.Char,uu())}uu()},multiLineCommentAsterisk(){switch(J){case"*":return void uu();case"/":return uu(),void(z="default");case void 0:throw Eu(x.Char,uu())}uu(),z="multiLineComment"},singleLineComment(){switch(J){case"\n":case"\r":case"\u2028":case"\u2029":return uu(),void(z="default");case void 0:return uu(),eu("eof")}uu()},value(){switch(J){case"{":case"[":return eu("punctuator",uu());case"n":return uu(),tu("ull"),eu("null",null);case"t":return uu(),tu("rue"),eu("boolean",!0);case"f":return uu(),tu("alse"),eu("boolean",!1);case"-":case"+":return"-"===uu()&&(Z=-1),void(z="numerical");case".":case"0":case"I":case"N":return void(z="numerical");case'"':case"'":return q=J,uu(),j="",void(z="string")}if(void 0===J||!H.JudgeUtil.isDigitWithoutZero(J))throw Eu(x.Char,uu());z="numerical"},numerical(){switch(J){case".":return j=uu(),void(z="decimalPointLeading");case"0":return j=uu(),void(z="zero");case"I":return uu(),tu("nfinity"),eu("numeric",Z*(1/0));case"N":return uu(),tu("aN"),eu("numeric",NaN)}if(void 0!==J&&H.JudgeUtil.isDigitWithoutZero(J))return j=uu(),void(z="decimalInteger");throw Eu(x.Char,uu())},zero(){switch(J){case".":case"e":case"E":return void(z="decimal");case"x":case"X":return j+=uu(),void(z="hexadecimal")}return eu("numeric",0)},decimalInteger(){switch(J){case".":case"e":case"E":return void(z="decimal")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimal(){switch(J){case".":j+=uu(),z="decimalFraction";break;case"e":case"E":j+=uu(),z="decimalExponent"}},decimalPointLeading(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalFraction");throw Eu(x.Char,uu())},decimalFraction(){switch(J){case"e":case"E":return j+=uu(),void(z="decimalExponent")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimalExponent(){switch(J){case"+":case"-":return j+=uu(),void(z="decimalExponentSign")}if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Eu(x.Char,uu())},decimalExponentSign(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Eu(x.Char,uu())},decimalExponentInteger(){if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},hexadecimal(){if(H.JudgeUtil.isHexDigit(J))return j+=uu(),void(z="hexadecimalInteger");throw Eu(x.Char,uu())},hexadecimalInteger(){if(!H.JudgeUtil.isHexDigit(J))return eu("numeric",Z*Number(j));j+=uu()},identifierNameStartEscape(){if("u"!==J)throw Eu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":break;default:if(!H.JudgeUtil.isIdStartChar(u))throw Eu(x.Identifier)}j+=u,z="identifierName"},identifierName(){switch(J){case"$":case"_":case"":case"":return void(j+=uu());case"\\":return uu(),void(z="identifierNameEscape")}if(!H.JudgeUtil.isIdContinueChar(J))return eu("identifier",j);j+=uu()},identifierNameEscape(){if("u"!==J)throw Eu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":case"":case"":break;default:if(!H.JudgeUtil.isIdContinueChar(u))throw Eu(x.Identifier)}j+=u,z="identifierName"},string(){switch(J){case"\\":return uu(),void(j+=function(){const u=Y(),D=function(){switch(Y()){case"b":return uu(),"\b";case"f":return uu(),"\f";case"n":return uu(),"\n";case"r":return uu(),"\r";case"t":return uu(),"\t";case"v":return uu(),"\v"}return}();if(D)return D;switch(u){case"0":if(uu(),H.JudgeUtil.isDigit(Y()))throw Eu(x.Char,uu());return"\0";case"x":return uu(),function(){let u="",D=Y();if(!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());if(u+=uu(),D=Y(),!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());return u+=uu(),String.fromCodePoint(parseInt(u,16))}();case"u":return uu(),ru();case"\n":case"\u2028":case"\u2029":return uu(),"";case"\r":return uu(),"\n"===Y()&&uu(),""}if(void 0===u||H.JudgeUtil.isDigitWithoutZero(u))throw Eu(x.Char,uu());return uu()}());case'"':case"'":if(J===q){const u=eu("string",j);return uu(),u}return void(j+=uu());case"\n":case"\r":case void 0:throw Eu(x.Char,uu());case"\u2028":case"\u2029":!function(u){console.warn(`JSON5: '${Fu(u)}' in strings is not valid ECMAScript; consider escaping.`)}(J)}j+=uu()}};function eu(u,D){return{type:u,value:D,line:$,column:k}}function tu(u){for(const D of u){if(Y()!==D)throw Eu(x.Char,uu());uu()}}function ru(){let u="",D=4;for(;D-- >0;){const D=Y();if(!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());u+=uu()}return String.fromCodePoint(parseInt(u,16))}const nu={start(){if("eof"===M.type)throw Eu(x.EOF);iu()},beforePropertyName(){switch(M.type){case"identifier":case"string":return V=M.value,void(W="afterPropertyName");case"punctuator":return void Cu();case"eof":throw Eu(x.EOF)}},afterPropertyName(){if("eof"===M.type)throw Eu(x.EOF);W="beforePropertyValue"},beforePropertyValue(){if("eof"===M.type)throw Eu(x.EOF);iu()},afterPropertyValue(){if("eof"===M.type)throw Eu(x.EOF);switch(M.value){case",":return void(W="beforePropertyName");case"}":Cu()}},beforeArrayValue(){if("eof"===M.type)throw Eu(x.EOF);"punctuator"!==M.type||"]"!==M.value?iu():Cu()},afterArrayValue(){if("eof"===M.type)throw Eu(x.EOF);switch(M.value){case",":return void(W="beforeArrayValue");case"]":Cu()}},end(){}};function iu(){const u=function(){let u;switch(M.type){case"punctuator":switch(M.value){case"{":u={};break;case"[":u=[]}break;case"null":case"boolean":case"numeric":case"string":u=M.value}return u}();if(K&&"object"==typeof u&&(u._line=$,u._column=k),void 0===G)G=u;else{const D=U[U.length-1];Array.isArray(D)?K&&"object"!=typeof u?D.push({value:u,_line:$,_column:k}):D.push(u):D[V]=K&&"object"!=typeof u?{value:u,_line:$,_column:k}:u}!function(u){if(u&&"object"==typeof u)U.push(u),W=Array.isArray(u)?"beforeArrayValue":"beforePropertyName";else{const u=U[U.length-1];W=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}}(u)}function Cu(){U.pop();const u=U[U.length-1];W=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}function Fu(u){const D={"'":"\\'",'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\v":"\\v","\0":"\\0","\u2028":"\\u2028","\u2029":"\\u2029"};if(D[u])return D[u];if(u<" "){const D=u.charCodeAt(0).toString(16);return`\\x${`00${D}`.substring(D.length)}`}return u}function Eu(u,D){let e="";switch(u){case x.Char:e=void 0===D?`JSON5: invalid end of input at ${$}:${k}`:`JSON5: invalid character '${Fu(D)}' at ${$}:${k}`;break;case x.EOF:e=`JSON5: invalid end of input at ${$}:${k}`;break;case x.Identifier:k-=5,e=`JSON5: invalid identifier character at ${$}:${k}`}const t=new Au(e);return t.lineNumber=$,t.columnNumber=k,t}class Au extends SyntaxError{}var ou={},au=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),cu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),su=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&au(D,u,e);return cu(D,u),D},lu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(ou,"__esModule",{value:!0}),ou.isFileExists=ou.offlinePluginConversion=ou.executeCommand=ou.getNpmPath=ou.hasNpmPackInPaths=void 0;const Bu=r,du=lu(e),fu=su(u),_u=i,pu=l;ou.hasNpmPackInPaths=function(u,D){try{return require.resolve(u,{paths:[...D]}),!0}catch(u){return!1}},ou.getNpmPath=function(){const u=process.execPath;return fu.join(fu.dirname(u),_u.NPM_TOOL)},ou.executeCommand=function(u,D,e){0!==(0,Bu.spawnSync)(u,D,e).status&&(0,pu.logErrorAndExit)(`Error: ${u} ${D} execute failed.See above for details.`)},ou.offlinePluginConversion=function(u,D){return D.startsWith("file:")||D.endsWith(".tgz")?fu.resolve(u,_u.HVIGOR,D.replace("file:","")):D},ou.isFileExists=function(u){return du.default.existsSync(u)&&du.default.statSync(u).isFile()};var Ou=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),hu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),Pu=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&Ou(D,u,e);return hu(D,u),D},vu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(P,"__esModule",{value:!0});var gu=P.initProjectWorkSpace=void 0;const mu=Pu(e),Ru=vu(D),yu=Pu(u),Iu=v,Nu=i,bu=g,Su=l,wu=ou;let Hu,xu,Mu;function Tu(u,D,e){return void 0!==e.dependencies&&(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,D.dependencies[u])===yu.normalize(e.dependencies[u])}function Vu(){const u=yu.join(Mu,Nu.WORK_SPACE);if((0,Su.logInfoPrintConsole)("Hvigor cleaning..."),!mu.existsSync(u))return;const D=mu.readdirSync(u);if(!D||0===D.length)return;const e=yu.resolve(Mu,"node_modules","@ohos","hvigor","bin","hvigor.js");mu.existsSync(e)&&(0,wu.executeCommand)(process.argv[0],[e,"--stop-daemon"],{});try{D.forEach((D=>{mu.rmSync(yu.resolve(u,D),{recursive:!0})}))}catch(D){(0,Su.logErrorAndExit)(`The hvigor build tool cannot be installed. Please manually clear the workspace directory and synchronize the project again.\n\n Workspace Path: ${u}.`)}}gu=P.initProjectWorkSpace=function(){if(Hu=function(){const u=yu.resolve(Nu.HVIGOR_PROJECT_WRAPPER_HOME,Nu.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);mu.existsSync(u)||(0,Su.logErrorAndExit)(`Error: Hvigor config file ${u} does not exist.`);return(0,bu.parseJsonFile)(u)}(),Mu=function(u){let D;D=function(u){let D=u.hvigorVersion;if(D.startsWith("file:")||D.endsWith(".tgz"))return!1;const e=u.dependencies,t=Object.getOwnPropertyNames(e);for(const u of t){const D=e[u];if(D.startsWith("file:")||D.endsWith(".tgz"))return!1}if(1===t.length&&"@ohos/hvigor-ohos-plugin"===t[0])return D>"2.5.0";return!1}(u)?function(u){let D=`${Nu.HVIGOR_ENGINE_PACKAGE_NAME}@${u.hvigorVersion}`;const e=u.dependencies;if(e){Object.getOwnPropertyNames(e).sort().forEach((u=>{D+=`,${u}@${e[u]}`}))}return(0,Iu.hash)(D)}(u):(0,Iu.hash)(process.cwd());return yu.resolve(Ru.default.homedir(),".hvigor","project_caches",D)}(Hu),xu=function(){const u=yu.resolve(Mu,Nu.WORK_SPACE,Nu.DEFAULT_PACKAGE_JSON);return mu.existsSync(u)?(0,bu.parseJsonFile)(u):{dependencies:{}}}(),!(0,wu.hasNpmPackInPaths)(Nu.HVIGOR_ENGINE_PACKAGE_NAME,[yu.join(Mu,Nu.WORK_SPACE)])||(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion)!==xu.dependencies[Nu.HVIGOR_ENGINE_PACKAGE_NAME]||!function(){function u(u){const D=null==u?void 0:u.dependencies;return void 0===D?0:Object.getOwnPropertyNames(D).length}const D=u(Hu),e=u(xu);if(D+1!==e)return!1;for(const u in null==Hu?void 0:Hu.dependencies)if(!(0,wu.hasNpmPackInPaths)(u,[yu.join(Mu,Nu.WORK_SPACE)])||!Tu(u,Hu,xu))return!1;return!0}()){Vu();try{!function(){(0,Su.logInfoPrintConsole)("Hvigor installing...");for(const u in Hu.dependencies)Hu.dependencies[u]&&(Hu.dependencies[u]=(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.dependencies[u]));const u={dependencies:{...Hu.dependencies}};u.dependencies[Nu.HVIGOR_ENGINE_PACKAGE_NAME]=(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion);const D=yu.join(Mu,Nu.WORK_SPACE);try{mu.mkdirSync(D,{recursive:!0});const e=yu.resolve(D,Nu.DEFAULT_PACKAGE_JSON);mu.writeFileSync(e,JSON.stringify(u))}catch(u){(0,Su.logErrorAndExit)(u)}(function(){const u=["config","set","store-dir",Nu.HVIGOR_PNPM_STORE_PATH],D={cwd:yu.join(Mu,Nu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(Nu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)})(),function(){const u=["install"],D={cwd:yu.join(Mu,Nu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(Nu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)}(),(0,Su.logInfoPrintConsole)("Hvigor install success.")}()}catch(u){Vu()}}return Mu};var Gu={};!function(t){var C=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),F=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),E=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&C(D,u,e);return F(D,u),D},A=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(t,"__esModule",{value:!0}),t.executeInstallPnpm=t.isPnpmInstalled=t.environmentHandler=t.checkNpmConifg=t.PNPM_VERSION=void 0;const o=r,a=E(e),c=A(D),s=E(u),B=i,d=l,f=ou;t.PNPM_VERSION="7.30.0",t.checkNpmConifg=function(){const u=s.resolve(B.HVIGOR_PROJECT_ROOT_DIR,".npmrc"),D=s.resolve(c.default.homedir(),".npmrc");if((0,f.isFileExists)(u)||(0,f.isFileExists)(D))return;const e=(0,f.getNpmPath)(),t=(0,o.spawnSync)(e,["config","get","prefix"],{cwd:B.HVIGOR_PROJECT_ROOT_DIR});if(0!==t.status||!t.stdout)return void(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.");const r=s.resolve(`${t.stdout}`.replace(/[\r\n]/gi,""),".npmrc");(0,f.isFileExists)(r)||(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.")},t.environmentHandler=function(){process.env["npm_config_update-notifier"]="false"},t.isPnpmInstalled=function(){return!!a.existsSync(B.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH)&&(0,f.hasNpmPackInPaths)("pnpm",[B.HVIGOR_WRAPPER_TOOLS_HOME])},t.executeInstallPnpm=function(){(0,d.logInfoPrintConsole)(`Installing pnpm@${t.PNPM_VERSION}...`);const u=(0,f.getNpmPath)();!function(){const u=s.resolve(B.HVIGOR_WRAPPER_TOOLS_HOME,B.DEFAULT_PACKAGE_JSON);try{a.existsSync(B.HVIGOR_WRAPPER_TOOLS_HOME)||a.mkdirSync(B.HVIGOR_WRAPPER_TOOLS_HOME,{recursive:!0});const D={dependencies:{}};D.dependencies[B.PNPM]=t.PNPM_VERSION,a.writeFileSync(u,JSON.stringify(D))}catch(D){(0,d.logErrorAndExit)(`Error: EPERM: operation not permitted,create ${u} failed.`)}}(),(0,f.executeCommand)(u,["install","pnpm"],{cwd:B.HVIGOR_WRAPPER_TOOLS_HOME,stdio:["inherit","inherit","inherit"],env:process.env}),(0,d.logInfoPrintConsole)("Pnpm install success.")}}(Gu),function(){Gu.checkNpmConifg(),Gu.environmentHandler(),Gu.isPnpmInstalled()||Gu.executeInstallPnpm();const D=gu();_(u.join(D,i.WORK_SPACE))}();
\ No newline at end of file
diff --git a/drumming/drummer/hvigorfile.ts b/drumming/drummer/hvigorfile.ts
new file mode 100644
index 0000000..f3cb9f1
--- /dev/null
+++ b/drumming/drummer/hvigorfile.ts
@@ -0,0 +1,6 @@
+import { appTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/drumming/drummer/hvigorw b/drumming/drummer/hvigorw
new file mode 100644
index 0000000..ff6a29a
--- /dev/null
+++ b/drumming/drummer/hvigorw
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# ----------------------------------------------------------------------------
+# Hvigor startup script, version 1.0.0
+#
+# Required ENV vars:
+# ------------------
+# NODE_HOME - location of a Node home dir
+# or
+# Add /usr/local/nodejs/bin to the PATH environment variable
+# ----------------------------------------------------------------------------
+
+HVIGOR_APP_HOME="`pwd -P`"
+HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js
+warn() {
+ echo ""
+ echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m"
+}
+
+error() {
+ echo ""
+ echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m"
+}
+
+fail() {
+ error "$@"
+ exit 1
+}
+
+# Determine node to start hvigor wrapper script
+if [ -n "${NODE_HOME}" ];then
+ EXECUTABLE_NODE="${NODE_HOME}/bin/node"
+ if [ ! -x "$EXECUTABLE_NODE" ];then
+ fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed"
+ fi
+else
+ EXECUTABLE_NODE="node"
+ which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path"
+fi
+
+# Check hvigor wrapper script
+if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then
+ fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}"
+fi
+
+# start hvigor-wrapper script
+exec "${EXECUTABLE_NODE}" \
+ "${HVIGOR_WRAPPER_SCRIPT}" "$@"
diff --git a/drumming/drummer/hvigorw.bat b/drumming/drummer/hvigorw.bat
new file mode 100644
index 0000000..d570007
--- /dev/null
+++ b/drumming/drummer/hvigorw.bat
@@ -0,0 +1,64 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Hvigor startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js
+set NODE_EXE=node.exe
+
+goto start
+
+:start
+@rem Find node.exe
+if defined NODE_HOME goto findNodeFromNodeHome
+
+%NODE_EXE% --version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH.
+echo.
+echo Please set the NODE_HOME variable in your environment to match the
+echo location of your NodeJs installation.
+
+goto fail
+
+:findNodeFromNodeHome
+set NODE_HOME=%NODE_HOME:"=%
+set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE%
+
+if exist "%NODE_EXE_PATH%" goto execute
+echo.
+echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH.
+echo.
+echo Please set the NODE_HOME variable in your environment to match the
+echo location of your NodeJs installation.
+
+goto fail
+
+:execute
+@rem Execute hvigor
+"%NODE_EXE%" "%WRAPPER_MODULE_PATH%" %*
+
+if "%ERRORLEVEL%" == "0" goto hvigorwEnd
+
+:fail
+exit /b 1
+
+:hvigorwEnd
+if "%OS%" == "Windows_NT" endlocal
+
+:end
diff --git a/drumming/drummer/oh-package-lock.json5 b/drumming/drummer/oh-package-lock.json5
new file mode 100644
index 0000000..ddc8a54
--- /dev/null
+++ b/drumming/drummer/oh-package-lock.json5
@@ -0,0 +1,13 @@
+{
+ "lockfileVersion": 1,
+ "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
+ "specifiers": {
+ "@ohos/hypium@1.0.11": "@ohos/hypium@1.0.11"
+ },
+ "packages": {
+ "@ohos/hypium@1.0.11": {
+ "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.11.tgz",
+ "integrity": "sha512-KawcLnv43C3QIYv1UbDnKCFX3MohtDxGuFvzlUxT/qf2DBilR56Ws6zrj90LdH6PjloJQwOPESuBQIHBACAK7w=="
+ }
+ }
+}
\ No newline at end of file
diff --git a/drumming/drummer/oh-package.json5 b/drumming/drummer/oh-package.json5
new file mode 100644
index 0000000..692f9ce
--- /dev/null
+++ b/drumming/drummer/oh-package.json5
@@ -0,0 +1,12 @@
+{
+ "license": "",
+ "devDependencies": {
+ "@ohos/hypium": "1.0.11"
+ },
+ "author": "",
+ "name": "drawpart3",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "version": "1.0.0",
+ "dependencies": {}
+}
diff --git a/drumming/figs/evolution.png b/drumming/figs/evolution.png
new file mode 100644
index 0000000..4ad4eae
Binary files /dev/null and b/drumming/figs/evolution.png differ
diff --git a/drumming/figs/overall.png b/drumming/figs/overall.png
new file mode 100644
index 0000000..214b181
Binary files /dev/null and b/drumming/figs/overall.png differ
diff --git a/drumming/figs/relation.png b/drumming/figs/relation.png
new file mode 100644
index 0000000..3bd1569
Binary files /dev/null and b/drumming/figs/relation.png differ