Skip to content

About

A free web straightedge-and-compass toolkit: play levels, build your own, solve with a solver — 300+ classic construction puzzles included.

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Latest commit

 

History

229 Commits

Folders and files

Repository files navigation

Geommunity Blueprint

中文 | English

一个在浏览器里就能玩的几何作图工具站:可以照着关卡做题、自由在画板上作图、自己出题,还能用内置求解器自动找出作图步骤。零依赖、零安装,打开网页即可使用。

在线地址:https://mt9799.github.io/Geommunity-Blueprint/


四个入口

入口 用途
画板 自由作图,所有工具全开,适合随手画、研究构造。
关卡游玩 照着收录的关卡做题,达到 L / E 步数下限即过关。
自制关卡 制题器:自己出题并标好给定与所求,然后试玩自制的关卡。
求解器 给定几个图形,让程序自动帮你找出满足所求的作图步骤。

界面与玩法

关卡列表与搜索

  • 关卡包页与搜索页都能按标题 / 副标题 / 关键词 / 步数(5L、6E、5 都行)搜索,空格分隔多个条件是「与」;从关卡包进搜索默认只搜该包。
  • 列表每页 20 关,从关卡返回列表会停在原来的位置。

关卡游玩

  • 目标:作出该关的所求(画布上以金色亮起)即通关。目标步数(如 5L 6E)显示在缩略图上(未达成黑、达成金):L 是工具使用数(高级工具计 1L),E 是基础图形(直线与两点圆)数,多解关卡还要作出全部解(V)。要先作出解 L / E 才会点亮;所求一旦离开画布(撤销 / 清空)勾会熄灭并清零,重新作出时再评一次。
  • 通关界面:真正多作出一条解时滑入,显示步数与目标对比(如 L 1 / 5 ✓ E 1 / 6 ✓),5 秒后或撤销回未完成状态时收起。菜单「查看答案」列出该关收录的解法图:点一下开全屏大图。
  • 工具限制:部分关卡限定单尺(移动 / 点 / 直线 / 交点)、单规(移动 / 点 / 圆 / 交点)或网格(移动 / 点 / 直线 / 交点 —— 题目自带一副网格,等于在棋盘上用直尺作图,网格本身不算步数),工具栏里只出现允许的工具。
  • 撤销:画到一半(画布上还有跟随鼠标的半透明预览)时按撤销只取消这次的半成品,不会撤掉上一步。菜单里还有清空画布 / 重开本关(回到初始图形并清进度)、探索视图(按 explore= 显示另一套内容,自己画的会保留)、设计模式(把隐藏图形全部显示并自动标好给定与所求)、打开求解器(要先作出至少一个解才可点)。
  • 手机端:缩略图挪到右上角按钮下面一排,面板与 L / E 计数器随屏幕缩小。

