Skip to content

Latest commit

 

History

188 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Geommunity Blueprint

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

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


四个入口

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

界面与玩法

关卡列表与搜索

  • 关卡包总览页、关卡包页顶部都有搜索框,点一下进入搜索页。搜索支持空格分隔的多个条件(「与」关系),可搜标题、副标题、关键词与 L / E 步数(5L、6E、5 这类写法都可以),并有带命中数量的推荐搜索;从关卡包页进入时默认只搜该包。
  • 关卡列表每页 20 关,页码写在地址栏的 page 参数里,可以直接分享或收藏某一页。
  • 从关卡返回列表时停在原来的位置,不会跳回顶部(关卡包、搜索条件、页码不同,各自记各自的位置),而退回更上一层之后就作废。

关卡游玩

  • 目标:作出该关的所求(result),画布上会以金色亮起。缩略图收起时,本关目标(如 5L 6E)做成一个小窗浮在示意图的左上角 —— 未达成的是黑色、达成的是金色;点开缩略图后,这一行写在「目标:」后面,紧跟完成 / L / E / V 四个勾(未达成黑色、达成金色)。
  • 手机端:缩略图会挪到右上角按钮下面一排,不会盖住撤销 / 重做;工具栏面板与里面的按钮等高,L / E 计数器、构造档角标、通关界面都随屏幕缩小,通关界面的宽度也按屏幕算(窄屏下四个勾排一行,不会换行盖住图片)。
  • L / E 要先作出解才会点亮(没作出所求之前步数再少也不算达成);作出过之后,即使后来步数超了也不熄灭(步数后面会标「曾达标」);一旦所求不在画布上(被撤销、清空画布),勾会一起熄灭并清空记录,重新作出时再评一次。
  • 通关界面:每真正多作出一条解时滑入,显示步数与目标对比(如 L 1 / 5 ✓ E 1 / 6 ✓),停留 5 秒后自动收起;撤销到未完成状态会立即收起。
  • 查看答案:菜单「查看答案」会先确认一次,再列出该关收录的解法图(点图可放大)。没有收录答案图的关卡,菜单项直接置灰,点一下会说明。
  • 工具限制:部分关卡限定单尺(只用直尺:移动、点、直线、交点)或单规(只用圆规:移动、点、圆、交点),工具栏里只出现允许的工具;不限制的关卡给全。
  • 撤销:如果某个工具已经画到一半(画布上还有跟随鼠标的半透明预览),按撤销只取消这次的半成品,不会把上一步也撤销掉;画到一半时撤销按钮即使没有历史也会亮起来。
  • 清空画布 / 重开本关:丢弃当前作图,回到关卡的初始图形并清空进度。
  • 探索视图:按 gmt 的 explore= 显示另一套内容,用在探索视图里自己画的图形会保留,来回复看不会丢;关卡自带的解法仍保持隐藏。
  • 设计模式:把关卡里隐藏的图形全部显示出来,并自动标好给定与所求,便于照着改题或研究构造。
  • 打开求解器:要先作出至少一个解才可点(否则菜单里是灰的,点击会弹出说明)。带过去的图形是完整的(含关卡里隐藏的部分),标记只保留「给定」与「所求判定」两类。

