已关闭
AR Engine #2
weitingting创建于 8月5日关闭于 22 天前
AR Engine #2
已关闭
weitingting创建于 8月5日关闭于 22 天前
共 25 个文件变更+1345-526
ALICENSE+78-0
@@ -0,0 +1,78 @@
1+ Copyright (c) 2026 Huawei Device Co., Ltd. All rights reserved.
2+ 
3+ Licensed under the Apache License, Version 2.0 (the "License");
4+ you may not use this file except in compliance with the License.
5+ You may obtain a copy of the License at
6+ 
7+ http://www.apache.org/licenses/LICENSE-2.0
8+ 
9+ Unless required by applicable law or agreed to in writing, software
10+ distributed under the License is distributed on an "AS IS" BASIS,
11+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ See the License for the specific language governing permissions and
13+ limitations under the License.
14+ 
15+Apache License, Version 2.0
16+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
17+ 
18+1. Definitions.
19+ 
20+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
21+ 
22+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
23+ 
24+"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.
25+ 
26+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
27+ 
28+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
29+ 
30+"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.
31+ 
32+"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).
33+ 
34+"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.
35+ 
36+"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 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."
37+ 
38+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
39+ 
40+2. Grant of Copyright License.
41+ 
42+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.
43+ 
44+3. Grant of Patent License.
45+ 
46+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.
47+ 
48+4. Redistribution.
49+ 
50+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:
51+1.You must give any other recipients of the Work or Derivative Works a copy of this License; and
52+2.You must cause any modified files to carry prominent notices stating that You changed the files; and
53+3.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
54+4.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.
55+ 
56+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.
57+ 
58+5. Submission of Contributions.
59+ 
60+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.
61+ 
62+6. Trademarks.
63+ 
64+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.
65+ 
66+7. Disclaimer of Warranty.
67+ 
68+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.
69+ 
70+8. Limitation of Liability.
71+ 
72+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.
73+ 
74+9. Accepting Warranty or Additional Liability.
75+ 
76+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.
77+ 
78+END OF TERMS AND CONDITIONS
AOAT.xml+35-0
@@ -0,0 +1,35 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<configuration>
3+ <oatconfig>
4+ <licensefile></licensefile>
5+ <policylist>
6+ <policy name="projectPolicy" desc="">
7+ <!--policyitem type="compatibility" name="GPL-2.0+" path="abc/.*" desc="Process that runs independently, invoked by the X process."/-->
8+ <!--policyitem type="license" name="LGPL" path="abc/.*" desc="Dynamically linked by module X"/-->
9+ <!--policyitem type="copyright" name="xxx" path="abc/.*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc="Developed by X Company"/-->
10+ <policyitem type="license" name="CC-BY-4.0" path=".*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc="License for documents"/>
11+ </policy>
12+ </policylist>
13+ <filefilterlist>
14+ <filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies">
15+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
16+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
17+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
18+ <filteritem type="filename" name="*.jpg|*.png|*.gif|*.pdf|*.xlsm|*.zip|*.ogg|*.mp3|*.jpeg|*.webp|*.glb" desc="already checked"/>
19+ </filefilter>
20+ <filefilter name="copyrightPolicyFilter" desc="Filters for copyright policies">
21+ <filteritem type="filename" name="*.shader" desc="already checked"/>
22+ </filefilter>
23+ </filefilterlist>
24+ <licensematcherlist>
25+ <!--licensematcher name="uvwxyz License" desc="If the scanning result is InvalidLicense, you can define matching rules here. Note that quotation marks must be escaped.">
26+ <licensetext name="
27+ uvwxyz license textA xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
28+ " desc=""/>
29+ <licensetext name="
30+ uvwxyz license textB xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
31+ " desc=""/>
32+ </licensematcher-->
33+ </licensematcherlist>
34+ </oatconfig>
35+</configuration>
RREADME_EN.md→README.en.md+32-50
@@ -65,6 +65,27 @@ provided by AR Engine.
653. If you choose to add a local image, the image adding screen will be displayed. Otherwise, this step is skipped.653. If you choose to add a local image, the image adding screen will be displayed. Otherwise, this step is skipped.
664. The tracking starts for the target image, which is framed in blue.664. The tracking starts for the target image, which is framed in blue.
67 67 
68+### ARFace preview
69+ 
70+| **App home screen** | **Recognizing and rendering faces** |
71+|:-----------------------------:|:-----------------------------------:|
72+| ![](screenshots/homePage.png) | ![](screenshots/selectFace.png) |
73+ 
74+1. On the home screen of your phone, tap ARSampleArkts to start the app. The ARFace button is displayed on the main screen.
75+2. Select ARFace.
76+3. After entering the screen, you can scan your face.
77+4. After the face is recognized, the face mesh can be rendered.
78+ 
79+### ARBody preview
80+ 
81+| **App home screen** | **Identify and render human skeletal points** |
82+|:-----------------------------:|:-------------------------:|
83+| ![](screenshots/homePage.png) | ![](screenshots/body.png) |
84+ 
85+1. On the home screen of your phone, tap ARSampleArkts to start the app. On the main screen, you can see the ARBody button.
86+2. Tap ARBody and scan a human body.
87+3. After the human body is recognized, the skeleton points of the human body can be rendered.
88+ 
68## Usage Instructions89## Usage Instructions
69 90 
70Developers can open this project using DevEco, sign it, and directly run it on a real device.91Developers can open this project using DevEco, sign it, and directly run it on a real device.
@@ -78,7 +99,9 @@ Developers can open this project using DevEco, sign it, and directly run it on a
78│ ├──entryability99│ ├──entryability
79│ │ └──EntryAbility.ets // Entry point class100│ │ └──EntryAbility.ets // Entry point class
80│ ├──pages101│ ├──pages
102+│ │ ├──ARBody.ets // ARBody screen
81│ │ ├──ARDepth.ets // ARDepth screen103│ │ ├──ARDepth.ets // ARDepth screen
104+│ │ ├──ARFace.ets // ARFace screen
82│ │ ├──ARImage.ets // ARImage mode selection screen105│ │ ├──ARImage.ets // ARImage mode selection screen
83│ │ ├──ARImageByAdd.ets // ARImage local image mode screen106│ │ ├──ARImageByAdd.ets // ARImage local image mode screen
84│ │ ├──ARImageByDatabase.ets // ARImage local database mode screen107│ │ ├──ARImageByDatabase.ets // ARImage local database mode screen
@@ -100,43 +123,16 @@ Developers can open this project using DevEco, sign it, and directly run it on a
1001. Developers can use interface calls to determine whether the current device supports AR Engine.1231. Developers can use interface calls to determine whether the current device supports AR Engine.
101 The interface is called in the following way:124 The interface is called in the following way:
102 125 
103-```javascript126+ ```typescript
104-import { arViewController, ARView } from '@kit.AREngine';127+ import { arEngine, arViewController } from '@kit.AREngine';
105-import { BusinessError } from '@kit.BasicServicesKit';128+
106-@Component129+ let ret: boolean = arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_FACE);
107-struct ARTest {130+ ```
108- @State arContext?: arViewController.ARViewContext = undefined;131+ If the error code returned by the corresponding interface is 801, AR Engine does not support the current device.
109- build() {
110- NavDestination() {
111- RelativeContainer() {
112- if (this.arContext) {
113- ARView({ context: this.arContext })
114- }
115- }
116- }
117- .onAppear(() => {
118- this.initARView()
119- })
120- .onWillDisappear(() => {
121- this.arContext?.destroy();
122- })
123- }
124 132 
125- private initARView(): void {133+2. The HarmonyOS version must be HarmonyOS 6.1.0 Release or later.
126- let context = new arViewController.ARViewContext()134+3. The DevEco Studio version must be DevEco Studio 6.1.0 Release or later.
127- context.init().then(() => {135+4. The HarmonyOS SDK version must be HarmonyOS 6.1.0 Release SDK or later.
128- this.arContext = context;
129- console.info(`Succeeded in initializing ARView.`);
130- }).catch((err: BusinessError) => {
131- console.error(`Failed to init context. Code is ${err.code}, message is ${err.message}`);
132- });
133- }
134-}
135-```
136-If the error code returned by the corresponding interface is 801, AR Engine does not support the current device.
137- 
138-2. It is recommended that DevEco Studio 5.1.0 Release or later be used.
139-3. This sample is based on the stage model. It is recommended that HarmonyOS 5.1.0 Release SDK or later be used.
140 136 
141## AR Engine Depth Estimation Technology Limitations and Disclaimer137## AR Engine Depth Estimation Technology Limitations and Disclaimer
142 138 
@@ -163,20 +159,6 @@ If the error code returned by the corresponding interface is 801, AR Engine does
163 device. It is not intended for use as medical equipment. It also hasn't been approved to meet accessibility or159 device. It is not intended for use as medical equipment. It also hasn't been approved to meet accessibility or
164 life safety standards.160 life safety standards.
165 161 
166-## Change log
167- 
168-### Features of Version 1.0.0 Update
169- 
170-1. **ARWorld** capability launched.<br>
171- Plane recognition, collision detection, and object placement features are now available.
172-2. **ARDepth** capability launched.<br>
173- Depth estimation and depth map rendering features are now available.
174-3. **ARMesh** capability launched.<br>
175- Mesh scanning and mesh map rendering capabilities are now available.
176-4. **ARImage** capability launched.<br>
177- Image tracking capability is now available, enabling developers to detect environmental target feature points via
178- local images.
179- 
180## Related documents162## Related documents
181 163 
182[AREngine Development Guide](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ar-engine-kit-guide)164[AREngine Development Guide](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ar-engine-kit-guide)
RREADME_CN.md→README.md+76-47
@@ -52,6 +52,27 @@ AR Engine(以下统称AR引擎)助力应用融合虚拟世界与现实世界
523. 若选择添加图片,则弹出图片添加窗口,否则直接进入下一步。523. 若选择添加图片,则弹出图片添加窗口,否则直接进入下一步。
534. 开始跟踪目标图片,屏幕上可看见蓝色线框框选的目标图片。534. 开始跟踪目标图片,屏幕上可看见蓝色线框框选的目标图片。
54 54 
55+### ARFace效果预览
56+ 
57+| **应用首页** | **识别人脸并渲染** |
58+|:-----------------------------:|:-----------------------------------:|
59+| ![](screenshots/homePage.png) | ![](screenshots/selectFace.png) |
60+ 
61+1. 在手机的主屏幕,点击“ARSampleArkts”,启动应用,在主界面可见“ARFace”按钮。
62+2. 选择ARFace。
63+3. 进入后可以对着人脸进行扫描。
64+4. 识别到人脸后可以渲染出人脸Mesh。
65+ 
66+### ARBody效果预览
67+ 
68+| **应用首页** | **识别并渲染人体骨骼点** |
69+|:-----------------------------:|:-------------------------:|
70+| ![](screenshots/homePage.png) | ![](screenshots/body.png) |
71+ 
72+1. 在手机的主屏幕,点击“ARSampleArkts”,启动应用,在主界面可见“ARBody”按钮。
73+2. 点击“ARBody”并扫描人体。
74+3. 识别到人体后可以渲染处人体骨骼点。
75+ 
55## 使用说明76## 使用说明
56 77 
57开发者使用DevEco Studio打开本工程,签名后直接在真机设备上运行即可。78开发者使用DevEco Studio打开本工程,签名后直接在真机设备上运行即可。
@@ -65,7 +86,9 @@ AR Engine(以下统称AR引擎)助力应用融合虚拟世界与现实世界
65│ ├──entryability86│ ├──entryability
66│ │ └──EntryAbility.ets // 程序入口类87│ │ └──EntryAbility.ets // 程序入口类
67│ ├──pages88│ ├──pages
89+│ │ ├──ARBody.ets // ARBody界面
68│ │ ├──ARDepth.ets // ARDepth选择模式界面90│ │ ├──ARDepth.ets // ARDepth选择模式界面
91+│ │ ├──ARFace.ets // ARFace界面
69│ │ ├──ARImage.ets // ARImage选择模式界面92│ │ ├──ARImage.ets // ARImage选择模式界面
70│ │ ├──ARImageByAdd.ets // ARImage本地图片模式界面93│ │ ├──ARImageByAdd.ets // ARImage本地图片模式界面
71│ │ ├──ARImageByDatabase.ets // ARImage本地数据库模式界面94│ │ ├──ARImageByDatabase.ets // ARImage本地数据库模式界面
@@ -86,43 +109,15 @@ AR Engine(以下统称AR引擎)助力应用融合虚拟世界与现实世界
86 109 
871. 开发者可通过接口调用判断当前设备是否支持AR Engine。接口的调用参考方式如下:1101. 开发者可通过接口调用判断当前设备是否支持AR Engine。接口的调用参考方式如下:
88 111 
89-```javascript112+ ```typescript
90-import { arViewController, ARView } from '@kit.AREngine';113+ import { arEngine, arViewController } from '@kit.AREngine';
91-import { BusinessError } from '@kit.BasicServicesKit';114+
92-@Component115+ let ret: boolean = arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_FACE);
93-struct ARTest {116+ ```
94- @State arContext?: arViewController.ARViewContext = undefined;117+ 若对应接口返回错误码为ARENGINE_ERROR_DEVICE_NOT_SUPPORTED,则表示AR Engine不支持当前设备。
95- build() {118+2. HarmonyOS系统:6.1.0 Release及以上。
96- NavDestination() {119+3. DevEco Studio版本:DevEco Studio 6.1.0 Release及以上。
97- RelativeContainer() {120+4. HarmonyOS SDK版本:推荐HarmonyOS 6.1.0 Release SDK及以上。
98- if (this.arContext) {
99- ARView({ context: this.arContext })
100- }
101- }
102- }
103- .onAppear(() => {
104- this.initARView()
105- })
106- .onWillDisappear(() => {
107- this.arContext?.destroy();
108- })
109- }
110- 
111- private initARView(): void {
112- let context = new arViewController.ARViewContext()
113- context.init().then(() => {
114- this.arContext = context;
115- console.info(`Succeeded in initializing ARView.`);
116- }).catch((err: BusinessError) => {
117- console.error(`Failed to init context. Code is ${err.code}, message is ${err.message}`);
118- });
119- }
120-}
121-```
122-若对应接口返回错误码为801,则表示AR Engine不支持当前设备。
123- 
124-2. 本实例支持的DevEco Studio版本:推荐DevEco Studio 5.1.0 Release及以上。
125-3. 本实例为Stage模型,支持的HarmonyOS SDK版本:推荐HarmonyOS 5.1.0 Release SDK及以上。
126 121 
127## AR Engine深度估计功能技术局限性及免责声明122## AR Engine深度估计功能技术局限性及免责声明
128 123 
@@ -140,18 +135,52 @@ struct ARTest {
140 1. 不作为医疗设备或生命安全系统设计。135 1. 不作为医疗设备或生命安全系统设计。
141 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。136 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。
142 137 
143-## 更新日志138+## AR Engine高精几何重建功能技术局限性及免责声明
144 139 
145-### 1.0.0版本更新功能介绍140+1. 技术局限性声明:本功能提供的能力,涉及几何数值精度可能受以下因素影响:
141+ 1. 环境光照条件(例如强光/弱光/反光场景)。
142+ 2. 物体表面材质特性(例如透明/镜面/单色物体)。
143+ 3. 设备硬件性能差异(例如摄像头/传感器参数)。
144+ 4. 动态场景下的实时性限制等。
145+2. 免责声明:
146+ 本高精几何重建仅提供功能,并不构成对产品的质量保证或任何承诺。开发者自主选择是否使用HarmonyOS提供的功能开发应用软件,开发者对应用软件的使用目的、
147+ 效果和责任自行负责。若开发用于视障人士避障、残疾人辅助等场景的应用软件,开发者应承担开展多场景压力测试,并建立数据校验机制,特别在安全相关场景需
148+ 部署冗余保障方案,并确保应用合法合规的开发和运营。HarmonyOS不承担由此产生的任何直接或间接责任。
146 149 
147-1. ARWorld能力上线。<br>150+ 高精几何重建提供的功能:
148- 平面识别、碰撞检测、物体摆放功能上线。151+ 1. 不作为医疗设备或生命安全系统设计。
149-2. ARDepth能力上线。<br>152+ 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。
150- 深度估计、深度图渲染功能上线。153+ 
151-3. ARMesh能力上线。<br>154+## AR Engine人脸跟踪功能技术局限性及免责声明
152- 网格扫描、网格图绘制能力上线。155+ 
153-4. ARImage能力上线。<br>156+1. 技术局限性声明:本功能提供的能力,涉及人脸跟踪精度可能受以下因素影响:
154- 图像跟踪能力上线,开发者可通过本地图片检测环境目标特征点。157+ 
158+ 1. 环境光照条件(例如强光/弱光/反光场景)。
159+ 2. 物体表面材质特性(例如透明/镜面/单色物体)。
160+ 3. 设备硬件性能差异(例如摄像头/传感器参数)。
161+ 4. 动态场景下的实时性限制等。
162+2. 免责声明:
163+ 本人脸跟踪仅提供功能,并不构成对产品的质量保证或任何承诺。开发者自主选择是否使用HarmonyOS提供的功能开发应用软件,开发者对应用软件的使用目的、
164+ 效果和责任自行负责。若开发用于视障人士避障、残疾人辅助等场景的应用软件,开发者应承担开展多场景压力测试,并建立数据校验机制,特别在安全相关场景需
165+ 部署冗余保障方案,并确保应用合法合规的开发和运营。HarmonyOS不承担由此产生的任何直接或间接责任。
166+ 
167+ 人脸跟踪提供的功能:
168+ 1. 不作为医疗设备或生命安全系统设计。
169+ 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。
170+ 
171+## AR Engine人体跟踪与骨骼关键点识别功能技术局限性及免责声明
172+1. 技术局限性声明:本功能提供的能力,涉及人体跟踪与骨骼关键点识别精度可能受以下因素影响:
173+ 
174+ 1. 环境光照条件(例如强光/弱光/反光场景)。
175+ 2. 物体表面材质特性(例如透明/镜面/单色物体)。
176+ 3. 设备硬件性能差异(例如摄像头/传感器参数)。
177+ 4. 动态场景下的实时性限制等。
178+2. 免责声明:
179+ 本人体跟踪与骨骼关键点识别仅提供功能,并不构成对产品的质量保证或任何承诺。开发者自主选择是否使用HarmonyOS提供的功能开发应用软件,开发者对应用软件的使用目的、效果和责任自行负责。若开发用于视障人士避障、残疾人辅助等场景的应用软件,开发者应承担开展多场景压力测试,并建立数据校验机制,特别在安全相关场景需部署冗余保障方案,并确保应用合法合规的开发和运营。HarmonyOS不承担由此产生的任何直接或间接责任。
180+ 
181+ 人体跟踪与骨骼关键点识别提供的功能:
182+ 1. 不作为医疗设备或生命安全系统设计。
183+ 2. 未经认证,不作为医疗辅助设备;不作为医疗器材;未经无障碍设施认证或生命安全认证。
155 184 
156## 相关文档185## 相关文档
157 186 
@@ -6,9 +6,9 @@
6 {6 {
7 "name": "default",7 "name": "default",
8 "signingConfig": "default",8 "signingConfig": "default",
9- "compatibleSdkVersion": "6.0.0(20)",9+ "compatibleSdkVersion": "6.1.0(23)",
10 "runtimeOS": "HarmonyOS",10 "runtimeOS": "HarmonyOS",
11- "targetSdkVersion": "6.0.0(20)"11+ "targetSdkVersion": "6.1.0(23)"
12 }12 }
13 ],13 ],
14 "buildModeSet": [14 "buildModeSet": [
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -13,10 +13,10 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16-import { window } from '@kit.ArkUI';
17import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit';16import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit';
18-import { Logger } from '../utils/Logger';17+import { window } from '@kit.ArkUI';
19import { BusinessError } from '@kit.BasicServicesKit';18import { BusinessError } from '@kit.BasicServicesKit';
19+import { logger } from '../utils/Logger';
20 20 
21export default class EntryAbility extends UIAbility {21export default class EntryAbility extends UIAbility {
22 onCreate(): void {22 onCreate(): void {
@@ -24,40 +24,40 @@ export default class EntryAbility extends UIAbility {
24 this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);24 this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
25 } catch (error) {25 } catch (error) {
26 const err: BusinessError = error as BusinessError;26 const err: BusinessError = error as BusinessError;
27- Logger.error(`Failed to getApplicationContext. Code is ${err.code}, message is ${err.message}`);27+ logger.error(`Failed to getApplicationContext. Code is ${err.code}, message is ${err.message}`);
28 }28 }
29- Logger.info('Ability onCreate.');29+ logger.info('Ability onCreate.');
30 }30 }
31 31 
32 onDestroy(): void {32 onDestroy(): void {
33- Logger.info('Ability onDestroy.');33+ logger.info('Ability onDestroy.');
34 }34 }
35 35 
36 onWindowStageCreate(windowStage: window.WindowStage): void {36 onWindowStageCreate(windowStage: window.WindowStage): void {
37 // Main window is created, set main page for this ability37 // Main window is created, set main page for this ability
38- Logger.info('Ability onWindowStageCreate.');38+ logger.info('Ability onWindowStageCreate.');
39 39 
40 windowStage.loadContent('pages/Selector', (err) => {40 windowStage.loadContent('pages/Selector', (err) => {
41 if (err.code) {41 if (err.code) {
42- Logger.error(`Failed to load the content. Code: ${err.code}, message: ${err.message}.`);42+ logger.error(`Failed to load the content. Code: ${err.code}, message: ${err.message}.`);
43 return;43 return;
44 }44 }
45- Logger.info('Succeeded in loading the content.');45+ logger.info('Succeeded in loading the content.');
46 })46 })
47 }47 }
48 48 
49 onWindowStageDestroy(): void {49 onWindowStageDestroy(): void {
50 // Main window is destroyed, release UI related resources50 // Main window is destroyed, release UI related resources
51- Logger.info('Ability onWindowStageDestroy.');51+ logger.info('Ability onWindowStageDestroy.');
52 }52 }
53 53 
54 onForeground(): void {54 onForeground(): void {
55 // Ability has brought to foreground55 // Ability has brought to foreground
56- Logger.info('Ability onForeground.');56+ logger.info('Ability onForeground.');
57 }57 }
58 58 
59 onBackground(): void {59 onBackground(): void {
60 // Ability has back to background60 // Ability has back to background
61- Logger.info('Ability onBackground.');61+ logger.info('Ability onBackground.');
62 }62 }
63}63}
@@ -0,0 +1,302 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+import { arEngine, ARView, arViewController } from '@kit.AREngine';
17+import { Node, Scene } from '@kit.ArkGraphics3D';
18+import { display } from '@kit.ArkUI';
19+import { BusinessError } from '@kit.BasicServicesKit';
20+import { logger } from '../utils/Logger';
21+ 
22+interface BodyInfo {
23+ trackId: number,
24+ landmarks: arEngine.ARBodyLandmark2D[]
25+}
26+ 
27+function arLandmarksToMap(
28+ arLandmarks: arEngine.ARBodyLandmark2D[]): Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D> {
29+ let typeToLandmarks: Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D> = new Map();
30+ for (let arLandmark of arLandmarks) {
31+ typeToLandmarks.set(arLandmark.type, arLandmark);
32+ }
33+ return typeToLandmarks;
34+}
35+ 
36+type OnBodyInfoCallback = (bodyInfo: arEngine.ARBody[]) => void;
37+ 
38+const DEFAULT_WIDTH: number = 1440;
39+const DEFAULT_HEIGHT: number = 1080;
40+const DEFAULT_CONVERT_FACTOR: number = 4 / 3;
41+const FULL_SCREEN_SIZE: string = '100%';
42+ 
43+class ARViewCallbackImpl extends arViewController.ARViewCallback {
44+ private callback?: OnBodyInfoCallback;
45+ 
46+ setCallback(cb: OnBodyInfoCallback) {
47+ this.callback = cb;
48+ }
49+ 
50+ onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
51+ }
52+ 
53+ onAnchorUpdate(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
54+ }
55+ 
56+ async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> {
57+ if (!ctx.session) {
58+ return;
59+ }
60+ logger.info('onFrameUpdate enter');
61+ const arSession: arEngine.ARSession = ctx.session;
62+ try {
63+ const frame: arEngine.ARFrame = arSession.getFrame();
64+ const bodies: arEngine.ARBody[] = frame.acquireBodySkeleton();
65+ this.callback ? this.callback(bodies) : logger.error(`callback not set`);
66+ } catch (error) {
67+ const err: BusinessError = error as BusinessError;
68+ logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`);
69+ }
70+ }
71+}
72+ 
73+@Builder
74+export function ARBodyBuilder() {
75+ ARBody();
76+}
77+ 
78+@ComponentV2
79+struct ARBody {
80+ @Local arContext?: arViewController.ARViewContext = undefined;
81+ @Local bodyInfos: BodyInfo[] = [];
82+ @Local displayWidth: number = DEFAULT_WIDTH;
83+ @Local displayHeight: number = DEFAULT_HEIGHT;
84+ private uiContext: UIContext = this.getUIContext();
85+ 
86+ build() {
87+ NavDestination() {
88+ Stack() {
89+ Column() {
90+ RelativeContainer() {
91+ if (this.arContext) {
92+ ARView({ context: this.arContext })
93+ .height(FULL_SCREEN_SIZE)
94+ .width(FULL_SCREEN_SIZE)
95+ .alignRules({
96+ center: { anchor: '__container__', align: VerticalAlign.Center },
97+ middle: { anchor: '__container__', align: HorizontalAlign.Center }
98+ })
99+ }
100+ }
101+ }
102+ .width(this.uiContext.px2vp(this.displayWidth))
103+ .height(this.uiContext.px2vp(this.displayHeight))
104+ .justifyContent(FlexAlign.Center)
105+ .alignItems(HorizontalAlign.Center)
106+ 
107+ this.drawBodyPerception()
108+ }
109+ .width(FULL_SCREEN_SIZE)
110+ .height(FULL_SCREEN_SIZE)
111+ }
112+ .onAppear(() => {
113+ this.initARView();
114+ })
115+ .onWillDisappear(() => {
116+ this.stopARView();
117+ })
118+ .onShown(() => {
119+ this.resumeARView();
120+ })
121+ .onHidden(() => {
122+ this.pauseARView();
123+ })
124+ .hideTitleBar(true)
125+ .hideBackButton(true)
126+ .hideToolBar(true)
127+ }
128+ 
129+ @Builder
130+ drawBodyPerception() {
131+ Shape() {
132+ ForEach(this.bodyInfos, (bodyInfo: BodyInfo) => {
133+ this.drawBodyBones(arLandmarksToMap(bodyInfo.landmarks));
134+ this.drawBodyLandmarks(bodyInfo.landmarks);
135+ })
136+ }
137+ .width(this.uiContext.px2vp(this.displayWidth))
138+ .height(this.uiContext.px2vp(this.displayHeight))
139+ }
140+ 
141+ @Builder
142+ drawBodyLandmarks(bodyLandmarks: arEngine.ARBodyLandmark2D[]) {
143+ ForEach(bodyLandmarks, (landmark: arEngine.ARBodyLandmark2D) => {
144+ Circle({ width: 4, height: 4 })
145+ .position({
146+ x: this.uiContext.px2vp(landmark.x),
147+ y: this.uiContext.px2vp(landmark.y)
148+ })
149+ .fillOpacity(1)
150+ .fill(Color.White)
151+ })
152+ }
153+ 
154+ @Builder
155+ drawBodyBones(bodyLandmarks: Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D>) {
156+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.NOSE, arEngine.ARBodyLandmarkType.LEFT_SHOULDER,
157+ Color.Orange);
158+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.NOSE, arEngine.ARBodyLandmarkType.RIGHT_SHOULDER,
159+ Color.Orange);
160+ 
161+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_SHOULDER,
162+ arEngine.ARBodyLandmarkType.RIGHT_SHOULDER, Color.Orange);
163+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_SHOULDER,
164+ arEngine.ARBodyLandmarkType.RIGHT_HIP, Color.Orange);
165+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_HIP, arEngine.ARBodyLandmarkType.LEFT_HIP,
166+ Color.Orange);
167+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_HIP,
168+ arEngine.ARBodyLandmarkType.LEFT_SHOULDER, Color.Orange);
169+ 
170+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_SHOULDER,
171+ arEngine.ARBodyLandmarkType.LEFT_ELBOW, Color.Green);
172+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_ELBOW, arEngine.ARBodyLandmarkType.LEFT_WRIST,
173+ Color.Green);
174+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_HIP, arEngine.ARBodyLandmarkType.LEFT_KNEE,
175+ Color.Green);
176+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.LEFT_KNEE, arEngine.ARBodyLandmarkType.LEFT_ANKLE,
177+ Color.Green);
178+ 
179+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_SHOULDER,
180+ arEngine.ARBodyLandmarkType.RIGHT_ELBOW, Color.Blue);
181+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_ELBOW,
182+ arEngine.ARBodyLandmarkType.RIGHT_WRIST, Color.Blue);
183+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_HIP, arEngine.ARBodyLandmarkType.RIGHT_KNEE,
184+ Color.Blue);
185+ this.drawBodyBoneLine(bodyLandmarks, arEngine.ARBodyLandmarkType.RIGHT_KNEE,
186+ arEngine.ARBodyLandmarkType.RIGHT_ANKLE, Color.Blue);
187+ }
188+ 
189+ @Builder
190+ drawBodyBoneLine(bodyLandmarks: Map<arEngine.ARBodyLandmarkType, arEngine.ARBodyLandmark2D>,
191+ start: arEngine.ARBodyLandmarkType, end: arEngine.ARBodyLandmarkType, color: Color) {
192+ if (bodyLandmarks.has(start) && bodyLandmarks.has(end)) {
193+ Line()
194+ .startPoint([this.uiContext.px2vp(bodyLandmarks.get(start)?.x),
195+ this.uiContext.px2vp(bodyLandmarks.get(start)?.y)])
196+ .endPoint([this.uiContext.px2vp(bodyLandmarks.get(end)?.x), this.uiContext.px2vp(bodyLandmarks.get(end)?.y)])
197+ .stroke(color)
198+ .strokeWidth(3)
199+ }
200+ }
201+ 
202+ private onBodyInfoCb: OnBodyInfoCallback = (bodyInfos: arEngine.ARBody[]) => {
203+ let preWidth = this.displayWidth;
204+ let preHeight = this.displayHeight;
205+ try {
206+ if (display.getDefaultDisplaySync().availableWidth < display.getDefaultDisplaySync().availableHeight) {
207+ this.displayWidth = display.getDefaultDisplaySync().availableWidth;
208+ this.displayHeight = this.displayWidth * DEFAULT_CONVERT_FACTOR;
209+ } else {
210+ this.displayHeight = display.getDefaultDisplaySync().availableHeight;
211+ this.displayWidth = this.displayHeight * DEFAULT_CONVERT_FACTOR;
212+ }
213+ } catch (e) {
214+ logger.error(`display.getDefaultDisplaySync failed error.message:${e?.message}, error.code:${e?.code}`);
215+ this.displayWidth = preWidth;
216+ this.displayHeight = preHeight;
217+ }
218+ 
219+ this.bodyInfos = bodyInfos.map((value: arEngine.ARBody) => {
220+ let landmarks: arEngine.ARBodyLandmark2D[] = [];
221+ try {
222+ landmarks = value.getLandmarks2D();
223+ } catch (error) {
224+ const err: BusinessError = error as BusinessError;
225+ logger.error(`Failed to get Landmarks2D. Code is ${err.code}, message is ${err.message}`);
226+ }
227+ landmarks.forEach((value: arEngine.ARBodyLandmark2D) => {
228+ value.x = value.x * this.displayWidth;
229+ value.y = value.y * this.displayHeight;
230+ })
231+ let info: BodyInfo = {
232+ trackId: value.trackId,
233+ landmarks: landmarks
234+ }
235+ return info;
236+ })
237+ }
238+ 
239+ private async initARView(): Promise<void> {
240+ Scene.load().then(async (scene: Scene) => {
241+ let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();
242+ viewContext.scene = scene;
243+ let callback = new ARViewCallbackImpl();
244+ callback.setCallback(this.onBodyInfoCb);
245+ viewContext.callback = callback;
246+ viewContext.config = {
247+ type: arEngine.ARType.BODY,
248+ planeFindingMode: arEngine.ARPlaneFindingMode.DISABLED,
249+ powerMode: arEngine.ARPowerMode.NORMAL,
250+ semanticMode: arEngine.ARSemanticMode.NONE,
251+ poseMode: arEngine.ARPoseMode.GRAVITY,
252+ depthMode: arEngine.ARDepthMode.DISABLED,
253+ meshMode: arEngine.ARMeshMode.DISABLED,
254+ focusMode: arEngine.ARFocusMode.AUTO,
255+ maxDetectedBodyNum: 1,
256+ cameraLensFacing: 0
257+ };
258+ viewContext.init().then(() => {
259+ this.arContext = viewContext;
260+ logger.info('Succeeded in initializing ARView.');
261+ }).catch((err: BusinessError) => {
262+ logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
263+ })
264+ })
265+ }
266+ 
267+ private async stopARView(): Promise<void> {
268+ if (!this.arContext) {
269+ return;
270+ }
271+ try {
272+ await this.arContext.destroy();
273+ } catch (error) {
274+ const err: BusinessError = error as BusinessError;
275+ logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
276+ }
277+ }
278+ 
279+ private pauseARView(): void {
280+ if (!this.arContext) {
281+ return;
282+ }
283+ try {
284+ this.arContext.pause();
285+ } catch (error) {
286+ const err: BusinessError = error as BusinessError;
287+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);
288+ }
289+ }
290+ 
291+ private resumeARView(): void {
292+ if (!this.arContext) {
293+ return;
294+ }
295+ try {
296+ this.arContext.resume();
297+ } catch (error) {
298+ const err: BusinessError = error as BusinessError;
299+ logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
300+ }
301+ }
302+}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -16,7 +16,7 @@
16import { arEngine, ARView, arViewController } from '@kit.AREngine';16import { arEngine, ARView, arViewController } from '@kit.AREngine';
17import { Node, Scene } from '@kit.ArkGraphics3D';17import { Node, Scene } from '@kit.ArkGraphics3D';
18import { BusinessError } from '@kit.BasicServicesKit';18import { BusinessError } from '@kit.BasicServicesKit';
19-import { Logger } from '../utils/Logger';19+import { logger } from '../utils/Logger';
20import { arrayBufferInt32ToNumber } from '../utils/Utils';20import { arrayBufferInt32ToNumber } from '../utils/Utils';
21 21 
22@Builder22@Builder
@@ -29,11 +29,11 @@ let centerConfidence: number;
29 29 
30@Component30@Component
31struct ARDepth {31struct ARDepth {
32- private delayInterval: number = 33;
33- private intervalId: number = -1;
34 @State arContext?: arViewController.ARViewContext = undefined;32 @State arContext?: arViewController.ARViewContext = undefined;
35 @State depthConfidence: number = 0;33 @State depthConfidence: number = 0;
36 @State depthDistance: string = '0';34 @State depthDistance: string = '0';
35+ private delayInterval: number = 33;
36+ private intervalId: number = -1;
37 37 
38 build(): void {38 build(): void {
39 NavDestination() {39 NavDestination() {
@@ -109,9 +109,9 @@ struct ARDepth {
109 }109 }
110 viewContext.init().then(() => {110 viewContext.init().then(() => {
111 this.arContext = viewContext;111 this.arContext = viewContext;
112- Logger.info('Succeeded in initting ARView.');112+ logger.info('Succeeded in initting ARView.');
113 }).catch((err: BusinessError) => {113 }).catch((err: BusinessError) => {
114- Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}`);114+ logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}`);
115 })115 })
116 })116 })
117 }117 }
@@ -126,19 +126,22 @@ struct ARDepth {
126 }, this.delayInterval)126 }, this.delayInterval)
127 }127 }
128 128 
129- private stopARView(): void {129+ private async stopARView(): Promise<void> {
130 if (!this.arContext) {130 if (!this.arContext) {
131 return;131 return;
132 }132 }
133+ 
134+ clearInterval(this.intervalId);
135+ 
133 try {136 try {
134- clearInterval(this.intervalId);137+ await this.arContext.destroy();
135- this.arContext.destroy();
136- centerDistance = 0;
137- centerConfidence = 0;
138 } catch (error) {138 } catch (error) {
139 const err: BusinessError = error as BusinessError;139 const err: BusinessError = error as BusinessError;
140- Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);140+ logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
141 }141 }
142+ 
143+ centerDistance = 0;
144+ centerConfidence = 0;
142 }145 }
143 146 
144 private resumeARView(): void {147 private resumeARView(): void {
@@ -149,7 +152,7 @@ struct ARDepth {
149 this.arContext.resume();152 this.arContext.resume();
150 } catch (error) {153 } catch (error) {
151 const err: BusinessError = error as BusinessError;154 const err: BusinessError = error as BusinessError;
152- Logger.error(`Failed to resume ARView. Code is ${err.code}, message is ${err.message}`);155+ logger.error(`Failed to resume ARView. Code is ${err.code}, message is ${err.message}`);
153 }156 }
154 }157 }
155 158 
@@ -161,7 +164,7 @@ struct ARDepth {
161 this.arContext.pause();164 this.arContext.pause();
162 } catch (error) {165 } catch (error) {
163 const err: BusinessError = error as BusinessError;166 const err: BusinessError = error as BusinessError;
164- Logger.error(`Failed to pause ARView. Code is ${err.code}, message is ${err.message}`);167+ logger.error(`Failed to pause ARView. Code is ${err.code}, message is ${err.message}`);
165 }168 }
166 }169 }
167}170}
@@ -178,7 +181,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
178 return;181 return;
179 }182 }
180 183 
181- let session: arEngine.ARSession | undefined = ctx.session;184+ let session: arEngine.ARSession = ctx.session;
182 185 
183 try {186 try {
184 let frame: arEngine.ARFrame = session.getFrame();187 let frame: arEngine.ARFrame = session.getFrame();
@@ -192,7 +195,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
192 centerConfidence = confidencePlane[index];195 centerConfidence = confidencePlane[index];
193 } catch (error) {196 } catch (error) {
194 const err: BusinessError = error as BusinessError;197 const err: BusinessError = error as BusinessError;
195- Logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}`);198+ logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}`);
196 }199 }
197 }200 }
198}201}
Rentry/src/main/ets/pages/ARSemanticDense.ets→entry/src/main/ets/pages/ARFace.ets+186-82
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -14,28 +14,56 @@
14 */14 */
15 15 
16import { arEngine, ARView, arViewController } from '@kit.AREngine';16import { arEngine, ARView, arViewController } from '@kit.AREngine';
17-import { Node, Scene } from '@kit.ArkGraphics3D';17+import {
18+ CustomGeometry,
19+ Geometry,
20+ Material,
21+ MaterialType,
22+ MeshResource,
23+ Node,
24+ PrimitiveTopology,
25+ Scene,
26+ SceneResourceFactory,
27+ Shader,
28+ ShaderMaterial,
29+ Vec3
30+} from '@kit.ArkGraphics3D';
18import { BusinessError } from '@kit.BasicServicesKit';31import { BusinessError } from '@kit.BasicServicesKit';
19-import { Logger } from '../utils/Logger';32+import { logger } from '../utils/Logger';
20- 33+import {
21-let cubeVertexData: Array<number>;34+ arrayBufferFloat32ToNumber,
22-let cubeConfidence: number;35+ arrayBufferInt32ToNumber,
23-let cubeLabel: arEngine.ARSemanticPlaneLabel;36+ generateFaceMeshIndex,
37+ generateMeshInput,
38+ getFaceFrontVertices
39+} from '../utils/Utils';
24 40 
25@Builder41@Builder
26-export function ARSemanticDenseBuilder(): void {42+export function ARFaceBuilder() {
27- ARSemanticDense();43+ ARFace();
28}44}
29 45 
30-@Component46+let faceVertices: number[] = [];
31-struct ARSemanticDense {47+let faceGeoArr: Geometry[] = [];
32- @State arContext?: arViewController.ARViewContext = undefined;48+let shader: Shader | undefined = undefined;
33- // 平面类型49+let material: ShaderMaterial | undefined = undefined;
34- private intervalId: number = -1;50+let rf: SceneResourceFactory | undefined = undefined;
35- // 重复调用函数时间间隔为33ms,即设定为30fps
36- private delayInterval: number = 33;
37 51 
38- build(): void {52+interface Blend {
53+ enabled: boolean;
54+}
55+ 
56+interface Material2 extends Material {
57+ blend: Blend;
58+};
59+ 
60+@Component
61+export struct ARFace {
62+ pageInfos: NavPathStack = new NavPathStack();
63+ @State context: Context = this.getUIContext().getHostContext() as Context;
64+ @State arContext?: arViewController.ARViewContext = undefined;
65+ 
66+ build() {
39 NavDestination() {67 NavDestination() {
40 RelativeContainer() {68 RelativeContainer() {
41 if (this.arContext) {69 if (this.arContext) {
@@ -49,16 +77,12 @@ struct ARSemanticDense {
49 }77 }
50 }78 }
51 }79 }
52- .onAppear(() => {80+ .onAppear(async () => {
53 this.initARView();81 this.initARView();
54- // 设定在30fps下更新获取物体体积信息
55- this.intervalId = setInterval(async () => {
56- }, this.delayInterval);
57 })82 })
58- .onWillDisappear(() => {83+ .onWillDisappear(async () => {
59- // 退出setInterval函数84+ await this.stopARView();
60- clearInterval(this.intervalId);85+ this.clearGlobalVariables();
61- this.stopARView();
62 })86 })
63 .onShown(() => {87 .onShown(() => {
64 this.resumeARView();88 this.resumeARView();
@@ -71,51 +95,15 @@ struct ARSemanticDense {
71 .hideToolBar(true)95 .hideToolBar(true)
72 }96 }
73 97 
74- private initARView(): void {98+ private async stopARView(): Promise<void> {
75- Scene.load().then(async (scene: Scene) => {
76- let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();
77- viewContext.scene = scene;
78- viewContext.callback = new ARViewCallbackImpl();
79- viewContext.config = {
80- type: arEngine.ARType.WORLD,
81- planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
82- powerMode: arEngine.ARPowerMode.NORMAL,
83- semanticDenseMode: arEngine.ARSemanticDenseMode.CUBE_VOLUME, // 开启体积测量
84- poseMode: arEngine.ARPoseMode.GRAVITY,
85- depthMode: arEngine.ARDepthMode.DISABLED,
86- meshMode: arEngine.ARMeshMode.DISABLED,
87- focusMode: arEngine.ARFocusMode.AUTO
88- }
89- viewContext.init().then(() => {
90- this.arContext = viewContext;
91- Logger.info('Succeeded in initting ARView.');
92- }).catch((err: BusinessError) => {
93- Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
94- })
95- })
96- }
97- 
98- private stopARView(): void {
99 if (!this.arContext) {99 if (!this.arContext) {
100 return;100 return;
101 }101 }
102 try {102 try {
103- this.arContext.destroy();103+ await this.arContext.destroy();
104 } catch (error) {104 } catch (error) {
105 const err: BusinessError = error as BusinessError;105 const err: BusinessError = error as BusinessError;
106- Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);106+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}`);
107- }
108- }
109- 
110- private resumeARView(): void {
111- if (!this.arContext) {
112- return;
113- }
114- try {
115- this.arContext.resume();
116- } catch (error) {
117- const err: BusinessError = error as BusinessError;
118- Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
119 }107 }
120 }108 }
121 109 
@@ -127,9 +115,59 @@ struct ARSemanticDense {
127 this.arContext.pause();115 this.arContext.pause();
128 } catch (error) {116 } catch (error) {
129 const err: BusinessError = error as BusinessError;117 const err: BusinessError = error as BusinessError;
130- Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);118+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}`);
131 }119 }
132 }120 }
121+ 
122+ private resumeARView(): void {
123+ if (!this.arContext) {
124+ return;
125+ }
126+ try {
127+ this.arContext.resume();
128+ } catch (error) {
129+ const err: BusinessError = error as BusinessError;
130+ logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}`);
131+ }
132+ }
133+ 
134+ private initARView(): void {
135+ Scene.load().then(async (result: Scene) => {
136+ try {
137+ let ret: boolean = arViewController.isARTypeSupported(arEngine.ARFeatureType.ARENGINE_FEATURE_TYPE_FACE);
138+ logger.info(`ARFace isARTypeSupported is ${ret}`);
139+ } catch (error) {
140+ const err: BusinessError = error as BusinessError;
141+ logger.error(
142+ `Failed to get whether the device is support ARFace. Code is ${err.code}, message is ${err.message}`);
143+ }
144+ 
145+ let context = new arViewController.ARViewContext();
146+ context.scene = result;
147+ context.callback = new ARViewCallbackImpl();
148+ context.config = {
149+ type: arEngine.ARType.FACE,
150+ planeFindingMode: arEngine.ARPlaneFindingMode.DISABLED,
151+ powerMode: arEngine.ARPowerMode.NORMAL,
152+ focusMode: arEngine.ARFocusMode.AUTO,
153+ cameraLensFacing: arEngine.ARCameraLensFacing.FRONT,
154+ multiFaceMode: arEngine.ARMultiFaceMode.MULTIFACE_ENABLE,
155+ };
156+ context.init().then(() => {
157+ this.arContext = context;
158+ }).catch((err: BusinessError) => {
159+ logger.error(`Failed to init context. Code is ${err.code}, message is ${err.message}`);
160+ });
161+ })
162+ }
163+ 
164+ private clearGlobalVariables() {
165+ // Clearing Global Variables
166+ faceGeoArr = [];
167+ rf = undefined;
168+ shader = undefined;
169+ material = undefined;
170+ }
133}171}
134 172 
135class ARViewCallbackImpl extends arViewController.ARViewCallback {173class ARViewCallbackImpl extends arViewController.ARViewCallback {
@@ -141,31 +179,97 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
141 179 
142 async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> {180 async onFrameUpdate(ctx: arViewController.ARViewContext, sysBootTs: number): Promise<void> {
143 if (!ctx.session) {181 if (!ctx.session) {
182+ logger.error('arSession is undefined');
144 return;183 return;
145 }184 }
146 185 
147- let arSession: arEngine.ARSession = ctx.session;186+ let session: arEngine.ARSession = ctx.session;
187+ if (!rf) {
188+ rf = ctx.scene.getResourceFactory();
189+ }
190+ if (!shader) {
191+ // The plane color is controlled by the file plane.shader
192+ shader = await rf.createShader({ name: 'CustomShader', uri: $rawfile('shaders/custom_shader/face.shader') });
193+ }
194+ if (!material) {
195+ material = await rf.createMaterial({ name: 'CustomMaterial' }, MaterialType.SHADER);
196+ material.colorShader = shader;
197+ (material as Material2).blend = { enabled: true };
198+ }
148 199 
149 try {200 try {
150- let frame: arEngine.ARFrame = arSession.getFrame();201+ let mesh = new CustomGeometry();
151- if (frame) {202+ let geometry: Geometry | null = null;
152- let semanticData: arEngine.ARSemanticDenseData = frame.acquireSemanticDense();203+ if (session == null) {
153- if (semanticData) {204+ logger.error('session is null');
154- if (semanticData.cubeDataSize > 0) {205+ }
155- // 获取第一个Cube的体积数据206+ 
156- let semanticCubeData: arEngine.ARSemanticDenseCubeData = semanticData.acquireCubeData()[0];207+ let vertexArray: Vec3[][] = [];
157- cubeVertexData = semanticCubeData.vertexData;208+ let indexArray: Map<number, number[]> = new Map;
158- cubeConfidence = semanticCubeData.confidence;209+ 
159- cubeLabel = semanticCubeData.label;210+ // Acquire face data
160- }211+ let trackables: arEngine.ARTrackable[] = session.getAllTrackables(arEngine.ARTrackableType.FACE);
161- semanticData.release().catch((err: BusinessError) => {212+ logger.debug(`the faceList length is ${trackables.length}`);
162- Logger.error(`semanticData failed to release. Code is ${err.code}, message is ${err.message}.`);213+ for (let i = 0; i < trackables.length; ++i) {
163- });214+ let face: arEngine.ARFace = trackables[i] as arEngine.ARFace;
215+ let centerPose = face.getPose();
216+ let viewMatrix = centerPose.getMatrix();
217+ 
218+ if (trackables[i].state !== arEngine.ARTrackingState.TRACKING) {
219+ logger.error(`Face not in tracking state`);
220+ continue;
221+ }
222+ // Data Process
223+ let faceGeometry: arEngine.ARGeometry = face.getGeometry();
224+ let faceBlendShapes: arEngine.ARBlendShapes = face.getBlendShapes();
225+ let tmpVert = faceGeometry.getVertices();
226+ let tmpIndices = faceGeometry.getIndices();
227+ faceVertices = arrayBufferFloat32ToNumber(tmpVert);
228+ let faceIndices: number[] = arrayBufferInt32ToNumber(tmpIndices);
229+ vertexArray.push(getFaceFrontVertices(viewMatrix, faceVertices));
230+ indexArray.set(i, faceIndices);
231+ 
232+ // BlendShapes Print
233+ logger.info('the count of blendShapes is' + faceBlendShapes.count);
234+ logger.info('the data of blendShapes is' + arrayBufferFloat32ToNumber(faceBlendShapes.getData()));
235+ logger.info('the types of blendShapes is' + faceBlendShapes.getTypes());
236+ }
237+ 
238+ 
239+ // Draw
240+ mesh.topology = PrimitiveTopology.TRIANGLE_LIST;
241+ mesh.vertices = generateMeshInput(vertexArray);
242+ mesh.indices = generateFaceMeshIndex(indexArray, faceVertices.length / 3);
243+ 
244+ let meshRes: MeshResource = await rf.createMesh({ name: 'resource' }, mesh);
245+ geometry = await rf.createGeometry({ name: 'face' }, meshRes);
246+ meshRes.destroy();
247+ 
248+ geometry.mesh.materialOverride = material;
249+ 
250+ // The node from the previous frame needs to be cleared
251+ if (ctx.scene) {
252+ // face
253+ let len: number = faceGeoArr.length;
254+ for (let index = len - 1; index >= 0; index--) {
255+ let geo = faceGeoArr[index];
256+ ctx.scene.root?.children.remove(geo);
257+ geo.destroy();
258+ faceGeoArr.splice(index, 1);
259+ logger.info('geo.destroy()');
164 }260 }
165 }261 }
262+ 
263+ // Adding a Face to a Node
264+ if (geometry) {
265+ ctx.scene?.root?.children.append(geometry);
266+ faceGeoArr.push(geometry);
267+ }
268+ vertexArray = [];
269+ indexArray.clear();
166 } catch (error) {270 } catch (error) {
167 const err: BusinessError = error as BusinessError;271 const err: BusinessError = error as BusinessError;
168- Logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`);272+ logger.error(`Failed to acquire face information. Code is ${err.code}, message is ${err.message}`)
169 }273 }
170 }274 }
171-}275+}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -13,9 +13,9 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16-import { photoAccessHelper } from '@kit.MediaLibraryKit';
17-import { Logger } from '../utils/Logger';
18import { BusinessError } from '@kit.BasicServicesKit';16import { BusinessError } from '@kit.BasicServicesKit';
17+import { photoAccessHelper } from '@kit.MediaLibraryKit';
18+import { logger } from '../utils/Logger';
19 19 
20@Builder20@Builder
21export function ARImageBuilder(): void {21export function ARImageBuilder(): void {
@@ -67,17 +67,21 @@ struct ARImage {
67 67 
68 photoPicker.select(photoOption).then((photoResult) => {68 photoPicker.select(photoOption).then((photoResult) => {
69 if (photoResult.photoUris.length > 0 && photoResult.photoUris[0].length > 0) {69 if (photoResult.photoUris.length > 0 && photoResult.photoUris[0].length > 0) {
70- this.pageInfos.pushDestinationByName('ARImageByAdd', photoResult.photoUris);70+ this.pageInfos.pushDestinationByName('ARImageByAdd', photoResult.photoUris).catch((err: BusinessError) => {
71+ logger.error(`Failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`);
72+ });
71 }73 }
74+ }).catch((err: BusinessError) => {
75+ logger.warn(`Failed to select photos. Code is ${err.code}, message is ${err.message}.`);
72 })76 })
73 } catch (error) {77 } catch (error) {
74- Logger.error(`Failed to select by photoPicker. Code: ${error.code}.`);78+ logger.error(`Failed to select by photoPicker. Code: ${error.code}.`);
75 }79 }
76 }80 }
77 81 
78 private loadDatabaseToTrack(): void {82 private loadDatabaseToTrack(): void {
79 this.pageInfos.pushDestinationByName('ARImageByDatabase', null).catch((err: BusinessError) => {83 this.pageInfos.pushDestinationByName('ARImageByDatabase', null).catch((err: BusinessError) => {
80- Logger.error(`ARImageByDatabase failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`);84+ logger.error(`ARImageByDatabase failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`);
81 })85 })
82 }86 }
83}87}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -34,8 +34,8 @@ import { Matrix4 } from '@kit.ArkUI';
34import { BusinessError } from '@kit.BasicServicesKit';34import { BusinessError } from '@kit.BasicServicesKit';
35import { fileIo } from '@kit.CoreFileKit';35import { fileIo } from '@kit.CoreFileKit';
36import { image } from '@kit.ImageKit';36import { image } from '@kit.ImageKit';
37-import { Logger } from '../utils/Logger';37+import { logger } from '../utils/Logger';
38-import { calculatePoint, createImageIndex, getImageVertices } from '../utils/Utils';38+import { calculatePoint, createImageIndex, getImageVertices, getResourceString } from '../utils/Utils';
39 39 
40@Builder40@Builder
41export function ARImageByAddBuilder(): void {41export function ARImageByAddBuilder(): void {
@@ -55,28 +55,25 @@ interface CustomerMaterial extends Material {
55@Component55@Component
56struct ARImageByAdd {56struct ARImageByAdd {
57 pageInfos: NavPathStack = new NavPathStack();57 pageInfos: NavPathStack = new NavPathStack();
58- private imagePathArray: string[] = [];
59- // When destroy is set to true, the addImage function is used to determine whether to continue adding images.
60- private isProgramExits: boolean = false;
61- private isSaveDatabase: boolean = false;
62 @State arContext?: arViewController.ARViewContext = undefined;58 @State arContext?: arViewController.ARViewContext = undefined;
63 @State context: Context = this.getUIContext().getHostContext() as Context;59 @State context: Context = this.getUIContext().getHostContext() as Context;
64- @State totalImageCounts: number = this.imagePathArray.length;
65 @State addFailedImageCounts: number = 0;60 @State addFailedImageCounts: number = 0;
66 @State succeedImageCounts: number = 0;61 @State succeedImageCounts: number = 0;
67 @State addFailedMessage: string[] = [];62 @State addFailedMessage: string[] = [];
63+ private imagePathArray: string[] = [];
64+ @State totalImageCounts: number = this.imagePathArray.length;
65+ // When destroy is set to true, the addImage function is used to determine whether to continue adding images.
66+ private isProgramExits: boolean = false;
67+ private isSaveDatabase: boolean = false;
68 68 
69 build(): void {69 build(): void {
70 NavDestination() {70 NavDestination() {
71 RelativeContainer() {71 RelativeContainer() {
72 Column() {72 Column() {
73- Text(`${this.context.resourceManager.getStringByNameSync(73+ Text(`${getResourceString(this.context, 'add_image_msg_count')} ${this.succeedImageCounts +
74- 'add_image_msg_count')} ${this.succeedImageCounts +
75 this.addFailedImageCounts} / ${this.totalImageCounts}`)74 this.addFailedImageCounts} / ${this.totalImageCounts}`)
76- Text(`${this.context.resourceManager.getStringByNameSync('add_image_msg_success') +75+ Text(`${getResourceString(this.context, 'add_image_msg_success') + this.succeedImageCounts}`)
77- this.succeedImageCounts}`)76+ Text(`${getResourceString(this.context, 'add_image_msg_fail') + this.addFailedImageCounts}`)
78- Text(`${this.context.resourceManager.getStringByNameSync('add_image_msg_fail') +
79- this.addFailedImageCounts}`)
80 77 
81 if (this.addFailedMessage) {78 if (this.addFailedMessage) {
82 ForEach(this.addFailedMessage, (item: string) => {79 ForEach(this.addFailedMessage, (item: string) => {
@@ -86,7 +83,7 @@ struct ARImageByAdd {
86 }83 }
87 }84 }
88 .visibility(this.addFailedImageCounts + this.succeedImageCounts < this.totalImageCounts ? Visibility.Visible :85 .visibility(this.addFailedImageCounts + this.succeedImageCounts < this.totalImageCounts ? Visibility.Visible :
89- Visibility.None)86+ Visibility.None)
90 .foregroundColor(Color.Red)87 .foregroundColor(Color.Red)
91 .zIndex(1)88 .zIndex(1)
92 .alignRules({89 .alignRules({
@@ -112,14 +109,16 @@ struct ARImageByAdd {
112 109 
113 this.addImage(dataBase).then(() => {110 this.addImage(dataBase).then(() => {
114 if (this.addFailedImageCounts === this.totalImageCounts) {111 if (this.addFailedImageCounts === this.totalImageCounts) {
115- this.ShowDialog(this.context.resourceManager.getStringByNameSync('invalid_image_added'));112+ this.ShowDialog(getResourceString(this.context, 'invalid_image_added'));
116 }113 }
117 if (this.totalImageCounts === this.succeedImageCounts + this.addFailedImageCounts) {114 if (this.totalImageCounts === this.succeedImageCounts + this.addFailedImageCounts) {
118 this.initARView();115 this.initARView();
119 this.isSaveDatabase = true;116 this.isSaveDatabase = true;
120 }117 }
121 })118 })
122- })119+ }).catch((err: BusinessError) => {
120+ logger.warn(`Failed to create database. Code is ${err.code}, message is ${err.message}`);
121+ })
123 })122 })
124 .onWillDisappear(async () => {123 .onWillDisappear(async () => {
125 this.stopARView();124 this.stopARView();
@@ -140,6 +139,80 @@ struct ARImageByAdd {
140 .hideToolBar(true)139 .hideToolBar(true)
141 }140 }
142 141 
142+ // Asynchronously execute the task of adding pictures
143+ async addImage(dataBase: arEngine.ARAugmentedImageDatabase): Promise<void> {
144+ for (let index = 0; index < this.totalImageCounts; index++) {
145+ const magePath: string = this.imagePathArray[index];
146+ let file: fileIo.File;
147+ try {
148+ file = fileIo.openSync(magePath, fileIo.OpenMode.READ_ONLY);
149+ } catch (error) {
150+ const err: BusinessError = error as BusinessError;
151+ logger.error(`Failed to open image. Code is ${err.code}, message is ${err.message}.`);
152+ return;
153+ }
154+ let imageName: string = file.name;
155+ const imageSourceApi: image.ImageSource = image.createImageSource(file.fd);
156+ try {
157+ fileIo.closeSync(file);
158+ } catch (error) {
159+ const err: BusinessError = error as BusinessError;
160+ logger.error(`Failed to closeSync. Code: ${err.code}.`);
161+ this.addFailedImageCounts += 1;
162+ continue;
163+ }
164+ const imageInfo: image.ImageInfo = imageSourceApi.getImageInfoSync(0);
165+ if (!imageInfo) {
166+ logger.error('Failed to obtain the image pixel map information.');
167+ this.addFailedImageCounts += 1;
168+ continue;
169+ }
170+ const opts: image.DecodingOptions = {
171+ editable: true,
172+ desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
173+ desiredSize: { width: imageInfo.size.width, height: imageInfo.size.height }
174+ }
175+ let pixelMap: image.PixelMap = imageSourceApi.createPixelMapSync(opts);
176+ try {
177+ const info: image.ImageInfo = pixelMap.getImageInfoSync();
178+ let size: number = pixelMap.getPixelBytesNumber();
179+ logger.info(`The ets create pixelMap width: ${info.size.width}, height: ${info.size.height}, size: ${size}.`);
180+ } catch (error) {
181+ const err: BusinessError = error as BusinessError;
182+ logger.error(`Failed to get image info. Code is ${err.code}, message is ${err.message}.`);
183+ }
184+ 
185+ 
186+ if (this.isProgramExits) {
187+ break;
188+ }
189+ 
190+ await dataBase.addImage(imageName, pixelMap, 10).then((result: arEngine.ARAddAugmentedImageResult) => {
191+ logger.info(`The imageResult: ${result.index} ${result.stateReason}.`);
192+ if (result.stateReason !== arEngine.ARAddAugmentedImageReason.NONE) {
193+ this.addFailedImageCounts += 1;
194+ let stateReason: string = '';
195+ try {
196+ stateReason = errcode.get(result.stateReason) ?? '';
197+ } catch (error) {
198+ const err: BusinessError = error as BusinessError;
199+ logger.error(`Failed to get stateReason. Code is ${err.code}, message is ${err.message}.`);
200+ }
201+ this.addFailedMessage.push(`${getResourceString(this.context, 'image_name_by_add') +
202+ imageName + '\n' + getResourceString(this.context, 'image_add_reason_by_add') +
203+ stateReason + '\n'}`);
204+ } else {
205+ this.succeedImageCounts += 1;
206+ }
207+ }).catch(() => {
208+ this.addFailedImageCounts += 1;
209+ })
210+ 
211+ imageSourceApi.release();
212+ pixelMap.release();
213+ }
214+ }
215+ 
143 private initARView(): void {216 private initARView(): void {
144 Scene.load().then(async (scene: Scene) => {217 Scene.load().then(async (scene: Scene) => {
145 let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();218 let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();
@@ -157,9 +230,9 @@ struct ARImageByAdd {
157 }230 }
158 viewContext.init().then(() => {231 viewContext.init().then(() => {
159 this.arContext = viewContext;232 this.arContext = viewContext;
160- Logger.info('Succeeded in initting ARView.');233+ logger.info('Succeeded in initting ARView.');
161 }).catch((err: BusinessError) => {234 }).catch((err: BusinessError) => {
162- Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);235+ logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
163 })236 })
164 })237 })
165 }238 }
@@ -171,14 +244,14 @@ struct ARImageByAdd {
171 try {244 try {
172 this.isProgramExits = true;245 this.isProgramExits = true;
173 if (this.isSaveDatabase) {246 if (this.isSaveDatabase) {
174- SaveBufferToLocal(dataBase, this.context);247+ saveBufferToLocal(dataBase, this.context);
175 }248 }
176 249 
177 await dataBase.release();250 await dataBase.release();
178 await this.arContext?.destroy();251 await this.arContext?.destroy();
179 } catch (error) {252 } catch (error) {
180 const err: BusinessError = error as BusinessError;253 const err: BusinessError = error as BusinessError;
181- Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);254+ logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
182 }255 }
183 }256 }
184 257 
@@ -190,7 +263,7 @@ struct ARImageByAdd {
190 this.arContext.resume();263 this.arContext.resume();
191 } catch (error) {264 } catch (error) {
192 const err: BusinessError = error as BusinessError;265 const err: BusinessError = error as BusinessError;
193- Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);266+ logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
194 }267 }
195 }268 }
196 269 
@@ -202,61 +275,7 @@ struct ARImageByAdd {
202 this.arContext.pause();275 this.arContext.pause();
203 } catch (error) {276 } catch (error) {
204 const err: BusinessError = error as BusinessError;277 const err: BusinessError = error as BusinessError;
205- Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);278+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);
206- }
207- }
208- 
209- // Asynchronously execute the task of adding pictures
210- async addImage(dataBase: arEngine.ARAugmentedImageDatabase): Promise<void> {
211- for (let index = 0; index < this.totalImageCounts; index++) {
212- const magePath: string = this.imagePathArray[index];
213- let file: fileIo.File = fileIo.openSync(magePath, fileIo.OpenMode.READ_ONLY);
214- let imageName: string = file.name;
215- const imageSourceApi: image.ImageSource = image.createImageSource(file.fd);
216- try {
217- fileIo.closeSync(file);
218- } catch (error) {
219- const err: BusinessError = error as BusinessError;
220- Logger.error(`Failed to closeSync. Code: ${err.code}.`);
221- this.addFailedImageCounts += 1;
222- continue;
223- }
224- const imageInfo: image.ImageInfo = imageSourceApi.getImageInfoSync(0);
225- if (!imageInfo) {
226- Logger.error('Failed to obtain the image pixel map information.');
227- this.addFailedImageCounts += 1;
228- continue;
229- }
230- const opts: image.DecodingOptions = {
231- editable: true,
232- desiredPixelFormat: image.PixelMapFormat.RGBA_8888,
233- desiredSize: { width: imageInfo.size.width, height: imageInfo.size.height }
234- }
235- let pixelMap: image.PixelMap = imageSourceApi.createPixelMapSync(opts);
236- const info: image.ImageInfo = pixelMap.getImageInfoSync();
237- let size: number = pixelMap.getPixelBytesNumber();
238- Logger.info(`The ets create pixelMap width: ${info.size.width}, height: ${info.size.height}, size: ${size}.`);
239- 
240- if (this.isProgramExits) {
241- break;
242- }
243- 
244- await dataBase.addImage(imageName, pixelMap, 10).then((result: arEngine.ARAddAugmentedImageResult) => {
245- Logger.info(`The imageResult: ${result.index} ${result.stateReason}.`);
246- if (result.stateReason !== arEngine.ARAddAugmentedImageReason.NONE) {
247- this.addFailedImageCounts += 1;
248- this.addFailedMessage.push(`${this.context.resourceManager.getStringByNameSync('image_name_by_add') +
249- imageName + '\n' + this.context.resourceManager.getStringByNameSync('image_add_reason_by_add') +
250- errcode.get(result.stateReason) + '\n'}`);
251- } else {
252- this.succeedImageCounts += 1;
253- }
254- }).catch(() => {
255- this.addFailedImageCounts += 1;
256- })
257- 
258- imageSourceApi.release();
259- pixelMap.release();
260 }279 }
261 }280 }
262 281 
@@ -281,7 +300,7 @@ struct ARImageByAdd {
281 style: DialogButtonStyle.HIGHLIGHT,300 style: DialogButtonStyle.HIGHLIGHT,
282 value: $r('app.string.back'),301 value: $r('app.string.back'),
283 action: () => {302 action: () => {
284- Logger.info('Callback when the second button is clicked.');303+ logger.info('Callback when the second button is clicked.');
285 this.pageInfos.pop();304 this.pageInfos.pop();
286 return;305 return;
287 }306 }
@@ -291,23 +310,46 @@ struct ARImageByAdd {
291}310}
292 311 
293// Save the file locally312// Save the file locally
294-async function SaveBufferToLocal(dataBase: arEngine.ARAugmentedImageDatabase, context: Context): Promise<void> {313+async function saveBufferToLocal(dataBase: arEngine.ARAugmentedImageDatabase, context: Context): Promise<void> {
295 let filesDir: string = context.filesDir;314 let filesDir: string = context.filesDir;
296- let file: fileIo.File = fileIo.openSync(315+ let file: fileIo.File;
297- filesDir + '/test.bin', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC);316+ try {
298- let buf: ArrayBuffer = await dataBase.serialize();317+ file = fileIo.openSync(filesDir + '/test.bin',
299- let writeLen: number = fileIo.writeSync(file.fd, buf);318+ fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC);
300- Logger.info(`The length of buffer is: ${writeLen}.`);319+ } catch (error) {
301- fileIo.closeSync(file);320+ const err: BusinessError = error as BusinessError;
321+ logger.error(`Failed to open database. Code is ${err.code}, message is ${err.message}.`);
322+ return;
323+ }
324+ let buf: ArrayBuffer;
325+ try {
326+ buf = await dataBase.serialize();
327+ try {
328+ let writeLen: number = fileIo.writeSync(file.fd, buf);
329+ logger.info(`The length of buffer is: ${writeLen}.`);
330+ } catch (error) {
331+ const err: BusinessError = error as BusinessError;
332+ logger.error(`Failed to write database. Code is ${err.code}, message is ${err.message}.`);
333+ }
334+ } catch (error) {
335+ const err: BusinessError = error as BusinessError;
336+ logger.error(`Failed to serialize database. Code is ${err.code}, message is ${err.message}.`);
337+ }
338+ 
339+ try {
340+ fileIo.closeSync(file);
341+ } catch (error) {
342+ const err: BusinessError = error as BusinessError;
343+ logger.error(`Failed to close database file. Code is ${err.code}, message is ${err.message}.`);
344+ }
302}345}
303 346 
304class ARViewCallbackImpl extends arViewController.ARViewCallback {347class ARViewCallbackImpl extends arViewController.ARViewCallback {
305- geoArr: Geometry[] = [];348+ private geoArr: Geometry[] = [];
306- scene: Scene | null = null;349+ private scene: Scene | null = null;
307- shader: Shader | null = null;350+ private shader: Shader | null = null;
308- material: ShaderMaterial | null = null;351+ private material: ShaderMaterial | null = null;
309- image: Image | null = null;352+ private currentGeometry: Geometry | undefined = undefined;
310- currentGeometry: Geometry | undefined = undefined;
311 353 
312 clearAllNode(): void {354 clearAllNode(): void {
313 let childrenNum: number | undefined = this.scene?.root?.children.count();355 let childrenNum: number | undefined = this.scene?.root?.children.count();
@@ -338,16 +380,29 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
338 380 
339 try {381 try {
340 let imageNumber: number = dataBase.getImageCount();382 let imageNumber: number = dataBase.getImageCount();
341- Logger.info(`The number of images in the database is ${imageNumber}.`);383+ logger.info(`The number of images in the database is ${imageNumber}.`);
342 } catch (error) {384 } catch (error) {
343 const err: BusinessError = error as BusinessError;385 const err: BusinessError = error as BusinessError;
344- Logger.error(`Failed to got image count. Code is ${err.code}, message is ${err.message}`);386+ logger.error(`Failed to get image count. Code is ${err.code}, message is ${err.message}`);
345 }387 }
346 388 
347- let imageCapacity: number = dataBase.getCapacity();389+ try {
348- Logger.info(`The dataBase image capacity is: ${imageCapacity}.`);390+ let imageCapacity: number = dataBase.getCapacity();
391+ logger.info(`The dataBase image capacity is: ${imageCapacity}.`);
392+ } catch (error) {
393+ const err: BusinessError = error as BusinessError;
394+ logger.error(`Failed to get capacity. Code is ${err.code}, message is ${err.message}`);
395+ }
349 396 
350- let trackables: arEngine.ARTrackable[] = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE);397+ 
398+ let trackables: arEngine.ARTrackable[];
399+ try {
400+ trackables = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE);
401+ } catch (error) {
402+ const err: BusinessError = error as BusinessError;
403+ logger.error(`Failed to get capacity. Code is ${err.code}, message is ${err.message}`);
404+ return;
405+ }
351 406 
352 // The target image color is controlled by the file plane.shader407 // The target image color is controlled by the file plane.shader
353 let rf: SceneResourceFactory = ctx.scene.getResourceFactory();408 let rf: SceneResourceFactory = ctx.scene.getResourceFactory();
@@ -356,21 +411,35 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
356 this.material.colorShader = this.shader;411 this.material.colorShader = this.shader;
357 (this.material as CustomerMaterial).blend = { enabled: true };412 (this.material as CustomerMaterial).blend = { enabled: true };
358 413 
359- Logger.info(`The image trackable size: ${trackables.length}.`);414+ logger.info(`The image trackable size: ${trackables.length}.`);
360 let validImage: number = 0;415 let validImage: number = 0;
361 for (let i = 0; i < trackables.length; ++i) {416 for (let i = 0; i < trackables.length; ++i) {
362 if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) {417 if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) {
363- let arimage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage;418+ let arImage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage;
364- if (arEngine.ARTrackingState.TRACKING !== arimage.state) {419+ if (arEngine.ARTrackingState.TRACKING !== arImage.state) {
365 continue;420 continue;
366 }421 }
367 validImage += 1;422 validImage += 1;
368- let centerPose: arEngine.ARPose = arimage.getPose();423+ let centerPose: arEngine.ARPose;
369- let modelMat: Matrix4 = centerPose.getMatrix();424+ try {
425+ centerPose = arImage.getPose();
426+ } catch (error) {
427+ const err: BusinessError = error as BusinessError;
428+ logger.error(`Failed to get pose. Code is ${err.code}, message is ${err.message}`);
429+ return;
430+ }
431+ let modelMat: Matrix4;
432+ try {
433+ modelMat = centerPose.getMatrix();
434+ } catch (error) {
435+ const err: BusinessError = error as BusinessError;
436+ logger.error(`Failed to get matirx. Code is ${err.code}, message is ${err.message}`);
437+ return;
438+ }
370 let pos: Vec3 = centerPose.translation;439 let pos: Vec3 = centerPose.translation;
371- let drawPos: number[] = calculatePoint(arimage.extendX, arimage.extendZ, pos);440+ let drawPos: number[] = calculatePoint(arImage.extendX, arImage.extendZ, pos);
372 441 
373- Logger.info(`The image width: ${arimage.extendX}, height: ${arimage.extendZ}.`);442+ logger.info(`The image width: ${arImage.extendX}, height: ${arImage.extendZ}.`);
374 443 
375 if (ctx?.scene) {444 if (ctx?.scene) {
376 let mesh: CustomGeometry = new CustomGeometry();445 let mesh: CustomGeometry = new CustomGeometry();
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -32,7 +32,7 @@ import {
32import { Matrix4 } from '@kit.ArkUI';32import { Matrix4 } from '@kit.ArkUI';
33import { BusinessError } from '@kit.BasicServicesKit';33import { BusinessError } from '@kit.BasicServicesKit';
34import { fileIo, ReadOptions } from '@kit.CoreFileKit';34import { fileIo, ReadOptions } from '@kit.CoreFileKit';
35-import { Logger } from '../utils/Logger';35+import { logger } from '../utils/Logger';
36import { calculatePoint, createImageIndex, getImageVertices } from '../utils/Utils';36import { calculatePoint, createImageIndex, getImageVertices } from '../utils/Utils';
37 37 
38@Builder38@Builder
@@ -76,16 +76,22 @@ struct ARImageByDatabase {
76 dataBase = arDataBase;76 dataBase = arDataBase;
77 77 
78 try {78 try {
79- let databaseBuffer: ArrayBuffer = ReadBuffer(this.context);79+ let databaseBuffer: ArrayBuffer = readBuffer(this.context);
80 dataBase.deserialize(databaseBuffer).then(() => {80 dataBase.deserialize(databaseBuffer).then(() => {
81 this.initARView();81 this.initARView();
82 })82 })
83+ .catch((err: BusinessError) => {
84+ logger.error(`Failed to deserialize database. Code is ${err.code}, message is ${err.message}.`);
85+ })
83 } catch (error) {86 } catch (error) {
84 const err: BusinessError = error as BusinessError;87 const err: BusinessError = error as BusinessError;
85- Logger.error(`Failed to init context. Code is ${err.code}, message is ${err.message}.`);88+ logger.error(`Failed to init context. Code is ${err.code}, message is ${err.message}.`);
86 this.ShowDialog(this.context.resourceManager.getStringByNameSync('invalid_image_added'));89 this.ShowDialog(this.context.resourceManager.getStringByNameSync('invalid_image_added'));
87 }90 }
88 })91 })
92+ .catch((err: BusinessError) => {
93+ logger.warn(`Failed to create database. Code is ${err.code}, message is ${err.message}`);
94+ })
89 })95 })
90 .onWillDisappear(async () => {96 .onWillDisappear(async () => {
91 this.stopARView();97 this.stopARView();
@@ -104,67 +110,6 @@ struct ARImageByDatabase {
104 .hideToolBar(true)110 .hideToolBar(true)
105 }111 }
106 112 
107- private initARView(): void {
108- Scene.load().then(async (scene: Scene) => {
109- let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();
110- viewContext.scene = scene;
111- viewContext.callback = new ARViewCallbackImpl();
112- viewContext.config = {
113- type: arEngine.ARType.IMAGE,
114- planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
115- powerMode: arEngine.ARPowerMode.NORMAL,
116- semanticMode: arEngine.ARSemanticMode.NONE,
117- poseMode: arEngine.ARPoseMode.GRAVITY,
118- depthMode: arEngine.ARDepthMode.AUTOMATIC,
119- meshMode: arEngine.ARMeshMode.DISABLED,
120- focusMode: arEngine.ARFocusMode.AUTO
121- }
122- viewContext.init().then(() => {
123- this.arContext = viewContext;
124- Logger.info('Succeeded in initting ARView.');
125- }).catch((err: BusinessError) => {
126- Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
127- })
128- })
129- }
130- 
131- private async stopARView(): Promise<void> {
132- if (!this.arContext) {
133- return;
134- }
135- try {
136- await dataBase.release();
137- await this.arContext?.destroy();
138- } catch (error) {
139- const err: BusinessError = error as BusinessError;
140- Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
141- }
142- }
143- 
144- private resumeARView(): void {
145- if (!this.arContext) {
146- return;
147- }
148- try {
149- this.arContext.resume();
150- } catch (error) {
151- const err: BusinessError = error as BusinessError;
152- Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
153- }
154- }
155- 
156- private pauseARView(): void {
157- if (!this.arContext) {
158- return;
159- }
160- try {
161- this.arContext.pause();
162- } catch (error) {
163- const err: BusinessError = error as BusinessError;
164- Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);
165- }
166- }
167- 
168 ShowDialog(msg: string): void {113 ShowDialog(msg: string): void {
169 this.getUIContext().showAlertDialog({114 this.getUIContext().showAlertDialog({
170 title: $r('app.string.warning'),115 title: $r('app.string.warning'),
@@ -186,41 +131,126 @@ struct ARImageByDatabase {
186 style: DialogButtonStyle.HIGHLIGHT,131 style: DialogButtonStyle.HIGHLIGHT,
187 value: $r('app.string.back'),132 value: $r('app.string.back'),
188 action: () => {133 action: () => {
189- Logger.info('Callback when the second button is clicked.');134+ logger.info('Callback when the second button is clicked.');
190 this.pageInfos.pop();135 this.pageInfos.pop();
191 return;136 return;
192 }137 }
193 }]138 }]
194 })139 })
195 }140 }
141+ 
142+ private initARView(): void {
143+ Scene.load().then(async (scene: Scene) => {
144+ let viewContext: arViewController.ARViewContext = new arViewController.ARViewContext();
145+ viewContext.scene = scene;
146+ viewContext.callback = new ARViewCallbackImpl();
147+ viewContext.config = {
148+ type: arEngine.ARType.IMAGE,
149+ planeFindingMode: arEngine.ARPlaneFindingMode.HORIZONTAL_AND_VERTICAL,
150+ powerMode: arEngine.ARPowerMode.NORMAL,
151+ semanticMode: arEngine.ARSemanticMode.NONE,
152+ poseMode: arEngine.ARPoseMode.GRAVITY,
153+ depthMode: arEngine.ARDepthMode.AUTOMATIC,
154+ meshMode: arEngine.ARMeshMode.DISABLED,
155+ focusMode: arEngine.ARFocusMode.AUTO
156+ }
157+ viewContext.init().then(() => {
158+ this.arContext = viewContext;
159+ logger.info('Succeeded in initting ARView.');
160+ }).catch((err: BusinessError) => {
161+ logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
162+ })
163+ })
164+ }
165+ 
166+ private async stopARView(): Promise<void> {
167+ if (!this.arContext) {
168+ return;
169+ }
170+ try {
171+ await dataBase.release();
172+ await this.arContext?.destroy();
173+ } catch (error) {
174+ const err: BusinessError = error as BusinessError;
175+ logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
176+ }
177+ }
178+ 
179+ private resumeARView(): void {
180+ if (!this.arContext) {
181+ return;
182+ }
183+ try {
184+ this.arContext.resume();
185+ } catch (error) {
186+ const err: BusinessError = error as BusinessError;
187+ logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
188+ }
189+ }
190+ 
191+ private pauseARView(): void {
192+ if (!this.arContext) {
193+ return;
194+ }
195+ try {
196+ this.arContext.pause();
197+ } catch (error) {
198+ const err: BusinessError = error as BusinessError;
199+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);
200+ }
201+ }
196}202}
197 203 
198// Read local files into buffer204// Read local files into buffer
199-function ReadBuffer(context: Context): ArrayBuffer {205+function readBuffer(context: Context): ArrayBuffer {
200 let filesDir: string = context.filesDir;206 let filesDir: string = context.filesDir;
201- let srcFile: fileIo.File = fileIo.openSync(207+ let srcFile: fileIo.File;
202- filesDir + '/test.bin', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);208+ try {
203- const fileStat: fileIo.Stat = fileIo.statSync(srcFile.fd);209+ srcFile = fileIo.openSync(filesDir + '/test.bin', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
204- // Read the contents of the source file and write it to the destination file210+ } catch (error) {
205- let readSize: number = 0;211+ const err: BusinessError = error as BusinessError;
206- let buf: ArrayBuffer = new ArrayBuffer(fileStat.size);212+ logger.error(`Failed to open file. Code is ${err.code}, message is ${err.message}.`);
207- let readOptions: ReadOptions = {213+ return new ArrayBuffer(0);
208- offset: readSize,214+ }
209- length: fileStat.size215+ let fileStat: fileIo.Stat;
216+ let buf: ArrayBuffer;
217+ try {
218+ fileStat = fileIo.statSync(srcFile.fd);
219+ // Read the contents of the source file and write it to the destination file
220+ let readSize: number = 0;
221+ buf = new ArrayBuffer(fileStat.size);
222+ let readOptions: ReadOptions = {
223+ offset: readSize,
224+ length: fileStat.size
225+ }
226+ try {
227+ fileIo.readSync(srcFile.fd, buf, readOptions);
228+ } catch (error) {
229+ const err: BusinessError = error as BusinessError;
230+ logger.error(`Failed to read buffer. Code is ${err.code}, message is ${err.message}.`);
231+ }
232+ } catch (error) {
233+ const err: BusinessError = error as BusinessError;
234+ logger.error(`Failed to get file stat. Code is ${err.code}, message is ${err.message}.`);
235+ return new ArrayBuffer(0);
236+ }
237+ 
238+ 
239+ try {
240+ fileIo.closeSync(srcFile);
241+ } catch (error) {
242+ const err: BusinessError = error as BusinessError;
243+ logger.error(`Failed to close database file. Code is ${err.code}, message is ${err.message}.`);
210 }244 }
211- let readLen: number = fileIo.readSync(srcFile.fd, buf, readOptions);
212- Logger.info(`The length of buffer is: ${readLen}.`);
213- fileIo.closeSync(srcFile);
214 return buf;245 return buf;
215}246}
216 247 
217class ARViewCallbackImpl extends arViewController.ARViewCallback {248class ARViewCallbackImpl extends arViewController.ARViewCallback {
218- geoArr: Geometry[] = [];249+ private geoArr: Geometry[] = [];
219- scene: Scene | null = null;250+ private scene: Scene | null = null;
220- shader: Shader | null = null;251+ private shader: Shader | null = null;
221- material: ShaderMaterial | null = null;252+ private material: ShaderMaterial | null = null;
222- image: Image | null = null;253+ private currentGeometry: Geometry | undefined = undefined;
223- currentGeometry: Geometry | undefined = undefined;
224 254 
225 clearAllNode(): void {255 clearAllNode(): void {
226 let childrenNum: number | undefined = this.scene?.root?.children.count();256 let childrenNum: number | undefined = this.scene?.root?.children.count();
@@ -251,21 +281,27 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
251 281 
252 try {282 try {
253 let imageNumber: number = dataBase.getImageCount();283 let imageNumber: number = dataBase.getImageCount();
254- Logger.info(`The number of images in the database is ${imageNumber}.`);284+ logger.info(`The number of images in the database is ${imageNumber}.`);
255 } catch (error) {285 } catch (error) {
256 const err: BusinessError = error as BusinessError;286 const err: BusinessError = error as BusinessError;
257- Logger.error(`Failed to got image count. Code is ${err.code}, message is ${err.message}.`);287+ logger.error(`Failed to got image count. Code is ${err.code}, message is ${err.message}.`);
258 }288 }
259 289 
260 try {290 try {
261 let imageCapacity: number = dataBase.getCapacity();291 let imageCapacity: number = dataBase.getCapacity();
262- Logger.info(`The dataBase image capacity = ${imageCapacity}.`);292+ logger.info(`The dataBase image capacity = ${imageCapacity}.`);
263 } catch (error) {293 } catch (error) {
264 const err: BusinessError = error as BusinessError;294 const err: BusinessError = error as BusinessError;
265- Logger.error(`Failed to getCapacity. Code is ${err.code}, message is ${err.message}`);295+ logger.error(`Failed to getCapacity. Code is ${err.code}, message is ${err.message}`);
266 }296 }
267 297 
268- let trackables: arEngine.ARTrackable[] = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE);298+ let trackables: arEngine.ARTrackable[] = [];
299+ try {
300+ trackables = session.getAllTrackables(arEngine.ARTrackableType.AUGMENTED_IMAGE);
301+ } catch (error) {
302+ const err: BusinessError = error as BusinessError;
303+ logger.error(`Failed to get all trackables. Code is ${err.code}, message is ${err.message}`);
304+ }
269 305 
270 // The target image color is controlled by the file plane.shader306 // The target image color is controlled by the file plane.shader
271 let rf: SceneResourceFactory = ctx.scene.getResourceFactory();307 let rf: SceneResourceFactory = ctx.scene.getResourceFactory();
@@ -274,21 +310,35 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
274 this.material.colorShader = this.shader;310 this.material.colorShader = this.shader;
275 (this.material as CustomerMaterial).blend = { enabled: true };311 (this.material as CustomerMaterial).blend = { enabled: true };
276 312 
277- Logger.info(`The image trackable size: ${trackables.length}.`);313+ logger.info(`The image trackable size: ${trackables.length}.`);
278 let validImage: number = 0;314 let validImage: number = 0;
279 for (let i = 0; i < trackables.length; ++i) {315 for (let i = 0; i < trackables.length; ++i) {
280 if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) {316 if (trackables[i].type === arEngine.ARTrackableType.AUGMENTED_IMAGE) {
281- let arimage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage;317+ let arImage: arEngine.ARAugmentedImage = trackables[i] as arEngine.ARAugmentedImage;
282- if (arEngine.ARTrackingState.TRACKING !== arimage.state) {318+ if (arEngine.ARTrackingState.TRACKING !== arImage.state) {
283 continue;319 continue;
284 }320 }
285 validImage++;321 validImage++;
286- let centerPose: arEngine.ARPose = arimage.getPose();322+ let centerPose: arEngine.ARPose;
287- let modelMat: Matrix4 = centerPose.getMatrix();323+ try {
324+ centerPose = arImage.getPose();
325+ } catch (error) {
326+ const err: BusinessError = error as BusinessError;
327+ logger.error(`Failed to get pose. Code is ${err.code}, message is ${err.message}`);
328+ return;
329+ }
330+ let modelMat: Matrix4;
331+ try {
332+ modelMat = centerPose.getMatrix();
333+ } catch (error) {
334+ const err: BusinessError = error as BusinessError;
335+ logger.error(`Failed to get matirx. Code is ${err.code}, message is ${err.message}`);
336+ return;
337+ }
288 let pos: Vec3 = centerPose.translation;338 let pos: Vec3 = centerPose.translation;
289- let drawPos: number[] = calculatePoint(arimage.extendX, arimage.extendZ, pos);339+ let drawPos: number[] = calculatePoint(arImage.extendX, arImage.extendZ, pos);
290 340 
291- Logger.info(`The image width: ${arimage.extendX}, height: ${arimage.extendZ}.`);341+ logger.info(`The image width: ${arImage.extendX}, height: ${arImage.extendZ}.`);
292 342 
293 if (ctx?.scene) {343 if (ctx?.scene) {
294 let mesh: CustomGeometry = new CustomGeometry();344 let mesh: CustomGeometry = new CustomGeometry();
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -29,10 +29,9 @@ import {
29 ShaderMaterial,29 ShaderMaterial,
30 Vec330 Vec3
31} from '@kit.ArkGraphics3D';31} from '@kit.ArkGraphics3D';
32-import { window } from '@kit.ArkUI';
33import { BusinessError } from '@kit.BasicServicesKit';32import { BusinessError } from '@kit.BasicServicesKit';
34-import { Logger } from '../utils/Logger';33+import { logger } from '../utils/Logger';
35-import { arrayBufferFloat32ToNumber, arrayBufferInt32ToNumber, arrayToVec3 } from '../utils/Utils';34+import { arrayBufferFloat32ToNumber, arrayBufferInt32ToNumber, arrayToVec3, getStatusBarHeight } from '../utils/Utils';
36 35 
37@Builder36@Builder
38export function ARMeshBuilder(): void {37export function ARMeshBuilder(): void {
@@ -40,7 +39,7 @@ export function ARMeshBuilder(): void {
40}39}
41 40 
42let frame: arEngine.ARFrame;41let frame: arEngine.ARFrame;
43-let statusBarHeight: number = 0;42+ 
44let isDisplayCube: boolean = false;43let isDisplayCube: boolean = false;
45let isStopFrameUpdate: boolean = false;44let isStopFrameUpdate: boolean = false;
46let hitPoseList: Vec3[] = [];45let hitPoseList: Vec3[] = [];
@@ -63,6 +62,11 @@ interface CustomerMaterial extends Material {
63struct ARMesh {62struct ARMesh {
64 @State arContext?: arViewController.ARViewContext = undefined;63 @State arContext?: arViewController.ARViewContext = undefined;
65 @State context: Context = this.getUIContext().getHostContext() as Context;64 @State context: Context = this.getUIContext().getHostContext() as Context;
65+ @State statusBarHeight: number = 0;
66+ 
67+ async aboutToAppear(): Promise<void> {
68+ this.statusBarHeight = await getStatusBarHeight(this.context);
69+ }
66 70 
67 build(): void {71 build(): void {
68 NavDestination() {72 NavDestination() {
@@ -83,10 +87,9 @@ struct ARMesh {
83 }87 }
84 .onAppear(() => {88 .onAppear(() => {
85 this.initARView();89 this.initARView();
86- this.getAvoidArea();
87 })90 })
88- .onWillDisappear(() => {91+ .onWillDisappear(async () => {
89- this.stopARView();92+ await this.stopARView();
90 })93 })
91 .onShown(() => {94 .onShown(() => {
92 this.resumeARView();95 this.resumeARView();
@@ -101,12 +104,12 @@ struct ARMesh {
101 104 
102 private objectCollisionDetection(event: ClickEvent): void {105 private objectCollisionDetection(event: ClickEvent): void {
103 let x: number = this.getUIContext().vp2px(event.windowX);106 let x: number = this.getUIContext().vp2px(event.windowX);
104- let y: number = this.getUIContext().vp2px(event.windowY) - statusBarHeight;107+ let y: number = this.getUIContext().vp2px(event.windowY) - this.statusBarHeight;
105- Logger.info(`Get onclick position, x: ${x} y: ${y}.`);108+ logger.info(`Get onclick position, x: ${x} y: ${y}.`);
106 109 
107 try {110 try {
108 let result: arEngine.ARHitResult[] = frame.hitTest(x, y);111 let result: arEngine.ARHitResult[] = frame.hitTest(x, y);
109- Logger.info(`The hitresult size is: ${result.length}.`);112+ logger.info(`The hitresult size is: ${result.length}.`);
110 if (!result) {113 if (!result) {
111 return;114 return;
112 }115 }
@@ -114,7 +117,7 @@ struct ARMesh {
114 for (let i = 0; i < result.length; i++) {117 for (let i = 0; i < result.length; i++) {
115 let hitResult: arEngine.ARHitResult = result[i];118 let hitResult: arEngine.ARHitResult = result[i];
116 let distance: number = hitResult.distance;119 let distance: number = hitResult.distance;
117- Logger.info(`The hitresult distance is: ${distance}.`);120+ logger.info(`The hitresult distance is: ${distance}.`);
118 121 
119 if (distance <= 0) {122 if (distance <= 0) {
120 continue;123 continue;
@@ -131,10 +134,10 @@ struct ARMesh {
131 hitAnchorList.splice(0, 1);134 hitAnchorList.splice(0, 1);
132 }135 }
133 }136 }
134- Logger.info('Succeeded in getting hit result.');137+ logger.info('Succeeded in getting hit result.');
135 } catch (error) {138 } catch (error) {
136 const err: BusinessError = error as BusinessError;139 const err: BusinessError = error as BusinessError;
137- Logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}`);140+ logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}`);
138 }141 }
139 }142 }
140 143 
@@ -155,34 +158,22 @@ struct ARMesh {
155 }158 }
156 viewContext.init().then(() => {159 viewContext.init().then(() => {
157 this.arContext = viewContext;160 this.arContext = viewContext;
158- Logger.info('Succeeded in initting ARView.');161+ logger.info('Succeeded in initting ARView.');
159 }).catch((err: BusinessError) => {162 }).catch((err: BusinessError) => {
160- Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);163+ logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
161 })164 })
162 })165 })
163 }166 }
164 167 
165- private getAvoidArea(): void {168+ private async stopARView(): Promise<void> {
166- let avoidAreaType: window.AvoidAreaType = window.AvoidAreaType.TYPE_SYSTEM;
167- window.getLastWindow(this.context).then((data) => {
168- // Get the top status bar height
169- let avoidArea1: window.AvoidArea = data.getWindowAvoidArea(avoidAreaType);
170- statusBarHeight = avoidArea1.topRect.height;
171- Logger.info(`The statusBarHeight is ${statusBarHeight}.`);
172- }).catch((err: BusinessError) => {
173- Logger.error(`Failed to obtain the window. Code is ${err.code}, message is ${err.message}.`);
174- })
175- }
176- 
177- private stopARView(): void {
178 if (!this.arContext) {169 if (!this.arContext) {
179 return;170 return;
180 }171 }
181 try {172 try {
182- this.arContext.destroy();173+ await this.arContext.destroy();
183 } catch (error) {174 } catch (error) {
184 const err: BusinessError = error as BusinessError;175 const err: BusinessError = error as BusinessError;
185- Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);176+ logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
186 }177 }
187 }178 }
188 179 
@@ -195,7 +186,7 @@ struct ARMesh {
195 isStopFrameUpdate = false;186 isStopFrameUpdate = false;
196 } catch (error) {187 } catch (error) {
197 const err: BusinessError = error as BusinessError;188 const err: BusinessError = error as BusinessError;
198- Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);189+ logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
199 }190 }
200 }191 }
201 192 
@@ -208,16 +199,16 @@ struct ARMesh {
208 } catch (error) {199 } catch (error) {
209 isStopFrameUpdate = true;200 isStopFrameUpdate = true;
210 const err: BusinessError = error as BusinessError;201 const err: BusinessError = error as BusinessError;
211- Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);202+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);
212 }203 }
213 }204 }
214}205}
215 206 
216class ARViewCallbackImpl extends arViewController.ARViewCallback {207class ARViewCallbackImpl extends arViewController.ARViewCallback {
208+ private scene: Scene | null = null;
209+ private shader: Shader | null = null;
210+ private material: ShaderMaterial | null = null;
217 private nodeKey: number = 0;211 private nodeKey: number = 0;
218- scene: Scene | null = null;
219- shader: Shader | null = null;
220- material: ShaderMaterial | null = null;
221 212 
222 onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {213 onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
223 }214 }
@@ -335,7 +326,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
335 cubeGeometry.position.z = -pos.x;326 cubeGeometry.position.z = -pos.x;
336 } catch (error) {327 } catch (error) {
337 const err: BusinessError = error as BusinessError;328 const err: BusinessError = error as BusinessError;
338- Logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}.`);329+ logger.error(`Failed to acquire depth information. Code is ${err.code}, message is ${err.message}.`);
339 }330 }
340 }331 }
341}332}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -29,17 +29,22 @@ import {
29 ShaderMaterial,29 ShaderMaterial,
30 Vec330 Vec3
31} from '@kit.ArkGraphics3D';31} from '@kit.ArkGraphics3D';
32-import { Matrix4, window } from '@kit.ArkUI';32+import { Matrix4 } from '@kit.ArkUI';
33import { BusinessError } from '@kit.BasicServicesKit';33import { BusinessError } from '@kit.BasicServicesKit';
34-import { Logger } from '../utils/Logger';34+import { logger } from '../utils/Logger';
35-import { arrayBufferFloat32ToNumber, generateMeshIndex, generateMeshInput, getVertices } from '../utils/Utils';35+import {
36+ arrayBufferFloat32ToNumber,
37+ generateMeshIndex,
38+ generateMeshInput,
39+ getStatusBarHeight,
40+ getVertices
41+} from '../utils/Utils';
36 42 
37@Builder43@Builder
38export function ARWorldBuilder(): void {44export function ARWorldBuilder(): void {
39 ARWorld();45 ARWorld();
40}46}
41 47 
42-let statusBarHeight: number = 0;
43let isDisplayCube: boolean = false;48let isDisplayCube: boolean = false;
44let isStopFrameUpdate: boolean = false;49let isStopFrameUpdate: boolean = false;
45let modelMat: Matrix4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];50let modelMat: Matrix4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
@@ -63,6 +68,11 @@ interface CustomerMaterial extends Material {
63struct ARWorld {68struct ARWorld {
64 @State arContext?: arViewController.ARViewContext = undefined;69 @State arContext?: arViewController.ARViewContext = undefined;
65 @State context: Context = this.getUIContext().getHostContext() as Context;70 @State context: Context = this.getUIContext().getHostContext() as Context;
71+ @State statusBarHeight: number = 0;
72+ 
73+ async aboutToAppear(): Promise<void> {
74+ this.statusBarHeight = await getStatusBarHeight(this.context);
75+ }
66 76 
67 build(): void {77 build(): void {
68 NavDestination() {78 NavDestination() {
@@ -81,12 +91,11 @@ struct ARWorld {
81 }91 }
82 }92 }
83 }93 }
84- .onAppear(() => {94+ .onAppear(async () => {
85 this.initARView();95 this.initARView();
86- this.getAvoidArea();
87 })96 })
88- .onWillDisappear(() => {97+ .onWillDisappear(async () => {
89- this.stopARView();98+ await this.stopARView();
90 })99 })
91 .onShown(() => {100 .onShown(() => {
92 this.resumeARView();101 this.resumeARView();
@@ -101,12 +110,12 @@ struct ARWorld {
101 110 
102 private objectCollisionDetection(event: ClickEvent): void {111 private objectCollisionDetection(event: ClickEvent): void {
103 let x: number = this.getUIContext().vp2px(event.windowX);112 let x: number = this.getUIContext().vp2px(event.windowX);
104- let y: number = this.getUIContext().vp2px(event.windowY) - statusBarHeight;113+ let y: number = this.getUIContext().vp2px(event.windowY) - this.statusBarHeight;
105- Logger.info(`Get onclick position, x: ${x} y: ${y}.`);114+ logger.info(`Get onclick position, x: ${x} y: ${y}.`);
106 115 
107 try {116 try {
108 let result: arEngine.ARHitResult[] = frame.hitTest(x, y);117 let result: arEngine.ARHitResult[] = frame.hitTest(x, y);
109- Logger.info(`The hitresult size is: ${result.length}.`);118+ logger.info(`The hitresult size is: ${result.length}.`);
110 if (!result) {119 if (!result) {
111 return;120 return;
112 }121 }
@@ -123,7 +132,7 @@ struct ARWorld {
123 let hitPose: arEngine.ARPose = hitResult.getHitPose();132 let hitPose: arEngine.ARPose = hitResult.getHitPose();
124 let inPolygon: boolean = hitPlane.isPoseInPolygon(hitPose);133 let inPolygon: boolean = hitPlane.isPoseInPolygon(hitPose);
125 let distance: number = hitResult.distance;134 let distance: number = hitResult.distance;
126- Logger.info(`The hitresult inPolygon is: ${inPolygon}, distance is: ${distance}.`);135+ logger.info(`The hitresult inPolygon is: ${inPolygon}, distance is: ${distance}.`);
127 136 
128 if (!inPolygon || distance <= 0) {137 if (!inPolygon || distance <= 0) {
129 continue;138 continue;
@@ -140,10 +149,10 @@ struct ARWorld {
140 hitAnchorList.splice(0, 1);149 hitAnchorList.splice(0, 1);
141 }150 }
142 }151 }
143- Logger.info('Succeeded in getting hit result.');152+ logger.info('Succeeded in getting hit result.');
144 } catch (error) {153 } catch (error) {
145 const err: BusinessError = error as BusinessError;154 const err: BusinessError = error as BusinessError;
146- Logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}.`);155+ logger.error(`Failed to get hitResults. Code is ${err.code}, message is ${err.message}.`);
147 }156 }
148 }157 }
149 158 
@@ -164,34 +173,22 @@ struct ARWorld {
164 }173 }
165 viewContext.init().then(() => {174 viewContext.init().then(() => {
166 this.arContext = viewContext;175 this.arContext = viewContext;
167- Logger.info('Succeeded in initting ARView.');176+ logger.info('Succeeded in initting ARView.');
168 }).catch((err: BusinessError) => {177 }).catch((err: BusinessError) => {
169- Logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);178+ logger.error(`Failed to init ARView. Code is ${err.code}, message is ${err.message}.`);
170 })179 })
171 })180 })
172 }181 }
173 182 
174- private getAvoidArea(): void {183+ private async stopARView(): Promise<void> {
175- let avoidAreaType: window.AvoidAreaType = window.AvoidAreaType.TYPE_SYSTEM;
176- window.getLastWindow(this.context).then((data) => {
177- // Get the top status bar height
178- let avoidArea: window.AvoidArea = data.getWindowAvoidArea(avoidAreaType);
179- statusBarHeight = avoidArea.topRect.height;
180- Logger.info(`The statusBarHeight is ${statusBarHeight}.`);
181- }).catch((err: BusinessError) => {
182- Logger.error(`Failed to obtain the window. Code is ${err.code}, message is ${err.message}.`);
183- })
184- }
185- 
186- private stopARView(): void {
187 if (!this.arContext) {184 if (!this.arContext) {
188 return;185 return;
189 }186 }
190 try {187 try {
191- this.arContext.destroy();188+ await this.arContext.destroy();
192 } catch (error) {189 } catch (error) {
193 const err: BusinessError = error as BusinessError;190 const err: BusinessError = error as BusinessError;
194- Logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);191+ logger.error(`Failed to stop context. Code is ${err.code}, message is ${err.message}`);
195 }192 }
196 }193 }
197 194 
@@ -204,7 +201,7 @@ struct ARWorld {
204 isStopFrameUpdate = false;201 isStopFrameUpdate = false;
205 } catch (error) {202 } catch (error) {
206 const err: BusinessError = error as BusinessError;203 const err: BusinessError = error as BusinessError;
207- Logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);204+ logger.error(`Failed to resume context. Code is ${err.code}, message is ${err.message}.`);
208 }205 }
209 }206 }
210 207 
@@ -217,16 +214,16 @@ struct ARWorld {
217 isStopFrameUpdate = true;214 isStopFrameUpdate = true;
218 } catch (error) {215 } catch (error) {
219 const err: BusinessError = error as BusinessError;216 const err: BusinessError = error as BusinessError;
220- Logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);217+ logger.error(`Failed to pause context. Code is ${err.code}, message is ${err.message}.`);
221 }218 }
222 }219 }
223}220}
224 221 
225class ARViewCallbackImpl extends arViewController.ARViewCallback {222class ARViewCallbackImpl extends arViewController.ARViewCallback {
223+ private scene: Scene | null = null;
224+ private shader: Shader | null = null;
225+ private material: ShaderMaterial | null = null;
226 private nodeKey: number = 0;226 private nodeKey: number = 0;
227- scene: Scene | null = null;
228- shader: Shader | null = null;
229- material: ShaderMaterial | null = null;
230 227 
231 onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {228 onAnchorAdd(ctx: arViewController.ARViewContext, node: Node, anchor: arEngine.ARAnchor): void {
232 }229 }
@@ -267,14 +264,14 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
267 for (let i = 0; i < trackables.length; ++i) {264 for (let i = 0; i < trackables.length; ++i) {
268 if (trackables[i].state !== arEngine.ARTrackingState.TRACKING) {265 if (trackables[i].state !== arEngine.ARTrackingState.TRACKING) {
269 // Do not render current plane when not in tracking state266 // Do not render current plane when not in tracking state
270- Logger.info('No plane in tracking state!');267+ logger.info('No plane in tracking state!');
271 continue;268 continue;
272 }269 }
273 270 
274 let plane: arEngine.ARPlane = trackables[i] as arEngine.ARPlane;271 let plane: arEngine.ARPlane = trackables[i] as arEngine.ARPlane;
275 if (plane.getSubsumedBy() !== undefined) {272 if (plane.getSubsumedBy() !== undefined) {
276 // Do not render current plane when parent plane exists273 // Do not render current plane when parent plane exists
277- Logger.info('Parent plane exists!');274+ logger.info('Parent plane exists!');
278 continue;275 continue;
279 }276 }
280 277 
@@ -363,7 +360,7 @@ class ARViewCallbackImpl extends arViewController.ARViewCallback {
363 cubeGeometry.position.z = -pos.x;360 cubeGeometry.position.z = -pos.x;
364 } catch (error) {361 } catch (error) {
365 const err: BusinessError = error as BusinessError;362 const err: BusinessError = error as BusinessError;
366- Logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`);363+ logger.error(`Failed to update data. Code is ${err.code}, message is ${err.message}.`);
367 }364 }
368 }365 }
369}366}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -15,15 +15,15 @@
15 15 
16import { abilityAccessCtrl, PermissionRequestResult } from '@kit.AbilityKit';16import { abilityAccessCtrl, PermissionRequestResult } from '@kit.AbilityKit';
17import { BusinessError } from '@kit.BasicServicesKit';17import { BusinessError } from '@kit.BasicServicesKit';
18-import { Logger } from '../utils/Logger';18+import { logger } from '../utils/Logger';
19import { requestPermissionOnSetting } from '../utils/Utils';19import { requestPermissionOnSetting } from '../utils/Utils';
20 20 
21@Entry21@Entry
22@Component22@Component
23struct Selector {23struct Selector {
24 pageInfos: NavPathStack = new NavPathStack();24 pageInfos: NavPathStack = new NavPathStack();
25- private hasPermission: boolean = false;
26 @State context: Context = this.getUIContext().getHostContext() as Context;25 @State context: Context = this.getUIContext().getHostContext() as Context;
26+ private hasPermission: boolean = false;
27 27 
28 build(): void {28 build(): void {
29 Navigation(this.pageInfos) {29 Navigation(this.pageInfos) {
@@ -32,7 +32,8 @@ struct Selector {
32 this.sampleButton('ARMesh');32 this.sampleButton('ARMesh');
33 this.sampleButton('ARDepth');33 this.sampleButton('ARDepth');
34 this.sampleButton('ARImage');34 this.sampleButton('ARImage');
35- this.sampleButton('ARSemanticDense');35+ this.sampleButton('ARFace');
36+ this.sampleButton('ARBody');
36 }37 }
37 .justifyContent(FlexAlign.SpaceEvenly)38 .justifyContent(FlexAlign.SpaceEvenly)
38 .width('100%')39 .width('100%')
@@ -62,7 +63,7 @@ struct Selector {
62 }63 }
63 this.pageInfos.clear();64 this.pageInfos.clear();
64 this.pageInfos.pushDestinationByName(sampleName, null).catch((err: BusinessError) => {65 this.pageInfos.pushDestinationByName(sampleName, null).catch((err: BusinessError) => {
65- Logger.error(`Failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`);66+ logger.error(`Failed to pushDestinationByName. Code is ${err.code}, message is ${err.message}.`);
66 });67 });
67 })68 })
68 }69 }
@@ -74,14 +75,14 @@ struct Selector {
74 let grantStatus: number[] = data.authResults;75 let grantStatus: number[] = data.authResults;
75 if (grantStatus[0] === 0) {76 if (grantStatus[0] === 0) {
76 this.hasPermission = true;77 this.hasPermission = true;
77- Logger.info('Succeeded in getting requestPermission.');78+ logger.info('Succeeded in getting requestPermission.');
78 } else {79 } else {
79 this.hasPermission = false;80 this.hasPermission = false;
80- Logger.error('Failed to get requestPermission, user rejected.');81+ logger.error('Failed to get requestPermission, user rejected.');
81 }82 }
82 })83 })
83 .catch((err: BusinessError) => {84 .catch((err: BusinessError) => {
84- Logger.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}.`);85+ logger.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}.`);
85 })86 })
86 }87 }
87}88}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -42,4 +42,4 @@ class LoggerModel {
42 }42 }
43}43}
44 44 
45-export let Logger = new LoggerModel('[ArDemo][Js]');45+export let logger = new LoggerModel('[ArDemo][Js]');
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -13,11 +13,11 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16-import { Matrix4 } from '@kit.ArkUI';
17-import { Vec3 } from '@kit.ArkGraphics3D';
18import { abilityAccessCtrl } from '@kit.AbilityKit';16import { abilityAccessCtrl } from '@kit.AbilityKit';
17+import { Vec3 } from '@kit.ArkGraphics3D';
18+import { Matrix4, window } from '@kit.ArkUI';
19import { BusinessError } from '@kit.BasicServicesKit';19import { BusinessError } from '@kit.BasicServicesKit';
20-import { Logger } from './Logger';20+import { logger } from './Logger';
21 21 
22export function arrayBufferFloat32ToNumber(buffer: ArrayBuffer): number[] {22export function arrayBufferFloat32ToNumber(buffer: ArrayBuffer): number[] {
23 let view: Float32Array = new Float32Array(buffer);23 let view: Float32Array = new Float32Array(buffer);
@@ -38,12 +38,9 @@ export function getVertices(mat: Matrix4, point: number[]): Vec3[] {
38 let result: Vec3[] = [];38 let result: Vec3[] = [];
39 for (let i = 0; i < point.length; i += 2) {39 for (let i = 0; i < point.length; i += 2) {
40 let single: Vec3 = {40 let single: Vec3 = {
41- x: (mat[2] * point[i] + mat[6] * 041+ x: (mat[2] * point[i] + mat[6] * 0 + mat[10] * point[i + 1] + mat[14] * 1.0),
42- + mat[10] * point[i + 1] + mat[14] * 1.0),42+ y: mat[1] * point[i] + mat[5] * 0 + mat[9] * point[i + 1] + mat[13] * 1.0,
43- y: mat[1] * point[i] + mat[5] * 043+ z: -(mat[0] * point[i] + mat[4] * 0 + mat[8] * point[i + 1] + mat[12] * 1.0)
44- + mat[9] * point[i + 1] + mat[13] * 1.0,
45- z: -(mat[0] * point[i] + mat[4] * 0
46- + mat[8] * point[i + 1] + mat[12] * 1.0),
47 }44 }
48 result.push(single);45 result.push(single);
49 }46 }
@@ -54,12 +51,9 @@ export function getImageVertices(mat: Matrix4, point: number[]): Vec3[] {
54 let result: Vec3[] = [];51 let result: Vec3[] = [];
55 for (let i = 2; i < point.length; i += 2) {52 for (let i = 2; i < point.length; i += 2) {
56 let single: Vec3 = {53 let single: Vec3 = {
57- x: (mat[2] * point[i] + mat[6] * 054+ x: (mat[2] * point[i] + mat[6] * 0 + mat[10] * point[i + 1] + mat[14] * 1.0),
58- + mat[10] * point[i + 1] + mat[14] * 1.0),55+ y: mat[1] * point[i] + mat[5] * 0 + mat[9] * point[i + 1] + mat[13] * 1.0,
59- y: mat[1] * point[i] + mat[5] * 056+ z: -(mat[0] * point[i] + mat[4] * 0 + mat[8] * point[i + 1] + mat[12] * 1.0)
60- + mat[9] * point[i + 1] + mat[13] * 1.0,
61- z: -(mat[0] * point[i] + mat[4] * 0
62- + mat[8] * point[i + 1] + mat[12] * 1.0),
63 }57 }
64 result.push(single);58 result.push(single);
65 }59 }
@@ -182,13 +176,89 @@ export async function requestPermissionOnSetting(context: Context): Promise<bool
182 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();176 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
183 await atManager.requestPermissionOnSetting(context, ['ohos.permission.CAMERA'])177 await atManager.requestPermissionOnSetting(context, ['ohos.permission.CAMERA'])
184 .then((data: abilityAccessCtrl.GrantStatus[]) => {178 .then((data: abilityAccessCtrl.GrantStatus[]) => {
185- Logger.info('data:' + JSON.stringify(data));179+ logger.info('data:' + JSON.stringify(data));
186 if (data[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {180 if (data[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
187 requestResult = true;181 requestResult = true;
188 }182 }
189 })183 })
190 .catch((err: BusinessError) => {184 .catch((err: BusinessError) => {
191- Logger.error('data:' + JSON.stringify(err));185+ logger.error('data:' + JSON.stringify(err));
192 })186 })
193 return requestResult;187 return requestResult;
194}188}
189+ 
190+export function getFaceFrontVertices(mat: Matrix4, point: number[]): Vec3[] {
191+ let result: Vec3[] = [];
192+ for (let i = 0; i < point.length; i += 3) {
193+ let single: Vec3 = {
194+ x: (mat[2] * point[i] + mat[6] * point[i + 1] + mat[10] * point[i + 2] + mat[14] * 1.0),
195+ y: -(mat[1] * point[i] + mat[5] * point[i + 1] + mat[9] * point[i + 2] + mat[13] * 1.0),
196+ z: -(mat[0] * point[i] + mat[4] * point[i + 1] + mat[8] * point[i + 2] + mat[12] * 1.0),
197+ }
198+ result.push(single);
199+ }
200+ return result;
201+}
202+ 
203+export function getFaceRearVertices(mat: Matrix4, point: number[]): Vec3[] {
204+ let result: Vec3[] = [];
205+ for (let i = 0; i < point.length; i += 3) {
206+ let single: Vec3 = {
207+ x: (mat[2] * point[i] + mat[6] * point[i + 1] + mat[10] * point[i + 2] + mat[14] * 1.0),
208+ y: (mat[1] * point[i] + mat[5] * point[i + 1] + mat[9] * point[i + 2] + mat[13] * 1.0),
209+ z: -(mat[0] * point[i] + mat[4] * point[i + 1] + mat[8] * point[i + 2] + mat[12] * 1.0),
210+ }
211+ result.push(single);
212+ }
213+ return result;
214+}
215+ 
216+export function generateFaceMeshIndex(input: Map<number, number[]>, offset: number): number[] {
217+ let result: number[] = []
218+ let start = 0;
219+ 
220+ for (let i = 0; i < input.size; i++) {
221+ let tmp = input.get(i);
222+ if (tmp) {
223+ for (let j = 0; j < tmp.length; j++) {
224+ result.push(tmp[j] + start)
225+ }
226+ }
227+ start += offset;
228+ }
229+ return result
230+}
231+ 
232+export async function getStatusBarHeight(context: Context): Promise<number> {
233+ let lastWindow: window.Window | undefined;
234+ let statusBarHeight: number = 0;
235+ 
236+ try {
237+ lastWindow = await window.getLastWindow(context);
238+ } catch (error) {
239+ const err: BusinessError = error as BusinessError;
240+ logger.error(`ailed to obtain the window. Code is ${err.code}, message is ${err.message}`);
241+ return statusBarHeight;
242+ }
243+ 
244+ try {
245+ let avoidArea: window.AvoidArea = lastWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
246+ statusBarHeight = avoidArea.topRect.height;
247+ logger.info(`the statusBarHeight is ${statusBarHeight}`);
248+ } catch (error) {
249+ const err: BusinessError = error as BusinessError;
250+ logger.error(`Failed to get avoid area. Code is ${err.code}, message is ${err.message}`);
251+ }
252+ 
253+ return statusBarHeight;
254+}
255+ 
256+export function getResourceString(context: Context, resourceName: string): string {
257+ try {
258+ return context.resourceManager.getStringByNameSync(resourceName);
259+ } catch (error) {
260+ const err: BusinessError = error as BusinessError;
261+ logger.warn(`Failed to get string ${resourceName}. Code is ${err.code}, message is ${err.message}`);
262+ return '';
263+ }
264+}
@@ -1,5 +1,5 @@
1/*1/*
2- * Copyright (c) 2024-2025 Huawei Device Co., Ltd.2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at5 * You may obtain a copy of the License at
@@ -21,7 +21,8 @@
21 "mainElement": "EntryAbility",21 "mainElement": "EntryAbility",
22 "deviceTypes": [22 "deviceTypes": [
23 "phone",23 "phone",
24- "tablet"24+ "tablet",
25+ "tv"
25 ],26 ],
26 "deliveryWithInstall": true,27 "deliveryWithInstall": true,
27 "installationFree": false,28 "installationFree": false,
@@ -47,13 +47,20 @@
47 "data": {47 "data": {
48 "description": "This is ARWorld."48 "description": "This is ARWorld."
49 }49 }
50+ },{
51+ "name": "ARFace",
52+ "pageSourceFile": "src/main/ets/pages/ARFace.ets",
53+ "buildFunction": "ARFaceBuilder",
54+ "data": {
55+ "description": "This is ARFace"
56+ }
50 },57 },
51 {58 {
52- "name": "ARSemanticDense",59+ "name": "ARBody",
53- "pageSourceFile": "src/main/ets/pages/ARSemanticDense.ets",60+ "pageSourceFile": "src/main/ets/pages/ARBody.ets",
54- "buildFunction": "ARSemanticDenseBuilder",61+ "buildFunction": "ARBodyBuilder",
55 "data": {62 "data": {
56- "description": "This is ARSemanticDense."63+ "description": "This is ARBody."
57 }64 }
58 }65 }
59 ]66 ]
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+{
16+ "compatibility_info" : { "version" : "22.00", "type" : "shader" },
17+ "vert": "3dshaders://shader/core3d_dm_fw.vert.spv",
18+ "frag": "appshaders://custom_shader/custom_material_sample.frag.spv",
19+ "vertexInputDeclaration": "3dvertexinputdeclarations://core3d_dm_fw.shadervid",
20+ "state": {
21+ "rasterizationState": {
22+ "enableDepthClamp": false,
23+ "enableDepthBias": false,
24+ "enableRasterizerDiscard": false,
25+ "polygonMode": "fill",
26+ "cullModeFlags": "back",
27+ "frontFace": "counter_clockwise"
28+ },
29+ "depthStencilState": {
30+ "enableDepthTest": true,
31+ "enableDepthWrite": true,
32+ "enableDepthBoundsTest": false,
33+ "enableStencilTest": false,
34+ "depthCompareOp": "less_or_equal"
35+ },
36+ "colorBlendState": {
37+ "colorAttachments": [
38+ {
39+ "enableBlend": true,
40+ "colorWriteMask": "r_bit|g_bit|b_bit|a_bit",
41+ "srcColorBlendFactor": "one",
42+ "dstColorBlendFactor": "one_minus_src_alpha",
43+ "colorBlendOp": "add",
44+ "srcAlphaBlendFactor": "one",
45+ "dstAlphaBlendFactor": "one_minus_src_alpha",
46+ "alphaBlendOp": "add"
47+ }
48+ ]
49+ }
50+ },
51+ "materialMetadata": [
52+ {
53+ "name": "MaterialComponent",
54+ "customProperties": [
55+ {
56+ "data": [
57+ {
58+ "name": "vec_1",
59+ "displayName": "Color",
60+ "type": "vec4",
61+ "value" : [0.0,0.6,0.6,0.4]
62+ }
63+ ]
64+ }
65+ ]
66+ }
67+ ]
68+}
@@ -1,3 +1,17 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
1{15{
2 "compatibility_info" : { "version" : "22.00", "type" : "shader" },16 "compatibility_info" : { "version" : "22.00", "type" : "shader" },
3 "vert": "appshaders://custom_shader/custom_material_sample.vert.spv",17 "vert": "appshaders://custom_shader/custom_material_sample.vert.spv",
@@ -1,3 +1,17 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
1{15{
2 "compatibility_info" : { "version" : "22.00", "type" : "shader" },16 "compatibility_info" : { "version" : "22.00", "type" : "shader" },
3 "vert": "3dshaders://shader/core3d_dm_fw.vert.spv",17 "vert": "3dshaders://shader/core3d_dm_fw.vert.spv",