制题器与求解器

  • 标记工具在顶部菜单栏:「标记给定」有 给定 / 带标签给定(弹命名框,留空用对象名)/ 可移动点,「标记所求」有 所求判定 / 所求显示 / 探索显示;导出到 gmt 分别是 initial= / named= / movepoints= / result=(冒号前是判定、冒号后是显示)/ explore=。点画布上的对象即可加上或取消标记,标记面板按分组列出、旁边的叉号清空全部标记。标记之间互斥(画布上标记与元素一览里补标记是同一条规则):给定 / 带标签给定 / 可移动点三者只能有一个;所求判定 / 所求显示 / 探索显示彼此可以共存,但它们与给定那三类互斥 —— 把一个标了给定的图形标成所求判定,它的给定标记就被撤掉,反之亦然。读入口径:有些 gmt / 记录里同一个对象既写在 initial= 又写在 named= 里,这种对象一律按 named(带标签给定)读 —— 标签显示、也不在「给定」栏里重复出现。
  • 可移动点只能是能拖动的自由点(点交点 / 线上点 / 中点这类跟随别人的点时会弹提示);制题器与求解器的点默认不显示标签(可在「配置点样式」里打开),带标签给定的对象仍显示自己的标签。
  • 多解:选中「标记所求 / 所求显示」后,切换栏里出现解选择器 1、+、−,点数字切换当前解、+ 增加一组、− 删掉最后一组。
  • 导入 / 导出 gmt:菜单「导出 gmt」可查看代码、存成文件或提交 Issue;「导入 gmt」可粘贴代码或读取文件。载入之后可以按顺序撤回 —— 载入的图形本身就是一步步作出来的,撤销会从最后一个对象开始退到空画布(网格当作一整块,整块算一步)。
  • 求解器只需要「给定」与「所求判定」两类标记(面板也只有这两栏)。搜索的已知条件只取标为「给定」的对象(关卡里隐藏 / 预绘制的图形不会混进条件,免得搜出「用了题面里没有的点」的假解);一个给定标记都没有时,把画布上除目标以外的对象都当作条件。
  • 求解器面板可设置最大步数、可用工具(尺规 / 单尺 / 单规 / 网格)、时间上限与要几个解,然后点「开始求解」;搜索中按钮变「停止求解」。步数上限就是你要的步数 —— 搜索是「找到就返回」,不保证最短,想要更短的就把上限调小。解是边搜边端上来的:内核一找到就立刻显示一条(状态栏同步「已找到 n 个解(搜索中)」),这些行当场就能点开逐步看;并行搜索收够解数就当场收工,不必等在飞的任务跑完。面板上还有一条进度条,右边跟着百分比(百分比就是条的比例)。进度 = 已搜情况数 / 预估总情况数 。并行线程数的标题里给出推荐值(按设备逻辑核心数估)。搜索到的解不保证正确(内核只保证在你给的这组数上命中目标),请自行核对,或者打开高级选项里的 「解法自检」;同一栏里的 「逐步搜索」会按 1 步、2 步…… 一直到步数上限各搜一遍;同一栏的 「启发式搜索(测试)」:勾上后改用 beam 搜索(只找解、不证明穷尽,步数上限调大时通常快得多);结束时如果没找到,面板写的是「启发式已停止(不代表无解)」,而不是「已搜尽」。求解面板旁边还有一块 「关系雷达」:勾选类别后点「开始探测」,会把画板上多点共线 / 多点共圆 / 平行垂直 / 定点角 / 线上比 / 等距中心 / 角平分线 / 紧圆规这些隐含关系逐条列出来,点一条就在画布上用紫色半透明标出来;图形一动标记跟着走,关系不再成立就标「已失效」。它只判定与显示,不改动画布、不插入任何点。
  • 网格作图:画板 / 制题器 / 求解器的工具栏在菜单右边有「生成网格」(列数、行数 2–20,单位长度默认 50;已经有网格时再点一次就是改大小)。网格是按 gmt 模板「作」出来的(作图过程的辅助对象自动隐藏,格线是黑色细虚线),生成后视图自动适配到网格范围;界面上把它当作一整块:点任意一条格线就选中全部,不能删除 / 换线型 / 被样式刷与隐藏刷改动,元素一览与标记栏里都只占「格线」一行,也不能对它做标记。制题器里生成即算「给定」,导出成 #grid= / #gridstyle= 两行(加上 initial= 里的格线);撤销 / 重做连同网格大小一起还原。
  • 求解器「网格」模式(搜索内核的模式 3):画布上有网格时自动切过去、最大步数切到 4,面板里显示网格尺寸;请求按网格单位换算成内核坐标(格点=整数、点域 [0,m]×[0,n],画布 +y 朝下所以 y 取反),网格线段不必当给定送出(内核自己按 m / n 铺,且网格与格点都不计 E)。关卡也可以用 "tools": "grid" 限定成网格直尺。
  • 高级选项(面板里默认收起的那一栏):对称剪枝 / 目标优先 / 低内存 / 置换表内存 / 去重表容量 / 并行线程数 / 容差 / 解法自检 —— 都是搜索内核本来就有的参数(自检除外),改动会被记住。默认值=内核默认,所以不动它们时行为与以前一模一样。并行=按搜索树的前缀切分给多个 Worker(与参考实现 C++ 版 bs_v8 同一个分工):1 号 worker 先把树切成一串前缀任务,其余的各自独占搜一段,页面上汇总去重、按步数排序;线程数 1 就是原来的单 worker 整棵搜索。并行任务内部不用置换表(与 C++ 一致,任务之间不共享失败状态)。容差留空时自动按图幅的长度量级取(1e-11 × 图幅),输入框的提示里会写出这次实际生效的值。解法自检(默认关)对每个解把所有可动点按图幅的 0.1% 轻挪一下(两个互相垂直的一般方向,点挪回后不留痕迹)、让画布自己重算所求、再用构造计划重算一遍:只在当前这组数上成立的特解会被剔掉(内核搜的是具体数值实例,这类「凑巧命中」的构造不带这个开关看不出来)。
  • 解法画在画布上:半透明品红按步画出(第几步作了哪条线、哪个圆、哪个交点),多个解时右侧列出「解法 1(N 步)…」,点哪一项就换成那一解,「上一步 / 下一步」可逐步看,「清除解法」收掉。解法只是覆盖层,不能选取、不能导出,也不影响你自己的作图;给定里的线段 / 射线只会用范围内那一段(求解器按端点夹取交点,想延长得自己多作一步直线,步数也如实计);拖动给定图形时解法跟着重算(某一步作不出来时会暂时消失,拖回去又长回来)。每个解还有「文字步骤」按钮,弹出一份「怎么作」的中文说明。
  • 试玩:进入的页面与关卡游玩一致 —— 先只显示给定,自己作出来或判定通过后才显示其余内容;返回制题器会还原编辑状态。图形多的关卡也照旧能试玩(历史放不下时自动少带一些,图形与标记一定带过去)。题目里带网格时,试玩页会按网格直尺限制工具(移动 / 点 / 直线 / 交点),与关卡里写 "tools": "grid" 效果一致 —— 试玩没有关卡文件,就按「有没有网格」判。
  • 手机端面板收进底部上拉栏:画布左上角的圆形按钮(「已标记对象」与求解器里的「求解参数」)从底部拉起面板;电脑端仍是浮在画布上的两个面板。
  • 鸣谢:求解器的搜索内核由 Ander 与 zzzzzz 共同开发,本项目使用它的 JavaScript 移植版;启发式搜索移植自 墨染荷韵 的 bs v12;关系雷达移植自 YX-hueimie 的 Geosolver。