自制关卡与求解器

  • 标记工具在顶部菜单栏:「标记给定」有 给定 / 带标签给定 / 可移动点 三个选项,「标记所求」有 所求判定 / 所求显示 / 探索显示 三个选项。点开后工具栏里会换成一排选项,切回任一工具分类即退出标记模式。

    工具 选项 选项颜色 画布上的显示 导出到 gmt
    标记给定 给定(默认) 黑色 黑色 initial=
    带标签给定 黑色 黑色 + 标签,标记时弹命名框(留空即用对象名,只允许单个字母 / 数字) named=
    可移动点 蓝色 蓝色 movepoints=
    标记所求 所求判定(默认) 金色 金色 result= 冒号前
    所求显示 金色 金色 result= 冒号后
    探索显示 金色 金色 explore=
  • 画布上的金色范围:平时只把「所求判定」的那批图形标成金色;选到「所求显示」时只标它所对应的显示图形,选到「探索显示」时只标探索对象。给定 / 可移动点的标记色(黑 / 蓝)始终保留 —— 切换高亮范围只会让金色换地方,不会把给定图形变回灰 / 红。

  • 已标记对象:右侧面板按「给定 / 所求 / 探索」分组列出,点画布上的对象即可加上或取消标记,旁边的叉号清空全部标记(含探索与多解,并把解数收回 1)。

  • 可移动点只能是能拖动的自由点:点交点 / 线上点 / 中点这类跟随其它对象的点时,会从顶部弹提示说明不能标。

  • 制题器与求解器的点默认不显示标签(可在「配置点样式」里打开),带标签给定的对象仍显示自己的标签。

  • 多解:选中「标记所求」或「所求显示」后,切换栏里会出现解选择器:默认 1、+、−,点数字切换当前解,+ 增加一组、− 删掉最后一组(连同它的标记)。

  • 导入 / 导出 gmt:菜单「导出 gmt」可查看代码、导出文件或提交 Issue;「导入 gmt」可粘贴代码或读取文件。

  • 载入之后可以按顺序撤回:载入关卡、导入 gmt、从游玩页带图形进制题器时,载入的图形本身就是一步步作出来的,撤销会从最后一个对象开始一个个退,直到空画布,而不是只能整体撤到载入前。

  • 求解器里只需「给定」与「所求判定」两类标记(标记面板也只有这两栏),显示金色。

  • 手机端:面板收进了底部上拉栏:手机上画布左上角有两个圆形按钮 —— 上面一个是「已标记对象」(给定 / 所求 / 探索的分组列表),下面一个是「求解参数」(只在求解器里有)。点按钮从底部拉起对应的上拉栏,标题栏右上角的 ✕ 收起。电脑端仍旧是原来浮在画布上的两个面板。

  • 求解器面板(上拉栏里)可以设置最大步数、可用工具(尺规 / 单尺 / 单规)、时间上限与需要的解数,然后点「开始求解」。

  • 搜索的已知条件只取标为「给定」的对象:点开求解器时,关卡里的「给定 / 带标签给定 / 可移动点」会合并成同一栏「给定」(对搜索来说它们都只是「题面给了什么」)——这样关卡里隐藏或预绘制的图形(例如某关藏起来的中点)就不会混进条件里,否则会搜出「用了题面里没有的点」的假解(比如真实下限 5E 的关跑出个 4E)。一个给定标记都没有时(直接在求解器页自己画图),才把画布上除目标以外的对象都当作条件。

  • 目标是标为「所求判定」的对象,可以是直线、圆、点,也可以同时是好几样。

  • 解法会画在画布上:用品红按步画出来(第几步作了哪条线、哪个圆、哪个交点),一次要多个解时右侧会列出「解法 1(N 步)…」,点哪一项就换成哪一解;「上一步 / 下一步」可以自己逐步看(退回一步就把最后作的那个图形收掉,旁边显示「第 k / n 步」);「清除解法」把它们收掉。解法只是覆盖层,不能选取也不能导出,不影响你自己的作图。

  • 拖动图形时解法跟着动:品红解法不是一张固定贴图,而是按构造步骤实时重算的 —— 拖动给定点(或给定直线的端点)让图形变形时,它作出的圆、直线与交点会一起跟着变,方便检查这条构造是否真的成立。拖到某一步作不出来的位置时(例如两个圆不再相交),那一步的图形会暂时消失(与画板对无效对象的处理一致),把图形拖回去它又会自己长回来。

  • 文字步骤:每个解后面有「文字步骤」按钮,点开弹出一份中文说明 —— 已知对象有哪些、第几步以哪个点为圆心过哪个点作圆(或过哪两点作直线)、每两条线的交点是哪个点、以及最后目标对应到图上哪条线 / 哪个点。搜索是给出构造,不是给证明,所以这份说明只说「怎么作」,不说「为什么对」。

  • 步数上限就是你要的步数:搜索是「找到就返回」,不保证拿到最短解,所以想要更短的解就把最大步数调小;时间上限到了会停下并报告已找到几个解。搜索进行中按钮会变成「停止求解」,再点一下即可中断(不必等满时间上限)。

  • 从关卡 / 试玩进来再返回:返回按钮会回到进来时的界面,并把之前的状态(图形、标记、步数)一并还原,来回一趟不会白画。

  • 试玩:进入的页面与关卡游玩一致 —— 只显示给定,其余(包括解法)先藏起来,自己作出来或判定通过后才显示;返回制题器时会还原编辑状态,不会把刚画好的题弄丢。

  • 鸣谢:求解器的搜索内核由 Ander 与 zzzzzz 共同开发,本项目使用的是它的 JavaScript 移植版。

