专题的多圆显示区:前台按并集裁剪迷雾
上一批把显示区改成了「一组圆」,这一批让前台真正认多个圆。 - clip-path 从 circle() 换成 path():circle() 只表达得了一个圆。每个圆一段 圆弧子路径、**同绕向**(sweep=0),nonzero 填充规则下多段自然取并集, 重叠处不会互相挖洞。 - 拼路径的活单独放 fog-clip.js(rond-map.js 是个大 IIFE,函数取不出来), 这样能直接跑 node 单测;layout 里必须排在 rond-map.js 之前加载。 - 顺带清掉已经死掉的 fitCircle:多圆之后「圆心 + 半径」表达不了,留着就是 两套并行的贴合口径。fitParams 现在只给 fitBBox(单圆时外接矩形正是那个 圆的外接正方形,视野等价)。 - 守卫改名 TestTopicFogIsClippedToAreas,钉住两端(模板发 fogAreas ↔ 脚本认 fogAreas)与两处顺序(裁剪注册在 applyFit 之后、fog-clip.js 在 rond-map.js 之前);fogAreas 必须**无条件**下发(空数组 = 不限区域),不要包在条件里。 - verify.sh 挂进前端单测:以前只靠人记得手动跑 = 等于没有。找不到 node 直接 失败而不是跳过(跳过会让验收看着全绿、实际一行前端脚本都没验)。 验证(临时两圆专题,SQL 造的,已删): - fog-clip.test.js 15 条:把生成的 path 解析回圆弧再验几何,含绕向一致性与 半径 0/负数的夹取。 - 端到端像素验证:自校准出「CSS 容器坐标 → 图像像素」(scale≈2、offset≈19.2, 正是 site_cdp 的 --clip 外扩 10px×scale,别再手算);三张掩码图证明并集语义 —— A 独有点只有 A 亮、B 独有点只有 B 亮、**重叠中心两圆都亮且并集也亮 (无洞)**,整体集合差异 0.013%。 - 迷雾掩码:86,776 个像素,按两圆并集的解析式判 **圆外 0 个**;两个圆内分别 有 52,609 / 46,757 个迷雾像素 ⇒ 第二个圆真的被认了,不是只看第一个。
This commit is contained in:
@@ -312,25 +312,31 @@ func TestTopicAreasBoundsCoversEveryCircle(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 贴合参数:有显示区就走 bbox、不再给 fitCircle(多圆表达不了圆心+半径)。
|
||||
func TestFitParamsUsesBBoxForAreas(t *testing.T) {
|
||||
circle, bbox := fitParams(Topic{Areas: []TopicArea{{Lat: 25, Lon: 100, RadiusKm: 3}}}, nil)
|
||||
if string(circle) != "null" {
|
||||
t.Errorf("限定显示区时不该再给 fitCircle,实得 %s", circle)
|
||||
}
|
||||
// 贴合参数:有显示区就用所有圆的并集外接矩形,否则退回点集包围盒;
|
||||
// 两者都没有时给 null(脚本据此再退回它自己的 fit)。
|
||||
func TestFitParamsUsesBBox(t *testing.T) {
|
||||
var b struct{ MinLat, MaxLat, MinLon, MaxLon float64 }
|
||||
|
||||
bbox := fitParams(Topic{Areas: []TopicArea{{Lat: 25, Lon: 100, RadiusKm: 3}}}, nil)
|
||||
if err := json.Unmarshal([]byte(bbox), &b); err != nil {
|
||||
t.Fatalf("fitBBox 不是合法 JSON: %s (%v)", bbox, err)
|
||||
t.Fatalf("有显示区时 fitBBox 不是合法 JSON: %s (%v)", bbox, err)
|
||||
}
|
||||
if !(b.MinLat < 25 && b.MaxLat > 25 && b.MinLon < 100 && b.MaxLon > 100) {
|
||||
t.Errorf("fitBBox 应包住圆心:%+v", b)
|
||||
}
|
||||
|
||||
// 不限区域时仍退回点集包围盒
|
||||
circle, bbox = fitParams(Topic{}, []MapPoint{{Lat: 21, Lon: 99}, {Lat: 23, Lon: 103}})
|
||||
if string(circle) != "null" {
|
||||
t.Errorf("点集路径不该给 fitCircle,实得 %s", circle)
|
||||
// 多圆:矩形要同时包住两个圆,而不是只包住第一个
|
||||
bbox = fitParams(Topic{Areas: []TopicArea{
|
||||
{Lat: 25, Lon: 100, RadiusKm: 3}, {Lat: 30, Lon: 110, RadiusKm: 5}}}, nil)
|
||||
if err := json.Unmarshal([]byte(bbox), &b); err != nil {
|
||||
t.Fatalf("多圆 fitBBox 不是合法 JSON: %s (%v)", bbox, err)
|
||||
}
|
||||
if !(b.MinLat < 25 && b.MaxLat > 30 && b.MinLon < 100 && b.MaxLon > 110) {
|
||||
t.Errorf("多圆 fitBBox 应同时包住两个圆:%+v", b)
|
||||
}
|
||||
|
||||
// 不限区域时退回点集包围盒
|
||||
bbox = fitParams(Topic{}, []MapPoint{{Lat: 21, Lon: 99}, {Lat: 23, Lon: 103}})
|
||||
if err := json.Unmarshal([]byte(bbox), &b); err != nil {
|
||||
t.Fatalf("点集 fitBBox 不是合法 JSON: %s", bbox)
|
||||
}
|
||||
@@ -338,10 +344,9 @@ func TestFitParamsUsesBBoxForAreas(t *testing.T) {
|
||||
t.Errorf("点集包围盒 = %+v", b)
|
||||
}
|
||||
|
||||
// 两者都没有 → 两个 null
|
||||
circle, bbox = fitParams(Topic{}, nil)
|
||||
if string(circle) != "null" || string(bbox) != "null" {
|
||||
t.Errorf("既无显示区又无点时应给两个 null,实得 %s / %s", circle, bbox)
|
||||
// 两者都没有 → null,交给脚本自己的点集贴合
|
||||
if got := fitParams(Topic{}, nil); string(got) != "null" {
|
||||
t.Errorf("既无显示区又无点时应给 null,实得 %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user