工具与样式

  • 工具按「通用 / 点 / 线 / 圆 / 构造」分档;构造档按钮右下角的小角标始终显示该档当前选中的高级工具。
  • 重复的图形不会再作一次(只看图形本身,不看它怎么作出来的):再连一次 AB、在 a 上取点作 a 的平行线、以 A 为圆心 AB 为半径作圆,都不会多出第二个同样的图形;直线 / 射线 / 线段之间互不算重复。隐藏或已失效的图形不算重复,也不会被点击吸附或选中;光标(吸附后)落在一个已有点上时,点工具这一下就作废(不会叠出重合的点,也不占步数)。
  • 样式刷(制题器 / 求解器):先点一个图形当来源,之后点到的图形套用它的颜色 / 粗细 / 标签显示;切换线类型:点直线 / 射线 / 线段就换成下一种。
  • 直接拖动图形:图形的定义点全是自由点时,拖动它就是整体平移(子图形跟着走);定义点里有点不动或还牵着别的图形时,拖动是平移画布。移动工具支持多选:Ctrl/Cmd+A 全选、按住 Ctrl/Cmd 点加选(再点一次取消这一个);选中的点与图形拖动时一起平移,「调整对象样式」与「删除选中对象」也一起作用在这一批上。
  • 电脑端快捷键:Ctrl/Cmd+Z 撤回、Ctrl/Cmd+Shift+Z 或 Ctrl/Cmd+Y 重做、Ctrl/Cmd+S 保存记录(记录面板的加号)、Delete / Backspace 删除选中的图形。
  • 元素一览(菜单):一行 15 格的小格子(序号 / 名称 + 缩略图),右上角 ✕ 关掉面板。全部点一下看详情;隐藏用黑框标出当前看不见的图形、点一下切换显示(游玩 / 试玩里这一档只读);初始 / 可动点 / 所求 / 探索看的是标记,用红框标出,点一下只补标记、不会取消。
  • 预览与吸附:作图时光标下的预览点会先吸附到已有点与相交处(含还没作出点对象的「隐交点」),其次吸附到最近的直线 / 圆 —— 吸到哪里图形就画在哪里。取图形统一为「点优先、其余按距离」:先取吸附范围内的点(点之间按距离;作图取点时还会把光标下的隐藏点、相交的位置一并算进来,隐藏点只有点工具会真的用上),没有点再取其他图形(按距离,一样近才比「线段 → 射线 → 直线 → 圆」的档位、再按作图倒序;同处多个点先给能拖动的自由点)。移动 / 橡皮 / 样式刷 / 隐藏刷 / 切换线类型 / 标记都走同一套口径,不再各有各的特例;网格是背景,永远让位给用户自己画的图形。
  • 点在「隐交点」上就直接显示它:不管当前是哪个工具(点工具 / 交点工具 / 直线、圆这些取点时顺手点的点都一样),点一下就把它变成真正的交点对象落在画布上;点工具与交点工具之外的,还照常继续自己这一步(第一点落在这儿,接着点第二点就行)。撤销的归口看这一下是谁的动作:点工具 / 交点工具显示(或作出)的这个点自占一格(撤回它自己也撤掉)。非游玩模式下及非网格格点时,点工具压在隐藏的点对象上时同样直接把它显示出来,不再在它旁边叠一个新点。
  • 复制圆规先点一个圆,之后圆心跟着光标走、半径取那个圆的半径。「点两条线作角平分线」和「点直线作两条平行切线」由于不兼容 gmt 故暂时取消。提示与确认统一用站内样式。