工具与样式

  • 工具分类:顶部按「通用 / 点 / 线 / 圆 / 构造」分档;构造档按钮右下角的小角标始终显示构造档当前选中的那个高级工具。
  • 样式刷(制题器 / 求解器:通用档里紧挨橡皮擦):先点一个图形当样式来源,之后每点到一个图形,它的颜色、粗细(点的大小)与标签显示就套用来源的样式;点空白不生效。
  • 切换线类型(制题器 / 求解器:线档最后):选中它之后,点画布上的直线 / 射线 / 线段就把它的类型换成下一种(直线 → 射线 → 线段 → 直线),点击时光标是个圆环。
  • 重复的图形不会再作一次:即将作出的直线 / 圆如果画布上已经有一个(只看图形本身,不看它是怎么作出来的),这次作图就作废 —— 画布上不会多出图形,游玩模式也不会记步数。所以「再连一次 AB」、在 a 上取点作 a 的平行线、用三点圆规以 A 为圆心 AB 为半径作圆,都不会产生第二个同样的图形。直线 / 射线 / 线段之间互不算重复(同一条线上的直线与射线是两个图形)。
  • 三点圆规的第三点(圆心)可以与前两点重合:以 A 为圆心、AB 为半径作圆时,第三下直接点 A 或 B 就行(前两点仍然不能是同一个点)。
  • 直接拖动图形:移动工具下,如果一个图形的定义点全是自由点(由坐标定义的点),拖动它就会把整个图形平移 —— 这些定义点一起挪动,依此作出的直线、圆等跟着走;定义点里有点不动(交点、线上点…)或还牵着别的图形时,拖动仍是平移画布。
  • 切工具会清掉上一个工具画到一半的选中,切回来不会发现上次选的图形还挂着;重选当前工具则保留(不会把正在选的东西清掉)。
  • 撤销 / 重做:样式刷的每一步、拖动图形、切换线类型都各记一步,可以一次次退回。

画布、光标与预览

  • 画布可以一直放大缩小、平移,没有实际的上限感;初始视野较宽,视角乱了可以点「还原视角」回到居中。

  • 光标提示:靠近对象时变成手指,并浮出「对象名称 · 类型」,提示点下去会选中它;空白处是四向箭头(可拖动看画布);中键拖动时是抓手;有作图半成品时是十字。

  • 预览点:作图工具的光标预览点半透明,样式与点图形一致,跟随鼠标并会吸附:

    优先级 吸附到
    1 已有点,或两图形相交处(包括还没有作出交点对象的「隐交点」);两者都在范围内时取更近的一个
    2 上面都没有时,吸附到最近的直线 / 圆上(点工具落在对象上就是这个位置)
  • 预览点吸到哪里,图形就画在哪里:草稿线与最终落下的点都跟着吸附位置走,所以在隐交点上也能直接落笔 —— 包括「最后一个点点在隐交点上」这种情形,不会出现点不出点、画不出图形的情况。

  • 叠在一起时选谁:点优先于线段、线段优先于射线、射线优先于直线、最后是圆(交点常压在直线 / 圆上,所以点优先)。同一个位置压着好几个点时,先选能拖动的自由点,其余按作图倒序(后画的先被选到);这只影响「鼠标选到谁」,不影响显示层序。

  • 移动工具下的删除按钮:移动工具选中某个对象后,切换栏里的垃圾桶按钮会亮起,点它会连同所有子对象(依赖它作出来的图形)一起删除,并且可以撤销。

  • 圆心工具不看光标位置,而是预览「光标靠近的那个圆的圆心」,样式与点预览一致;离开圆就消失。

  • 自由点不会被当成特殊点:一个自由点恰好落在(或非常靠近)某条线、某个圆上时,它仍旧只是个普通自由点 —— 「圆的定义点」这类判定只认真正构造出来的点(线上点、交点…),不会因为位置巧合而改变交点取哪一个(否则拖动图形时交点会忽然跳到另一侧)。

  • 复制圆规先点一个圆,之后圆心跟着光标走,半径取那个圆的半径。

  • 两直线角平分线全程不显示光标下的点预览;点了第一条线之后,靠近第二条线才把两条角平分线一起预览出来。

  • 预览离开对象就立刻消失,不会留下残影(圆心离开圆、角平分线离开第二条线都会马上重绘一次)。

  • 提示与确认统一用站内样式(顶部提示条与站内弹层),不用浏览器弹框。

其他

  • 帮助:四个工作区(画板、关卡、制题器、求解器)的「菜单 → 帮助」说明当前界面的用法,菜单与按钮悬停时信息栏也会给出说明。
  • 项目说明:首页右上角的「项目说明」取回本文件并用页面风格渲染成弹窗。
  • 中英双语:首页右上角可切换中文 / English,选择会被记住。
  • L / E / V:L / E 是步数要求(如 5L / 6E),V 是多解(要求作出全部解);单解关卡不显示 V,没有目标时不显示对应勾。

进阶:本地运行与制题

以下内容主要面向想自己跑一份站点、增删关卡或直接编辑 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 关卡扁平列表(346 关)
data/answers/<包id>/<关卡id>/… 各关收录的解法图(每关一个文件夹,文件名是 5L.png / 6E.png 这类星标)

关卡 id 就是短名:ewp11、xmath2020-1、Conway Circle、inc-circumc-re-trapezoid 这样,不再是 包:目录/文件.gmt 那种路径。文件名、示意图、答案文件夹都跟着用同一个 id(ewp11.gmt / ewp11.png / answers/ewp/ewp11/),所以「id = 文件名(不含扩展名)」,找一关的东西只看它的 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 列表显示「步数未标注」
number 列表里的序号 按顺序生成 001…
diagram 示意图路径(相对 data/) 列表用 ◇ 占位,卡片无图
note 关卡列表行的悬停提示 无
tools 工具限制:"straightedge" 单尺 / "compass" 单规 不限制,给全部工具
keywords 搜索用关键词数组 搜索时只用标题与 subtitle
solutions 该关收录的解法图:[{"file": "answers/<包id>/…", "star": "5L"}, …](file 相对 data/) 该关没有收录答案图(「查看答案」置灰)

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

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

{
    "id": "ewp11",
    "pack": "ewp",
    "title": "EWP11",
    "subtitle": "圆内有一点A,作过A点的弦,弦长等于半径。",
    "targetSteps": "5L / 6E",
    "diagram": "diagrams/ewp/ewp11.png",
    "file": "levels/ewp/ewp11.gmt",
    "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"}
    ]
}

新增关卡包 / 关卡:先把 .gmt 与示意图放进 data/levels、data/diagrams,再在 levelpacks.json 加一个包、在 levels.json 加一条关卡即可。若是脚本批量生成,记得保留 name / description / icon / title / subtitle / number / 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(限制条件):遇到这些指令时该对象会被丢掉,依赖它的对象也一并丢掉,其余部分照常解析(现行 346 个关卡文件里没有用到 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 即可;在画板上直接点出来的点也按同一套换算,所以「自己画的」与「载入关卡里的」位置一致。

交点编号(index)

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

  • 线与线:只有一个交点,x=0;
  • 线与圆:候选按线的方向(第一个定义点 → 第二个定义点)升序,编号规则照 原版游戏实测:
    • 同一对基底上的第一个 Intersect 一律取「非定义点」那个候选——x 写 0 还是 1 都一样(c1=Circle[A,B]、s1=Line[B,A] 时 E=Intersect[c1,s1,0] 与 F=Intersect[c1,s1,1] 都落在 A 上);
    • 第二个及以后才按 x 正常取(同例改成 s1=Line[A,B]:E=Intersect[c1,s1,0] 是 A、F=Intersect[c1,s1,1] 是 B);
    • 「定义点」指圆的半径端点(Circle[A,B] 的 B),线自己的定义点不算——所以「过圆上一点作直线」时那个已知点仍是有效候选;
  • 圆与圆:候选以对象1圆心 → 对象2圆心的方向起逆时针升序,编号规则与线与圆同源(第一个 Intersect 取非定义点那个,第二个及以后按 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°」;角平分线取角的开口方向;平行线与被平行的线同向;切线以切点为第一定义点;
  • 取编号前先按范围过滤候选:线段 / 射线上的交点只可能是范围内的那个,范围外的候选不该占编号(Intersect[射线,圆,0] 指的是射线方向上的交点)。一个候选都不在范围内时保留原列表,编号照旧取、最后按「失效但不删除」处理,拖动图形使交点暂时跑到线外时不会跳到另一个交点上;
  • 第四个参数(如 Intersect[s3,c2,1,F] 里的 F)是已知的那个交点,编号时先把它排除再取 x(可省略或写 -);这个排除关系会随撤销、存读档一起保留。

配了 index 之后,交点是被「记住」的:当线段、射线的范围变化使交点跑到线外(或两对象不再相交)时,该点失效但不删除(不显示、不参与作图),重新相交后自动恢复。用交点工具创建交点时,也按点击位置选中离点击处最近的那一个。

设定行

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 里坐标定义的点与线上点,和可移动点一样能拖(选中移动工具时临时显示成蓝色,退出移动工具还原)。

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

10 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages