属于同一个分支 #1
|
|
@ -0,0 +1,202 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. Please also get an ISBN
|
||||
number for the printed version if you plan to publish a book
|
||||
using this license. We recommend considering the
|
||||
Apache License 2.0 for software, and the CC-BY license for
|
||||
other creative works.
|
||||
|
||||
Copyright 2026 LI Meng
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
# TSZQ_KaiYuanHongMengRISC-VJuShenZhiNengKeChengGongJianZhuanQu
|
||||
计算思维程序实践共14个计算思维程序实践项目编写开源鸿蒙应用程序,为学生系统剖析了每一个实践项目的计算思维,为学生搭建了一个五级阶梯的程序实现路径,同时为学生提供了由零基础开发到最终系统实现的初始版程序和程序实现环境,供学生上机调试与研讨。
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# datafiting
|
||||
### 1、项目说明
|
||||
总体思路曲线拟合绘制程序项目开发的总体思路如图所示,以实现交互界面与交互控制和数据结构和数值分析算法等。程序有两个主体功能:(1)对设置的数据集和用户选择的拟合曲线类型进行拟合,并计算出拟合优度和函数表达式;(2)根据拟合得到的函数表达式进行绘制。
|
||||
图1.1 项目开发的总体思路图
|
||||
<img src="figs/overall.png" width=80% /></center>
|
||||
|
||||
|
||||
### 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 项目主要函数调用关系图
|
||||
<img src="figs/relation.png" width=80% /></center>
|
||||
|
||||
|
||||
图3.2 项目输出界面演进图
|
||||
<img src="figs/evolution.png" width=80% /></center>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/node_modules
|
||||
/oh_modules
|
||||
/local.properties
|
||||
/.idea
|
||||
**/build
|
||||
/.hvigor
|
||||
.cxx
|
||||
/.clangd
|
||||
/.clang-format
|
||||
/.clang-tidy
|
||||
**/.test
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "app_name",
|
||||
"value": "MyApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"app": {
|
||||
"signingConfigs": [
|
||||
{
|
||||
"name": "default",
|
||||
"material": {
|
||||
"certpath": "C:\\Users\\nInse\\.ohos\\config\\openharmony\\auto_ohos_default_draw2_com.test.myapplication.cer",
|
||||
"storePassword": "0000001B23074682D90DCDA10157D8187E84EC05F50638D319B2E510345811D5FEB541650A0415030813F5",
|
||||
"keyAlias": "debugKey",
|
||||
"keyPassword": "0000001B291E996BCAB68900B133F9F5D394B9749AFD0C20146A22CE85070C1340F65B45506D15A417CC08",
|
||||
"profile": "C:\\Users\\nInse\\.ohos\\config\\openharmony\\auto_ohos_default_draw2_com.test.myapplication.p7b",
|
||||
"signAlg": "SHA256withECDSA",
|
||||
"storeFile": "C:\\Users\\nInse\\.ohos\\config\\openharmony\\auto_ohos_default_draw2_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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
||||
|
|
@ -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",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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. */
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"license": "",
|
||||
"devDependencies": {},
|
||||
"author": "",
|
||||
"name": "entry",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
export class COMMON_VALUE{
|
||||
/**
|
||||
* 字体大小(大)
|
||||
*/
|
||||
static readonly BIG_SIZE_FONT = 20
|
||||
/**
|
||||
* 字体大小(小)
|
||||
*/
|
||||
static readonly SMALL_SIZE_FONT = 15
|
||||
/**
|
||||
* 字体大小(超小)
|
||||
*/
|
||||
static readonly SMALLER_SIZE_FONT = 10
|
||||
/**
|
||||
* 浅蓝
|
||||
*/
|
||||
static readonly LIGHT_BLUE = '#87BAD4'
|
||||
/**
|
||||
* 紫色
|
||||
*/
|
||||
static readonly PURPLE = '#BE4AD9'
|
||||
/**
|
||||
* 灰色
|
||||
*/
|
||||
static readonly GREY = '#B8B8B8'
|
||||
/**
|
||||
* 浅白
|
||||
*/
|
||||
static readonly LIGHT_WHITE = '#F2F2F2'
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
import promptAction from '@ohos.promptAction'
|
||||
import { COMMON_VALUE } from '../common/commonValue'
|
||||
import { handle } from '../utils/algorithm/createPotGather'
|
||||
import { BoardInfo, Quadrant } from '../utils/drawBoard/BoardType'
|
||||
import { IPoint, PointType } from '../utils/drawBoard/DrawPoint'
|
||||
import { funcInfo, functionUtilIns } from '../utils/FunctionUtil'
|
||||
|
||||
@Component
|
||||
export struct controlBar {
|
||||
@State func:funcInfo|undefined = undefined
|
||||
|
||||
//TODO
|
||||
@Prop fitFuncList:Array<funcInfo>
|
||||
@Prop selectFitFunc:funcInfo
|
||||
@Prop curFuncIndex:number
|
||||
@Link isStartFit:boolean
|
||||
@Link isShowFunc:boolean
|
||||
@Link quadrant:Quadrant
|
||||
@Link points:Array<IPoint>
|
||||
@Link selectFuncIndex:number
|
||||
|
||||
@Consume board:BoardInfo
|
||||
@Consume isRefreshPoint:boolean
|
||||
@Consume isRefreshFunc:boolean
|
||||
controller:CustomDialogController = new CustomDialogController({
|
||||
builder:addPoints({points:$points,isStartFit:$isStartFit,isShowFunc:$isShowFunc})
|
||||
})
|
||||
build() {
|
||||
Column(){
|
||||
Row({space:5}){
|
||||
|
||||
Row(){
|
||||
Text('坐标轴')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
Radio({value:"四象限",group:"Quadrant"})
|
||||
.checked(this.quadrant == Quadrant.FOUR)
|
||||
.onClick(()=>{
|
||||
this.quadrant = Quadrant.FOUR
|
||||
AppStorage.setOrCreate("quadrant",Quadrant.FOUR)
|
||||
})
|
||||
Text('四象限')
|
||||
.fontSize(COMMON_VALUE.SMALLER_SIZE_FONT)
|
||||
Radio({value:"一象限",group:"Quadrant"})
|
||||
.checked(this.quadrant == Quadrant.ONE)
|
||||
.onClick(()=>{
|
||||
this.quadrant = Quadrant.ONE
|
||||
AppStorage.setOrCreate("quadrant",Quadrant.ONE)
|
||||
})
|
||||
Text('一象限')
|
||||
.fontSize(COMMON_VALUE.SMALLER_SIZE_FONT)
|
||||
}.backgroundColor(COMMON_VALUE.GREY)
|
||||
|
||||
Row({space:5}){
|
||||
Button('生成数据集')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
|
||||
this.isRefreshPoint = !this.isRefreshPoint
|
||||
this.points = this.board.initMap()
|
||||
this.board.drawAxis()
|
||||
this.board.drawPoints()
|
||||
|
||||
})
|
||||
Button('显示数据集')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
this.isShowFunc = false
|
||||
})
|
||||
Button('显示数据集与曲线')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
if(this.points.length == 0){
|
||||
promptAction.showToast({
|
||||
message:"还未选择样本点!"
|
||||
})
|
||||
return
|
||||
}
|
||||
if(this.selectFuncIndex != this.curFuncIndex){
|
||||
this.isShowFunc = false
|
||||
this.selectFuncIndex = this.curFuncIndex
|
||||
}
|
||||
setTimeout(()=>{
|
||||
this.isStartFit = false
|
||||
if (this.isShowFunc == false) {
|
||||
this.isShowFunc = true
|
||||
}
|
||||
},100)
|
||||
})
|
||||
Button('手动输入数据集')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
this.controller.open()
|
||||
})
|
||||
Button('拟合')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
if(this.points.length == 0){
|
||||
promptAction.showToast({
|
||||
message:"还未选择样本点!"
|
||||
})
|
||||
return
|
||||
}
|
||||
//TODO
|
||||
this.selectFitFunc = this.fitFuncList[this.curFuncIndex]
|
||||
|
||||
this.func = functionUtilIns.getFitFunc(this.selectFitFunc,this.points)
|
||||
this.isStartFit = true
|
||||
this.isShowFunc = false
|
||||
setTimeout(()=>{
|
||||
this.isShowFunc = true
|
||||
},100)
|
||||
this.selectFuncIndex = this.curFuncIndex
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Row(){
|
||||
Text('结果输出:')
|
||||
Swiper(){
|
||||
if(this.isStartFit){
|
||||
Text('函数形式:' + this.selectFitFunc.name + "\t" + this.selectFitFunc.body)
|
||||
.width('100%').height('100%')
|
||||
Text('函数系数:' + this.func!.body)
|
||||
.width('100%').height('100%')
|
||||
Text('拟合优度R^2:' + this.func!.R!)
|
||||
.width('100%').height('100%')
|
||||
}
|
||||
}.backgroundColor(COMMON_VALUE.GREY).layoutWeight(1).vertical(true).height('100%').autoPlay(false).indicator(false).loop(false)
|
||||
|
||||
}.width('100%').layoutWeight(1).padding(5)
|
||||
}.backgroundColor(COMMON_VALUE.LIGHT_WHITE).width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
|
||||
@CustomDialog
|
||||
struct addPoints{
|
||||
controller:CustomDialogController
|
||||
@State setNum:number = -1
|
||||
@State num:number = 0
|
||||
@State msg:string = ''
|
||||
@Link points:Array<IPoint>
|
||||
@Link isStartFit:boolean
|
||||
@Link isShowFunc:boolean
|
||||
@Consume board:BoardInfo
|
||||
@Consume isRefreshPoint:boolean
|
||||
@Consume isRefreshFunc:boolean
|
||||
build(){
|
||||
Column({space:5}){
|
||||
Row(){
|
||||
if(this.setNum == -1){
|
||||
Text('设置样本点个数')
|
||||
TextInput({text:this.num.toString()})
|
||||
.onChange((value)=>{
|
||||
this.num = Number(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
if(this.setNum == -1){
|
||||
Row(){
|
||||
Button('确定')
|
||||
.onClick(()=>{
|
||||
if(this.num <= 0){
|
||||
promptAction.showToast({
|
||||
message:"必须为正整数"
|
||||
})
|
||||
return
|
||||
}
|
||||
this.points = []
|
||||
this.setNum = this.num
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
Button('取消')
|
||||
.onClick(()=>{
|
||||
this.controller.close()
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
}.width('100%').justifyContent(FlexAlign.SpaceAround)
|
||||
}else {
|
||||
Column(){
|
||||
if(this.setNum == 0){
|
||||
Text('是否保存数据集')
|
||||
}else{
|
||||
Text("输入" + this.setNum + "个样本点,格式为X,X")
|
||||
TextInput({text:this.msg})
|
||||
.onChange((value)=>{
|
||||
this.msg = value
|
||||
})
|
||||
}
|
||||
Row(){
|
||||
Button('确定')
|
||||
.onClick(()=>{
|
||||
if(this.setNum == 0){
|
||||
this.isRefreshPoint = !this.isRefreshPoint
|
||||
this.board.initMap(this.points)
|
||||
this.board.drawAxis()
|
||||
this.board.drawPoints()
|
||||
this.controller.close()
|
||||
}else{
|
||||
let arr = this.msg.split(',')
|
||||
this.points.push({x:Number(arr[0]),y:Number(arr[1]),pointType:PointType.CONTINUE})
|
||||
this.msg = ''
|
||||
this.setNum--
|
||||
}
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
Button('取消')
|
||||
.onClick(()=>{
|
||||
this.controller.close()
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
}.width('100%').justifyContent(FlexAlign.SpaceAround)
|
||||
}
|
||||
}
|
||||
}.padding(5)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
import { COMMON_VALUE } from '../common/commonValue'
|
||||
import { funcInfo } from '../utils/FunctionUtil'
|
||||
|
||||
@Component
|
||||
export struct fitFuncType {
|
||||
@Link fitFuncList:Array<funcInfo>
|
||||
@Link curFuncIndex:number
|
||||
controller:CustomDialogController = new CustomDialogController({
|
||||
builder:addFitFuncType({fitFuncList:$fitFuncList})
|
||||
})
|
||||
build() {
|
||||
Column({space:5}){
|
||||
Text('拟合曲线类型')
|
||||
.height('12%')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
List(){
|
||||
ForEach(this.fitFuncList,(item:funcInfo,index:number)=>{
|
||||
ListItem(){
|
||||
funcItem({curFuncIndex:$curFuncIndex,func:item,funcIndex:index})
|
||||
}.width('100%').height(30)
|
||||
})
|
||||
}.width('100%').layoutWeight(1).backgroundColor(COMMON_VALUE.LIGHT_BLUE)
|
||||
|
||||
Button('新增拟合曲线类型')
|
||||
.onClick(()=>{
|
||||
this.controller.open()
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
.backgroundColor(COMMON_VALUE.PURPLE)
|
||||
}.width('100%').height('100%').padding({top:5,bottom:5,right:5}).backgroundColor(COMMON_VALUE.LIGHT_WHITE)
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
struct funcItem {
|
||||
@Link curFuncIndex:number
|
||||
@Prop func:funcInfo
|
||||
@Prop funcIndex:number
|
||||
build() {
|
||||
Row({space:5}){
|
||||
Radio({value:this.func.name,group:"fitFunc"})
|
||||
.width(20)
|
||||
.height(20)
|
||||
.checked(this.curFuncIndex == this.funcIndex)
|
||||
.onChange((value)=>{
|
||||
if(this.curFuncIndex == this.funcIndex){
|
||||
|
||||
return
|
||||
}
|
||||
this.curFuncIndex = this.funcIndex
|
||||
})
|
||||
Text(this.func.name)
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
Text(this.func.body)
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
|
||||
let funcList:Array<funcInfo> = [
|
||||
{name:"对数函数",body:"a*log(x,b)+c"},
|
||||
{name:"指数函数",body:"a*e^(0.01x)+b"}
|
||||
]
|
||||
@CustomDialog
|
||||
struct addFitFuncType{
|
||||
@Link fitFuncList:Array<funcInfo>
|
||||
controller:CustomDialogController
|
||||
@State index:number = 0
|
||||
@Builder item(index:number){
|
||||
Row(){
|
||||
Text(funcList[index].name)
|
||||
.fontColor(this.index == index?Color.Red:Color.Black)
|
||||
Text(funcList[index].body)
|
||||
.fontColor(this.index == index?Color.Red:Color.Black)
|
||||
}.width('100%').onClick(()=>{
|
||||
this.index = index
|
||||
})
|
||||
}
|
||||
build(){
|
||||
Column({space:5}){
|
||||
|
||||
ForEach(funcList,(item:funcInfo,index)=>{
|
||||
this.item(index)
|
||||
})
|
||||
|
||||
Row(){
|
||||
Button('确定')
|
||||
.onClick(()=>{
|
||||
this.fitFuncList.push({name:funcList[this.index].name,body:funcList[this.index].body})
|
||||
funcList.splice(this.index,1)
|
||||
this.controller.close()
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
Button('取消')
|
||||
.onClick(()=>{
|
||||
this.controller.close()
|
||||
})
|
||||
.type(ButtonType.Normal)
|
||||
}.width('100%').justifyContent(FlexAlign.SpaceAround)
|
||||
}.width('100%').padding(5)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import { COMMON_VALUE } from '../common/commonValue'
|
||||
import { BoardInfo } from '../utils/drawBoard/BoardType'
|
||||
import { IPoint, PointType } from '../utils/drawBoard/DrawPoint'
|
||||
import picker from '@ohos.file.picker';
|
||||
import fs from '@ohos.file.fs';
|
||||
import { createDocument } from '../model/createDocumentModel';
|
||||
import buffer from '@ohos.buffer';
|
||||
|
||||
@Component
|
||||
export struct header {
|
||||
@State filePath:string = ''
|
||||
@Link points:Array<IPoint>
|
||||
@Link isStartFit:boolean
|
||||
@Link isShowFunc:boolean
|
||||
@Consume board:BoardInfo
|
||||
@Consume isRefreshPoint:boolean
|
||||
@Consume isRefreshFunc:boolean
|
||||
context = getContext()
|
||||
build() {
|
||||
Row({space:5}){
|
||||
Text('样本数据集文件')
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
Text(this.filePath)
|
||||
.textOverflow({overflow:TextOverflow.MARQUEE})
|
||||
.layoutWeight(1)
|
||||
.fontSize(COMMON_VALUE.SMALL_SIZE_FONT)
|
||||
.backgroundColor(COMMON_VALUE.LIGHT_BLUE)
|
||||
.height('100%')
|
||||
Button('装载')
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(async ()=>{
|
||||
this.points = []
|
||||
let documentPicker = new picker.DocumentViewPicker()
|
||||
let DocumentSelectOptions = new picker.DocumentSelectOptions();
|
||||
let DocumentSelectResult: Array<string> = await documentPicker.select(DocumentSelectOptions)
|
||||
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)
|
||||
|
||||
|
||||
//解析路径
|
||||
this.filePath = file.path
|
||||
fs.closeSync(file)
|
||||
|
||||
//解析文件样本点
|
||||
let arr = str.split('\n')
|
||||
for (let index = 0; index < arr.length; index++) {
|
||||
this.points.push({x:Number(arr[index].split(' ')[0]),y:Number(arr[index].split(' ')[1]),pointType:PointType.CONTINUE})
|
||||
}
|
||||
|
||||
|
||||
this.isStartFit = false
|
||||
this.isShowFunc = false
|
||||
this.isRefreshPoint = !this.isRefreshPoint
|
||||
this.board.initMap(this.points)
|
||||
this.board.drawPoints()
|
||||
this.board.drawAxis()
|
||||
})
|
||||
}.width('100%').height('100%').padding(5).backgroundColor(COMMON_VALUE.LIGHT_WHITE)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { BoardInfo, CONTEXT, FIT_CONTEXT } from '../utils/drawBoard/BoardType'
|
||||
import { IPoint } from '../utils/drawBoard/DrawPoint'
|
||||
import { funcInfo, functionUtilIns } from '../utils/FunctionUtil'
|
||||
|
||||
@Component
|
||||
export struct imageShow {
|
||||
@Prop loop_board:string
|
||||
@Prop loop_fit:string
|
||||
@Prop isShowFunc:boolean
|
||||
@Prop points:Array<IPoint>
|
||||
@Prop selectFitFunc:funcInfo
|
||||
@Consume board:BoardInfo
|
||||
@Consume isRefreshFunc:boolean
|
||||
build() {
|
||||
Stack(){
|
||||
Canvas(CONTEXT)
|
||||
.width('100%').height(this.loop_board)
|
||||
if(this.isShowFunc){
|
||||
Canvas(FIT_CONTEXT)
|
||||
.width('100%').height(this.loop_fit)
|
||||
.onReady(()=>{
|
||||
//获取到经过拟合后的函数
|
||||
functionUtilIns.getFitFunc(this.selectFitFunc,this.points)
|
||||
//解析函数 获取对应坐标点
|
||||
let points:Array<IPoint> = functionUtilIns.getPoints()
|
||||
this.board.drawFitFunc(points)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
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';
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import picker from '@ohos.file.picker';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
import fs from '@ohos.file.fs';
|
||||
|
||||
/**
|
||||
* 创建一个包含样本点的文件 用于读取测试
|
||||
*/
|
||||
export let createDocument = async ()=>{
|
||||
let documentPicker = new picker.DocumentViewPicker();
|
||||
let uris:Array<string> = []
|
||||
let DocumentSaveOptions = new picker.DocumentSaveOptions();
|
||||
DocumentSaveOptions.newFileNames = ['testPoints.txt']
|
||||
uris = await documentPicker.save(DocumentSaveOptions)
|
||||
let uri = uris[0]
|
||||
let file = fs.openSync(uri, fs.OpenMode.WRITE_ONLY);
|
||||
let writeLen = 0
|
||||
writeLen += fs.writeSync(file.fd, '2 2\n100 100\n123 323\n200.99 199.999\n302.8 300.6\n400 400.9\n450 450');//todo必须加结束位 否则多读
|
||||
fs.closeSync(file);
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
import { controlBar } from '../component/ControlBar'
|
||||
import { fitFuncType } from '../component/FitFuncType'
|
||||
import { header } from '../component/Header'
|
||||
import { imageShow } from '../component/ImageShow'
|
||||
import { createDocument } from '../model/createDocumentModel'
|
||||
import { B1Q2D, B4Q2D, BoardInfo, Quadrant } from '../utils/drawBoard/BoardType'
|
||||
import { IPoint } from '../utils/drawBoard/DrawPoint'
|
||||
import { funcInfo } from '../utils/FunctionUtil'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
@State fitFuncList:Array<funcInfo> = [
|
||||
{name:"一次函数",body:"a*x+b"},
|
||||
{name:"二次函数",body:"a*x^2+b*x+c"},
|
||||
{name:"三次函数",body:"a*x^3+b*x^2+c*x+d"},
|
||||
{name:"四次函数",body:"a*x^4+b*x^3+c*x^2+d*x+e"}
|
||||
]
|
||||
@State curFuncIndex:number = 0
|
||||
@State selectFuncIndex:number = 0
|
||||
/**
|
||||
* 是否开始拟合 点击拟合按钮改为true
|
||||
* 响应:结果输出栏
|
||||
*/
|
||||
@State isStartFit:boolean = false
|
||||
/**
|
||||
* 是否展示拟合函数 点击显示数据集与曲线改为true
|
||||
* 响应:出现函数图像
|
||||
*/
|
||||
@State isShowFunc:boolean = false
|
||||
|
||||
@State @Watch('refreshBoard')quadrant:Quadrant = Quadrant.FOUR
|
||||
|
||||
@Provide board:BoardInfo = B4Q2D
|
||||
@Provide @Watch('refreshPoint')isRefreshPoint:boolean = false
|
||||
@Provide @Watch('refreshFunc')isRefreshFunc:boolean = false
|
||||
@State loop_board:string = '100%'
|
||||
@State loop_fit:string = '100%'
|
||||
@State virtualPoints:Array<IPoint> = []
|
||||
@State boardPoints:Array<IPoint> = []
|
||||
|
||||
refreshPoint(){
|
||||
if(this.loop_board == '100%'){
|
||||
this.loop_board = '101%'
|
||||
}else {
|
||||
this.loop_board = '100%'
|
||||
}
|
||||
console.log('REFRESH point')
|
||||
this.isStartFit = false
|
||||
this.isShowFunc = false
|
||||
}
|
||||
|
||||
refreshFunc(){
|
||||
if(this.loop_fit == '100%'){
|
||||
this.loop_fit = '101%'
|
||||
}else {
|
||||
this.loop_fit = '100%'
|
||||
}
|
||||
console.log('REFRESH func')
|
||||
}
|
||||
|
||||
refreshBoard(){
|
||||
console.log('REFRESH board')
|
||||
if(this.quadrant == Quadrant.ONE){
|
||||
this.board = B1Q2D
|
||||
}else{
|
||||
this.board = B4Q2D
|
||||
}
|
||||
if(this.virtualPoints.length != 0){
|
||||
this.isRefreshPoint = !this.isRefreshPoint
|
||||
this.isRefreshFunc = !this.isRefreshFunc
|
||||
this.board.initMap(this.virtualPoints)
|
||||
this.board.drawAxis()
|
||||
this.board.drawPoints()
|
||||
this.isStartFit = false
|
||||
}
|
||||
}
|
||||
|
||||
async aboutToAppear(){
|
||||
await createDocument()
|
||||
}
|
||||
|
||||
|
||||
build() {
|
||||
Column(){
|
||||
|
||||
Row(){
|
||||
Column(){
|
||||
|
||||
header({points:$virtualPoints,isShowFunc:$isShowFunc,isStartFit:$isStartFit}).width('100%').height('15%')
|
||||
|
||||
imageShow({points:this.virtualPoints,selectFitFunc:this.fitFuncList[this.curFuncIndex],loop_board:this.loop_board,loop_fit:this.loop_fit,isShowFunc:this.isShowFunc}).width('100%').height('85%')
|
||||
|
||||
}.width('60%').height('100%')
|
||||
|
||||
fitFuncType({fitFuncList:$fitFuncList,curFuncIndex:$curFuncIndex}).width('40%').height('100%')
|
||||
|
||||
}.width('100%').height('75%')
|
||||
|
||||
//TODO
|
||||
controlBar({points:$virtualPoints,isShowFunc:$isShowFunc,quadrant:$quadrant,isStartFit:$isStartFit,selectFitFunc:this.fitFuncList[this.selectFuncIndex],selectFuncIndex:$selectFuncIndex,curFuncIndex:this.curFuncIndex,fitFuncList:this.fitFuncList}).width('100%').layoutWeight(1)
|
||||
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import { IPoint, PointType } from './drawBoard/DrawPoint'
|
||||
import { handle } from './algorithm/createPotGather'
|
||||
|
||||
export interface funcInfo {
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* 函数体
|
||||
*/
|
||||
body: string
|
||||
/**
|
||||
* 拟合优度
|
||||
*/
|
||||
R?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供函数拟合的方法
|
||||
*/
|
||||
class FunctionUtil {
|
||||
/**
|
||||
* 根据拟合函数类型和样本点计算出拟合函数
|
||||
* 返回funcInfo
|
||||
*/
|
||||
getFitFunc(fitFunc: funcInfo, points: Array<IPoint>) {
|
||||
handle.fit(fitFunc.body, points);
|
||||
let func: funcInfo = { name: "", body: handle.fitFunc, R: handle.R_squared }
|
||||
return func
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据函数返回xyMap
|
||||
*/
|
||||
getPoints() {
|
||||
//TODO 结合原代码
|
||||
//用来测试的拟合函数
|
||||
handle.compote_curveData();
|
||||
let point: Array<IPoint> = handle.coordinatePoint;
|
||||
// console.log(handle.fitFunc)
|
||||
// for (let i = 0; i < point.length; i++) {
|
||||
// console.log("testTag", "(" + point[i].x + ", " + point[i].y + ")");
|
||||
// }
|
||||
return point
|
||||
}
|
||||
}
|
||||
|
||||
export let functionUtilIns = new FunctionUtil()
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
import { mathjs } from './evaluate';
|
||||
import {IPoint, PointType} from "../drawBoard/DrawPoint"
|
||||
import {FitRes} from "./fittingResult"
|
||||
import { Matrix } from './least_square';
|
||||
import { Quadrant } from '../drawBoard/BoardType';
|
||||
import { Exponent } from './exponentialFit';
|
||||
import { Logarithm } from './logarithmicFit';
|
||||
|
||||
function arrange(low: number, high: number, step: number): number[] {
|
||||
const rangeArray: number[] = [];
|
||||
if (step <= 0) {
|
||||
throw new Error("Step must be greater than zero.");
|
||||
}
|
||||
for (let i = low; i <= high; i += step) {
|
||||
rangeArray.push(i);
|
||||
}
|
||||
return rangeArray;
|
||||
}
|
||||
|
||||
const replaceVar: Array<string> = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
|
||||
|
||||
class CreatePointGather {
|
||||
coordinatePoint: Array<IPoint>;
|
||||
theta: number[];
|
||||
error: number;
|
||||
R_squared: number;
|
||||
fitFunc: string;
|
||||
funcFormat: string;
|
||||
private m: number;
|
||||
|
||||
constructor() {
|
||||
this.coordinatePoint = [];
|
||||
this.theta = [];
|
||||
this.error = 0;
|
||||
this.R_squared = 0;
|
||||
this.fitFunc = "";
|
||||
this.funcFormat = "";
|
||||
this.m = 0;
|
||||
}
|
||||
|
||||
private judgeWhichM(funcFormat: string) {
|
||||
const index = funcFormat.indexOf("^");
|
||||
if (index === -1) {
|
||||
this.m = 1;
|
||||
} else {
|
||||
this.m = Number(funcFormat[index + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
private getFitFunc(funcFormat: string): void {
|
||||
this.funcFormat = funcFormat;
|
||||
this.fitFunc = funcFormat;
|
||||
if (funcFormat == "a*e^(0.01x)+b") {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
if (this.theta[i] < 0 && this.fitFunc[this.fitFunc.indexOf(replaceVar[i]) - 1] == "+") {
|
||||
this.fitFunc = this.fitFunc.substring(0, this.fitFunc.indexOf(replaceVar[i]) - 1) + this.fitFunc.substring(this.fitFunc.indexOf(replaceVar[i]));
|
||||
}
|
||||
this.fitFunc = this.fitFunc.replace(replaceVar[i], this.theta[i].toFixed(4));
|
||||
}
|
||||
} else if (funcFormat == "a*log(x,b)+c") {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (this.theta[i] < 0 && this.fitFunc[this.fitFunc.indexOf(replaceVar[i]) - 1] == "+") {
|
||||
this.fitFunc = this.fitFunc.substring(0, this.fitFunc.indexOf(replaceVar[i]) - 1) + this.fitFunc.substring(this.fitFunc.indexOf(replaceVar[i]));
|
||||
}
|
||||
this.fitFunc = this.fitFunc.replace(replaceVar[i], this.theta[i].toFixed(4));
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.m + 1; i++) {
|
||||
if (this.theta[i] < 0 && this.fitFunc[this.fitFunc.indexOf(replaceVar[i]) - 1] == "+") {
|
||||
this.fitFunc = this.fitFunc.substring(0, this.fitFunc.indexOf(replaceVar[i]) - 1) + this.fitFunc.substring(this.fitFunc.indexOf(replaceVar[i]));
|
||||
}
|
||||
this.fitFunc = this.fitFunc.replace(replaceVar[i], this.theta[i].toFixed(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private calculate_function(x:number): number {
|
||||
if (this.funcFormat == "a*e^(0.01x)+b") {
|
||||
return Exponent.expFunc(x, this.theta[0], this.theta[1]);
|
||||
} else if (this.funcFormat == "a*log(x,b)+c") {
|
||||
if (x > 0) return Logarithm.logFunc(x, this.theta[0], this.theta[1], this.theta[2]);
|
||||
else return Number.NaN;
|
||||
}
|
||||
return mathjs.evaluate(this.fitFunc, [{char: 'x', value: x}]);
|
||||
}
|
||||
|
||||
compote_curveData(low: number = -500, high: number = 500, step: number = 1): Array<IPoint> {
|
||||
if (AppStorage.get("quadrant") == Quadrant.ONE) {
|
||||
low = 0;
|
||||
high = 1000;
|
||||
}
|
||||
this.coordinatePoint = [];
|
||||
let xValues = arrange(low, high, step);
|
||||
let yValues: number[] = [];
|
||||
let size = xValues.length;
|
||||
yValues = Array<number>(size).fill(0).map((_, index) => {
|
||||
return this.calculate_function(xValues[index]);
|
||||
})
|
||||
for (let i = 0; i < size; i++) {
|
||||
if (!Number.isNaN(yValues[i])) {
|
||||
this.coordinatePoint.push({
|
||||
x: xValues[i],
|
||||
y: yValues[i],
|
||||
pointType: PointType.CONTINUE
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!this.coordinatePoint.length) {
|
||||
this.coordinatePoint.push({
|
||||
x: 0,
|
||||
y: 0,
|
||||
pointType: PointType.CONTINUE
|
||||
})
|
||||
}
|
||||
return this.coordinatePoint;
|
||||
}
|
||||
|
||||
private error_func(sampleData: Array<IPoint>): FitRes {
|
||||
let sx: number[] = sampleData.map(value => value.x);
|
||||
let sy: number[] = sampleData.map(value => value.y);
|
||||
let y_predict: number[] = Array<number>(sx.length).fill(0).map((_, index) => {
|
||||
return this.calculate_function(sx[index]);
|
||||
})
|
||||
let error: number = 0;
|
||||
for (let i = 0; i < sx.length; i++) {
|
||||
error += (sy[i] - y_predict[i]) ** 2;
|
||||
}
|
||||
const mean = sy.reduce((acc, val) => acc + val, 0) / sy.length;
|
||||
let sum: number = sy.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0);
|
||||
let R_squared: number = 1 - (error/sum);
|
||||
if (R_squared <= 0) R_squared = 0;
|
||||
let fitRes: FitRes = {
|
||||
error: error,
|
||||
R_squared: R_squared
|
||||
};
|
||||
return fitRes;
|
||||
}
|
||||
|
||||
fit(funcFormat: string, sampleData: Array<IPoint>): void {
|
||||
let fitRes: FitRes = {};
|
||||
if (funcFormat == "a*e^(0.01x)+b") {
|
||||
this.theta = Exponent.gradientDescentExp(sampleData);
|
||||
} else if (funcFormat == "a*log(x,b)+c") {
|
||||
this.theta = Logarithm.gradientDescentLog(sampleData);
|
||||
} else {
|
||||
this.judgeWhichM(funcFormat);
|
||||
fitRes = Matrix.leastSquareMethod(this.m, sampleData);
|
||||
if (fitRes.theta) this.theta = fitRes.theta;
|
||||
}
|
||||
this.getFitFunc(funcFormat);
|
||||
fitRes = this.error_func(sampleData);
|
||||
if (fitRes.error && fitRes.R_squared) {
|
||||
this.error = fitRes.error;
|
||||
this.R_squared = fitRes.R_squared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export let handle: CreatePointGather = new CreatePointGather();
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
interface replaceableVar {
|
||||
char: string,
|
||||
value: number
|
||||
}
|
||||
|
||||
class MathExpressionParser {
|
||||
private tokens: string[];
|
||||
private currentTokenIndex: number;
|
||||
private variables: replaceableVar[];
|
||||
|
||||
constructor() {
|
||||
this.tokens = []
|
||||
this.currentTokenIndex = 0
|
||||
this.variables = []
|
||||
}
|
||||
|
||||
private tokenize(expression: string): string[] {
|
||||
const regex: RegExp = new RegExp("(\\d+\\.?\\d*|\\.\\d+|[a-zA-Z]+|\\S)", "g");
|
||||
const originalTokens = expression.match(regex) || [];
|
||||
|
||||
const Tokens: string[] = [];
|
||||
for (let i = 0; i < originalTokens.length; i++) {
|
||||
Tokens.push(originalTokens[i]);
|
||||
if (i < originalTokens.length - 1) {
|
||||
const currentToken = originalTokens[i];
|
||||
const nextToken = originalTokens[i + 1];
|
||||
if ((this.isNumber(currentToken) && this.isVariable(nextToken)) ||
|
||||
(this.isVariable(currentToken) && this.isVariable(nextToken)) ||
|
||||
(currentToken === ')' && (this.isVariable(nextToken) || this.isNumber(nextToken))) ||
|
||||
(this.isNumber(currentToken) && nextToken === '(') ||
|
||||
(this.isVariable(currentToken) && nextToken === '(')) {
|
||||
Tokens.push('*');
|
||||
}
|
||||
}
|
||||
}
|
||||
return Tokens;
|
||||
}
|
||||
|
||||
private existInArr(token: string): number {
|
||||
for (let i: number = 0; i < this.variables.length; i++) {
|
||||
if (this.variables[i].char == token) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private isNumber(token: string): boolean {
|
||||
const num = Number(token);
|
||||
return !Number.isNaN(num) && Number.isFinite(num);
|
||||
}
|
||||
|
||||
private isVariable(token: string): boolean {
|
||||
const temp: RegExp = new RegExp("^[a-zA-Z]+$")
|
||||
return temp.test(token) && !this.isFunction(token) && token !== "π";
|
||||
}
|
||||
|
||||
private isFunction(token: string): boolean {
|
||||
return ['pow', 'sin', 'cos', 'tan', 'ln', 'sqrt', 'log2', 'log10'].includes(token);
|
||||
}
|
||||
|
||||
private getCurrentToken(): string {
|
||||
return this.tokens[this.currentTokenIndex];
|
||||
}
|
||||
|
||||
private getNextToken(): string {
|
||||
this.currentTokenIndex++;
|
||||
return this.getCurrentToken();
|
||||
}
|
||||
|
||||
private parsePrimaryExpression(): number {
|
||||
const token = this.getCurrentToken();
|
||||
if (token === '(') {
|
||||
this.getNextToken(); // consume '('
|
||||
const result = this.parseExpression();
|
||||
if (this.getCurrentToken() !== ')') {
|
||||
throw new Error("Expected ')' after expression");
|
||||
}
|
||||
this.getNextToken(); // consume ')'
|
||||
return result;
|
||||
} else if (token === 'π') {
|
||||
this.getNextToken(); // consume 'π'
|
||||
return Math.PI;
|
||||
} else if (this.isVariable(token)) {
|
||||
if (this.existInArr(token) != -1) {
|
||||
const value = this.variables[this.existInArr(token)].value;
|
||||
this.getNextToken(); // consume variable
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`Unknown variable: ${token}`);
|
||||
}
|
||||
} else if (this.isFunction(token)) {
|
||||
return this.parseFunction();
|
||||
} else {
|
||||
const num = Number(token);
|
||||
this.getNextToken(); // consume number
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
private parseFunction(): number {
|
||||
const functionName = this.getCurrentToken();
|
||||
this.getNextToken(); // consume function name
|
||||
if (this.getCurrentToken() !== '(') {
|
||||
throw new Error(`Expected '(' after function name ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume '('
|
||||
|
||||
const argument1 = this.parseExpression();
|
||||
|
||||
if (functionName === 'pow') {
|
||||
if (this.getCurrentToken() !== ',') {
|
||||
throw new Error(`Expected ',' between arguments for function ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume ','
|
||||
const argument2 = this.parseExpression();
|
||||
if (this.getCurrentToken() !== ')') {
|
||||
throw new Error(`Expected ')' after arguments for function ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume ')'
|
||||
return Math.pow(argument1, argument2);
|
||||
}
|
||||
|
||||
if (this.getCurrentToken() !== ')') {
|
||||
throw new Error(`Expected ')' after argument for function ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume ')'
|
||||
|
||||
switch (functionName) {
|
||||
case 'sin':
|
||||
return Math.sin(argument1);
|
||||
case 'cos':
|
||||
return Math.cos(argument1);
|
||||
case 'tan':
|
||||
return Math.tan(argument1);
|
||||
case 'ln':
|
||||
return Math.log(argument1);
|
||||
case 'sqrt':
|
||||
return Math.sqrt(argument1);
|
||||
case 'log2':
|
||||
return Math.log2(argument1);
|
||||
case 'log10':
|
||||
return Math.log10(argument1);
|
||||
default:
|
||||
throw new Error(`Unknown function: ${functionName}`);
|
||||
}
|
||||
}
|
||||
|
||||
private parseFactor(): number {
|
||||
// 处理一元负号
|
||||
if (this.getCurrentToken() === '-') {
|
||||
this.getNextToken(); // consume '-'
|
||||
return -this.parseFactor();
|
||||
}
|
||||
// 处理初级表达式
|
||||
let result = this.parsePrimaryExpression();
|
||||
// 处理乘法和除法
|
||||
while (this.getCurrentToken() === '*' || this.getCurrentToken() === '/') {
|
||||
const operator = this.getCurrentToken();
|
||||
this.getNextToken(); // consume operator
|
||||
const operand = this.parsePrimaryExpression();
|
||||
if (operator === '*') {
|
||||
result *= operand;
|
||||
} else if (operator === '/') {
|
||||
if (operand === 0) {
|
||||
throw new Error("Division by zero");
|
||||
}
|
||||
result /= operand;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private parseExpression(): number {
|
||||
let result = this.parseFactor();
|
||||
while (this.getCurrentToken() === '+' || this.getCurrentToken() === '-') {
|
||||
const operator = this.getCurrentToken();
|
||||
this.getNextToken();
|
||||
const operand = this.parseFactor();
|
||||
if (operator === '+') {
|
||||
result += operand;
|
||||
} else if (operator === '-') {
|
||||
result -= operand;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public evaluate(expression: string, variables: replaceableVar[] = []): number {
|
||||
let temp: RegExp = new RegExp("\\s+", "g");
|
||||
expression = expression.trim().replace(temp, "");
|
||||
temp = new RegExp("(\\([^()]*?(?:\\([^()]*?\\)[^()]*?)*\\)|\\w+)(?:\\*\\*|\\^)(\\([^()]*?(?:\\([^()]*?\\)[^()]*?)*\\)|\\w+)", "g");
|
||||
expression = expression.replace(temp, "pow($1, $2)");
|
||||
this.tokens = this.tokenize(expression);
|
||||
this.currentTokenIndex = 0;
|
||||
this.variables = variables;
|
||||
|
||||
const result = this.parseExpression();
|
||||
if (this.currentTokenIndex !== this.tokens.length) {
|
||||
console.log("Token:" + this.tokens);
|
||||
console.log("当前index:" + this.currentTokenIndex + " " + "当前length:" + this.tokens.length);
|
||||
throw new Error("Unexpected token");
|
||||
}
|
||||
return Number(result.toFixed(4));
|
||||
}
|
||||
}
|
||||
|
||||
export let mathjs: MathExpressionParser = new MathExpressionParser();
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import {IPoint} from "../drawBoard/DrawPoint"
|
||||
|
||||
export class Exponent {
|
||||
static expFunc(x: number, a: number, b: number): number {
|
||||
return a * Math.exp(0.01 * x) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 梯度下降法
|
||||
* @param sampleData
|
||||
* @param learningRate
|
||||
* @param epochs
|
||||
* @returns
|
||||
*/
|
||||
static gradientDescentExp(sampleData: Array<IPoint>, learningRate: number = 0.00001, epochs: number = 100000): number[] {
|
||||
let a = 0.001; // 初始化参数
|
||||
let b = 0.001;
|
||||
|
||||
for (let epoch = 0; epoch < epochs; epoch++) {
|
||||
let aGrad = 0;
|
||||
let bGrad = 0;
|
||||
const n = sampleData.length;
|
||||
for (let i = 0; i < n; i++) {
|
||||
const x = sampleData[i].x;
|
||||
const y = sampleData[i].y;
|
||||
const yPredict = Exponent.expFunc(x, a, b);
|
||||
aGrad += -2 * (y - yPredict) * Math.exp(0.01 * x);
|
||||
bGrad += -2 * (y - yPredict);
|
||||
}
|
||||
|
||||
a -= (aGrad / n) * learningRate;
|
||||
b -= (bGrad / n) * learningRate;
|
||||
|
||||
// 检查参数是否过大或过小,若是则终止训练
|
||||
if (Number.isNaN(a) || Number.isNaN(b) || !Number.isFinite(a) || !Number.isFinite(b) || Math.abs(a) > 1e10 || Math.abs(b) > 1e10) {
|
||||
console.error(`Parameters became unstable at epoch ${epoch + 1}`);
|
||||
break;
|
||||
}
|
||||
|
||||
// 每1000个epoch打印一次当前参数
|
||||
if (epoch % 1000 === 0) {
|
||||
console.log(`Epoch ${epoch + 1}, a: ${a}, b: ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
return [a, b];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* 拟合结果接口类型,包含拟合的函数系数和协方差矩阵
|
||||
*/
|
||||
export interface FitRes {
|
||||
theta?: number[];
|
||||
covarianceMatrix?: number[][];
|
||||
error?: number;
|
||||
R_squared?: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
import {IPoint} from "../drawBoard/DrawPoint"
|
||||
import {FitRes} from "./fittingResult"
|
||||
|
||||
|
||||
/**
|
||||
* 矩阵运算工具类
|
||||
*/
|
||||
export class Matrix {
|
||||
/**
|
||||
* 矩阵转置
|
||||
* @param matrix
|
||||
* @returns
|
||||
*/
|
||||
static transpose(matrix: number[][]): number[][] {
|
||||
return matrix[0].map((_, colIndex) => matrix.map(row => row[colIndex]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 矩阵乘法
|
||||
* @param a
|
||||
* @param b
|
||||
* @returns
|
||||
*/
|
||||
static multiplyMatrices(a: number[][], b: number[][]): number[][] {
|
||||
const result: number[][] = [];
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
// result[i] = [];
|
||||
result.push([]);
|
||||
for (let j = 0; j < b[0].length; j++) {
|
||||
let sum = 0;
|
||||
for (let k = 0; k < b.length; k++) {
|
||||
sum += a[i][k] * b[k][j];
|
||||
}
|
||||
// result[i][j] = sum;
|
||||
result[i].push(sum);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 矩阵求逆
|
||||
* @param matrix
|
||||
* @returns
|
||||
*/
|
||||
static inverseMatrix(matrix: number[][]): number[][] {
|
||||
const size = matrix.length;
|
||||
const augmented = matrix.map((row, i) => [
|
||||
...row,
|
||||
...Array<number>(size).fill(0).map((_, j) => (i === j ? 1 : 0))
|
||||
]);
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
let maxRow = i;
|
||||
for (let k = i + 1; k < size; k++) {
|
||||
if (Math.abs(augmented[k][i]) > Math.abs(augmented[maxRow][i])) {
|
||||
maxRow = k;
|
||||
}
|
||||
}
|
||||
|
||||
let temp = augmented[i];
|
||||
augmented[i] = augmented[maxRow];
|
||||
augmented[maxRow] = temp;
|
||||
// [augmented[i], augmented[maxRow]] = [augmented[maxRow], augmented[i]];
|
||||
|
||||
const divisor = augmented[i][i];
|
||||
for (let j = 0; j < augmented[0].length; j++) {
|
||||
augmented[i][j] /= divisor;
|
||||
}
|
||||
|
||||
for (let k = 0; k < size; k++) {
|
||||
if (k !== i) {
|
||||
const factor = augmented[k][i];
|
||||
for (let j = 0; j < augmented[0].length; j++) {
|
||||
augmented[k][j] -= factor * augmented[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return augmented.map(row => row.slice(size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 最小二乘法
|
||||
* @param m
|
||||
* @param sampleData
|
||||
* @returns
|
||||
*/
|
||||
static leastSquareMethod(m: number, sampleData: Array<IPoint>): FitRes {
|
||||
const x = sampleData.map(d => d.x);
|
||||
const y = sampleData.map(d => d.y);
|
||||
|
||||
const A = x.map(xi => {
|
||||
const row: number[] = [];
|
||||
for (let i = m; i >= 0; i--) {
|
||||
row.push(Math.pow(xi, i));
|
||||
}
|
||||
return row;
|
||||
});
|
||||
|
||||
const yVec = y.map(yi => [yi]);
|
||||
|
||||
const At = Matrix.transpose(A);
|
||||
const AtA = Matrix.multiplyMatrices(At, A);
|
||||
const Aty = Matrix.multiplyMatrices(At, yVec);
|
||||
|
||||
const invAtA = Matrix.inverseMatrix(AtA);
|
||||
const theta = Matrix.multiplyMatrices(invAtA, Aty);
|
||||
|
||||
const residuals = yVec.map((yi, i) => [yi[0] - A[i].reduce((sum, aij, j) => sum + aij * theta[j][0], 0)]);
|
||||
const residualsSquaredSum = residuals.reduce((sum, ri) => sum + ri[0] ** 2, 0);
|
||||
|
||||
const degreesOfFreedom = y.length - (m + 1);
|
||||
const meanSquaredError = residualsSquaredSum / degreesOfFreedom;
|
||||
|
||||
const covarianceMatrix = invAtA.map(row => row.map(value => value * meanSquaredError));
|
||||
|
||||
const fitRes: FitRes = {
|
||||
theta: theta.map(row => row[0]),
|
||||
covarianceMatrix: covarianceMatrix
|
||||
};
|
||||
|
||||
return fitRes;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import {IPoint} from "../drawBoard/DrawPoint"
|
||||
import {Matrix} from "./least_square"
|
||||
|
||||
export class Logarithm {
|
||||
static logFunc(x: number, a: number, b: number, c: number): number {
|
||||
return a * Math.log(x) / Math.log(b) + c;
|
||||
}
|
||||
|
||||
linearRegressionLog(sampleData: Array<IPoint>, base: number): number[] {
|
||||
const A = sampleData.map((value) => [Math.log(value.x) / Math.log(base), 1]);
|
||||
const y = sampleData.map((value) => value.y);
|
||||
|
||||
const At = Matrix.transpose(A);
|
||||
const AtA = Matrix.multiplyMatrices(At, A);
|
||||
const Aty = Matrix.multiplyMatrices(At, y.map(v => [v]));
|
||||
|
||||
const invAtA = Matrix.inverseMatrix(AtA);
|
||||
const theta = Matrix.multiplyMatrices(invAtA, Aty);
|
||||
|
||||
const a: number = theta.map(row => row[0])[0];
|
||||
const c: number = theta.map(row => row[0])[1];
|
||||
|
||||
return [ a, c ];
|
||||
}
|
||||
|
||||
static gradientDescentLog(sampleData: Array<IPoint>, learningRate: number = 0.00001, epochs: number = 100000): number[] {
|
||||
let a = 0.001; // 初始化参数
|
||||
let b = 2; // 初始对数底数,需 > 0 且 ≠ 1
|
||||
let c = 0.001;
|
||||
|
||||
const n = sampleData.length;
|
||||
|
||||
for (let epoch = 0; epoch < epochs; epoch++) {
|
||||
let aGrad = 0;
|
||||
let bGrad = 0;
|
||||
let cGrad = 0;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const x = sampleData[i].x;
|
||||
const y = sampleData[i].y;
|
||||
let yPredict = Logarithm.logFunc(x, a, b, c);
|
||||
let logX = Math.log(x);
|
||||
let logB = Math.log(b);
|
||||
if (x > 0 && b > 0 && b != 1) {
|
||||
yPredict = Logarithm.logFunc(x, a, b, c);
|
||||
logX = Math.log(x);
|
||||
logB = Math.log(b);
|
||||
} else continue;
|
||||
aGrad += -2 * (y - yPredict) * (logX / logB);
|
||||
bGrad += -2 * (y - yPredict) * (-a * logX / (logB * logB) * (1 / b)); // 链式法则计算 b 的梯度
|
||||
cGrad += -2 * (y - yPredict);
|
||||
}
|
||||
|
||||
a -= (aGrad / n) * learningRate;
|
||||
c -= (cGrad / n) * learningRate;
|
||||
if (b - (bGrad / n) * learningRate <= 0) break;
|
||||
else b -= (bGrad / n) * learningRate;
|
||||
if (Number.isNaN(a) || Number.isNaN(b) || Number.isNaN(c)) {
|
||||
console.error(`Parameters became NaN at epoch ${epoch + 1}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [ a, b, c ];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,425 @@
|
|||
import { IPoint, PointType } from './DrawPoint';
|
||||
//标准值
|
||||
/**
|
||||
* imageShow组件的大小(vp)
|
||||
*/
|
||||
export const I_WIDTH = 480;//3568 340
|
||||
/**
|
||||
* imageShow组件的大小(vp)
|
||||
*/
|
||||
export const I_HEIGHT = 250;//3568 160
|
||||
/**
|
||||
* 第一个X刻度对应的值
|
||||
*/
|
||||
const X_VALUE = 100
|
||||
/**
|
||||
* 第一个Y刻度对应的值
|
||||
*/
|
||||
const Y_VALUE = 100
|
||||
|
||||
export const SETTING:RenderingContextSettings = new RenderingContextSettings(true)
|
||||
export const CONTEXT:CanvasRenderingContext2D = new CanvasRenderingContext2D(SETTING)
|
||||
export const FIT_CONTEXT:CanvasRenderingContext2D = new CanvasRenderingContext2D(SETTING)
|
||||
CONTEXT.font = '10px sans-serif'
|
||||
export enum Quadrant{
|
||||
ONE = 1,
|
||||
FOUR = 4
|
||||
}
|
||||
|
||||
export abstract class BoardInfo{
|
||||
/**
|
||||
* x刻度之间的像素距离
|
||||
* 初始化后不允许修改
|
||||
*/
|
||||
XGapLen:number
|
||||
/**
|
||||
* y刻度之间的像素距离
|
||||
* 初始化后不允许修改
|
||||
*/
|
||||
YGapLen:number
|
||||
/**
|
||||
* x刻度个数 (+)
|
||||
* 初始化后不允许修改
|
||||
*/
|
||||
XCount:number
|
||||
/**
|
||||
* y刻度个数 (+)
|
||||
* 初始化后不允许修改
|
||||
*/
|
||||
YCount:number
|
||||
/**
|
||||
* 原点的x坐标
|
||||
* 初始化后不可修改
|
||||
*/
|
||||
X_centre:number = 0
|
||||
/**
|
||||
* 原点的y坐标
|
||||
* 初始化后不可修改
|
||||
*/
|
||||
Y_centre:number = 0
|
||||
/**
|
||||
* canvas组件的margin (不包括箭头)
|
||||
* 初始化后不可修改
|
||||
*/
|
||||
margin:number
|
||||
/**
|
||||
* 显示象限
|
||||
* 初始化后不可修改
|
||||
*/
|
||||
quadrant:Quadrant
|
||||
/**
|
||||
* 画板的宽
|
||||
* 初始化后不可修改
|
||||
*/
|
||||
width:number
|
||||
/**
|
||||
* 画板的高
|
||||
* 初始化后不可修改
|
||||
*/
|
||||
height:number
|
||||
|
||||
//以下属性可修改
|
||||
|
||||
/**
|
||||
* 单位X刻度对应的虚拟像素
|
||||
* 根据倍数动态修正
|
||||
*/
|
||||
XUnit2vp:number
|
||||
/**
|
||||
* 单位Y刻度对应虚拟像素
|
||||
* 根据倍数动态修正
|
||||
*/
|
||||
YUnit2vp:number
|
||||
/**
|
||||
* 第一个X刻度对应的值 (+)
|
||||
* 根据倍数动态修正
|
||||
*/
|
||||
XValue:number = X_VALUE
|
||||
/**
|
||||
* 第一个Y刻度对应的值 (+)
|
||||
* 根据倍数动态修正
|
||||
*/
|
||||
YValue:number = Y_VALUE
|
||||
|
||||
/**
|
||||
* 虚拟点集
|
||||
*/
|
||||
virtualPoints:Array<IPoint> = []
|
||||
|
||||
/**
|
||||
* 拟合后得到的函数的点集
|
||||
*/
|
||||
boardPoints:Array<IPoint> = []
|
||||
|
||||
constructor(margin:number,quadrant:Quadrant,Xgl:number,Ygl:number,XC:number,YC:number) {
|
||||
this.margin = margin
|
||||
this.quadrant = quadrant
|
||||
//画板属性
|
||||
this.width = I_WIDTH - 2 * this.margin
|
||||
this.height = I_HEIGHT - 2 * this.margin
|
||||
//定义单位刻度对应像素
|
||||
this.XGapLen = Xgl
|
||||
this.YGapLen = Ygl
|
||||
this.XUnit2vp = Xgl/X_VALUE
|
||||
this.YUnit2vp = Ygl/Y_VALUE
|
||||
//定义个数
|
||||
this.XCount = XC
|
||||
this.YCount = YC
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制坐标轴
|
||||
*/
|
||||
public abstract drawAxis();
|
||||
|
||||
/**
|
||||
* 设置画板上的xy数对
|
||||
* 同时根据xy数对计算出合适的
|
||||
*
|
||||
* 初始化点集 需要保证xy都有赋值
|
||||
*
|
||||
* 返回virtualPoints
|
||||
*/
|
||||
public initMap(virtualPoints?:Array<IPoint>){
|
||||
this.virtualPoints = []
|
||||
if(virtualPoints){
|
||||
this.virtualPoints = virtualPoints
|
||||
}else{
|
||||
let x = 0;
|
||||
let y = 0
|
||||
if(this.quadrant == Quadrant.ONE){
|
||||
for (let index = 0; index < 20; index++) {
|
||||
x = Math.random()*1000
|
||||
y = Math.random()*1000
|
||||
this.virtualPoints.push({x:x,y:y,pointType:PointType.CONTINUE})
|
||||
}
|
||||
}else{
|
||||
for (let index = 0; index < 20; index++) {
|
||||
x = ((Math.random()*2)-1)*500
|
||||
y = ((Math.random()*2)-1)*500
|
||||
this.virtualPoints.push({x:x,y:y,pointType:PointType.CONTINUE})
|
||||
}
|
||||
}
|
||||
}
|
||||
this.boardPoints = []
|
||||
this.transToBoardPoints()
|
||||
|
||||
return this.virtualPoints
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绘制拟合函数
|
||||
*/
|
||||
public drawFitFunc(points:Array<IPoint>){
|
||||
this.transToBoardPoints(points)
|
||||
this.drawGraph(FIT_CONTEXT,points)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制散点图
|
||||
*/
|
||||
public drawPoints(context:CanvasRenderingContext2D = CONTEXT,boardPoints:Array<IPoint> = this.boardPoints){
|
||||
context.beginPath()
|
||||
for (let index = 0; index < boardPoints.length; index++) {
|
||||
context.fillRect(vp2px(boardPoints[index].x-1),vp2px(boardPoints[index].y-1),vp2px(1.5),vp2px(1.5))
|
||||
}
|
||||
context.stroke()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制折线
|
||||
*/
|
||||
public drawGraph(context:CanvasRenderingContext2D = FIT_CONTEXT,boardPoints:Array<IPoint> = this.boardPoints){
|
||||
FIT_CONTEXT.strokeStyle = 'red'
|
||||
let isMeetDisContinue = false
|
||||
let i = 0
|
||||
while(boardPoints[i].pointType == PointType.DISCONTINUE && i <= boardPoints.length - 1){
|
||||
i++
|
||||
}
|
||||
if(i == boardPoints.length){
|
||||
//全间断点
|
||||
return
|
||||
}
|
||||
if(i != 0){
|
||||
context.fillRect(vp2px(boardPoints[i].x - 1),vp2px(boardPoints[i].y - 1),vp2px(1.5),vp2px(1.5))
|
||||
}
|
||||
context.moveTo(vp2px(boardPoints[i].x),vp2px(boardPoints[i].y))
|
||||
//间断点画圆
|
||||
for (let index = i + 1; index < boardPoints.length ; index++) {
|
||||
if(boardPoints[index].pointType == PointType.DISCONTINUE){
|
||||
isMeetDisContinue = true
|
||||
continue
|
||||
}
|
||||
if(isMeetDisContinue){
|
||||
context.moveTo(vp2px(boardPoints[index].x),vp2px(boardPoints[index].y))
|
||||
context.fillRect(vp2px(boardPoints[index].x - 1),vp2px(boardPoints[index].y - 1),vp2px(1.5),vp2px(1.5))
|
||||
isMeetDisContinue = false
|
||||
}else{
|
||||
context.lineTo(vp2px(boardPoints[index].x),vp2px(boardPoints[index].y))
|
||||
}
|
||||
}
|
||||
context.stroke()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将虚拟点集(二维)转变为画布点集
|
||||
*/
|
||||
protected transToBoardPoints(virtualPoints?:Array<IPoint>){
|
||||
if(virtualPoints){
|
||||
for (let index = 0; index < virtualPoints.length; index++) {
|
||||
virtualPoints[index].x = this.getXPosition(virtualPoints[index].x)
|
||||
virtualPoints[index].y = this.getYPosition(virtualPoints[index].y)
|
||||
virtualPoints[index].pointType = virtualPoints[index].pointType
|
||||
}
|
||||
return
|
||||
}
|
||||
for (let index = 0; index < this.virtualPoints.length; index++) {
|
||||
this.boardPoints.push({x:this.getXPosition(this.virtualPoints[index].x),y:this.getYPosition(this.virtualPoints[index].y),pointType:this.virtualPoints[index].pointType})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将虚拟坐标转变为画布坐标x(二维)
|
||||
*/
|
||||
private getXPosition(x:number){
|
||||
return this.X_centre + this.XUnit2vp*x
|
||||
}
|
||||
/**
|
||||
* 将虚拟坐标转变为画布坐标x(二维)
|
||||
*/
|
||||
private getYPosition(y:number){
|
||||
return this.Y_centre - this.YUnit2vp*y
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class board4Q2D extends BoardInfo{
|
||||
|
||||
constructor() {
|
||||
super(5,Quadrant.FOUR,43,21,5,5);//根据具体环境配置
|
||||
this.X_centre = (2 * this.margin + this.width)/2
|
||||
this.Y_centre = (2 * this.margin + this.height)/2
|
||||
}
|
||||
|
||||
public drawAxis(){
|
||||
CONTEXT.beginPath()
|
||||
CONTEXT.moveTo(vp2px(this.margin),vp2px(this.Y_centre))
|
||||
CONTEXT.lineTo(vp2px(this.width + this.margin),vp2px(this.Y_centre))
|
||||
CONTEXT.lineTo(vp2px(this.width + this.margin),vp2px(this.Y_centre + 3))
|
||||
CONTEXT.lineTo(vp2px(this.width + this.margin + 3),vp2px(this.Y_centre))
|
||||
CONTEXT.lineTo(vp2px(this.width + this.margin),vp2px(this.Y_centre - 3))
|
||||
CONTEXT.lineTo(vp2px(this.width + this.margin),vp2px(this.Y_centre))
|
||||
CONTEXT.moveTo(vp2px(this.X_centre),vp2px(this.height + this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre),vp2px(this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre - 3),vp2px(this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre),vp2px(this.margin - 3))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre + 3),vp2px(this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre),vp2px(this.margin))
|
||||
CONTEXT.fill()
|
||||
CONTEXT.fillText("0",vp2px(this.X_centre + 2),vp2px(this.Y_centre + 15))
|
||||
let valueX = this.X_centre - this.XValue * this.XUnit2vp * (this.XCount + 1)
|
||||
let valueY = this.Y_centre + 6
|
||||
let value = -this.XValue * this.XCount
|
||||
let count = 2 * this.XCount
|
||||
//绘制X轴
|
||||
while(--count >= 0) {
|
||||
valueX += this.XGapLen
|
||||
if(value == 0){
|
||||
value += this.XValue
|
||||
valueX += this.XGapLen
|
||||
}
|
||||
CONTEXT.moveTo(vp2px(valueX),vp2px(this.Y_centre))
|
||||
CONTEXT.lineTo(vp2px(valueX),vp2px(this.Y_centre - 5))
|
||||
CONTEXT.fillText(value.toString(),vp2px(valueX - this.getXTextBias(value.toString()) - 6),vp2px(valueY + 7))
|
||||
value += this.XValue
|
||||
}
|
||||
//绘制Y轴
|
||||
valueX = this.X_centre
|
||||
valueY = this.Y_centre + this.YValue * this.YUnit2vp * (this.YCount + 1)
|
||||
value = -this.YValue * this.YCount
|
||||
count = 2 * this.YCount
|
||||
while(--count >= 0) {
|
||||
valueY -= this.YGapLen
|
||||
if(value == 0){
|
||||
value += this.YValue
|
||||
valueY -= this.YGapLen
|
||||
}
|
||||
CONTEXT.moveTo(vp2px(this.X_centre),vp2px(valueY))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre + 3),vp2px(valueY))
|
||||
CONTEXT.fillText(value.toString(),vp2px(valueX - this.getYTextBias(value.toString()) + 15),vp2px(valueY + 5))
|
||||
value += this.YValue
|
||||
}
|
||||
CONTEXT.stroke()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修正后的X刻度对应的文本的显示横向偏移量
|
||||
*/
|
||||
private getXTextBias(value:string){
|
||||
|
||||
let bias = value.length
|
||||
let flag = 1
|
||||
if(value[0] == '-'){
|
||||
flag = -2
|
||||
}
|
||||
return (bias * (bias - 1))/2 + 1 + flag * this.XGapLen/50 ;
|
||||
}
|
||||
/**
|
||||
* 获取修正后的Y刻度对应的文本的显示横向偏移量
|
||||
*/
|
||||
private getYTextBias(value:string){
|
||||
let bias = value.length
|
||||
if(bias == 1){
|
||||
return 1
|
||||
}
|
||||
return bias * 3.5 + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class board1Q2D extends BoardInfo{
|
||||
|
||||
constructor() {
|
||||
super(20,Quadrant.ONE,42,20,10,10);//根据具体环境配置
|
||||
|
||||
this.X_centre = this.margin
|
||||
this.Y_centre = this.margin + this.height
|
||||
}
|
||||
|
||||
public drawAxis(){
|
||||
CONTEXT.beginPath()
|
||||
CONTEXT.moveTo(vp2px(this.margin),vp2px(this.margin + this.height))
|
||||
CONTEXT.lineTo(vp2px(this.margin),vp2px(this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.margin - 3),vp2px(this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.margin),vp2px(this.margin - 3))
|
||||
CONTEXT.lineTo(vp2px(this.margin + 3),vp2px(this.margin))
|
||||
CONTEXT.lineTo(vp2px(this.margin),vp2px(this.margin))
|
||||
CONTEXT.fill()
|
||||
CONTEXT.moveTo(vp2px(this.margin),vp2px(this.margin + this.height))
|
||||
CONTEXT.lineTo(vp2px(this.margin + this.width),vp2px(this.margin + this.height))
|
||||
CONTEXT.lineTo(vp2px(this.margin + this.width),vp2px(this.margin + this.height + 3))
|
||||
CONTEXT.lineTo(vp2px(this.margin + this.width + 3),vp2px(this.margin + this.height))
|
||||
CONTEXT.lineTo(vp2px(this.margin + this.width),vp2px(this.margin + this.height - 3))
|
||||
CONTEXT.lineTo(vp2px(this.margin + this.width),vp2px(this.margin + this.height))
|
||||
CONTEXT.fill()
|
||||
CONTEXT.fillText("0",vp2px(this.X_centre - 10),vp2px(this.Y_centre + 10))
|
||||
let valueX = this.X_centre
|
||||
let valueY = this.Y_centre + 6
|
||||
let value = this.XValue
|
||||
let count = this.XCount
|
||||
//x轴
|
||||
while(--count >= 0) {
|
||||
valueX += this.XGapLen
|
||||
CONTEXT.moveTo(vp2px(valueX),vp2px(this.Y_centre))
|
||||
CONTEXT.lineTo(vp2px(valueX),vp2px(this.Y_centre - 5))
|
||||
CONTEXT.fillText(value.toString(),vp2px(valueX-this.getXTextBias(value.toString()) - 10),vp2px(valueY + 10))
|
||||
value += this.XValue
|
||||
}
|
||||
//y轴
|
||||
valueX = this.X_centre
|
||||
valueY = this.Y_centre
|
||||
value = this.YValue
|
||||
count = this.YCount
|
||||
while(--count >= 0) {
|
||||
valueY -= this.YGapLen
|
||||
if(value == 0){
|
||||
value += this.YValue
|
||||
valueY -= this.YGapLen
|
||||
}
|
||||
CONTEXT.moveTo(vp2px(this.X_centre),vp2px(valueY))
|
||||
CONTEXT.lineTo(vp2px(this.X_centre + 5),vp2px(valueY))
|
||||
CONTEXT.fillText(value.toString(),vp2px(valueX + 20 - this.getYTextBias(value.toString())),vp2px(valueY + 3))
|
||||
value += this.YValue
|
||||
}
|
||||
CONTEXT.stroke()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修正后的X刻度对应的文本的显示横向偏移量
|
||||
*/
|
||||
private getXTextBias(value:string){
|
||||
|
||||
let bias = value.length
|
||||
let flag = 1
|
||||
if(value[0] == '-'){
|
||||
flag = -2
|
||||
}
|
||||
return (bias * (bias - 1))/2 + 1 + flag * this.XGapLen/50;
|
||||
}
|
||||
/**
|
||||
* 获取修正后的Y刻度对应的文本的显示横向偏移量
|
||||
*/
|
||||
private getYTextBias(value:string){
|
||||
let bias = value.length
|
||||
if(bias == 1){
|
||||
return 1
|
||||
}
|
||||
return bias * 3.5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export let B4Q2D:BoardInfo = new board4Q2D()
|
||||
export let B1Q2D:BoardInfo = new board1Q2D()
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
export enum PointType{
|
||||
DISCONTINUE = 0,
|
||||
CONTINUE = 1
|
||||
}
|
||||
/**
|
||||
* 虚拟点集
|
||||
* 根据函数关系和步长、个数直接得到的点集
|
||||
*/
|
||||
export interface IPoint{
|
||||
x:number
|
||||
|
||||
y:number
|
||||
|
||||
pointType:PointType
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"module": {
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.FILE_ACCESS_MANAGER"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED"
|
||||
}
|
||||
|
||||
],
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"src": [
|
||||
"pages/Index"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "模块描述"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import abilityTest from './Ability.test';
|
||||
|
||||
export default function testsuite() {
|
||||
abilityTest();
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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%')
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"src": [
|
||||
"testability/pages/Index"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import localUnitTest from './LocalUnit.test';
|
||||
|
||||
export default function testsuite() {
|
||||
localUnitTest();
|
||||
}
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
|
@ -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. */
|
||||
}
|
||||
|
|
@ -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}" "$@"
|
||||
|
|
@ -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
|
||||
|
|
@ -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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@ohos/hypium": "1.0.11"
|
||||
},
|
||||
"author": "",
|
||||
"name": "myapplication",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
|
@ -0,0 +1,18 @@
|
|||
# drawing
|
||||
### 1、项目说明
|
||||
本项目拟实现一个复杂函数计算及其曲线绘制系统,基本功能为:(1)用户能够设置自变量的步长和个数;(2)程序会根据用户设置的函数以及步长和个数信息,解析函数获取点集并执行绘制的操作(3)用户能够自定义函数并保存在可识别函数中,使其能够参与新函数的构造;(4)用户能够切换函数分别在二维和三维环境下的绘制;(5)程序会解析用户输入的函数,将多项式分成单项式并以“树”的形式呈现。
|
||||
### 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 项目主要函数调用关系图
|
||||
<img src="figs/relation.png" width=80% /></center>
|
||||
|
||||
|
||||
图3.2 项目输出界面演进图
|
||||
<img src="figs/evolution.png" width=80% /></center>
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/node_modules
|
||||
/oh_modules
|
||||
/local.properties
|
||||
/.idea
|
||||
**/build
|
||||
/.hvigor
|
||||
.cxx
|
||||
/.clangd
|
||||
/.clang-format
|
||||
/.clang-tidy
|
||||
**/.test
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "app_name",
|
||||
"value": "MyApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"app": {
|
||||
"signingConfigs": [
|
||||
{
|
||||
"name": "default",
|
||||
"material": {
|
||||
"certpath": "C:\\Users\\cyf20\\.ohos\\config\\openharmony\\auto_ohos_default_draw_com.test.myapplication.cer",
|
||||
"storePassword": "0000001A41B8AC51384118050B5A9A22C9E132AA02BE60C9CBE187B4D7B69A17D2C070E0DF83E06DC1E4",
|
||||
"keyAlias": "debugKey",
|
||||
"keyPassword": "0000001A98556E0863F7805DEA1D57929A5FF99B10F6509C866A4450C6198E40C8F4F0701876AB99F565",
|
||||
"profile": "C:\\Users\\cyf20\\.ohos\\config\\openharmony\\auto_ohos_default_draw_com.test.myapplication.p7b",
|
||||
"signAlg": "SHA256withECDSA",
|
||||
"storeFile": "C:\\Users\\cyf20\\.ohos\\config\\openharmony\\auto_ohos_default_draw_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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
||||
|
|
@ -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",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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. */
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"license": "",
|
||||
"devDependencies": {},
|
||||
"author": "",
|
||||
"name": "entry",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
@Observed
|
||||
export class basicFuncInfo{
|
||||
/**
|
||||
* 函数名
|
||||
*/
|
||||
private funcName:string
|
||||
/**
|
||||
* 函数体
|
||||
*/
|
||||
private funcBody:string
|
||||
|
||||
constructor(funcName: string, funcBody: string) {
|
||||
this.funcName = funcName
|
||||
this.funcBody = funcBody
|
||||
}
|
||||
|
||||
setFuncName(name:string){
|
||||
this.funcName = name
|
||||
}
|
||||
setFuncBody(body:string){
|
||||
this.funcBody = body
|
||||
}
|
||||
getFuncName(){
|
||||
return this.funcName
|
||||
}
|
||||
getFuncBody(){
|
||||
return this.funcBody
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
export class VALUE{
|
||||
/**
|
||||
* 栏颜色
|
||||
*/
|
||||
static readonly COLOR_BAR = "#EDEDED"
|
||||
/**
|
||||
* 字体大小(大)
|
||||
*/
|
||||
static readonly BIG_SIZE_FONT = 15
|
||||
/**
|
||||
* 字体大小(大)
|
||||
*/
|
||||
static readonly MIDDLE_SIZE_FONT = 12
|
||||
/**
|
||||
* 字体大小(小)
|
||||
*/
|
||||
static readonly SMALL_SIZE_FONT = 10
|
||||
/**
|
||||
* controlBar的背景色
|
||||
*/
|
||||
static readonly COLOR_COMP_CONTROLBAR_BG = "#9490D9"
|
||||
/**
|
||||
* controlBar的栏颜色
|
||||
*/
|
||||
static readonly COLOR_COMP_CONTROLBAR_BAR = "#8767F7"
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import { basicFuncInfo } from '../common/BasicFuncInfo'
|
||||
import { VALUE } from '../common/CommonValue'
|
||||
|
||||
@Component
|
||||
export struct basicFunctionsBar {
|
||||
@Consume('basicFuncList') basicFuncList:Array<basicFuncInfo>
|
||||
build() {
|
||||
Column(){
|
||||
Text("可识别基本函数")
|
||||
.fontSize(VALUE.BIG_SIZE_FONT)
|
||||
.fontColor(Color.Red)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
Row(){
|
||||
Row(){
|
||||
Text('函数名')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
}.justifyContent(FlexAlign.Center).width('28%')
|
||||
Blank()
|
||||
Row(){
|
||||
Text('函数体')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
}.justifyContent(FlexAlign.Center).width('68%')
|
||||
}.width('100%').padding({left:5,right:5})
|
||||
List(){
|
||||
ForEach(this.basicFuncList,(item:basicFuncInfo)=>{
|
||||
ListItem(){
|
||||
basicFuncItem({basicFunc:item})
|
||||
}
|
||||
})
|
||||
}.layoutWeight(1).width('100%')
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
struct basicFuncItem {
|
||||
@ObjectLink basicFunc:basicFuncInfo
|
||||
build() {
|
||||
Row(){
|
||||
Scroll(){
|
||||
Row(){
|
||||
Text(this.basicFunc.getFuncName())
|
||||
.fontSize(VALUE.MIDDLE_SIZE_FONT)
|
||||
}.justifyContent(FlexAlign.Center)
|
||||
}.width('28%').scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)
|
||||
Blank()
|
||||
Scroll(){
|
||||
Row(){
|
||||
Text(this.basicFunc.getFuncBody())
|
||||
.fontSize(VALUE.MIDDLE_SIZE_FONT)
|
||||
}.justifyContent(FlexAlign.Center)
|
||||
}.width('68%').scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)
|
||||
}.width('100%').height(25)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
import promptAction from '@ohos.promptAction'
|
||||
import { basicFuncInfo } from '../common/BasicFuncInfo'
|
||||
import { VALUE } from '../common/CommonValue'
|
||||
import { IBoardOption, BoardType, Dimension, Quadrant } from '../utils/drawBoard/BoardType'
|
||||
import { FuncType } from '../utils/drawBoard/drawPoint'
|
||||
import { handle } from '../utils/handleFunc/processFunc'
|
||||
|
||||
@Component
|
||||
export struct controlBar {
|
||||
@Link inputFuncBody:string
|
||||
|
||||
@Link outputState:boolean
|
||||
|
||||
@Link boardOption:IBoardOption
|
||||
|
||||
build() {
|
||||
Column({space:5}){
|
||||
InputFunc({funcBody:$inputFuncBody,outputState:$outputState})
|
||||
InputPara({boardOption:$boardOption})
|
||||
}.width('100%').height('100%').backgroundColor(VALUE.COLOR_COMP_CONTROLBAR_BG).padding({top:5,bottom:5})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
struct InputFunc {
|
||||
@Consume isFuncChange:boolean
|
||||
@Link funcBody:string
|
||||
@Link outputState:boolean
|
||||
@Consume('basicFuncList') basicFuncList:Array<basicFuncInfo>
|
||||
@Consume dimension:Dimension
|
||||
|
||||
build() {
|
||||
Row({space:10}){
|
||||
Text('')
|
||||
.width(15)
|
||||
.height(30)
|
||||
Stack(){
|
||||
Rect().width('100%').height('100%').fill(Color.White)
|
||||
TextInput({text:this.funcBody}).width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
this.funcBody = value
|
||||
this.isFuncChange = true
|
||||
})
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
Button('用户命名并新增基本函数')
|
||||
.height(30)
|
||||
.backgroundColor(Color.Yellow)
|
||||
.fontColor(Color.Black)
|
||||
.fontSize(VALUE.BIG_SIZE_FONT)
|
||||
.width(210)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
if(handle.isValidFunc(this.funcBody)) {
|
||||
handle.separateFunc(this.funcBody);
|
||||
let funcName = handle.funcHead;
|
||||
let funcBody = handle.funcBody;
|
||||
if (funcName && handle.getFuncType(this.dimension) != FuncType.NONE) {
|
||||
let info: basicFuncInfo = new basicFuncInfo(funcName, funcBody)
|
||||
this.basicFuncList.push(info)
|
||||
}
|
||||
else {
|
||||
promptAction.showToast({
|
||||
message: "不支持此函数类型或未为函数命名",
|
||||
duration: 1500,
|
||||
bottom: 20
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
Button('输出')
|
||||
.height(30)
|
||||
.backgroundColor(Color.Pink)
|
||||
.fontColor(Color.Black)
|
||||
.fontSize(VALUE.BIG_SIZE_FONT)
|
||||
.width(70)
|
||||
.type(ButtonType.Normal)
|
||||
.onClick(()=>{
|
||||
for (let i: number = 0; i < this.basicFuncList.length; i++) {
|
||||
let standard: RegExp = new RegExp(`(^|[^a-zA-Z])${this.basicFuncList[i].getFuncName().replace('(', '\\(').replace(')', '\\)')}($|[^a-zA-Z])`);
|
||||
if (standard.test(this.funcBody)) {
|
||||
let index: number = this.funcBody.indexOf(this.basicFuncList[i].getFuncName());
|
||||
let length: number = this.basicFuncList[i].getFuncName().length;
|
||||
this.funcBody = this.funcBody.substring(0, index) + "(" + this.basicFuncList[i].getFuncBody() + ")" + this.funcBody.substring(index + length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle.isValidFunc(this.funcBody)) {
|
||||
if (this.dimension == Dimension.TWO) {
|
||||
handle.getPoints2D();
|
||||
} else {
|
||||
handle.getPoints3D();
|
||||
for (let i = 0; i < handle.coPot.length; i++) {
|
||||
console.log(`(${handle.coPot[i].x}, ${handle.coPot[i].y}, ${handle.coPot[i].z})`);
|
||||
}
|
||||
|
||||
}
|
||||
this.outputState = !this.outputState;
|
||||
}
|
||||
})
|
||||
}.width('100%').padding({left:10,right:10})
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
struct InputPara {
|
||||
@Link boardOption:IBoardOption
|
||||
@Consume isFuncChange:boolean
|
||||
@Consume quadrant:Quadrant
|
||||
@Consume dimension:Dimension
|
||||
|
||||
@Builder xStep(){
|
||||
Row(){
|
||||
Text('x步长')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
Stack(){
|
||||
Rect().width('100%').height('100%').fill(Color.White)
|
||||
TextInput().width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
if(this.dimension == Dimension.THREE && Number(value) < 1){
|
||||
WARNING("输入的x步长过小!x步长最小为1")
|
||||
return
|
||||
}
|
||||
this.boardOption.xStep = Number(value)
|
||||
handle.xStep = this.boardOption.xStep;
|
||||
})
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
}.width('100%').layoutWeight(0.5)
|
||||
}
|
||||
|
||||
@Builder xNumber(){
|
||||
Row(){
|
||||
Text('x个数')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
Stack(){
|
||||
Rect().width('100%').height('100%').fill(Color.White)
|
||||
TextInput().width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
this.boardOption.xNumber = Number(value)
|
||||
handle.xCount = this.boardOption.xNumber;
|
||||
})
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
}.width('100%').layoutWeight(0.5)
|
||||
}
|
||||
|
||||
@Builder yStep(){
|
||||
Row(){
|
||||
Text('y步长')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
Stack(){
|
||||
Rect().width('100%').height('100%').fill(Color.White)
|
||||
TextInput({text:this.boardOption.yStep.toString()}).width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
this.boardOption.yStep = Number(value)
|
||||
handle.yStep = this.boardOption.yStep;
|
||||
})
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
}.width('100%').layoutWeight(0.5)
|
||||
}
|
||||
|
||||
@Builder quadrantSwitch(quadrant:Quadrant){
|
||||
Row(){
|
||||
Radio({value:quadrant.toString(),group:"Quadrant"})
|
||||
.checked(this.quadrant == quadrant)
|
||||
.onClick(()=>{
|
||||
this.quadrant = quadrant
|
||||
this.isFuncChange = true
|
||||
if(quadrant == Quadrant.FOUR){
|
||||
handle.setEdge(-10, 10);
|
||||
}else{
|
||||
handle.setEdge(0, 20);
|
||||
}
|
||||
handle.updateEdge(this.boardOption.xAmplification);
|
||||
})
|
||||
Text(quadrant == Quadrant.ONE?"一象限":"四象限")
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
}.width('100%').height('50%').alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
|
||||
}
|
||||
|
||||
@Builder dimensionSwitch(dimension:Dimension){
|
||||
Row(){
|
||||
Radio({value:dimension.toString(),group:"TwoDimensional"})
|
||||
.checked(this.dimension == dimension)
|
||||
.onClick(()=>{
|
||||
this.boardOption = {
|
||||
xStep:0.1,
|
||||
xNumber:0,//
|
||||
yStep:1,
|
||||
yNumber:0,
|
||||
xAmplification:1,
|
||||
yAmplification:1,
|
||||
zAmplification:1
|
||||
}
|
||||
this.dimension = dimension
|
||||
this.quadrant = Quadrant.FOUR
|
||||
this.isFuncChange = true
|
||||
if(dimension == Dimension.TWO){
|
||||
handle.setEdge(-10, 10);
|
||||
handle.updateEdge(this.boardOption.xAmplification);
|
||||
}else{
|
||||
handle.setEdge(-5, 5, 10);
|
||||
handle.updateEdge(this.boardOption.xAmplification, this.boardOption.yAmplification)
|
||||
}
|
||||
})
|
||||
Text(dimension == Dimension.TWO?"二维":"三维")
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
}.width('100%').height('50%').alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
|
||||
}
|
||||
|
||||
@Builder xAmplification(){
|
||||
Row(){
|
||||
Text('x放大倍数')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
Stack(){
|
||||
Rect().width('100%').height('100%').fill(Color.White)
|
||||
TextInput({text:this.boardOption.xAmplification.toString()}).width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
this.boardOption.xAmplification = Number(value)
|
||||
handle.updateEdge(this.boardOption.xAmplification);
|
||||
})
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
}.width('100%').layoutWeight(0.5)
|
||||
}
|
||||
|
||||
@Builder yAmplification(){
|
||||
Row(){
|
||||
Text('y放大倍数')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
Stack(){
|
||||
Rect().width('100%').height('100%').fill(Color.White)
|
||||
TextInput({text:this.boardOption.yAmplification.toString()}).width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
this.boardOption.yAmplification = Number(value)
|
||||
})
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
}.width('100%').layoutWeight(0.5)
|
||||
}
|
||||
|
||||
@Builder zAmplification(){
|
||||
Row(){
|
||||
Text('z放大倍数')
|
||||
.fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
Stack(){
|
||||
TextInput({text:this.boardOption.zAmplification.toString()}).width('100%').height('100%').backgroundColor(Color.White).fontColor(Color.Black).fontSize(VALUE.SMALL_SIZE_FONT)
|
||||
.onChange((value)=>{
|
||||
this.boardOption.zAmplification = Number(value)
|
||||
})
|
||||
.fontSize(10)
|
||||
}.height(30).backgroundColor(Color.White).layoutWeight(1)
|
||||
}.width('100%')
|
||||
}
|
||||
|
||||
|
||||
build() {
|
||||
Row({space:10}){
|
||||
|
||||
Column({space:5}){
|
||||
this.xStep()
|
||||
Blank()
|
||||
if (this.dimension == Dimension.TWO){
|
||||
this.xNumber()
|
||||
}else{
|
||||
this.yStep()
|
||||
}
|
||||
}.width('24%').height('100%').backgroundColor(VALUE.COLOR_COMP_CONTROLBAR_BAR)
|
||||
|
||||
if(this.dimension == Dimension.TWO){
|
||||
Row(){
|
||||
Column(){
|
||||
Text("坐标轴")
|
||||
.fontSize(VALUE.BIG_SIZE_FONT)
|
||||
}.height('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).width('40%')
|
||||
Column(){
|
||||
this.quadrantSwitch(Quadrant.FOUR)
|
||||
this.quadrantSwitch(Quadrant.ONE)
|
||||
}.height('100%').width('60%')
|
||||
}.width('22%').height('100%').backgroundColor(VALUE.COLOR_COMP_CONTROLBAR_BAR)
|
||||
}else{
|
||||
Column(){
|
||||
this.xAmplification()
|
||||
Blank()
|
||||
this.yAmplification()
|
||||
}.width('22%').height('100%').backgroundColor(VALUE.COLOR_COMP_CONTROLBAR_BAR)
|
||||
}
|
||||
|
||||
Row(){
|
||||
Column(){
|
||||
Text("维度")
|
||||
.fontSize(VALUE.BIG_SIZE_FONT)
|
||||
}.height('100%').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).width('40%')
|
||||
Column(){
|
||||
this.dimensionSwitch(Dimension.TWO)
|
||||
this.dimensionSwitch(Dimension.THREE)
|
||||
}.height('100%').width('60%')
|
||||
}.width('22%').height('100%').backgroundColor(VALUE.COLOR_COMP_CONTROLBAR_BAR)
|
||||
|
||||
Column({space:5}){
|
||||
if(this.dimension == Dimension.TWO){
|
||||
this.xAmplification()
|
||||
Blank()
|
||||
this.yAmplification()
|
||||
}else{
|
||||
this.zAmplification()
|
||||
}
|
||||
}.width('28%').height('100%').backgroundColor(VALUE.COLOR_COMP_CONTROLBAR_BAR)
|
||||
|
||||
}.width('100%').layoutWeight(1).padding({left:10,right:10})
|
||||
}
|
||||
}
|
||||
|
||||
let WARNING = (message:string,duration?:number)=>{
|
||||
promptAction.showToast({
|
||||
message:message,
|
||||
duration:duration??2000
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { handle } from '../utils/handleFunc/processFunc'
|
||||
|
||||
@Component
|
||||
export struct functionParsingDiagram {
|
||||
@Link @Watch('initNodeTree') outputState: boolean
|
||||
@Link func: string
|
||||
@State nodeTree: Array<Array<string>> = []
|
||||
|
||||
initNodeTree() {
|
||||
this.nodeTree = handle.createNodeTree(this.func);
|
||||
}
|
||||
|
||||
build() {
|
||||
Column(){
|
||||
if (this.nodeTree) {
|
||||
ForEach(this.nodeTree, (item: string[]) => {
|
||||
Row() {
|
||||
ForEach(item, (node: string) => {
|
||||
Scroll() {
|
||||
Text(node)
|
||||
.textAlign(TextAlign.Center)
|
||||
.fontSize(12)
|
||||
}
|
||||
.scrollable(ScrollDirection.Horizontal)
|
||||
.scrollBar(BarState.Off)
|
||||
.layoutWeight(node.length < 8 ? 1 : 2)
|
||||
.borderWidth(1)
|
||||
.borderColor(Color.Black)
|
||||
.margin(1)
|
||||
})
|
||||
}
|
||||
.size({ width: "100%", height: 25 })
|
||||
})
|
||||
}
|
||||
}.justifyContent(FlexAlign.Start).alignItems(HorizontalAlign.Center).width('100%').height('100%').padding(5)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { AREA_CONTEXT, BoardInfo, CONTEXT, Dimension, } from '../utils/drawBoard/BoardType'
|
||||
|
||||
@Component
|
||||
export struct imageShow {
|
||||
@Prop board:BoardInfo
|
||||
@Prop loop1:string
|
||||
@Prop loop2:string
|
||||
build() {
|
||||
Stack(){
|
||||
Canvas(CONTEXT).width("100%").height(this.loop2)
|
||||
.onReady(()=>{
|
||||
this.board.drawAxis()
|
||||
if (this.board.dimension == Dimension.TWO) {
|
||||
this.board.drawGraph()
|
||||
}
|
||||
})
|
||||
Canvas(AREA_CONTEXT).width("100%").height(this.loop1)
|
||||
.onReady(()=>{
|
||||
if (this.board.dimension == Dimension.THREE) {
|
||||
this.board.drawGraph()
|
||||
}
|
||||
})
|
||||
}.width('100%').height('100%')
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
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';
|
||||
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
import { basicFuncInfo } from '../common/BasicFuncInfo';
|
||||
import { basicFunctionsBar } from '../component/BasicFunctionsBar';
|
||||
import { controlBar } from '../component/ControlBar';
|
||||
import { functionParsingDiagram } from '../component/FunctionParsingDiagram';
|
||||
import { imageShow } from '../component/ImageShow';
|
||||
import { B1Q2D, B4Q2D, B4Q3D, BoardInfo, IBoardOption, Dimension, Quadrant } from '../utils/drawBoard/BoardType';
|
||||
import { FuncType, IVirtualPoints, PointType } from '../utils/drawBoard/drawPoint';
|
||||
import { handle } from '../utils/handleFunc/processFunc'
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
@Provide('basicFuncList') basicFuncList:Array<basicFuncInfo> = []
|
||||
//输入的函数
|
||||
@State inputFunction:string = ""
|
||||
//是否把输入的函数输出成图像
|
||||
@State @Watch('initBoard')outputState:boolean = false
|
||||
//函数是否更改
|
||||
@Provide isFuncChange:boolean = false
|
||||
@State loop1:string = '100%'
|
||||
@State loop2:string = '100%'
|
||||
//board
|
||||
@State board:BoardInfo|null = null
|
||||
@Provide quadrant:Quadrant = Quadrant.FOUR
|
||||
@Provide dimension:Dimension = Dimension.TWO
|
||||
//boardOption
|
||||
//默认的option
|
||||
@State boardOption:IBoardOption = {
|
||||
xStep:0.1,
|
||||
xNumber:0,//
|
||||
yStep:1,
|
||||
yNumber:0,
|
||||
xAmplification:1,
|
||||
yAmplification:1,
|
||||
zAmplification:1
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
handle.setEdge(-10, 10);
|
||||
handle.updateEdge(this.boardOption.xAmplification);
|
||||
}
|
||||
/**
|
||||
* 初始化board
|
||||
*/
|
||||
initBoard(){
|
||||
this.createBoard()
|
||||
|
||||
//二维测试样例
|
||||
//三维测试样例
|
||||
// 1.YC y=3
|
||||
// point.points.push({y:3,pointType:PointType.CONTINUE})
|
||||
// point.funcType = FuncType.YC
|
||||
//2.XC x=3
|
||||
// point.points.push({x:3,pointType:PointType.CONTINUE})
|
||||
// point.funcType = FuncType.XC
|
||||
//3.ZC z=3
|
||||
// point.points.push({z:3,pointType:PointType.CONTINUE})
|
||||
// point.funcType = FuncType.ZC
|
||||
//4.YX y=x^2 [-5,5]
|
||||
// let c=-5
|
||||
// while(c <= 5){
|
||||
// point.points.push({x:c,y:-c*c,pointType:PointType.CONTINUE})
|
||||
// c+=0.1;
|
||||
// }
|
||||
// point.funcType = FuncType.YX
|
||||
//5.YX x=y^2
|
||||
// let c = -5
|
||||
// while(c <= 5){
|
||||
// point.points.push({x:-c*c,y:c,pointType:PointType.CONTINUE})
|
||||
// c+=1;
|
||||
// }
|
||||
// point.funcType = FuncType.YX
|
||||
//6.ZX z=x^2
|
||||
// let c=-5
|
||||
// while(c <= 5){
|
||||
// point.points.push({x:c,z:c*c,pointType:PointType.CONTINUE})
|
||||
// c+=1;
|
||||
// }
|
||||
// point.funcType = FuncType.ZX
|
||||
//6.ZY z=y^2
|
||||
// let c=-5
|
||||
// while(c <= 5){
|
||||
// point.points.push({y:c,z:c*c,pointType:PointType.CONTINUE})
|
||||
// c+=1;
|
||||
// }
|
||||
// point.funcType = FuncType.ZY
|
||||
//7.ZXY z= x^2 + y^2 需要先调整步长 否则报错
|
||||
// let dx = -3
|
||||
// let dy = -3
|
||||
// for(let index = 0;index < 6;index++){//this.boardOption.xNumber
|
||||
// for (let index = 0; index < 6; index++) {//this.boardOption.yNumber
|
||||
// point.points.push({x:dx,y:dy,z:-(dx*dx + dy*dy),pointType:PointType.CONTINUE})
|
||||
// dy+=1
|
||||
// }
|
||||
// dx+=1;
|
||||
// dy=-3
|
||||
// }
|
||||
// point.funcType = FuncType.ZXY
|
||||
// //获取到点集
|
||||
let point:IVirtualPoints = {
|
||||
points: handle.coPot,
|
||||
funcType: handle.getFuncType(this.dimension)
|
||||
}
|
||||
console.log('testTag','points_len:',point.points.length)
|
||||
console.log('testTag','points_funcType:',point.funcType)
|
||||
|
||||
|
||||
//初始化画板下的需要绘制的坐标点
|
||||
if(this.isFuncChange){
|
||||
if(this.board != null){
|
||||
this.boardOption = this.board.initMap(point)
|
||||
}
|
||||
this.isFuncChange = false
|
||||
}else{
|
||||
if (this.board != null) {
|
||||
this.board.setOption(this.boardOption,point)
|
||||
}
|
||||
}
|
||||
|
||||
//刷新canvas组件
|
||||
if(this.loop1 == '100%'){
|
||||
this.loop1 = '101%'
|
||||
}else{
|
||||
this.loop1 = '100%'
|
||||
}
|
||||
if(this.loop2 == '100%'){
|
||||
this.loop2 = '101%'
|
||||
}else{
|
||||
this.loop2 = '100%'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
build() {
|
||||
Column(){
|
||||
|
||||
Row(){
|
||||
if(this.board != null){
|
||||
imageShow({loop1:this.loop1,loop2:this.loop2,board:this.board})
|
||||
.width('50%')
|
||||
.height('100%')
|
||||
.backgroundColor('#F5F5F5')
|
||||
}else{
|
||||
Text('')
|
||||
.width('50%')
|
||||
.height('100%')
|
||||
.backgroundColor('#F5F5F5')
|
||||
}
|
||||
|
||||
functionParsingDiagram({ outputState: this.outputState, func: this.inputFunction })
|
||||
.width('25%')
|
||||
.height('100%')
|
||||
.backgroundColor('#F5F5F5')
|
||||
|
||||
basicFunctionsBar()
|
||||
.width('25%')
|
||||
.height('100%')
|
||||
.backgroundColor(Color.Pink)
|
||||
|
||||
}.width('100%').layoutWeight(1)
|
||||
|
||||
controlBar({inputFuncBody:$inputFunction,outputState:$outputState,boardOption:$boardOption}).width('100%').height('30%')
|
||||
|
||||
}.width('100%').height('100%')
|
||||
|
||||
}
|
||||
/**
|
||||
* 创建board
|
||||
*/
|
||||
createBoard(){
|
||||
if (this.dimension == Dimension.TWO){
|
||||
if(this.quadrant == Quadrant.ONE){
|
||||
B1Q2D.setOption(this.boardOption)
|
||||
this.board = B1Q2D
|
||||
}
|
||||
if(this.quadrant == Quadrant.FOUR){
|
||||
B4Q2D.setOption(this.boardOption)
|
||||
this.board = B4Q2D
|
||||
}
|
||||
}
|
||||
|
||||
if(this.dimension == Dimension.THREE){
|
||||
B4Q3D.setOption(this.boardOption)
|
||||
this.board = B4Q3D
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
export enum PointType{
|
||||
DISCONTINUE = 0,
|
||||
CONTINUE = 1,
|
||||
}
|
||||
export enum FuncType{
|
||||
|
||||
/**
|
||||
* 未选择
|
||||
*/
|
||||
NONE = -1,
|
||||
/**
|
||||
* y = c型
|
||||
* 为此枚举值时 只能填y的值且其为c的值 且虚拟点集长度为1 pointType字段必为CONTINUE
|
||||
*/
|
||||
YC = 0,
|
||||
/**
|
||||
* x = c型
|
||||
* 为此枚举值时 只能填x的值且其为c的值 且虚拟点集长度为1 pointType字段必为CONTINUE
|
||||
*/
|
||||
XC = 1,
|
||||
/*
|
||||
* y = f(x)型
|
||||
* 为此枚举值时 虚拟点集中只能填xy的值
|
||||
*/
|
||||
YX = 2,
|
||||
/**
|
||||
* z = c型
|
||||
* 为此枚举值时 只能填z的值且其为c的值 且虚拟点集长度为1 pointType字段必为CONTINUE
|
||||
*/
|
||||
ZC = 3,
|
||||
/**
|
||||
* z = f(x)型
|
||||
* 为此枚举值时 虚拟点集中只能填zx的值
|
||||
*/
|
||||
ZX = 4,
|
||||
/**
|
||||
* z = f(y)型
|
||||
* 为此枚举值时 虚拟点集中只能填zy的值
|
||||
*/
|
||||
ZY = 5,
|
||||
/**
|
||||
* z = f(x,y)型
|
||||
* 为此枚举值时 虚拟点集中只能填zxy的值
|
||||
* 且必须为边界值
|
||||
*
|
||||
*/
|
||||
ZXY = 6
|
||||
}
|
||||
/**
|
||||
* 虚拟点集
|
||||
* 根据函数关系和步长、个数直接得到的点集
|
||||
*/
|
||||
export interface IPoint{
|
||||
x?:number
|
||||
|
||||
y?:number
|
||||
|
||||
z?:number
|
||||
|
||||
pointType:PointType
|
||||
}
|
||||
export interface IVirtualPoints{
|
||||
|
||||
points:Array<IPoint>
|
||||
|
||||
funcType:FuncType
|
||||
|
||||
}
|
||||
/**
|
||||
* 画板点集
|
||||
* 以屏幕左上角为原点的平面坐标系下的点集
|
||||
*/
|
||||
export interface IBoardPoint{
|
||||
x:number
|
||||
|
||||
y:number
|
||||
|
||||
pointType:PointType
|
||||
}
|
||||
|
|
@ -0,0 +1,361 @@
|
|||
interface replaceableVar {
|
||||
char: string,
|
||||
value: number
|
||||
}
|
||||
|
||||
class MathExpressionParser {
|
||||
private tokens: string[];
|
||||
private currentTokenIndex: number;
|
||||
private variables: replaceableVar[];
|
||||
private nodeTree: string[][];
|
||||
private exp: string;
|
||||
private currentExpIndex: number;
|
||||
private layer: number;
|
||||
|
||||
constructor() {
|
||||
this.tokens = []
|
||||
this.currentTokenIndex = 0
|
||||
this.variables = []
|
||||
// this.nodeTree = new Array(50).fill([])
|
||||
this.nodeTree = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
this.nodeTree.push([]);
|
||||
}
|
||||
this.exp = ""
|
||||
this.currentExpIndex = 0
|
||||
this.layer = 0
|
||||
}
|
||||
|
||||
private tokenize(expression: string): string[] {
|
||||
const regex: RegExp = new RegExp("(\\d+\\.?\\d*|\\.\\d+|[a-zA-Z]+|\\S)", "g");
|
||||
const originalTokens = expression.match(regex) || [];
|
||||
|
||||
const Tokens: string[] = [];
|
||||
for (let i = 0; i < originalTokens.length; i++) {
|
||||
Tokens.push(originalTokens[i]);
|
||||
if (i < originalTokens.length - 1) {
|
||||
const currentToken = originalTokens[i];
|
||||
const nextToken = originalTokens[i + 1];
|
||||
if ((this.isNumber(currentToken) && this.isVariable(nextToken)) ||
|
||||
(this.isVariable(currentToken) && this.isVariable(nextToken)) ||
|
||||
(currentToken === ')' && (this.isVariable(nextToken) || this.isNumber(nextToken))) ||
|
||||
(this.isNumber(currentToken) && nextToken === '(') ||
|
||||
(this.isVariable(currentToken) && nextToken === '(')) {
|
||||
Tokens.push('*');
|
||||
}
|
||||
}
|
||||
}
|
||||
return Tokens;
|
||||
}
|
||||
|
||||
private existInArr(token: string): number {
|
||||
for (let i: number = 0; i < this.variables.length; i++) {
|
||||
if (this.variables[i].char == token) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private isNumber(token: string): boolean {
|
||||
const num = Number(token);
|
||||
return !Number.isNaN(num) && Number.isFinite(num);
|
||||
}
|
||||
|
||||
private isVariable(token: string): boolean {
|
||||
const temp: RegExp = new RegExp("^[a-zA-Z]+$")
|
||||
return temp.test(token) && !this.isFunction(token) && token !== "π";
|
||||
}
|
||||
|
||||
private isFunction(token: string): boolean {
|
||||
return ['pow', 'sin', 'cos', 'tan', 'ln', 'sqrt', 'log2', 'log10'].includes(token);
|
||||
}
|
||||
|
||||
private getCurrentToken(): string {
|
||||
return this.tokens[this.currentTokenIndex];
|
||||
}
|
||||
|
||||
private getNextToken(): string {
|
||||
this.currentTokenIndex++;
|
||||
return this.getCurrentToken();
|
||||
}
|
||||
|
||||
private parsePrimaryExpression(): number {
|
||||
const token = this.getCurrentToken();
|
||||
if (token === '(') {
|
||||
this.getNextToken(); // consume '('
|
||||
const result = this.parseExpression();
|
||||
if (this.getCurrentToken() !== ')') {
|
||||
throw new Error("Expected ')' after expression");
|
||||
}
|
||||
this.getNextToken(); // consume ')'
|
||||
return result;
|
||||
} else if (token === 'π') {
|
||||
this.getNextToken(); // consume 'π'
|
||||
return Math.PI;
|
||||
} else if (this.isVariable(token)) {
|
||||
if (this.existInArr(token) != -1) {
|
||||
const value = this.variables[this.existInArr(token)].value;
|
||||
this.getNextToken(); // consume variable
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`Unknown variable: ${token}`);
|
||||
}
|
||||
} else if (this.isFunction(token)) {
|
||||
return this.parseFunction();
|
||||
} else {
|
||||
const num = Number(token);
|
||||
this.getNextToken(); // consume number
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
private parseFunction(): number {
|
||||
const functionName = this.getCurrentToken();
|
||||
this.getNextToken(); // consume function name
|
||||
if (this.getCurrentToken() !== '(') {
|
||||
throw new Error(`Expected '(' after function name ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume '('
|
||||
|
||||
const argument1 = this.parseExpression();
|
||||
|
||||
if (functionName === 'pow') {
|
||||
if (this.getCurrentToken() !== ',') {
|
||||
throw new Error(`Expected ',' between arguments for function ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume ','
|
||||
const argument2 = this.parseExpression();
|
||||
if (this.getCurrentToken() !== ')') {
|
||||
throw new Error(`Expected ')' after arguments for function ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume ')'
|
||||
return Math.pow(argument1, argument2);
|
||||
}
|
||||
|
||||
if (this.getCurrentToken() !== ')') {
|
||||
throw new Error(`Expected ')' after argument for function ${functionName}`);
|
||||
}
|
||||
this.getNextToken(); // consume ')'
|
||||
|
||||
switch (functionName) {
|
||||
case 'sin':
|
||||
return Math.sin(argument1);
|
||||
case 'cos':
|
||||
return Math.cos(argument1);
|
||||
case 'tan':
|
||||
return Math.tan(argument1);
|
||||
case 'ln':
|
||||
return Math.log(argument1);
|
||||
case 'sqrt':
|
||||
return Math.sqrt(argument1);
|
||||
case 'log2':
|
||||
return Math.log2(argument1);
|
||||
case 'log10':
|
||||
return Math.log10(argument1);
|
||||
default:
|
||||
throw new Error(`Unknown function: ${functionName}`);
|
||||
}
|
||||
}
|
||||
|
||||
private parseFactor(): number {
|
||||
// 处理一元负号
|
||||
if (this.getCurrentToken() === '-') {
|
||||
this.getNextToken(); // consume '-'
|
||||
return -this.parseFactor();
|
||||
}
|
||||
// 处理初级表达式
|
||||
let result = this.parsePrimaryExpression();
|
||||
// 处理乘法和除法
|
||||
while (this.getCurrentToken() === '*' || this.getCurrentToken() === '/') {
|
||||
const operator = this.getCurrentToken();
|
||||
this.getNextToken(); // consume operator
|
||||
const operand = this.parsePrimaryExpression();
|
||||
if (operator === '*') {
|
||||
result *= operand;
|
||||
} else if (operator === '/') {
|
||||
if (operand === 0) {
|
||||
throw new Error("Division by zero");
|
||||
}
|
||||
result /= operand;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private parseExpression(): number {
|
||||
let result = this.parseFactor();
|
||||
while (this.getCurrentToken() === '+' || this.getCurrentToken() === '-') {
|
||||
const operator = this.getCurrentToken();
|
||||
this.getNextToken();
|
||||
const operand = this.parseFactor();
|
||||
if (operator === '+') {
|
||||
result += operand;
|
||||
} else if (operator === '-') {
|
||||
result -= operand;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public evaluate(expression: string, variables: replaceableVar[] = []): number {
|
||||
this.tokens = this.tokenize(expression);
|
||||
this.currentTokenIndex = 0;
|
||||
this.variables = variables;
|
||||
|
||||
const result = this.parseExpression();
|
||||
if (this.currentTokenIndex !== this.tokens.length) {
|
||||
console.log("Token:" + this.tokens);
|
||||
console.log("当前index:" + this.currentTokenIndex + " " + "当前length:" + this.tokens.length);
|
||||
throw new Error("Unexpected token");
|
||||
}
|
||||
return Number(result.toFixed(4));
|
||||
}
|
||||
|
||||
private getCurrentChar(): string {
|
||||
return this.exp[this.currentExpIndex];
|
||||
}
|
||||
|
||||
private getNextChar(): string {
|
||||
this.currentExpIndex++;
|
||||
return this.getCurrentChar();
|
||||
}
|
||||
|
||||
private recursivePartitioning(expression: string) {
|
||||
this.exp = "";
|
||||
this.tokens = [];
|
||||
this.tokens = this.tokenize(expression);
|
||||
for (let i = 0; i < this.tokens.length; i++) {
|
||||
this.exp += this.tokens[i];
|
||||
}
|
||||
console.log(this.exp + "----" + this.layer)
|
||||
let temp: RegExp = new RegExp("^\\-?[a-zA-Z0-9]+$");
|
||||
if (temp.test(this.exp)) return;
|
||||
this.currentExpIndex = 0;
|
||||
let parenthesizedForms: Array<string> = [];
|
||||
let leftIndex: number = 0;
|
||||
let rightIndex: number = 0;
|
||||
let char: string = "$";
|
||||
while (this.currentExpIndex < this.exp.length) {
|
||||
if (this.getCurrentChar() == '(') {
|
||||
parenthesizedForms.push("");
|
||||
let count: number = parenthesizedForms.length - 1;
|
||||
let leftCount: number = 1;
|
||||
let rightCount: number = 0;
|
||||
this.getNextChar();
|
||||
leftIndex = this.currentExpIndex;
|
||||
let sign: number = 0;
|
||||
while (true) {
|
||||
if (this.getCurrentChar() == '(') leftCount++;
|
||||
if (this.getCurrentChar() == ')') rightCount++;
|
||||
if (leftCount == rightCount) {
|
||||
rightIndex = this.currentExpIndex;
|
||||
break;
|
||||
}
|
||||
parenthesizedForms[count] += this.getCurrentChar();
|
||||
this.getNextChar();
|
||||
sign++;
|
||||
if (sign > 1000) {
|
||||
console.log("leftCount: " + leftCount + " " + "rightCount: " + rightCount);
|
||||
this.exp = "";
|
||||
this.tokens = [];
|
||||
this.nodeTree = [];
|
||||
this.layer = 0;
|
||||
this.currentExpIndex = 0;
|
||||
throw new Error("The number of brackets does not match");
|
||||
}
|
||||
}
|
||||
let repeatNum: number = parenthesizedForms[count].length - count.toString().length;
|
||||
if (repeatNum >= 0) {
|
||||
this.exp = this.exp.substring(0, leftIndex) + count.toString() + char.repeat(repeatNum) + this.exp.substring(rightIndex);
|
||||
} else {
|
||||
throw new Error("Functions with so many parentheses are not supported");
|
||||
}
|
||||
leftIndex = rightIndex = 0;
|
||||
}
|
||||
this.getNextChar();
|
||||
}
|
||||
let nail: number = this.nodeTree[this.layer].length;
|
||||
let symbolParts: RegExpMatchArray | null = null;
|
||||
let includesAS: RegExp = new RegExp("(?<=\\d|[a-zA-Z]|\\))[+-](?=\\d|[a-zA-Z]|\\()", "g");
|
||||
let includesMD: RegExp = new RegExp("(?<=\\d|[a-zA-Z]|\\))[*/](?=\\d|[a-zA-Z]|\\()", "g");
|
||||
let includesPow: RegExp = new RegExp("(?<=\\d|[a-zA-Z]|\\))(?:[*]{2}|[\\^])(?=\\d|[a-zA-Z]|\\()", "g");
|
||||
if (includesAS.test(this.exp)) {
|
||||
this.nodeTree[this.layer] = this.nodeTree[this.layer].concat(this.exp.split(includesAS));
|
||||
symbolParts = this.exp.match(includesAS);
|
||||
} else if (includesMD.test(this.exp)) {
|
||||
this.nodeTree[this.layer] = this.nodeTree[this.layer].concat(this.exp.split(includesMD));
|
||||
} else if (includesPow.test(this.exp)) {
|
||||
this.nodeTree[this.layer] = this.nodeTree[this.layer].concat(this.exp.split(includesPow));
|
||||
} else {
|
||||
if (this.exp[0] != "(") {
|
||||
let label = 0;
|
||||
for (let i = 0; i < this.exp.length; i++) {
|
||||
if (this.exp[i] == "(") {
|
||||
label = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.exp = this.exp.substring(label);
|
||||
}
|
||||
this.nodeTree[this.layer].push(this.exp);
|
||||
}
|
||||
|
||||
if (symbolParts) {
|
||||
symbolParts.forEach((match, index) => {
|
||||
if (match == "-") {
|
||||
this.nodeTree[this.layer][index + 1] = "-".concat(this.nodeTree[this.layer][index + 1]);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// for (let i = nail; i < this.nodeTree[this.layer].length; i++) {
|
||||
// console.log(this.nodeTree[this.layer][i]);
|
||||
// }
|
||||
|
||||
// for (let i: number = 0; i < parenthesizedForms.length; i++) {
|
||||
// console.log("需替换字符" + parenthesizedForms[i] + ":" + i + " " + this.layer)
|
||||
// }
|
||||
|
||||
for (let i = nail; i < this.nodeTree[this.layer].length; i++) {
|
||||
for (let j = 0; j < this.nodeTree[this.layer][i].length; j++) {
|
||||
if (this.nodeTree[this.layer][i][j] == "(") {
|
||||
let standard: RegExp = new RegExp("(?<=\\d|[a-zA-Z]|\\))([+-]|[*/]|[*]{2}|[\\^])(?=\\d|[a-zA-Z]|\\()", "g");
|
||||
let flag: Boolean = false;
|
||||
if (j == 0 && !standard.test(this.nodeTree[this.layer][i])) {
|
||||
flag = true;
|
||||
}
|
||||
let start = j + 1;
|
||||
let storeIndex = Number(this.nodeTree[this.layer][i][start]);
|
||||
this.nodeTree[this.layer][i] = this.nodeTree[this.layer][i].substring(0, start) + parenthesizedForms[storeIndex] + this.nodeTree[this.layer][i].substring(start + parenthesizedForms[storeIndex].length);
|
||||
if (flag) this.nodeTree[this.layer][i] = this.nodeTree[this.layer][i].substring(1, this.nodeTree[this.layer][i].length - 1);
|
||||
// break;
|
||||
j += parenthesizedForms[storeIndex].length + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for (let i = nail; i < this.nodeTree[this.layer].length; i++) {
|
||||
// console.log(this.layer + " " + this.nodeTree[this.layer][i]);
|
||||
// }
|
||||
|
||||
for (let i = nail; i < this.nodeTree[this.layer].length; i++) {
|
||||
this.layer++;
|
||||
this.recursivePartitioning(this.nodeTree[this.layer - 1][i]);
|
||||
this.layer--;
|
||||
}
|
||||
}
|
||||
|
||||
createNode(func: string) {
|
||||
// this.nodeTree.length = 0;
|
||||
// this.nodeTree = new Array(50).fill([]);
|
||||
this.nodeTree = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
this.nodeTree.push([]);
|
||||
}
|
||||
this.recursivePartitioning(func);
|
||||
return this.nodeTree;
|
||||
}
|
||||
}
|
||||
|
||||
export let mathjs: MathExpressionParser = new MathExpressionParser();
|
||||
|
|
@ -0,0 +1,399 @@
|
|||
import promptAction from '@ohos.promptAction';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
import { mathjs } from '../handleFunc/analysis'
|
||||
import { PointType, IPoint, FuncType } from '../drawBoard/drawPoint'
|
||||
import { Dimension } from '../drawBoard/BoardType'
|
||||
|
||||
export class ProcessFunc {
|
||||
private funcId: string; // 函数(用以查询坐标轴进而确定函数类型)
|
||||
private funcText: string; // 函数体(经过一定符号转换处理,用以计算)
|
||||
xMin: number; // x轴最小整数值
|
||||
xMax: number; // y轴最大整数值
|
||||
private yMin: number; // x轴最小整数值
|
||||
private yMax: number; // y轴最大整数值
|
||||
private xMinDefault: number;
|
||||
private xMaxDefault: number;
|
||||
private yMinDefault: number;
|
||||
private yMaxDefault: number;
|
||||
|
||||
nodeTree: Array<Array<string>> // 节点树数组
|
||||
coPot: Array<IPoint>; // 存储获取的坐标点
|
||||
funcHead: string; // 存储函数头
|
||||
funcBody: string; // 存储函数体
|
||||
xCount: number; // 取x点个数
|
||||
yCount: number; // 取y点个数
|
||||
xStep: number; // x轴步长值,决定每个点取点间隔
|
||||
yStep: number; // y轴步长值,决定每个点取点间隔
|
||||
row: number; // 三维取点点集转为二维数组的行数
|
||||
col: number; // 三维取点点集转为二维数组的列数
|
||||
|
||||
constructor() {
|
||||
this.nodeTree = [];
|
||||
this.coPot = [];
|
||||
this.funcId = this.funcText = this.funcHead = this.funcBody = "";
|
||||
this.xStep = this.yStep = 0.1;
|
||||
this.xCount = this.yCount = 0;
|
||||
this.xMin = this.xMax = this.yMin = this.yMax = 0;
|
||||
this.xMinDefault = this.xMaxDefault = this.yMinDefault = this.yMaxDefault = 0;
|
||||
this.row = this.col = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对promptAction.showToast()的封装。默认持续1.5s,距离底部50vp
|
||||
* @param message 弹窗提示的文本信息
|
||||
*/
|
||||
private showToast(message: string) {
|
||||
try {
|
||||
promptAction.showToast({
|
||||
message: message,
|
||||
duration: 1500,
|
||||
bottom: 20
|
||||
});
|
||||
} catch (error) {
|
||||
let message = (error as BusinessError).message
|
||||
let code = (error as BusinessError).code
|
||||
console.error(`showToast args error code is ${code}, message is ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化默认的坐标轴最大最小边界点
|
||||
* @param xMin
|
||||
* @param xMax
|
||||
* @param yMin
|
||||
* @param yMax
|
||||
*/
|
||||
setEdge(xMin: number, xMax: number, y?: number, ) {
|
||||
this.xMinDefault = xMin;
|
||||
this.xMaxDefault = xMax;
|
||||
if (y) {
|
||||
this.yMinDefault = -y;
|
||||
this.yMaxDefault = y;
|
||||
}
|
||||
this.updateEdge(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据放大倍数更新边缘点
|
||||
* @param xTimes
|
||||
* @param yTimes
|
||||
*/
|
||||
updateEdge(xTimes: number, yTimes?: number) {
|
||||
this.xMin = this.xMinDefault * xTimes;
|
||||
this.xMax = this.xMaxDefault * xTimes;
|
||||
if (yTimes) {
|
||||
this.yMin = this.yMinDefault * yTimes;
|
||||
this.yMax = this.yMaxDefault * yTimes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断输入的数学表达式是否合法,能够运算则合法,抛出错误则不合法
|
||||
* @param func 数学表达式
|
||||
* @returns
|
||||
*/
|
||||
isValidFunc(func: string): boolean {
|
||||
this.funcId = this.funcText = "";
|
||||
let temp: RegExp = new RegExp("\\s+", "g");
|
||||
func = func.trim().replace(temp, "");
|
||||
// temp = new RegExp("(\\(([^()]+)\\)|\\w+)(?:\\*\\*|\\^)(\\(([^()]+)\\)|\\w+)", "g");
|
||||
temp = new RegExp("(\\([^()]*?(?:\\([^()]*?\\)[^()]*?)*\\)|\\w+)(?:\\*\\*|\\^)(\\([^()]*?(?:\\([^()]*?\\)[^()]*?)*\\)|\\w+)", "g");
|
||||
func = func.replace(temp, "pow($1, $2)");
|
||||
if (func.includes("=") && func.split("=").length === 2) {
|
||||
this.funcId = func.split("=")[0];
|
||||
this.funcText = func.split("=")[1];
|
||||
} else {
|
||||
this.funcId = "";
|
||||
this.funcText = func;
|
||||
}
|
||||
|
||||
try {
|
||||
let testX: number = 1;
|
||||
let testY: number = 1;
|
||||
let uselessRes: number = mathjs.evaluate(this.funcText, [{ char: "x", value: testX }, { char: "y", value: testY }]);
|
||||
if (!Number.isNaN(uselessRes)) return true;
|
||||
else return false;
|
||||
} catch (err) {
|
||||
console.log("错误的函数格式为:" + this.funcText)
|
||||
this.showToast("函数错误!" + err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分离函数头和函数体
|
||||
* @param func
|
||||
*/
|
||||
separateFunc(func: string) {
|
||||
this.funcHead = this.funcBody = "";
|
||||
let temp: RegExp = new RegExp("\\s+", "g");
|
||||
func = func.trim().replace(temp, "");
|
||||
if (func.includes("=") && func.split("=").length === 2) {
|
||||
this.funcHead = func.split("=")[0];
|
||||
this.funcBody = func.split("=")[1];
|
||||
} else {
|
||||
this.funcHead = "";
|
||||
this.funcBody = func;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取函数类型
|
||||
* @param dimension 当前的维度
|
||||
* @returns
|
||||
*/
|
||||
getFuncType(dimension: Dimension): FuncType {
|
||||
if (dimension === Dimension.TWO) {
|
||||
if (!this.funcId) {
|
||||
if (Number.isNaN(Number(this.funcText))) return FuncType.YX;
|
||||
else this.showToast("请输入正确的函数格式");
|
||||
}
|
||||
if (this.funcId && this.funcId != "x") {
|
||||
if (Number.isNaN(Number(this.funcText))) return FuncType.YX;
|
||||
else return FuncType.YC;
|
||||
}
|
||||
else if (!Number.isNaN(Number(this.funcText))) return FuncType.XC;
|
||||
else this.showToast("函数命名不可以与自变量同名!");
|
||||
} else if (dimension === Dimension.THREE) {
|
||||
let standard_x: RegExp = new RegExp("(^|[^a-zA-Z])x($|[^a-zA-Z])");
|
||||
let standard_y: RegExp = new RegExp("(^|[^a-zA-Z])y($|[^a-zA-Z])");
|
||||
if (!this.funcId) {
|
||||
if (standard_x.test(this.funcText) && standard_y.test(this.funcText)) return FuncType.ZXY;
|
||||
else if (standard_x.test(this.funcText) && !standard_y.test(this.funcText)) return FuncType.ZX;
|
||||
else if (!standard_x.test(this.funcText) && standard_y.test(this.funcText)) return FuncType.ZY;
|
||||
else this.showToast("请输入正确的函数格式");
|
||||
}
|
||||
if (this.funcId && this.funcId != "x" && this.funcId != "y") {
|
||||
if (!Number.isNaN(Number(this.funcText))) return FuncType.ZC;
|
||||
else if (standard_x.test(this.funcText) && standard_y.test(this.funcText)) return FuncType.ZXY;
|
||||
else if (standard_x.test(this.funcText) && !standard_y.test(this.funcText)) return FuncType.ZX;
|
||||
else if (!standard_x.test(this.funcText) && standard_y.test(this.funcText)) return FuncType.ZY;
|
||||
}
|
||||
else if (this.funcId == "x" && !Number.isNaN(Number(this.funcText))) return FuncType.XC;
|
||||
else if (this.funcId == "y" && !Number.isNaN(Number(this.funcText))) return FuncType.YC;
|
||||
else this.showToast("没有YX或XY类型函数||函数命名不可与自变量同名!");
|
||||
}
|
||||
return FuncType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前点是否连续
|
||||
* @param xValue 当前x值
|
||||
* @param yValue 当前y值
|
||||
* @param zValue 当前z值
|
||||
* @param epsilon 模拟的逼近无限小值
|
||||
* @param threshold 模拟的逼近无限大值
|
||||
* @returns 返回一个布尔值,true则表示此点连续,false表示不连续
|
||||
*/
|
||||
private judgeContinue(xValue: number, yValue: number, zValue?: number, epsilon: number = 1e-10, threshold: number = 1e5): boolean {
|
||||
if (Number.isNaN(yValue) || Number.isNaN(zValue)) {
|
||||
return false;
|
||||
}
|
||||
let leftLimitX: number = 0;
|
||||
let rightLimitX: number = 0;
|
||||
let leftLimitY: number = 0;
|
||||
let rightLimitY: number = 0;
|
||||
let standard: RegExp = new RegExp("(^|[^a-zA-Z])y($|[^a-zA-Z])")
|
||||
try {
|
||||
if (!standard.test(this.funcText)) {
|
||||
leftLimitX = mathjs.evaluate(this.funcText, [{ char: "x", value: xValue - epsilon }]);
|
||||
rightLimitX = mathjs.evaluate(this.funcText, [{ char: "x", value: xValue + epsilon }]);
|
||||
if (Math.abs(rightLimitX - leftLimitX) > 10 * epsilon) {
|
||||
return false;
|
||||
}
|
||||
if (Math.abs(leftLimitX - yValue) > 10 * epsilon || Math.abs(rightLimitX - yValue) > 10 * epsilon) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
leftLimitX = mathjs.evaluate(this.funcText, [{ char: "x", value: xValue - epsilon }, { char: "y", value: yValue }]);
|
||||
rightLimitX = mathjs.evaluate(this.funcText, [{ char: "x", value: xValue + epsilon }, { char: "y", value: yValue }]);
|
||||
leftLimitX = mathjs.evaluate(this.funcText, [{ char: "x", value: xValue }, { char: "y", value: yValue - epsilon }]);
|
||||
rightLimitX = mathjs.evaluate(this.funcText, [{ char: "x", value: xValue }, { char: "y", value: yValue + epsilon }]);
|
||||
if (Math.abs(rightLimitX - leftLimitX) > 10 * epsilon && Math.abs(rightLimitY - leftLimitY) > 10 * epsilon) {
|
||||
return false;
|
||||
}
|
||||
if ((Math.abs(leftLimitX - zValue!) > 10 * epsilon || Math.abs(rightLimitX - zValue!) > 10 * epsilon) && (Math.abs(leftLimitY - zValue!) > 10 * epsilon || Math.abs(rightLimitY - zValue!) > 10 * epsilon)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const isLeftInfinite: boolean = Math.abs(leftLimitX) > threshold || Math.abs(leftLimitY) > threshold;
|
||||
const isRightInfinite: boolean = Math.abs(rightLimitX) > threshold || Math.abs(rightLimitY) > threshold;
|
||||
if (isLeftInfinite || isRightInfinite) {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
this.showToast("判断是否为间断点时发生错误,默认此点非连续");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一元函数获取点的方法封装
|
||||
* @param independentVar 自变量
|
||||
* @param dependentVar 因变量
|
||||
* @param min 负轴坐标最大值
|
||||
* @param max 正轴坐标最大值
|
||||
* @param step 步长值
|
||||
* @param count 个数
|
||||
*/
|
||||
private getPointsBasic(independentVar: string, dependentVar: string, min: number, max: number, step: number, count?: number) {
|
||||
let _temp: number = Math.abs(max - min) / step;
|
||||
if (_temp <= 10000) {
|
||||
for (let argument: number = min; argument <= max && step > 0; argument += step) {
|
||||
argument = Number(argument.toFixed(4));
|
||||
let standard: RegExp = new RegExp(`(^|[^a-zA-Z])${independentVar}($|[^a-zA-Z])`)
|
||||
let calculateRes: number = 0;
|
||||
if (standard.test(this.funcText)) {
|
||||
try {
|
||||
calculateRes = mathjs.evaluate(this.funcText, [{ char: independentVar, value: argument }]);
|
||||
} catch {
|
||||
this.coPot.length = 0;
|
||||
this.showToast("运算过程中出错");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.judgeContinue(argument, calculateRes)) {
|
||||
// this.coPot.push({ [independentVar]: argument, [dependentVar]: calculateRes, pointType: PointType.CONTINUE });
|
||||
this.coPot.push({ pointType: PointType.CONTINUE });
|
||||
let index: number = this.coPot.length - 1;
|
||||
if (independentVar == "x") this.coPot[index].x = argument;
|
||||
else if (independentVar == "y") this.coPot[index].y = argument;
|
||||
if (dependentVar == "y") this.coPot[index].y = calculateRes;
|
||||
else if (dependentVar == "z") this.coPot[index].z = calculateRes;
|
||||
} else {
|
||||
// this.coPot.push({ [independentVar]: argument, pointType: PointType.DISCONTINUE })
|
||||
this.coPot.push({ pointType: PointType.DISCONTINUE });
|
||||
let index: number = this.coPot.length - 1;
|
||||
if (independentVar == "x") this.coPot[index].x = argument;
|
||||
else if (independentVar == "y") this.coPot[index].y = argument;
|
||||
}
|
||||
if (argument != max && argument + step > max) {
|
||||
argument = max - step;
|
||||
}
|
||||
}
|
||||
if (count && count > 0 && count < this.coPot.length) {
|
||||
this.coPot = this.coPot.slice(0, count);
|
||||
}
|
||||
} else {
|
||||
this.coPot.push({ x: 0, y: 0, pointType: PointType.CONTINUE });
|
||||
this.coPot.length = 1;
|
||||
this.showToast("请适当增大步长");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用于绘制2维图像的各个坐标点
|
||||
* @returns 返回坐标点信息数组
|
||||
*/
|
||||
getPoints2D(): IPoint[] {
|
||||
this.coPot.length = 0;
|
||||
if (this.getFuncType(Dimension.TWO) == FuncType.YX) {
|
||||
this.getPointsBasic("x", "y", this.xMin, this.xMax, this.xStep, this.xCount);
|
||||
} else if (this.getFuncType(Dimension.TWO) == FuncType.YC) {
|
||||
this.coPot.push({ y: Number(this.funcText), pointType: PointType.CONTINUE })
|
||||
} else if (this.getFuncType(Dimension.TWO) == FuncType.XC) {
|
||||
this.coPot.push({ x: Number(this.funcText), pointType: PointType.CONTINUE })
|
||||
}
|
||||
return this.coPot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用于绘制3维图像的各个坐标点
|
||||
* @returns 返回坐标点信息数组
|
||||
*/
|
||||
getPoints3D(): IPoint[] {
|
||||
this.coPot.length = 0;
|
||||
if (this.getFuncType(Dimension.THREE) == FuncType.ZXY) {
|
||||
let _temp: number = (Math.abs(this.yMax - this.yMin) / this.yStep) * (Math.abs(this.xMax - this.xMin) / this.xStep);
|
||||
if (_temp <= 10000) {
|
||||
for (let x: number = this.xMin; x <= this.xMax && this.xStep > 0; x += this.xStep) {
|
||||
for (let y: number = this.yMin; y <= this.yMax && this.yStep > 0; y += this.yStep) {
|
||||
x = Number(x.toFixed(4));
|
||||
y = Number(y.toFixed(4));
|
||||
let standard: RegExp = new RegExp("(^|[^a-zA-Z])(?:x|y)($|[^a-zA-Z])")
|
||||
let z: number = 0;
|
||||
if (standard.test(this.funcText)) {
|
||||
try {
|
||||
z = mathjs.evaluate(this.funcText, [{ char: "x", value: x }, { char: "y", value: y }]);
|
||||
} catch {
|
||||
this.coPot.length = 0;
|
||||
this.showToast("运算过程中出错");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
if (this.judgeContinue(x, y)) {
|
||||
this.coPot.push({ x: x, y: y, z: z, pointType: PointType.CONTINUE });
|
||||
} else {
|
||||
this.coPot.push({ x: x, y: y, pointType: PointType.DISCONTINUE })
|
||||
}
|
||||
if (y != this.yMax && y + this.yStep >= this.yMax) {
|
||||
let temp: number = y + this.yStep + (-this.yMin);
|
||||
this.col = Number((temp / this.yStep).toFixed(0)) + 1;
|
||||
y = this.yMax - this.yStep;
|
||||
}
|
||||
}
|
||||
if (x != this.xMax && x + this.xStep >= this.xMax) {
|
||||
let temp: number = x + this.xStep + (-this.xMin);
|
||||
this.row = Number((temp / this.xStep).toFixed(0)) + 1;
|
||||
x = this.xMax - this.xStep;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.coPot.push({ x: 0, y: 0, pointType: PointType.CONTINUE });
|
||||
this.coPot.length = 1;
|
||||
this.showToast("请适当增大步长");
|
||||
}
|
||||
// let row: number = Math.abs(this.xMax - this.xMin) / this.xStep;
|
||||
// let col: number = Math.abs(this.yMax - this.yMin) / this.yStep;
|
||||
// let temp: IPoint[] = [];
|
||||
// if (this.xCount > 0 && this.yCount > 0 && this.xCount < row && this.yCount < col) {
|
||||
// for (let i: number = 0; i < this.xCount * col; i += col) {
|
||||
// temp = temp.concat(this.coPot.slice(i, i + this.yCount));
|
||||
// }
|
||||
// this.coPot = temp;
|
||||
// } else if (this.xCount > 0 && this.xCount < row) {
|
||||
// this.coPot = this.coPot.slice(0, this.xCount * col);
|
||||
// } else if (this.yCount > 0 && this.xCount < col) {
|
||||
// for (let i: number = 0; i < this.coPot.length; i += col) {
|
||||
// temp = temp.concat(this.coPot.slice(i, i + this.yCount));
|
||||
// }
|
||||
// this.coPot = temp;
|
||||
// }
|
||||
} else if (this.getFuncType(Dimension.THREE) == FuncType.ZX) {
|
||||
this.getPointsBasic("x", "z", this.xMin, this.xMax, this.xStep);
|
||||
} else if (this.getFuncType(Dimension.THREE) == FuncType.ZY) {
|
||||
this.getPointsBasic("y", "z", this.yMin, this.yMax, this.yStep);
|
||||
} else if (this.getFuncType(Dimension.THREE) == FuncType.ZC) {
|
||||
this.coPot.push({ z: Number(this.funcText), pointType: PointType.CONTINUE });
|
||||
} else if (this.getFuncType(Dimension.THREE) == FuncType.XC) {
|
||||
this.coPot.push({ x: Number(this.funcText), pointType: PointType.CONTINUE });
|
||||
} else if (this.getFuncType(Dimension.THREE) == FuncType.YC) {
|
||||
this.coPot.push({ y: Number(this.funcText), pointType: PointType.CONTINUE });
|
||||
}
|
||||
return this.coPot;
|
||||
}
|
||||
|
||||
createNodeTree(func: string) {
|
||||
this.separateFunc(func);
|
||||
this.nodeTree = [];
|
||||
if (this.isValidFunc(func)) {
|
||||
let temp: RegExp = new RegExp("\\s+", "g");
|
||||
let machiningFunc: string = this.funcBody;
|
||||
machiningFunc = machiningFunc.trim().replace(temp, "");
|
||||
this.nodeTree.push([machiningFunc]);
|
||||
let leaf: Array<Array<string>> = mathjs.createNode(machiningFunc);
|
||||
for (let i: number = 0; i < leaf.length; i++) {
|
||||
if (leaf[i][0]) this.nodeTree.push([]);
|
||||
else break;
|
||||
for (let j: number = 0; j < leaf[i].length; j++) {
|
||||
this.nodeTree[i + 1].push(leaf[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.nodeTree;
|
||||
}
|
||||
}
|
||||
|
||||
export let handle: ProcessFunc = new ProcessFunc();
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"module": {
|
||||
"name": "entry",
|
||||
"type": "entry",
|
||||
"description": "$string:module_desc",
|
||||
"mainElement": "EntryAbility",
|
||||
"metadata": [
|
||||
{
|
||||
"name": "ArkTSPartialUpdate",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"src": [
|
||||
"pages/Index"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "模块描述"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import abilityTest from './Ability.test';
|
||||
|
||||
export default function testsuite() {
|
||||
abilityTest();
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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%')
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.6 KiB |