记录(作图存档)

  • 一条记录存作图(gmt 文本)+ 可撤回的图形名单 + 各图形的样式表 + 基本信息(模式 / 关卡 id / 是否作出所求 / 步数),保存在本机浏览器里;载回来跟保存那一刻一样,也能接着撤销 / 重做。关卡名与目标步数按关卡 id 反查,不另存。载入记录(或导入 gmt)时视图会自动适配:有网格就适配到网格范围,没有网格就按图形本身的外接矩形适配。
  • 菜单「记录」打开记录面板(试玩没有):只列当前上下文的记录(哪一关就只看那一关),每条显示序号 / 名称(默认是保存时间,可重命名)/ 步数(没作出所求灰、作出所求黑、达标的 L / E 金)。左上角加号手动保存(要求已经画出图形且没有一致的构造),关卡游玩里作出所求时自动保存一条(之后接着画不再存,所求消失后再作出会重新存);右上角「多选」可批量导出 / 删除。
  • 导入 / 导出:面板里的导入可收下 gmt 代码或文件(一次多条),导出可看代码或存成文件(多条合并成一份 .gmts,单条仍是 .gmt);导出文本长什么样见「进阶 → 记录文件的附加信息」。首页 footer 的清除缓存按模式(关卡游玩再按关卡包 → 关卡)列出本机全部记录,操作与面板一致。
  • 各个列表都会读本机记录:关卡页缩略图与关卡列表 / 关卡包 / 搜索页里,作出过所求的目标步数转成正文色,其中不超过目标的那一项(L 或 E)标金。
  • 关掉记录面板:游玩模式还回打开面板之前的工具;画板 / 制题器 / 求解器与元素一览一致,回到构造面板时停在「移动」上。

其他

  • 帮助:各工作区「菜单 → 帮助」说明当前界面的用法,菜单与按钮悬停时信息栏也会给出说明。
  • 项目说明:首页右上角的「项目说明」取回本文件并渲染成弹窗;中英双语:首页右上角可切换中文 / English,选择会被记住。

进阶:本地运行、存档格式与制题

以下内容主要面向想自己跑一份站点、增删关卡或直接编辑 gmt 的贡献者 / 制题者。普通玩家无需阅读。

本地预览与部署

在仓库根目录运行 python -m http.server 8000,然后访问 http://localhost:8000。GitHub Pages 发布源请选择仓库根目录。

根目录的 package.json 只收纳两个便捷脚本:npm start(起本地静态服务)与 npm run check(校验脚本语法)。

关卡数据怎么放

路径 内容
data/levels/<包id>/<序号>.<关卡id>.gmt 每关的 GMT 图形定义(序号见下)
data/diagrams/<包id>/<序号>.<关卡id>.png / .jpg 每关的示意图(关卡缩略图)
data/packcovers/<包id>.png / .jpg / .webp 关卡包封面(总览卡片左侧大图)
data/levelpacks.json 关卡包列表(10 个包)
data/levels.json 关卡扁平列表(345 关)
data/answers/<包id>/<序号>.<关卡id>/… 各关收录的解法图(每关一个文件夹,文件名是 5L.png / 6E.png 这类星标)

关卡 id 就是短名:ewp11、xmath2020-1、Conway Circle、inc-circumc-re-trapezoid 这样,不再是 包:目录/文件.gmt 那种路径。文件名、示意图、答案文件夹都跟着用同一个 id。c-s-pzls-other、euc-addit、from-baidu-tieba、straightedge-only-pzls 这四个包的三种文件都带一个与网页里显示顺序一致的三位序号(001.xxx.gmt / 001.xxx.png / answers/<包id>/001.xxx/),目录一眼能对上列表里的编号;其余包(ewp、xmath、mingjing-forum、xeuclidea-puzzle、sprfes、xewpc)不带序号。找一关的东西只看它的 id 就够了。id 由标题得来:去掉尾部 (5,6,2) 这类 L / E / V 括号与前导 025. 序号,_ 换 -,没有空格的整串转小写(EWP11 → ewp11、XMath2020_1 → xmath2020-1),本来就好看的英文题名原样保留(Conway Circle、Malfatti's Problem)。中文标题的关卡取 gmt 首行 #名字(作者自己起的英文简称,如 #double_degrees、#Equal_Chord),首行也是中文的才另起短名(重叠大小地图 → OverlapMaps)。同包内重名时用原始资料里的目录号区分(EWP53 与 EWP289 两关同名 → ewp53 / ewp289;SprFes 的 01–05 两年各一套 → sprfes2024-01 / sprfes2025-01;XEP18 与 XEP18 (2) → xep18 / xep18-2)。

两个 JSON 各管一件事,不再有额外的覆盖表:一个管关卡包,一个管关卡,页面直接读它们。

关卡信息怎么调

直接改上面两个文件即可,写进去的就是页面显示的内容;不写就用页面默认值。

data/levelpacks.json(关卡包,数组,顺序即总览里的顺序)

字段 作用 不写时
id 包 id(关卡的 pack 字段对应它) —
name 关卡包名称(总览卡片、关卡列表标题) —
description 关卡包说明(总览卡片、关卡列表副标题) 卡片「来自原始资料夹的几何作图题。」;列表「N 个关卡,选择一关开始作图。」
cover 总览卡片封面(相对 data/,可写任意图片路径) 依次找 data/packcovers/<包id>.png / .jpg / .webp
icon 总览卡片图(封面找不到时的后备,相对 data/) 该包第一个有示意图的关卡
source 原始资料出处,仅备注用 —

data/levels.json(关卡,扁平数组,同包内顺序即列表顺序)

字段 作用 不写时
id 关卡 id(短名,同时就是文件名) —
pack 所属包 id —
file GMT 文件路径(相对 data/) —
title 关卡标题(列表、关卡页缩略图标题、页面标题) —
subtitle 关卡页缩略图(点开卡片)里的说明文字 不显示;从画板「试玩」进入时默认「GMT 已加载」
targetSteps 步数(列表右侧、关卡页底部),如 5L / 6E 列表显示「步数未标注」
diagram 示意图路径(相对 data/) 列表用 ◇ 占位,卡片无图
note 关卡列表行的悬停提示 无
tools 工具限制:"straightedge" 单尺 / "compass" 单规 / "grid" 网格直尺 不限制,给全部工具
keywords 搜索用关键词数组 搜索时只用标题与 subtitle
solutions 该关收录的解法图:[{"file": "answers/<包id>/…", "star": "5L"}, …](file 相对 data/) 该关没有收录答案图(「查看答案」置灰)

ewp 与 xmath 两个包的 subtitle 已经批量填好(取自原始题库的题目说明),想改哪一关直接改它的 subtitle 即可。

示例(data/levels.json 里的一条):

{
    "id": "ewp11",
    "pack": "ewp",
    "file": "levels/ewp/ewp11.gmt",
    "title": "EWP11",
    "subtitle": "圆内有一点A,作过A点的弦,弦长等于半径。",
    "targetSteps": "5L / 6E",
    "diagram": "diagrams/ewp/ewp11.png",
    "note": "",
    "tools": "",
    "keywords": ["EWP", "弦", "半径", "多解"],
    "solutions": [
        {"file": "answers/ewp/ewp11/5L.png", "star": "5L"},
        {"file": "answers/ewp/ewp11/6E.png", "star": "6E"},
        {"file": "answers/ewp/ewp11/2V.png", "star": "2V"}
    ]
}

字段顺序就是上面两个表的顺序;没有的字段写空串 / 空数组(页面按「不写」处理,等同用默认值),tools 这类开关型字段不限制时直接不写、不要留空串。文件与目录名里不出现下划线,一律用连字符 -。

新增关卡包 / 关卡:先把 .gmt 与示意图放进 data/levels、data/diagrams,再在 levelpacks.json 加一个包、在 levels.json 加一条关卡即可。若是脚本批量生成,记得保留 name / description / icon / title / subtitle / note 这些展示字段,别覆盖掉。

当前标注了工具限制的关卡共 47 关(43 关单尺 + 4 关单规);extra-straightedge-only-pzls 整包 36 关都是单尺。

data/diagrams/ewp/ 里还留着 5 张当年没用上的题图(EWP021--Def.png / EWP049--Def.png / EWP066--Def.png / EWP088--Def.png / EWP091--Def.png,这几关不在 levels.json 里),它们没有关卡引用,下划线也一并换成了连字符,需要的话删掉即可。

关于原始资料

题目与解法图来自「Geommunity Blueprint」造题计划收集的原始资料:每关一个文件夹,里面有题图、当时的最优解图与 gmt 代码(非官方题目的解法单独放在 Solutions 文件夹里,方便玩家先自己思考)。gmt 的用法是打开游戏的 apk、在 res/raw 下用文本方式覆盖对应关卡的文件,安装后即可游玩。

这些资料已经按上面的表格整理进 data/:gmt 在 data/levels/、题图在 data/diagrams/、解法图在 data/answers/,文件名与目录名一律换成关卡短 id;levelpacks.json 的 source 字段仍保留原始资料里的目录名作为出处备注。资料与解法图版权归原作者所有。

solutions 是按原始资料的关卡目录整理出来的:答案图取关卡目录、Solutions 子目录或包级 Solutions/<同名目录> 下的图片,按 5L / 6E / 2V 这样的星标排序,题图(Def)与说明图不算答案。整理进 data/answers/ 时改成「每关一个文件夹」(answers/<包id>/<关卡id>/5L.png),多关共用一个原始答案目录的(如 SprFes 的 Solutions/、XEP 的若干组)已各自复制一份,所以现在每个关卡文件夹里的图都属于这一关;同一个星标共图的情况会保留两条 solutions 记录指向同一个文件。新增关卡后照这个规则补一遍即可。

GMT 与画板的对应关系

关卡与画板的「导入 gmt」用同一套解析规则。语法参考 ggb2gmt 的《ggb与gmt文件结构、语法》。

预绘制指令

gmt 画板里的基底 说明
A=[x,y] 自由点 坐标与 gmt 一致(y 轴朝下)
Line[A,B] / Segment[A,B] / Ray[A,B] 直线 / 线段 / 射线 定义点顺序即线的方向
Circle[A,B] 圆(两点基底) A 为圆心、B 在圆上
Compass[A,B,C] 圆(圆规基底) C 为圆心、AB 为半径
Circle3[A,B,C] 圆(三点基底) 过 A、B、C
EdgePoint[s,x] 无穷远点 线 s 上负方向(x=0)/ 正方向(x=1)的假想点。作图时按精确方向算(Line[A,E] 就是过 A 的平行线、Segment[E,H] 是从 H 出发的半直线、CopyAngle 里 E 当一条边),这个点本身任何时候都不画
PolarPoint[s,c] 极点 线 s 关于圆 c 的极点(与极线互为逆运算)
Intersect[对象1,对象2,x,已知点] 交点 见下方「交点参数」
Linepoint[对象,x] 对象上的点 见下方「参数怎么算」
Midpoint[A,B] 中点
Perp[A,s] / Parallel[A,s] 垂线 / 平行线
PBisect[A,B] 中垂线
ABisect[A,B,C] 角平分线 B 为角的顶点(gmt 只有三点这一种)
CenterPoint[c] 圆心 大小写不敏感(Centerpoint 也能识别)
Tangent[A,c,x] 切线 过点 A 作圆 c 的切线;点在圆上时只有一条;x 从圆心到 A 逆时针数
PolarLine[A,c] 极线 与连心线垂直,到圆心距离 r²/|CP|
CopyAngle[A,B,C,D,E] 复制角射线 以 E 为顶点、D 为边上一点
FixAngle[A,B,x] 定值角射线 以 A 为顶点、AB 为始边逆时针转 x 度

暂不支持 ShiftSeg、ABisect[两线]、Tangent[两圆] 以及 rules(限制条件):遇到这些指令时该对象会被丢掉,依赖它的对象也一并丢掉,其余部分照常解析(现行的关卡文件里没有用到 ShiftSeg 与两圆切线)。解析时行尾注释(赋值 # 注释)、大小写混写的指令名都能识别;另有少数文件的个别对象在初始位置自身退化(三点共线、点在圆内等)而暂时画不出来。

参数怎么算(Linepoint[对象,x])

圆上的点按从 x 轴正方向起的弧度(顺时针为正,与画布一致);线上的点按定义点距离倍数,逐类如下:

线型 x 的含义
直线 / 射线 / 线段 第一定义点为起点,1 个单位 = 两定义点之间的距离
平行线 定义点为起点(x=0 就是定义点)
垂线 参数从定义点往前退一个单位(x=1 才是定义点 A,x=0 在 A 沿垂线方向退一个单位长处)
中垂线 以 AB 中点 M 为圆心、A 顺转 90° 后的位置为起点
角平分线 以顶点为起点,1 个单位随图形变化,见下
切线 1 个单位 = 切圆半径

角平分线的 1 个单位有多长:不是固定 100。过顶点作角平分线的垂线,取「角的两条边上的定义点到这条垂线的距离」与「画布 50 单位」三者中的最小值,它的两倍就是 1 个单位的长度。

边上的点到「过顶点垂线」的距离 1 个单位
都在 50 以上 100
有一边是 40 80
角度很大、两条边都向垂线靠拢 随之变小,于是同一个 x 对应的点到顶点距离会缩短

写 gmt 时按这个约定写 x 即可;在画板上直接点出来的点也按同一套换算,所以「自己画的」与「载入关卡里的」位置一致。

交点参数(Intersect[对象1,对象2,x,已知点])

Intersect 的第三个参数 x 就是交点的身份,画板按同一套约定计算与保存,图形移动时交点不会跳到另一个上:

  • 线与线:只有一个交点,x=0;
  • 线与圆:候选按线的方向(第一定义点 → 第二定义点)升序编号,取哪一个按下面的规则(据原版游戏实测):
    • 同一对基底上的第一个 Intersect 一律取「非定义点」那个候选 —— x 写 0 还是 1 都一样(c1=Circle[A,B]、s1=Line[B,A] 时,E=Intersect[c1,s1,0] 与 F=Intersect[c1,s1,1] 取到的都是圆上那个不是 B 的点);
    • 第二个及以后才按 x 正常取(同例改成 s1=Line[A,B]:0 号是 B、1 号是另一个点);
    • 「定义点」指圆的半径端点(Circle[A,B] 的 B)—— 也就是画这个圆时点出来的、圆上本来就有的那个点;
    • 某个候选位置上是图形的定义点时(这个半径端点 B 被本条线穿过、两圆的公共半径端点 B、直线就是过那个点作出来的…)—— 也就是这一对图形已经有的那个交点 —— 建议把它写成第四个参数(见下):不写的话,编号只能靠「第一个取非定义点」这类规则去推,容易解析错、或落到另一个候选上;L=Linepoint[c1] 这类后来才落在图形上的点不算(它们不参与图形的定义);
  • 圆与圆:候选以对象1圆心 → 对象2圆心的方向起逆时针升序,取法与线与圆相同(第一个取非定义点那个,第二个及以后按 x)。以 a=Circle[A,B]、b=Circle[C,B] 为例(B 同时是两个圆的半径端点,所以本身就是其中一个交点):D=Intersect[a,b,0]、E=Intersect[a,b,1] 在 B 位于连心线一侧时得到 D=非 B、E=B,位于另一侧则得到 D=E=非 B;两行反过来写(先 1 后 0)结果正好对称;只写一行时,0 号与 1 号都取到「非 B」那个点;
  • 选中的候选身份会被记住(不跳变):交点算出来后,把它的身份 —— 「与定义点重合的那个」还是「另一个」—— 记住,之后每次重算都优先按记下的身份取。于是拖动图形、甚至把 B 从连心线一侧拖到另一侧,交点也不会突然跳到另一个点上;
  • 线自己的方向要与上表一致,否则编号会整体颠倒:直线 / 线段 / 射线取第一定义点 → 第二定义点;垂线与中垂线取「参考线方向顺转 90°」;角平分线取角的开口方向;平行线与被平行的线同向;切线以切点为第一定义点;
  • 线段 / 射线只做范围过滤,不重编号:x 仍按所在直线的候选顺序算,不会因为范围内只剩一个交点就把它当成 0 号。一个候选都不在范围内时保留原列表,编号照旧取,最后按「失效但不删除」处理 —— 拖动图形使交点暂时跑到线外时不会跳到另一个交点上;

Intersect 的第四个参数(如 Intersect[s3,c2,1,F] 里的 F)是已知的那个交点:编号时先把它排除,再取 x(可省略或写 -),这个排除关系会随撤销、存读档一起保留。用交点工具在相交处取交点时(点工具吸附到交点、以及作图过程中顺手取交点也一样),如果某个候选位置上有一个图形的定义点(画这个图形时点出来的那个点,见上),那个点会自动记成已知交点 —— 「另一个交点」因此有了固定身份:拖动时不会滑到已知点上。既然身份已经确定,第三个参数就不再重要,导出 gmt 时写成 Intersect[图形1,图形2,-,已知点](如「圆 AB 上有 C、直线 DC 过 C」时,取到的另一个交点就是 Intersect[s1,c1,-,C])。后来用交点工具标出来的交点不算:标一对图形的两个交点时,第二个不会把第一个记成已知交点(否则第四个参数指的就是自己刚作出来的点,而不是题面给的已知条件),两点各按自己的 x 编号、身份照旧被记住。

配好交点参数之后,交点是被「记住」的:线段、射线的范围变化使交点跑到线外(或两个对象不再相交)时,该点失效但不删除(不显示、不参与作图),重新相交后自动恢复。

设定行

gmt 作用
initial 初始显示的对象(黑色,不带标签)
named 带标签的初始条件(也算初始显示)。named=A.M 中 A 是变量名、M 是要显示出来的标签名;只写 A 时标签就取 A
hidden 作图时隐藏的对象(画板里一律不显示)
movepoints 可移动点:选中移动工具时出现(可拖动),切换工具后隐藏
result 所求,可写多条(多解)。a:b,c,d 中冒号前是判定用的对象、冒号后是判定成功后要显示的图形(判直线而不判线段时会用到);没写冒号时就把判定对象本身显示出来。每作出一个图形就判定一次:任意一条 result 的判定对象都作出即算过关,并显示对应的图形(金色);多条都作出则算多解。判定只认玩家自己作出的图形,关卡文件里预绘制的对象不算
explore 探索视图里标金色的内容,与 result 冒号后的显示互不影响
rules、ver、check_level 解析时跳过(rules 的限制条件尚未实现)

多解标记:解 1 沿用 result,解 k 用 result k(result2、resultShown2…);导入 / 导出时每条 result= 就是一个解。导出格式固定为「每个解一行 result=判定:显示」,空解也留一行空行(第几行就是第几个解);initial= / named= / movepoints= / result= / explore= 这些设定行无论有没有标记都会写出来,没标记的留空,便于对照与手动编辑。

几条与文件一致的显示规则

  • 只有 initial、named 的对象一开始就显示;gmt 里预绘制出的「解」会被隐藏(画板「导入 gmt」时相反,全部可见,方便编辑);
  • 标签只给 named 的对象,initial 里的对象只显示图形、不显示标签;
  • 关卡加载后会自动把初始图形居中显示(画布坐标原点在左上角,gmt 坐标以图形为中心);
  • 点类对象压在线圆之上绘制;线类对象之间线段压在射线之上、射线压在直线之上,与作图先后无关;
  • 制题器 / 求解器里对象的「样式色」与「标记色」分开:标成给定 / 所求会换成黑 / 金,取消标记后回到自己的样式色(自由点红、其余点与线圆灰);
  • initial / named 里坐标定义的点与线上点,和可移动点一样能拖(选中移动工具时临时显示成蓝色,退出移动工具还原)。

记录文件的附加信息

记录面板导出的文本就是一份可读的 gmt:对象行与标记行之后空一行,接一段 recordname= / time= / saved= / mode= / pack= / levelid= / reached= / steps=5:6 / undolist=3,5,7 / styles=a#ff0000,a~1,a$;多条合并成一份 .gmts 时,每条前面加一行 # ===== 记录 N ===== 隔开。v1.1.3 及以前的老格式(# undolist= / # styles= / # info= 三行 JSON)仍能读。

  • 网格由文件头的 #grid=m,n,单位 / #gridstyle=… 两行带出,不在 styles 里重复记:它决定网格大小、单位与格线样式,载入时(记录 / 关卡 / 试玩)按这两行把网格还原成一整块。
  • undolist 是可撤回的图形在 gmt 里的对象行序号:游玩模式只列玩家自己画的(关卡自带的图形撤不掉,跟关卡里作图的撤回手感一致),其它模式列全部,所有图形都能逐一撤回。取消撤回后重做会把图形放回来。
  • 样式表只写非默认项:黑 / 宽 1 / 点显示标签 / 线圆不显示 / 实线 / 可见算默认;a#ff0000 颜色、a~1 … a~5 点线径(档位号,1 最小 5 最大)、a$ 显示标签 / a^ 不显示、a& 虚线(线 / 圆)、a@ 隐藏 / a! 可见、a%a1 名称为 a1。自由模式导出时,点默认就有的标签也要写 a$;游玩模式与制题器导出时,所有非给定对象都要把显隐写出来(a@ / a!)。

About

A free web straightedge-and-compass toolkit: play levels, build your own, solve with a solver — 300+ classic construction puzzles included.

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages