news 2026/10/6 2:52:37

鸿蒙国庆节特辑:用华为云码道打造「五星红旗 · 标准制图室」,GB 12982 几何规范一屏看懂

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙国庆节特辑:用华为云码道打造「五星红旗 · 标准制图室」,GB 12982 几何规范一屏看懂

鸿蒙国庆节特辑:用华为云码道打造「五星红旗 · 标准制图室」,GB 12982 几何规范一屏看懂

与其做一个节日工具,不如把节日里最庄严的那个符号本身做成作品。五星红旗的画法不是「凭感觉」,而是白纸黑字写在 GB 12982《国旗》国家标准里的——30×20 网格、大星圆心 (5,5) 格、四颗小星各有一尖正对大星中心。我让码道 Agent 把这些几何规范落成一个鸿蒙「制图室」应用:滑杆调旗高,Canvas 实时重绘,点任意一颗星看它的坐标和旋转角。

仓库地址:https://atomgit.com/2501_92427005/flag-drafting-lab


0. 缘起:为什么是它

国庆节刷到升旗视频,弹幕里总有人争论「四颗小星到底是怎么摆的」。其实答案在 GB 12982 里写得清清楚楚:

  • 旗面 3:2,划分为 30 行 × 20 列的网格;
  • 大五角星外接圆直径 = 旗高 3/10,圆心在左上方 5×5 格正方形中心;
  • 四颗小五角星外接圆直径 = 旗高 1/10,圆心分别在 (10,2)、(12,4)、(12,7)、(10,9) 格;
  • 每颗小星都有一个角尖正对大星中心点——这意味着四颗小星各有各的旋转角,不是复制粘贴。

这套几何天然适合做成「可交互的画法几何教具」,也天然适合考验码道 Agent:坐标全是分数与三角函数,错一点视觉上立刻穿帮。同时它跟前几篇(日晷、卫星、流星雨、中秋小游戏)完全不撞题。

照例先立六条硬约束,写进给 Agent 的提示词:

  • 纯本地、零权限:requestPermissions保持[],不联网、不存储、不定位;
  • ArkTS + ArkUI 声明式,API 12+,DevEco Studio 5.0 直接打开可构建;
  • domain 层纯净:几何计算不 import 任何@ohos.*,能在 Node 里跑单元测试;
  • 数值必经 sanitize:滑杆输入先过sanitizeHeightPx,空串/NaN/越界全部有兜底;
  • 中文文案全走资源:resources/base|dark/element,深浅色双主题;
  • 测试必须真绿:npm test用 Node 的--experimental-strip-types直接跑。

1. 给 Agent 的提示词:先建仓,再开发

沿用系列验证过的「两步走」:

第一步,建仓。让码道在 AtomGit 创建flag-drafting-lab仓库,只写 README 标题 + MIT License +.gitignore(含.hvigor/、*.hap这些鸿蒙特有忽略项),不写任何业务代码:

Agent 思考后选择 AtomGit 平台,调ag-cli技能完成建仓:

建仓完成,仓库地址落定:https://atomgit.com/2501_92427005/flag-drafting-lab

第二步,开发。单独开会话,把完整需求喂给 Agent——核心是 GB 12982 的几何参数表,我直接把星位坐标、直径比例、旋转角约束全写死在提示词里,不给模型「自由发挥」的空间:

红线清单也一并给出:禁 any/unknown、禁旧作换皮(日晷/节气/流星雨/假期打卡全点名)、公式永不进组件事件、提交语义化:

2. 需求拆解:一根滑杆、一面旗、五颗可点的星

应用功能一句话讲完:

拖滑杆改旗高,屏幕上的五星红旗按国标重画,点星星看它为什么在那个位置。

具体三层:

层职责
domain由旗高推出全部几何量:网格尺寸、五颗星圆心、内外半径、旋转角
uiCanvas 绘制旗面/网格/星形,滑杆与开关,星位命中检测
resources深浅色色板、全部中文文案、免责声明

交互细节:

  • 旗高滑杆 100–300 px,步进 1 px,实时重绘;
  • 「网格辅助线」开关:叠 30×20 半透明网格,教学场景专用;
  • 「显示坐标」开关:画布上方打出 H/W/格值;
  • 点击画布 → 命中检测 → 下方卡片显示该星圆心、外接圆半径;小星额外显示旋转角(度)。

3. 架构:domain 层是全部精华

工程结构(Agent 产出,我未改分层):

entry/src/main/ets/ ├── domain/ │ └── flagGeometry.ets 纯几何:常量表 + 纯函数,零 @ohos.* import ├── entryability/ │ └── EntryAbility.ets └── pages/ └── Index.ets @Entry:Canvas + 滑杆 + 命中卡片

3.1 常量表:把国标翻译成代码

// domain/flagGeometry.etsexportconstBIG_STAR=Object.freeze({centerXUnits:5,centerYUnits:5,diameterRatio:3/10,});exportconstSMALL_STARS:ReadonlyArray<{readonlycenterXUnits:number;readonlycenterYUnits:number;}>=[{centerXUnits:10,centerYUnits:2},{centerXUnits:12,centerYUnits:4},{centerXUnits:12,centerYUnits:7},{centerXUnits:10,centerYUnits:9},];exportconstSMALL_STAR_DIAMETER_RATIO=1/10;

Object.freeze+readonly双保险,常量不可能在运行期被改写。

3.2 旋转角:小星「看向」大星

四颗小星的旋转角不是查表查出来的,是算出来的:

exportfunctionpointToAngleRad(fromX:number,fromY:number,toX:number,toY:number):number{returnMath.atan2(toX-fromX,-(toY-fromY));}

注意第二个参数取负:屏幕坐标 y 向下,而「角尖朝上」对应-y方向。这个符号坑过不少人,Agent 一次写对。

3.3 五角星顶点:外内交替的 10 个点

constINNER_OUTER_RATIO=Math.sin(Math.PI/10)/Math.sin((7*Math.PI)/10);exportfunctionstarPolygonPoints(star:StarInfo):Array<{x:number;y:number}>{constpoints:Array<{x:number;y:number}>=[];conststep=Math.PI/5;for(leti=0;i<10;i++){constr=i%2===0?star.outerRadius:star.innerRadius;consta=star.rotationRad+i*step;points.push({x:star.centerX+r*Math.sin(a),y:star.centerY-r*Math.cos(a),});}returnpoints;}

内外半径比sin(18°)/sin(126°)是标准五角星的几何性质,写成一个常量而不是魔法数字。

3.4 输入消毒:滑杆值也要过安检

exportfunctionsanitizeHeightPx(input:unknown,fallback:number):number{constn=toFiniteNumber(input);if(n===null)returnfallback;returnMath.max(100,Math.min(300,n));}

空串、null、NaN、字符串数字、越界值各有归宿。calcFlagLayout内部先调它,所以即使 UI 层忘了 sanitize,domain 层也不会产出 NaN 布局。

4. UI:Canvas 绘制与命中检测

4.1 绘制流程

privatedrawFlag():void{constlayout=calcFlagLayout(this.heightPx);// 1. 居中偏移constoffsetX=(ctx.width-layout.widthPx)/2;constoffsetY=(ctx.height-layout.heightPx)/2;// 2. 旗面ctx.fillStyle='#DE2910';ctx.fillRect(offsetX,offsetY,layout.widthPx,layout.heightPx);// 3. 可选网格if(this.showGrid){/* 30×20 半透明线 */}// 4. 五颗星this.drawStar(ctx,layout.bigStar,offsetX,offsetY);for(constsoflayout.smallStars)this.drawStar(ctx,s,offsetX,offsetY);}

旗面红#DE2910、星黄#FFDE00是国标色值,写进resources/base/element/color.json而非硬编码。

4.2 命中检测

exportfunctionnearestStar(layout:FlagLayout,x:number,y:number):{kind:'big'|'small';index:number;distancePx:number}|null{// 遍历五颗星,距离 ≤ 外接圆半径 + 4px 容差即命中,返回最近者}

容差 4 px 是给手指触控留的——纯几何命中区域对触摸来说太小。

5. 单元测试:Node 直跑 ArkTS 子集

沿用上一篇验证过的路子:tests/transpile.mjs把domain/*.ets改 import 路径、转存为.ts,Node 22+ 的--experimental-strip-types直接执行,零第三方依赖。

"scripts":{"pretest":"node tests/transpile.mjs","test":"node --experimental-strip-types --test tests/*.test.mjs"}

7 条用例覆盖:

  • 手算尺子:heightPx=300→gridSize=10、大星中心(50,50)、外接圆半径 45、小星①中心(100,20);
  • 比例一致性:任意旗高,宽 = 高 × 1.5 恒成立;
  • 旋转角正确性:四颗小星rotationRad与pointToAngleRad重算值误差 < 1e-9;
  • 星形顶点数 = 10,首顶点距圆心 = 外接圆半径;
  • 命中检测:大星中心命中 big/0,空白点返回 null;
  • sanitize:空串/null/NaN/-5/999/150 各断言;
  • 随机扫描:10000 组随机旗高全链路有限不抛错。
$npmtestℹ tests7ℹ pass7ℹ fail0

中间踩过一个坑值得记录:最初转译脚本输出.mjs,Node 的--experimental-strip-types只处理.ts后缀,.mjs里的类型注解直接炸SyntaxError: Missing initializer in const declaration。改成输出.ts后立刻全绿。这个教训已写进提示词方法论的备忘录。

6. DevEco 实测:四个状态

工程在 DevEco Studio 5.0 里 Sync 一次过(这次 Agent 把AppScope/、hvigor/骨架补齐了,上一篇踩的坑没复发),Pura X 模拟器直接 Run:

默认首屏,旗高 200 px:

网格 + 坐标全开,旗高 277 px——教学场景下能逐格核对星位:

关网格、开坐标,旗高 148 px 的小旗形态:

纯旗面展示,旗高 238 px——四颗小星的朝向差异在这个尺寸下肉眼可辨:

边界实测:

  • 滑杆拉到最小 100 px / 最大 300 px,星形不变形、不溢出;
  • 快速来回拖滑杆,Canvas 重绘无闪烁(@State驱动,声明式渲染);
  • 点旗面空白处,星位卡片收起;点小星边缘 4 px 内仍能命中。

小结

这篇是系列里技术密度最高的一篇,但代码量反而最小——因为国标把所有「决策」都做完了,剩下的只是忠实翻译。三点心得:

  1. 规范即需求。GB 12982 把坐标、比例、朝向全定死了,提示词可以直接写成「常量表 + 公式」,Agent 没有自由发挥的空间,输出反而最稳。这比写「帮我画一面好看的国旗」靠谱一万倍。

  2. 可测试性来自几何的确定性。(5,5)格、3/10直径、atan2旋转角,每个数都有手算答案,单元测试的「尺子」随手就能写。反过来,如果需求里有「大概」「差不多」「看起来」,测试就废了。

  3. 踩坑要变成下一条提示词。上篇踩的「中文路径」「AppScope 缺失」这次提前写进提示词就没复发;这次踩的「.mjs不吃--experimental-strip-types」已经进了我的提示词备忘录。每一篇的坑都是下一篇的护城河。

下一个想做的方向:既然旗的几何做完了,不如把「时间」加进来——天安门广场每日升旗时刻 = 当日北京日出时刻,背后是太阳赤纬 + 均时差 + 大气折射的一整套天文算法,正好接上系列前六篇的天文科普人设。如果下期挑战赛还在周期内,继续更。

  • 应用名称:Flag Drafting Lab(五星红旗 · 标准制图室)
  • 仓库地址:https://atomgit.com/2501_92427005/flag-drafting-lab
  • 技术栈:HarmonyOS ArkTS + ArkUI 声明式 · API 12+ · Canvas 2D · Nodenode:test(--experimental-strip-types)
  • 核心特性:零权限、纯本地、GB 12982 几何内核、30×20 教学网格、星位命中检测、深浅色双主题、7 条单元测试全绿
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 2:51:52

一点小碎片——每个线程独有的东西——上下文+栈

修正一下&#xff1a;这里是TCB。但是由于一个进程内所有线程共享虚拟地址&#xff0c;这些所谓“独有”的资源是可以被同进程另一个线程访问到的。不过只要指针不瞎指&#xff0c;只有对应线程知道自己的栈空间。这个“独有”就像只纸老虎&#xff0c;原则上你是害怕他&#x…

作者头像 李华
网站建设 2026/10/6 2:50:51

samba共享匿名访问,用户访问,账户映射,访问地址限制方法

Samba 是linux中的一个共享服务&#xff0c;可以让linux&#xff0c;windows 互相共享&#xff0c;今天来搭建一下。这里用的镜像里的包。rpm -ivh samba-3.6.9-164.el6.x86_64 # 下载sambavim /etc/samba/smb.conf #配置文件先搞一个匿名的访问。下面是要修改的参数。…

作者头像 李华
网站建设 2026/10/6 2:50:20

page_alloc zone_names

zone_names[]——每个 zone 类型对应的人类可读名称字符串数组。它是 zone 的"名字表"&#xff0c;用于打印、调试、/proc 输出等。mcrjnjfbr一、作用与类型char * const zone_names[MAX_NR_ZONES] { ... };类型&#xff1a;char * const [MAX_NR_ZONES]char *&…

作者头像 李华
网站建设 2026/10/6 2:49:17

基于YOLO姿态估计和XGBoost的零售店铺盗窃行为实时检测系统

摘要&#xff1a;随着零售业的快速发展&#xff0c;商品失窃问题日益严重&#xff0c;传统的视频监控系统依赖人工监控&#xff0c;效率低下且成本高昂。针对上述问题&#xff0c;本文提出了一种基于深度学习的智能盗窃行为检测系统&#xff0c;以实现对零售环境中可疑行为的自…

作者头像 李华
网站建设 2026/10/6 2:49:16

OpenCV 在 C# 中的“低代码开发”,核心思路是把常用的视觉算法封装成可视化工作流节点,在图形界面里拖拽连线完成算法编排,然后一键导出 C# 代码调用

OpenCV 在 C# 中的“低代码开发”&#xff0c;核心思路是把常用的视觉算法封装成可视化工作流节点&#xff0c;在图形界面里拖拽连线完成算法编排&#xff0c;然后一键导出 C# 代码调用。它解决的是传统 OpenCvSharp 开发中“每个算子都要手写代码、调参靠反复编译”的效率问题…

作者头像 李华