V2.8 Stage-1 Spec-to-Image Review · K26

Stage-1 只审 spec 代码/结构到图片的实现质量,不审 QA/答案。展示未通过或不能进入 Stage-2 的样本。
28/100
stage-1 pass
29
go stage2
5.13
avg score
72
shown

verdicts: unusable:4, major_spec_issue:66, minor_spec_issue:2 · repair: major:27, toolkit:23, minor:19, rewrite:3 · issues: label_overlap:44, camera_or_view:32, mesh_clutter:29, plan_coverage:28, aux_line_clutter:26, label_missing:22, sphere_render:21, angle_arc:19, frustum_render:12, other:11, shape_primitive:10, tangent_or_section:8, cone_cylinder_render:7, renderer_support:3, label_position:1, cylinder_render:1

acute_dihedral__S012

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 2 unusable major hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
一个直圆锥,顶点 S,底面圆心 O,底面水平放置。两斜平面 α、β 共过底面圆周上的公共弦 AB,分处 AB 两侧:α 向底面前上方抬起,与底面所成二面角张口明显小于 90°;β 向底面后上方抬起,张口明显大于 90°。两平面截圆锥各得一椭圆弧截面(可见部分实线)。圆锥内部置一球,球心 O1 在轴 SO 上,球与底面切于 O,与 α 切于 T1,与 β 切于 T2。底面隐藏半圆、轴 SO 及内部被遮挡轮廓均画虚线。
Must Render
[
  "直圆锥及底面圆(含隐藏虚线半圆)",
  "公共弦 AB",
  "两椭圆截面弧及平面 α、β 的可见边界",
  "球、球心 O1、切点 O/T1/T2",
  "轴 SO 及内部被遮挡轮廓虚线"
]

Spec Summary

{
  "title": "锐二面角识别",
  "meta": "直圆锥双斜截面球切点",
  "camera": {
    "position": [
      6.006,
      5.498,
      10.683
    ],
    "target": [
      0.0,
      0.646,
      0.281
    ],
    "orthoSize": 3.931
  },
  "counts": {
    "vertices": 47,
    "faces": 0,
    "edges": 35,
    "aux_lines": 12,
    "aux_faces": 0,
    "primitives": 2,
    "labels": 7
  },
  "primitive_types": [
    "cone",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cone",
      "radius": 2.106653110915029,
      "height": 3.2579544029403023,
      "position": [
        0,
        1.6289772014701511,
        0
      ]
    },
    {
      "type": "sphere",
      "radius": 2.090590471625998,
      "position": [
        0,
        2.090590471625998,
        0
      ]
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "O1",
      "text": "O1"
    },
    {
      "point": "T1",
      "text": "T1"
    },
    {
      "point": "T2",
      "text": "T2"
    }
  ],
  "vertex_names": [
    "O",
    "S",
    "A",
    "B",
    "O1",
    "T1",
    "T2",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "aL1",
    "aL2",
    "aL3",
    "aL4",
    "aL5",
    "aL6",
    "aR6",
    "aR5",
    "aR4",
    "aR3",
    "aR2",
    "aR1",
    "bL1",
    "bL2",
    "bL3",
    "bL4",
    "bL5",
    "bL6",
    "bR6",
    "bR5",
    "bR4",
    "bR3",
    "bR2",
    "bR1"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "C0",
        "C1"
      ]
    },
    {
      "points": [
        "C1",
        "C2"
      ]
    },
    {
      "points": [
        "C2",
        "C3"
      ]
    },
    {
      "points": [
        "C3",
        "C4"
      ]
    },
    {
      "points": [
        "C4",
        "C5"
      ]
    },
    {
      "points": [
        "C13",
        "C14"
      ]
    },
    {
      "points": [
        "C14",
        "C15"
      ]
    },
    {
      "points": [
        "C15",
        "C0"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "A",
        "aL1"
      ]
    },
    {
      "points": [
        "aL1",
        "aL2"
      ]
    },
    {
      "points": [
        "aL2",
        "aL3"
      ]
    },
    {
      "points": [
        "aL3",
        "aL4"
      ]
    },
    {
      "points": [
        "aL4",
        "aL5"
      ]
    },
    {
      "points": [
        "aL5",
        "aL6"
      ]
    },
    {
      "points": [
        "aL6",
        "aR6"
      ]
    },
    {
      "points": [
        "aR6",
        "aR5"
      ]
    },
    {
      "points": [
        "aR5",
        "aR4"
      ]
    },
    {
      "points": [
        "aR4",
        "aR3"
      ]
    },
    {
      "points": [
        "aR3",
        "aR2"
      ]
    },
    {
      "points": [
        "aR2",
        "aR1"
      ]
    },
    {
      "points": [
        "aR1",
        "B"
      ]
    },
    {
      "points": [
        "A",
        "bL1"
      ]
    },
    {
      "points": [
        "bL1",
        "bL2"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "C5",
        "C6"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C6",
        "C7"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C7",
        "C8"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C8",
        "C9"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C9",
        "C10"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C10",
        "C11"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C11",
        "C12"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "C12",
        "C13"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "S",
        "O"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "O1",
        "T1"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "O1",
        "T2"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "O1",
        "O"
      ],
      "dash": true,
      "color": "#555555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.uniform(1.6, 2.2)
4:     H = rng.uniform(2.5, 3.5)
5:     d = rng.uniform(0.28 * R, 0.52 * R)

22:     T2 = (0.0, r * (1.0 - math.sin(ta)), -r * math.cos(ta))
23: 
24:     vertices = {"O": O, "S": S, "A": A, "B": B, "O1": O1, "T1": T1, "T2": T2}
25:     edges = []
26:     aux_lines = []
27: 
28:     n = 16
29:     step = 360.0 / n
30:     azim = 30.0
31:     for i in range(n):
32:         ang = math.radians(i * step)
33:         vertices[f"C{i}"] = (R * math.cos(ang), 0.0, R * math.sin(ang))
34:     for i in range(n):
35:         a0 = i * step
36:         a1 = ((i + 1) % n) * step
37:         mid = (a0 + a1) / 2.0 if a1 > a0 else (a0 + a1 + 360.0) / 2.0
41:         vis = abs(rel) < 90.0
42:         p, q = f"C{i}", f"C{(i + 1) % n}"
43:         (edges if vis else aux_lines).append((tk.edge(p, q) if vis else tk.aux(p, q, dash=True, color="#333333")))
44: 
45:     aux_lines.append(tk.aux("S", "O", dash=True, color="#333333"))
46:     edges.append(tk.edge("A", "B"))
47: 
48:     def arc(m, tag):
49:         a = (R / H) ** 2 - m * m
50:         b = -2.0 * (R * R / H + d * m)
51:         c = R * R - d * d
52:         disc = b * b - 4.0 * a * c
68:         for i in range(1, len(pts)):
69:             nm = f"{tag}L{i}"
70:             vertices[nm] = (-pts[i][0], pts[i][1], pts[i][2])
71:             seq.append(nm)
72:         for i in range(len(pts) - 1, 0, -1):
73:             nm = f"{tag}R{i}"
74:             vertices[nm] = (pts[i][0], pts[i][1], pts[i][2])
75:             seq.append(nm)
76:         seq.append("B")
77:         for i in range(len(seq) - 1):
78:             edges.append(tk.edge(seq[i], seq[i + 1]))
79: 
80:     arc(1.0 / math.tan(ta), "a")
81:     arc(1.0 / math.tan(tb), "b")
82: 
83:     aux_lines.append(tk.aux("O1", "T1", dash=True, color="#555555"))
84:     aux_lines.append(tk.aux("O1", "T2", dash=True, color="#555555"))
85:     aux_lines.append(tk.aux("O1", "O", dash=True, color="#555555"))
86: 
87:     primitives = [tk.prim_cone(r=R, h=H, center=(0, 0, 0)),
88:                   tk.prim_sphere(r=r, center=(0, r, 0))]
89:     camera = tk.auto_camera(vertices, primitives=primitives, azim=azim, elev=22.0, pad=1.7)
90:     spec = tk.make_spec("锐二面角识别", "直圆锥双斜截面球切点", vertices,
91:                         edges=edges, aux_lines=aux_lines, primitives=primitives,
92:                         labels=["S", "O", "A", "B", "O1", "T1", "T2"], camera=camera)
93: 
94:     opts = {
95:         "A": "平面 α 与底面所成的二面角",
96:         "B": "平面 β 与底面所成的二面角",
97:         "C": "平面 α 与底面所成的二面角和平面 β 与底面所成的二面角",
98:         "D": "平面 α 与平面 β 所成的二面角"
99:     }
100:     items = list(opts.items())
101:     rng.shuffle(items)
102:     options = {k: v for k, v in items}
103:     ans = [k for k, v in options.items() if v == opts["A"]][0]
104:     return tk.item(spec, "如图,平面 α、β 均与圆锥底面相交于弦 AB,分别形成两个二面角。观察其张口大小,其中为锐二面角的是( )", options, ans)

K26 Spec-to-Image Review

  • fatal/sphere_render
    球半径2.09接近圆锥尺寸,球顶远超顶点,实心球完全包裹圆锥,违背内置球plan
    spec_fix: 重算半径使球完全在圆锥内;将球primitive改为线框或仅画大圆/截面圆,避免实心遮挡
  • major/angle_arc
    两视图均无angle_arc或二面角平面角标注,无法直观判断α、β与底面所成角锐钝
    spec_fix: 在AB上取点M,向α、β内作垂直于AB的射线,用angle_arc标出两二面角的平面角
  • major/label_overlap
    current图中T1贴底面边缘,section_plane图中T1位于底面O附近,与球面切点位置不符
    spec_fix: 校验T1、T2为球与对应平面的真实切点;调整标签位置避免贴线或重叠
  • minor/mesh_clutter
    底面圆16段离散及截面弧折线化明显,球与圆锥轮廓交叠处线条杂乱
    spec_fix: 底面圆与截面弧增加分段或改用平滑曲线;减少不必要的辅助线
  • minor/camera_or_view
    section_plane视角未突出二面角,球遮挡下圆锥侧面不可见,辅助视角失效
    spec_fix: 调整camera避开球遮挡,或先修复球渲染后再重新构图
What To Change
  • 球半径逻辑:令球完全内接于圆锥且与底面切于O
  • 球渲染:取消实心填充,改用轮廓圆/大圆表示
  • 增加二面角平面角angle_arc及标注
  • 修正T1/T2坐标与标签位置
Preferred Primitives
  • cone
  • sphere_wireframe
  • angle_arc
Remove/Simplify
  • 移除实心球填充,仅保留必要截面圆
  • 简化底面离散边为平滑圆
  • 移除球内过多虚线半径
Label Fixes
  • T1、T2移至真实切点旁空白处
  • S、O1避免与球轮廓重叠
Camera Fixes
  • current视角抬高并拉远,确保圆锥整体可见
  • section_plane侧视角聚焦AB与二面角张开区域
Toolkit/Renderer: 需toolkit支持sphere线框/轮廓渲染及angle_arc primitive,否则spec需用aux_lines模拟大圆与角弧
representation: 球与圆锥尺寸关系完全错误,实心球遮挡导致圆锥不可见;无二面角角弧,核心几何信息缺失
readability: 两图均因实心大球遮挡而混乱,关键线面不可定位,无法用于判断锐二面角
plan: plan要求的圆锥内置球、二面角识别均未正确表达;仅底面圆与弦AB基本可见
next: spec_rewrite

acute_dihedral__S002

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
绘制正四棱锥 S-ABCD,底面 ABCD 水平放置(A 左前、B 右前、C 右后、D 左后),顶点 S 位于底面中心 O 的正上方。过侧棱 SA 与底边 BC 上一点 E(BE:EC=1:2)作斜截面 SAE,该面与底面相交于线段 AE,与侧面 SBC 相交于线段 SE。从 S 向底面作垂线 SO(投影线),垂足为 O;底面内画出对角线 AC、BD,其中被遮挡的后半段及后侧棱 SC、SD 均用虚线表示。在锥体内放置一个与底面 ABCD 和侧面 SCD 都相切的辅助球,球心为 O1,切点分别标记为 T1(底面)和 T2(侧面)。用带数字的弧线在图中标注四个二面角的平面角:①截面 SAE 与底面沿 AE 所成平面角约 40°;②侧面 SAB 与侧面 SAD 沿 SA 所成平面角约 125°;③截面 SAE 与侧面 SBC 沿 SE 所成平面角约 95°;④截面 SAE 与侧面 SCD 沿交线 SF(F 为 AE 与 CD 延长线的交点)所成平面角约 110°。
Must Render
[
  "斜截面 SAE 及其与底面、侧面的交线 AE、SE",
  "投影线 SO 及垂足 O",
  "底面对角线 AC、BD(含虚线遮挡段)与后侧棱虚线",
  "辅助球及切点 T1、T2",
  "四个带数字的平面角弧线(①约40°、②约125°、③约95°、④约110°)"
]

Spec Summary

{
  "title": "正四棱锥截面",
  "meta": "acute_dihedral_S002",
  "camera": {
    "position": [
      6.079,
      5.04,
      9.652
    ],
    "target": [
      0.185,
      0.547,
      0.22
    ],
    "orthoSize": 3.08
  },
  "counts": {
    "vertices": 23,
    "faces": 5,
    "edges": 16,
    "aux_lines": 12,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 15
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.4399350998717739,
      "position": [
        0.0,
        0.4399350998717739,
        -0.790141640584201
      ]
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "T1",
      "text": "T1"
    },
    {
      "point": "T2",
      "text": "T2"
    },
    {
      "point": "O1",
      "text": "O1"
    },
    {
      "point": "①m",
      "text": "①"
    },
    {
      "point": "②m",
      "text": "②"
    },
    {
      "point": "③m",
      "text": "③"
    },
    {
      "point": "④m",
      "text": "④"
    }
  ],
  "vertex_names": [
    "C",
    "D",
    "A",
    "B",
    "S",
    "E",
    "O",
    "F",
    "O1",
    "T1",
    "T2",
    "①1",
    "①2",
    "①m",
    "②1",
    "②2",
    "②m",
    "③1",
    "③2",
    "③m",
    "④1",
    "④2",
    "④m"
  ],
  "faces_sample": [
    {
      "points": [
        "B",
        "A",
        "D",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "S"
      ]
    },
    {
      "points": [
        "D",
        "A",
        "S"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "S"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "S"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "B"
      ]
    },
    {
      "points": [
        "S",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "S",
        "D"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "O"
      ]
    },
    {
      "points": [
        "B",
        "O"
      ]
    },
    {
      "points": [
        "O",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "O",
        "D"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "E"
      ]
    },
    {
      "points": [
        "S",
        "E"
      ]
    },
    {
      "points": [
        "S",
        "F"
      ]
    },
    {
      "points": [
        "S",
        "O"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        [
          -3.3306690738754696e-16,
          0.0,
          1.2814739505083497
        ],
        [
          -0.05692099788297421,
          0.0,
          1.110710956859428
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          -3.3306690738754696e-16,
          0.0,
          1.2814739505083497
        ],
        [
          0.03235376903007697,
          -0.14809569978725454,
          1.3785352575985816
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          -0.05692099788297421,
          0.0,
          1.110710956859428
        ],
        [
          0.03235376903007697,
          -0.14809569978725454,
          1.3785352575985816
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          -0.9611054628812624,
          0.8798701997435479,
          0.9611054628812621
        ],
        [
          -0.8162472348584517,
          0.8077225941947975,
          1.0399141983594236
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          -0.9611054628812624,
          0.8798701997435479,
          0.9611054628812621
        ],
        [
          -1.039914198359424,
          0.8077225941947975,
          0.8162472348584513
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          -0.8162472348584517,
          0.8077225941947975,
          1.0399141983594236
        ],
        [
          -1.039914198359424,
          0.8077225941947975,
          0.8162472348584513
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          0.9611054628812621,
          0.8798701997435479,
          0.32036848762708753
        ],
        [
          0.8445522759518473,
          0.790419880490795,
          0.4243588477676795
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          0.9611054628812621,
          0.8798701997435479,
          0.32036848762708753
        ],
        [
          0.992803878846566,
          0.8508510206169054,
          0.14557404358466397
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          0.8445522759518473,
          0.790419880490795,
          0.4243588477676795
        ],
        [
          0.992803878846566,
          0.8508510206169054,
          0.14557404358466397
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          0.7688843703050097,
          1.6189611675281281,
          -0.15377687406100193
        ],
        [
          0.7638059102089869,
          1.4743217851541635,
          -0.04675510847323168
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          0.7688843703050097,
          1.6189611675281281,
          -0.15377687406100193
        ],
        [
          0.7217778486068984,
          1.5016525826918634,
          -0.2819161402412473
        ]
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        [
          0.7638059102089869,
          1.4743217851541635,
          -0.04675510847323168
        ],
        [
          0.7217778486068984,
          1.5016525826918634,
          -0.2819161402412473
        ]
      ],
      "dash": false,
      "color": "#555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     a = rng.uniform(1.5, 2.0)
4:     h = a * rng.uniform(0.87, 0.93)
5:     r = a * math.sqrt(2.0)

19:     for k, p in [("E",E),("O",O),("F",F),("O1",O1),("T1",T1),("T2",T2)]:
20:         vs[k] = p
21:     faces = [{"points": [mp[p] for p in f["points"]]} for f in f0]
22:     edges = [tk.edge(u,v) for u,v in [("A","B"),("B","C"),("C","D"),("D","A"),
23:              ("S","A"),("S","B")]] + [tk.edge("S","C",True),tk.edge("S","D",True)]
24:     edges += [tk.edge("A","O"),tk.edge("B","O"),tk.edge("O","C",True),tk.edge("O","D",True)]
25:     edges += [tk.edge("A","E"),tk.edge("S","E"),tk.edge("S","F"),tk.edge("S","O",True)]
26:     aux_lines = []
27:     labs = [{"point":k,"text":k} for k in ["A","B","C","D","S","E","O","F","T1","T2","O1"]]
28:     def mk(P, d, n1, n2, t, o=0.18):
29:         d = tk.unit(d); u = tk.unit(tk.cross(n1, d)); v = tk.unit(tk.cross(n2, d))
30:         p1 = tk.vadd(P, tk.vmul(u, o)); p2 = tk.vadd(P, tk.vmul(v, o))
31:         pm = tk.vadd(P, tk.vmul(tk.vadd(u, v), o*0.6))
32:         vs[t+"1"], vs[t+"2"], vs[t+"m"] = p1, p2, pm
33:         aux_lines.extend([tk.aux(P, p1, False, "#555"), tk.aux(P, p2, False, "#555"), tk.aux(p1, p2, False, "#555")])
34:         labs.append({"point": t+"m", "text": t})
35:     n0 = (0.0, 1.0, 0.0)
36:     nSAE = tk.cross(tk.vsub(S, A), tk.vsub(E, A))
37:     nSAB = tk.cross(tk.vsub(A, S), tk.vsub(B, S))
43:     mk(tk.midpoint(S, E), tk.vsub(E, S), nSAE, nSBC, "③")
44:     mk(tk.vadd(S, tk.vmul(tk.vsub(F, S), 0.08)), tk.vsub(F, S), nSAE, nSCD, "④")
45:     cam = tk.auto_camera({k:v for k,v in vs.items() if k != "F"}, azim=32.0, elev=22.0)
46:     spec = tk.make_spec("正四棱锥截面", "acute_dihedral_S002", vs, faces=faces, edges=edges,
47:                         aux_lines=aux_lines, primitives=[tk.prim_sphere(R, center=O1)],
48:                         labels=None, camera=cam)
49:     spec["labels"] = labs
50:     opts = {"A":"①", "B":"②", "C":"③", "D":"④"}
51:     return tk.item(spec, "如图,用带数字的弧线标注了四个二面角的平面角,其中属于锐二面角的是( )", opts, "A", meta={"self_ref": True})

K26 Spec-to-Image Review

  • major/angle_arc
    plan要求带数字弧线标注二面角,但spec用12条aux_lines拼成四个小三角形,图中无圆弧。
    spec_fix: 改用angle_arc primitive绘制角弧;若toolkit不支持,需先扩展toolkit。
  • major/plan_coverage
    edges仅含AO/BO/CO/DO,缺少plan要求的完整底面对角线AC与BD。
    spec_fix: 添加tk.edge('A','C')与tk.edge('B','D'),并按视角遮挡设置虚线。
  • major/label_overlap
    current图中S、O1、T2、②、③、④等标签堆叠在锥顶与球心处,难以辨认。
    spec_fix: 调整标签偏移量或camera角度,使角标与球标签分散布置,避免重叠。
  • minor/camera_or_view
    SF线延伸至画面极右侧,F远离主体,两视角均构图松散。
    spec_fix: 缩小orthoSize或调整target与camera位置,使主体居中并弱化长线干扰。
What To Change
  • 角标记改用angle_arc弧线
  • 补全底面对角线AC、BD
  • 调整标签偏移避免重叠
Preferred Primitives
  • angle_arc
  • sphere
Remove/Simplify
  • 移除mk函数生成的12条aux_lines三角形角标
  • 若AO/BO/CO/DO与对角线重复可简化
Label Fixes
  • ①②③④角标外移至弧线外侧
  • O1/T1/T2增加引线或外移
Camera Fixes
  • 调整orthoSize与target,使锥体主体居中
Toolkit/Renderer: 需toolkit支持angle_arc primitive绘制二面角平面角弧线;当前硬搓三角形不可接受。
representation: 棱锥与球结构基本正确,但二面角标记未使用弧线primitive,且底面对角线缺失,关键几何表达不完整。
readability: 标签拥挤于锥顶与球心,SF长线导致视觉焦点分散;补充视角未解决重叠与构图问题。
plan: 遗漏底面对角线AC、BD;二面角标注形式与plan要求的弧线不符;球与切点已画出。
next: toolkit_patch

acute_dihedral__S003

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 锐二面角识别
难度 L2
Scene
一个正三棱柱A₁B₁C₁-A₂B₂C₂水平放置,下底面A₂B₂C₂水平,侧棱竖直。上底面A₁B₁C₁内有一个与其共中心、边平行且较小的内接正三角形ABC。以ABC为底面的扁平正三棱锥P-ABC置于上底面之上,顶点P较低,使相邻侧面夹角呈钝角。三棱锥遮挡上底面中心区域。图中用虚线画出三棱柱被遮挡的侧棱与上底面内线段,并在棱AB、PA、A₁B₁、BC处分别标注二面角①②③④的平面角弧线与辅助垂线。
Must Render
[
  "正三棱柱的可见侧棱与上下底面正三角形",
  "内接于上底面的较小正三角形ABC及顶点P",
  "三棱锥的三条侧棱PA、PB、PC",
  "被三棱锥遮挡的侧棱与底面边用虚线表示",
  "四个二面角位置标注的辅助垂线与平面角弧线"
]

Spec Summary

{
  "title": "锐二面角识别",
  "meta": "正三棱柱与内接正三棱锥",
  "camera": {
    "position": [
      5.657,
      5.961,
      8.909
    ],
    "target": [
      -0.221,
      2.032,
      -0.143
    ],
    "orthoSize": 2.941
  },
  "counts": {
    "vertices": 28,
    "faces": 8,
    "edges": 0,
    "aux_lines": 16,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 14
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'B', 'text': 'B'}",
      "text": "{'point': 'B', 'text': 'B'}"
    },
    {
      "point": "{'point': 'C', 'text': 'C'}",
      "text": "{'point': 'C', 'text': 'C'}"
    },
    {
      "point": "{'point': 'P', 'text': 'P'}",
      "text": "{'point': 'P', 'text': 'P'}"
    },
    {
      "point": "{'point': 'A1', 'text': 'A1'}",
      "text": "{'point': 'A1', 'text': 'A1'}"
    },
    {
      "point": "{'point': 'B1', 'text': 'B1'}",
      "text": "{'point': 'B1', 'text': 'B1'}"
    },
    {
      "point": "{'point': 'C1', 'text': 'C1'}",
      "text": "{'point': 'C1', 'text': 'C1'}"
    },
    {
      "point": "{'point': 'A2', 'text': 'A2'}",
      "text": "{'point': 'A2', 'text': 'A2'}"
    },
    {
      "point": "{'point': 'B2', 'text': 'B2'}",
      "text": "{'point': 'B2', 'text': 'B2'}"
    },
    {
      "point": "{'point': 'C2', 'text': 'C2'}",
      "text": "{'point': 'C2', 'text': 'C2'}"
    },
    {
      "point": "{'point': 'a1', 'text': '①'}",
      "text": "{'point': 'a1', 'text': '①'}"
    },
    {
      "point": "{'point': 'a2', 'text': '②'}",
      "text": "{'point': 'a2', 'text': '②'}"
    },
    {
      "point": "{'point': 'r3', 'text': '③'}",
      "text": "{'point': 'r3', 'text': '③'}"
    },
    {
      "point": "{'point': 'a4', 'text': '④'}",
      "text": "{'point': 'a4', 'text': '④'}"
    }
  ],
  "vertex_names": [
    "A2",
    "B2",
    "C2",
    "A1",
    "B1",
    "C1",
    "A",
    "B",
    "C",
    "P",
    "M1",
    "u1",
    "u2",
    "a1",
    "Q",
    "v1",
    "v2",
    "a2",
    "M2",
    "u3",
    "u4",
    "r1",
    "r2",
    "r3",
    "N",
    "u5",
    "u6",
    "a4"
  ],
  "faces_sample": [
    {
      "points": [
        "A2",
        "C2",
        "B2"
      ]
    },
    {
      "points": [
        "A1",
        "B1",
        "C1"
      ]
    },
    {
      "points": [
        "A2",
        "B2",
        "B1",
        "A1"
      ]
    },
    {
      "points": [
        "B2",
        "C2",
        "C1",
        "B1"
      ]
    },
    {
      "points": [
        "C2",
        "A2",
        "A1",
        "C1"
      ]
    },
    {
      "points": [
        "P",
        "A",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "B",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "C",
        "A"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "M1",
        "u1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "M1",
        "u2"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "u2",
        "a1"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "a1",
        "u1"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "Q",
        "v1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "Q",
        "v2"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "v1",
        "a2"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "a2",
        "v2"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "M2",
        "u3"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "M2",
        "u4"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "r1",
        "r3"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "r3",
        "r2"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "N",
        "u5"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "N",
        "u6"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "u5",
        "a4"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "a4",
        "u6"
      ],
      "dash": false,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     r = rng.uniform(1.6, 2.2)
3:     h = rng.uniform(1.8, 2.4)
4:     k = rng.uniform(0.38, 0.50)
5:     hp = rng.uniform(0.28, 0.42)

15:     vs["C"] = sc(C1, k)
16:     vs["P"] = (ctr[0], ctr[1] + hp, ctr[2])
17:     faces = [{"points": ["A2", "C2", "B2"]}, {"points": ["A1", "B1", "C1"]},
18:              {"points": ["A2", "B2", "B1", "A1"]}, {"points": ["B2", "C2", "C1", "B1"]},
19:              {"points": ["C2", "A2", "A1", "C1"]},
20:              {"points": ["P", "A", "B"]}, {"points": ["P", "B", "C"]}, {"points": ["P", "C", "A"]}]
21:     auxs = []
45:     vs["a2"] = tk.vadd(Q, tk.vmul(tk.unit(tk.vadd(e1, e2)), 0.14))
46:     auxs += [tk.aux("v1", "a2", dash=False), tk.aux("a2", "v2", dash=False)]
47:     # ③ C1-A1B1-A(直二面角,侧面取 A2 构造)
48:     M2 = tk.midpoint(vs["A1"], vs["B1"])
49:     vs["M2"] = M2
50:     d3 = tk.unit(tk.vsub(vs["C1"], M2))
51:     vs["u3"] = tk.vadd(M2, tk.vmul(d3, 0.22))
68:     for t, p in [("①", "a1"), ("②", "a2"), ("③", "r3"), ("④", "a4")]:
69:         labs.append({"point": p, "text": t})
70:     spec = tk.make_spec("锐二面角识别", "正三棱柱与内接正三棱锥", vs,
71:                         faces=faces, aux_lines=auxs, labels=labs,
72:                         camera=tk.auto_camera(vs, azim=33, elev=20))
73:     opts = {"A": "①②", "B": "①④", "C": "②③", "D": "③④"}
74:     q = "如图,正三棱柱$A_1B_1C_1-A_2B_2C_2$的上底面内接有正三棱锥$P-ABC$(顶点$P$在上底面上方,底面$ABC$位于上底面内且边长小于上底面)。记①二面角$P-AB-C$,②二面角$B-PA-C$,③二面角$C_1-A_1B_1-A$,④二面角$P-BC-B_1$。其中为锐二面角的是( )"
75:     return tk.item(spec, q, opts, "B", meta={"self_ref": True})

K26 Spec-to-Image Review

  • major/angle_arc
    四个二面角平面角由16条aux_lines线段硬搓,无弧度、半径过小,不呈教材标准角弧形态
    spec_fix: 改用angle_arc primitive绘制二面角平面角;若toolkit不支持则申请添加该primitive
  • major/label_missing
    渲染图中A,B,C,P,A1,B1,C1,A2,B2,C2共10个字母标签完全不可见,仅见①②③④
    spec_fix: 检查label坐标与深度偏移,确保字母标签位于可视区域且不被面片遮挡
  • minor/camera_or_view
    current视角orthoSize 2.941偏大,顶部角弧区域占比过小,细节难辨
    spec_fix: 缩小orthoSize至1.5左右或调整相机高度,使顶部标注区域占据更大画面比例
  • minor/aux_line_clutter
    角弧与辅助垂线同为红色#d9480f,密集交织于顶部小三角形区域
    spec_fix: 区分角弧与辅助垂线颜色,适当增大角弧半径,减少视觉重叠
What To Change
  • 角弧改用angle_arc primitive替代aux_lines硬搓
  • 修复字母标签可见性
  • 优化相机聚焦顶部标注区
Preferred Primitives
  • angle_arc
Remove/Simplify
  • 移除硬搓角弧的8-10条aux_lines,仅保留必要辅助垂线
  • 简化顶部冗余构造点
Label Fixes
  • 确保A,B,C,P及上下底顶点标签可见且不贴线
  • ①②③④对准对应角弧中心
Camera Fixes
  • current视角缩小orthoSize或降低高度放大顶面
  • detail_zoom保持并进一步聚焦
Toolkit/Renderer: 若toolkit无angle_arc primitive,需新增该primitive以支持标准二面角平面角绘制
representation: 几何体结构正确,但二面角平面角未使用标准角弧primitive,且核心字母标签缺失,不符合教材图示规范
readability: current图顶部细节过小,detail_zoom有所改善,但因角弧细碎、标签缺失,关键信息仍难定位
plan: 基本覆盖plan要求的三棱柱、内接锥体及四个二面角位置,但标注呈现方式不达标
next: spec_rewrite

acute_dihedral__S004

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
直三棱柱ABC-A1B1C1,底面ABC水平放置,∠ABC=60°,AB=5,BC=4,侧棱垂直底面且长6。在AA1上取点D使AD=2,连接DB、DC形成斜截面DBC。过A作AH⊥BC于H,连接DH,由三垂线定理DH⊥BC,故∠DHA为二面角D-BC-A的平面角。在棱柱内部、平面DAH内放置球心O、半径0.8的球,球与底面切于N(在AH上),与截面DBC切于M(在DH上)。另取AB中点P,连接DP;在平面DBC内标注∠DBC。隐藏侧棱与底边用虚线表示,球被遮挡部分画虚线。
Must Render
[
  "直三棱柱可见轮廓及侧棱AA1、BB1、CC1",
  "斜截面三角形DBC及其边DB、DC",
  "底面垂线AH、截面内垂线DH及平面角∠DHA的弧线标记",
  "球、球心O及切点M、N",
  "辅助线DP(P为AB中点)",
  "干扰角∠DBC的弧线标记",
  "虚线表示的隐藏棱、隐藏底边及球的隐藏轮廓"
]

Spec Summary

{
  "title": "锐二面角识别 S004",
  "meta": "直三棱柱斜截面与底面所成二面角",
  "camera": {
    "position": [
      10.372,
      7.911,
      15.522
    ],
    "target": [
      2.051,
      1.197,
      1.803
    ],
    "orthoSize": 4.79
  },
  "counts": {
    "vertices": 18,
    "faces": 6,
    "edges": 0,
    "aux_lines": 7,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 12
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.8011274721368609,
      "position": [
        2.5688843703050095,
        0.8011274721368609,
        3.9093187534841833
      ],
      "color": "#e5e5e5"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "A1",
      "text": "A1"
    },
    {
      "point": "B1",
      "text": "B1"
    },
    {
      "point": "C1",
      "text": "C1"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "A1",
    "B1",
    "C1",
    "D",
    "H",
    "P",
    "O",
    "M",
    "N",
    "h1",
    "h2",
    "h3",
    "b1",
    "b2",
    "b3"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C"
      ]
    },
    {
      "points": [
        "A1",
        "B1",
        "C1"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "B1",
        "A1"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "C1",
        "B1"
      ]
    },
    {
      "points": [
        "C",
        "A",
        "A1",
        "C1"
      ]
    },
    {
      "points": [
        "D",
        "B",
        "C"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "H"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "D",
        "H"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "D",
        "P"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "M"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "N"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "h1",
        "h2",
        "h3"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "b1",
        "b2",
        "b3"
      ],
      "dash": false,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 参数轻微随机化以满足铁律4,同时保持 sketch 几何结构
4:     AB = rng.uniform(4.8, 5.2)
5:     BC = rng.uniform(3.8, 4.2)

25:     nn = math.hypot(ny, nz)
26: 
27:     # 球心O、切点N、M(O在平面DAH内,N在AH上,M在DH上)
28:     zO = r * (AB * s3 * 0.5 + math.sqrt(3.0 * AB * AB * 0.25 + AD * AD)) / AD
29:     O = (A[0], r, zO)
30:     N = (A[0], 0.0, zO)
31:     M = (O[0], O[1] - r * ny / nn, O[2] - r * nz / nn)
35: 
36:     # 角度标记辅助点(用折线近似小弧线)
37:     def arc(v, u, w, rad):
38:         uu = tk.unit(tk.vsub(u, v))
39:         ww = tk.unit(tk.vsub(w, v))
40:         bb = tk.unit(tk.vadd(uu, ww))
41:         return tk.vadd(v, tk.vmul(uu, rad)), tk.vadd(v, tk.vmul(bb, rad)), tk.vadd(v, tk.vmul(ww, rad))
42: 
43:     vs["h1"], vs["h2"], vs["h3"] = arc(H, A, D, 0.3)
44:     vs["b1"], vs["b2"], vs["b3"] = arc(B, C, D, 0.3)
45: 
46:     faces = [
47:         {"points": ["A", "B", "C"]},
48:         {"points": ["A1", "B1", "C1"]},
49:         {"points": ["A", "B", "B1", "A1"]},
50:         {"points": ["B", "C", "C1", "B1"]},
53:     ]
54: 
55:     aux_lines = [
56:         tk.aux("A", "H", dash=False),
57:         tk.aux("D", "H", dash=False),
58:         tk.aux("D", "P", dash=False),
59:         tk.aux("O", "M", dash=False),
63:     ]
64: 
65:     prims = [tk.prim_sphere(r=r, center=O, color="#e5e5e5")]
66:     cam = tk.auto_camera(vs, primitives=prims, azim=rng.uniform(28, 36), elev=rng.uniform(18, 24), pad=1.5)
67: 
68:     spec = tk.make_spec(
69:         "锐二面角识别 S004",
70:         "直三棱柱斜截面与底面所成二面角",
71:         vs,
72:         faces=faces,
73:         aux_lines=aux_lines,
74:         primitives=prims,
75:         camera=cam,
76:         labels=["A", "B", "C", "A1", "B1", "C1", "D", "H", "P", "O", "M", "N"]
77:     )
78: 
79:     # 几何计算验证:平面角 ∠DHA 必为锐角
80:     AH = AB * s3 * 0.5
81:     angle_DHA = math.degrees(math.atan(AD / AH))
82: 
83:     options = {
84:         "A": "该二面角为锐二面角",
85:         "B": "该二面角为直二面角",
86:         "C": "该二面角为钝二面角,因为球心角∠MON为钝角",
87:         "D": "该二面角为锐二面角,其平面角应为∠DBC",
88:     }
89:     # 由构造可知 0 < angle_DHA < 90°,故恒为锐二面角
90:     ans = "A"
91:     return tk.item(spec, "如图,关于平面DBC与底面ABC所成二面角的判断,正确的是( )。", options, ans)

K26 Spec-to-Image Review

  • major/angle_arc
    ∠DHA与∠DBC用h1-h2-h3/b1-b2-b3三段折线近似,无显著弧线
    spec_fix: 改用toolkit的angle_arc primitive绘制显著角弧,移除折线近似
  • major/sphere_render
    球渲染为实心填充圆盘,无大圆轮廓及被遮挡虚线,遮挡内部线
    spec_fix: toolkit需支持球的大圆轮廓渲染,被遮挡半圆画虚线,避免实心填充
  • major/label_overlap
    A/N/O/P/M/H等标签在底部聚集严重重叠,三视角均未解决
    spec_fix: 调整相机elev/azim或增大orthoSize,为底部标签设置偏移避免重叠
  • minor/aux_line_clutter
    球半径0.8过大,实心填充遮挡AH/DH/DP等关键辅助线
    spec_fix: 适当减小球半径至0.4-0.5,或降低球心确保关键红色辅助线可见
What To Change
  • angle_arc primitive替换折线角标
  • 球渲染改为轮廓圆+虚线遮挡
  • 调整相机展开底部标签
Preferred Primitives
  • angle_arc
  • sphere(dashed_hidden_contour)
Remove/Simplify
  • 移除h1-h2-h3与b1-b2-b3折线近似点
  • 取消球实心填充
Label Fixes
  • A/N/O/P/M添加像素级偏移或换俯视角度
  • 放大orthoSize或降低球半径
Camera Fixes
  • 尝试更高elev或更侧azim拉开左下角标签间距
Toolkit/Renderer: toolkit需原生支持angle_arc primitive;sphere renderer应输出大圆轮廓并区分可见/遮挡虚线,禁止实心填充圆盘
representation: 棱柱与截面结构正确,但球渲染风格与角度标记方式不符合教材规范
readability: 底部标签极度拥挤,球实心填充遮挡关键线,三视角未能有效改善可读性
plan: 必须元素均已出现,但球虚线轮廓与显著角度弧线未满足plan要求
next: toolkit_patch

acute_dihedral__S006

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
一个三棱锥 S-ABC,侧棱 SA 垂直于底面直角三角形 ABC(∠ABC=90°)。底面内作斜边 AC 及高 BD⊥AC,连接 SD。侧棱 SB、SC 上分别取中点 E 和靠近 S 的三等分点 F,作截面 AEF 并延长交 BC 于 G。从 F、E 分别向底面作垂线 FF'、EE'(F' 在 AC 上,E' 在 AB 上)。画一个辅助球与侧面 SBC 切于 T、与底面切于 H。隐藏棱 SC 后段及底面 AC 后段用虚线。
Must Render
[
  "三棱锥 S-ABC 及 SA⊥底面的直角关系",
  "底面高 BD 及连线 SD",
  "截面 AEF 及其与底面的交线 AG",
  "投影线 EE'、FF'",
  "辅助球及其切点 T、H",
  "隐藏线虚线(SC 后段、AC 后段等)"
]

Spec Summary

{
  "title": "锐二面角识别",
  "meta": "acute_dihedral_S006; aux_lines include BD, SD, AG, EE', FF', OT, OH; hidden FC and DC dashed",
  "camera": {
    "position": [
      11.025,
      8.467,
      16.326
    ],
    "target": [
      1.685,
      0.503,
      0.56
    ],
    "orthoSize": 5.537
  },
  "counts": {
    "vertices": 19,
    "faces": 4,
    "edges": 11,
    "aux_lines": 11,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 13
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.5656815170458908,
      "position": [
        1.46076362067655,
        0.5656815170458908,
        1.3863007589680012
      ],
      "color": "#93c5fd"
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "E'",
      "text": "E'"
    },
    {
      "point": "F'",
      "text": "F'"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "H",
      "text": "H"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "S",
    "D",
    "E",
    "F",
    "G",
    "E'",
    "F'",
    "O",
    "H",
    "T",
    "A1",
    "A2",
    "A3",
    "B1",
    "B2",
    "B3"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C"
      ],
      "color": "#e0f2fe",
      "opacity": 0.18
    },
    {
      "points": [
        "S",
        "A",
        "B"
      ],
      "color": "#f8fafc",
      "opacity": 0.22
    },
    {
      "points": [
        "S",
        "B",
        "C"
      ],
      "color": "#f1f5f9",
      "opacity": 0.2
    },
    {
      "points": [
        "S",
        "C",
        "A"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    }
  ],
  "edges_sample": [
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "B"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "S",
        "F"
      ]
    },
    {
      "points": [
        "F",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "E"
      ],
      "color": "#b45309"
    },
    {
      "points": [
        "E",
        "F"
      ],
      "color": "#b45309"
    },
    {
      "points": [
        "F",
        "A"
      ],
      "color": "#b45309"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "B",
        "D"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "S",
        "D"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "A",
        "G"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "E",
        "E'"
      ],
      "dash": true,
      "color": "#2563eb"
    },
    {
      "points": [
        "F",
        "F'"
      ],
      "dash": true,
      "color": "#2563eb"
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#64748b"
    },
    {
      "points": [
        "O",
        "T"
      ],
      "dash": true,
      "color": "#64748b"
    },
    {
      "points": [
        "A1",
        "A3"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "A3",
        "A2"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B1",
        "B3"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B3",
        "B2"
      ],
      "dash": false,
      "color": "#111827"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "A",
        "E",
        "F"
      ],
      "color": "#fde68a",
      "opacity": 0.36
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 尺寸随机化:底面为直角三角形 ABC,SA 垂直底面于 A
5:     a = rng.uniform(2.8, 3.8)   # AB

22:          S[2] * 2.0 / 3.0 + C[2] / 3.0)
23: 
24:     # 截面 AEF 延长后与直线 BC 交于 G
25:     G = (0.0, 0.0, -c)
26: 
27:     # 从 E、F 向底面作垂线,垂足分别在 AB、AC 上
28:     Ep = (E[0], 0.0, E[2])
29:     Fp = (F[0], 0.0, F[2])
30: 
31:     # 辅助球:与底面 y=0 切于 H,与侧面 SBC 切于 T
32:     r = min(a, c, h) * rng.uniform(0.16, 0.20)
33:     s = math.sqrt(a * a + h * h)
34:     gamma = rng.uniform(0.36, 0.44)
35:     oz = gamma * c
58:     }
59: 
60:     faces = [
61:         tk.face(["A", "B", "C"], color="#e0f2fe", opacity=0.18),
62:         tk.face(["S", "A", "B"], color="#f8fafc", opacity=0.22),
63:         tk.face(["S", "B", "C"], color="#f1f5f9", opacity=0.20),
64:         tk.face(["S", "C", "A"], color="#f8fafc", opacity=0.16),
66: 
67:     # 主棱与隐藏线:SC 后段、AC 后段显式用虚线
68:     edges = [
69:         tk.edge("S", "A"),
70:         tk.edge("S", "B"),
71:         tk.edge("A", "B"),
72:         tk.edge("B", "C"),
80:     ]
81: 
82:     # 关键辅助线必须进入 aux_lines:BD、SD、AG、EE'、FF'、球半径 OT/OH、直角标记
83:     aux_lines = [
84:         tk.aux("B", "D", dash=True, color="#d9480f"),
85:         tk.aux("S", "D", dash=True, color="#d9480f"),
86:         tk.aux("A", "G", dash=True, color="#d9480f"),
87:         tk.aux("E", "E'", dash=True, color="#2563eb"),
95:     ]
96: 
97:     aux_faces = [
98:         tk.face(["A", "E", "F"], color="#fde68a", opacity=0.36),
99:     ]
100: 
101:     primitives = [
102:         tk.prim_sphere(r=r, center=O, color="#93c5fd"),
103:     ]
104: 
105:     cam = tk.auto_camera(
106:         vs,
107:         primitives=primitives,
108:         azim=rng.uniform(27.0, 36.0),
109:         elev=rng.uniform(18.0, 25.0),
110:         pad=1.50,
111:     )
112: 
113:     spec = tk.make_spec(
114:         title="锐二面角识别",
115:         meta="acute_dihedral_S006; aux_lines include BD, SD, AG, EE', FF', OT, OH; hidden FC and DC dashed",
116:         vertices=vs,
117:         faces=faces,
118:         edges=edges,
119:         aux_lines=aux_lines,
120:         aux_faces=aux_faces,
121:         primitives=primitives,
122:         labels=["S", "A", "B", "C", "D", "E", "F", "G", "E'", "F'", "O", "T", "H"],
123:         camera=cam,
124:     )
125: 
126:     # 用几何量判定唯一锐二面角
127:     acute_sbc_abc = tk.angle_deg(tk.vsub(A, B), tk.vsub(S, B))
128: 
129:     n_sab = tk.cross(tk.vsub(A, S), tk.vsub(B, S))
130:     n_sbc = tk.cross(tk.vsub(B, S), tk.vsub(C, S))
131:     ang_sab_sbc = tk.angle_deg(n_sab, n_sbc)
132:     ang_sab_sbc = min(ang_sab_sbc, 180.0 - ang_sab_sbc)
133: 
134:     n_sac = tk.cross(tk.vsub(A, S), tk.vsub(C, S))
135:     n_abc = tk.cross(tk.vsub(B, A), tk.vsub(C, A))
136:     ang_sac_abc = tk.angle_deg(n_sac, n_abc)
137:     ang_sac_abc = min(ang_sac_abc, 180.0 - ang_sac_abc)
138: 
139:     n_aef = tk.cross(tk.vsub(E, A), tk.vsub(F, A))
140:     small_aef_sab = tk.angle_deg(n_aef, n_sab)
141:     small_aef_sab = min(small_aef_sab, 180.0 - small_aef_sab)
142:     obtuse_aef_sab = 180.0 - small_aef_sab
143: 
144:     options = {
145:         "A": "平面 SBC 与平面 ABC 所成的二面角",
146:         "B": "平面 SAB 与平面 SBC 所成二面角",
147:         "C": "平面 SAC 与底面所成二面角",
148:         "D": "截面 AEF 与平面 SAB 所成二面角",
149:     }
150: 
151:     angle_by_option = {
152:         "A": acute_sbc_abc,
153:         "B": ang_sab_sbc,
154:         "C": ang_sac_abc,
155:         "D": obtuse_aef_sab,
156:     }
157:     answer = [k for k in "ABCD" if angle_by_option[k] < 89.0][0]
158: 
159:     return tk.item(
160:         spec,
161:         "如图,在三棱锥 S-ABC 中,SA⊥平面 ABC,∠ABC=90°,图中给出了高线 BD、截面 AEF 及辅助球等构造。下列二面角中,属于锐二面角的是?",
162:         options,
163:         answer,
164:     )

K26 Spec-to-Image Review

  • major/sphere_render
    Sphere drawn as dense wireframe with grid lines.
    spec_fix: Use smooth sphere; reduce renderer wireframe segments.
  • major/plan_coverage
    G misplaced, not on BC line in oblique view.
    spec_fix: Fix geometry: compute plane AEF intersection with line BC for G.
  • minor/label_overlap
    F,E,G labels crowded near top in main view.
    spec_fix: Increase label offsets or adjust camera to separate F, E, G.
  • minor/aux_line_clutter
    Sphere mesh and dashed aux lines make center busy.
    spec_fix: Simplify sphere to outline+one great circle; tone down aux line colors.
What To Change
  • Fix G computation so plane AEF intersects line BC and G lies on BC.
  • Reduce sphere wireframe density or switch to smooth sphere primitive.
Preferred Primitives
  • sphere
Remove/Simplify
  • Remove dense sphere mesh lines, keep only center O and tangent radii.
  • Simplify right-angle tick marks if they overlap with edges.
Label Fixes
  • Reposition G to actual BC intersection; separate F,E,G labels.
Camera Fixes
  • Adjust main camera azimuth to avoid G overlapping E,F region.
Toolkit/Renderer: Patch renderer sphere primitive to support smooth or low-segment wireframe.
representation: Sphere renders as dense mesh; section and projections good. G geometry wrong.
readability: Sphere mesh clutters main; oblique helps base. G misplaced.
plan: Most elements ok, but G not on BC; tangency points obscured by mesh.
next: spec_rewrite

acute_dihedral__S008

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
绘制斜三棱台 ABC-A₁B₁C₁,下底 △ABC 水平放置且为锐角三角形,上底 △A₁B₁C₁ 相对下底向右侧平移并缩小。侧棱 AA₁、BB₁、CC₁ 均向右上倾斜且长度不等。在棱 BC 上取中点 M,在 B₁C₁ 上取中点 M₁,连接 MM₁ 作为侧面 BCC₁B₁ 的斜高(辅助线);过 A₁ 作底面 ABC 的垂线,垂足 H 落在 △ABC 外部,用虚线画出 A₁H 及其底面投影线 HK。使侧面 BCC₁B₁ 与底面成锐二面角,侧面 ABB₁A₁ 与底面成钝二面角,侧面 ACC₁A₁ 与底面接近直二面角。
Must Render
[
  "斜三棱台 ABC-A₁B₁C₁ 的上下底面、三条可见侧棱及被遮挡侧棱虚线",
  "斜高 MM₁ 实线及垂线 A₁H、投影线 HK 虚线",
  "下底面 △ABC 的水平放置基线与顶点标注"
]

Spec Summary

{
  "title": "斜三棱台",
  "meta": "斜三棱台二面角",
  "camera": {
    "position": [
      6.81,
      5.29,
      8.68
    ],
    "target": [
      0.627,
      0.935,
      -0.151
    ],
    "orthoSize": 2.979
  },
  "counts": {
    "vertices": 10,
    "faces": 5,
    "edges": 0,
    "aux_lines": 3,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 10
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "A1",
      "text": "A1"
    },
    {
      "point": "B1",
      "text": "B1"
    },
    {
      "point": "C1",
      "text": "C1"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "M1",
      "text": "M1"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "K",
      "text": "K"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "A1",
    "B1",
    "C1",
    "M",
    "M1",
    "H",
    "K"
  ],
  "faces_sample": [
    {
      "points": [
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "A1",
        "B1",
        "C1"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "B1",
        "A1"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "C1",
        "B1"
      ]
    },
    {
      "points": [
        "C",
        "A",
        "A1",
        "C1"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "M",
        "M1"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "A1",
        "H"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "H",
        "K"
      ],
      "dash": true,
      "color": "#495057"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 1. 随机构造参数(正三棱台底面 + 斜向平移上底)
4:     r = rng.uniform(1.8, 2.2)
5:     s = rng.uniform(0.35, 0.50)

8:     tz = math.sqrt(3.0) * tx - r * (1.0 - s)
9: 
10:     # 2. 用 frustum 生成正三棱台骨架,再改造成斜三棱台
11:     vs, faces = tk.frustum(3, r_bot=r, r_top=s * r, h=h)
12:     rename = {"D": "A1", "E": "B1", "F": "C1"}
13:     vs = {rename.get(k, k): v for k, v in vs.items()}
14:     for f in faces:
15:         f["points"] = [rename.get(p, p) for p in f["points"]]
16: 
17:     # 上底整体斜向平移,使侧棱不等长
18:     for k in ("A1", "B1", "C1"):
40: 
41:     # 5. 辅助线:MM₁ 实线(斜高),A₁H、HK 虚线
42:     aux_lines = [
43:         tk.aux("M", "M1", dash=False, color="#2b8a3e"),
44:         tk.aux("A1", "H", dash=True, color="#495057"),
45:         tk.aux("H", "K", dash=True, color="#495057"),
46:     ]
47: 
48:     # 6. 组装场景
49:     camera = tk.auto_camera(vs, azim=35.0, elev=22.0)
50:     spec = tk.make_spec(
51:         "斜三棱台",
52:         "斜三棱台二面角",
53:         vs,
54:         faces=faces,
55:         aux_lines=aux_lines,
56:         labels="auto",
57:         camera=camera,
58:     )
59: 
60:     # 7. 选项:B 为正确答案(BCC₁B₁ 锐二面角),其余为常见误判
61:     opts = {
62:         "A": "侧面 ABB₁A₁ 与底面 ABC 所成的二面角是锐二面角",
63:         "B": "侧面 BCC₁B₁ 与底面 ABC 所成的二面角是锐二面角",
64:         "C": "侧面 ACC₁A₁ 与底面 ABC 所成的二面角是锐二面角",
65:         "D": "侧面 ACC₁A₁ 与底面 ABC 所成的二面角是钝二面角",
66:     }
67:     question = "如图,在斜三棱台 ABC-A₁B₁C₁ 中,观察各侧面与底面 ABC 所成的二面角,下列判断正确的是?"
68:     return tk.item(spec, question, opts, "B")

K26 Spec-to-Image Review

  • major/angle_arc
    图中无任何二面角平面角或角弧标记,三个侧面与底面的二面角大小完全不可读
    spec_fix: 在棱BC、AB、AC处添加二面角平面角辅助线,并用angle_arc或粗aux_lines标记出锐/直/钝角
  • major/plan_coverage
    plan要求区分锐/钝/直二面角,但spec仅画出棱台线框,未将二面角关系可视化
    spec_fix: 补充各侧面与底面交线处的垂线及平面角,使二面角类型可直观判断
  • minor/aux_line_clutter
    HK投影线几何意义不明,K点未在代码中定义,与M点位置接近易混淆
    spec_fix: 明确K为H向底面某棱作垂线的垂足并标注直角,或删除HK改用更清晰的二面角平面角构造
  • minor/label_overlap
    current视角中K与M标签距离过近,oblique_plus45中K与M亦相邻,易误读
    spec_fix: 调整K点位置或camera角度,拉开K与M的图上距离,避免标签重叠
What To Change
  • 在三条侧棱处分别构造二面角平面角(面内垂直于棱的射线)
  • 添加显著的角度标记或angle_arc以区分锐/直/钝二面角
  • 明确或移除意义不明的HK辅助线
Preferred Primitives
  • angle_arc
Remove/Simplify
  • 移除冗余的HK投影线,或将其改为二面角平面角的可识别边
Label Fixes
  • 拉开K与M标签间距
  • 为二面角平面角顶点新增标签如P、Q
Camera Fixes
  • 保持双视角,但确保二面角标记在至少一个视角中无遮挡
Toolkit/Renderer: 建议toolkit增加angle_arc primitive,用于显著标注二面角平面角及各类空间角
representation: 三棱台线框结构基本正确,但完全缺失二面角平面角与角度标记,无法支撑识别任务。
readability: 双视角有助于观察侧面形态,但因缺少角度标记,无法判断各侧面与底面的二面角大小。
plan: 未落实plan中锐/钝/直二面角的可视化表达,关键几何关系在图中不可定位。
next: spec_rewrite

acute_dihedral__S010

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
三棱锥 P-ABC,底面 ABC 为锐角不等边三角形;顶点 P 的垂足 H 落在三角形外部 AB 边远离 C 的一侧,PH 画虚线。取 AB 中点 M 作截面 PMC。内部作内切球,球心 I,半径 IG、ID、IE 画虚线。对 BC 边构造二面角 P-BC-A 平面角:HR⊥BC 于 R 连 PR。隐藏线为球背侧轮廓。
Must Render
[
  "三棱锥 P-ABC 的可见侧棱与底面边",
  "顶点 P 到底面的垂足 H 及虚线 PH",
  "AB 中点 M 与截面三角形 PMC",
  "内切球及其球心 I、切点 G/D/E 和虚线半径 IG/ID/IE",
  "BC 边的二面角平面角构造线 HR 与 PR",
  "球背侧及被遮挡棱的虚线轮廓"
]

Spec Summary

{
  "title": "三棱锥与二面角",
  "meta": "acute_dihedral_S010",
  "camera": {
    "position": [
      12.521,
      7.208,
      14.323
    ],
    "target": [
      2.285,
      0.628,
      -0.077
    ],
    "orthoSize": 4.952
  },
  "counts": {
    "vertices": 11,
    "faces": 5,
    "edges": 6,
    "aux_lines": 6,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 11
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.995187276551947,
      "position": [
        2.584317342388962,
        0.995187276551947,
        -0.046180835191962366
      ],
      "color": "#f5f5f4"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "R",
      "text": "R"
    },
    {
      "point": "I",
      "text": "I"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "P",
    "H",
    "M",
    "R",
    "I",
    "G",
    "D",
    "E"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "A",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "B",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "C",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "M",
        "C"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "C"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "H"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "H",
        "R"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "P",
        "R"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "I",
        "G"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "I",
        "D"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "I",
        "E"
      ],
      "dash": true,
      "color": "#555555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 参数化构造:底面 ABC 在 y=0,AB 沿 x 轴,H 在 A 外侧,P 在 H 正上方
5:     while True:

42:             continue
43: 
44:         # 内切球半径与球心(底面 y=0,侧面 PAB 恰在 z=0,解析可算)
45:         L2 = math.sqrt((h * cz) ** 2 + ((d + b) * cz) ** 2 + (h * (b - cx)) ** 2)
46:         L3 = math.sqrt((h * cz) ** 2 + (d * cz) ** 2 + (h * cx) ** 2)
47:         denom = L2 + L3 + d * cz + h * b
48:         if denom < 1e-6:
84:     }
85: 
86:     faces = [
87:         {"points": ["A", "B", "C"]},
88:         {"points": ["P", "A", "B"]},
89:         {"points": ["P", "B", "C"]},
90:         {"points": ["P", "C", "A"]},
92:     ]
93: 
94:     edges = [
95:         tk.edge("A", "B"), tk.edge("B", "C"), tk.edge("C", "A"),
96:         tk.edge("P", "A"), tk.edge("P", "B"), tk.edge("P", "C"),
97:     ]
98: 
99:     aux_lines = [
100:         tk.aux("P", "H", dash=True, color="#555555"),
101:         tk.aux("H", "R", dash=False, color="#d9480f"),
102:         tk.aux("P", "R", dash=False, color="#d9480f"),
103:         tk.aux("I", "G", dash=True, color="#555555"),
106:     ]
107: 
108:     primitives = [tk.prim_sphere(r=r, center=I, color="#f5f5f4")]
109: 
110:     camera = tk.auto_camera(vs, primitives, azim=rng.uniform(26.0, 38.0), elev=rng.uniform(18.0, 26.0))
111: 
112:     spec = tk.make_spec(
113:         "三棱锥与二面角",
114:         "acute_dihedral_S010",
115:         vs,
116:         faces=faces,
117:         edges=edges,
118:         aux_lines=aux_lines,
119:         primitives=primitives,
120:         labels="auto",
121:         camera=camera,
122:     )
123: 
124:     question = "如图,在三棱锥 P-ABC 中,PH⊥平面 ABC 于 H,截面 PMC 与内切球等辅助元素如图所示。观察二面角 P-BC-A 的平面角,可判断该二面角属于( )"
125: 
126:     opts = ["锐二面角", "直二面角", "钝二面角", "平二面角"]
127:     rng.shuffle(opts)
128:     letters = ["A", "B", "C", "D"]
129:     options = {letters[i]: opts[i] for i in range(4)}
130:     answer = [k for k, v in options.items() if v == "锐二面角"][0]
131: 
132:     return tk.item(spec, question, options, answer)

K26 Spec-to-Image Review

  • major/angle_arc
    二面角平面角∠PRH未标弧线,无法直观判断锐钝
    spec_fix: spec添加tk.angle_arc('P','R','H'),并补充HR⊥BC直角标记
  • major/plan_coverage
    faces含PMC但edges缺PM、MC,截面三角形边线未画出
    spec_fix: edges列表补充tk.edge('P','M')与tk.edge('M','C'),可见性设为实线
  • major/renderer_support
    sphere primitive渲染为带经纬线实心球,如地球仪,遮挡切点
    spec_fix: toolkit应支持sphere仅绘轮廓大圆与必要虚线截面圆,去除网格
  • minor/label_overlap
    current视图中M标签位于球体中部,未贴合AB中点底面位置
    spec_fix: 调整M标签锚点至AB中点,设置偏移避免与球体及PC棱重叠
  • minor/other
    HR与PR构造线已画出,但缺少HR⊥BC的直角符号标识
    spec_fix: 在R点添加直角标记primitive或aux_lines表示HR垂直于BC
What To Change
  • 补充PMC截面边PM与MC
  • 添加二面角angle_arc及直角标记
  • 修正球体渲染方式
Preferred Primitives
  • angle_arc
  • right_angle
  • section_circle
Remove/Simplify
  • 去除球体多余经纬网格线
  • 简化或隐藏非必要辅助虚线
Label Fixes
  • M锚定AB中点底面
  • D/E/G避免球体重叠
Camera Fixes
  • oblique视角target对齐模型中心
  • 确保底面ABC与R点清晰
Toolkit/Renderer: renderer需支持sphere仅绘大圆/截面圆;增加angle_arc与right_angle primitive支持
representation: 三棱锥框架正确,但缺截面边、缺角度弧线、球体渲染过密,几何表达不完整
readability: 球体网格严重干扰线框阅读,标签M位置偏离,角度缺失导致无法直接判断
plan: 遗漏截面PMC边线与二面角平面角标记,与plan要求的完整构造不符
next: spec_rewrite

acute_dihedral__S013

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue minor hold

Plan / Must Render

题型 锐二面角识别
难度 L2
Scene
绘制三棱锥 S-ABC,底面 ABC 水平放置,顶点 S 位于底面左上方空间。在棱 BC 上取垂足点 D,使底面内线段 AD⊥BC,侧面 SBC 内线段 SD⊥BC,AD 与 SD 构成二面角 S-BC-A 的平面角 ∠SDA,用橙色圆弧高亮标注。在棱 AC 上取点 E 构造钝二面角平面角 ∠SEC 作为干扰。底面 ABC 浅灰填充,被遮挡棱画虚线,两平面交线加粗显示。
Must Render
[
  "三棱锥 S-ABC 的可见棱 SA、SB、SC 与水平底面 ABC",
  "棱 BC 上的辅助点 D 及垂线段 AD、SD",
  "∠SDA 的橙色圆弧标注",
  "棱 AC 上的辅助点 E 及钝角 ∠SEC 的辅助线作为干扰"
]

Spec Summary

{
  "title": "三棱锥S-ABC",
  "meta": "锐二面角识别",
  "camera": {
    "position": [
      6.444,
      4.869,
      8.657
    ],
    "target": [
      0.681,
      0.182,
      0.006
    ],
    "orthoSize": 2.917
  },
  "counts": {
    "vertices": 24,
    "faces": 4,
    "edges": 0,
    "aux_lines": 20,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 6
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "S",
    "D",
    "E",
    "a0",
    "a1",
    "a2",
    "a3",
    "a4",
    "a5",
    "a6",
    "a7",
    "a8",
    "b0",
    "b1",
    "b2",
    "b3",
    "b4",
    "b5",
    "b6",
    "b7",
    "b8"
  ],
  "faces_sample": [
    {
      "points": [
        "C",
        "B",
        "A"
      ],
      "color": "#e5e5e5",
      "opacity": 0.35
    },
    {
      "points": [
        "A",
        "B",
        "S"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "S"
      ]
    },
    {
      "points": [
        "C",
        "A",
        "S"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "D"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "S",
        "D"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "S",
        "E"
      ],
      "dash": false,
      "color": "#555"
    },
    {
      "points": [
        "B",
        "C"
      ],
      "dash": false,
      "color": "#000"
    },
    {
      "points": [
        "a0",
        "a1"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a1",
        "a2"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a2",
        "a3"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a3",
        "a4"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a4",
        "a5"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a5",
        "a6"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a6",
        "a7"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "a7",
        "a8"
      ],
      "dash": false,
      "color": "#f97316"
    },
    {
      "points": [
        "b0",
        "b1"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b1",
        "b2"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b2",
        "b3"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b3",
        "b4"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b4",
        "b5"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b5",
        "b6"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b6",
        "b7"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "b7",
        "b8"
      ],
      "dash": false,
      "color": "#9ca3af"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     r = rng.uniform(1.6, 2.2)
4:     h = rng.uniform(1.8, 2.4)
5:     zs = -r * rng.uniform(0.25, 0.45)

7:     rot = rng.uniform(30.0, 45.0)
8: 
9:     vs, faces = tk.pyramid(3, r=r, h=h, apex="S")
10:     vs["S"] = (0.0, h, zs)
11:     vs = {k: tk.rot_y(v, rot) for k, v in vs.items()}
12: 
13:     A, B, C, S = vs["A"], vs["B"], vs["C"], vs["S"]
17:     vs["E"] = E
18: 
19:     faces[0]["color"] = "#e5e5e5"
20:     faces[0]["opacity"] = 0.35
21: 
22:     aux = [
23:         tk.aux("A", "D", dash=False, color="#333"),
24:         tk.aux("S", "D", dash=False, color="#333"),
27:     ]
28: 
29:     def arc(c, p1, p2, col, pre):
30:         u = tk.unit(tk.vsub(p1, c))
31:         v = tk.unit(tk.vsub(p2, c))
32:         ang = math.radians(tk.angle_deg(u, v))
33:         if tk.norm(tk.cross(u, v)) < 1e-6 or ang < 1e-6:
34:             return
35:         ca, sa = math.cos(ang), math.sin(ang)
36:         perp = tk.unit(tk.vsub(v, tk.vmul(u, ca)))
45:             aux.append(tk.aux(pts[i], pts[i + 1], dash=False, color=col))
46: 
47:     arc(D, A, S, "#f97316", "a")
48:     arc(E, C, S, "#9ca3af", "b")
49: 
50:     DA = tk.vsub(A, D)
51:     DS = tk.vsub(S, D)
52:     cos_val = tk.dot(DA, DS) / (tk.norm(DA) * tk.norm(DS) + 1e-12)
53:     if cos_val > 0.02:
54:         ans, ans_txt = "A", "锐二面角"
55:     elif cos_val < -0.02:
56:         ans, ans_txt = "B", "钝二面角"
57:     else:
58:         ans, ans_txt = "C", "直二面角"
59: 
60:     cam = tk.auto_camera(vs, azim=rng.uniform(28, 42), elev=rng.uniform(18, 26))
61:     spec = tk.make_spec("三棱锥S-ABC", "锐二面角识别", vs, faces=faces, aux_lines=aux,
62:                         labels=["A", "B", "C", "S", "D", "E"], camera=cam)
63:     opts = {"A": "锐二面角", "B": "钝二面角", "C": "直二面角", "D": "无法确定"}
64:     q = "如图,在三棱锥S-ABC中,D为棱BC上一点,AD⊂平面ABC,SD⊂平面SBC,且AD⊥BC,SD⊥BC。图中橙色圆弧标注的∠SDA是二面角S-BC-A的平面角。观察其张口程度,该二面角属于( )"
65:     return tk.item(spec, q, opts, ans, answer_text=ans_txt)

K26 Spec-to-Image Review

  • fatal/angle_arc
    current视图中主标注∠SDA的橙色圆弧在D点不可见,而干扰角∠SEC的圆弧在E点异常醒目,主次完全颠倒。
    spec_fix: 调整相机使D点正对观察者,确保角ADS两边及橙色圆弧清晰可见;若renderer未正确渲染灰色,需检查#9ca3af输出。
  • major/label_overlap
    current视图中A标签与S几乎重叠,导致底面顶点A的空间位置难以辨认。
    spec_fix: 将A标签向左下方偏移,远离S;D标签适当下移避免与BC贴线。
  • minor/plan_coverage
    plan要求两平面交线BC加粗,但spec中BC为普通#000实线,渲染后未突出。
    spec_fix: 将BC的aux_line线宽设为加粗(如2.0px),或改用edges显式加粗绘制。
  • minor/camera_or_view
    oblique_minus45虽稍好,但D处圆弧仍过小,current视角下AD与SD夹角被严重压缩。
    spec_fix: 采用更高视角(elev>30)并绕Y轴旋转,使底面AD与侧棱SD展开,平面角开口清晰。
What To Change
  • 相机改为俯视偏右前方,正对D点展开∠SDA
  • BC线宽加粗
  • A标签远离S
Preferred Primitives
  • angle_arc
Remove/Simplify
  • 干扰角∠SEC的圆弧改用更淡灰色或虚线,降低视觉权重
Label Fixes
  • A标签左移避免与S重叠
  • D标签置于角开口外侧
Camera Fixes
  • 提升仰角并右旋,使AD、SD及橙色圆弧无遮挡
Toolkit/Renderer: 检查renderer是否正确渲染#9ca3af灰色aux_line,若灰色被错误映射为橙色则需toolkit修复。
representation: 三棱锥结构与底面填充基本正确,但edges=0依赖面边界,交线BC未加粗。
readability: current视图主标注不可读,oblique_minus45略有改善但仍不足;E处干扰弧喧宾夺主。
plan: 必须渲染的∠SDA橙色圆弧在图中缺失或不可见,核心教学信息丢失。
next: spec_rewrite

acute_dihedral__S014

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 锐二面角识别
难度 L2
Scene
直圆锥顶点朝上,底面为水平圆O。一个不过圆心的斜截面截圆锥得到椭圆截线,截面与底面相交于直线l(棱)。底面圆心O向l作垂线,垂足为C;在截面内过C作CE⊥l交椭圆于E,∠OCE为二面角平面角。图中放置一个Dandelin球,球心O′,与截面切于点F,与圆锥切于一水平圆;从O′向底面作投影线O′H。底面圆后弧、圆锥后母线及切圆后弧均画虚线。
Must Render
[
  "直圆锥及底面圆O",
  "斜截面及其椭圆截线",
  "交线(棱)l与垂足C",
  "底面内垂线OC与截面内垂线CE",
  "Dandelin球、切点F及球心投影线O′H",
  "底面圆后弧、圆锥后母线、切圆后弧等虚线隐藏边"
]

Spec Summary

{
  "title": "圆锥斜截面二面角",
  "meta": "acute_dihedral",
  "camera": {
    "position": [
      4.559,
      6.681,
      11.2
    ],
    "target": [
      0.034,
      0.79,
      -0.0
    ],
    "orthoSize": 3.474
  },
  "counts": {
    "vertices": 87,
    "faces": 0,
    "edges": 40,
    "aux_lines": 42,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 6
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.6631139430133062,
      "position": [
        0.0,
        1.8786697868014728,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "O'",
      "text": "O'"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "S",
      "text": "S"
    }
  ],
  "vertex_names": [
    "O",
    "C",
    "S",
    "E",
    "O'",
    "F",
    "B1",
    "B2",
    "H",
    "p0",
    "p1",
    "p2",
    "p3",
    "p4",
    "p5",
    "p6",
    "p7",
    "p8",
    "p9",
    "p10",
    "p11",
    "p12",
    "p13",
    "p14",
    "p15",
    "m0",
    "m1",
    "m2",
    "m3",
    "m4",
    "m5",
    "m6",
    "m7",
    "q0",
    "q1",
    "q2",
    "q3",
    "q4",
    "q5",
    "q6",
    "q7",
    "q8",
    "q9",
    "q10",
    "q11",
    "ef0",
    "eb0",
    "ef1",
    "eb1",
    "ef2",
    "eb2",
    "ef3",
    "eb3",
    "ef4",
    "eb4",
    "ef5",
    "eb5",
    "ef6",
    "eb6",
    "ef7",
    "eb7",
    "ef8",
    "eb8",
    "ef9",
    "eb9",
    "ef10",
    "eb10",
    "ef11",
    "eb11",
    "ef12",
    "eb12",
    "ef13",
    "eb13",
    "ef14",
    "eb14",
    "ef15",
    "eb15",
    "ef16",
    "eb16",
    "ef17",
    "eb17",
    "ef18",
    "eb18",
    "ef19",
    "eb19",
    "ef20",
    "eb20"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "p0",
        "p1"
      ]
    },
    {
      "points": [
        "p1",
        "p2"
      ]
    },
    {
      "points": [
        "p2",
        "p3"
      ]
    },
    {
      "points": [
        "p3",
        "p4"
      ]
    },
    {
      "points": [
        "p4",
        "p5"
      ]
    },
    {
      "points": [
        "p5",
        "p6"
      ]
    },
    {
      "points": [
        "p6",
        "p7"
      ]
    },
    {
      "points": [
        "p7",
        "p8"
      ]
    },
    {
      "points": [
        "m0",
        "S"
      ]
    },
    {
      "points": [
        "m1",
        "S"
      ]
    },
    {
      "points": [
        "m2",
        "S"
      ]
    },
    {
      "points": [
        "m3",
        "S"
      ]
    },
    {
      "points": [
        "m4",
        "S"
      ]
    },
    {
      "points": [
        "q0",
        "q1"
      ]
    },
    {
      "points": [
        "q1",
        "q2"
      ]
    },
    {
      "points": [
        "q2",
        "q3"
      ]
    },
    {
      "points": [
        "q3",
        "q4"
      ]
    },
    {
      "points": [
        "q4",
        "q5"
      ]
    },
    {
      "points": [
        "q5",
        "q6"
      ]
    },
    {
      "points": [
        "ef0",
        "ef1"
      ]
    },
    {
      "points": [
        "ef1",
        "ef2"
      ]
    },
    {
      "points": [
        "ef2",
        "ef3"
      ]
    },
    {
      "points": [
        "ef3",
        "ef4"
      ]
    },
    {
      "points": [
        "ef4",
        "ef5"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "p8",
        "p9"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p9",
        "p10"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p10",
        "p11"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p11",
        "p12"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p12",
        "p13"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p13",
        "p14"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p14",
        "p15"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p15",
        "p0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "m5",
        "S"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "m6",
        "S"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "m7",
        "S"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "q6",
        "q7"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "q7",
        "q8"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "q8",
        "q9"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "q9",
        "q10"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "q10",
        "q11"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "q11",
        "q0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb0",
        "eb1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb1",
        "eb2"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb2",
        "eb3"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb3",
        "eb4"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb4",
        "eb5"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb5",
        "eb6"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb6",
        "eb7"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb7",
        "eb8"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb8",
        "eb9"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb9",
        "eb10"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb10",
        "eb11"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb11",
        "eb12"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb12",
        "eb13"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb13",
        "eb14"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb14",
        "eb15"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb15",
        "eb16"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb16",
        "eb17"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb17",
        "eb18"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb18",
        "eb19"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "eb19",
        "eb20"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "B1",
        "B2"
      ],
      "dash": false,
      "color": "#2563eb"
    },
    {
      "points": [
        "O",
        "C"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C",
        "E"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "O'",
        "F"
      ],
      "dash": false,
      "color": "#555555"
    },
    {
      "points": [
        "O'",
        "H"
      ],
      "dash": false,
      "color": "#555555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.choice([2.0, 2.2, 2.4])
4:     H = rng.choice([2.8, 3.0, 3.2])
5:     c = math.hypot(R, H)

89:         es.append(tk.edge("ef%d" % i, "ef%d" % (i + 1)))
90:         ax.append(tk.aux("eb%d" % i, "eb%d" % (i + 1)))
91:     # 交线(棱)l:保留在 edges 并显式写入 aux_lines 供审查识别
92:     es.append(tk.edge("B1", "B2"))
93:     ax.append(tk.aux("B1", "B2", dash=False, color="#2563eb"))
94:     # 关键辅助线 OC、CE 显式写入 aux_lines
95:     ax.append(tk.aux("O", "C", dash=False, color="#d9480f"))
96:     ax.append(tk.aux("C", "E", dash=False, color="#d9480f"))
97:     # Dandelin 球相关辅助线
98:     ax.append(tk.aux("O'", "F", dash=False, color="#555555"))
99:     ax.append(tk.aux("O'", "H", dash=False, color="#555555"))
100:     cam = tk.auto_camera(vs, azim=22.0, elev=26.0)
101:     spec = tk.make_spec("圆锥斜截面二面角", "acute_dihedral", vs, edges=es,
102:                         aux_lines=ax, primitives=[tk.prim_sphere(r=r, center=(0.0, y0, 0.0))],
103:                         labels=["O", "C", "E", "O'", "F", "S"], camera=cam)
104:     opts = {"A": "锐二面角", "B": "直二面角", "C": "钝二面角", "D": "平二面角"}
105:     return tk.item(spec, "如图,斜截面与圆锥底面相交于直线l,点C为底面圆心O在棱l上的垂足,CE在截面内垂直于l且交椭圆截线于点E。观察二面角的平面角∠OCE,判断该二面角属于( )", opts, "A")

K26 Spec-to-Image Review

  • major/cone_cylinder_render
    spec未使用cone primitive,以8条母线+16边形底面拼成棱锥状圆锥
    spec_fix: 改用cone primitive或仅保留2条轮廓母线与底面椭圆弧,删除离散边
  • major/renderer_support
    sphere primitive渲染为带密集经纬线的线框球,严重遮挡截面与母线
    spec_fix: toolkit应支持sphere光滑渲染或仅显示必要大圆;当前若不支持,改用两个正交截面圆表示球
  • major/angle_arc
    二面角平面角∠OCE处无角弧,无法直观标识所讨论的锐角
    spec_fix: 在C点添加angle_arc primitive,两边分别指向O与E,半径取OC长度的1/4左右
  • minor/label_missing
    投影垂足H有线段O'H但无标签,且labels列表未包含H
    spec_fix: 在labels中增加H标签,绑定顶点H,避免与O、C重叠
  • minor/mesh_clutter
    切圆与截面椭圆分段过密,大量短线段造成视觉杂乱
    spec_fix: 切圆减至12段,截面椭圆保持12段但确保渲染为平滑曲线而非折线
What To Change
  • 圆锥改用cone primitive或2条轮廓母线+底面椭圆弧
  • 添加∠OCE的angle_arc primitive
  • 补充H点标签并检查l线标签
  • 减少切圆与底面圆分段数
Preferred Primitives
  • cone
  • angle_arc
  • sphere
  • section_circle
Remove/Simplify
  • 删除m0-m7多余母线及p0-p15密分底面边
  • 切圆ef/eb减为12段并只画可见弧
Label Fixes
  • 增加H标签于投影垂足
  • 确保O'、F、C、E互不重叠
Camera Fixes
  • 保持当前斜视角度,确保角弧与CE、OC可见
Toolkit/Renderer: sphere primitive需支持光滑或极简线框渲染;若不支持,建议用两个正交截面圆替代
representation: spec未使用cone primitive,离散拼成棱锥;球体渲染为密集线框,且缺角弧
readability: 两张图均因母线过密和球体网格显得杂乱,二面角平面角缺乏角弧,可读性差
plan: 必要元素OC、CE、O'H、Dandelin球、截面椭圆均已画出,但关键角∠OCE未突出
next: spec_rewrite

acute_dihedral__S017

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue minor hold

Plan / Must Render

题型 锐二面角识别
难度 L2
Scene
绘制三棱锥 D-ABC,底面 ABC 为水平等腰三角形,E 为 AB 中点且 CE⊥AB。顶点 D 在底面左上方,使 DA=DB 且 DE⊥AB,构成二面角 D-AB-C 的平面角∠DEC。以圆弧标注∠DEC 为 θ,交线 AB 加粗,面 DAB 与面 CAB 分别填充淡蓝、淡黄色,隐藏棱画虚线。
Must Render
[
  "三棱锥 D-ABC 的可见棱与隐藏虚线",
  "AB 中点 E 及垂线段 CE、DE",
  "平面角 ∠DEC 的圆弧标注 θ",
  "交线 AB 的加粗高亮",
  "面 DAB 与面 CAB 的淡色填充区分"
]

Spec Summary

{
  "title": "三棱锥 D-ABC",
  "meta": "锐二面角识别 S017",
  "camera": {
    "position": [
      7.148,
      5.37,
      10.682
    ],
    "target": [
      0.0,
      0.456,
      0.843
    ],
    "orthoSize": 3.664
  },
  "counts": {
    "vertices": 14,
    "faces": 4,
    "edges": 6,
    "aux_lines": 9,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 6
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'B', 'text': 'B'}",
      "text": "{'point': 'B', 'text': 'B'}"
    },
    {
      "point": "{'point': 'C', 'text': 'C'}",
      "text": "{'point': 'C', 'text': 'C'}"
    },
    {
      "point": "{'point': 'D', 'text': 'D'}",
      "text": "{'point': 'D', 'text': 'D'}"
    },
    {
      "point": "{'point': 'E', 'text': 'E'}",
      "text": "{'point': 'E', 'text': 'E'}"
    },
    {
      "point": "{'point': 'th', 'text': 'θ'}",
      "text": "{'point': 'th', 'text': 'θ'}"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "a0",
    "a1",
    "a2",
    "a3",
    "a4",
    "a5",
    "a6",
    "a7",
    "th"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C"
      ],
      "color": "#fef08a",
      "opacity": 0.28
    },
    {
      "points": [
        "A",
        "B",
        "D"
      ],
      "color": "#bfdbfe",
      "opacity": 0.32
    },
    {
      "points": [
        "B",
        "C",
        "D"
      ],
      "color": "#f5f5f4",
      "opacity": 0.12
    },
    {
      "points": [
        "C",
        "A",
        "D"
      ],
      "color": "#f5f5f4",
      "opacity": 0.12
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "color": "#dc2626"
    },
    {
      "points": [
        "A",
        "C"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "A",
        "D"
      ]
    },
    {
      "points": [
        "B",
        "D"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ],
      "dash": true,
      "color": "#9ca3af"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "a0",
        "a1"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "a1",
        "a2"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "a2",
        "a3"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "a3",
        "a4"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "a4",
        "a5"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "a5",
        "a6"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "a6",
        "a7"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "C",
        "E"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "D",
        "E"
      ],
      "dash": false,
      "color": "#374151"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     a = rng.uniform(1.8, 2.2)
4:     c = rng.uniform(2.2, 2.8)
5:     h = rng.uniform(2.5, 3.5)

7:     d = h / math.tan(math.radians(theta_deg))
8: 
9:     _, faces = tk.pyramid(3, r=1.0, h=1.0)
10:     vs = {"A": (-a, 0.0, 0.0), "B": (a, 0.0, 0.0), "C": (0.0, 0.0, c),
11:           "D": (0.0, h, d), "E": (0.0, 0.0, 0.0)}
12: 
13:     for f in faces:
14:         pts = [("D" if p == "P" else p) for p in f["points"]]
15:         if pts == ["C", "B", "A"]:
16:             pts = ["A", "B", "C"]
17:         f["points"] = pts
18:     faces[0]["color"] = "#fef08a"; faces[0]["opacity"] = 0.28
19:     faces[1]["color"] = "#bfdbfe"; faces[1]["opacity"] = 0.32
20:     faces[2]["color"] = "#f5f5f4"; faces[2]["opacity"] = 0.12
21:     faces[3]["color"] = "#f5f5f4"; faces[3]["opacity"] = 0.12
22: 
23:     # 圆弧标注 θ
24:     r_arc = 0.32 * min(c, math.sqrt(h * h + d * d))
25:     cos_theta = d / math.sqrt(h * h + d * d)
26:     theta = math.acos(max(-1.0, min(1.0, cos_theta)))
27:     aux_lines = []
28:     for i in range(8):
29:         phi = theta * i / 7.0
30:         vs[f"a{i}"] = (0.0, r_arc * math.sin(phi), r_arc * math.cos(phi))
31:     for i in range(7):
32:         aux_lines.append(tk.aux(f"a{i}", f"a{i+1}", dash=False, color="#374151"))
33:     aux_lines.append(tk.aux("C", "E", dash=False, color="#374151"))
34:     aux_lines.append(tk.aux("D", "E", dash=False, color="#374151"))
35: 
36:     # 显式定义棱:可见棱实线 + 隐藏棱虚线
37:     edges = [
38:         tk.edge("A", "B", color="#dc2626"),      # 交线 AB 加粗高亮
39:         tk.edge("A", "C"),
40:         tk.edge("B", "C"),
41:         tk.edge("A", "D"),
45: 
46:     phi_mid = theta / 2.0
47:     vs["th"] = (0.0, r_arc * math.sin(phi_mid) * 1.5, r_arc * math.cos(phi_mid) * 1.5)
48:     labels = [{"point": k, "text": k} for k in ["A", "B", "C", "D", "E"]]
49:     labels.append({"point": "th", "text": "θ"})
50: 
51:     camera = tk.auto_camera(vs, azim=36.0, elev=22.0, pad=1.55)
52:     spec = tk.make_spec("三棱锥 D-ABC", "锐二面角识别 S017", vs, faces=faces,
53:                         edges=edges, aux_lines=aux_lines, labels=labels, camera=camera)
54: 
55:     pool = [
56:         ("锐二面角(0°<平面角<90°)", "acute"),
57:         ("直角二面角(平面角=90°)", "right"),
58:         ("钝二面角(90°<平面角<180°)", "obtuse"),
59:         ("平角(平面角=180°)", "flat"),
60:     ]
61:     rng.shuffle(pool)
62:     options = {k: v[0] for k, v in zip("ABCD", pool)}
71:     answer = [k for k, v in zip("ABCD", pool) if v[1] == tag][0]
72: 
73:     question = "如图,图中以 θ 标注的 ∠DEC 是二面角 D-AB-C 的平面角,依据其张口大小判断,该二面角属于哪一类?"
74:     return tk.item(spec, question, options, answer)

K26 Spec-to-Image Review

  • fatal/label_missing
    两视角图中仅θ标签可见,顶点A,B,C,D及中点E的字母标注全部缺失,无法定位关键点与交线端点
    spec_fix: 检查label定义与renderer文字渲染,确保各顶点标签在视图中可见且不重叠
  • major/angle_arc
    ∠DEC圆弧由8个辅助点a0-a7及7段aux_lines硬搓而成,未使用angle_arc primitive,不教材
    spec_fix: 调用tk.angle_arc(E,C,D,radius)生成标准角弧,删除硬搓的辅助点与线段
  • major/other
    spec定义了淡黄/淡蓝面填充,但两视角图中两面均为纯白,无法区分面DAB与面CAB
    spec_fix: 提高face opacity至0.5以上,并验证renderer支持半透明面片渲染
  • minor/other
    交线AB在图中呈红色虚线,未体现plan要求的加粗实线高亮效果
    spec_fix: AB edge去除dash属性,使用更粗线宽或高对比色突出交线
What To Change
  • 修复顶点标签渲染,确保A,B,C,D,E均可见
  • 将硬搓圆弧替换为angle_arc primitive
  • 提高面填充不透明度确保可见
Preferred Primitives
  • angle_arc
Remove/Simplify
  • 移除a0-a7辅助顶点及7段aux_lines圆弧近似线段
Label Fixes
  • 检查label坐标与renderer字体渲染,确保5个顶点标签及θ均可见
Camera Fixes
  • none
Toolkit/Renderer: 如renderer不支持angle_arc primitive,需在toolkit层补充;面填充不可见需确认renderer面片支持
representation: 三棱锥结构与二面角平面角定义基本正确,但圆弧硬搓、面填充不可见、标签缺失严重影响教学表达
readability: 两视角构图合理,但因缺少标签和面填充,学生无法识别关键元素与二面角所在面
plan: 覆盖了主要几何元素,但交线高亮、面区分、角弧标注三项核心要求未在图中兑现
next: spec_rewrite

acute_dihedral__S019

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 锐二面角识别
难度 L2
Scene
一个直圆台,轴线略向左前方倾斜以便观察;下底大、上底小,上底面为水平椭圆。上底椭圆内接一个正四棱锥P-ABCD,底面正方形顶点A、B、C、D落在椭圆上,顶点P在圆台上方且锥高较短。取公共上底边AB,M为其中点。画出圆台轴线OO'、上底面内垂线MO(M→圆心O)、侧面斜高MP,并在M处用弧线标出二面角平面角∠PMO。圆台背面母线、上底被遮挡圆弧、棱锥底面对角线均画虚线。
Must Render
[
  "圆台上底椭圆及内接正方形ABCD",
  "顶点P及侧面斜高MP",
  "上底面内MO垂直AB并标注∠PMO弧线",
  "圆台背面母线与遮挡圆弧虚线"
]

Spec Summary

{
  "title": "直圆台与内接正四棱锥",
  "meta": "acute_dihedral_S019",
  "camera": {
    "position": [
      6.422,
      6.107,
      9.889
    ],
    "target": [
      -0.081,
      0.996,
      -0.081
    ],
    "orthoSize": 3.299
  },
  "counts": {
    "vertices": 32,
    "faces": 14,
    "edges": 8,
    "aux_lines": 8,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 7
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "M",
      "text": "M"
    }
  ],
  "vertex_names": [
    "A1",
    "B1",
    "C1",
    "D1",
    "E1",
    "F1",
    "G1",
    "H1",
    "I1",
    "J1",
    "K1",
    "L1",
    "A",
    "N",
    "P1",
    "B",
    "R",
    "S",
    "C",
    "U",
    "V",
    "D",
    "X",
    "Y",
    "O",
    "O2",
    "P",
    "M",
    "a0",
    "a1",
    "a2",
    "a3"
  ],
  "faces_sample": [
    {
      "points": [
        "L1",
        "K1",
        "J1",
        "I1",
        "H1",
        "G1",
        "F1",
        "E1",
        "D1",
        "C1",
        "B1",
        "A1"
      ]
    },
    {
      "points": [
        "A",
        "N",
        "P1",
        "B",
        "R",
        "S",
        "C",
        "U",
        "V",
        "D",
        "X",
        "Y"
      ]
    },
    {
      "points": [
        "A1",
        "B1",
        "N",
        "A"
      ]
    },
    {
      "points": [
        "B1",
        "C1",
        "P1",
        "N"
      ]
    },
    {
      "points": [
        "C1",
        "D1",
        "B",
        "P1"
      ]
    },
    {
      "points": [
        "D1",
        "E1",
        "R",
        "B"
      ]
    },
    {
      "points": [
        "E1",
        "F1",
        "S",
        "R"
      ]
    },
    {
      "points": [
        "F1",
        "G1",
        "C",
        "S"
      ]
    },
    {
      "points": [
        "G1",
        "H1",
        "U",
        "C"
      ]
    },
    {
      "points": [
        "H1",
        "I1",
        "V",
        "U"
      ]
    },
    {
      "points": [
        "I1",
        "J1",
        "D",
        "V"
      ]
    },
    {
      "points": [
        "J1",
        "K1",
        "X",
        "D"
      ]
    },
    {
      "points": [
        "K1",
        "L1",
        "Y",
        "X"
      ]
    },
    {
      "points": [
        "L1",
        "A1",
        "A",
        "Y"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "D"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O2",
        "O"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "M",
        "O"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "M",
        "P"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "a0",
        "a1"
      ],
      "dash": false,
      "color": "#e8590c"
    },
    {
      "points": [
        "a1",
        "a2"
      ],
      "dash": false,
      "color": "#e8590c"
    },
    {
      "points": [
        "a2",
        "a3"
      ],
      "dash": false,
      "color": "#e8590c"
    },
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "B",
        "D"
      ],
      "dash": true,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     r_bot = rng.uniform(1.6, 2.2)
4:     r_top = rng.uniform(1.0, 1.4)
5:     h = rng.uniform(1.4, 2.0)

8:         h_p = r_top / 2.2
9: 
10:     vs, faces = tk.frustum(12, r_bot, r_top, h)
11:     base_map = {c: c + "1" for c in "ABCDEFGHIJKL"}
12:     top_old = ["M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"]
13:     top_new = ["A", "N", "P1", "B", "R", "S", "C", "U", "V", "D", "X", "Y"]
14:     remap = {**base_map, **dict(zip(top_old, top_new))}
15:     vs = {remap.get(k, k): v for k, v in vs.items()}
16:     faces = [{"points": [remap.get(p, p) for p in f["points"]]} for f in faces]
17: 
18:     vs["O"] = (0.0, h, 0.0)
19:     vs["O2"] = (0.0, 0.0, 0.0)
20:     vs["P"] = (0.0, h + h_p, 0.0)
23:     MO = tk.vsub(vs["O"], vs["M"])
24:     MP = tk.vsub(vs["P"], vs["M"])
25:     ang = math.radians(tk.angle_deg(MO, MP))
26:     u = tk.vmul(MO, 1.0 / (tk.norm(MO) + 1e-12))
27:     v = tk.vmul(MP, 1.0 / (tk.norm(MP) + 1e-12))
28:     r_arc = min(tk.norm(MO), tk.norm(MP)) * 0.22
29:     arc = []
30:     for i in range(4):
31:         t = ang * i / 3.0
32:         d = (u[0] * math.cos(t) + v[0] * math.sin(t),
33:              u[1] * math.cos(t) + v[1] * math.sin(t),
34:              u[2] * math.cos(t) + v[2] * math.sin(t))
35:         nm = "a%d" % i
36:         vs[nm] = tk.vadd(vs["M"], tk.vmul(d, r_arc))
37:         arc.append(nm)
38: 
39:     aux = []
40:     aux.append(tk.aux("O2", "O", dash=False, color="#b45309"))
41:     aux.append(tk.aux("M", "O", dash=False, color="#333333"))
42:     aux.append(tk.aux("M", "P", dash=False, color="#333333"))
43:     for i in range(3):
44:         aux.append(tk.aux(arc[i], arc[i + 1], dash=False, color="#e8590c"))
45:     aux.append(tk.aux("A", "C", dash=True, color="#888888"))
46:     aux.append(tk.aux("B", "D", dash=True, color="#888888"))
47: 
48:     edges = []
49:     for a, b in [("A", "B"), ("B", "C"), ("C", "D"), ("D", "A"),
50:                  ("P", "A"), ("P", "B"), ("P", "C"), ("P", "D")]:
51:         edges.append(tk.edge(a, b))
52: 
53:     cam = tk.auto_camera(vs, azim=rng.uniform(28.0, 38.0),
54:                          elev=rng.uniform(20.0, 28.0), pad=1.4)
55:     spec = tk.make_spec("直圆台与内接正四棱锥", "acute_dihedral_S019",
56:                         vs, faces=faces, edges=edges, aux_lines=aux,
57:                         labels=["A", "B", "C", "D", "P", "O", "M"], camera=cam)
58: 
59:     opts = ["锐二面角", "钝二面角", "直二面角", "无法确定"]
60:     rng.shuffle(opts)
61:     options = {chr(65 + i): opts[i] for i in range(4)}
62:     ans = [k for k, v in options.items() if v == "锐二面角"][0]
63:     q = "如图,正四棱锥P-ABCD的底面内接于圆台上底面,M为AB的中点。则侧面PAB与圆台上底面所成二面角应判定为?"
64:     return tk.item(spec, q, options, ans)

K26 Spec-to-Image Review

  • major/frustum_render
    圆台用12边形mesh硬搓,侧面呈棱台状,上下底为折线而非椭圆,完全丧失旋转体特征
    spec_fix: 使用frustum primitive或教材式上下椭圆+母线表示,移除离散化侧面网格
  • major/angle_arc
    ∠PMO用4段短线段拼接,current与side视图中均细弱不可见,无法起到标注作用
    spec_fix: 使用angle_arc primitive绘制显著角弧,增大半径与线宽,确保可见
  • major/label_overlap
    current view中M与P标签重叠压扁于上方,side view中M几乎不可见,D位置异常
    spec_fix: 调整M标签至角弧外侧空白处,分离P与M,校正D标签位置避免与O重叠
  • minor/camera_or_view
    side view未消除压缩,反而暴露上底多边形折线,标签混乱,无辅助理解价值
    spec_fix: 删除无效side view,主相机略俯视侧偏,使MP、MO及角弧清晰不重叠
What To Change
  • 圆台改用光滑椭圆上下底+母线表示,移除12边形mesh
  • 角弧改用primitive或显著圆弧,移除4段线段拼接
  • 优化M、P、D标签位置避免重叠
Preferred Primitives
  • frustum
  • angle_arc
  • section_circle
Remove/Simplify
  • 移除12边形侧面网格与多余顶点
  • 移除4段线段拼接的伪角弧
  • 移除无帮助的side view
Label Fixes
  • M标签移至角弧外侧空白处
  • P标签置于锥顶上方
  • 检查D、O标签避免重叠
Camera Fixes
  • 主相机略俯视并侧偏,确保MP、MO及角弧不重叠
Toolkit/Renderer: toolkit需支持frustum primitive(教材式椭圆底+母线)和angle_arc primitive,避免几何体离散化为多边形mesh
representation: 圆台被离散为12边形棱台,完全丧失旋转体特征;角弧由线段拼接且不可见,不符合教材规范。
readability: current view尚可辨认棱锥但圆台呈棱台状;side view暴露多边形底边且标签混乱,未起到辅助作用。
plan: plan要求的圆台、二面角弧线、MO/MP等要素均有尝试,但核心几何体表示错误导致题图失真。
next: toolkit_patch

acute_dihedral__S020

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
斜三棱锥 P-ABC,底面 ABC 水平放置。棱 AB 为侧面 PAB 与底面交线,取 M∈AB 使 CM⊥AB、PM⊥AB,∠PMC=60° 标为 α(锐二面角平面角)。P 到底面垂足 H(虚线 PH),底面内 HN⊥AB 于 N(N≠M),连 PN 标 ∠PNH=β(错误投影)。棱 AC 上取 K 使 BK⊥AC、PK⊥AC,标 ∠PKB=120° 为 γ(相似钝角构型)。体内画与三面均相切的内切球,球心 O,OT⊥底面于 T(虚线),连 TM 标 ∠OTM=δ(错误球心位置)。
Must Render
[
  "三棱锥 P-ABC 及水平底面 ABC",
  "棱 AB 上的垂足 M,CM⊥AB、PM⊥AB 及 ∠PMC=60° 的弧线标注",
  "P 到底面的垂线 PH(虚线)及底面内 HN⊥AB、连线 PN",
  "棱 AC 上的垂足 K,BK⊥AC、PK⊥AC 及 ∠PKB=120° 的弧线标注",
  "体内内切球球心 O、OT⊥底面(虚线)及连线 TM"
]

Spec Summary

{
  "title": "斜三棱锥 P-ABC",
  "meta": "锐二面角识别",
  "camera": {
    "position": [
      13.455,
      7.511,
      14.58
    ],
    "target": [
      2.513,
      0.256,
      1.298
    ],
    "orthoSize": 4.903
  },
  "counts": {
    "vertices": 14,
    "faces": 4,
    "edges": 0,
    "aux_lines": 15,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 14
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.26722109257625243,
      "position": [
        2.856404049491452,
        1.2373069250871074,
        1.6033265554575147
      ],
      "color": "#f59f00"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "N",
      "text": "N"
    },
    {
      "point": "K",
      "text": "K"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "al",
      "text": "α(60°)"
    },
    {
      "point": "be",
      "text": "β"
    },
    {
      "point": "ga",
      "text": "γ(120°)"
    },
    {
      "point": "de",
      "text": "δ"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "P",
    "M",
    "H",
    "N",
    "K",
    "O",
    "T",
    "al",
    "be",
    "ga",
    "de"
  ],
  "faces_sample": [
    {
      "points": [
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "P"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "P"
      ]
    },
    {
      "points": [
        "C",
        "A",
        "P"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "H"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "C",
        "M"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "P",
        "M"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "H",
        "N"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "P",
        "N"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "B",
        "K"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "P",
        "K"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "O",
        "T"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "T",
        "M"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        [
          2.4083907679774708,
          0.0,
          0.0
        ],
        [
          2.4083907679774708,
          0.0,
          0.16033265554575146
        ]
      ],
      "dash": false,
      "color": "#000000"
    },
    {
      "points": [
        [
          2.4083907679774708,
          0.0,
          0.16033265554575146
        ],
        [
          2.2480581124317194,
          0.0,
          0.16033265554575146
        ]
      ],
      "dash": false,
      "color": "#000000"
    },
    {
      "points": [
        [
          2.9245845192497817,
          0.0,
          0.0
        ],
        [
          2.9245845192497817,
          0.0,
          0.16033265554575146
        ]
      ],
      "dash": false,
      "color": "#000000"
    },
    {
      "points": [
        [
          2.9245845192497817,
          0.0,
          0.16033265554575146
        ],
        [
          2.7642518637040303,
          0.0,
          0.16033265554575146
        ]
      ],
      "dash": false,
      "color": "#000000"
    },
    {
      "points": [
        [
          1.3890187579562507,
          0.0,
          2.6417474392313047
        ],
        [
          1.4636352886424846,
          0.0,
          2.7836591504559354
        ]
      ],
      "dash": false,
      "color": "#000000"
    },
    {
      "points": [
        [
          1.4636352886424846,
          0.0,
          2.7836591504559354
        ],
        [
          1.3217235774178542,
          0.0,
          2.8582756811421692
        ]
      ],
      "dash": false,
      "color": "#000000"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     s = rng.uniform(0.9, 1.1)
4:     c = 6.0 * s
5:     d = (2.0 + rng.uniform(-0.2, 0.2)) * s

20:     O = ((A[0] + B[0] + C[0] + P[0]) / 4.0, (A[1] + B[1] + C[1] + P[1]) / 4.0 + 0.3 * s, (A[2] + B[2] + C[2] + P[2]) / 4.0)
21:     T = (O[0], 0.0, O[2])
22:     vs, faces = tk.pyramid(3, r=2.0 * s, h=2.0 * s)
23:     vs.update({"A": A, "B": B, "C": C, "P": P, "M": M, "H": H, "N": N, "K": K, "O": O, "T": T})
24:     def u(a, b):
25:         v = (a[0] - b[0], a[1] - b[1], a[2] - b[2])
26:         n = math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) + 1e-9
34:     vs["ga"] = (K[0] + (uk[0] + ub[0]) * 0.35 * s, K[1] + (uk[1] + ub[1]) * 0.35 * s, K[2] + (uk[2] + ub[2]) * 0.35 * s)
35:     vs["de"] = (T[0] + (ut[0] + uo[0]) * 0.35 * s, T[1] + (ut[1] + uo[1]) * 0.35 * s, T[2] + (ut[2] + uo[2]) * 0.35 * s)
36:     aux_lines = [
37:         tk.aux("P", "H", True, "#555555"), tk.aux("C", "M", False, "#333333"),
38:         tk.aux("P", "M", False, "#333333"), tk.aux("H", "N", False, "#333333"),
39:         tk.aux("P", "N", False, "#333333"), tk.aux("B", "K", False, "#333333"),
40:         tk.aux("P", "K", False, "#333333"), tk.aux("O", "T", True, "#555555"),
47:     acn = math.sqrt(ac2)
48:     dx, dz = d / acn, e / acn
49:     aux_lines += [
50:         tk.aux((K[0], 0.0, K[2]), (K[0] + 0.15 * s * dx, 0.0, K[2] + 0.15 * s * dz), False, "#000000"),
51:         tk.aux((K[0] + 0.15 * s * dx, 0.0, K[2] + 0.15 * s * dz), (K[0] + 0.15 * s * dx - 0.15 * s * dz, 0.0, K[2] + 0.15 * s * dz + 0.15 * s * dx), False, "#000000"),
52:     ]
53:     cam = tk.auto_camera(vs, azim=rng.uniform(25.0, 40.0), elev=rng.uniform(18.0, 28.0))
54:     spec = tk.make_spec("斜三棱锥 P-ABC", "锐二面角识别", vs, faces=faces, aux_lines=aux_lines, camera=cam, labels=["A", "B", "C", "P", "M", "H", "N", "K", "O", "T", "al", "be", "ga", "de"])
55:     spec["labels"] = [{"point": k, "text": k} for k in ["A", "B", "C", "P", "M", "H", "N", "K", "O", "T"]] + [
56:         {"point": "al", "text": "α(60°)"}, {"point": "be", "text": "β"}, {"point": "ga", "text": "γ(120°)"}, {"point": "de", "text": "δ"}
57:     ]
58:     spec["primitives"] = [tk.prim_sphere(0.25 * s, (O[0], O[1], O[2]), "#f59f00")]
59:     q = "如图,在三棱锥 P-ABC 中,侧面 PAB 与底面 ABC 所成锐二面角的平面角是图中标注的哪一个?"
60:     gold = "∠PMC(角 α)"
61:     wrongs = ["∠PNH(角 β)", "∠PKB(角 γ)", "∠OTM(角 δ)"]
62:     opts = [gold] + wrongs
63:     rng.shuffle(opts)

K26 Spec-to-Image Review

  • major/angle_arc
    ∠PMC=60°、∠PKB=120°、∠PNH=β、∠OTM=δ 均只有文字标签,无角弧,开口方向不可辨
    spec_fix: toolkit 增加 angle_arc primitive,spec 在 M、K、N、T 处绑定角弧与文字标注
  • major/camera_or_view
    top 视图为纯俯视,立体结构压扁,P 与 H 重合、线标签重叠,完全丧失二面角空间信息
    spec_fix: 删除 top 视图,或改为 elev=50°–60° 微斜俯视以保留高度与相对位置
  • minor/sphere_render
    内切球渲染为带经纬网格的实心球,教材中通常仅用轮廓圆或截面圆表示
    spec_fix: sphere primitive 增加仅渲染轮廓圆/大圆选项,或叠加半透明截面圆暗示球心位置
  • minor/aux_line_clutter
    M、N、K 处直角符号由多段细碎 aux_lines 拼成,显琐碎且易与主线混淆
    spec_fix: toolkit 增加 right_angle primitive,或统一用简化 L 型符号替代碎线段
What To Change
  • 增加 angle_arc 标注四个角
  • 删除或替换 top 视图为微斜视角
  • 球体改为简洁轮廓圆
Preferred Primitives
  • angle_arc
  • sphere
  • right_angle
Remove/Simplify
  • 移除纯俯视 top 视图
  • 简化球体网格为轮廓圆
  • 合并 M/N/K 处碎线直角符号
Label Fixes
  • 角标文字绑定弧线中点,避免漂浮
  • 斜视角下隐藏被遮挡的底面角标
Camera Fixes
  • top 视图改为 elev=55° azim 微调斜俯视
Toolkit/Renderer: toolkit 需增加 angle_arc primitive 支持顶点/三点角弧绘制;sphere 支持仅轮廓圆渲染
representation: 三棱锥结构及干扰线完整,但核心角度标注缺失弧线,球体网格过密,直角符号由碎线拼成
readability: current 与 oblique 视角基本可读,top 视图压扁立体结构导致线标签重叠,可读性差
plan: 覆盖了所有要求点线面,但角度标注形式不满足教材规范,影响二面角识别
next: toolkit_patch

acute_dihedral__S016

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 5 major_spec_issue minor hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
斜三棱锥 P-ABC,P 的投影 O 位于 △ABC 外近 AB 处,PO 为虚线。OH⊥AB 于 H,连 PH,∠PHO 为二面角 P-AB-C 的平面角且为锐角。再作 OG⊥AC 于 G,连 PG,使 ∠PGO>90° 形成相似钝角干扰。侧棱 PA 中点 M 的底面投影为 N,连 NH、MN,形成 ∠MNH 作为错误投影干扰。画球 I 与面 ABC、PAB 相切,但球心 I 标在 ∠PHO 外部,形成错误球心干扰。隐藏棱虚线。
Must Render
[
  "三棱锥 P-ABC 的可见棱及隐藏虚线棱",
  "PO(虚线)、OH、PH 及其锐角标记 ∠PHO",
  "OG、PG 及其钝角标记 ∠PGO",
  "点 M、N 及 MN(虚线)、NH",
  "球 I 及其与平面 ABC、PAB 的切点",
  "球心 I 的位置标记(位于 ∠PHO 外部)"
]

Spec Summary

{
  "title": "斜三棱锥 P-ABC",
  "meta": "锐二面角识别",
  "camera": {
    "position": [
      9.015,
      6.261,
      10.898
    ],
    "target": [
      1.946,
      0.392,
      -0.35
    ],
    "orthoSize": 3.77
  },
  "counts": {
    "vertices": 14,
    "faces": 4,
    "edges": 0,
    "aux_lines": 9,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 12
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.4459496472586931,
      "position": [
        3.056236451899884,
        0.4459496472586931,
        -0.7422075986275427
      ],
      "color": "#f59f00"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    },
    {
      "point": "I",
      "text": "I"
    },
    {
      "point": "a1",
      "text": "∠PHO"
    },
    {
      "point": "a2",
      "text": "∠PGO"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "P",
    "O",
    "H",
    "G",
    "M",
    "N",
    "I",
    "T1",
    "T2",
    "a1",
    "a2"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "A",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "B",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "C",
        "A"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "O"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "P",
        "H"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "O",
        "G"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "P",
        "G"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "M",
        "N"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "N",
        "H"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "I",
        "T1"
      ],
      "dash": true,
      "color": "#999999"
    },
    {
      "points": [
        "I",
        "T2"
      ],
      "dash": true,
      "color": "#999999"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 随机几何参数
5:     b = rng.uniform(3.0, 5.0)

12:     dx = rng.uniform(0.8, 2.0)
13: 
14:     # 顶点构造(斜三棱锥,P 的投影 O 在 △ABC 外近 AB 处)
15:     A = (0.0, 0.0, 0.0)
16:     B = (b, 0.0, 0.0)
17:     C = (cx, 0.0, cz)
18:     O = (ox, 0.0, oz)
27:     N = (M[0], 0.0, M[2])
28: 
29:     # 球心 I:与底面 ABC(y=0) 及面 PAB(0, -oz, h)·(x,y,z)=0 等距,且位于二面角内部
30:     iy = r
31:     nlen = math.sqrt(oz * oz + h * h)
32:     iz = r * (oz - nlen) / h
33:     ix = ox + dx
37: 
38:     # 三棱锥面(供渲染器自动推导棱与隐藏线)
39:     faces = [{"points": ["A", "B", "C"]}, {"points": ["P", "A", "B"]},
40:              {"points": ["P", "B", "C"]}, {"points": ["P", "C", "A"]}]
41: 
42:     # 辅助线与构造线
43:     aux = [tk.aux("P", "O", True, "#777777"), tk.aux("O", "H", False, "#333333"),
46:            tk.aux("N", "H", False, "#333333")]
47: 
48:     # 切点(球与两面相切)
49:     T1 = (ix, 0.0, iz)
50:     T2 = (ix, iy + r * (-oz) / nlen, iz + r * h / nlen)
51:     vs["T1"] = T1
52:     vs["T2"] = T2
62:     vs["a2"] = tk.vadd(G, tk.vmul(tk.unit(tk.vadd(u, v)), 0.4))
63: 
64:     # 球 primitive
65:     prim = [tk.prim_sphere(r=r, center=I, color="#f59f00")]
66: 
67:     # 相机
68:     cam = tk.auto_camera(vs, prim, azim=rng.uniform(25.0, 40.0), elev=rng.uniform(18.0, 28.0))
69: 
70:     spec = tk.make_spec("斜三棱锥 P-ABC", "锐二面角识别", vs, faces=faces,
71:                         aux_lines=aux, primitives=prim, labels=None, camera=cam)
72: 
73:     # 自定义标签:顶点 + 角标记
74:     spec["labels"] = [{"point": k, "text": k} for k in vs if k not in ("a1", "a2", "T1", "T2")]
75:     spec["labels"].append({"point": "a1", "text": "∠PHO"})
76:     spec["labels"].append({"point": "a2", "text": "∠PGO"})
77: 
78:     # 选项(固定内容,顺序随机化)
79:     opts = [
80:         ("二面角 P-AB-C 为锐二面角,其平面角是 ∠PHO", True),
81:         ("二面角 P-AC-B 为钝二面角,其平面角是 ∠PGO", False),
82:         ("∠MNH 是二面角 P-AB-C 的平面角", False),
83:         ("球心 I 在棱 AB 上,二面角 P-AB-C 是直二面角", False),
84:     ]
85:     rng.shuffle(opts)
86:     letters = "ABCD"
87:     options = {letters[i]: opts[i][0] for i in range(4)}
90: 
91:     q = ("如图,在三棱锥 P-ABC 中,PO⊥平面 ABC 于 O,OH⊥AB,PH⊥AB,OG⊥AC,PG⊥AC,"
92:          "M 为 PA 中点且在底面的投影为 N,球 I 与平面 ABC、平面 PAB 都相切。"
93:          "下列判断正确的是( )")
94:     return tk.item(spec, q, options, ans, answer_text=opts[correct_idx][0])

K26 Spec-to-Image Review

  • major/angle_arc
    ∠PHO与∠PGO仅文字标签,无角度弧线,无法直观识别锐角/钝角
    spec_fix: 在H、G处添加angle_arc primitive标记两角,文字标签置于弧线外侧
  • minor/label_missing
    切点T1、T2无标签,球与两面切点位置不易定位
    spec_fix: 为T1、T2添加标签,或确保虚线IT1、IT2端点与切点清晰对应
  • minor/camera_or_view
    current视角底面元素拥挤,H、N、G与角标、球距离过近
    spec_fix: 微调camera target使底面居中,适当增大orthoSize或拉远视角
What To Change
  • 在H、G处添加angle_arc primitive表示∠PHO和∠PGO
  • 为T1、T2补充标签
Preferred Primitives
  • angle_arc
Remove/Simplify
  • 移除纯文字角标,改用弧线+文字组合
Label Fixes
  • ∠PHO、∠PGO文字标签移至弧线外侧,避免贴线重叠
Camera Fixes
  • 调整target至底面重心,增大orthoSize减少元素重叠
Toolkit/Renderer: none
representation: 三棱锥与球体表示正确,但核心二面角平面角缺少angle_arc几何标记,仅文字标签不满足教材规范。
readability: 三视角设计合理,top视角有助于理解底面垂足关系,current视角元素略拥挤。
plan: 基本覆盖plan要求的几何元素与干扰项,但角度标记方式未达可识别标准。
next: spec_rewrite

acute_dihedral__S018

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 5 major_spec_issue toolkit hold

Plan / Must Render

题型 锐二面角识别
难度 L3
Scene
正四棱锥 P-ABCD 水平放置,底面 ABCD 为正方形,顶点 P 在底面中心 O 正上方。画出四条侧棱及底面两条对角线 AC、BD(被遮挡部分用虚线)。取侧棱 PB、PC 中点 M、N,连接 A-M-N 形成截面三角形 AMN,其中 MN 为截面与侧面 PBC 的交线。从 P 向底面作垂线 PO(虚线),并连接 OM、ON(虚线隐藏线)。
Must Render
[
  "正四棱锥 P-ABCD 的可见轮廓及底面正方形",
  "截面三角形 AMN 及其与侧面 PBC 的交线 MN",
  "底面中心 O 及虚线高 PO、虚线底面对角线"
]

Spec Summary

{
  "title": "正四棱锥截面二面角",
  "meta": "acute_dihedral_S018;AM、AN、MN 为截面三角形边;MN 为截面与侧面 PBC 的交线;PO、OM、ON、AC、BD 为虚线辅助线",
  "camera": {
    "position": [
      6.266,
      4.444,
      9.593
    ],
    "target": [
      -0.122,
      0.286,
      0.122
    ],
    "orthoSize": 3.202
  },
  "counts": {
    "vertices": 8,
    "faces": 5,
    "edges": 11,
    "aux_lines": 8,
    "aux_faces": 1,
    "primitives": 0,
    "labels": 8
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "P",
    "O",
    "M",
    "N"
  ],
  "faces_sample": [
    {
      "points": [
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "P"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "P"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "P"
      ]
    },
    {
      "points": [
        "D",
        "A",
        "P"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "D"
      ]
    },
    {
      "points": [
        "A",
        "M"
      ],
      "color": "#2563eb"
    },
    {
      "points": [
        "A",
        "N"
      ],
      "color": "#2563eb"
    },
    {
      "points": [
        "M",
        "N"
      ],
      "color": "#2563eb"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "O"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "M"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "N"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "B",
        "D"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "A",
        "M"
      ],
      "dash": false,
      "color": "#2563eb"
    },
    {
      "points": [
        "A",
        "N"
      ],
      "dash": false,
      "color": "#2563eb"
    },
    {
      "points": [
        "M",
        "N"
      ],
      "dash": false,
      "color": "#2563eb"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "A",
        "M",
        "N"
      ],
      "color": "#93c5fd",
      "opacity": 0.24
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 正四棱锥尺寸:控制 h/r 较小,保证二面角 A-MN-P 为锐二面角
5:     r = rng.uniform(1.7, 2.0)

6:     h = r * rng.uniform(0.48, 0.62)
7: 
8:     # 正四棱锥 P-ABCD,底面在水平面 y=0,P 在底面中心 O 正上方
9:     vs, faces = tk.pyramid(4, r=r, h=h, apex="P")
10:     vs["O"] = (0.0, 0.0, 0.0)
11: 
12:     # M、N 分别为侧棱 PB、PC 的中点
13:     vs["M"] = tk.midpoint(vs["P"], vs["B"])
14:     vs["N"] = tk.midpoint(vs["P"], vs["C"])
15: 
16:     # 显式列出底面边、四条侧棱,以及截面三角形 AMN 的三条边
17:     # 修复点:AM、AN、MN 同时出现在 edges 中,避免 spec 摘要遗漏截面边
18:     edges = [
19:         tk.edge("A", "B"),
20:         tk.edge("B", "C"),
21:         tk.edge("C", "D"),
22:         tk.edge("D", "A"),
31: 
32:     # 虚线辅助线:高 PO、底面对角线 AC/BD、隐藏的 OM/ON
33:     # 修复点:截面三边 AM、AN、MN 也显式加入 aux_lines,且为实线,支持 must_render
34:     aux_lines = [
35:         tk.aux("P", "O", dash=True, color="#d9480f"),
36:         tk.aux("O", "M", dash=True, color="#d9480f"),
37:         tk.aux("O", "N", dash=True, color="#d9480f"),
38:         tk.aux("A", "C", dash=True, color="#495057"),
43:     ]
44: 
45:     # 透明截面面片,强调截面三角形 AMN
46:     aux_faces = [
47:         tk.face(["A", "M", "N"], color="#93c5fd", opacity=0.24),
48:     ]
49: 
50:     # 由几何计算二面角 A-MN-P 的平面角分类
51:     Q = tk.midpoint(vs["M"], vs["N"])
52:     mn = tk.vsub(vs["N"], vs["M"])
53: 
54:     qa = tk.vsub(vs["A"], Q)
55:     qp = tk.vsub(vs["P"], Q)
56: 
57:     # 分别投影到垂直于 MN 的平面内,得到二面角的平面角两边
58:     mn2 = tk.dot(mn, mn) + 1e-12
59:     qa_perp = tk.vsub(qa, tk.vmul(mn, tk.dot(qa, mn) / mn2))
60:     qp_perp = tk.vsub(qp, tk.vmul(mn, tk.dot(qp, mn) / mn2))
61: 
62:     angle = tk.angle_deg(qa_perp, qp_perp)
63:     if angle < 89.0:
64:         answer = "A"
65:     elif angle <= 91.0:
66:         answer = "B"
67:     elif angle < 175.0:
68:         answer = "C"
69:     else:
70:         answer = "D"
71: 
72:     options = {
73:         "A": "锐二面角",
74:         "B": "直角二面角",
75:         "C": "钝二面角",
76:         "D": "平二面角",
77:     }
78: 
79:     camera = tk.auto_camera(vs, azim=34.0, elev=20.0, pad=1.46)
80:     spec = tk.make_spec(
81:         "正四棱锥截面二面角",
82:         "acute_dihedral_S018;AM、AN、MN 为截面三角形边;MN 为截面与侧面 PBC 的交线;PO、OM、ON、AC、BD 为虚线辅助线",
83:         vs,
84:         faces=faces,
85:         edges=edges,
86:         aux_lines=aux_lines,
87:         aux_faces=aux_faces,
88:         labels=["A", "B", "C", "D", "P", "O", "M", "N"],
89:         camera=camera,
90:     )
91: 
92:     question = (
93:         "如图,在正四棱锥 P-ABCD 中,M、N 分别为侧棱 PB、PC 的中点,"
94:         "截面 AMN 与侧面 PBC 相交于线段 MN。观察图中二面角 A-MN-P 的平面角张口程度,"
95:         "该二面角属于( )"
96:     )
97: 
98:     return tk.item(
99:         spec,
103:         answer_text=options[answer],
104:         meta={
105:             "diversity_tag": "截面-中位线 / 棱锥-二面角",
106:             "computed_plane_angle_deg": round(angle, 3),
107:         },
108:     )

K26 Spec-to-Image Review

  • major/angle_arc
    二面角识别题未标记平面角,三视图中均无角弧、垂足Q及垂线AQ/PQ
    spec_fix: 在MN上取垂足Q,添加AQ⊥MN、PQ⊥MN,并用angle_arc标记∠AQP
  • major/camera_or_view
    section_plane视角A、N、O与轴线重叠,MN显示过短,未能展示平面角
    spec_fix: 调整相机使视线垂直于MN法平面,展开A、P与MN的垂直距离
  • minor/aux_line_clutter
    AM、AN、MN同时重复出现在edges与aux_lines列表中
    spec_fix: 从aux_lines中删除重复的AM、AN、MN,仅保留edges中的蓝色截面边
What To Change
  • 添加垂足Q及AQ、PQ垂线并标记直角
  • 添加angle_arc标记二面角平面角
  • 删除aux_lines中重复的AM、AN、MN
  • 调整section_plane相机避免A/N/O共线重叠
Preferred Primitives
  • angle_arc
Remove/Simplify
  • aux_lines中重复的AM、AN、MN线段
Label Fixes
  • section_plane视角下为A、N、O添加偏移避免堆叠
Camera Fixes
  • section_plane相机应正对二面角棱MN法平面,展开平面角
Toolkit/Renderer: 需新增angle_arc primitive支持,用于显式标记二面角A-MN-P的平面角
representation: 正四棱锥与截面三角形结构正确,但二面角核心标记完全缺失,平面角不可定位
readability: current与oblique视图可读,section_plane视角因元素共线重叠而失效
plan: 画出截面与棱锥,但未实现识别二面角所需的角度显式标注
next: toolkit_patch

acute_dihedral__S007

三维立体 › 空间位置关系 › 空间面面位置关系 › 二面角 › 二面角大小分类 › 锐二面角识别
STAGE-1 FAIL score 6 major_spec_issue minor hold

Plan / Must Render

题型 锐二面角识别
难度 L2
Scene
直三棱柱 ABC-A₁B₁C₁ 与正三棱锥 P-A₁B₁C₁ 共上底面 A₁B₁C₁(正三角形水平放置),顶点 P 向上。取 A₁B₁ 中点 M,连接正三棱锥斜高 PM(PM⊥A₁B₁);在棱柱前侧面 AA₁B₁B 内,连接竖直线段 MN 交下底边 AB 于中点 N(MN∥AA₁)。∠PMN 为二面角 P-A₁B₁-B 的平面角。隐藏的下底边 AC、BC 及后侧棱 CC₁ 用虚线表示,强调锥体对棱柱上后部的遮挡。
Must Render
[
  "直三棱柱与正三棱锥的组合体及重合的上底面 A₁B₁C₁",
  "正三棱锥斜高 PM(M 为 A₁B₁ 中点)及棱柱侧面内的竖直线段 MN(N 为 AB 中点)",
  "被遮挡的棱柱下底边 AC、BC 及后侧棱 CC₁(虚线)"
]

Spec Summary

{
  "title": "直三棱柱与正三棱锥组合体",
  "meta": "acute_dihedral_S007",
  "camera": {
    "position": [
      -5.453,
      6.354,
      9.129
    ],
    "target": [
      -0.137,
      1.396,
      -0.079
    ],
    "orthoSize": 3.157
  },
  "counts": {
    "vertices": 9,
    "faces": 8,
    "edges": 12,
    "aux_lines": 2,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 9
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "A1",
      "text": "A1"
    },
    {
      "point": "B1",
      "text": "B1"
    },
    {
      "point": "C1",
      "text": "C1"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "A1",
    "B1",
    "C1",
    "P",
    "M",
    "N"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "C",
        "B"
      ]
    },
    {
      "points": [
        "A1",
        "B1",
        "C1"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "B1",
        "A1"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "C1",
        "B1"
      ]
    },
    {
      "points": [
        "C",
        "A",
        "A1",
        "C1"
      ]
    },
    {
      "points": [
        "A1",
        "B1",
        "P"
      ]
    },
    {
      "points": [
        "B1",
        "C1",
        "P"
      ]
    },
    {
      "points": [
        "C1",
        "A1",
        "P"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "B",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "A1",
        "B1"
      ]
    },
    {
      "points": [
        "B1",
        "C1"
      ]
    },
    {
      "points": [
        "C1",
        "A1"
      ]
    },
    {
      "points": [
        "A",
        "A1"
      ]
    },
    {
      "points": [
        "B",
        "B1"
      ]
    },
    {
      "points": [
        "C",
        "C1"
      ],
      "dash": true
    },
    {
      "points": [
        "P",
        "A1"
      ]
    },
    {
      "points": [
        "P",
        "B1"
      ]
    },
    {
      "points": [
        "P",
        "C1"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "M"
      ],
      "dash": false,
      "color": "#c2410c"
    },
    {
      "points": [
        "M",
        "N"
      ],
      "dash": false,
      "color": "#c2410c"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     # 随机尺寸,保证图随 seed 变化
3:     r = rng.uniform(1.0, 1.5)
4:     h_prism = rng.uniform(1.6, 2.4)
5:     h_pyr = rng.uniform(1.2, 2.0)

10: 
11:     # 组合顶点:下底 ABC,上底 A1B1C1,锥顶 P
12:     vertices = {}
13:     for k in ['A', 'B', 'C']:
14:         vertices[k] = vs_prism[k]
15:     vertices['A1'] = vs_prism['D']
16:     vertices['B1'] = vs_prism['E']
17:     vertices['C1'] = vs_prism['F']
18:     p = vs_pyr['P']
19:     vertices['P'] = (p[0], p[1] + h_prism, p[2])
20: 
21:     # 中点 M(A1B1)、N(AB)
22:     vertices['M'] = tk.midpoint(vertices['A1'], vertices['B1'])
23:     vertices['N'] = tk.midpoint(vertices['A'], vertices['B'])
24: 
25:     # 面定义(供渲染器消隐与半透明面片)
26:     faces = [
27:         {"points": ["A", "C", "B"]},          # 棱柱下底
28:         {"points": ["A1", "B1", "C1"]},       # 棱柱上底(与锥底重合)
29:         {"points": ["A", "B", "B1", "A1"]},   # 前侧面 AA1B1B
30:         {"points": ["B", "C", "C1", "B1"]},   # 侧面 BB1C1C
36: 
37:     # 显式边:控制 AC、BC、CC1 为虚线,其余实线
38:     edges = [
39:         tk.edge("A", "B"), tk.edge("A", "C", dash=True), tk.edge("B", "C", dash=True),
40:         tk.edge("A1", "B1"), tk.edge("B1", "C1"), tk.edge("C1", "A1"),
41:         tk.edge("A", "A1"), tk.edge("B", "B1"), tk.edge("C", "C1", dash=True),
42:         tk.edge("P", "A1"), tk.edge("P", "B1"), tk.edge("P", "C1"),
44: 
45:     # 辅助线:斜高 PM 与竖直段 MN(实线、深色)
46:     aux_lines = [
47:         tk.aux("P", "M", dash=False, color="#c2410c"),
48:         tk.aux("M", "N", dash=False, color="#c2410c"),
49:     ]
50: 
51:     # 相机:左前上方,突出前侧面与锥体遮挡关系
52:     camera = tk.auto_camera(vertices, azim=-30.0, elev=25.0, pad=1.5)
53: 
54:     spec = tk.make_spec(
55:         "直三棱柱与正三棱锥组合体",
56:         "acute_dihedral_S007",
57:         vertices,
58:         faces=faces,
59:         edges=edges,
60:         aux_lines=aux_lines,
61:         labels="auto",
62:         camera=camera,
63:     )
64: 
65:     # 由几何计算判定平面角类型:
66:     # PM 为斜高,MN 为竖直方向;二面角平面角等于 PM 与竖直向上射线的夹角,
67:     # 即 90°−α(α 为侧面与底面夹角),必为锐角。
68:     vMP = tk.vsub(vertices['P'], vertices['M'])
69:     vMN = tk.vsub(vertices['M'], vertices['N'])  # 竖直向上 (0, h_prism, 0)
70:     angle = tk.angle_deg(vMP, vMN)
71: 
72:     opts = {"A": "锐二面角", "B": "直二面角", "C": "钝二面角"}
73:     if angle < 90.0 - 1e-6:
74:         ans = "A"
75:     elif angle > 90.0 + 1e-6:
76:         ans = "C"
77:     else:
78:         ans = "B"
79: 
80:     return tk.item(
81:         spec,
82:         "如图,正三棱锥侧面 PA₁B₁ 与直三棱柱侧面 AA₁B₁B 所成的二面角 P-A₁B₁-B 属于( )",
83:         opts,
84:         ans,
85:     )

K26 Spec-to-Image Review

  • major/angle_arc
    三视图均未绘制∠PMN角弧,仅两段红色aux_lines,无法显著标识二面角平面角
    spec_fix: 在spec中增加angle_arc primitive,顶点M,两边指向P与N,弧内或旁标注角符号
  • minor/camera_or_view
    current视图前侧面AA1B1B几乎edge-on,投影坍缩为一条竖线,立体感丧失
    spec_fix: 调整current相机azim至-50°、elev至35°,避免前侧面正对视线
  • minor/label_overlap
    current视图中A1、B1、A、B、M、N标签堆叠于左侧同一直线,互相遮挡
    spec_fix: 修复相机后重新分布标签,或弃用current视图,以oblique_plus45为主图
What To Change
  • 增加∠PMN的angle_arc标记
  • 调整current相机参数避免edge-on
Preferred Primitives
  • angle_arc
Remove/Simplify
  • 若current视图修复后仍混乱则弃用
Label Fixes
  • M、N标签略偏离端点,避免贴线;current视图防止竖直堆叠
Camera Fixes
  • current相机azim从-30改为-50,elev从25改为35,orthoSize适当缩小
Toolkit/Renderer: none
representation: 棱柱与棱锥用面片边线表示合理,但缺少二面角平面角的角弧primitive,核心考查对象未突出
readability: oblique_plus45与section_plane可读性尚可,current视图因投影坍缩导致标签堆叠、结构不清
plan: 基本覆盖must_render,PM、MN、虚线隐藏棱均已绘制
next: spec_rewrite

archimedean_solid_recognition__S011

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 2 unusable major hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
绘制截角二十面体与正六棱柱的组合体。截角二十面体居下,其顶部一个正六边形面与正六棱柱底面完全贴合,正六棱柱从上方遮挡该面。视角为右前上方透视,可见截角二十面体的正五边形与正六边形面,被遮挡棱画虚线。在截角二十面体正面标注一个正五边形顶点A-E,正六棱柱轴线用点画线表示。
Must Render
[
  "截角二十面体可见的正五边形与正六边形面及其实线棱",
  "被正六棱柱遮挡的截角二十面体顶部棱与贴合面边界(虚线)",
  "正六棱柱的可见侧棱与顶面",
  "正六棱柱的轴线(点画线辅助线)"
]

Spec Summary

{
  "title": "组合体",
  "meta": "截角二十面体与正六棱柱",
  "camera": {
    "position": [
      10.261,
      7.612,
      13.724
    ],
    "target": [
      -0.0,
      0.549,
      -0.21
    ],
    "orthoSize": 4.908
  },
  "counts": {
    "vertices": 74,
    "faces": 38,
    "edges": 6,
    "aux_lines": 1,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 5
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    }
  ],
  "vertex_names": [
    "I2_I0",
    "I4_I0",
    "I6_I0",
    "I8_I0",
    "I10_I0",
    "I1_I3",
    "I3_I1",
    "I1_I4",
    "I4_I1",
    "I1_I6",
    "I6_I1",
    "I1_I9",
    "I9_I1",
    "I1_I11",
    "I11_I1",
    "I2_I5",
    "I5_I2",
    "I2_I7",
    "I7_I2",
    "I2_I8",
    "I8_I2",
    "I3_I5",
    "I5_I3",
    "I3_I7",
    "I7_I3",
    "I3_I9",
    "I9_I3",
    "I4_I6",
    "I6_I4",
    "I4_I8",
    "I8_I4",
    "I4_I9",
    "I9_I4",
    "I5_I7",
    "I7_I5",
    "I5_I8",
    "I8_I5",
    "I5_I9",
    "I9_I5",
    "I8_I9",
    "I9_I8",
    "I10_I2",
    "I2_I10",
    "I10_I6",
    "I6_I10",
    "I10_I7",
    "I7_I10",
    "I10_I11",
    "I11_I10",
    "I11_I3",
    "I3_I11",
    "I11_I6",
    "I6_I11",
    "I11_I7",
    "I7_I11",
    "A",
    "B",
    "C",
    "D",
    "E",
    "PA",
    "PB",
    "PC",
    "PD",
    "PE",
    "PF",
    "PG",
    "PH",
    "PI",
    "PJ",
    "PK",
    "PL",
    "O1",
    "O2"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C",
        "D",
        "E"
      ]
    },
    {
      "points": [
        "I1_I3",
        "I1_I11",
        "I1_I6",
        "I1_I4",
        "I1_I9"
      ]
    },
    {
      "points": [
        "I2_I0",
        "I2_I10",
        "I2_I7",
        "I2_I5",
        "I2_I8"
      ]
    },
    {
      "points": [
        "I3_I1",
        "I3_I11",
        "I3_I7",
        "I3_I5",
        "I3_I9"
      ]
    },
    {
      "points": [
        "I4_I0",
        "I4_I8",
        "I4_I9",
        "I4_I1",
        "I4_I6"
      ]
    },
    {
      "points": [
        "I5_I2",
        "I5_I7",
        "I5_I3",
        "I5_I9",
        "I5_I8"
      ]
    },
    {
      "points": [
        "I6_I0",
        "I6_I4",
        "I6_I1",
        "I6_I11",
        "I6_I10"
      ]
    },
    {
      "points": [
        "I7_I2",
        "I7_I5",
        "I7_I3",
        "I7_I11",
        "I7_I10"
      ]
    },
    {
      "points": [
        "I8_I0",
        "I8_I4",
        "I8_I9",
        "I8_I5",
        "I8_I2"
      ]
    },
    {
      "points": [
        "I9_I1",
        "I9_I4",
        "I9_I8",
        "I9_I5",
        "I9_I3"
      ]
    },
    {
      "points": [
        "I10_I0",
        "I10_I2",
        "I10_I7",
        "I10_I11",
        "I10_I6"
      ]
    },
    {
      "points": [
        "I11_I1",
        "I11_I3",
        "I11_I7",
        "I11_I10",
        "I11_I6"
      ]
    },
    {
      "points": [
        "I2_I5",
        "I5_I2",
        "I5_I7",
        "I7_I5",
        "I7_I2",
        "I2_I7"
      ]
    },
    {
      "points": [
        "I1_I11",
        "I11_I1",
        "I11_I3",
        "I3_I11",
        "I3_I1",
        "I1_I3"
      ]
    },
    {
      "points": [
        "I11_I3",
        "I3_I11",
        "I3_I7",
        "I7_I3",
        "I7_I11",
        "I11_I7"
      ]
    },
    {
      "points": [
        "D",
        "I4_I0",
        "I4_I8",
        "I8_I4",
        "I8_I0",
        "E"
      ]
    },
    {
      "points": [
        "I5_I8",
        "I8_I5",
        "I8_I9",
        "I9_I8",
        "I9_I5",
        "I5_I9"
      ]
    },
    {
      "points": [
        "I10_I11",
        "I11_I10",
        "I11_I6",
        "I6_I11",
        "I6_I10",
        "I10_I6"
      ]
    },
    {
      "points": [
        "B",
        "I10_I0",
        "I10_I2",
        "I2_I10",
        "I2_I0",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "I2_I0",
        "I2_I8",
        "I8_I2",
        "I8_I0",
        "E"
      ]
    },
    {
      "points": [
        "I1_I4",
        "I4_I1",
        "I4_I9",
        "I9_I4",
        "I9_I1",
        "I1_I9"
      ]
    },
    {
      "points": [
        "I3_I5",
        "I5_I3",
        "I5_I7",
        "I7_I5",
        "I7_I3",
        "I3_I7"
      ]
    },
    {
      "points": [
        "I1_I11",
        "I11_I1",
        "I11_I6",
        "I6_I11",
        "I6_I1",
        "I1_I6"
      ]
    },
    {
      "points": [
        "D",
        "I4_I0",
        "I4_I6",
        "I6_I4",
        "I6_I0",
        "C"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "I1_I4",
        "I4_I1"
      ],
      "dash": true
    },
    {
      "points": [
        "I4_I1",
        "I4_I6"
      ],
      "dash": true
    },
    {
      "points": [
        "I4_I6",
        "I6_I4"
      ],
      "dash": true
    },
    {
      "points": [
        "I6_I4",
        "I6_I1"
      ],
      "dash": true
    },
    {
      "points": [
        "I6_I1",
        "I1_I6"
      ],
      "dash": true
    },
    {
      "points": [
        "I1_I6",
        "I1_I4"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O1",
        "O2"
      ],
      "dash": true,
      "color": "#333333"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     phi = (1 + math.sqrt(5)) / 2
4:     scale = 1.5
5:     base, perms = (0, 1, phi), [(0, 1, 2), (1, 2, 0), (2, 0, 1)]

51:                 tv[f"{u}_{v}"] = tk.vmul(tk.vadd(tk.vmul(ico[u], 2), ico[v]), 1.0 / 3.0)
52:                 tv[f"{v}_{u}"] = tk.vmul(tk.vadd(ico[u], tk.vmul(ico[v], 2)), 1.0 / 3.0)
53:     faces = []
54:     for u in names:
55:         faces.append({"points": [en[(u, v) if u < v else (v, u)][0 if u < v else 1] for v in norder[u]]})
56:     for tri in tris:
57:         u, v, w = tri
58:         def near(a, b):
59:             k = (a, b) if a < b else (b, a)
60:             return en[k][0 if a < b else 1]
61:         faces.append({"points": [near(u, v), near(v, u), near(v, w), near(w, v), near(w, u), near(u, w)]})
62:     fc = lambda pts: tk.centroid([tv[p] for p in pts])
63:     hexes = [f for f in faces if len(f["points"]) == 6]
64:     pents = [f for f in faces if len(f["points"]) == 5]
65:     top = max(hexes, key=lambda f: fc(f["points"])[1])
66:     front = max(pents, key=lambda f: fc(f["points"])[2])
67:     omap = {}
68:     for i, old in enumerate(front["points"]):
71:     def remap(p):
72:         return omap.get(p, p)
73:     for f in faces:
74:         f["points"] = [remap(p) for p in f["points"]]
75:     top_pts = [remap(p) for p in top["points"]]
76:     c0 = fc(top_pts)
77:     R = tk.dist(c0, tv[top_pts[0]])
87:         pvmap[k] = f"P{k}"
88:         tv[f"P{k}"] = tk.vadd(c0, tk.vadd(tk.vmul(ex, p[0] * R), tk.vadd(tk.vmul(yax, p[1]), tk.vmul(ez, p[2] * R))))
89:     pfaces = [{"points": [pvmap[p] for p in f["points"]]} for f in pfs[1:]]
90:     hid = [tk.edge(top_pts[i], top_pts[(i + 1) % 6], dash=True) for i in range(6)]
91:     c1 = tk.vadd(c0, tk.vmul(yax, h))
92:     tv["O1"], tv["O2"] = c0, c1
93:     aux = [tk.aux("O1", "O2", dash=True, color="#333333")]
94:     render_faces = [f for f in faces if f != top] + pfaces
95:     spec = tk.make_spec("组合体", "截角二十面体与正六棱柱", tv, faces=render_faces, edges=hid, aux_lines=aux, labels=["A", "B", "C", "D", "E"], camera=tk.auto_camera(tv, azim=rng.uniform(25, 40), elev=rng.uniform(18, 28)))
96:     texts = ["是半正多面体,属于截角二十面体", "是半正多面体,属于正十二面体", "是半正多面体,属于截角八面体", "不是半正多面体,属于约翰逊立体"]
97:     rng.shuffle(texts)
98:     opts = {"ABCD"[i]: texts[i] for i in range(4)}
99:     ans = [k for k, v in opts.items() if v == "是半正多面体,属于截角二十面体"][0]

K26 Spec-to-Image Review

  • fatal/plan_coverage
    截角二十面体仅渲染面片,无任何可见实线棱,呈现一团白色轮廓。
    spec_fix: 为截角二十面体与正六棱柱的所有可见棱添加实线 edge 至 edges 列表,区分虚实。
  • major/label_missing
    A-E标签悬浮于无边界面的左侧,未与正面正五边形顶点精确对齐。
    spec_fix: 将标签绑定到正面正五边形的实际顶点坐标,并确保该顶点处有可见棱线交点。
  • minor/camera_or_view
    视角过于正面,截角二十面体面特征不明显,近似圆球。
    spec_fix: 调整相机方位角至更右侧、仰角稍降,增强正五边形与正六边形的分布可读性。
What To Change
  • 补充截角二十面体全部可见棱为实线 edges,被遮挡棱设为虚线。
  • 补充正六棱柱可见侧棱与顶面棱为实线。
Preferred Primitives
none
Remove/Simplify
none
Label Fixes
  • 将A-E精确定位到正面正五边形顶点,避免悬浮。
Camera Fixes
  • 调整相机至右前上方更偏侧视角,降低仰角,增强立体感。
Toolkit/Renderer: 若 renderer 不自动绘制 face 边界,需 toolkit 提供自动提取多面体棱边并判断可见性的辅助函数;当前可先在代码中手动枚举添加。
representation: 截角二十面体仅用填充面片表示,缺失所有边界棱线,立体结构完全不可读。
readability: 两视角均无法辨认正五边形与正六边形面,图像组未能帮助理解几何体特征。
plan: 必须渲染的可见实线棱与正面正五边形顶点标注均未正确呈现,核心几何信息丢失。
next: spec_rewrite

archimedean_solid_recognition__S002

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
绘制一个截半立方体,呈顶点朝上、正方形面朝前的斜二测视角。过其几何中心作一个垂直于上下顶点连线的正六边形截面,截面边界用红色实线、内部半透明黄色填充。多面体内切一球,球心标记为O,在上前侧正方形面和相邻三角形面上分别标出切点T1、T2。前侧正方形面内画一条对角线作为辅助线。从顶部顶点向底面中心作垂直投影线(点划线)。所有被遮挡的棱、截面隐蔽边及球体不可见轮廓均用灰色虚线表示。
Must Render
[
  "截半立方体外轮廓及灰色虚线隐藏棱",
  "红色实线正六边形截面及半透明填充",
  "内切球、球心O及切点T1、T2",
  "前侧正方形面的辅助对角线",
  "顶部至底面中心的垂直投影线(点划线)"
]

Spec Summary

{
  "title": "截半立方体",
  "meta": "半正多面体识别S002:外轮廓、灰色虚线隐藏棱、红色正六边形截面、内切球、O、T1、T2、前方面对角线与B-H投影线",
  "camera": {
    "position": [
      5.253,
      3.949,
      7.097
    ],
    "target": [
      0.009,
      0.009,
      0.022
    ],
    "orthoSize": 2.829
  },
  "counts": {
    "vertices": 77,
    "faces": 14,
    "edges": 0,
    "aux_lines": 67,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 15
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.04,
      "position": [
        0.0,
        0.0,
        0.0
      ],
      "color": "#74c0fc"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "I",
      "text": "I"
    },
    {
      "point": "J",
      "text": "J"
    },
    {
      "point": "K",
      "text": "K"
    },
    {
      "point": "L",
      "text": "L"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T1",
      "text": "T1"
    },
    {
      "point": "T2",
      "text": "T2"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "O",
    "T1",
    "T2",
    "P0",
    "P1",
    "P2",
    "P3",
    "P4",
    "P5",
    "Q0",
    "Q1",
    "Q2",
    "Q3",
    "Q4",
    "Q5",
    "Q6",
    "Q7",
    "Q8",
    "Q9",
    "Q10",
    "Q11",
    "Q12",
    "Q13",
    "Q14",
    "Q15",
    "Q16",
    "Q17",
    "Q18",
    "Q19",
    "Q20",
    "Q21",
    "Q22",
    "Q23",
    "Q24",
    "Q25",
    "Q26",
    "Q27",
    "M0",
    "M1",
    "M2",
    "M3",
    "M4",
    "M5",
    "M6",
    "M7",
    "M8",
    "M9",
    "M10",
    "M11",
    "M12",
    "M13",
    "M14",
    "M15",
    "M16",
    "M17",
    "M18",
    "M19",
    "M20",
    "M21",
    "M22",
    "M23",
    "M24",
    "M25",
    "M26",
    "M27"
  ],
  "faces_sample": [
    {
      "points": [
        "C",
        "D",
        "F",
        "G"
      ]
    },
    {
      "points": [
        "J",
        "K",
        "L",
        "I"
      ]
    },
    {
      "points": [
        "A",
        "C",
        "B",
        "J"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "H",
        "L"
      ]
    },
    {
      "points": [
        "A",
        "G",
        "H",
        "I"
      ]
    },
    {
      "points": [
        "B",
        "D",
        "E",
        "K"
      ]
    },
    {
      "points": [
        "A",
        "C",
        "G"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "D"
      ]
    },
    {
      "points": [
        "A",
        "J",
        "I"
      ]
    },
    {
      "points": [
        "B",
        "J",
        "K"
      ]
    },
    {
      "points": [
        "G",
        "F",
        "H"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "F"
      ]
    },
    {
      "points": [
        "E",
        "K",
        "L"
      ]
    },
    {
      "points": [
        "H",
        "I",
        "L"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "J",
        "K"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "K",
        "L"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "L",
        "I"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "I",
        "J"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "E",
        "K"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "K",
        "B"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "E",
        "L"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "L",
        "H"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "H",
        "I"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "D",
        "E"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "E",
        "F"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "F",
        "H"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "F",
        "D"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "A",
        "I"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "C",
        "J"
      ],
      "dash": false,
      "color": "#1864ab"
    },
    {
      "points": [
        "B",
        "H"
      ],
      "dash": true,
      "color": "#212529"
    },
    {
      "points": [
        "P0",
        "P1"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P1",
        "P2"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P2",
        "P3"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P3",
        "P4"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P4",
        "P5"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P5",
        "P0"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P2",
        "P3"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "P3",
        "P4"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "P4",
        "P5"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "Q0",
        "Q1"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q1",
        "Q2"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q2",
        "Q3"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q3",
        "Q4"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q4",
        "Q5"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q5",
        "Q6"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q6",
        "Q7"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q7",
        "Q8"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q8",
        "Q9"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q9",
        "Q10"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q10",
        "Q11"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q11",
        "Q12"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q12",
        "Q13"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q13",
        "Q14"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q14",
        "Q15"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q15",
        "Q16"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q16",
        "Q17"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q17",
        "Q18"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q18",
        "Q19"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q19",
        "Q20"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q20",
        "Q21"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q21",
        "Q22"
      ],
      "dash": true,
      "color": "#6c757d"
    },
    {
      "points": [
        "Q22",
        "Q23"
      ],
      "dash": true,
      "color": "#6c757d"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "P0",
        "P1",
        "P2",
        "P3",
        "P4",
        "P5"
      ],
      "color": "#ffe066",
      "opacity": 0.36
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     s = rng.choice([1.0, 1.04, 1.08])
5:     rt2 = math.sqrt(2.0)

6: 
7:     # Cuboctahedron (rectified cube), with opposite vertices B-H as the up-down axis.
8:     vs = {
9:         "A": (0.0, 0.0, rt2 * s),
10:         "B": (0.0, rt2 * s, 0.0),
11:         "C": (s, rt2 * s / 2.0, rt2 * s / 2.0),
22:     }
23: 
24:     # Tangency marks: T1 on the upper-front square face A-C-B-J, T2 on adjacent triangle A-C-G.
25:     vs["T1"] = (0.0, rt2 * s / 2.0, rt2 * s / 2.0)
26:     vs["T2"] = (2.0 * s / 3.0, 0.0, 2.0 * rt2 * s / 3.0)
27: 
28:     faces = [
29:         {"points": ["C", "D", "F", "G"]},
30:         {"points": ["J", "K", "L", "I"]},
31:         {"points": ["A", "C", "B", "J"]},
32:         {"points": ["E", "F", "H", "L"]},
43:     ]
44: 
45:     aux_lines = []
46: 
47:     # Explicit grey dashed hidden edges, as required by the plan/review.
48:     hidden_edges = [
49:         ("J", "K"), ("K", "L"), ("L", "I"), ("I", "J"),
50:         ("E", "K"), ("K", "B"), ("E", "L"), ("L", "H"),
51:         ("H", "I"), ("D", "E"), ("E", "F"), ("F", "H"),
52:         ("F", "D"), ("A", "I"),
53:     ]
54:     for a, b in hidden_edges:
55:         aux_lines.append(tk.aux(a, b, dash=True, color="#868e96"))
56: 
57:     # Front square-face auxiliary diagonal: solid line, no longer confused with hidden edges.
58:     aux_lines.append(tk.aux("C", "J", dash=False, color="#1864ab"))
59: 
60:     # Dot/dash-style vertical projection line along the top-bottom axis B-H through O.
61:     aux_lines.append(tk.aux("B", "H", dash=True, color="#212529"))
62: 
63:     # Regular hexagonal central section in the plane y=0, perpendicular to the B-H axis.
64:     hex_coords = [
65:         (0.0, 0.0, rt2 * s),
78:     # Red solid visible section boundary.
79:     for i in range(6):
80:         aux_lines.append(tk.aux(hex_names[i], hex_names[(i + 1) % 6], dash=False, color="#e03131"))
81: 
82:     # Explicit grey dashed hidden part of the section boundary.
83:     for i in [2, 3, 4]:
84:         aux_lines.append(tk.aux(hex_names[i], hex_names[(i + 1) % 6], dash=True, color="#868e96"))
85: 
86:     aux_faces = [tk.face(hex_names, color="#ffe066", opacity=0.36)]
87: 
88:     # Inscribed sphere body plus explicit grey dashed invisible contour.
89:     r_sphere = 1.0 * s
90:     primitives = [tk.prim_sphere(r=r_sphere, center=(0.0, 0.0, 0.0), color="#74c0fc")]
91: 
92:     contour_names = []
93:     n = 28
94:     for i in range(n):
96:         nm = "Q" + str(i)
97:         # Equatorial back/invisible contour in the section plane, rendered explicitly as dashed grey.
98:         vs[nm] = (r_sphere * math.cos(ang), 0.0, r_sphere * math.sin(ang))
99:         contour_names.append(nm)
100:     for i in range(n):
101:         aux_lines.append(tk.aux(contour_names[i], contour_names[(i + 1) % n], dash=True, color="#6c757d"))
102: 
103:     meridian_names = []
104:     for i in range(n):
105:         ang = 2.0 * math.pi * i / n
106:         nm = "M" + str(i)
107:         # A second rear meridian-like hidden contour to make the sphere's invisible outline explicit.
108:         vs[nm] = (0.0, r_sphere * math.cos(ang), r_sphere * math.sin(ang))
109:         meridian_names.append(nm)
110:     for i in range(n // 2, n):
111:         aux_lines.append(tk.aux(meridian_names[i], meridian_names[(i + 1) % n], dash=True, color="#6c757d"))
112: 
113:     az = rng.uniform(32.0, 38.0)
114:     el = rng.uniform(22.0, 27.0)
115:     camera = tk.auto_camera(vs, primitives=primitives, azim=az, elev=el, pad=1.48)
116:     camera["orthoSize"] = round(2.72 * s, 3)
117: 
118:     spec = tk.make_spec(
119:         "截半立方体",
120:         "半正多面体识别S002:外轮廓、灰色虚线隐藏棱、红色正六边形截面、内切球、O、T1、T2、前方面对角线与B-H投影线",
121:         vs,
122:         faces=faces,
123:         aux_lines=aux_lines,
124:         aux_faces=aux_faces,
125:         primitives=primitives,
126:         labels=["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "O", "T1", "T2"],
127:         camera=camera,
128:     )
129: 
130:     opts = [
131:         ("截半立方体", True),
143:             answer = ch
144: 
145:     question = "如图,图中展示了一个内切球且被正六边形截面贯穿的多面体。已知该立体每个顶点周围都交替连接两个正三角形和两个正方形,且所有棱长相等。据此判断该立体属于以下哪种半正多面体?"
146:     return tk.item(spec, question, options, answer)

K26 Spec-to-Image Review

  • major/sphere_render
    球体已用primitive,却硬搓28段赤道+14段经线虚线,aux_lines达67条,球内布满灰色虚线多边形,极杂乱
    spec_fix: 删除Q/M系列硬搓轮廓;若需不可见轮廓,改用12-16段单一大圆虚线,或依赖renderer自动绘制
  • major/aux_line_clutter
    六边形截面被遮边P2-P3等同时存在红色实线与灰色虚线aux_line,重复绘制导致视觉混乱
    spec_fix: 仅对被遮挡截面边绘制灰色虚线,移除对应红色实线边,避免同一边重叠
  • minor/label_overlap
    oblique视角中E、T1、A等标签位置漂移重叠,可读性差
    spec_fix: 精简辅助视角或优化标签自动避让布局
  • minor/camera_or_view
    oblique_minus45视角与主视角差异小,未显著改善T2定位,反而更乱
    spec_fix: 移除无增益的辅助视角,或改用俯视/侧视显著区分可见性
What To Change
  • 删除硬搓的球体不可见轮廓线(Q/M系列),或减至1个12段大圆
  • 修复六边形截面被遮挡边重复绘制问题
  • 评估并精简辅助视角
Preferred Primitives
  • sphere
Remove/Simplify
  • 移除28段赤道虚线圆和28点经线虚线
  • 移除六边形被遮挡边的红色实线(仅保留灰色虚线)
Label Fixes
  • 检查oblique视角标签自动布局,避免重叠漂移
Camera Fixes
  • 若辅助视角无显著增益则移除,保持单主视角清晰
Toolkit/Renderer: 若renderer的sphere primitive无法自动绘制不可见轮廓虚线,需toolkit支持sphere隐藏轮廓渲染,避免题目代码硬搓
representation: 截半立方体与六边形截面表示正确,但球体过度硬搓虚线轮廓导致严重杂乱,不符合教材简洁风格
readability: current视角主体可辨,但球内大量灰色虚线与隐藏棱混淆;oblique视角标签漂移,整体可读性低
plan: plan要求的元素均已覆盖,但球体不可见轮廓和截面隐蔽边的实现方式过度且重复
next: spec_rewrite

archimedean_solid_recognition__S005

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 4 major_spec_issue minor hold

Plan / Must Render

题型 半正多面体识别
难度 L2
Scene
绘制一个截半立方体的斜透视图,可见前下方1个正方形面与2个正三角形面,不可见棱用灰色虚线表示。标记最前方顶点为P,将交于P的四条棱加粗显示。用浅红色高亮左侧正三角形面,用浅蓝色高亮前方正方形面。从几何中心O向P引一条辅助虚线OP,并在右侧引线标注“所有棱长相等”。
Must Render
[
  "截半立方体的可见棱与隐藏虚线棱",
  "高亮的正三角形面与正方形面",
  "顶点P及加粗的四条邻棱",
  "从几何中心O到顶点P的辅助虚线OP"
]

Spec Summary

{
  "title": "截半立方体",
  "meta": "截半立方体斜透视图",
  "camera": {
    "position": [
      2.172,
      -4.202,
      11.14
    ],
    "target": [
      0.0,
      0.0,
      0.0
    ],
    "orthoSize": 3.07
  },
  "counts": {
    "vertices": 14,
    "faces": 14,
    "edges": 24,
    "aux_lines": 1,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 14
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'B', 'text': 'B'}",
      "text": "{'point': 'B', 'text': 'B'}"
    },
    {
      "point": "{'point': 'C', 'text': 'C'}",
      "text": "{'point': 'C', 'text': 'C'}"
    },
    {
      "point": "{'point': 'D', 'text': 'D'}",
      "text": "{'point': 'D', 'text': 'D'}"
    },
    {
      "point": "{'point': 'E', 'text': 'E'}",
      "text": "{'point': 'E', 'text': 'E'}"
    },
    {
      "point": "{'point': 'F', 'text': 'F'}",
      "text": "{'point': 'F', 'text': 'F'}"
    },
    {
      "point": "{'point': 'G', 'text': 'G'}",
      "text": "{'point': 'G', 'text': 'G'}"
    },
    {
      "point": "{'point': 'H', 'text': 'H'}",
      "text": "{'point': 'H', 'text': 'H'}"
    },
    {
      "point": "{'point': 'I', 'text': 'I'}",
      "text": "{'point': 'I', 'text': 'I'}"
    },
    {
      "point": "{'point': 'J', 'text': 'J'}",
      "text": "{'point': 'J', 'text': 'J'}"
    },
    {
      "point": "{'point': 'P', 'text': 'P'}",
      "text": "{'point': 'P', 'text': 'P'}"
    },
    {
      "point": "{'point': 'L', 'text': 'L'}",
      "text": "{'point': 'L', 'text': 'L'}"
    },
    {
      "point": "{'point': 'O', 'text': 'O'}",
      "text": "{'point': 'O', 'text': 'O'}"
    },
    {
      "point": "{'point': 'T', 'text': '所有棱长相等'}",
      "text": "{'point': 'T', 'text': '所有棱长相等'}"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "P",
    "L",
    "O",
    "T"
  ],
  "faces_sample": [
    {
      "points": [
        "B",
        "C",
        "D",
        "A"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "H",
        "G",
        "J",
        "I"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "A",
        "F",
        "G",
        "E"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "C",
        "P",
        "I",
        "L"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "B",
        "E",
        "H",
        "P"
      ],
      "color": "#dbeafe",
      "opacity": 0.35
    },
    {
      "points": [
        "D",
        "L",
        "J",
        "F"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "A",
        "E",
        "B"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "A",
        "D",
        "F"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "C",
        "B",
        "P"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "C",
        "L",
        "D"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "G",
        "H",
        "E"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "G",
        "F",
        "J"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "I",
        "P",
        "H"
      ],
      "color": "#fecaca",
      "opacity": 0.35
    },
    {
      "points": [
        "I",
        "J",
        "L"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    }
  ],
  "edges_sample": [
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "H",
        "G"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "G",
        "J"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "J",
        "I"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "I",
        "H"
      ]
    },
    {
      "points": [
        "A",
        "F"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "F",
        "G"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "G",
        "E"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "E",
        "A"
      ]
    },
    {
      "points": [
        "C",
        "P"
      ],
      "width": 2.5,
      "color": "#111827"
    },
    {
      "points": [
        "P",
        "I"
      ],
      "width": 2.5,
      "color": "#111827"
    },
    {
      "points": [
        "I",
        "L"
      ]
    },
    {
      "points": [
        "L",
        "C"
      ]
    },
    {
      "points": [
        "B",
        "E"
      ]
    },
    {
      "points": [
        "E",
        "H"
      ]
    },
    {
      "points": [
        "H",
        "P"
      ],
      "width": 2.5,
      "color": "#111827"
    },
    {
      "points": [
        "P",
        "B"
      ],
      "width": 2.5,
      "color": "#111827"
    },
    {
      "points": [
        "D",
        "L"
      ]
    },
    {
      "points": [
        "L",
        "J"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "J",
        "F"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "F",
        "D"
      ],
      "dash": true,
      "color": "#9ca3af"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "P"
      ],
      "dash": true,
      "color": "#6b7280"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 固定物体姿态:先绕x轴-30°(顶部前倾),再绕y轴-25°(左侧朝前),
5:     # 使得 P=(0,-1,1) 成为唯一最前方顶点,且左/右两侧三角形均可见。

42:         ["I", "P", "H"], ["I", "J", "L"]
43:     ]
44:     faces = [tk.face(ps) for ps in face_pts]
45:     faces[4]["color"] = "#dbeafe"   # 前方正方形面 Sz1 — 浅蓝
46:     faces[4]["opacity"] = 0.35
47:     faces[12]["color"] = "#fecaca"  # 左侧正三角形面 T7 — 浅红
48:     faces[12]["opacity"] = 0.35
49: 
50:     # 相机固定在右下方俯视,保证 P 最前且后方 T8 不可见
51:     cam = tk.auto_camera(
52:         {k: vs[k] for k in raw},
53:         azim=rng.uniform(8.0, 12.0),
54:         elev=rng.uniform(-22.0, -18.0),
55:         pad=1.4
58: 
59:     # 计算各面法向与可见性
60:     edge_to_faces = {}
61:     normals = []
62:     for i, ps in enumerate(face_pts):
63:         coords = [vs[p] for p in ps]
64:         u = tk.vsub(coords[1], coords[0])
72:             a, b = ps[j], ps[(j + 1) % m]
73:             key = tuple(sorted((a, b)))
74:             edge_to_faces.setdefault(key, []).append(i)
75:     vis = [tk.dot(n, view) > 0 for n in normals]
76: 
77:     # 24 条棱:可见实线,不可见灰色虚线;交于 P 的四条邻棱加粗
78:     all_edges = [
79:         ("B", "C"), ("C", "D"), ("D", "A"), ("A", "B"),
80:         ("H", "G"), ("G", "J"), ("J", "I"), ("I", "H"),
81:         ("A", "F"), ("F", "G"), ("G", "E"), ("E", "A"),
82:         ("C", "P"), ("P", "I"), ("I", "L"), ("L", "C"),
85:     ]
86:     p_set = {"B", "C", "H", "I", "P"}
87:     edges = []
88:     for a, b in all_edges:
89:         key = tuple(sorted((a, b)))
90:         ok = any(vis[i] for i in edge_to_faces.get(key, []))
91:         if not ok:
92:             edges.append(tk.edge(a, b, dash=True, color="#9ca3af"))
93:         elif "P" in {a, b} and a in p_set and b in p_set:
94:             e = tk.edge(a, b)
95:             e["width"] = 2.5
96:             e["color"] = "#111827"
97:             edges.append(e)
98:         else:
99:             edges.append(tk.edge(a, b))
100: 
101:     aux_lines = [tk.aux("O", "P", dash=True, color="#6b7280")]
102: 
103:     labels = [{"point": k, "text": k} for k in vs if k != "T"]
104:     labels.append({"point": "T", "text": "所有棱长相等"})
105: 
106:     spec = tk.make_spec(
107:         "截半立方体", "截半立方体斜透视图", vs,
108:         faces=faces, edges=edges, aux_lines=aux_lines,
109:         labels=labels, camera=cam
110:     )
111: 
112:     opts = [
113:         ("A", "截半立方体"),

K26 Spec-to-Image Review

  • major/other
    浅蓝/浅红高亮面opacity仅0.35且颜色极浅,在白色背景下完全不可辨识
    spec_fix: 将高亮面opacity提升至0.65-0.85,或改用#60a5fa/#f87171等更饱和的填充色
  • major/label_missing
    右侧无'所有棱长相等'文字标注,T标签未在图中渲染或坐标位置错误
    spec_fix: 将T设为右侧空白处引线标注,调整坐标至视锥内并避免与棱线重叠
  • minor/camera_or_view
    单视角下后方虚线较多,但结构尚可辨认
    spec_fix: 可保持当前相机,无需调整
What To Change
  • 提高高亮面opacity或改用饱和色确保可见
  • 将T标签改为右侧引线标注并校验坐标
Preferred Primitives
none
Remove/Simplify
none
Label Fixes
  • 将'所有棱长相等'改为右侧引线标注或调整T坐标
  • 确保标签层级高于线框避免被遮挡
Camera Fixes
none
Toolkit/Renderer: none
representation: 截半立方体线框结构正确,隐藏虚线处理得当,但面高亮与文本标注参数设置失效
readability: 单视角线框清晰,然而核心高亮面完全不可见且关键文字标签缺失,严重影响题意表达
plan: 几何体与棱线符合plan,但高亮面与右侧标注未有效呈现,需修复后重审
next: visual_repair

archimedean_solid_recognition__S017

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 半正多面体识别
难度 L2
Scene
绘制一个截半立方体(cuboctahedron),采用右前上方透视视角。全部24条棱以等长黑色实线绘制,被遮挡的棱用浅灰色虚线表示。表面由8个正三角形和6个正方形组成,其中顶部正方形面以浅蓝色半透明填充高亮,前侧一个正三角形面以浅黄色半透明填充高亮。用浅灰色虚线绘制一个外接立方体轮廓,截半立方体的12个顶点恰好位于该外接立方体各棱中点;并在高亮面上分别标注“正△”“正□”。
Must Render
[
  "截半立方体的全部等长实线可见棱",
  "被遮挡的隐藏棱(浅灰色虚线)",
  "浅蓝色高亮的顶部正方形面",
  "浅黄色高亮的前侧正三角形面",
  "外接立方体的虚线辅助轮廓",
  "面上的“正△”与“正□”文字标注"
]

Spec Summary

{
  "title": "截半立方体",
  "meta": "截半立方体识别",
  "camera": {
    "position": [
      5.022,
      4.269,
      5.952
    ],
    "target": [
      0.0,
      0.0,
      0.0
    ],
    "orthoSize": 2.229
  },
  "counts": {
    "vertices": 22,
    "faces": 14,
    "edges": 20,
    "aux_lines": 12,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 2
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "正□",
      "text": "正□"
    },
    {
      "point": "正△",
      "text": "正△"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "正□",
    "正△"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "E",
        "H",
        "D"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "B",
        "C",
        "G",
        "F"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "A",
        "J",
        "B",
        "I"
      ],
      "color": "#d0e8ff",
      "opacity": 0.45
    },
    {
      "points": [
        "D",
        "L",
        "C",
        "K"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "E",
        "I",
        "F",
        "L"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "H",
        "K",
        "G",
        "J"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "A",
        "I",
        "E"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "B",
        "F",
        "I"
      ],
      "color": "#fff4d0",
      "opacity": 0.45
    },
    {
      "points": [
        "C",
        "L",
        "F"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "D",
        "E",
        "L"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "A",
        "H",
        "J"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "B",
        "J",
        "G"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "C",
        "G",
        "K"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    },
    {
      "points": [
        "D",
        "K",
        "H"
      ],
      "color": "#f5f5f4",
      "opacity": 0.22
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "E"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "D",
        "H"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C",
        "G"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "B",
        "F"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "A",
        "J"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "B",
        "J"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "B",
        "I"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "A",
        "I"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "D",
        "L"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C",
        "L"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "C",
        "K"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "D",
        "K"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "E",
        "I"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "F",
        "I"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "F",
        "L"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "E",
        "L"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "H",
        "K"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "G",
        "K"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "G",
        "J"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "H",
        "J"
      ],
      "dash": true,
      "color": "#c0c0c0"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "C1",
        "C2"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C2",
        "C3"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C3",
        "C4"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C4",
        "C1"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C5",
        "C6"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C6",
        "C7"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C7",
        "C8"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C8",
        "C5"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C1",
        "C5"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C2",
        "C6"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C3",
        "C7"
      ],
      "dash": true,
      "color": "#c0c0c0"
    },
    {
      "points": [
        "C4",
        "C8"
      ],
      "dash": true,
      "color": "#c0c0c0"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     # 截半立方体顶点(外接立方体棱中点,外接立方体边长为 2)
3:     vs = {
4:         "A": (1, 1, 0), "B": (-1, 1, 0), "C": (-1, -1, 0), "D": (1, -1, 0),
5:         "E": (1, 0, 1), "F": (-1, 0, 1), "G": (-1, 0, -1), "H": (1, 0, -1),

18: 
19:     # 面定义(顶点顺序已保证外法向朝外)
20:     faces_data = [
21:         # 6 个正方形
22:         {"points": ["A", "E", "H", "D"], "type": "sq"},
23:         {"points": ["B", "C", "G", "F"], "type": "sq"},
24:         {"points": ["A", "J", "B", "I"], "type": "sq"},
39:     azim = rng.uniform(25, 45)
40:     elev = rng.uniform(22, 38)
41:     cam = tk.auto_camera(all_vs, azim=azim, elev=elev)
42:     cam_pos = tuple(cam["position"])
43: 
44:     # 判断各面是否朝向相机
45:     front = []
46:     for f in faces_data:
47:         pts = [vs[p] for p in f["points"]]
48:         n = tk.face_normal(pts)
49:         view = tk.vsub(cam_pos, pts[0])
50:         front.append(tk.dot(n, view) > 0)
51: 
52:     # 建立边到相邻面的映射
53:     edge_faces = {}
54:     for fi, f in enumerate(faces_data):
55:         pts = f["points"]
56:         for i in range(len(pts)):
57:             a, b = pts[i], pts[(i + 1) % len(pts)]
58:             key = tuple(sorted((a, b)))
59:             edge_faces.setdefault(key, []).append(fi)
60: 
61:     edges = []
62:     for (a, b), fis in edge_faces.items():
63:         if len(fis) != 2:
64:             continue
65:         hidden = (not front[fis[0]]) and (not front[fis[1]])
66:         col = "#c0c0c0" if hidden else "#1a1a1a"
67:         edges.append(tk.edge(a, b, dash=hidden, color=col))
68: 
69:     # 高亮面选择
70:     sq_indices = [i for i, f in enumerate(faces_data) if f["type"] == "sq"]
71:     tri_indices = [i for i, f in enumerate(faces_data) if f["type"] == "tri"]
72: 
73:     sq_cens = [(i, tk.centroid([vs[p] for p in faces_data[i]["points"]])) for i in sq_indices]
74:     top_sq = max(sq_cens, key=lambda x: x[1][1])[0]
75: 
76:     best = None
77:     for i in tri_indices:
78:         if not front[i]:
79:             continue
80:         cen = tk.centroid([vs[p] for p in faces_data[i]["points"]])
81:         n = tk.face_normal([vs[p] for p in faces_data[i]["points"]])
82:         d = tk.dot(n, tk.unit(tk.vsub(cam_pos, cen)))
83:         if best is None or d > best[0]:
84:             best = (d, i)
85:     front_tri = best[1] if best else tri_indices[0]
86: 
87:     faces = []
88:     for fi, f in enumerate(faces_data):
89:         pts = f["points"]
90:         if fi == top_sq:
91:             faces.append(tk.face(pts, color="#d0e8ff", opacity=0.45))
92:         elif fi == front_tri:
93:             faces.append(tk.face(pts, color="#fff4d0", opacity=0.45))
94:         else:
95:             faces.append(tk.face(pts, color="#f5f5f4", opacity=0.22))
96: 
97:     # 外接立方体虚线轮廓
98:     aux_lines = []
99:     cube_edges = [
100:         ("C1", "C2"), ("C2", "C3"), ("C3", "C4"), ("C4", "C1"),
101:         ("C5", "C6"), ("C6", "C7"), ("C7", "C8"), ("C8", "C5"),
102:         ("C1", "C5"), ("C2", "C6"), ("C3", "C7"), ("C4", "C8")
103:     ]
104:     for a, b in cube_edges:
105:         aux_lines.append(tk.aux(a, b, dash=True, color="#c0c0c0"))
106: 
107:     # 面心文字标注
108:     top_cen = tk.centroid([vs[p] for p in faces_data[top_sq]["points"]])
109:     tri_cen = tk.centroid([vs[p] for p in faces_data[front_tri]["points"]])
110:     all_vs["正□"] = top_cen
111:     all_vs["正△"] = tri_cen
112:     labels = ["正□", "正△"]
113: 
114:     spec = tk.make_spec("截半立方体", "截半立方体识别", all_vs,
115:                         faces=faces, edges=edges, aux_lines=aux_lines,
116:                         labels=labels, camera=cam)
117: 
118:     opts = [
119:         ("半正多面体——截半立方体", True),
120:         ("正多面体——正八面体", False),

K26 Spec-to-Image Review

  • major/shape_primitive
    截半立方体应24条棱,spec仅20条;面1(A,E,H,D)与面2(B,C,G,F)顶点顺序错误
    spec_fix: 修正面1顺序为A,E,D,H、面2为B,F,C,G,使edge_faces正确识别全部24条棱
  • major/label_missing
    图中完全无正△正□标注,spec虽定义面心顶点文字但渲染层未显示
    spec_fix: 检查label渲染层级与字体颜色,确保中文标注位于面心且不被面片遮挡
  • minor/mesh_clutter
    高亮面浅蓝/浅黄填充在图中几乎不可辨,opacity 0.45过低且被白面覆盖
    spec_fix: 提高高亮面opacity至0.6-0.8,非高亮面改用更低透明度或仅保留边线
What To Change
  • 修正两个正方形面顶点顺序以恢复24条棱
  • 增强高亮面颜色对比度
  • 确保label文字层置顶渲染
Preferred Primitives
none
Remove/Simplify
  • 非高亮面opacity降至0.1或改为不填充,减少视觉干扰
Label Fixes
  • 正□置于顶面重心,正△置于前侧三角形面重心,字体黑色无衬线
Camera Fixes
  • 当前右前上方视角可接受,无需调整
Toolkit/Renderer: 检查renderer面片透明度混合与文字标注层级,确保半透明填充和label同时正确输出
representation: 截半立方体拓扑大体正确,但两个面顶点顺序错误导致4条棱缺失,高亮与标注未正确渲染
readability: 单视角透视合理,外接立方体辅助线清晰,因缺棱和缺标注导致几何信息不完整
plan: 未满足全部24条棱绘制及面上标注要求,高亮面可视度不足
next: spec_rewrite

archimedean_solid_recognition__S004

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
画一个由8个蓝色三角形和6个橙色四边形围成的中心对称多面体,外观极似截半立方体。用虚线补全其外部包裹立方体的4条隐藏棱与底面。在两条相邻原始棱上分别标出分点P、Q,显示截取比例1:2与1:1(中点)不一致。在一个橙色四边形面上画对角线并标注邻边长a≠b。作外接球并标球心O,用实线连接O与上方三个顶点,长度标注显示不等。所有被遮挡的内部棱用虚线。
Must Render
[
  "8个三角形与6个四边形组成的多面体可见表面",
  "原始立方体的隐藏棱(虚线)及棱上分点比例标注",
  "四边形面上的对角线及邻边不等长标注",
  "外接球、球心O及O到不同顶点的连线长度标注"
]

Spec Summary

{
  "title": "截半变体识别",
  "meta": "S004",
  "camera": {
    "position": [
      11.428,
      6.977,
      15.537
    ],
    "target": [
      0.301,
      -0.283,
      0.077
    ],
    "orthoSize": 5.37
  },
  "counts": {
    "vertices": 28,
    "faces": 14,
    "edges": 0,
    "aux_lines": 16,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 10
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 3.5355339059327378,
      "position": [
        0,
        0,
        0
      ],
      "color": "#cbd5e1"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Q",
      "text": "Q"
    },
    {
      "point": "la",
      "text": "a"
    },
    {
      "point": "lb",
      "text": "b"
    },
    {
      "point": "l11",
      "text": "1:2"
    },
    {
      "point": "l12",
      "text": "1:1"
    },
    {
      "point": "r1",
      "text": "3.63"
    },
    {
      "point": "r2",
      "text": "3.54"
    },
    {
      "point": "r3",
      "text": "3.54"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "O",
    "P",
    "Q",
    "mCD",
    "mDA",
    "mEF",
    "mFG",
    "mGH",
    "mHE",
    "pAE",
    "pBF",
    "pCG",
    "pDH",
    "la",
    "lb",
    "l11",
    "l12",
    "r1",
    "r2",
    "r3"
  ],
  "faces_sample": [
    {
      "points": [
        "P",
        "mDA",
        "pAE"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "P",
        "pBF",
        "Q"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "Q",
        "pCG",
        "mCD"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "mCD",
        "pDH",
        "mDA"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "mHE",
        "mEF",
        "pAE"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "mEF",
        "mFG",
        "pBF"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "mFG",
        "mGH",
        "pCG"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "mGH",
        "mHE",
        "pDH"
      ],
      "color": "#3b82f6",
      "opacity": 0.35
    },
    {
      "points": [
        "P",
        "Q",
        "mCD",
        "mDA"
      ],
      "color": "#f97316",
      "opacity": 0.35
    },
    {
      "points": [
        "mEF",
        "mFG",
        "mGH",
        "mHE"
      ],
      "color": "#f97316",
      "opacity": 0.35
    },
    {
      "points": [
        "P",
        "pBF",
        "mEF",
        "pAE"
      ],
      "color": "#f97316",
      "opacity": 0.35
    },
    {
      "points": [
        "Q",
        "pCG",
        "mFG",
        "pBF"
      ],
      "color": "#f97316",
      "opacity": 0.35
    },
    {
      "points": [
        "mCD",
        "pDH",
        "mGH",
        "pCG"
      ],
      "color": "#f97316",
      "opacity": 0.35
    },
    {
      "points": [
        "mDA",
        "pAE",
        "mHE",
        "pDH"
      ],
      "color": "#f97316",
      "opacity": 0.35
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "B",
        "C"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "C",
        "D"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "D",
        "A"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "E",
        "F"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "F",
        "G"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "G",
        "H"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "H",
        "E"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "A",
        "E"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "B",
        "F"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "C",
        "G"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "D",
        "H"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "O",
        "mEF"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "O",
        "mFG"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "O",
        "pBF"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "Q",
        "mDA"
      ],
      "dash": false,
      "color": "#374151"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     L = rng.choice([4.0, 5.0, 6.0])
4:     s = L / 2.0
5:     r = rng.choice([1.0/3.0, 2.0/3.0])

24:     quad = [["P", "Q", "mCD", "mDA"], ["mEF", "mFG", "mGH", "mHE"], ["P", "pBF", "mEF", "pAE"],
25:             ["Q", "pCG", "mFG", "pBF"], ["mCD", "pDH", "mGH", "pCG"], ["mDA", "pAE", "mHE", "pDH"]]
26:     faces = [tk.face(f, color="#3b82f6", opacity=0.35) for f in tri] + [tk.face(f, color="#f97316", opacity=0.35) for f in quad]
27: 
28:     aux = [tk.aux(a, b, dash=True, color="#9ca3af") for a, b in [("A", "B"), ("B", "C"), ("C", "D"), ("D", "A"),
29:             ("E", "F"), ("F", "G"), ("G", "H"), ("H", "E"), ("A", "E"), ("B", "F"), ("C", "G"), ("D", "H")]]
30:     aux += [tk.aux("O", x, dash=False, color="#374151") for x in ["mEF", "mFG", "pBF"]]
32: 
33:     Rsph = tk.dist((0, 0, 0), lrp((-s, -s, s), (s, -s, s), 0.5))
34:     prims = [tk.prim_sphere(r=Rsph, center=(0, 0, 0), color="#cbd5e1")]
35: 
36:     vs["la"] = tk.midpoint(P, Q); vs["lb"] = tk.midpoint(Q, mCD)
37:     vs["l11"] = tk.vadd(P, (0.0, -0.25, 0.0)); vs["l12"] = tk.vadd(Q, (0.0, -0.25, 0.0))
38: 
42: 
43:     a_len = tk.dist(P, Q); b_len = tk.dist(Q, mCD); d1 = tk.dist(vs["O"], mEF); d3 = tk.dist(vs["O"], pBF)
44:     is_arch = abs(a_len - b_len) < 1e-6 and abs(d1 - d3) < 1e-6
45: 
46:     correct = "不是半正多面体(非均匀截半变体)"
47:     wrongs = ["是截半立方体(标准半正多面体)", "是截角立方体(半正多面体)", "是半正多面体,球心O位置标错了"]
48:     opts = [correct] + wrongs; rng.shuffle(opts)
49:     letters = "ABCD"; options = {letters[i]: opts[i] for i in range(4)}
50:     ans = letters[opts.index(correct)]
51: 
52:     spec = tk.make_spec("截半变体识别", "S004", vs, faces=faces, aux_lines=aux, primitives=prims, labels=None,
53:                         camera=tk.auto_camera(vs, primitives=prims, azim=rng.uniform(28, 36), elev=rng.uniform(18, 26)))
54:     spec["labels"] = [{"point": "O", "text": "O"}, {"point": "P", "text": "P"}, {"point": "Q", "text": "Q"},
55:                       {"point": "la", "text": "a"}, {"point": "lb", "text": "b"},
56:                       {"point": "l11", "text": "1:2"}, {"point": "l12", "text": "1:1"},
57:                       {"point": "r1", "text": t1}, {"point": "r2", "text": t2}, {"point": "r3", "text": t3}]
58: 
59:     q = "如图,该多面体由某立方体连接各棱上分点再截去顶角得到。图中给出了原始立方体的隐藏棱(虚线)、外接球及球心O、一条四边形面的对角线与边长标注,以及不同棱上截取比例不一致的标记。根据这些辅助信息,判断该多面体是否为半正多面体,并给出结论。"
60:     return tk.item(spec, q, options, ans)

K26 Spec-to-Image Review

  • major/label_missing
    邻边长标注a在current与oblique两图中均不可见,仅见b
    spec_fix: 将la标签沿PQ边法向偏移0.3或调整camera,确保a不被P/1:2遮挡
  • major/plan_coverage
    plan要求被遮挡内部棱画虚线,但spec中edges=0,背棱与可见棱同为实线
    spec_fix: 显式添加多面体全部棱到spec edges,由renderer消隐并自动将不可见棱设为dash
  • minor/aux_line_clutter
    plan仅要求补全4条隐藏棱与底面,spec将立方体12条棱全设为虚线
    spec_fix: 仅将真正不可见的立方体棱保留为dash,其余可见棱改为solid或移除
  • minor/camera_or_view
    oblique_minus45与current差异不足,仍未显示缺失的a标签
    spec_fix: 第二视角正对目标四边形面并拉远orthoSize,确保a、b同时可见
What To Change
  • 显式添加多面体26条棱到edges并区分可见性
  • 修正立方体虚线aux仅保留隐藏棱
  • 调整la标签位置避免被遮挡
Preferred Primitives
none
Remove/Simplify
  • 移除多余的立方体可见棱虚线aux
  • 合并或替换重复视角
Label Fixes
  • 将la标签沿PQ边法向偏移0.3确保可见
  • 检查r1/r2/r3标签避免与O连线重叠
Camera Fixes
  • oblique视角绕至四边形面正面,适当降低elev
Toolkit/Renderer: 若renderer不支持edges自动消隐与dash样式,需在toolkit层增加hidden-line removal
representation: 多面体仅用face定义而未显式给出edges,导致无法按plan要求渲染被遮挡虚线棱;球体primitive使用正确。
readability: 整体结构可辨,但缺失a标签且背棱未虚线化,略有混乱;两视角差异小,帮助有限。
plan: 覆盖了球心O、外接球、P/Q比例、对角线及长度标注,但未实现被遮挡棱虚线与a标签可见性。
next: spec_rewrite

archimedean_solid_recognition__S010

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
绘制一个截半立方体的斜二测直观图,可见棱用黑色实线、8条被遮挡的棱用黑色虚线。过几何中心作一个平行于上侧正方形面的辅助截面平面α,截面轮廓为正八边形,可见边用双实线、不可见边用双虚线画出,顶点记为P1–P8。在上侧一个可见正方形面内画一条面对角线(细实线)。从立体最上方顶点向底面正方形中心作投影垂线VH(细虚线),垂足H落在中心并标注。
Must Render
[
  "截半立方体的全部棱线(实线与虚线区分可见与隐藏)",
  "正八边形截面及其顶点P1–P8",
  "正方形面上的面对角线",
  "顶点到底面中心的投影垂线VH"
]

Spec Summary

{
  "title": "截半立方体直观图",
  "meta": "24条棱中8条虚线表示隐藏棱;α为辅助正八边形截面,另有面对角线A-G与投影线V-H",
  "camera": {
    "position": [
      0.006,
      9.04,
      0.418
    ],
    "target": [
      0.006,
      0.002,
      0.34
    ],
    "orthoSize": 2.448
  },
  "counts": {
    "vertices": 40,
    "faces": 14,
    "edges": 24,
    "aux_lines": 18,
    "aux_faces": 1,
    "primitives": 0,
    "labels": 22
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "K",
      "text": "K"
    },
    {
      "point": "I",
      "text": "I"
    },
    {
      "point": "J",
      "text": "J"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    },
    {
      "point": "V",
      "text": "V"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "P1",
      "text": "P1"
    },
    {
      "point": "P2",
      "text": "P2"
    },
    {
      "point": "P3",
      "text": "P3"
    },
    {
      "point": "P4",
      "text": "P4"
    },
    {
      "point": "P5",
      "text": "P5"
    },
    {
      "point": "P6",
      "text": "P6"
    },
    {
      "point": "P7",
      "text": "P7"
    },
    {
      "point": "P8",
      "text": "P8"
    },
    {
      "point": "Alpha",
      "text": "α"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "K",
    "I",
    "J",
    "M",
    "N",
    "V",
    "H",
    "O",
    "P1",
    "P2",
    "P3",
    "P4",
    "P5",
    "P6",
    "P7",
    "P8",
    "Alpha",
    "Q1a",
    "Q1b",
    "Q2a",
    "Q2b",
    "Q3a",
    "Q3b",
    "Q4a",
    "Q4b",
    "Q5a",
    "Q5b",
    "Q6a",
    "Q6b",
    "Q7a",
    "Q7b",
    "Q8a",
    "Q8b"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "D",
        "C"
      ]
    },
    {
      "points": [
        "G",
        "K",
        "J",
        "I"
      ]
    },
    {
      "points": [
        "A",
        "E",
        "G",
        "F"
      ]
    },
    {
      "points": [
        "D",
        "M",
        "J",
        "N"
      ]
    },
    {
      "points": [
        "B",
        "E",
        "K",
        "M"
      ]
    },
    {
      "points": [
        "C",
        "F",
        "I",
        "N"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "E"
      ]
    },
    {
      "points": [
        "A",
        "C",
        "F"
      ]
    },
    {
      "points": [
        "D",
        "B",
        "M"
      ]
    },
    {
      "points": [
        "D",
        "C",
        "N"
      ]
    },
    {
      "points": [
        "G",
        "E",
        "K"
      ]
    },
    {
      "points": [
        "G",
        "F",
        "I"
      ]
    },
    {
      "points": [
        "J",
        "M",
        "K"
      ]
    },
    {
      "points": [
        "J",
        "N",
        "I"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "A"
      ]
    },
    {
      "points": [
        "G",
        "K"
      ]
    },
    {
      "points": [
        "K",
        "J"
      ],
      "dash": true
    },
    {
      "points": [
        "J",
        "I"
      ],
      "dash": true
    },
    {
      "points": [
        "I",
        "G"
      ]
    },
    {
      "points": [
        "A",
        "E"
      ]
    },
    {
      "points": [
        "E",
        "G"
      ]
    },
    {
      "points": [
        "G",
        "F"
      ]
    },
    {
      "points": [
        "F",
        "A"
      ]
    },
    {
      "points": [
        "D",
        "M"
      ],
      "dash": true
    },
    {
      "points": [
        "M",
        "J"
      ],
      "dash": true
    },
    {
      "points": [
        "J",
        "N"
      ],
      "dash": true
    },
    {
      "points": [
        "N",
        "D"
      ],
      "dash": true
    },
    {
      "points": [
        "B",
        "E"
      ]
    },
    {
      "points": [
        "E",
        "K"
      ]
    },
    {
      "points": [
        "K",
        "M"
      ]
    },
    {
      "points": [
        "M",
        "B"
      ]
    },
    {
      "points": [
        "C",
        "F"
      ]
    },
    {
      "points": [
        "F",
        "I"
      ]
    },
    {
      "points": [
        "I",
        "N"
      ],
      "dash": true
    },
    {
      "points": [
        "N",
        "C"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "G"
      ],
      "dash": false,
      "color": "#666666"
    },
    {
      "points": [
        "V",
        "H"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "P1",
        "P2"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "Q1a",
        "Q1b"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "P2",
        "P3"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "Q2a",
        "Q2b"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "P3",
        "P4"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "Q3a",
        "Q3b"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "P4",
        "P5"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "Q4a",
        "Q4b"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "P5",
        "P6"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "Q5a",
        "Q5b"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "P6",
        "P7"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "Q6a",
        "Q6b"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "P7",
        "P8"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "Q7a",
        "Q7b"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "P8",
        "P1"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "Q8a",
        "Q8b"
      ],
      "dash": false,
      "color": "#b45309"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "P1",
        "P2",
        "P3",
        "P4",
        "P5",
        "P6",
        "P7",
        "P8"
      ],
      "color": "#fde68a",
      "opacity": 0.13
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 斜二测投影参数;g 只改变左右斜向,题意不变
5:     c = math.sqrt(2.0) / 2.0

6:     s = 0.50
7:     d = 0.40
8:     g = rng.choice([1, -1])
30:     vs["O"] = proj((0, 0, 0))
31: 
32:     # 正八边形辅助截面 α:平行于一组正方形面;P1-P8 必须首尾正确相连
33:     z0 = 2.0 - math.sqrt(2.0)
34:     t = 1.0 - z0
35:     oct0 = [
36:         (1, t, z0), (t, 1, z0), (-t, 1, z0), (-1, t, z0),
41:     vs["Alpha"] = proj((0.18, 0.18, z0))
42: 
43:     faces = [
44:         {"points": ["A", "B", "D", "C"]}, {"points": ["G", "K", "J", "I"]},
45:         {"points": ["A", "E", "G", "F"]}, {"points": ["D", "M", "J", "N"]},
46:         {"points": ["B", "E", "K", "M"]}, {"points": ["C", "F", "I", "N"]},
47:         {"points": ["A", "B", "E"]}, {"points": ["A", "C", "F"]},
51:     ]
52: 
53:     all_edges = [
54:         ("A", "B"), ("B", "D"), ("D", "C"), ("C", "A"),
55:         ("G", "K"), ("K", "J"), ("J", "I"), ("I", "G"),
56:         ("A", "E"), ("E", "G"), ("G", "F"), ("F", "A"),
57:         ("D", "M"), ("M", "J"), ("J", "N"), ("N", "D"),
59:         ("C", "F"), ("F", "I"), ("I", "N"), ("N", "C")
60:     ]
61:     hidden_edges = {
62:         ("D", "M"), ("M", "J"), ("J", "N"), ("N", "D"),
63:         ("K", "J"), ("J", "I"), ("I", "N"), ("N", "C")
64:     }
65:     edges = [
66:         tk.edge(a, b, dash=((a, b) in hidden_edges or (b, a) in hidden_edges))
67:         for a, b in all_edges
68:     ]
69: 
70:     aux_lines = []
71:     # 上侧可见正方形面 A-E-G-F 内的面对角线
72:     aux_lines.append(tk.aux("A", "G", dash=False, color="#666666"))
73:     # 从最上方顶点 V 到下侧正方形面中心 H 的投影垂线
74:     aux_lines.append(tk.aux("V", "H", dash=True, color="#666666"))
75: 
76:     # 正八边形截面边:显式修复为 P_i 到 P_{i+1},并加一条内侧平行线形成“双线”
77:     visible_oct_edges = {0, 1, 2, 7}
78:     cx = sum(vs[f"P{i}"][0] for i in range(1, 9)) / 8.0
79:     cz = sum(vs[f"P{i}"][2] for i in range(1, 9)) / 8.0
80:     eps = 0.035
81:     for i in range(8):
82:         a = f"P{i + 1}"
83:         b = f"P{((i + 1) % 8) + 1}"  # 关键修复:连接到下一个顶点,而不是自环
84:         dash = i not in visible_oct_edges
85:         aux_lines.append(tk.aux(a, b, dash=dash, color="#b45309"))
86: 
87:         pa, pb = vs[a], vs[b]
88:         dx, dz = pb[0] - pa[0], pb[2] - pa[2]
89:         ln = math.sqrt(dx * dx + dz * dz) + 1e-12
95:         vs[qa] = (pa[0] + eps * nx, pa[1], pa[2] + eps * nz)
96:         vs[qb] = (pb[0] + eps * nx, pb[1], pb[2] + eps * nz)
97:         aux_lines.append(tk.aux(qa, qb, dash=dash, color="#b45309"))
98: 
99:     aux_faces = [tk.face([f"P{i}" for i in range(1, 9)], color="#fde68a", opacity=0.13)]
100: 
101:     spec = tk.make_spec(
102:         "截半立方体直观图",
103:         "24条棱中8条虚线表示隐藏棱;α为辅助正八边形截面,另有面对角线A-G与投影线V-H",
104:         vs,
105:         faces=faces,
106:         edges=edges,
107:         aux_lines=aux_lines,
108:         aux_faces=aux_faces,
109:         labels=[],
110:         camera=tk.flat_camera(vs, pad=1.55)
111:     )
112:     spec["labels"] = (
113:         [{"point": x, "text": x} for x in ["A", "B", "C", "D", "E", "G", "K", "I", "J", "M", "N", "V", "H"]]
114:         + [{"point": f"P{i}", "text": f"P{i}"} for i in range(1, 9)]
115:         + [{"point": "Alpha", "text": "α"}]
116:     )
117: 
118:     # 用面型与等棱长计算确认正确项,而不是依赖选项位置
119:     tri_count = sum(1 for f in faces if len(f["points"]) == 3)
120:     sq_count = sum(1 for f in faces if len(f["points"]) == 4)
121:     raw_lengths = [
122:         math.sqrt(
123:             (R[a][0] - R[b][0]) ** 2 +
124:             (R[a][1] - R[b][1]) ** 2 +
125:             (R[a][2] - R[b][2]) ** 2
126:         )
127:         for a, b in all_edges
128:     ]
129:     is_cuboctahedron = (tri_count == 8 and sq_count == 6 and max(raw_lengths) - min(raw_lengths) < 1e-9)
130: 
131:     correct_text = "是半正多面体,为截半立方体(立方八面体)"
141:     answer = [k for k, v in options.items() if v == correct_text][0]
142: 
143:     question = "图中展示了一个三维多面体及其辅助截面、投影线与面对角线。判断该多面体是否为半正多面体;若是,请指出其具体名称。"
144:     return tk.item(spec, question, options, answer)

K26 Spec-to-Image Review

  • major/plan_coverage
    H标注在八边形截面高度,未落在底面正方形中心,VH虚线终止于截面而非底面
    spec_fix: 将H坐标修正为底面正方形中心,VH aux_line终点同步调整至该面心
  • minor/label_missing
    labels列表缺少顶点F,图中上侧正方形面AEGF无F标注
    spec_fix: 补充F标签;若V与F为同一点,应统一命名并在labels中体现
  • minor/aux_line_clutter
    正八边形双线用16个Q辅助顶点和16条aux_lines硬搓,spec冗余
    spec_fix: 如toolkit支持polyline双线样式则改用;否则保留并清理未用辅助顶点
What To Change
  • 修正H为底面正方形中心坐标
  • 补充F标签或统一V/F命名
  • 检查P1-P8标签避免与截面边重叠
Preferred Primitives
none
Remove/Simplify
  • 移除未进入labels的Q1a-Q8b等冗余辅助顶点名(保留aux_lines)
Label Fixes
  • 添加F标签
  • 将H移至底面中心
  • 确保α不被截面线遮挡
Camera Fixes
none
Toolkit/Renderer: none
representation: 截半立方体线框与正八边形截面结构正确,但VH投影关键辅助元素位置错误
readability: 单图较拥挤,P5-P8与K、M标签密集,但主体可辨;双线截面表达符合教材习惯
plan: 缺少底面中心H的正确标注,导致投影垂线VH未表达到底面,覆盖度不足
next: spec_rewrite

archimedean_solid_recognition__S019

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
以斜二测视角绘制一个截角二十面体,可见棱用黑实线;以多面体中心为球心作一个半径略大于多面体的球,球面遮挡下半部,被遮挡的多面体棱改画虚线,球面可见轮廓及与多面体的截交线用实线画出;内部用浅灰虚线画一个同心正二十面体,其12个顶点分别对准截角二十面体的12个正五边形面中心;顶部区域完整露出一个正五边形面及环绕它的5个正六边形面中的3个。
Must Render
[
  "截角二十面体的可见棱(黑实线)",
  "被球面遮挡的截角二十面体棱(虚线)",
  "遮挡球面的可见轮廓及与多面体的截交线",
  "内部同心正二十面体的虚线框架",
  "顶部完整的正五边形面及相邻的若干正六边形面"
]

Spec Summary

{
  "title": "截角二十面体",
  "meta": "archimedean_solid",
  "camera": {
    "position": [
      5.567,
      4.321,
      7.951
    ],
    "target": [
      0.0,
      0.0,
      0.0
    ],
    "orthoSize": 2.589
  },
  "counts": {
    "vertices": 89,
    "faces": 32,
    "edges": 90,
    "aux_lines": 46,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 0
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.4471354279820856,
      "position": [
        0,
        0,
        0
      ],
      "color": "#dddddd"
    }
  ],
  "labels": [],
  "vertex_names": [
    "0_4",
    "0_8",
    "0_1",
    "0_9",
    "0_5",
    "1_8",
    "1_6",
    "1_7",
    "1_9",
    "1_0",
    "2_4",
    "2_10",
    "2_3",
    "2_11",
    "2_5",
    "3_10",
    "3_6",
    "3_7",
    "3_11",
    "3_2",
    "4_0",
    "4_8",
    "4_10",
    "4_2",
    "4_5",
    "5_0",
    "5_4",
    "5_2",
    "5_11",
    "5_9",
    "6_1",
    "6_8",
    "6_10",
    "6_3",
    "6_7",
    "7_1",
    "7_6",
    "7_3",
    "7_11",
    "7_9",
    "8_0",
    "8_1",
    "8_6",
    "8_10",
    "8_4",
    "9_0",
    "9_5",
    "9_11",
    "9_7",
    "9_1",
    "10_4",
    "10_8",
    "10_6",
    "10_3",
    "10_2",
    "11_5",
    "11_2",
    "11_3",
    "11_7",
    "11_9",
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15",
    "c16"
  ],
  "faces_sample": [
    {
      "points": [
        "0_4",
        "0_8",
        "0_1",
        "0_9",
        "0_5"
      ]
    },
    {
      "points": [
        "1_8",
        "1_6",
        "1_7",
        "1_9",
        "1_0"
      ]
    },
    {
      "points": [
        "2_4",
        "2_10",
        "2_3",
        "2_11",
        "2_5"
      ]
    },
    {
      "points": [
        "3_10",
        "3_6",
        "3_7",
        "3_11",
        "3_2"
      ]
    },
    {
      "points": [
        "4_0",
        "4_8",
        "4_10",
        "4_2",
        "4_5"
      ]
    },
    {
      "points": [
        "5_0",
        "5_4",
        "5_2",
        "5_11",
        "5_9"
      ]
    },
    {
      "points": [
        "6_1",
        "6_8",
        "6_10",
        "6_3",
        "6_7"
      ]
    },
    {
      "points": [
        "7_1",
        "7_6",
        "7_3",
        "7_11",
        "7_9"
      ]
    },
    {
      "points": [
        "8_0",
        "8_1",
        "8_6",
        "8_10",
        "8_4"
      ]
    },
    {
      "points": [
        "9_0",
        "9_5",
        "9_11",
        "9_7",
        "9_1"
      ]
    },
    {
      "points": [
        "10_4",
        "10_8",
        "10_6",
        "10_3",
        "10_2"
      ]
    },
    {
      "points": [
        "11_5",
        "11_2",
        "11_3",
        "11_7",
        "11_9"
      ]
    },
    {
      "points": [
        "0_4",
        "4_0",
        "4_8",
        "8_4",
        "8_0",
        "0_8"
      ]
    },
    {
      "points": [
        "0_8",
        "8_0",
        "8_1",
        "1_8",
        "1_0",
        "0_1"
      ]
    },
    {
      "points": [
        "0_1",
        "1_0",
        "1_9",
        "9_1",
        "9_0",
        "0_9"
      ]
    },
    {
      "points": [
        "0_9",
        "9_0",
        "9_5",
        "5_9",
        "5_0",
        "0_5"
      ]
    },
    {
      "points": [
        "0_5",
        "5_0",
        "5_4",
        "4_5",
        "4_0",
        "0_4"
      ]
    },
    {
      "points": [
        "1_8",
        "8_1",
        "8_6",
        "6_8",
        "6_1",
        "1_6"
      ]
    },
    {
      "points": [
        "1_6",
        "6_1",
        "6_7",
        "7_6",
        "7_1",
        "1_7"
      ]
    },
    {
      "points": [
        "1_7",
        "7_1",
        "7_9",
        "9_7",
        "9_1",
        "1_9"
      ]
    },
    {
      "points": [
        "2_4",
        "4_2",
        "4_10",
        "10_4",
        "10_2",
        "2_10"
      ]
    },
    {
      "points": [
        "2_10",
        "10_2",
        "10_3",
        "3_10",
        "3_2",
        "2_3"
      ]
    },
    {
      "points": [
        "2_3",
        "3_2",
        "3_11",
        "11_3",
        "11_2",
        "2_11"
      ]
    },
    {
      "points": [
        "2_11",
        "11_2",
        "11_5",
        "5_11",
        "5_2",
        "2_5"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "2_4",
        "2_5"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "0_5",
        "5_0"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "5_9",
        "9_5"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "4_0",
        "4_8"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "9_1",
        "9_7"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "3_7",
        "7_3"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "0_4",
        "0_8"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "8_1",
        "8_6"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "6_3",
        "6_7"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "9_11",
        "9_5"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "11_7",
        "7_11"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "0_5",
        "0_9"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "2_10",
        "2_3"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "0_8",
        "8_0"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "0_4",
        "0_5"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "1_7",
        "1_9"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "7_9",
        "9_7"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "10_4",
        "10_8"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "8_10",
        "8_4"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "10_4",
        "4_10"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "8_0",
        "8_1"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "11_2",
        "11_3"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "6_7",
        "7_6"
      ],
      "color": "#1a1a1a"
    },
    {
      "points": [
        "5_0",
        "5_9"
      ],
      "color": "#1a1a1a"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "0",
        "4"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "0",
        "8"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "0",
        "1"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "0",
        "9"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "0",
        "5"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "1",
        "8"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "1",
        "6"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "1",
        "7"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "1",
        "9"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "2",
        "4"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "2",
        "10"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "2",
        "3"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "2",
        "11"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "2",
        "5"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "3",
        "10"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "3",
        "6"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "3",
        "7"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "3",
        "11"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "4",
        "8"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "4",
        "10"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "4",
        "5"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "5",
        "11"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "5",
        "9"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "6",
        "8"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "6",
        "10"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "6",
        "7"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "7",
        "11"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "7",
        "9"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "8",
        "10"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "9",
        "11"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "c0",
        "c1"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c1",
        "c2"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c2",
        "c3"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c3",
        "c4"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c4",
        "c5"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c5",
        "c6"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c6",
        "c7"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c7",
        "c8"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c8",
        "c9"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c9",
        "c10"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c10",
        "c11"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c11",
        "c12"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c12",
        "c13"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c13",
        "c14"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c14",
        "c15"
      ],
      "dash": false,
      "color": "#444444"
    },
    {
      "points": [
        "c15",
        "c0"
      ],
      "dash": false,
      "color": "#444444"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     p = (1 + math.sqrt(5)) / 2
4:     s = (5 - math.sqrt(5)) / 10
5:     V = [(0, 1, p), (0, -1, p), (0, 1, -p), (0, -1, -p),

41:     Rt = max(math.sqrt(sum(x * x for x in v)) for v in T.values())
42:     Rs = Rt * rng.uniform(0.82, 0.92)
43:     cam = tk.auto_camera(vs, azim=35, elev=24, pad=1.35)
44:     pos, tgt = cam["position"], cam["target"]
45:     n = tk.unit(tk.vsub(pos, tgt))
46:     tmp = (0, 1, 0) if abs(n[1]) < 0.9 else (1, 0, 0)
47:     u = tk.unit(tk.cross(n, tmp))
70:         else:
71:             ED.append(tk.edge(a, b, dash=False, color="#1a1a1a"))
72:     spec = tk.make_spec("截角二十面体", "archimedean_solid", vs, faces=F, edges=ED,
73:                         aux_lines=AL + CE, primitives=[tk.prim_sphere(Rs, center=(0, 0, 0), color="#dddddd")],
74:                         camera=cam, labels=[])
75:     opts = {"A": "半正多面体(阿基米德立体)中的截角二十面体",
76:             "B": "约翰逊多面体(顶点配置不一致)",
77:             "C": "截半二十面体(由三角形与五边形围成)",
78:             "D": "正十二面体与正六棱柱的组合体"}
81:     options = {k: opts[k] for k in L}
82:     ans = [k for k, v in options.items() if "截角二十面体" in v][0]
83:     return tk.item(spec, "如图,图中实体线框表示一个被同心球面部分遮挡的多面体,内部浅灰虚线为其对应的正二十面体框架。根据可见部分的面形与顶点连接方式判断,该多面体属于( )", options, ans)

K26 Spec-to-Image Review

  • major/plan_coverage
    plan要求球半径略大于多面体,代码却设Rs=0.82~0.92Rt,导致球与多面体相交;若严格按plan包裹多面体则不应存在截交线
    spec_fix: 统一需求:若需截交线则Rs应略小于Rt(如0.9Rt),若需球包裹多面体则删截交线并令Rs>Rt
  • minor/tangent_or_section
    截交线由c0~c15共16段aux_lines硬搓,渲染呈明显多边形而非圆滑圆弧
    spec_fix: 截交线离散为32~64段,或调用toolkit的section_circle/ellipse primitive
What To Change
  • 明确球半径与多面体关系:若需截交线则Rs<Rt,若需包裹则Rs>Rt并删除截交线
  • 截交线分段数提高至32~64段
Preferred Primitives
  • section_circle
Remove/Simplify
  • 若球包裹多面体,移除截交线aux_lines;若保留截交线,修正球半径参数
Label Fixes
  • none
Camera Fixes
  • none
Toolkit/Renderer: 建议toolkit增加section_circle primitive用于绘制球面与多面体的圆滑截交线
representation: 截角二十面体mesh表示正确,球primitive使用合理,但球半径与截交线需求在plan层面存在矛盾,spec未正确处理
readability: 单视角斜二测清晰度尚可,球面半透明、虚实线区分明显,但多面体穿出球面与题意描述冲突
plan: 顶部五边形面、内部正二十面体框架、虚实线处理均已覆盖,但球-多面体位置关系与plan文字不符
next: spec_rewrite

archimedean_solid_recognition__S014

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 6 major_spec_issue minor hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
绘制一个截半立方体(cuboctahedron)的斜透视图,可见8个正三角形面与6个正方形面,所有隐藏棱用灰色虚线表示。添加一个过立体中心且垂直于上下正方形面中心轴的辅助截面,截痕为正六边形,用红色实线标出。从右上前方一顶点向底面所在平面作垂线段(投影线),垂足标为H;连接该顶点与左下后方相对顶点的体对角线,被遮挡段用虚线。
Must Render
[
  "截半立方体的全部14个面(8正三角+6正方)的边界线",
  "正六边形截痕(红色实线)",
  "顶点到底面的垂线段(投影线)及垂足H",
  "体对角线及其被遮挡的虚线段",
  "所有不可见棱的灰色虚线"
]

Spec Summary

{
  "title": "截半立方体",
  "meta": "含全部14个面:8个正三角形面与6个正方形面;由faces推导全部棱,渲染时不可见棱为灰色虚线;红色实线为正六边形截痕,蓝色为投影线。",
  "camera": {
    "position": [
      5.774,
      3.768,
      8.579
    ],
    "target": [
      0.062,
      -0.101,
      -0.036
    ],
    "orthoSize": 2.922
  },
  "counts": {
    "vertices": 14,
    "faces": 14,
    "edges": 0,
    "aux_lines": 9,
    "aux_faces": 1,
    "primitives": 0,
    "labels": 14
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "I",
      "text": "I"
    },
    {
      "point": "J",
      "text": "J"
    },
    {
      "point": "K",
      "text": "K"
    },
    {
      "point": "L",
      "text": "L"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "P",
      "text": "P"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "O",
    "P"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "E",
        "B",
        "F"
      ],
      "color": "#f1f3f5",
      "opacity": 0.3
    },
    {
      "points": [
        "D",
        "H",
        "C",
        "G"
      ],
      "color": "#f1f3f5",
      "opacity": 0.3
    },
    {
      "points": [
        "A",
        "I",
        "D",
        "J"
      ],
      "color": "#f1f3f5",
      "opacity": 0.3
    },
    {
      "points": [
        "B",
        "L",
        "C",
        "K"
      ],
      "color": "#f1f3f5",
      "opacity": 0.3
    },
    {
      "points": [
        "E",
        "I",
        "H",
        "L"
      ],
      "color": "#f1f3f5",
      "opacity": 0.3
    },
    {
      "points": [
        "F",
        "J",
        "G",
        "K"
      ],
      "color": "#f1f3f5",
      "opacity": 0.3
    },
    {
      "points": [
        "A",
        "I",
        "E"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "A",
        "F",
        "J"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "B",
        "E",
        "L"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "B",
        "K",
        "F"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "D",
        "H",
        "I"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "D",
        "J",
        "G"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "C",
        "L",
        "H"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    },
    {
      "points": [
        "C",
        "G",
        "K"
      ],
      "color": "#e7f5ff",
      "opacity": 0.26
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "F",
        "J"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "J",
        "D"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "D",
        "H"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "H",
        "L"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "L",
        "B"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "B",
        "F"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "A",
        "P"
      ],
      "dash": false,
      "color": "#1971c2"
    },
    {
      "points": [
        "A",
        "O"
      ],
      "dash": false,
      "color": "#343a40"
    },
    {
      "points": [
        "O",
        "C"
      ],
      "dash": true,
      "color": "#343a40"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "F",
        "J",
        "D",
        "H",
        "L",
        "B"
      ],
      "color": "#ffe3e3",
      "opacity": 0.14
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     scale = rng.uniform(1.06, 1.26)
5:     az = rng.uniform(29.0, 35.0)

21:         return (p[0], c * p[1] - s * p[2], s * p[1] + c * p[2])
22: 
23:     # 将三重对称轴 (1,1,1) 转到竖直 y 轴,使过中心辅助截面为正六边形
24:     phi = math.degrees(math.atan2(-math.sqrt(2.0), 1.0))
25:     vs = {}
26:     for name, p in raw.items():
27:         p1 = tk.rot_y(p, -45.0)
29:         vs[name] = (scale * p2[0], scale * p2[1], scale * p2[2])
30: 
31:     faces = [
32:         {"points": ["A", "E", "B", "F"], "color": "#f1f3f5", "opacity": 0.30},
33:         {"points": ["D", "H", "C", "G"], "color": "#f1f3f5", "opacity": 0.30},
34:         {"points": ["A", "I", "D", "J"], "color": "#f1f3f5", "opacity": 0.30},
35:         {"points": ["B", "L", "C", "K"], "color": "#f1f3f5", "opacity": 0.30},
49:     # 过立体中心的正六边形截痕,红色实线标出
50:     hex_ord = ["F", "J", "D", "H", "L", "B"]
51:     aux_lines = [
52:         tk.aux(hex_ord[i], hex_ord[(i + 1) % 6], dash=False, color="#e03131")
53:         for i in range(6)
54:     ]
55:     aux_faces = [
56:         {"points": hex_ord, "color": "#ffe3e3", "opacity": 0.14}
57:     ]
58: 
59:     # 中心点 O 用于分段表示体对角线;不与任何原顶点重名
63:     y_bottom = min(vs[name][1] for name in raw)
64:     vs["P"] = (vs["A"][0], y_bottom, vs["A"][2])
65:     aux_lines.append(tk.aux("A", "P", dash=False, color="#1971c2"))
66: 
67:     # 体对角线 A-C:靠近观察端为实线,被遮挡段用虚线
68:     aux_lines.append(tk.aux("A", "O", dash=False, color="#343a40"))
69:     aux_lines.append(tk.aux("O", "C", dash=True, color="#343a40"))
70: 
71:     camera = tk.auto_camera(vs, azim=az, elev=el, pad=1.48)
72:     labels = list("ABCDEFGHIJKL") + ["O", "P"]
73: 
74:     spec = tk.make_spec(
75:         "截半立方体",
76:         "含全部14个面:8个正三角形面与6个正方形面;由faces推导全部棱,渲染时不可见棱为灰色虚线;红色实线为正六边形截痕,蓝色为投影线。",
77:         vs,
78:         faces=faces,
79:         aux_lines=aux_lines,
80:         aux_faces=aux_faces,
81:         labels=labels,
82:         camera=camera
83:     )
84: 
85:     # 基于几何结构计算识别结论
86:     tri_count = sum(1 for f in faces if len(f["points"]) == 3)
87:     sq_count = sum(1 for f in faces if len(f["points"]) == 4)
88: 
89:     edge_set = set()
90:     for f in faces:
91:         pts = f["points"]
92:         for i in range(len(pts)):
93:             a, b = pts[i], pts[(i + 1) % len(pts)]
94:             edge_set.add(tuple(sorted((a, b))))
95:     edge_lengths = [tk.dist(vs[a], vs[b]) for a, b in edge_set]
96:     equal_edges = max(edge_lengths) - min(edge_lengths) < 1e-6
97: 
98:     def regular_triangle(f):
99:         pts = f["points"]
100:         ds = [tk.dist(vs[pts[i]], vs[pts[(i + 1) % 3]]) for i in range(3)]
101:         return max(ds) - min(ds) < 1e-6
102: 
110:         return abs(tk.dot(u, v)) < 1e-6
111: 
112:     regular_faces = all(
113:         regular_triangle(f) if len(f["points"]) == 3 else square_face(f)
114:         for f in faces
115:     )
116:     is_cuboctahedron = (tri_count == 8 and sq_count == 6 and len(edge_set) == 24
117:                         and equal_edges and regular_faces)
118: 
119:     correct_text = "是半正多面体,具体为截半立方体(cuboctahedron)"
120:     options = {
121:         "A": correct_text,
126:     answer = "A" if is_cuboctahedron else "C"
127: 
128:     question = "如图,图中展示了一个带有辅助截面、投影线与体对角线的三维多面体。观察其面形状、面数及顶点连接方式,判断该立体是否为半正多面体;若是,请指出其具体名称。"
129: 
130:     return tk.item(
131:         spec,
132:         question,
135:         answer_text=options[answer],
136:         meta={
137:             "triangular_faces": tri_count,
138:             "square_faces": sq_count,
139:             "edges": len(edge_set),
140:             "equal_edges": equal_edges,
141:             "projection_foot_label": "P"
142:         }
143:     )

K26 Spec-to-Image Review

  • major/label_missing
    plan明确要求垂足标为H,但spec将垂足命名为P,且原顶点H已被六边形截痕顶点占用,导致关键辅助点与plan描述完全不符
    spec_fix: 将垂足P重命名为H,原顶点H改为未用字母如M,同步更新faces、aux_lines、labels及meta中所有H引用
What To Change
  • 垂足标签由P改为H
  • 原顶点H及其在faces、aux_lines中的引用改为M
Preferred Primitives
none
Remove/Simplify
none
Label Fixes
  • 垂足标注为H
  • 原H顶点改为M避免命名冲突
Camera Fixes
none
Toolkit/Renderer: none
representation: 截半立方体用faces推导棱并区分虚实线,正六边形截痕、体对角线分段及投影线表示合理,面透明度适中
readability: 双视角有助于理解空间关系,但垂足标签错误会导致读者无法按plan定位关键辅助点H
plan: 除垂足命名错误外,其余几何元素(14个面、正六边形截痕、投影线、体对角线、隐藏棱)均已覆盖
next: spec_rewrite

archimedean_solid_recognition__S015

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 6 major_spec_issue minor hold

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
斜二测视角下,一个截半立方体居中放置,其朝上的正方形面与一个正四棱锥共面拼接(锥顶标记为P)。截半立方体的左后方被一个正三棱柱部分遮挡,两者共享一条竖直公共棱。截半立方体所有被遮挡的棱用虚线绘出,可见的正三角形面以浅灰色填充,可见的正方形面留白。补画一条从截半立方体中心O到后方被遮挡顶点Q的辅助虚线,并标注可见顶点A、B、C。
Must Render
[
  "截半立方体可见的正三角形面(浅灰填充)与正方形面(留白)",
  "截半立方体被遮挡的隐藏棱(虚线)",
  "正四棱锥及其与截半立方体的拼接线",
  "正三棱柱的遮挡部分及公共竖直棱",
  "从中心O到后方顶点Q的辅助虚线"
]

Spec Summary

{
  "title": "半正多面体识别",
  "meta": "截半立方体:正三角形面浅灰、正方形面留白;后方及被三棱柱遮挡棱为虚线;OQ为辅助虚线",
  "camera": {
    "position": [
      5.192,
      3.657,
      6.715
    ],
    "target": [
      -0.359,
      0.082,
      -0.154
    ],
    "orthoSize": 2.695
  },
  "counts": {
    "vertices": 18,
    "faces": 23,
    "edges": 37,
    "aux_lines": 13,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 8
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Q",
      "text": "Q"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "Q",
    "O",
    "P",
    "M",
    "N",
    "U",
    "V"
  ],
  "faces_sample": [
    {
      "points": [
        "D",
        "A",
        "H",
        "K"
      ],
      "color": "#ffffff",
      "opacity": 0.13
    },
    {
      "points": [
        "F",
        "Q",
        "J",
        "C"
      ],
      "color": "#ffffff",
      "opacity": 0.13
    },
    {
      "points": [
        "B",
        "D",
        "E",
        "F"
      ],
      "color": "#ffffff",
      "opacity": 0.13
    },
    {
      "points": [
        "G",
        "H",
        "I",
        "J"
      ],
      "color": "#ffffff",
      "opacity": 0.13
    },
    {
      "points": [
        "B",
        "A",
        "G",
        "C"
      ],
      "color": "#ffffff",
      "opacity": 0.13
    },
    {
      "points": [
        "E",
        "K",
        "I",
        "Q"
      ],
      "color": "#ffffff",
      "opacity": 0.13
    },
    {
      "points": [
        "B",
        "D",
        "A"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "B",
        "C",
        "F"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "E",
        "K",
        "D"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "E",
        "F",
        "Q"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "G",
        "A",
        "H"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "G",
        "J",
        "C"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "I",
        "H",
        "K"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "I",
        "Q",
        "J"
      ],
      "color": "#d9d9d9",
      "opacity": 0.54
    },
    {
      "points": [
        "P",
        "A",
        "G"
      ],
      "color": "#ffffff",
      "opacity": 0.18
    },
    {
      "points": [
        "P",
        "G",
        "C"
      ],
      "color": "#ffffff",
      "opacity": 0.18
    },
    {
      "points": [
        "P",
        "C",
        "B"
      ],
      "color": "#ffffff",
      "opacity": 0.18
    },
    {
      "points": [
        "P",
        "B",
        "A"
      ],
      "color": "#ffffff",
      "opacity": 0.18
    },
    {
      "points": [
        "I",
        "M",
        "N"
      ],
      "color": "#e5e5e5",
      "opacity": 0.46
    },
    {
      "points": [
        "J",
        "V",
        "U"
      ],
      "color": "#e5e5e5",
      "opacity": 0.46
    },
    {
      "points": [
        "I",
        "M",
        "U",
        "J"
      ],
      "color": "#e5e5e5",
      "opacity": 0.46
    },
    {
      "points": [
        "M",
        "N",
        "V",
        "U"
      ],
      "color": "#e5e5e5",
      "opacity": 0.46
    },
    {
      "points": [
        "N",
        "I",
        "J",
        "V"
      ],
      "color": "#e5e5e5",
      "opacity": 0.46
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "A",
        "D"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "A",
        "G"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "A",
        "H"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "B",
        "C"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "B",
        "D"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "B",
        "F"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "C",
        "F"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "C",
        "G"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "C",
        "J"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "D",
        "E"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "D",
        "K"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "E",
        "F"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "E",
        "K"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "E",
        "Q"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "F",
        "Q"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "G",
        "H"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "G",
        "J"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "H",
        "I"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "H",
        "K"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "I",
        "J"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "I",
        "K"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "I",
        "Q"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "J",
        "Q"
      ],
      "dash": true,
      "color": "#555555"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "Q"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "C",
        "J"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "E",
        "K"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "E",
        "Q"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "F",
        "Q"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "G",
        "H"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "G",
        "J"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "H",
        "I"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "H",
        "K"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "I",
        "J"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "I",
        "K"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "I",
        "Q"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "J",
        "Q"
      ],
      "dash": true,
      "color": "#666666"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     r2 = math.sqrt(2.0)
5:     h = rng.uniform(0.92, 1.12)

36:     vs["V"] = (-1.0 - prism_s / 2.0, r2 / 2.0, z0 - prism_s * math.sqrt(3.0) / 2.0)
37: 
38:     square_faces = [
39:         ["D", "A", "H", "K"],
40:         ["F", "Q", "J", "C"],
41:         ["B", "D", "E", "F"],
42:         ["G", "H", "I", "J"],
44:         ["E", "K", "I", "Q"],
45:     ]
46:     triangle_faces = [
47:         ["B", "D", "A"],
48:         ["B", "C", "F"],
49:         ["E", "K", "D"],
50:         ["E", "F", "Q"],
54:         ["I", "Q", "J"],
55:     ]
56:     pyramid_faces = [
57:         ["P", "A", "G"],
58:         ["P", "G", "C"],
59:         ["P", "C", "B"],
60:         ["P", "B", "A"],
61:     ]
62:     prism_faces = [
63:         ["I", "M", "N"],
64:         ["J", "V", "U"],
65:         ["I", "M", "U", "J"],
66:         ["M", "N", "V", "U"],
68:     ]
69: 
70:     faces = []
71:     for f in square_faces:
72:         faces.append(tk.face(f, "#ffffff", 0.13))
73:     for f in triangle_faces:
74:         faces.append(tk.face(f, "#d9d9d9", 0.54))
75:     for f in pyramid_faces:
76:         faces.append(tk.face(f, "#ffffff", 0.18))
77:     for f in prism_faces:
78:         faces.append(tk.face(f, "#e5e5e5", 0.46))
79: 
80:     def key(a, b):
81:         return tuple(sorted((a, b)))
82: 
83:     cuboct_faces = square_faces + triangle_faces
84:     cuboct_edges = set()
85:     for poly in cuboct_faces:
86:         for i in range(len(poly)):
87:             cuboct_edges.add(key(poly[i], poly[(i + 1) % len(poly)]))
88: 
89:     # 显式修复:截半立方体后方/被正三棱柱遮挡的棱全部以虚线绘出
90:     hidden_cuboct_edges = {
91:         key("G", "H"), key("H", "I"), key("I", "J"), key("J", "G"),
92:         key("I", "H"), key("I", "K"), key("H", "K"),
93:         key("I", "Q"), key("J", "Q"), key("J", "C"),
94:         key("F", "Q"), key("E", "Q"), key("E", "K"),
95:     }
96: 
97:     edges = []
98:     for a, b in sorted(cuboct_edges):
99:         is_hidden = key(a, b) in hidden_cuboct_edges
100:         edges.append(tk.edge(a, b, dash=is_hidden, color="#555555" if is_hidden else "#222222"))
101: 
102:     # 正四棱锥棱及其与共底面 A-G-C-B 的拼接线
103:     pyramid_edges = {
104:         key("P", "A"), key("P", "G"), key("P", "C"), key("P", "B"),
105:         key("A", "G"), key("G", "C"), key("C", "B"), key("B", "A"),
106:     }
107:     for a, b in sorted(pyramid_edges):
108:         if key(a, b) not in cuboct_edges:
109:             edges.append(tk.edge(a, b, dash=False, color="#222222"))
110: 
111:     # 正三棱柱棱;公共竖直棱 I-J 加深,强调共享棱
112:     prism_edges = {
113:         key("I", "M"), key("M", "N"), key("N", "I"),
114:         key("J", "U"), key("U", "V"), key("V", "J"),
115:         key("I", "J"), key("M", "U"), key("N", "V"),
116:     }
117:     for a, b in sorted(prism_edges):
118:         edges.append(tk.edge(a, b, dash=False, color="#222222"))
119: 
120:     aux_lines = [tk.aux("O", "Q", True, "#555555")]
121: 
122:     # 另在 aux_lines 中登记遮挡隐藏棱,保证 spec 摘要中也能显式体现“隐藏棱虚线”
123:     for a, b in sorted(hidden_cuboct_edges):
124:         aux_lines.append(tk.aux(a, b, True, "#666666"))
125: 
126:     labels = [
127:         {"point": "A", "text": "A"},
128:         {"point": "B", "text": "B"},
129:         {"point": "C", "text": "C"},
130:         {"point": "O", "text": "O"},
135:     ]
136: 
137:     spec = tk.make_spec(
138:         "半正多面体识别",
139:         "截半立方体:正三角形面浅灰、正方形面留白;后方及被三棱柱遮挡棱为虚线;OQ为辅助虚线",
140:         vs,
141:         faces=faces,
142:         edges=edges,
143:         aux_lines=aux_lines,
144:         labels=None,
145:         camera=tk.auto_camera(vs, azim=azi, elev=elv, pad=1.62),
146:     )
147:     spec["labels"] = labels
148: 
149:     tri_count = sum(1 for f in cuboct_faces if len(f) == 3)
150:     square_count = sum(1 for f in cuboct_faces if len(f) == 4)
151:     correct_text = "截半立方体(cuboctahedron)" if tri_count == 8 and square_count == 6 else "未知半正多面体"
152: 
153:     opts = [
154:         correct_text,

K26 Spec-to-Image Review

  • major/plan_coverage
    正方形面opacity仅0.13、三角形面0.54,渲染后呈纯线框,浅灰填充与留白效果不可见
    spec_fix: 正方形面opacity提至0.85以上,三角形面opacity提至0.6-0.8并确保颜色#d9d9d9在白色背景可见
  • major/aux_line_clutter
    隐藏棱同时在edges与aux_lines中登记,导致同一条棱虚线重复绘制,图面杂乱
    spec_fix: 删除aux_lines中重复隐藏棱,仅保留edges中的虚线绘制
  • minor/mesh_clutter
    公共棱IJ在cuboct_edges中被标为虚线,又在prism_edges中绘为实线,属性冲突且重复
    spec_fix: 统一公共棱IJ为单一实线并加粗,从hidden_cuboct_edges中移除IJ
  • minor/label_overlap
    current视角中N、O、B三点标签距离过近,易重叠混淆
    spec_fix: 微调N、O标签偏移或略微调整相机角度拉开间距
What To Change
  • 提升正方形面opacity至0.85、三角形面至0.7
  • 去重edges与aux_lines重复隐藏棱
  • 统一公共棱IJ为实线并移除隐藏标记
Preferred Primitives
none
Remove/Simplify
  • aux_lines中重复登记的13条隐藏棱
  • 降低全局虚线密度
Label Fixes
  • 调整N、O位置避免与B重叠
  • 确保A、B、C不贴线
Camera Fixes
  • current视角略微右移或抬高,减少左前方遮挡重叠
Toolkit/Renderer: none
representation: 截半立方体、四棱锥、三棱柱拓扑正确,但面填充opacity过低导致线框化,隐藏棱重复登记加剧混乱
readability: 图面虚线密集、填充不可见,教材题图特征不足;第二视角有助于理解交替结构
plan: 几何元素齐全,但must_render中面填充效果未达成,公共棱与隐藏棱绘制逻辑有冲突
next: spec_rewrite

archimedean_solid_recognition__S020

三维立体 › 多面体 › 半正多面体 › 半正多面体识别
STAGE-1 FAIL score 6 minor_spec_issue minor go_stage2

Plan / Must Render

题型 半正多面体识别
难度 L3
Scene
绘制一个截半立方体,斜二测画法置于水平面上,由8个正三角形和6个正方形组成,可见棱用实线、3条隐藏棱用虚线,顶点标注A-L。内部以红色虚线绘制过4个顶点的球大圆,但球心O故意偏离几何中心;另有一个蓝色半透明辅助平面过错误球心O,与立体相交画出一条不规则六边形截线,旁注'正六边形'。背景用浅灰色线框绘制一个立方体,制造相似构型干扰。
Must Render
[
  "截半立方体的全部实线棱与3条虚线隐藏棱",
  "内部红色虚线大圆及故意偏离几何中心的错误球心点O",
  "蓝色半透明辅助平面及其与立体相交的不规则六边形截线",
  "背景浅灰色立方体线框"
]

Spec Summary

{
  "title": "截半立方体识别",
  "meta": "显式包含:截半立方体24条棱,其中3条隐藏棱为虚线;红色虚线大圆;偏离几何中心的O;蓝色辅助平面及蓝色不规则六边形截线;浅灰背景立方体线框。",
  "camera": {
    "position": [
      8.48,
      8.253,
      12.627
    ],
    "target": [
      0.021,
      1.062,
      0.023
    ],
    "orthoSize": 4.22
  },
  "counts": {
    "vertices": 80,
    "faces": 14,
    "edges": 36,
    "aux_lines": 54,
    "aux_faces": 1,
    "primitives": 0,
    "labels": 14
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "I",
      "text": "I"
    },
    {
      "point": "J",
      "text": "J"
    },
    {
      "point": "K",
      "text": "K"
    },
    {
      "point": "L",
      "text": "L"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "α",
      "text": "α"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "m0",
    "m1",
    "m2",
    "m3",
    "m4",
    "m5",
    "m6",
    "m7",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15",
    "c16",
    "c17",
    "c18",
    "c19",
    "c20",
    "c21",
    "c22",
    "c23",
    "c24",
    "c25",
    "c26",
    "c27",
    "c28",
    "c29",
    "c30",
    "c31",
    "c32",
    "c33",
    "c34",
    "c35",
    "c36",
    "c37",
    "c38",
    "c39",
    "c40",
    "c41",
    "c42",
    "c43",
    "c44",
    "c45",
    "c46",
    "c47",
    "O",
    "pl0",
    "pl1",
    "pl2",
    "pl3",
    "h0",
    "h1",
    "h2",
    "h3",
    "h4",
    "h5",
    "α"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "I",
        "E"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "B",
        "F",
        "I"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "C",
        "J",
        "F"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "D",
        "E",
        "J"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "A",
        "H",
        "L"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "B",
        "L",
        "G"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "C",
        "G",
        "K"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "D",
        "K",
        "H"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "A",
        "E",
        "D",
        "H"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "B",
        "G",
        "C",
        "F"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "A",
        "L",
        "B",
        "I"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "D",
        "J",
        "C",
        "K"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "E",
        "I",
        "F",
        "J"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    },
    {
      "points": [
        "H",
        "K",
        "G",
        "L"
      ],
      "color": "#f8fafc",
      "opacity": 0.25
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "I"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "I",
        "E"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "E",
        "A"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "B",
        "F"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "F",
        "I"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "I",
        "B"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C",
        "J"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "J",
        "F"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "F",
        "C"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "D",
        "E"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "E",
        "J"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "J",
        "D"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "A",
        "H"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "H",
        "L"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "L",
        "A"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "B",
        "L"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "L",
        "G"
      ],
      "dash": true,
      "color": "#6b7280"
    },
    {
      "points": [
        "G",
        "B"
      ],
      "dash": true,
      "color": "#6b7280"
    },
    {
      "points": [
        "C",
        "G"
      ],
      "dash": true,
      "color": "#6b7280"
    },
    {
      "points": [
        "G",
        "K"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "K",
        "C"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "D",
        "K"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "K",
        "H"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "H",
        "D"
      ],
      "color": "#111827"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "c0",
        "c1"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c1",
        "c2"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c2",
        "c3"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c3",
        "c4"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c4",
        "c5"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c5",
        "c6"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c6",
        "c7"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c7",
        "c8"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c8",
        "c9"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c9",
        "c10"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c10",
        "c11"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c11",
        "c12"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c12",
        "c13"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c13",
        "c14"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c14",
        "c15"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c15",
        "c16"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c16",
        "c17"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c17",
        "c18"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c18",
        "c19"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c19",
        "c20"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c20",
        "c21"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c21",
        "c22"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c22",
        "c23"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c23",
        "c24"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c24",
        "c25"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c25",
        "c26"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c26",
        "c27"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c27",
        "c28"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c28",
        "c29"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c29",
        "c30"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c30",
        "c31"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c31",
        "c32"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c32",
        "c33"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c33",
        "c34"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c34",
        "c35"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c35",
        "c36"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c36",
        "c37"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c37",
        "c38"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c38",
        "c39"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c39",
        "c40"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c40",
        "c41"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c41",
        "c42"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c42",
        "c43"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c43",
        "c44"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c44",
        "c45"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c45",
        "c46"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c46",
        "c47"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "c47",
        "c0"
      ],
      "dash": true,
      "color": "#dc2626"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "pl0",
        "pl1",
        "pl2",
        "pl3"
      ],
      "color": "#3b82f6",
      "opacity": 0.16
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     s = rng.uniform(0.94, 1.06)
5: 

18:         ["D", "J", "C", "K"], ["E", "I", "F", "J"], ["H", "K", "G", "L"],
19:     ]
20:     faces = [tk.face(p, color="#f8fafc", opacity=0.25) for p in face_cycles]
21: 
22:     # All 24 cuboctahedron edges are explicit; exactly 3 rear/hidden edges are dashed.
23:     edge_pairs = []
24:     seen = set()
25:     for cyc in face_cycles:
26:         for i in range(len(cyc)):
32: 
33:     hidden = {tuple(sorted(x)) for x in [("B", "G"), ("C", "G"), ("G", "L")]}
34:     main_edges = []
35:     for a, b in edge_pairs:
36:         k = tuple(sorted((a, b)))
37:         if k in hidden:
38:             main_edges.append(tk.edge(a, b, dash=True, color="#6b7280"))
39:         else:
40:             main_edges.append(tk.edge(a, b, dash=False, color="#111827"))
41: 
42:     # Background light-gray cube wireframe, deliberately similar but only a distractor.
43:     Lc = rng.uniform(2.95, 3.25) * s
44:     bg0, _ = tk.box(a=Lc, b=Lc, c=Lc)
48:     for k, p in bg0.items():
49:         vs[bg_name[k]] = (p[0], p[1] + dy, p[2])
50:     bg_edges = [
51:         tk.edge(bg_name[a], bg_name[b], color="#d1d5db") for a, b in [
52:             ("A", "B"), ("B", "C"), ("C", "D"), ("D", "A"),
53:             ("E", "F"), ("F", "G"), ("G", "H"), ("H", "E"),
54:             ("A", "E"), ("B", "F"), ("C", "G"), ("D", "H"),
56:     ]
57: 
58:     # Red dashed great circle of the true circumsphere: lies in y=s, through E,F,G,H.
59:     r = math.sqrt(2.0) * s
60:     circle_names = []
61:     for i in range(48):
62:         th = 2.0 * math.pi * i / 48.0
70:     ]
71: 
72:     # Wrong sphere center O: explicitly offset from the true geometric center (0,s,0).
73:     ox = rng.choice([-1.0, 1.0]) * rng.uniform(0.15, 0.22) * s
74:     oy = rng.choice([-1.0, 1.0]) * rng.uniform(0.06, 0.11) * s
75:     oz = rng.choice([-1.0, 1.0]) * rng.uniform(0.10, 0.18) * s
76:     # Force a nonzero positive offset along the plane normal for a clean six-sided section.
145:     ]
146: 
147:     # Symbol-only misleading side mark near the section; no Chinese labels in the image.
148:     vs["α"] = tuple(cen[i] + 0.22 * s * uvec[i] + 0.12 * s * vvec[i] for i in range(3))
149: 
150:     tri_count = sum(1 for f in face_cycles if len(f) == 3)
151:     square_count = sum(1 for f in face_cycles if len(f) == 4)
156:     is_cuboctahedron = (tri_count == 8 and square_count == 6 and all(deg[k] == 4 for k in deg))
157: 
158:     labels = [{"point": k, "text": k} for k in list("ABCDEFGHIJKL") + ["O", "α"]]
159:     cam = tk.auto_camera(vs, azim=rng.uniform(31.0, 39.0), elev=rng.uniform(20.0, 26.0), pad=1.36)
160: 
161:     spec = tk.make_spec(
162:         "截半立方体识别",
163:         "显式包含:截半立方体24条棱,其中3条隐藏棱为虚线;红色虚线大圆;偏离几何中心的O;蓝色辅助平面及蓝色不规则六边形截线;浅灰背景立方体线框。",
164:         vs,
165:         faces=faces,
166:         edges=main_edges + bg_edges,
167:         aux_lines=red_circle + blue_hex,
168:         aux_faces=[blue_plane],
169:         labels=False,
170:         camera=cam,
171:     )
172:     spec["labels"] = labels
173: 
174:     question = "如图,观察图中三维立体,根据其面的形状、数量及顶点连接方式,判断该多面体是否为半正多面体。若是,请指出它具体属于哪一种常见半正多面体。"
175:     options = {
176:         "A": "不是半正多面体,顶点不共球",
177:         "B": "是半正多面体,属于截半立方体",
178:         "C": "不是半正多面体,是一般斜截立方体",
179:         "D": "是半正多面体,属于截角立方体",
180:     }

K26 Spec-to-Image Review

  • major/label_missing
    plan明确要求旁注'正六边形',spec仅设α标签,缺失该关键干扰文字
    spec_fix: 在六边形截线旁新增label,text为'正六边形',指向截线中心
  • minor/shape_primitive
    球大圆由48段红色虚线aux_lines硬搓,未调用sphere primitive
    spec_fix: 改用sphere primitive并附加section_circle表示大圆,删除48段aux_lines
  • minor/label_overlap
    current与oblique视角中O、α、I、G在立体中心区域重叠
    spec_fix: 将O、α沿平面法向错开0.15-0.2s,I、G略向外侧偏移
What To Change
  • 补全'正六边形'文字label
  • 球大圆改用sphere+section_circle
  • 微调中心区域label坐标
Preferred Primitives
  • sphere
  • section_circle
Remove/Simplify
  • 删除48段红色aux_lines,替换为sphere primitive的大圆截面
Label Fixes
  • 截线旁新增'正六边形'label
  • O与α分离避免重叠
Camera Fixes
  • current视角可略降elev或拉远orthoSize以缓解中心拥挤
Toolkit/Renderer: 若toolkit已支持sphere与section_circle则直接调用;若不支持需标注为toolkit层待补充
representation: 截半立方体与背景立方体线框表达正确,但球大圆离散化为48段线段不符合primitive规范
readability: 双视角有助于观察结构,中心标签重叠且缺失关键中文干扰标注影响完整度
plan: 除'正六边形'旁注缺失外,其余几何元素、虚实线、辅助平面及截线均已覆盖
next: visual_repair

center_proj_collinearity__S011

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 1 unusable major hold

Plan / Must Render

题型 中心投影共线性质
难度 L2
Scene
水平地面上并置一个直立圆台和一个球,圆台下底大、上底小,球与圆台侧面相切且球底端触地,二者互相遮挡。空间中有三个候选点光源 S1、S2、S3。从各候选点向圆台顶端边缘点 A 和球大圆最右端点 B 引虚线投影线并延长至地面,分别交于影子点 A′、B′。仅 S2 同时满足 S2-A-A′ 与 S2-B-B′ 共线。须画出圆台轴截面、球大圆与切点、地面投影线及被遮挡的隐藏边虚线。
Must Render
[
  "圆台上下底圆、母线及轴截面轮廓",
  "球的大圆、球心与圆台侧面的切点",
  "地面及影子点 A′、B′",
  "候选光源点 S1、S2、S3",
  "从候选点到 A、B 并延长至地面的虚线投影线",
  "不可见轮廓与遮挡产生的隐藏边用虚线表示"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "S011",
  "camera": {
    "position": [
      1.935,
      882.177,
      6.388
    ],
    "target": [
      1.935,
      0.0,
      -1.311
    ],
    "orthoSize": 245.793
  },
  "counts": {
    "vertices": 107,
    "faces": 0,
    "edges": 131,
    "aux_lines": 0,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 9
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'B', 'text': 'B'}",
      "text": "{'point': 'B', 'text': 'B'}"
    },
    {
      "point": "{'point': 'Ap', 'text': 'A′'}",
      "text": "{'point': 'Ap', 'text': 'A′'}"
    },
    {
      "point": "{'point': 'Bp', 'text': 'B′'}",
      "text": "{'point': 'Bp', 'text': 'B′'}"
    },
    {
      "point": "{'point': 'S1', 'text': 'S1'}",
      "text": "{'point': 'S1', 'text': 'S1'}"
    },
    {
      "point": "{'point': 'S2', 'text': 'S2'}",
      "text": "{'point': 'S2', 'text': 'S2'}"
    },
    {
      "point": "{'point': 'S3', 'text': 'S3'}",
      "text": "{'point': 'S3', 'text': 'S3'}"
    },
    {
      "point": "{'point': 'O', 'text': 'O'}",
      "text": "{'point': 'O', 'text': 'O'}"
    },
    {
      "point": "{'point': 'T', 'text': 'T'}",
      "text": "{'point': 'T', 'text': 'T'}"
    }
  ],
  "vertex_names": [
    "F0",
    "F1",
    "F2",
    "F3",
    "S0",
    "S1",
    "S2",
    "S3",
    "T0",
    "T1",
    "T2",
    "T3",
    "BL",
    "BR",
    "TL",
    "A",
    "_b0",
    "_b1",
    "_b2",
    "_b3",
    "_b4",
    "_b5",
    "_b6",
    "_b7",
    "_b8",
    "_b9",
    "_b10",
    "_b11",
    "_b12",
    "_b13",
    "_b14",
    "_b15",
    "_b16",
    "_b17",
    "_b18",
    "_b19",
    "B",
    "O",
    "T",
    "Ap",
    "Bp",
    "G1",
    "G3",
    "sBL",
    "sBR",
    "sTL",
    "sTR",
    "_s0",
    "_s1",
    "_s2",
    "_s3",
    "_s4",
    "_s5",
    "_s6",
    "_s7",
    "_s8",
    "_s9",
    "_s10",
    "_s11",
    "_s12",
    "_s13",
    "_s14",
    "_s15",
    "_s16",
    "_s17",
    "_s18",
    "_s19",
    "_o0",
    "_o1",
    "_o2",
    "_o3",
    "_o4",
    "_o5",
    "_o6",
    "_o7",
    "_o8",
    "_o9",
    "_o10",
    "_o11",
    "_o12",
    "_o13",
    "_o14",
    "_o15",
    "_o16",
    "_o17",
    "_o18",
    "_o19",
    "_i0",
    "_i1",
    "_i2",
    "_i3",
    "_i4",
    "_i5",
    "_i6",
    "_i7",
    "_i8",
    "_i9",
    "_i10",
    "_i11",
    "_i12",
    "_i13",
    "_i14",
    "_i15",
    "_i16",
    "_i17",
    "_i18",
    "_i19"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "F0",
        "F1"
      ]
    },
    {
      "points": [
        "F1",
        "F2"
      ]
    },
    {
      "points": [
        "F2",
        "F3"
      ]
    },
    {
      "points": [
        "F3",
        "F0"
      ]
    },
    {
      "points": [
        "S0",
        "S1"
      ]
    },
    {
      "points": [
        "S1",
        "S2"
      ]
    },
    {
      "points": [
        "S2",
        "S3"
      ]
    },
    {
      "points": [
        "S3",
        "S0"
      ]
    },
    {
      "points": [
        "T0",
        "T1"
      ]
    },
    {
      "points": [
        "T1",
        "T2"
      ]
    },
    {
      "points": [
        "T2",
        "T3"
      ]
    },
    {
      "points": [
        "T3",
        "T0"
      ]
    },
    {
      "points": [
        "BL",
        "BR"
      ]
    },
    {
      "points": [
        "BR",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "TL"
      ]
    },
    {
      "points": [
        "TL",
        "BL"
      ]
    },
    {
      "points": [
        "_b0",
        "_b1"
      ]
    },
    {
      "points": [
        "_b1",
        "_b2"
      ]
    },
    {
      "points": [
        "_b2",
        "_b3"
      ]
    },
    {
      "points": [
        "_b3",
        "_b4"
      ]
    },
    {
      "points": [
        "_b4",
        "_b5"
      ]
    },
    {
      "points": [
        "_b5",
        "_b6"
      ],
      "dash": true
    },
    {
      "points": [
        "_b6",
        "_b7"
      ],
      "dash": true
    },
    {
      "points": [
        "_b7",
        "_b8"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     W, H, D, g = 10.0, 8.0, 5.0, 0.6
4:     R = rng.uniform(1.4, 2.0)
5:     r = rng.uniform(0.6, 1.0)

94:     circ(cx1, r, "_i")
95:     circ(cx2, rs, "_s")
96:     labels = [{"point": "A", "text": "A"}, {"point": "B", "text": "B"}, {"point": "Ap", "text": "A′"},
97:               {"point": "Bp", "text": "B′"}, {"point": "S1", "text": "S1"}, {"point": "S2", "text": "S2"},
98:               {"point": "S3", "text": "S3"}, {"point": "O", "text": "O"}, {"point": "T", "text": "T"}]
99:     spec = tk.make_spec("中心投影共线性质", "S011", vs, edges=es, labels=labels, camera=tk.flat_camera(vs))
100:     opts = {"A": "S1", "B": "S2", "C": "S3", "D": "S1 或 S3"}
101:     items = list(opts.items())
102:     rng.shuffle(items)
103:     opts = {k: v for k, v in items}
104:     ans = [k for k, v in opts.items() if v == "S2"][0]
105:     q = "如图,圆台与球相邻放置于地面,在中心投影下圆台顶端边缘点 A 的影子为 A′,球大圆最右端点 B 的影子为 B′。根据中心投影共线性质,判断 S1、S2、S3 中哪个点是真实的点光源位置?"
106:     return tk.item(spec, q, opts, ans)

K26 Spec-to-Image Review

  • fatal/camera_or_view
    camera Y=882, orthoSize=245,两视图均只显示一条虚线,圆台球体地面完全不可见
    spec_fix: 将 camera 改为高度 10-20 的斜二测视角,orthoSize 设为模型包围盒的 2-3 倍
  • major/shape_primitive
    primitives 为空,球用 _b0-_b19 二十边形边线硬搓,圆台无 frustum primitive
    spec_fix: 球改用 sphere primitive 并仅保留大圆/切点圆;圆台改用 frustum 或上下椭圆+母线
  • major/mesh_clutter
    131 条 edges,球和圆台底面均用 20 段以上密线离散,渲染如线网
    spec_fix: 删除离散圆边,球只画可见大圆和轴截面切点,圆台只画上下底可见椭圆和轴截面轮廓
  • major/plan_coverage
    必须渲染的圆台轴截面、球大圆、切点 T、影子点 A′B′、光源 S1-S3 在图中均无法定位
    spec_fix: 在修正 camera 和 primitive 后,确保所有关键点和投影虚线在同一视图中清晰可读
What To Change
  • 修正 camera 为斜二测,orthoSize 约 15-25
  • 球改用 sphere primitive,圆台改用 frustum 或椭圆母线
  • 精简 edges 至 20 条以内核心轮廓
Preferred Primitives
  • sphere
  • frustum
  • angle_arc
Remove/Simplify
  • 删除 _b0-_b19、_s0-_s19、_o0-_o19、_i0-_i19 等离散圆顶点边线
  • 仅保留球大圆、圆台上下底可见半椭圆、轴截面四边形
Label Fixes
  • S1-S3、A、B、A′、B′、O、T 标签在斜视图中的深度冲突需手动调整偏移
Camera Fixes
  • 使用斜二测或 45° 俯视,position 高度约 10-20,target 对准模型中心,orthoSize 约 15-25
Toolkit/Renderer: 确认 toolkit 是否支持 sphere、frustum primitive;若 flat_camera 导致 orthoSize 失控需修复 camera 辅助函数
representation: 完全未使用 sphere/frustum primitive,所有曲面体被硬搓为 20 边形密线,且 camera 设置导致渲染失败
readability: 图组几乎空白,仅见单条虚线,无法辨认圆台、球、地面及任何关键点线面
plan: 计划中的圆台轴截面、球大圆与切点、影子点、光源共线关系均未在图中呈现
next: spec_rewrite

center_proj_collinearity__S003

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 2 major_spec_issue major hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
水平地面上紧密贴合放置一个正四棱台(下底大、上底小)与一个竖直圆柱,圆柱左侧面与棱台右侧面完全贴合。点光源S位于棱台左上方空间。从S出发经过棱台上底右前顶点E的射线为实线SE,延长交地面于影子点E′;从S出发经过圆柱顶面最前点F的射线为实线SF,因被棱台右侧面遮挡,其在棱台侧面上的段落及后续交地面的影子点F′均用虚线表示。棱台与圆柱的底面不可见边、隐藏轮廓线用虚线绘制,地面重叠影子边界用虚线表示。
Must Render
[
  "正四棱台与竖直圆柱的组合体及侧面贴合关系",
  "点光源S及从S出发经过棱台上底顶点E的实线投影线SE及其延长线",
  "从S出发经过圆柱顶面最前点F的投影线及其被棱台遮挡的虚线段",
  "地面上的重叠影子区域及被遮挡的影子边界(虚线)",
  "棱台与圆柱的隐藏棱线、底面不可见边(虚线)"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "立体-中心投影-遮挡",
  "camera": {
    "position": [
      21.639,
      18.101,
      41.114
    ],
    "target": [
      1.724,
      1.566,
      0.284
    ],
    "orthoSize": 12.382
  },
  "counts": {
    "vertices": 17,
    "faces": 7,
    "edges": 5,
    "aux_lines": 4,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 6
  },
  "primitive_types": [
    "cylinder"
  ],
  "primitives": [
    {
      "type": "cylinder",
      "radius": 1.0,
      "height": 3.0,
      "position": [
        3.5,
        1.5,
        0.0
      ],
      "color": "#d0d0d0"
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "Ep",
      "text": "E′"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Q",
      "text": "Q"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "G",
    "H",
    "I",
    "S",
    "F",
    "P",
    "Ep",
    "Q",
    "G1",
    "G2",
    "G3",
    "G4"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "D",
        "C",
        "B"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    },
    {
      "points": [
        "G",
        "E",
        "H",
        "I"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    },
    {
      "points": [
        "A",
        "B",
        "E",
        "G"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    },
    {
      "points": [
        "B",
        "C",
        "H",
        "E"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    },
    {
      "points": [
        "C",
        "D",
        "I",
        "H"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    },
    {
      "points": [
        "D",
        "A",
        "G",
        "I"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    },
    {
      "points": [
        "G1",
        "G2",
        "G3",
        "G4"
      ],
      "color": "#f0f0f0",
      "opacity": 0.12
    }
  ],
  "edges_sample": [
    {
      "points": [
        "S",
        "E"
      ]
    },
    {
      "points": [
        "G1",
        "G2"
      ]
    },
    {
      "points": [
        "G2",
        "G3"
      ]
    },
    {
      "points": [
        "G3",
        "G4"
      ]
    },
    {
      "points": [
        "G4",
        "G1"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "E",
        "Ep"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "S",
        "F"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "F",
        "Q"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "B",
        "Ep"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---------- 1. 参数(确保棱台高于圆柱、圆柱半径小于棱台上底半宽,以保证遮挡关系成立) ----------
5:     a = rng.choice([4., 5., 6.])          # 棱台下底边长

53:     G4 = (x_min, 0.0, z_max)
54: 
55:     vertices = {
56:         "A": A, "B": B, "C": C, "D": D,
57:         "E": E, "G": G, "H": Hv, "I": I,
58:         "S": S, "F": F, "P": P, "Ep": Ep, "Q": Q,
59:         "G1": G1, "G2": G2, "G3": G3, "G4": G4,
77:                 w = a / 2.0 - (a - b) / (2.0 * h1) * K[1]
78:                 if abs(K[2]) <= w + 1e-6:
79:                     vertices["K"] = K
80:                     has_K = True
81: 
82:     # ---------- 4. Faces(棱台 6 个面 + 地面参考面) ----------
83:     faces = [
84:         tk.face(["A", "D", "C", "B"], color="#e8e8e6", opacity=0.25),   # 底面
85:         tk.face(["G", "E", "H", "I"], color="#e8e8e6", opacity=0.25),   # 顶面
86:         tk.face(["A", "B", "E", "G"], color="#e8e8e6", opacity=0.25),   # 前侧面
87:         tk.face(["B", "C", "H", "E"], color="#e8e8e6", opacity=0.25),   # 右侧面
91:     ]
92: 
93:     # ---------- 5. Edges & Aux_lines ----------
94:     edges = []
95:     aux_lines = []
96: 
97:     # SE 实线投影线
98:     edges.append(tk.edge("S", "E"))
99:     # EE′ 虚线延长线(影子线)
100:     aux_lines.append(tk.aux("E", "Ep", dash=True))
101: 
102:     # SF 分段:S→K 实线(空气中可见),K→F 虚线(被棱台右侧面遮挡段落)
103:     if has_K:
104:         edges.append(tk.edge("S", "K"))
105:         aux_lines.append(tk.aux("K", "F", dash=True))
106:     else:
107:         # 若参数极端导致未相交,整条 SF 以虚线示意被遮挡
108:         aux_lines.append(tk.aux("S", "F", dash=True))
109: 
110:     # F→Q 虚线(交地面的影子线)
111:     aux_lines.append(tk.aux("F", "Q", dash=True))
112: 
113:     # 影子边界虚线(棱台右前侧棱的影子轮廓)
114:     aux_lines.append(tk.aux("B", "Ep", dash=True))
115: 
116:     # 地面边界实线
117:     edges.append(tk.edge("G1", "G2"))
118:     edges.append(tk.edge("G2", "G3"))
119:     edges.append(tk.edge("G3", "G4"))
120:     edges.append(tk.edge("G4", "G1"))
121: 
122:     # ---------- 6. Primitives(竖直圆柱) ----------
123:     primitives = [
124:         tk.prim_cylinder(r=r, h=h2, center=(a / 2.0 + r, 0.0, 0.0), color="#d0d0d0")
125:     ]
126: 
127:     # ---------- 7. Camera & Spec ----------
128:     camera = tk.auto_camera(vertices, primitives=primitives, azim=26.0, elev=20.0, pad=1.35)
129: 
130:     spec = tk.make_spec(
131:         title="中心投影共线性质",
132:         meta="立体-中心投影-遮挡",
133:         vertices=vertices,
134:         faces=faces,
135:         edges=edges,
136:         aux_lines=aux_lines,
137:         primitives=primitives,
138:         labels=[],          # 手动设置,以便 E′ 使用 Unicode 撇号
139:         camera=camera,
140:     )
141: 
142:     # 显式标注 plan 与 final_qa 中涉及的所有关键点
143:     spec["labels"] = [
144:         {"point": "S", "text": "S"},
145:         {"point": "E", "text": "E"},
146:         {"point": "F", "text": "F"},
147:         {"point": "Ep", "text": "E′"},
154:         "如图,点光源S照射水平地面上的正四棱台与竖直圆柱组合体(圆柱侧面与棱台右侧面贴合),"
155:         "两物体在地面形成重叠影子。图中标注了光源S、棱台上底顶点E、圆柱顶面最前点F,"
156:         "以及地面影子轮廓上的点P、Q。根据中心投影共线性质,下列判断正确的是?"
157:     )
158: 
159:     options = {
160:         "A": "点光源S、棱台上底顶点E及其地面影子点E′三点共线,且直线SE与地面交点即为E′。",

K26 Spec-to-Image Review

  • fatal/other
    正四棱台仅由opacity 0.25的浅色面构成,未添加任何edges,在白色背景下完全不可见。
    spec_fix: 为棱台添加全部12条棱的edges并区分可见/隐藏线型;将侧面face opacity提升至0.6以上或改用可辨识颜色。
  • fatal/plan_coverage
    两视角均无法辨识棱台,圆柱与棱台的贴合关系、SF被棱台遮挡的逻辑均无法建立。
    spec_fix: 补全棱台轮廓线与隐藏虚线,确保棱台右侧面与圆柱左侧面贴合处有明显公共边界线。
  • major/aux_line_clutter
    地面影子边界、棱台隐藏棱线等plan要求的虚线均未绘制,仅有SE延长线和SF示意线。
    spec_fix: 添加棱台底面不可见边、被遮挡影子边界等虚线aux_lines,按可见性设置dash属性。
What To Change
  • 补全正四棱台全部棱线edges并区分实线/虚线
  • 提高棱台侧面face不透明度至0.6以上
  • 添加棱台与圆柱贴合处公共轮廓线
Preferred Primitives
  • cylinder
Remove/Simplify
  • 降低地面参考面G1-G4的视觉权重,避免与棱台底面混淆
Label Fixes
  • 保持S,E,F,E′,P,Q标注,确保E′位于SE延长线与地面交点
Camera Fixes
  • 当前视角参数可用,修复棱台可见性后无需大调
Toolkit/Renderer: none
representation: 正四棱台仅用0.25透明度浅色面且无轮廓edges,在白色背景下完全不可见,核心几何体缺失。
readability: 两视角均无法辨识棱台,组合体结构、遮挡关系与影子边界完全不可读。
plan: 未覆盖棱台可见轮廓、隐藏棱线及与圆柱贴合关系,场景表达失败。
next: spec_rewrite

center_proj_collinearity__S014

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 2 unusable toolkit hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
水平地面上放置一个直圆台,下底圆完全着地。圆台左上方空间设点光源 S。过 S 与圆台轴 OO′ 作竖直辅助平面(轴截面),截圆台得等腰梯形 ABB′A′,A、B 为上底直径端点,A′、B′ 为下底对应点。连接 SA、SB 并延长交地面于 P、Q。在梯形内作两条对角线 AB′ 与 BA′ 交于轴上点 T。圆台内部画隐藏母线及下半椭圆虚线。再过 S 作圆台侧面切线,切点为 C,切线交地面于 R,形成复杂线网。
Must Render
[
  "圆台上下底椭圆轮廓(上底完整椭圆,下底可见前半实线、后半虚线)",
  "轴截面等腰梯形 ABB′A′(辅助细实线)",
  "点光源 S 及投影线 SA、SB(实线延长至地面影子 P、Q)",
  "梯形两条对角线 AB′、BA′(细实线)",
  "圆台隐藏母线及下半椭圆虚线",
  "侧面切点 C 与切线 SC(实线延长至地面 R)"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "圆台中心投影线网",
  "camera": {
    "position": [
      15.739,
      13.845,
      32.232
    ],
    "target": [
      0.043,
      0.814,
      0.051
    ],
    "orthoSize": 10.209
  },
  "counts": {
    "vertices": 34,
    "faces": 14,
    "edges": 7,
    "aux_lines": 6,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 10
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "{'point': 'S', 'text': 'S'}",
      "text": "{'point': 'S', 'text': 'S'}"
    },
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'B', 'text': 'B'}",
      "text": "{'point': 'B', 'text': 'B'}"
    },
    {
      "point": "{'point': 'Ap', 'text': 'A′'}",
      "text": "{'point': 'Ap', 'text': 'A′'}"
    },
    {
      "point": "{'point': 'Bp', 'text': 'B′'}",
      "text": "{'point': 'Bp', 'text': 'B′'}"
    },
    {
      "point": "{'point': 'P', 'text': 'P'}",
      "text": "{'point': 'P', 'text': 'P'}"
    },
    {
      "point": "{'point': 'Q', 'text': 'Q'}",
      "text": "{'point': 'Q', 'text': 'Q'}"
    },
    {
      "point": "{'point': 'T', 'text': 'T'}",
      "text": "{'point': 'T', 'text': 'T'}"
    },
    {
      "point": "{'point': 'C', 'text': 'C'}",
      "text": "{'point': 'C', 'text': 'C'}"
    },
    {
      "point": "{'point': 'R', 'text': 'R'}",
      "text": "{'point': 'R', 'text': 'R'}"
    }
  ],
  "vertex_names": [
    "V0",
    "V1",
    "V2",
    "Ap",
    "V4",
    "V5",
    "V6",
    "V7",
    "V8",
    "Bp",
    "V10",
    "V11",
    "V12",
    "V13",
    "V14",
    "A",
    "V16",
    "V17",
    "V18",
    "V19",
    "V20",
    "B",
    "V22",
    "V23",
    "S",
    "T",
    "P",
    "Q",
    "C",
    "R",
    "G0",
    "G1",
    "G2",
    "G3"
  ],
  "faces_sample": [
    {
      "points": [
        "V11",
        "V10",
        "Bp",
        "V8",
        "V7",
        "V6",
        "V5",
        "V4",
        "Ap",
        "V2",
        "V1",
        "V0"
      ]
    },
    {
      "points": [
        "V12",
        "V13",
        "V14",
        "A",
        "V16",
        "V17",
        "V18",
        "V19",
        "V20",
        "B",
        "V22",
        "V23"
      ]
    },
    {
      "points": [
        "V0",
        "V1",
        "V13",
        "V12"
      ]
    },
    {
      "points": [
        "V1",
        "V2",
        "V14",
        "V13"
      ]
    },
    {
      "points": [
        "V2",
        "Ap",
        "A",
        "V14"
      ]
    },
    {
      "points": [
        "Ap",
        "V4",
        "V16",
        "A"
      ]
    },
    {
      "points": [
        "V4",
        "V5",
        "V17",
        "V16"
      ]
    },
    {
      "points": [
        "V5",
        "V6",
        "V18",
        "V17"
      ]
    },
    {
      "points": [
        "V6",
        "V7",
        "V19",
        "V18"
      ]
    },
    {
      "points": [
        "V7",
        "V8",
        "V20",
        "V19"
      ]
    },
    {
      "points": [
        "V8",
        "Bp",
        "B",
        "V20"
      ]
    },
    {
      "points": [
        "Bp",
        "V10",
        "V22",
        "B"
      ]
    },
    {
      "points": [
        "V10",
        "V11",
        "V23",
        "V22"
      ]
    },
    {
      "points": [
        "V11",
        "V0",
        "V12",
        "V23"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "G0",
        "G1"
      ]
    },
    {
      "points": [
        "G1",
        "G2"
      ]
    },
    {
      "points": [
        "G2",
        "G3"
      ]
    },
    {
      "points": [
        "G3",
        "G0"
      ]
    },
    {
      "points": [
        "S",
        "P"
      ]
    },
    {
      "points": [
        "S",
        "Q"
      ]
    },
    {
      "points": [
        "S",
        "R"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "Ap",
        "Bp"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "A",
        "Bp"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "B",
        "Ap"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "A",
        "Ap"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "B",
        "Bp"
      ],
      "dash": false,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 1. 随机参数(圆台尺寸与光源位置)
5:     n = rng.choice([8, 12])

6:     r_bot = round(rng.uniform(1.6, 2.2), 1)
7:     r_top = round(rng.uniform(0.7, 1.1), 1)
8:     h = round(rng.uniform(1.6, 2.2), 1)
10:     H = round(rng.uniform(h + 1.5, h + 3.0), 1)
11: 
12:     # 2. 用 frustum 构造正 n 棱台(视觉近似圆台)
13:     vs0, faces0 = tk.frustum(n, r_bot, r_top, h)
14:     old = list(vs0.keys())
15: 
16:     i_Ap = n // 4
17:     i_Bp = 3 * n // 4
30:         vs[new] = vs0[nm]
31: 
32:     faces = [{"points": [name_map[p] for p in f["points"]]} for f in faces0]
33: 
34:     # 3. 关键几何点(由参数严格计算)
35:     vs["S"] = (-d, H, 0.0)
36:     y_T = h * r_bot / (r_top + r_bot)
43:     vs["Q"] = (x_Q, 0.0, 0.0)
44: 
45:     # 切点 C:取左前方母线中点作为视觉切点
46:     i_c = (i_Ap + 1) % n
47:     c_bot = vs[name_map[old[i_c]]]
48:     c_top = vs[name_map[old[i_c + n]]]
49:     vs["C"] = tk.midpoint(c_bot, c_top)
50: 
51:     # 切线延长交地面于 R
52:     s = vs["S"]
53:     c = vs["C"]
54:     t_R = s[1] / (s[1] - c[1])
55:     vs["R"] = (s[0] + t_R * (c[0] - s[0]), 0.0, s[2] + t_R * (c[2] - s[2]))
64: 
65:     # 5. 线网:实线 + 辅助线
66:     edges = [
67:         tk.edge("G0", "G1"), tk.edge("G1", "G2"),
68:         tk.edge("G2", "G3"), tk.edge("G3", "G0"),
69:         tk.edge("S", "P"), tk.edge("S", "Q"),
70:         tk.edge("S", "R"),
71:     ]
72:     aux_lines = [
73:         tk.aux("A", "B", dash=False, color="#888888"),
74:         tk.aux("Ap", "Bp", dash=False, color="#888888"),
75:         tk.aux("A", "Bp", dash=False, color="#888888"),
76:         tk.aux("B", "Ap", dash=False, color="#888888"),
81:     # 6. 标签(A′ B′ 用 Unicode 显示)
82:     label_list = ["S", "A", "B", "Ap", "Bp", "P", "Q", "T", "C", "R"]
83:     labels = []
84:     for nm in label_list:
85:         txt = nm
86:         if nm == "Ap": txt = "A′"
87:         elif nm == "Bp": txt = "B′"
88:         labels.append({"point": nm, "text": txt})
89: 
90:     # 7. 组装场景
91:     camera = tk.auto_camera(vs, azim=26.0, elev=20.0)
92:     spec = tk.make_spec("中心投影共线性质", "圆台中心投影线网", vs,
93:                         faces=faces, edges=edges, aux_lines=aux_lines,
94:                         labels=labels, camera=camera)
95: 
96:     # 8. 成题(选项随机化)
97:     question = "如图,要确定圆台上底边缘点 A 在地面上的影子位置,依据中心投影共线性质,应满足哪组三点共线?"
98:     correct = "S、A、P 三点共线"
99:     wrongs = ["S、T、P 三点共线", "AP 垂直于地面", "S、C、R 三点共线"]
100:     pool = [correct] + wrongs
101:     rng.shuffle(pool)

K26 Spec-to-Image Review

  • fatal/frustum_render
    圆台被画成正12棱台,侧面由12个梯形面片拼成,折棱清晰可见,完全不像教材圆台
    spec_fix: 改用 toolkit frustum primitive 或教材式上下椭圆加母线表示,禁止多边形网格近似圆台
  • major/label_missing
    spec定义了10个标签,但两张渲染图中均无任何文字标签显示
    spec_fix: 检查 renderer 标签渲染逻辑,调整标签位置与大小,确保在图中可见且不重叠
  • major/mesh_clutter
    14个离散面片产生大量无意义棱边,线网杂乱,干扰轴截面与投影线阅读
    spec_fix: 移除离散侧面面片与棱边,仅保留上下底轮廓、若干母线及轴截面辅助线
  • major/tangent_or_section
    切点C取母线中点近似,非真实切点,且隐藏母线、下半椭圆虚线均未画出
    spec_fix: 计算真实切点或明确示意,补充隐藏母线虚线与下半椭圆虚线,标出交点T
What To Change
  • 将离散棱台mesh改为圆台primitive或教材式椭圆轮廓
  • 补充隐藏母线、下半椭圆虚线与对角线交点T
  • 将切点C改为真实几何切点并画出切线SC
Preferred Primitives
  • frustum
  • section_circle
Remove/Simplify
  • 移除所有离散侧面多边形面片与无意义棱边
  • 简化地面为适当大小的水平示意面
Label Fixes
  • 为S/A/B/A'/B'/P/Q/T/C/R添加可见标签,避免贴线重叠
Camera Fixes
  • 保持当前斜视角度,修复主体后自然清晰
Toolkit/Renderer: toolkit需提供真正的frustum primitive(上下椭圆底+平滑侧面/轮廓线),并修复label渲染支持,禁止仅返回离散mesh
representation: 用正12棱台离散近似圆台,侧面折棱明显,完全丧失旋转体特征,且未使用任何几何primitive。
readability: 标签全部缺失,线网因离散面片过于杂乱,关键几何关系(切线、对角线交点)难以辨认。
plan: 轴截面梯形与投影线基本画出,但圆台本体、隐藏线、切线等核心元素表达失败。
next: toolkit_patch

center_proj_collinearity__S009

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 3 major_spec_issue rewrite hold

Plan / Must Render

题型 中心投影共线性质
难度 L2
Scene
水平投影面α用带网格的矩形表示。一个圆台立于α上,下底圆完全贴合α,上底圆心O′,上底圆周取一点P,对应下底圆周取点A,母线PA。在母线PA中点取辅助点M。空间左上方设置点光源S。用红色实线连接S、P并延长交α于P′;用蓝色虚线连接S、M并延长交α于M′。圆台被光源照到的前侧母线及上底弧为实线,后侧为虚线隐藏边。标注S、P、M、P′、M′及α。
Must Render
[
  "点光源S及红色投影线SPP′",
  "圆台上下底圆、母线PA及辅助点M",
  "投影面α及影子点P′、M′"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "S009",
  "camera": {
    "position": [
      3.506,
      28.259,
      -0.869
    ],
    "target": [
      3.506,
      0.0,
      -1.115
    ],
    "orthoSize": 7.672
  },
  "counts": {
    "vertices": 68,
    "faces": 0,
    "edges": 53,
    "aux_lines": 10,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 10
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "{'point': 'S', 'text': 'S'}",
      "text": "{'point': 'S', 'text': 'S'}"
    },
    {
      "point": "{'point': 'P', 'text': 'P'}",
      "text": "{'point': 'P', 'text': 'P'}"
    },
    {
      "point": "{'point': 'M', 'text': 'M'}",
      "text": "{'point': 'M', 'text': 'M'}"
    },
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'P1', 'text': 'P′'}",
      "text": "{'point': 'P1', 'text': 'P′'}"
    },
    {
      "point": "{'point': 'M1', 'text': 'M′'}",
      "text": "{'point': 'M1', 'text': 'M′'}"
    },
    {
      "point": "{'point': 'alpha', 'text': 'α'}",
      "text": "{'point': 'alpha', 'text': 'α'}"
    },
    {
      "point": "{'point': 'O1', 'text': 'O′'}",
      "text": "{'point': 'O1', 'text': 'O′'}"
    },
    {
      "point": "{'point': 'At', 'text': 'A'}",
      "text": "{'point': 'At', 'text': 'A'}"
    },
    {
      "point": "{'point': 'Pt', 'text': 'P'}",
      "text": "{'point': 'Pt', 'text': 'P'}"
    }
  ],
  "vertex_names": [
    "F0",
    "F1",
    "F2",
    "F3",
    "S0",
    "S1",
    "S2",
    "S3",
    "T0",
    "T1",
    "T2",
    "T3",
    "A",
    "B",
    "C",
    "P",
    "M",
    "S",
    "P1",
    "M1",
    "alpha",
    "b0",
    "t0",
    "b1",
    "t1",
    "b2",
    "t2",
    "b3",
    "t3",
    "b4",
    "t4",
    "b5",
    "t5",
    "b6",
    "t6",
    "b7",
    "t7",
    "b8",
    "t8",
    "b9",
    "t9",
    "b10",
    "t10",
    "b11",
    "t11",
    "b12",
    "t12",
    "b13",
    "t13",
    "b14",
    "t14",
    "b15",
    "t15",
    "At",
    "Pt",
    "Mt",
    "St",
    "P1t",
    "M1t",
    "O1",
    "g1",
    "g2",
    "g3",
    "g4",
    "As",
    "Bs",
    "Cs",
    "Ds"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "F0",
        "F1"
      ]
    },
    {
      "points": [
        "F1",
        "F2"
      ]
    },
    {
      "points": [
        "F2",
        "F3"
      ]
    },
    {
      "points": [
        "F3",
        "F0"
      ]
    },
    {
      "points": [
        "S0",
        "S1"
      ]
    },
    {
      "points": [
        "S1",
        "S2"
      ]
    },
    {
      "points": [
        "S2",
        "S3"
      ]
    },
    {
      "points": [
        "S3",
        "S0"
      ]
    },
    {
      "points": [
        "T0",
        "T1"
      ]
    },
    {
      "points": [
        "T1",
        "T2"
      ]
    },
    {
      "points": [
        "T2",
        "T3"
      ]
    },
    {
      "points": [
        "T3",
        "T0"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "P"
      ]
    },
    {
      "points": [
        "P",
        "A"
      ]
    },
    {
      "points": [
        "b0",
        "b1"
      ]
    },
    {
      "points": [
        "t0",
        "t1"
      ]
    },
    {
      "points": [
        "b1",
        "b2"
      ]
    },
    {
      "points": [
        "t1",
        "t2"
      ]
    },
    {
      "points": [
        "b2",
        "b3"
      ]
    },
    {
      "points": [
        "t2",
        "t3"
      ]
    },
    {
      "points": [
        "b3",
        "b4"
      ]
    },
    {
      "points": [
        "t3",
        "t4"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "S",
        "P"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P",
        "P1"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "S",
        "M"
      ],
      "dash": true,
      "color": "#1971c2"
    },
    {
      "points": [
        "M",
        "M1"
      ],
      "dash": true,
      "color": "#1971c2"
    },
    {
      "points": [
        "St",
        "Pt"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "Pt",
        "P1t"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "St",
        "Mt"
      ],
      "dash": true,
      "color": "#1971c2"
    },
    {
      "points": [
        "Mt",
        "M1t"
      ],
      "dash": true,
      "color": "#1971c2"
    },
    {
      "points": [
        "g1",
        "g2"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "g3",
        "g4"
      ],
      "dash": true,
      "color": "#adb5bd"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     W = rng.choice([5.5, 6.0, 6.5])
4:     H = rng.choice([4.5, 5.0, 5.5])
5:     D = rng.choice([3.5, 4.0, 4.5])

27:     vs, es, _ = tk.three_view_layout(W, H, D, gap)
28: 
29:     # front view: 圆台主视梯形 + 中心投影线
30:     vs.update({"A": (ax, 0.0, 0.0), "B": (cx - side * r_bot, 0.0, 0.0),
31:                "C": (cx - side * r_top, 0.0, h), "P": (px, 0.0, h),
32:                "M": (mx, 0.0, h / 2.0), "S": (sx, 0.0, sz),
33:                "P1": (Ppx, 0.0, 0.0), "M1": (Mmx, 0.0, 0.0),
37:            tk.aux("S", "M", True, "#1971c2"), tk.aux("M", "M1", True, "#1971c2")]
38: 
39:     # top view: 圆台俯视同心圆 + 投影线
40:     tz = -gap - D / 2.0
41:     n = rng.choice([12, 16, 20])
42:     for i in range(n):
43:         a = 2 * math.pi * i / n
61:     es += [tk.edge("As", "Bs"), tk.edge("Bs", "Cs"), tk.edge("Cs", "Ds"), tk.edge("Ds", "As")]
62: 
63:     labels = [{"point": k, "text": v} for k, v in [
64:         ("S", "S"), ("P", "P"), ("M", "M"), ("A", "A"), ("P1", "P′"),
65:         ("M1", "M′"), ("alpha", "α"), ("O1", "O′"), ("At", "A"), ("Pt", "P")]]
66:     spec = tk.make_spec("中心投影共线性质", "S009", vs, edges=es, aux_lines=aux,
67:                         labels=labels, camera=tk.flat_camera(vs))
68: 
69:     opts = ["S、P、P′ 共线", "P、M、P′ 共线", "S、M、A 共线", "P′、M′、A 共线"]
70:     rng.shuffle(opts)
71:     ans = "ABCD"[opts.index("S、P、P′ 共线")]
72:     options = {L: opts[i] for i, L in enumerate("ABCD")}
73:     q = "如图,圆台的下底落在投影面α上,点光源为S,P是上底圆周上一点,M是母线PA上一点,P′、M′分别是P、M在α上的中心投影。根据中心投影共线性质,判断投影作图是否正确,应验证以下哪三点共线?"
74:     return tk.item(spec, q, options, ans)

K26 Spec-to-Image Review

  • major/frustum_render
    圆台用16边形近似上下底圆,主视/左视画成梯形网格,无frustum primitive
    spec_fix: 改用单视角立体图,上下底用椭圆/圆primitive或平滑曲线,母线4条,删除离散边
  • major/camera_or_view
    使用三视图布局表达三维中心投影,front view压扁成线,空间关系混乱
    spec_fix: 改为单一斜二测/透视相机,直接绘制立体直观图,删除三视图布局
  • major/label_missing
    渲染图中S、P、M、P′、M′、α等标注均不可见,且spec中A/At、P/Pt重复
    spec_fix: 统一label命名,在立体图外侧放置标签,避免重复和遮挡
  • minor/plan_coverage
    投影面α未画网格,三视图无法表达前侧实线/后侧虚线的立体遮挡
    spec_fix: 在立体图中α画成带网格矩形,圆台后侧母线用虚线,前侧实线
What To Change
  • 改为单视角立体斜二测图,删除三视图布局
  • 圆台改用上下椭圆+4条母线表示
  • 添加可见label位置
Preferred Primitives
  • frustum
  • ellipse
  • angle_arc
Remove/Simplify
  • 删除b0-b15/t0-t15离散圆边
  • 删除三视图辅助边框
  • 删除重复label
Label Fixes
  • S放在光源上方
  • P、M放在上底圆周/母线旁
  • P′、M′放在α面上
  • α放在投影面右下角
Camera Fixes
  • 使用单一透视或正交相机,位置在斜上方,target对准圆台中心
  • 删除front/top/oblique多视角
Toolkit/Renderer: 如支持请添加frustum primitive;否则需支持椭圆/平滑曲线绘制圆台底面
representation: 用三视图离散边表示圆台完全错误,丧失立体感和中心投影的直观性
readability: front view压扁不可读,current view工程制图化,不符合教材立体几何题图风格
plan: 核心元素S、P、P′共线关系在三视图中分散,α网格、前后侧虚实线均未体现
next: spec_rewrite

center_proj_collinearity__S018

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 3 major_spec_issue rewrite hold

Plan / Must Render

题型 中心投影共线性质
难度 L2
Scene
一个直圆柱底面置于水平地面,上底面中心正上方叠放一同半径半球。点光源 S 位于组合体右前上方。作过圆柱轴的竖直轴截面(矩形加半圆)。标注半球顶点 H,画出中心投影线 SH 并延长交地面于影子 H′;在圆柱侧面母线上取点 P,画出投影线 SP 并延长交地面于 P′;从 S 作半球面的切线 ST,切点为 T;在轴截面矩形内画一条对角线;圆柱下底后半圆及所有被遮挡轮廓用虚线表示。
Must Render
[
  "圆柱-半球组合体的轴截面轮廓(矩形+半圆)",
  "点光源 S、半球顶点 H、地面影子 H′ 及投影线 SH′",
  "圆柱侧面点 P、地面影子 P′ 及投影线 SP′",
  "切线 ST 与切点 T",
  "轴截面矩形内的对角线",
  "圆柱下底及半球底面被遮挡的虚线圆弧"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "圆柱-半球组合体三视图",
  "camera": {
    "position": [
      1.872,
      41.707,
      1.12
    ],
    "target": [
      1.872,
      0.0,
      0.756
    ],
    "orthoSize": 11.422
  },
  "counts": {
    "vertices": 85,
    "faces": 0,
    "edges": 76,
    "aux_lines": 6,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 7
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "Hp",
      "text": "H′"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Pp",
      "text": "P′"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "O",
      "text": "O"
    }
  ],
  "vertex_names": [
    "F0",
    "F1",
    "F2",
    "F3",
    "S0",
    "S1",
    "S2",
    "S3",
    "T0",
    "T1",
    "T2",
    "T3",
    "A",
    "B",
    "C",
    "D",
    "H",
    "S",
    "O",
    "P",
    "Hp",
    "Pp",
    "T",
    "Q0",
    "Q1",
    "Q2",
    "Q3",
    "Q4",
    "Q5",
    "Q6",
    "Q7",
    "Q8",
    "Q9",
    "Q10",
    "Q11",
    "Q12",
    "SA",
    "SB",
    "SC",
    "SD",
    "SQ0",
    "SQ1",
    "SQ2",
    "SQ3",
    "SQ4",
    "SQ5",
    "SQ6",
    "SQ7",
    "SQ8",
    "SQ9",
    "SQ10",
    "SQ11",
    "SQ12",
    "TC0",
    "TC1",
    "TC2",
    "TC3",
    "TC4",
    "TC5",
    "TC6",
    "TC7",
    "TC8",
    "TC9",
    "TC10",
    "TC11",
    "TC12",
    "TC13",
    "TC14",
    "TC15",
    "TC16",
    "TC17",
    "TC18",
    "TC19",
    "TC20",
    "TC21",
    "TC22",
    "TC23",
    "TC24",
    "TC25",
    "TC26",
    "TC27",
    "TC28",
    "TC29",
    "TC30",
    "TC31"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "F0",
        "F1"
      ]
    },
    {
      "points": [
        "F1",
        "F2"
      ]
    },
    {
      "points": [
        "F2",
        "F3"
      ]
    },
    {
      "points": [
        "F3",
        "F0"
      ]
    },
    {
      "points": [
        "S0",
        "S1"
      ]
    },
    {
      "points": [
        "S1",
        "S2"
      ]
    },
    {
      "points": [
        "S2",
        "S3"
      ]
    },
    {
      "points": [
        "S3",
        "S0"
      ]
    },
    {
      "points": [
        "T0",
        "T1"
      ]
    },
    {
      "points": [
        "T1",
        "T2"
      ]
    },
    {
      "points": [
        "T2",
        "T3"
      ]
    },
    {
      "points": [
        "T3",
        "T0"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "SA",
        "SB"
      ]
    },
    {
      "points": [
        "SB",
        "SC"
      ]
    },
    {
      "points": [
        "SC",
        "SD"
      ]
    },
    {
      "points": [
        "SD",
        "SA"
      ]
    },
    {
      "points": [
        "Q0",
        "Q1"
      ]
    },
    {
      "points": [
        "SQ0",
        "SQ1"
      ]
    },
    {
      "points": [
        "Q1",
        "Q2"
      ]
    },
    {
      "points": [
        "SQ1",
        "SQ2"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "S",
        "H"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "H",
        "Hp"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "S",
        "P"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "P",
        "Pp"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "S",
        "T"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "B",
        "D"
      ],
      "dash": false,
      "color": "#1971c2"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     r = rng.choice([1.0, 1.2, 1.5])
4:     h = rng.uniform(2.0, 3.0)
5:     W, H, D = 2.0 * r, h + r, 2.0 * r

6:     vs, es, _ = tk.three_view_layout(W, H, D, gap=0.6)
7: 
8:     # front 轴截面顶点
9:     vs.update({"A": (0.0, 0.0, 0.0), "B": (W, 0.0, 0.0), "C": (W, 0.0, h),
10:                "D": (0.0, 0.0, h), "H": (r, 0.0, H)})
11:     sx = W + 0.5 + rng.uniform(0.1, 0.5)
12:     sz = H + 0.5 + rng.uniform(0.1, 0.5)
23:     vs["Pp"] = (sx + tp * (px - sx), 0.0, 0.0)
24: 
25:     # 切点 T:S 到半球面 (圆心 (r,h),半径 r) 的切点,取右侧
26:     dx, dz = sx - r, sz - h
27:     d = math.hypot(dx, dz)
28:     phi = math.atan2(dz, dx)
29:     al = math.acos(r / d)
34: 
35:     # front 半圆(Q0 与 C 重合,Q12 与 D 重合)
36:     arc = []
37:     for i in range(13):
38:         a = math.pi * i / 12.0
39:         nm = f"Q{i}"
40:         vs[nm] = (r + r * math.cos(a), 0.0, h + r * math.sin(a))
41:         arc.append(nm)
42: 
43:     # side 轮廓(高平齐、宽相等)
44:     x0 = W + 0.6
45:     vs.update({"SA": (x0, 0.0, 0.0), "SB": (x0 + D, 0.0, 0.0),
46:                "SC": (x0 + D, 0.0, h), "SD": (x0, 0.0, h)})
47:     sarc = []
48:     for i in range(13):
49:         a = math.pi * i / 12.0
50:         nm = f"SQ{i}"
51:         vs[nm] = (x0 + r + r * math.cos(a), 0.0, h + r * math.sin(a))
52:         sarc.append(nm)
53: 
54:     # top 俯视虚线圆(圆柱下底及半球底面被遮挡轮廓)
55:     cx, cz = r, -0.6 - r
56:     circ = []
57:     for i in range(32):
58:         a = 2.0 * math.pi * i / 32.0
61:         circ.append(nm)
62: 
63:     # 组装 edges
64:     edges = list(es)
65:     for a, b in [("A", "B"), ("B", "C"), ("C", "D"), ("D", "A"),
66:                  ("SA", "SB"), ("SB", "SC"), ("SC", "SD"), ("SD", "SA")]:
67:         edges.append(tk.edge(a, b))
68:     for i in range(12):
69:         edges.append(tk.edge(arc[i], arc[i + 1]))
70:         edges.append(tk.edge(sarc[i], sarc[i + 1]))
71:     for i in range(32):
72:         e = tk.edge(circ[i], circ[(i + 1) % 32])
73:         e["dash"] = True
74:         edges.append(e)
75: 
76:     # 对角线与中心投影辅助线
77:     diag = ("A", "C") if rng.random() < 0.5 else ("B", "D")
78:     aux = [
79:         tk.aux("S", "H", False, "#d9480f"), tk.aux("H", "Hp", False, "#d9480f"),
80:         tk.aux("S", "P", False, "#d9480f"), tk.aux("P", "Pp", False, "#d9480f"),
82:     ]
83: 
84:     spec = tk.make_spec("中心投影共线性质", "圆柱-半球组合体三视图", vs,
85:                         faces=None, edges=edges, aux_lines=aux, labels=None,
86:                         camera=tk.flat_camera(vs))
87:     spec["labels"] = [{"point": k, "text": t} for k, t in [
88:         ("S", "S"), ("H", "H"), ("Hp", "H′"), ("P", "P"),
89:         ("Pp", "P′"), ("T", "T"), ("O", "O")]]
90: 
91:     corr = "点光源 S、半球顶点 H 及其地面影子 H′ 三点共线"
92:     wrong = [
93:         "S、圆柱轴心 O 与切点 T 三点共线",
94:         "H′、P′ 与切点 T 三点共线",
95:         "S、P、T 三点共线"
96:     ]
97:     texts = [corr] + wrong
98:     rng.shuffle(texts)
100:     ans = [k for k, v in opts.items() if v == corr][0]
101: 
102:     q = ("如图,点光源 S 照射圆柱-半球组合体,H 为半球顶点,H′ 为 H 在地面上的影子,"
103:          "T 为从 S 引向半球面的切点,P 为圆柱侧面上一点。根据中心投影的共线性质,"
104:          "下列哪三点必定共线?")
105:     return tk.item(spec, q, opts, ans)

K26 Spec-to-Image Review

  • fatal/plan_coverage
    plan要求单幅轴截面展示中心投影,spec生成three_view_layout平铺三视图,仅front含投影线,side/top空洞无意义
    spec_fix: 删除three_view_layout,改为单幅轴截面图或立体直观图,仅保留矩形+半圆主体与投影线
  • major/camera_or_view
    camera俯视三视图拼贴,H'在极左,S在右下,投影线横贯视图间隙,空间关系破碎
    spec_fix: 调整camera为正视单图,target在轴截面中心,orthoSize适配图幅,避免俯视拼贴
  • major/mesh_clutter
    76条edges大量用于三视图外框与32段虚线俯视圆,主体投影线被淹没在方块中
    spec_fix: 移除side/top外框与俯视圆,仅保留轴截面轮廓、半圆弧线及必要虚线
  • minor/tangent_or_section
    切点T与切线ST在拼贴图中几乎不可见,未突出切点位置与切线性质
    spec_fix: 单幅图中加粗ST切线,T用实心点标记并配标签,确保与半球轮廓相切关系清晰
  • minor/label_overlap
    H'、P'、O标签因三视图拼贴位置散乱,H'远离几何主体,易误读
    spec_fix: 单幅图中将标签置于对应点外侧空白处,H'靠近地面基线,避免贴线或跨视图
What To Change
  • 删除three_view_layout及side/top视图
  • 改为单幅轴截面图,添加地面基线
  • 保留front矩形+半圆及投影线
Preferred Primitives
  • arc
Remove/Simplify
  • 移除三视图外框edges
  • 移除32段虚线俯视圆
  • 简化半球为单条半圆弧线
Label Fixes
  • H'、P'置于地面基线旁
  • S置于右上方空白处
  • T用实心点并旁注标签
Camera Fixes
  • 改为正视camera对准轴截面平面
  • orthoSize适配单图范围
Toolkit/Renderer: none
representation: 误用三视图拼贴表达单一轴截面,76条edges大量消耗于视图外框与side/top轮廓,front投影线被稀释
readability: current视图三视图堆叠混乱,oblique_plus45旋转后更不可读;H'远离主体,SHH'共线被视图间隙割裂
plan: 必须元素虽在front视图中存在,但side/top空洞且遮挡主体,切点T几乎不可见,未满足轴截面清晰展示要求
next: spec_rewrite

center_proj_collinearity__S002

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 中心投影共线性质
难度 L2
Scene
一个轴线水平的正圆台,下底大圆在左、上底小圆在右,置于水平地面上;右侧设竖直投影面(墙面)。圆台轴截面为等腰梯形,用细双点划线标出;内部画一条从下底前边缘到上底后边缘的空间对角线,被遮挡段为虚线。圆台右上方置一球与最上母线切于点T,球心为O。点光源S在左上方,从S过圆台右端最上边缘点A、最下边缘点B分别作投影线交墙面于A′、B′,墙面上影子轮廓为梯形。所有隐藏边均用虚线绘制。
Must Render
[
  "轴线水平的正圆台(含上下底椭圆、母线、隐藏虚线)",
  "点光源S及从S过边缘点A、B到墙面影子A′、B′的投影线",
  "圆台轴截面(等腰梯形)",
  "圆台内部空间对角线(含虚线隐藏段)",
  "与母线相切的球及切点T、球心O",
  "右侧竖直投影面及梯形影子轮廓"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "圆台中心投影",
  "camera": {
    "position": [
      7.933,
      6.22,
      16.579
    ],
    "target": [
      1.901,
      1.163,
      0.008
    ],
    "orthoSize": 4.813
  },
  "counts": {
    "vertices": 41,
    "faces": 14,
    "edges": 1,
    "aux_lines": 18,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 9
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.3310214822059005,
      "position": [
        1.5873864374476077,
        2.0385340910907583,
        0.3310214822059005
      ]
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A′",
      "text": "A′"
    },
    {
      "point": "B′",
      "text": "B′"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "J",
      "text": "J"
    }
  ],
  "vertex_names": [
    "a0",
    "b0",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "P",
    "A",
    "R",
    "s0",
    "t0",
    "U",
    "V",
    "B",
    "X",
    "Y",
    "S",
    "A′",
    "B′",
    "W1",
    "W2",
    "W3",
    "W4",
    "E1",
    "E2",
    "E3",
    "E4",
    "G1",
    "G2",
    "G3",
    "G4",
    "T",
    "O"
  ],
  "faces_sample": [
    {
      "points": [
        "L",
        "K",
        "J",
        "I",
        "H",
        "G",
        "F",
        "E",
        "D",
        "C",
        "b0",
        "a0"
      ]
    },
    {
      "points": [
        "M",
        "N",
        "P",
        "A",
        "R",
        "s0",
        "t0",
        "U",
        "V",
        "B",
        "X",
        "Y"
      ]
    },
    {
      "points": [
        "a0",
        "b0",
        "N",
        "M"
      ]
    },
    {
      "points": [
        "b0",
        "C",
        "P",
        "N"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "A",
        "P"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "R",
        "A"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "s0",
        "R"
      ]
    },
    {
      "points": [
        "F",
        "G",
        "t0",
        "s0"
      ]
    },
    {
      "points": [
        "G",
        "H",
        "U",
        "t0"
      ]
    },
    {
      "points": [
        "H",
        "I",
        "V",
        "U"
      ]
    },
    {
      "points": [
        "I",
        "J",
        "B",
        "V"
      ]
    },
    {
      "points": [
        "J",
        "K",
        "X",
        "B"
      ]
    },
    {
      "points": [
        "K",
        "L",
        "Y",
        "X"
      ]
    },
    {
      "points": [
        "L",
        "a0",
        "M",
        "Y"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "a0",
        "t0"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "S",
        "A′"
      ],
      "dash": false,
      "color": "#111"
    },
    {
      "points": [
        "S",
        "B′"
      ],
      "dash": false,
      "color": "#111"
    },
    {
      "points": [
        "D",
        "A"
      ],
      "dash": true,
      "color": "#888"
    },
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true,
      "color": "#888"
    },
    {
      "points": [
        "B",
        "J"
      ],
      "dash": true,
      "color": "#888"
    },
    {
      "points": [
        "J",
        "D"
      ],
      "dash": true,
      "color": "#888"
    },
    {
      "points": [
        "W1",
        "W2"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "W2",
        "W3"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "W3",
        "W4"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "W4",
        "W1"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "E1",
        "E2"
      ],
      "dash": false,
      "color": "#444"
    },
    {
      "points": [
        "E2",
        "E3"
      ],
      "dash": false,
      "color": "#444"
    },
    {
      "points": [
        "E3",
        "E4"
      ],
      "dash": false,
      "color": "#444"
    },
    {
      "points": [
        "E4",
        "E1"
      ],
      "dash": false,
      "color": "#444"
    },
    {
      "points": [
        "G1",
        "G2"
      ],
      "dash": true,
      "color": "#bbb"
    },
    {
      "points": [
        "G2",
        "G3"
      ],
      "dash": true,
      "color": "#bbb"
    },
    {
      "points": [
        "G3",
        "G4"
      ],
      "dash": true,
      "color": "#bbb"
    },
    {
      "points": [
        "G4",
        "G1"
      ],
      "dash": true,
      "color": "#bbb"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     n = 12
4:     R = rng.uniform(0.9, 1.3)
5:     r = rng.uniform(0.35, 0.6)

6:     L = rng.uniform(2.2, 3.0)
7:     vs, faces = tk.frustum(n, R, r, L)
8:     # 轴线水平:原 y 轴 → x 轴,原 x 轴 → y 轴(反向)并平移使底部贴地
9:     vs = {k: (y, R - x, z) for k, (x, y, z) in vs.items()}
10:     # 重命名:把 must_render 需要的 A、B、S、T 让出来
11:     rep = {"A": "a0", "B": "b0", "Q": "A", "W": "B", "S": "s0", "T": "t0"}
12:     vs = {rep.get(k, k): v for k, v in vs.items()}
13:     faces = [{"points": [rep.get(p, p) for p in f["points"]]} for f in faces]
14:     D, J, a0, t0 = "D", "J", "a0", "t0"
15:     # 点光源 S(左上方)
16:     ds = rng.uniform(1.8, 2.6)
17:     hs = rng.uniform(1.4, 2.2)
33:         "G3": (WX + 0.3, 0.0, -1.5), "G4": (-1.0, 0.0, -1.5),
34:     })
35:     # 球与最上母线切于 T,球心 O
36:     tt = rng.uniform(0.55, 0.8)
37:     Tx = tt * L
38:     Ty = 2.0 * R + tt * (r - R)
39:     rad = rng.uniform(0.25, 0.42)
40:     vs["T"] = (Tx, Ty, 0.0)
41:     vs["O"] = (Tx, Ty, rad)
42:     # 辅助线:投影线、轴截面、墙面、影子梯形、地面
43:     auxs = [
44:         tk.aux("S", "A′", False, "#111"), tk.aux("S", "B′", False, "#111"),
45:         tk.aux(D, "A", True, "#888"), tk.aux("A", "B", True, "#888"),
46:         tk.aux("B", J, True, "#888"), tk.aux(J, D, True, "#888"),
52:         tk.aux("G3", "G4", True, "#bbb"), tk.aux("G4", "G1", True, "#bbb"),
53:     ]
54:     cam = tk.auto_camera(vs, azim=20.0, elev=16.0)
55:     spec = tk.make_spec("中心投影共线性质", "圆台中心投影", vs, faces=faces,
56:                         edges=[tk.edge(a0, t0)], aux_lines=auxs,
57:                         labels=["A", "B", "S", "A′", "B′", "O", "T", "D", "J"],
58:                         camera=cam)
59:     spec["primitives"] = [tk.prim_sphere(rad, vs["O"])]
60:     opts = [
61:         "S、A、A′三点共线",
62:         "S、A、A′三点不共线,因为被圆台轴截面遮挡",
63:         "S、球心O、A′三点共线",
64:         "S、A、切点T三点共线"
65:     ]
66:     rng.shuffle(opts)
67:     letters = "ABCD"
68:     options = {letters[i]: opts[i] for i in range(4)}
69:     ans = letters[opts.index("S、A、A′三点共线")]
70:     return tk.item(spec, "如图,在点光源S的照射下,圆台在右侧墙面上形成影子。根据中心投影共线性质,下列关于点光源S、圆台边缘点A及其影子A′的说法正确的是( )", options, ans)

K26 Spec-to-Image Review

  • major/frustum_render
    圆台侧面由12个平面拼成,棱线明显呈棱台而非圆台
    spec_fix: 改用frustum primitive光滑渲染或上下底椭圆+母线表示,删除离散侧面网格
  • major/mesh_clutter
    12边形mesh导致圆台轮廓线密集,教材图中圆台应为光滑曲面
    spec_fix: 仅保留上下底椭圆和必要母线、隐藏线,移除密铺网格
  • minor/tangent_or_section
    空间对角线仅一条实线edge,被圆台遮挡段未画为虚线
    spec_fix: 将对角线分为可见段和被遮挡段,分别用实线和虚线绘制
  • minor/label_overlap
    球心O与切点T标签紧贴球体,T点无显著切点标记
    spec_fix: O、T外移至球右上方空白处,T处添加小圆点标记
  • minor/other
    轴截面要求细双点划线,spec仅用普通虚线
    spec_fix: 若renderer支持则改用双点划线,否则在spec中注明线型差异
What To Change
  • 圆台改用光滑表示,移除12边形mesh
  • 空间对角线分虚实两段
  • 轴截面线型修正
Preferred Primitives
  • frustum
  • sphere
Remove/Simplify
  • 12个离散侧面四边形面
  • 球体多余经纬线若存在
Label Fixes
  • O、T外移防贴线
  • T加切点标记
Camera Fixes
  • 保持current视角,oblique视角可辅助观察底面大小
Toolkit/Renderer: 需支持frustum光滑渲染(椭圆+母线)或提供圆台框架生成函数,支持双点划线型
representation: 圆台离散为12棱台严重失真,球与投影线基本正确但切点与遮挡线处理不足
readability: 投影关系与标签基本可读,但圆台多边形化导致几何直观性大幅下降
plan: 覆盖光源、投影线、墙面、球、轴截面等主要元素,但圆台光滑度和空间对角线虚实未达标
next: toolkit_patch

center_proj_collinearity__S007

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
直圆台置于水平地面,上底面放置一球并与之相切于圆心C。点光源S在组合体左上方。过S与圆台轴线作铅垂辅助面,用细虚线画出该面与圆台的轴截面(等腰梯形)及球的大圆。从S引实线过球左侧轮廓点A并延长交地面于A′;引实线过球最高点B并延长交地面于B′;取圆台右下底边缘点D,其在地面竖直下方标注干扰点D′。圆台背面母线、球背侧轮廓、下底面后半圆均画虚线,球与圆台的地面影子区域重叠,A′位于球影子左边界。
Must Render
[
  "圆台与球的组合体轮廓(含背面虚线隐藏边)",
  "点光源S及投影实线SA、SB及其地面交点A′、B′",
  "铅垂辅助面与组合体的交线(圆台轴截面和球大圆)",
  "切点C与干扰点D′"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "圆台与球中心投影",
  "camera": {
    "position": [
      9.312,
      7.801,
      14.746
    ],
    "target": [
      0.097,
      1.126,
      0.0
    ],
    "orthoSize": 4.89
  },
  "counts": {
    "vertices": 47,
    "faces": 14,
    "edges": 4,
    "aux_lines": 21,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 8
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.40178335005859267,
      "position": [
        0.0,
        1.739552090668612,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "FJ",
      "text": "D"
    },
    {
      "point": "Ap",
      "text": "A′"
    },
    {
      "point": "Bp",
      "text": "B′"
    },
    {
      "point": "Dp",
      "text": "D′"
    }
  ],
  "vertex_names": [
    "FA",
    "FB",
    "FC",
    "FD",
    "FE",
    "FF",
    "FG",
    "FH",
    "FI",
    "FJ",
    "FK",
    "FL",
    "FM",
    "FN",
    "FP",
    "FQ",
    "FR",
    "FS",
    "FT",
    "FU",
    "FV",
    "FW",
    "FX",
    "FY",
    "S",
    "A",
    "B",
    "C",
    "Dp",
    "Ap",
    "Bp",
    "sph0",
    "sph1",
    "sph2",
    "sph3",
    "sph4",
    "sph5",
    "sph6",
    "sph7",
    "sph8",
    "sph9",
    "sph10",
    "sph11",
    "sph12",
    "sph13",
    "sph14",
    "sph15"
  ],
  "faces_sample": [
    {
      "points": [
        "FL",
        "FK",
        "FJ",
        "FI",
        "FH",
        "FG",
        "FF",
        "FE",
        "FD",
        "FC",
        "FB",
        "FA"
      ]
    },
    {
      "points": [
        "FM",
        "FN",
        "FP",
        "FQ",
        "FR",
        "FS",
        "FT",
        "FU",
        "FV",
        "FW",
        "FX",
        "FY"
      ]
    },
    {
      "points": [
        "FA",
        "FB",
        "FN",
        "FM"
      ]
    },
    {
      "points": [
        "FB",
        "FC",
        "FP",
        "FN"
      ]
    },
    {
      "points": [
        "FC",
        "FD",
        "FQ",
        "FP"
      ]
    },
    {
      "points": [
        "FD",
        "FE",
        "FR",
        "FQ"
      ]
    },
    {
      "points": [
        "FE",
        "FF",
        "FS",
        "FR"
      ]
    },
    {
      "points": [
        "FF",
        "FG",
        "FT",
        "FS"
      ]
    },
    {
      "points": [
        "FG",
        "FH",
        "FU",
        "FT"
      ]
    },
    {
      "points": [
        "FH",
        "FI",
        "FV",
        "FU"
      ]
    },
    {
      "points": [
        "FI",
        "FJ",
        "FW",
        "FV"
      ]
    },
    {
      "points": [
        "FJ",
        "FK",
        "FX",
        "FW"
      ]
    },
    {
      "points": [
        "FK",
        "FL",
        "FY",
        "FX"
      ]
    },
    {
      "points": [
        "FL",
        "FA",
        "FM",
        "FY"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "Ap"
      ]
    },
    {
      "points": [
        "S",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "Bp"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        [
          1.4547726417641815,
          0.0,
          0.0
        ],
        [
          -1.4547726417641815,
          0.0,
          0.0
        ]
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        [
          -1.4547726417641815,
          0.0,
          0.0
        ],
        [
          -0.5261714742492535,
          1.3377687406100192,
          0.0
        ]
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        [
          -0.5261714742492535,
          1.3377687406100192,
          0.0
        ],
        [
          0.5261714742492535,
          1.3377687406100192,
          0.0
        ]
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        [
          0.5261714742492535,
          1.3377687406100192,
          0.0
        ],
        [
          1.4547726417641815,
          0.0,
          0.0
        ]
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "FJ",
        "Dp"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph0",
        "sph1"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph1",
        "sph2"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph2",
        "sph3"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph3",
        "sph4"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph4",
        "sph5"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph5",
        "sph6"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph6",
        "sph7"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph7",
        "sph8"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph8",
        "sph9"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph9",
        "sph10"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph10",
        "sph11"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph11",
        "sph12"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph12",
        "sph13"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph13",
        "sph14"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph14",
        "sph15"
      ],
      "dash": true,
      "color": "#adb5bd"
    },
    {
      "points": [
        "sph15",
        "sph0"
      ],
      "dash": true,
      "color": "#adb5bd"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 1. 随机几何参数
5:     h = rng.uniform(1.0, 1.4)

13:     y_top = h + 2.0 * r_sph
14: 
15:     # 2. 圆台(正12棱台近似),重命名避免冲突
16:     n = 12
17:     vs, faces = tk.frustum(n, r_bot=r_bot, r_top=r_top, h=h)
18:     rename = {k: "F" + k for k in vs}
19:     vs = {rename[k]: v for k, v in vs.items()}
20:     for f in faces:
21:         f["points"] = [rename[p] for p in f["points"]]
22: 
23:     # 3. 球左侧切点 A(S 与球心均在 z=0 平面,取左侧切点)
24:     dx, dy = sx, sy - y_sph
25:     d = math.hypot(dx, dy)
26:     cos_b = r_sph / d
27:     sin_b = math.sqrt(max(0.0, 1.0 - cos_b * cos_b))
40:     })
41: 
42:     # 4. 中心投影地面交点
43:     tA = sy / (sy - ay)
44:     vs["Ap"] = (sx + (ax - sx) * tA, 0.0, 0.0)
45:     tB = sy / (sy - y_top)
46:     vs["Bp"] = (sx * (1.0 - tB), 0.0, 0.0)
47: 
48:     # 5. 投影实线
49:     edges = [
50:         tk.edge("S", "A", dash=False),
51:         tk.edge("A", "Ap", dash=False),
52:         tk.edge("S", "B", dash=False),
53:         tk.edge("B", "Bp", dash=False),
54:     ]
55: 
56:     # 6. 铅垂辅助面(z=0)交线:圆台轴截面 + 球大圆 + 干扰竖线
57:     aux_lines = [
58:         tk.aux((r_bot, 0.0, 0.0), (-r_bot, 0.0, 0.0), dash=True, color="#adb5bd"),
59:         tk.aux((-r_bot, 0.0, 0.0), (-r_top, h, 0.0), dash=True, color="#adb5bd"),
60:         tk.aux((-r_top, h, 0.0), (r_top, h, 0.0), dash=True, color="#adb5bd"),
61:         tk.aux((r_top, h, 0.0), (r_bot, 0.0, 0.0), dash=True, color="#adb5bd"),
73:         circ.append(nm)
74:     for i in range(m):
75:         aux_lines.append(tk.aux(circ[i], circ[(i + 1) % m], dash=True, color="#adb5bd"))
76: 
77:     # 7. 球 primitive
78:     primitives = [tk.prim_sphere(r=r_sph, center=(0.0, y_sph, 0.0))]
79: 
80:     spec = tk.make_spec("中心投影共线性质", "圆台与球中心投影", vs, faces=faces,
81:                         edges=edges, aux_lines=aux_lines, primitives=primitives, labels=None)
82: 
83:     spec["labels"] = [
84:         {"point": "S", "text": "S"}, {"point": "A", "text": "A"},
85:         {"point": "B", "text": "B"}, {"point": "C", "text": "C"},
86:         {"point": "FJ", "text": "D"}, {"point": "Ap", "text": "A′"},
87:         {"point": "Bp", "text": "B′"}, {"point": "Dp", "text": "D′"},
89: 
90:     wrong = [
91:         "点光源 S、圆台底面边缘点 D 及其竖直下方点 D′ 三点共线,D′ 是 D 在地面上的真实影子",
92:         "点 B′ 是圆台顶点(或某条母线)在地面上的影子",
93:         "点光源 S、切点 C 及 C 在地面的竖直下方点三点共线,该下方点是 C 的真实影子",
94:     ]
95:     rng.shuffle(wrong)
96: 
97:     ans = rng.choice("ABCD")
100:     for L in "ABCD":
101:         if L == ans:
102:             opts[L] = "点光源 S、球面轮廓点 A 及其影子 A′ 三点共线,A′ 是 A 在地面上的真实影子"
103:         else:
104:             opts[L] = wrong[widx]
105:             widx += 1
106: 
107:     q = ("如图,点光源S照射由圆台与球组成的组合体,球与圆台上底面相切于点C。图中标注了地面上的三个点:"
108:          "A′在直线SA延长线上,B′在直线SB延长线上,D′为圆台底面边缘点D的竖直下方点。根据中心投影共线性质,下列判断正确的是?")
109:     return tk.item(spec, q, opts, ans)

K26 Spec-to-Image Review

  • major/frustum_render
    圆台被画成12棱台,侧面有12条母线与梯形网格,完全不像教材光滑圆台
    spec_fix: 移除棱台侧面faces,改用上下底圆加2-4条母线;需toolkit支持圆台或section_circle
  • major/label_overlap
    A、B、C在球左侧与顶部严重重叠,current视图中A'、D、D'几乎粘连
    spec_fix: 调整标签偏移:B放球正上方,A放球左外侧,C放球心下方;拉开A'、D、D'间距
  • minor/mesh_clutter
    12棱台侧面产生大量多余棱线和faces,图像杂乱
    spec_fix: 删除所有侧面faces与多余顶点,仅保留轮廓线与底面圆
  • minor/sphere_render
    球大圆由16段aux_lines硬搓成多边形,不够光滑
    spec_fix: 改用toolkit的section_circle primitive或提高分段数至32以上
  • minor/camera_or_view
    主视角A'与D、D'距离过近,仅靠detail_zoom辅助区分
    spec_fix: 微调相机角度或适当拉大orthoSize,使主视图即可分辨三点
What To Change
  • 圆台由12棱台faces改为上下底圆+母线
  • 球标签偏移避免重叠
  • 移除多余侧面faces
Preferred Primitives
  • frustum
  • section_circle
Remove/Simplify
  • 12个侧面梯形faces
  • 12条侧面棱线
  • 球大圆16段aux_lines
Label Fixes
  • B置于球顶上方
  • A置于球左轮廓外侧
  • C置于球心下方
  • A'DD'水平错开
Camera Fixes
  • 主视角略拉远orthoSize,使底面标签不重叠
Toolkit/Renderer: toolkit需支持圆台primitive(上下椭圆+母线)或section_circle,避免只能用多边形棱台近似
representation: 圆台使用12棱台faces严重偏离教材样式,侧面网格过密;球大圆线段近似尚可。
readability: 主视图标签重叠严重,球上A/B/C与底面A'/D/D'拥挤;detail_zoom仅部分缓解。
plan: 基本覆盖plan要求的元素,但圆台几何失真导致场景不像中心投影下的圆台与球组合体。
next: toolkit_patch

center_proj_collinearity__S010

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 4 major_spec_issue minor hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
水平投影面 β 上放置正四棱锥 S-ABCD,底面 ABCD 画实线并标注,补全虚线对角线 AC、BD。一个倾斜平面截四棱锥得四边形截面 EFGH,E、F、G、H 分别在四条侧棱上;截面与四棱锥内切球相交得到一个小圆,该圆与 EF、FG、GH、HE 切于 T1、T2、T3、T4。空间点光源 L 位于锥体左上方。从 L 向 E、F、G、H 引投影线并延长交 β 于影点 E′、F′、G′、H′。画出截面两条对角线 EG、FH,以及被遮挡侧棱的虚线隐藏段。
Must Render
[
  "正四棱锥 S-ABCD 及斜截面四边形 EFGH",
  "截面内切圆及其与四边的切点 T1、T2、T3、T4",
  "点光源 L 到 E、F、G、H 的投影线及延长到影点 E′、F′、G′、H′",
  "底面正方形 ABCD 及其对角线 AC、BD",
  "被遮挡棱线与隐藏边的虚线表示"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "正四棱锥S-ABCD;斜截面EFGH;小圆由内切球与截面平面相交生成;含隐藏侧棱虚线",
  "camera": {
    "position": [
      8.588,
      8.759,
      14.18
    ],
    "target": [
      0.017,
      0.807,
      0.025
    ],
    "orthoSize": 5.132
  },
  "counts": {
    "vertices": 65,
    "faces": 5,
    "edges": 12,
    "aux_lines": 60,
    "aux_faces": 2,
    "primitives": 1,
    "labels": 19
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.6973746497834034,
      "position": [
        0.0,
        0.6973746497834034,
        0.0
      ],
      "color": "#d0ebff"
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "G",
      "text": "G"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "L",
      "text": "L"
    },
    {
      "point": "T1",
      "text": "T1"
    },
    {
      "point": "T2",
      "text": "T2"
    },
    {
      "point": "T3",
      "text": "T3"
    },
    {
      "point": "T4",
      "text": "T4"
    },
    {
      "point": "Ep",
      "text": "E′"
    },
    {
      "point": "Fp",
      "text": "F′"
    },
    {
      "point": "Gp",
      "text": "G′"
    },
    {
      "point": "Hp",
      "text": "H′"
    },
    {
      "point": "beta",
      "text": "β"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "S",
    "E",
    "F",
    "G",
    "H",
    "O",
    "P",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15",
    "c16",
    "c17",
    "c18",
    "c19",
    "c20",
    "c21",
    "c22",
    "c23",
    "c24",
    "c25",
    "c26",
    "c27",
    "c28",
    "c29",
    "c30",
    "c31",
    "c32",
    "c33",
    "c34",
    "c35",
    "c36",
    "c37",
    "c38",
    "c39",
    "T1",
    "T2",
    "T3",
    "T4",
    "L",
    "Ep",
    "Fp",
    "Gp",
    "Hp",
    "b1",
    "b2",
    "b3",
    "b4",
    "beta"
  ],
  "faces_sample": [
    {
      "points": [
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "S"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "S"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "S"
      ]
    },
    {
      "points": [
        "D",
        "A",
        "S"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "B"
      ]
    },
    {
      "points": [
        "S",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "S",
        "D"
      ],
      "dash": true
    },
    {
      "points": [
        "E",
        "F"
      ]
    },
    {
      "points": [
        "F",
        "G"
      ]
    },
    {
      "points": [
        "G",
        "H"
      ]
    },
    {
      "points": [
        "H",
        "E"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "B",
        "D"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "E",
        "G"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "F",
        "H"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "S",
        "G"
      ],
      "dash": true,
      "color": "#212529"
    },
    {
      "points": [
        "G",
        "C"
      ],
      "dash": true,
      "color": "#212529"
    },
    {
      "points": [
        "S",
        "H"
      ],
      "dash": true,
      "color": "#212529"
    },
    {
      "points": [
        "H",
        "D"
      ],
      "dash": true,
      "color": "#212529"
    },
    {
      "points": [
        "L",
        "E"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "E",
        "Ep"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "L",
        "F"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "F",
        "Fp"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "L",
        "G"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "G",
        "Gp"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "L",
        "H"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "H",
        "Hp"
      ],
      "dash": false,
      "color": "#495057"
    },
    {
      "points": [
        "b1",
        "b2"
      ],
      "dash": false,
      "color": "#ced4da"
    },
    {
      "points": [
        "b2",
        "b3"
      ],
      "dash": false,
      "color": "#ced4da"
    },
    {
      "points": [
        "b3",
        "b4"
      ],
      "dash": false,
      "color": "#ced4da"
    },
    {
      "points": [
        "b4",
        "b1"
      ],
      "dash": false,
      "color": "#ced4da"
    },
    {
      "points": [
        "c0",
        "c1"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c1",
        "c2"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c2",
        "c3"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c3",
        "c4"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c4",
        "c5"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c5",
        "c6"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c6",
        "c7"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c7",
        "c8"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c8",
        "c9"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c9",
        "c10"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c10",
        "c11"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c11",
        "c12"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c12",
        "c13"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c13",
        "c14"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c14",
        "c15"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c15",
        "c16"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c16",
        "c17"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c17",
        "c18"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c18",
        "c19"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c19",
        "c20"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c20",
        "c21"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c21",
        "c22"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c22",
        "c23"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c23",
        "c24"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c24",
        "c25"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c25",
        "c26"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c26",
        "c27"
      ],
      "dash": false,
      "color": "#868e96"
    },
    {
      "points": [
        "c27",
        "c28"
      ],
      "dash": false,
      "color": "#868e96"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "b1",
        "b2",
        "b3",
        "b4"
      ],
      "color": "#e9ecef",
      "opacity": 0.1
    },
    {
      "points": [
        "E",
        "F",
        "G",
        "H"
      ],
      "color": "#ffd43b",
      "opacity": 0.2
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     def lerp(p, q, t):
5:         return tk.vadd(tk.vmul(p, 1.0 - t), tk.vmul(q, t))

15:         return tk.vadd(L, tk.vmul(tk.vsub(P, L), s))
16: 
17:     # ---------- 正四棱锥 S-ABCD,底面在水平投影面 beta 上 ----------
18:     r = rng.choice([1.25, 1.35, 1.45])
19:     h = rng.uniform(2.65, 3.10)
20:     vs0, pyr_faces = tk.pyramid(4, r=r, h=h, apex="S")
21: 
22:     S0 = vs0["S"]
23:     A0, B0, C0, D0 = vs0["A"], vs0["B"], vs0["C"], vs0["D"]
24: 
25:     # ---------- 正四棱锥内切球 ----------
26:     # base apothem = r/sqrt(2), inradius = a*h/(a+sqrt(a^2+h^2))
27:     a_ap = r / math.sqrt(2.0)
28:     slant = math.sqrt(h * h + a_ap * a_ap)
29:     R = a_ap * h / (a_ap + slant)
30:     O0 = (0.0, R, 0.0)
31: 
32:     # ---------- 斜截面:用平面 y = y0 + ux*x + uz*z 截四条侧棱 ----------
33:     # y0 取接近内切球与侧面切触高度,使球-面交圆视觉上与四边相切;
34:     # ux, uz 非零,保证截面为倾斜截面。
35:     y_touch = h - h * h * (h - R) / (h * h + a_ap * a_ap)
36: 
37:     for _ in range(80):
38:         ux = rng.choice([-1.0, 1.0]) * rng.uniform(0.030, 0.060)
67:     H0 = lerp(S0, D0, tD)
68: 
69:     # ---------- 按“内切球 ∩ 斜截面平面”严格生成小圆 ----------
70:     nunit = tk.unit(N)
71:     Osec0 = tk.vsub(O0, tk.vmul(nunit, signed_d))
72:     rc = math.sqrt(max(0.0, R * R - signed_d * signed_d))
73: 
94:         circle_names.append(nm)
95: 
96:     # 四边与小圆的切点:取截面圆心到四边所在直线的垂足,显式标注 T1~T4
97:     extra["T1"] = line_foot(E0, F0, Osec0)
98:     extra["T2"] = line_foot(F0, G0, Osec0)
99:     extra["T3"] = line_foot(G0, H0, Osec0)
100:     extra["T4"] = line_foot(H0, E0, Osec0)
101: 
102:     # ---------- 点光源与中心投影影点 ----------
103:     L0 = (-2.85, 3.75, -1.35)
104:     extra["L"] = L0
105:     extra["Ep"] = shadow_from_L(L0, E0)
106:     extra["Fp"] = shadow_from_L(L0, F0)
114:     vs = {k: tk.rot_y(v, rot) for k, v in all0.items()}
115: 
116:     # ---------- 水平投影面 beta 的边界与标注点 ----------
117:     plane_pts = [vs[k] for k in ["A", "B", "C", "D", "Ep", "Fp", "Gp", "Hp"]]
118:     xs = [p[0] for p in plane_pts]
119:     zs = [p[2] for p in plane_pts]
120:     mx, Mx = min(xs) - 0.95, max(xs) + 0.95
129: 
130:     # ---------- 面、边、辅助线 ----------
131:     faces = list(pyr_faces)
132: 
133:     # 显式边线:底面实线、正四棱锥侧棱,其中后侧棱作隐藏虚线;截面边界实线
134:     edges = [
135:         tk.edge("A", "B"), tk.edge("B", "C"), tk.edge("C", "D"), tk.edge("D", "A"),
136:         tk.edge("S", "A"), tk.edge("S", "B"),
137:         tk.edge("S", "C", dash=True), tk.edge("S", "D", dash=True),
138:         tk.edge("E", "F"), tk.edge("F", "G"), tk.edge("G", "H"), tk.edge("H", "E")
145:     aux.append(tk.aux("B", "D", dash=True, color="#d9480f"))
146: 
147:     # 截面两条对角线 EG、FH
148:     aux.append(tk.aux("E", "G", dash=False, color="#d9480f"))
149:     aux.append(tk.aux("F", "H", dash=False, color="#d9480f"))
150: 
151:     # 显式补足被遮挡侧棱虚线隐藏段(按截点拆分,便于 review/spec 识别)
153:         aux.append(tk.aux(a1, a2, dash=True, color="#212529"))
154: 
155:     # 点光源到 E,F,G,H 的投影线及延长到 beta 上的影点
156:     for p, pp in [("E", "Ep"), ("F", "Fp"), ("G", "Gp"), ("H", "Hp")]:
157:         aux.append(tk.aux("L", p, dash=False, color="#495057"))
158:         aux.append(tk.aux(p, pp, dash=False, color="#495057"))
159: 
160:     # beta 投影面边界
161:     for a1, a2 in [("b1", "b2"), ("b2", "b3"), ("b3", "b4"), ("b4", "b1")]:
162:         aux.append(tk.aux(a1, a2, dash=False, color="#ced4da"))
163: 
164:     # 球-截面交圆:用多段折线显式绘制
165:     for i in range(segs):
166:         aux.append(tk.aux(circle_names[i], circle_names[(i + 1) % segs],
167:                           dash=False, color="#868e96"))
168: 
169:     aux_faces = [
170:         tk.face(["b1", "b2", "b3", "b4"], color="#e9ecef", opacity=0.10),
171:         tk.face(["E", "F", "G", "H"], color="#ffd43b", opacity=0.20)
172:     ]
173: 
174:     primitives = [
175:         tk.prim_sphere(r=R, center=vs["O"], color="#d0ebff")
176:     ]
177: 
178:     cam = tk.auto_camera(
179:         vs,
180:         primitives=primitives,
181:         azim=rng.uniform(25.0, 38.0),
182:         elev=rng.uniform(21.0, 29.0),
183:         pad=1.52
184:     )
185: 
186:     spec = tk.make_spec(
187:         "中心投影共线性质",
188:         "正四棱锥S-ABCD;斜截面EFGH;小圆由内切球与截面平面相交生成;含隐藏侧棱虚线",
189:         vs,
190:         faces=faces,
191:         edges=edges,
192:         aux_lines=aux,
193:         aux_faces=aux_faces,
194:         primitives=primitives,
195:         labels=None,
196:         camera=cam
197:     )
198: 
199:     spec["labels"] = (
200:         [{"point": k, "text": k} for k in
201:          ["S", "A", "B", "C", "D", "E", "F", "G", "H", "L", "T1", "T2", "T3", "T4"]]
202:         + [
203:             {"point": "Ep", "text": "E′"},
210: 
211:     # ---------- 题目与选项;答案由 L、F、F′ 的共线计算确定 ----------
212:     q = ("如图,正四棱锥 S-ABCD 的底面置于水平投影面 β 上,空间点光源为 L。斜截面与四棱锥相交得四边形 EFGH,"
213:          "其各边与截面内的内切圆分别切于 T1、T2、T3、T4。E、F、G、H 在 β 上的影点依次为 E′、F′、G′、H′。"
214:          "下列关于共线关系的说法正确的是")
215: 
216:     def is_collinear(a, b, c):
217:         A, B, Cc = vs[a], vs[b], vs[c]
218:         return tk.norm(tk.cross(tk.vsub(B, A), tk.vsub(Cc, A))) < 1e-7
219: 
220:     correct_text = "点光源 L、截面顶点 F 及其在投影面上的影点 F′ 三点共线"
221:     statements = [
222:         (correct_text, is_collinear("L", "F", "Fp")),
223:         ("点光源 L、截面顶点 F 与底面顶点 B 三点共线", is_collinear("L", "F", "B")),
224:         ("点光源 L、顶点 G 与影点 E′ 三点共线", is_collinear("L", "G", "Ep")),
225:         ("点光源 L、切点 T2 及其在投影面上的影点三点共线", False)
226:     ]
227: 
228:     rng.shuffle(statements)
229:     letters = ["A", "B", "C", "D"]

K26 Spec-to-Image Review

  • major/sphere_render
    current view 中蓝色实心球遮挡截面 EFGH、切点 T1-T4 及投影线,教材图应仅保留截面交线圆
    spec_fix: 移除 tk.prim_sphere primitive,仅保留 aux_lines 绘制的截面交线圆;如需暗示球存在,改用极淡虚线大圆
  • major/label_overlap
    T1-T4 与球体及截面边重叠,Ep 在 current view 中难以辨识
    spec_fix: 删除球后重新排布标签偏移,确保 T1-T4 位于四边外侧,影点标签远离密集线网
  • minor/aux_line_clutter
    交线圆使用40段灰色折线,颜色与隐藏线接近且段数过多
    spec_fix: 将交线圆减至16-24段,颜色改为 #212529 或黑色实线以突出截面圆轮廓
  • minor/camera_or_view
    相机 target 接近球心,球体占据画面中心,两视角均未能避开遮挡
    spec_fix: 调整相机 target 至截面四边形中心,略微拉远或降低 elev,使截面与影点关系清晰
What To Change
  • 删除 sphere primitive
  • 交线圆减段并加深颜色
  • 调整相机 target 与标签偏移
Preferred Primitives
  • section_circle
Remove/Simplify
  • 移除 tk.prim_sphere
  • 交线圆 aux_lines 从40段减至20段
  • beta 面边框可改虚线或减淡
Label Fixes
  • T1-T4 向外侧偏移避免贴线
  • Ep、Fp 等影点标签确保在 beta 面上方无遮挡
Camera Fixes
  • target 移向截面中心
  • orthoSize 适当缩小或视角右旋以展开重叠
Toolkit/Renderer: none
representation: 整体结构正确,但错误使用实心球 primitive 表达截面圆,导致核心几何被遮挡,不符合教材图惯例
readability: current 与 oblique 两图均因球体遮挡而难以辨认切点与截面,可读性差
plan: plan 要求的元素均已列入 spec,但因渲染表现问题未能有效传达
next: spec_rewrite

center_proj_collinearity__S015

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
水平地面上放置一竖直圆台,轴截面为等腰梯形,上下底圆水平,隐藏后半圆与内侧母线用虚线;其右侧外放一球体与圆台侧面相切于点C,球心O略低于圆台上底面。点光源S位于圆台左上方空间。必须画出:S至球体最高点M的连线并延长交地面于Q(穿球体前侧为实线,穿圆台后侧为虚线);S至圆台顶端边缘点N的连线交地面于N′;圆台在地面的影子边界;干扰点P置于圆台影区内部且明显不在直线SM上。
Must Render
[
  "圆台轴截面轮廓及上下底圆(实线可见部分与虚线隐藏部分)",
  "球体大圆轮廓及与圆台的切点C、球心O",
  "点光源S到球体最高点M的投影线及其向地面的延长线(虚实分段表示遮挡)",
  "地面上的圆台影子区域及标记点P、Q"
]

Spec Summary

{
  "title": "中心投影共线性质 · S015",
  "meta": "圆台球组合体中心投影",
  "camera": {
    "position": [
      13.818,
      10.477,
      20.836
    ],
    "target": [
      0.809,
      1.054,
      0.018
    ],
    "orthoSize": 6.984
  },
  "counts": {
    "vertices": 28,
    "faces": 8,
    "edges": 24,
    "aux_lines": 8,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 9
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.4022549442737217,
      "position": [
        1.1029867021470254,
        1.9176390263212666,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "Q",
      "text": "Q"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Np",
      "text": "N′"
    },
    {
      "point": "R",
      "text": "R"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "N",
      "text": "N"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "S",
    "M",
    "Q",
    "P",
    "Np",
    "R",
    "O",
    "N",
    "sh0",
    "sh1",
    "sh2",
    "sh3",
    "g0",
    "g1",
    "g2",
    "g3"
  ],
  "faces_sample": [
    {
      "points": [
        "F",
        "E",
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "G",
        "H",
        "I",
        "J",
        "K",
        "L"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "H",
        "G"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "I",
        "H"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "J",
        "I"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "K",
        "J"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "L",
        "K"
      ]
    },
    {
      "points": [
        "F",
        "A",
        "G",
        "L"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "G",
        "H"
      ]
    },
    {
      "points": [
        "A",
        "G"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "H",
        "I"
      ]
    },
    {
      "points": [
        "B",
        "H"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "I",
        "J"
      ]
    },
    {
      "points": [
        "C",
        "I"
      ]
    },
    {
      "points": [
        "D",
        "E"
      ]
    },
    {
      "points": [
        "J",
        "K"
      ]
    },
    {
      "points": [
        "D",
        "J"
      ]
    },
    {
      "points": [
        "E",
        "F"
      ]
    },
    {
      "points": [
        "K",
        "L"
      ]
    },
    {
      "points": [
        "E",
        "K"
      ]
    },
    {
      "points": [
        "F",
        "A"
      ]
    },
    {
      "points": [
        "L",
        "G"
      ]
    },
    {
      "points": [
        "F",
        "L"
      ]
    },
    {
      "points": [
        "S",
        "M"
      ]
    },
    {
      "points": [
        "S",
        "N"
      ]
    },
    {
      "points": [
        "g0",
        "g1"
      ]
    },
    {
      "points": [
        "g1",
        "g2"
      ]
    },
    {
      "points": [
        "g2",
        "g3"
      ]
    },
    {
      "points": [
        "g3",
        "g0"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "M",
        "Q"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "N",
        "Np"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "sh0",
        "sh1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "sh1",
        "sh2"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "sh2",
        "sh3"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "sh3",
        "sh0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "M"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "C"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "sh0",
        "sh1",
        "sh2",
        "sh3"
      ],
      "color": "#c0c0b8",
      "opacity": 0.25
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 几何参数
5:     h = rng.uniform(1.8, 2.5)

6:     rb = rng.uniform(1.0, 1.4)
7:     rt = rng.uniform(0.4, 0.7)
8:     n = 6  # 六棱台近似圆台,避免命名冲突
9: 
10:     # 生成棱台(底面 A-F,顶面 G-L)
11:     vs, faces = tk.frustum(n, rb, rt, h)
12: 
13:     # 球与圆台侧面外切
14:     L = math.sqrt((rb - rt) ** 2 + h ** 2)
15:     delta = rng.uniform(0.2, 0.4)
16:     rs = rng.uniform(0.3, 0.5)
17:     tc = max(0.25, min(0.75, (h - delta - rs * (rb - rt) / L) / h))
22:     O = (ox, oy, 0.0)
23: 
24:     # 球最高点 M
25:     M = (ox, oy + rs, 0.0)
26: 
27:     # 光源 S(左上方)
28:     sx = rng.uniform(2.0, 3.5)
34:     Q = (-sx + tq * (ox + sx), 0.0, 0.0)
35: 
36:     # N:圆台顶端边缘最右示意点;Np:N 在地面上的中心投影
37:     N = (rt, h, 0.0)
38:     tn = sy / (sy - h)
39:     Np_x = -sx + tn * (rt + sx)
40:     Np = (Np_x, 0.0, 0.0)
45:     P = (px, 0.0, pz)
46: 
47:     # R:球面上非最高点 K 的中心投影(干扰点)
48:     k_vec = (rs * 0.6, rs * 0.3, rs * 0.7)
49:     k_norm = math.sqrt(k_vec[0] ** 2 + k_vec[1] ** 2 + k_vec[2] ** 2)
50:     K = (
51:         ox + rs * k_vec[0] / k_norm,
56:     R = (-sx + tr * (K[0] + sx), 0.0, tr * K[2])
57: 
58:     # 切点 C
59:     C = (cx, cy, 0.0)
60: 
61:     # 合并顶点(全部独立命名,无冲突)
62:     vs.update(
103: 
104:     # 实线边
105:     edges = []
106:     bot = ["A", "B", "C", "D", "E", "F"]
107:     top = ["G", "H", "I", "J", "K", "L"]
108:     for i in range(n):
109:         edges.append(tk.edge(bot[i], bot[(i + 1) % n]))
110:         edges.append(tk.edge(top[i], top[(i + 1) % n]))
111:         edges.append(tk.edge(bot[i], top[i]))
112:     edges.append(tk.edge("S", "M"))
113:     edges.append(tk.edge("S", "N"))
114:     edges.append(tk.edge("g0", "g1"))
115:     edges.append(tk.edge("g1", "g2"))
116:     edges.append(tk.edge("g2", "g3"))
117:     edges.append(tk.edge("g3", "g0"))
118: 
119:     # 虚线辅助线:投影延长线、影区边界、辅助半径
120:     aux_lines = []
121:     aux_lines.append(tk.aux("M", "Q", dash=True))
122:     aux_lines.append(tk.aux("N", "Np", dash=True))
123:     aux_lines.append(tk.aux("sh0", "sh1", dash=True))
124:     aux_lines.append(tk.aux("sh1", "sh2", dash=True))
125:     aux_lines.append(tk.aux("sh2", "sh3", dash=True))
126:     aux_lines.append(tk.aux("sh3", "sh0", dash=True))
127:     aux_lines.append(tk.aux("O", "M", dash=True))
128:     aux_lines.append(tk.aux("O", "C", dash=True))
129: 
130:     # 影子区域(半透明)
131:     aux_faces = [
132:         tk.face(["sh0", "sh1", "sh2", "sh3"], color="#c0c0b8", opacity=0.25)
133:     ]
134: 
135:     # 球体
136:     prim = [tk.prim_sphere(r=rs, center=O)]
137: 
138:     spec = tk.make_spec(
139:         "中心投影共线性质 · S015",
140:         "圆台球组合体中心投影",
141:         vs,
142:         faces=faces,
143:         edges=edges,
144:         aux_lines=aux_lines,
145:         aux_faces=aux_faces,
146:         primitives=prim,
147:         labels=None,
148:     )
149: 
150:     spec["labels"] = [
151:         {"point": "S", "text": "S"},
152:         {"point": "M", "text": "M"},
153:         {"point": "Q", "text": "Q"},
154:         {"point": "P", "text": "P"},
164: 
165:     q = (
166:         "如图,该组合体由竖直圆台及右侧与其外切的球体构成,点光源S在左上方,"
167:         "球体最高点为M,地面上标有P、Q两点,其中P位于圆台投下的影区内部,Q位于影区外。"
168:         "根据中心投影共线性质,S、M与M在地面上的影子三点应共线,则影子落点是哪个点?"
169:     )
170: 
171:     return tk.item(spec, q, options, answer, answer_text="Q点", meta={"self_ref": True})

K26 Spec-to-Image Review

  • major/frustum_render
    圆台被画成六棱台,上下底为六边形,侧面由6个梯形拼成,完全丧失圆台曲面特征
    spec_fix: 改用圆台primitive或教材式上下椭圆+4条母线,彻底移除多边形侧面网格
  • minor/mesh_clutter
    六棱台产生大量棱线与隐藏虚线,与投影线、半径线交织,画面杂乱
    spec_fix: 恢复为圆台后仅保留轴截面轮廓、上下底可见弧及必要隐藏虚线
  • minor/label_overlap
    N、M、O、C四点标签在球体与台顶交界处密集重叠,难以分辨
    spec_fix: 调整标签偏移量,使各标签远离切点与轮廓线,避免互相覆盖
  • minor/plan_coverage
    影子区域仅用四边形近似,未体现圆台影子的梯形或曲边特征
    spec_fix: 影子边界应与圆台上下底投影一致,改用梯形阴影区表示
What To Change
  • 将六棱台替换为教材式圆台(上下椭圆/圆+母线)
  • 影子区域改为与圆台投影一致的梯形阴影
Preferred Primitives
  • frustum
  • sphere
Remove/Simplify
  • 删除六棱台全部侧面多边形面与12条棱线
  • 隐藏线仅保留轴截面内侧两条母线
Label Fixes
  • 将N、M、O、C标签向四周散开,避免在球台切点处堆叠
  • P点应明显位于影区内部且偏离SMQ直线
Camera Fixes
  • 维持当前斜二测视角,确保轴截面与投影关系可见
Toolkit/Renderer: toolkit当前frustum仅生成棱台mesh,需新增圆台primitive或支持教材式圆台渲染(上下底圆+母线)
representation: 球体primitive使用正确,但圆台被六棱台完全替代,核心几何体严重失真
readability: 投影关系与影区意图可辨,但棱台网格线严重干扰空间关系判读
plan: 光源、切点、投影线、干扰点P、影区均已覆盖,因圆台形状错误导致场景偏离
next: toolkit_patch

center_proj_collinearity__S017

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 4 major_spec_issue minor hold

Plan / Must Render

题型 中心投影共线性质
难度 L1
Scene
水平地面上方有一点光源S(黄色高亮显示)。空间内倾斜悬置一根细杆PQ,P端高、Q端低且均不触地。地面上有细杆的中心投影影子P'Q'。用红色实线画出两条投影光线:S-P-P' 与 S-Q-Q';细杆PQ与影子P'Q'用加粗黑线;从P、Q分别向地面作铅垂虚线作为定位辅助;地面画成带网格的矩形平面,影子区域浅灰填充。所有关键点S、P、Q、P'、Q'均标注字母。
Must Render
[
  "点光源S(黄色高亮)",
  "倾斜细杆PQ",
  "地面影子P'Q'",
  "投影光线SPP'与SQQ'",
  "地面网格与垂线辅助虚线"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "中心投影共线性质",
  "camera": {
    "position": [
      13.281,
      9.038,
      21.063
    ],
    "target": [
      -0.146,
      0.35,
      0.081
    ],
    "orthoSize": 7.008
  },
  "counts": {
    "vertices": 51,
    "faces": 2,
    "edges": 2,
    "aux_lines": 24,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 5
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.12,
      "position": [
        -0.3857331995312587,
        4.84114316166169,
        0.018039554189773654
      ],
      "color": "#fcc419"
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Q",
      "text": "Q"
    },
    {
      "point": "P'",
      "text": "P'"
    },
    {
      "point": "Q'",
      "text": "Q'"
    }
  ],
  "vertex_names": [
    "S",
    "P",
    "Q",
    "P'",
    "Q'",
    "G0",
    "G1",
    "G2",
    "G3",
    "Sh0",
    "Sh1",
    "Sh2",
    "Sh3",
    "Hp",
    "Hq",
    "gx0a",
    "gx0b",
    "gx1a",
    "gx1b",
    "gx2a",
    "gx2b",
    "gx3a",
    "gx3b",
    "gx4a",
    "gx4b",
    "gx5a",
    "gx5b",
    "gx6a",
    "gx6b",
    "gx7a",
    "gx7b",
    "gx8a",
    "gx8b",
    "gz9a",
    "gz9b",
    "gz10a",
    "gz10b",
    "gz11a",
    "gz11b",
    "gz12a",
    "gz12b",
    "gz13a",
    "gz13b",
    "gz14a",
    "gz14b",
    "gz15a",
    "gz15b",
    "gz16a",
    "gz16b",
    "gz17a",
    "gz17b"
  ],
  "faces_sample": [
    {
      "points": [
        "G0",
        "G1",
        "G2",
        "G3"
      ],
      "color": "#f5f5f5",
      "opacity": 0.6
    },
    {
      "points": [
        "Sh0",
        "Sh1",
        "Sh2",
        "Sh3"
      ],
      "color": "#e9ecef",
      "opacity": 0.7
    }
  ],
  "edges_sample": [
    {
      "points": [
        "P",
        "Q"
      ],
      "color": "#000000"
    },
    {
      "points": [
        "P'",
        "Q'"
      ],
      "color": "#000000"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "S",
        "P"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P",
        "P'"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "S",
        "Q"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "Q",
        "Q'"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "P",
        "Hp"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "Q",
        "Hq"
      ],
      "dash": true,
      "color": "#868e96"
    },
    {
      "points": [
        "gx0a",
        "gx0b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx1a",
        "gx1b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx2a",
        "gx2b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx3a",
        "gx3b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx4a",
        "gx4b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx5a",
        "gx5b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx6a",
        "gx6b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx7a",
        "gx7b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gx8a",
        "gx8b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz9a",
        "gz9b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz10a",
        "gz10b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz11a",
        "gz11b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz12a",
        "gz12b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz13a",
        "gz13b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz14a",
        "gz14b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz15a",
        "gz15b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz16a",
        "gz16b"
      ],
      "dash": false,
      "color": "#dee2e6"
    },
    {
      "points": [
        "gz17a",
        "gz17b"
      ],
      "dash": false,
      "color": "#dee2e6"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 随机构造 3D 场景 ----
5:     w = rng.uniform(3.0, 5.0)          # 地面半宽

30:     vs.update({"G0": (-w, 0.0, -d), "G1": (w, 0.0, -d),
31:                "G2": (w, 0.0, d), "G3": (-w, 0.0, d)})
32:     faces = [{"points": ["G0", "G1", "G2", "G3"], "color": "#f5f5f5", "opacity": 0.6}]
33: 
34:     # 影子区域浅灰窄带填充
35:     dx, dz = qqx - ppx, qqz - ppz
36:     L = math.hypot(dx, dz)
41:                    "Sh2": (qqx - ux, 0.0, qqz - uz),
42:                    "Sh3": (qqx + ux, 0.0, qqz + uz)})
43:         faces.append({"points": ["Sh0", "Sh1", "Sh2", "Sh3"],
44:                       "color": "#e9ecef", "opacity": 0.7})
45: 
46:     # 铅垂足
47:     vs["Hp"] = (P[0], 0.0, P[2])
49: 
50:     # 细杆与影子黑线
51:     edges = [{"points": ["P", "Q"], "color": "#000000"},
52:              {"points": ["P'", "Q'"], "color": "#000000"}]
53: 
54:     # 投影光线(红色实线)与铅垂虚线
55:     aux_lines = [
56:         tk.aux("S", "P", dash=False, color="#e03131"),
57:         tk.aux("P", "P'", dash=False, color="#e03131"),
58:         tk.aux("S", "Q", dash=False, color="#e03131"),
59:         tk.aux("Q", "Q'", dash=False, color="#e03131"),
71:         vs[f"gx{gi}a"] = (x, 0.0, -d)
72:         vs[f"gx{gi}b"] = (x, 0.0, d)
73:         aux_lines.append(tk.aux(f"gx{gi}a", f"gx{gi}b",
74:                                 dash=False, color="#dee2e6"))
75:         gi += 1
76:     for z in [i * step for i in range(int(math.ceil(-d / step)),
77:                                       int(math.floor(d / step)) + 1)]:
80:         vs[f"gz{gi}a"] = (-w, 0.0, z)
81:         vs[f"gz{gi}b"] = (w, 0.0, z)
82:         aux_lines.append(tk.aux(f"gz{gi}a", f"gz{gi}b",
83:                                 dash=False, color="#dee2e6"))
84:         gi += 1
85: 
86:     # 点光源高亮
87:     primitives = [tk.prim_sphere(r=0.12, center=S, color="#fcc419")]
88: 
89:     spec = tk.make_spec(
90:         "中心投影共线性质",
91:         "中心投影共线性质",
92:         vs,
93:         faces=faces,
94:         edges=edges,
95:         aux_lines=aux_lines,
96:         primitives=primitives,
97:         labels=["S", "P", "Q", "P'", "Q'"],
98:         camera=tk.auto_camera(vs, primitives,
99:                               azim=rng.uniform(20.0, 45.0),
100:                               elev=rng.uniform(15.0, 30.0))
101:     )
102: 
116:     return tk.item(
117:         spec,
118:         "如图,点光源S照射空间中的细杆PQ,在水平地面上形成影子P'Q'。根据中心投影的共线性质,下列哪一组三点必定在同一条直线上?",
119:         options,
120:         answer
121:     )

K26 Spec-to-Image Review

  • major/camera_or_view
    current view相机与细杆倾斜方向近似共线,S/P/Q/P'/Q'投影压缩成垂直线,空间关系完全丢失
    spec_fix: 调整auto_camera azim至60°-120°范围,确保相机与杆及影子平面形成显著夹角,避免透视压缩
  • major/label_overlap
    current view中S、P、Q、P'、Q'标签密集堆叠,几乎无法分辨
    spec_fix: 优化相机视角或增加标签自动偏移,确保关键点标签在投影平面上分散可辨
  • minor/plan_coverage
    current view中影子区域浅灰填充与投影光线因压缩几乎不可见,铅垂虚线定位作用丧失
    spec_fix: 以侧视或斜俯视为主视角,确保影子区域、投影折线与铅垂辅助线均清晰可定位
What To Change
  • 调整相机方位角避免与细杆共线,消除透视压缩
  • 优化标签布局防止重叠
Preferred Primitives
  • sphere
Remove/Simplify
  • 若current view无法修复则弃用,保留oblique类侧视角度
Label Fixes
  • 增加标签与点的偏移量,确保P'与Q'标签分离
Camera Fixes
  • 固定azim在60°-120°,elev 20°-35°,target对准场景中心
Toolkit/Renderer: none
representation: 几何结构完整,球、线、面使用正确,但相机参数导致投影失败。
readability: oblique视角可读性强,current视角因严重压缩几乎无法阅读。
plan: 所有元素已包含,但current视图未能有效展示倾斜杆与影子的空间关系。
next: visual_repair

center_proj_collinearity__S019

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
水平地面上并置一个正圆台(轴截面为等腰梯形,下底大、上底小且落地)与一个球体,球与圆台侧面相切。点光源 S 位于圆台上方偏左。作直线 SA 交地面于 A',其中 A 为圆台上底边缘点;作直线 ST 切球于 T 并延长交地面于 T'。地面阴影区用浅灰表示,两体影子重叠处加深。虚线画出圆台隐藏母线、下底后半边及球背向光源的轮廓线。
Must Render
[
  "正圆台轴截面轮廓(等腰梯形)及隐藏母线虚线",
  "球的大圆轮廓及背向光源的隐藏虚线",
  "光源 S 到圆台边缘点 A 及其地面影子 A' 的连线",
  "光源 S 到球切点 T 及其地面影子 T' 的切线",
  "地面上的阴影区域(圆台影、球影,重叠处加深)"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "S019",
  "camera": {
    "position": [
      1.515,
      21.087,
      0.591
    ],
    "target": [
      1.515,
      0.0,
      0.407
    ],
    "orthoSize": 5.673
  },
  "counts": {
    "vertices": 45,
    "faces": 0,
    "edges": 18,
    "aux_lines": 14,
    "aux_faces": 3,
    "primitives": 0,
    "labels": 7
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "A'",
      "text": "A'"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "T'",
      "text": "T'"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "M",
      "text": "M"
    }
  ],
  "vertex_names": [
    "S",
    "A",
    "A'",
    "T",
    "T'",
    "O",
    "M",
    "B",
    "C",
    "D",
    "Gm",
    "Gp",
    "Cs",
    "As",
    "Oc",
    "Os",
    "Ts",
    "Ov",
    "Ovs",
    "L0",
    "L1",
    "L2",
    "L3",
    "L4",
    "L5",
    "L6",
    "L7",
    "L8",
    "L9",
    "L10",
    "L11",
    "L12",
    "R0",
    "R1",
    "R2",
    "R3",
    "R4",
    "R5",
    "R6",
    "R7",
    "R8",
    "R9",
    "R10",
    "R11",
    "R12"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "Gm",
        "Gp"
      ]
    },
    {
      "points": [
        "M",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "D"
      ]
    },
    {
      "points": [
        "S",
        "A'"
      ]
    },
    {
      "points": [
        "S",
        "T'"
      ]
    },
    {
      "points": [
        "L0",
        "L1"
      ]
    },
    {
      "points": [
        "L1",
        "L2"
      ]
    },
    {
      "points": [
        "L2",
        "L3"
      ]
    },
    {
      "points": [
        "L3",
        "L4"
      ]
    },
    {
      "points": [
        "L4",
        "L5"
      ]
    },
    {
      "points": [
        "L5",
        "L6"
      ]
    },
    {
      "points": [
        "L6",
        "L7"
      ]
    },
    {
      "points": [
        "L7",
        "L8"
      ]
    },
    {
      "points": [
        "L8",
        "L9"
      ]
    },
    {
      "points": [
        "L9",
        "L10"
      ]
    },
    {
      "points": [
        "L10",
        "L11"
      ]
    },
    {
      "points": [
        "L11",
        "L12"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "B",
        "M"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "D",
        "B"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R0",
        "R1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R1",
        "R2"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R2",
        "R3"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R3",
        "R4"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R4",
        "R5"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R5",
        "R6"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R6",
        "R7"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R7",
        "R8"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R8",
        "R9"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R9",
        "R10"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R10",
        "R11"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "R11",
        "R12"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "C",
        "A'",
        "As",
        "Cs"
      ],
      "color": "#d4d4d4",
      "opacity": 0.5
    },
    {
      "points": [
        "Oc",
        "T'",
        "Ts",
        "Os"
      ],
      "color": "#d4d4d4",
      "opacity": 0.5
    },
    {
      "points": [
        "Oc",
        "Ov",
        "Ovs",
        "Os"
      ],
      "color": "#737373",
      "opacity": 0.6
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 随机几何参数
4:     R = rng.uniform(0.9, 1.3)
5:     r = R * rng.uniform(0.35, 0.55)

9:     sz = rng.uniform(2.2, 3.2)
10: 
11:     # 球心 cx:与圆台右侧母线相切且落地
12:     dx = math.sqrt(h * h + (R - r) * (R - r))
13:     cx = (h * R + rs * dx - (R - r) * rs) / h
14: 
15:     # A'(SA 延长交地面 z=0)
16:     Apx = (r * sz - sx * h) / (sz - h)
17: 
18:     # 球的上切点 T(从 S 出发的两条切线中取 z 较高者)
19:     sox, soz = cx - sx, rs - sz
20:     d2 = sox * sox + soz * soz
21:     d = math.sqrt(d2)
22:     l = math.sqrt(max(0.0, d2 - rs * rs))
46:     V("M", 0.0, 0.0)
47: 
48:     # 圆台轴截面梯形顶点
49:     V("B", -R, 0.0)
50:     V("C", R, 0.0)
51:     V("D", -r, h)
52: 
69:     ax = [tk.aux("B", "M"), tk.aux("D", "B")]
70: 
71:     # 球大圆:左半边(面向光源)实线,右半边(背向光源)虚线
72:     for i in range(12):
73:         a1 = math.pi / 2 + i * math.pi / 12
74:         a2 = a1 + math.pi / 12
75:         n1, n2 = f"L{i}", f"L{i+1}"
91:     ]
92: 
93:     spec = tk.make_spec("中心投影共线性质", "S019", vs, edges=es, aux_lines=ax, aux_faces=fa,
94:                         labels=["S", "A", "A'", "T", "T'", "O", "M"], camera=tk.flat_camera(vs))
95: 
96:     opts = {
97:         "A": "S、T、T' 三点共线,且切点 T 决定球体影子的边界",
98:         "B": "S、圆台下底中心 M 与 A' 三点共线,由此可确定圆台影子的边界",
99:         "C": "S、球心 O 与 T' 三点共线,因此球心决定球影的边界",
100:         "D": "两立体相邻接触点与 S 的连线即为球影的边界",
101:     }
102:     q = "如图,正圆台与球体相邻放置于水平地面,点光源 S 位于圆台上方。图中标出了圆台上底边缘点 A 及其地面影子 A',以及球面上一点 T 及其地面影子 T'。根据中心投影共线性质,下列判断正确的是?"
103:     return tk.item(spec, q, opts, "A")

K26 Spec-to-Image Review

  • major/frustum_render
    圆台仅由轴截面梯形硬画,无上下底椭圆,未使用frustum primitive
    spec_fix: 使用frustum primitive或补全上下底椭圆与母线,移除纯线段逼近
  • major/sphere_render
    球体由24条线段离散画圆,未使用sphere primitive,无球面轮廓
    spec_fix: 使用sphere primitive,仅保留必要大圆/截面圆作为辅助线
  • major/plan_coverage
    全图呈二维轴截面,无三维地面、圆台底面及球体空间轮廓
    spec_fix: 按三维直观图重构,添加水平地面透视与立体轮廓
  • minor/label_overlap
    A'与T'间距过小,O旁出现未定义θ标记
    spec_fix: 拉开A'、T'标签间距,删除或修正θ乱码
  • minor/camera_or_view
    三视角均为侧视截面,无俯视或斜二测辅助视角
    spec_fix: 增加俯视或斜二测视角展示地面并置关系
What To Change
  • 引入sphere与frustum primitive
  • 补全圆台上下底椭圆与可见母线
  • 将场景扩展为三维直观图
Preferred Primitives
  • sphere
  • frustum
Remove/Simplify
  • 删除L0-L12/R0-R12线段逼近的球大圆
  • 删除纯梯形edges硬画
Label Fixes
  • 拉开A'与T'间距
  • 移除O旁θ乱码
  • S标签避免贴线
Camera Fixes
  • 改用斜二测或带透视的俯视角度
Toolkit/Renderer: 若toolkit无frustum/sphere primitive则需补充;renderer需支持球与圆台的光滑渲染
representation: 完全未使用几何primitive,圆台球均以线段硬画,且仅呈现轴截面而非三维体
readability: 三视图几乎相同,均为侧视截面,缺乏三维空间感,但共线关系尚可辨认
plan: 缺失圆台上下底面、球体空间轮廓及三维地面,未达成三维立体题图要求
next: spec_rewrite

center_proj_collinearity__S001

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 5 major_spec_issue toolkit hold

Plan / Must Render

题型 中心投影共线性质
难度 L1
Scene
在带轻微透视的斜二测直观图中,以水平网格地面为投影面。画面中央放置一根倾斜的圆柱细杆AB:A为上端点,B为下端点且B落在地面上。点光源S位于杆的左前上方,用黄色球体高亮。杆顶A在地面上的影子端点为A′,用红色高亮。从S经过A到A′画一条红色虚线作为辅助投影线,并继续向地面延伸。杆身AB用黑色实线,影子线段BA′用蓝色粗实线贴地画出。在杆右侧另设一干扰点C(绿色),其地面干扰点C′(绿色)故意不与S、C共线,以形成视觉对比。
Must Render
[
  "黄色高亮的点光源S",
  "倾斜细杆AB及接地点B",
  "红色高亮的影子端点A′及地面影子BA′",
  "红色虚线辅助投影线S-A-A′",
  "绿色干扰点C及其不共线的地面点C′"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "斜二测直观图中的中心投影",
  "camera": {
    "position": [
      15.309,
      11.36,
      15.019
    ],
    "target": [
      0.32,
      0.09,
      0.031
    ],
    "orthoSize": 7.121
  },
  "counts": {
    "vertices": 53,
    "faces": 0,
    "edges": 2,
    "aux_lines": 28,
    "aux_faces": 0,
    "primitives": 4,
    "labels": 8
  },
  "primitive_types": [
    "sphere",
    "sphere",
    "sphere",
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.18,
      "position": [
        -1.9410832497070367,
        2.5112747213686086,
        1.2049341374504143
      ],
      "color": "#fbbf24"
    },
    {
      "type": "sphere",
      "radius": 0.14,
      "position": [
        4.188772941261515,
        0.0,
        -0.8838591595830394
      ],
      "color": "#ef4444"
    },
    {
      "type": "sphere",
      "radius": 0.13,
      "position": [
        1.627038871227818,
        0.7819876356473564,
        -0.01872243667811535
      ],
      "color": "#22c55e"
    },
    {
      "type": "sphere",
      "radius": 0.13,
      "position": [
        4.290581203331372,
        0.0,
        0.2819936617117451
      ],
      "color": "#22c55e"
    }
  ],
  "labels": [
    {
      "point": "{'point': 'S', 'text': 'S'}",
      "text": "{'point': 'S', 'text': 'S'}"
    },
    {
      "point": "{'point': 'A', 'text': 'A'}",
      "text": "{'point': 'A', 'text': 'A'}"
    },
    {
      "point": "{'point': 'B', 'text': 'B'}",
      "text": "{'point': 'B', 'text': 'B'}"
    },
    {
      "point": "{'point': 'Ap', 'text': 'A′'}",
      "text": "{'point': 'Ap', 'text': 'A′'}"
    },
    {
      "point": "{'point': 'C', 'text': 'C'}",
      "text": "{'point': 'C', 'text': 'C'}"
    },
    {
      "point": "{'point': 'Cp', 'text': 'C′'}",
      "text": "{'point': 'Cp', 'text': 'C′'}"
    },
    {
      "point": "{'point': 'x', 'text': 'x'}",
      "text": "{'point': 'x', 'text': 'x'}"
    },
    {
      "point": "{'point': 'z', 'text': 'z'}",
      "text": "{'point': 'z', 'text': 'z'}"
    }
  ],
  "vertex_names": [
    "S",
    "A",
    "B",
    "Ap",
    "C",
    "Cp",
    "E",
    "g-5a",
    "g-5b",
    "h-5a",
    "h-5b",
    "g-4a",
    "g-4b",
    "h-4a",
    "h-4b",
    "g-3a",
    "g-3b",
    "h-3a",
    "h-3b",
    "g-2a",
    "g-2b",
    "h-2a",
    "h-2b",
    "g-1a",
    "g-1b",
    "h-1a",
    "h-1b",
    "g0a",
    "g0b",
    "h0a",
    "h0b",
    "g1a",
    "g1b",
    "h1a",
    "h1b",
    "g2a",
    "g2b",
    "h2a",
    "h2b",
    "g3a",
    "g3b",
    "h3a",
    "h3b",
    "g4a",
    "g4b",
    "h4a",
    "h4b",
    "g5a",
    "g5b",
    "h5a",
    "h5b",
    "x",
    "z"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "color": "#000000"
    },
    {
      "points": [
        "B",
        "Ap"
      ],
      "color": "#2563eb"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "g-5a",
        "g-5b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h-5a",
        "h-5b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g-4a",
        "g-4b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h-4a",
        "h-4b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g-3a",
        "g-3b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h-3a",
        "h-3b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g-2a",
        "g-2b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h-2a",
        "h-2b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g-1a",
        "g-1b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h-1a",
        "h-1b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g0a",
        "g0b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h0a",
        "h0b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g1a",
        "g1b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h1a",
        "h1b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g2a",
        "g2b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h2a",
        "h2b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g3a",
        "g3b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h3a",
        "h3b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g4a",
        "g4b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h4a",
        "h4b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "g5a",
        "g5b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "h5a",
        "h5b"
      ],
      "dash": false,
      "color": "#e5e7eb"
    },
    {
      "points": [
        "B",
        "x"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "B",
        "z"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "S",
        "A"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "A",
        "Ap"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "Ap",
        "E"
      ],
      "dash": true,
      "color": "#dc2626"
    },
    {
      "points": [
        "C",
        "Cp"
      ],
      "dash": true,
      "color": "#22c55e"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     # 随机构造斜二测风格3D场景
3:     B = (0.0, 0.0, 0.0)
4:     A = (rng.uniform(0.3, 0.7), rng.uniform(1.0, 1.6), rng.uniform(0.2, 0.5))
5:     S = (rng.uniform(-2.2, -1.2), rng.uniform(2.0, 3.0), rng.uniform(0.8, 1.8))

8:     t = S[1] / (S[1] - A[1])
9:     Ap = (S[0] + t * (A[0] - S[0]), 0.0, S[2] + t * (A[2] - S[2]))
10:     # 向地面外延伸一点,强化投影线视觉效果
11:     E = (Ap[0] + 0.5 * (Ap[0] - A[0]), 0.0, Ap[2] + 0.5 * (Ap[2] - A[2]))
12: 
13:     # 干扰点 C(空中)与 C′(地面),故意不与 S,C 共线
14:     C = (rng.uniform(1.0, 1.8), rng.uniform(0.6, 1.2), rng.uniform(-0.4, 0.4))
20:     vs = {"S": S, "A": A, "B": B, "Ap": Ap, "C": C, "Cp": Cp, "E": E}
21: 
22:     aux_lines = []
23:     # 地面网格(浅灰),暗示水平投影面
24:     for i in range(-5, 6):
25:         z = i * 0.5
26:         vs[f"g{i}a"] = (-2.5, 0.0, z)
27:         vs[f"g{i}b"] = (2.5, 0.0, z)
28:         aux_lines.append(tk.aux(f"g{i}a", f"g{i}b", dash=False, color="#e5e7eb"))
29:         x = i * 0.5
30:         vs[f"h{i}a"] = (x, 0.0, -2.5)
31:         vs[f"h{i}b"] = (x, 0.0, 2.5)
32:         aux_lines.append(tk.aux(f"h{i}a", f"h{i}b", dash=False, color="#e5e7eb"))
33: 
34:     # 斜二测坐标轴实线标注
35:     vs["x"] = (2.2, 0.0, 0.0)
36:     vs["z"] = (0.0, 0.0, 2.2)
37:     aux_lines.append(tk.aux("B", "x", dash=False, color="#374151"))
38:     aux_lines.append(tk.aux("B", "z", dash=False, color="#374151"))
39: 
40:     # 主体线条:杆身 AB(黑色实线)、影子 BA′(蓝色实线)
41:     edges = [
42:         tk.edge("A", "B", dash=False, color="#000000"),
43:         tk.edge("B", "Ap", dash=False, color="#2563eb"),
44:     ]
45: 
46:     # 辅助投影线 S-A-A′(红色虚线)及向地面延伸;干扰线 C-C′(绿色虚线)
47:     aux_lines += [
48:         tk.aux("S", "A", dash=True, color="#dc2626"),
49:         tk.aux("A", "Ap", dash=True, color="#dc2626"),
50:         tk.aux("Ap", "E", dash=True, color="#dc2626"),
51:         tk.aux("C", "Cp", dash=True, color="#22c55e"),
52:     ]
53: 
54:     # 高亮球体:光源(黄)、影子端点(红)、干扰点(绿)
55:     primitives = [
56:         tk.prim_sphere(r=0.18, center=vs["S"], color="#fbbf24"),
57:         tk.prim_sphere(r=0.14, center=vs["Ap"], color="#ef4444"),
58:         tk.prim_sphere(r=0.13, center=vs["C"], color="#22c55e"),
59:         tk.prim_sphere(r=0.13, center=vs["Cp"], color="#22c55e"),
60:     ]
61: 
62:     camera = tk.auto_camera(vs, azim=45.0, elev=28.0, pad=1.6)
63: 
64:     # 手动指定标签,确保 A′、C′ 在图上正确显示
65:     labels = [
66:         {"point": "S", "text": "S"},
67:         {"point": "A", "text": "A"},
68:         {"point": "B", "text": "B"},
69:         {"point": "Ap", "text": "A′"},
74:     ]
75: 
76:     spec = tk.make_spec(
77:         "中心投影共线性质",
78:         "斜二测直观图中的中心投影",
79:         vs,
80:         edges=edges,
81:         aux_lines=aux_lines,
82:         primitives=primitives,
83:         labels=labels,
84:         camera=camera,
85:     )
86: 
87:     question = "如图,点光源S照射倾斜细杆AB,B为杆与地面的接触点,A′为杆顶A在地面上的影子端点,红色虚线为辅助投影线。根据中心投影共线性质,下列说法正确的是(  )"
88:     options = {
89:         "A": "点光源S、杆顶A与影子端点A′三点共线",
90:         "B": "点光源S、杆底B与影子端点A′三点共线",
91:         "C": "杆身AB与其影子BA′共线即可,无需经过光源S",
92:         "D": "点光源S与杆身两端点A、B共线即构成投影关系",
93:     }
94:     return tk.item(
95:         spec,
96:         question,
98:         "A",
99:         answer_text="点光源S、杆顶A与影子端点A′三点共线",
100:         meta={"diversity_tag": "中心投影-共线判定"},
101:     )

K26 Spec-to-Image Review

  • major/sphere_render
    所有sphere primitive渲染为蓝灰色,光源S未呈黄色,影子端点A′未呈红色,干扰点C/C′未呈绿色,高亮语义完全丢失
    spec_fix: 修复renderer对sphere primitive color字段的支持,确保漫反射材质正确应用#fbbf24、#ef4444、#22c55e
  • minor/label_overlap
    S标签紧贴球体下缘,A′标签与球体重叠,阅读困难
    spec_fix: 调整S标签向右上偏移,A′标签向左上偏移,避开球体遮挡
  • minor/other
    杆顶A为投影线关键节点,但无任何primitive或醒目点标记,仅作为线段端点
    spec_fix: 给A点添加黑色小sphere(r≈0.08)或显著点标记,与B点同级
What To Change
  • 修复sphere颜色渲染bug
  • 给A点添加黑色小标记球
  • 微调S/A′/C/C′标签偏移避球
Preferred Primitives
  • sphere
Remove/Simplify
  • 无需简化
Label Fixes
  • S标签向右上偏移0.2单位
  • A′标签向左上偏移0.15单位
Camera Fixes
  • none
Toolkit/Renderer: 修复renderer对sphere primitive的color属性支持,当前线颜色正确而球颜色错误,属材质/着色器问题
representation: 几何关系与投影线表达正确,但sphere颜色未按spec渲染导致高亮语义丢失;细杆用edge表示在教材图中可接受
readability: 单视角能看清S-A-A′共线与影子关系,但颜色错误使光源/影子/干扰点无法通过颜色快速区分
plan: 所有must_render元素均已出现,但高亮颜色未兑现,影响中心投影共线性质的视觉教学效果
next: toolkit_patch

center_proj_collinearity__S020

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
水平地面画为平行四边形α,其上斜立一根空间折杆P-Q-R(P在地面上,Q为空间拐点,R为顶端,被遮挡的PQ下段画虚线)。右上方设真实点光源S,用实线连接S-P、S-Q、S-R并延长交地面于影点Q′、R′(P的影子为自身)。折杆左侧另立短杆T-U,其顶端U的“影子”U′故意偏离SU延长线。图中还用虚线小圆点标注干扰光源S′,使S′、Q、Q′看似共线。地面边界、投影线、杆件均用不同线型区分。
Must Render
[
  "水平地面及其边界平行四边形",
  "斜立折杆P-Q-R及被遮挡段的虚线",
  "真实点光源S与折杆各顶点的投影线SP、SQ、SR",
  "地面影点Q′、R′的精确位置",
  "左侧短杆T-U及偏离SU延长线的影点U′",
  "干扰光源点S′(虚线小圆点)"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "中心投影共线判断",
  "camera": {
    "position": [
      29.255,
      31.332,
      55.012
    ],
    "target": [
      -1.233,
      1.379,
      0.025
    ],
    "orthoSize": 18.822
  },
  "counts": {
    "vertices": 20,
    "faces": 1,
    "edges": 13,
    "aux_lines": 3,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 14
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Q",
      "text": "Q"
    },
    {
      "point": "R",
      "text": "R"
    },
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "U",
      "text": "U"
    },
    {
      "point": "Q'",
      "text": "Q'"
    },
    {
      "point": "R'",
      "text": "R'"
    },
    {
      "point": "U'",
      "text": "U'"
    },
    {
      "point": "S'",
      "text": "S'"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "Q'",
    "R'",
    "U'",
    "S'",
    "Sx1",
    "Sx2",
    "Sy1",
    "Sy2",
    "Sz1",
    "Sz2"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C",
        "D"
      ],
      "color": "#f5f5f4",
      "opacity": 0.25
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "Q"
      ],
      "dash": true
    },
    {
      "points": [
        "Q",
        "R"
      ]
    },
    {
      "points": [
        "T",
        "U"
      ]
    },
    {
      "points": [
        "S",
        "P"
      ]
    },
    {
      "points": [
        "S",
        "Q"
      ]
    },
    {
      "points": [
        "S",
        "R"
      ]
    },
    {
      "points": [
        "Q",
        "Q'"
      ]
    },
    {
      "points": [
        "R",
        "R'"
      ]
    },
    {
      "points": [
        "S",
        "U"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "Sx1",
        "Sx2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "Sy1",
        "Sy2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "Sz1",
        "Sz2"
      ],
      "dash": true,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     w = rng.uniform(2.2, 3.0)
4:     d = rng.uniform(1.6, 2.4)
5:     A = (-w, 0.0, d); B = (w, 0.0, d); C = (w, 0.0, -d); D = (-w, 0.0, -d)

32:                        ("Sz1", (Sp[0], Sp[1], Sp[2]-r)), ("Sz2", (Sp[0], Sp[1], Sp[2]+r))]:
33:         vs[tag] = coord
34:     faces = [{"points": ["A", "B", "C", "D"], "color": "#f5f5f4", "opacity": 0.25}]
35:     edges = [tk.edge("A", "B"), tk.edge("B", "C"), tk.edge("C", "D"), tk.edge("D", "A"),
36:              tk.edge("P", "Q", dash=True), tk.edge("Q", "R"), tk.edge("T", "U"),
37:              tk.edge("S", "P"), tk.edge("S", "Q"), tk.edge("S", "R"),
38:              tk.edge("Q", "Q'"), tk.edge("R", "R'"), tk.edge("S", "U")]
39:     auxs = [tk.aux("Sx1", "Sx2", dash=True, color="#888888"),
40:             tk.aux("Sy1", "Sy2", dash=True, color="#888888"),
41:             tk.aux("Sz1", "Sz2", dash=True, color="#888888")]
42:     cam = tk.auto_camera(vs, azim=rng.uniform(28, 38), elev=rng.uniform(22, 30))
43:     spec = tk.make_spec("中心投影共线性质", "中心投影共线判断", vs, faces=faces, edges=edges,
44:                         aux_lines=auxs, labels=["A","B","C","D","P","Q","R","S","T","U","Q'","R'","U'","S'"], camera=cam)
45:     opts = {"A": "S′、Q、Q′", "B": "S、U、U′", "C": "S、R、R′", "D": "S、Q、T"}
46:     return tk.item(spec, "如图,空间中有真实点光源S及一个干扰点S′,折杆P-Q-R与短杆T-U均立于地面。根据中心投影共线性质,以下哪一组点满足“点光源、物体边缘点及其影子对应点”严格共线?", opts, "C")

K26 Spec-to-Image Review

  • major/aux_line_clutter
    光源S周围有三条灰色虚线坐标轴(Sx/Sy/Sz),plan未要求,严重干扰投影线阅读
    spec_fix: 删除aux_lines中的Sx1-Sx2、Sy1-Sy2、Sz1-Sz2三条辅助线
  • major/camera_or_view
    R'影子极左而主体挤在右侧,图面水平跨度大、中间空旷失衡
    spec_fix: 调整几何参数(降低杆高或光源高度)使影子内收,并重新计算orthoSize与camera target
  • minor/label_overlap
    右侧Q、S'、P、T、U、Q'标签密集重叠,A与Q'贴线
    spec_fix: 为各label设置偏移量,避免与顶点、边线或其他label重叠
  • minor/plan_coverage
    U'是短杆影子但spec缺少U-U'连线,与Q-Q'、R-R'不对称,影子关系不明确
    spec_fix: 添加虚线edge(U, U')以明确影子对应关系,并确保U'明显偏离S-U延长线
  • minor/other
    投影线、杆件、地面边界均为实线,未按plan要求用不同线型区分
    spec_fix: 将投影线SP/SQ/SR/SU改为虚线或细线,与地面和杆件实线区分
What To Change
  • 移除S周围三条坐标轴aux_lines
  • 添加U-U'影子连线并确保偏离SU延长线
  • 调整几何或camera使R'与主体间距合理
  • 区分配线线型(投影线/杆件/地面)
Preferred Primitives
none
Remove/Simplify
  • 删除Sx1/Sx2/Sy1/Sy2/Sz1/Sz2辅助线
  • 简化右侧重叠标签
Label Fixes
  • 为Q'、S'、U'添加偏移避免与顶点/地面边重叠
  • 检查A与Q'间距
Camera Fixes
  • 缩小orthoSize至约12-15,或调整target使构图居中
  • 或降低折杆高度使影子内收
Toolkit/Renderer: none
representation: 基本几何元素齐全,但多余坐标轴aux_lines和失衡构图破坏教材图风格
readability: 单视角下右侧极度拥挤、左侧空旷,标签重叠,可读性较差
plan: 必须元素均已出现,但U'的影子关系表达不完整,线型区分未落实
next: spec_rewrite

center_proj_collinearity__S016

三维立体 › 三视图与直观图 › 中心投影与平行投影 › 中心投影 › 中心投影共线性质
STAGE-1 FAIL score 6 major_spec_issue minor hold

Plan / Must Render

题型 中心投影共线性质
难度 L3
Scene
空间中两条平行等长斜杆 AB、CD 悬于带透视边框的水平矩形地面 π 上方,AB 在左前、CD 在右后,均向右上倾斜。点光源 S 位于 AB 左上方。AB 在 π 上的中心投影为 A'B',用实线连接 S-A-A' 与 S-B-B' 以展示共线。CD 的影点 C'D' 按平行投影绘制,竖直线段 CC'、DD' 均垂直于 π,形成与 A'B' 相似的影子构型。S 与 AB 之间放置一透明球,球心为 O,切线 SA 切球于点 T,半径 OT 垂直于 SA。所有隐藏边及地面后侧用虚线。
Must Render
[
  "斜杆 AB 及其中心投影 A'B',以及实线投影线 SA、SB 并延长至地面 A'、B'",
  "平行斜杆 CD 及其垂直于地面的平行投影 C'D'(CC'、DD' 为竖直线段)",
  "辅助透明球、球心 O、切点 T 及半径 OT",
  "水平投影面 π 的透视矩形边框与虚线隐藏边"
]

Spec Summary

{
  "title": "中心投影共线性质",
  "meta": "中心投影共线示意图",
  "camera": {
    "position": [
      17.588,
      14.704,
      23.393
    ],
    "target": [
      -0.078,
      1.577,
      0.612
    ],
    "orthoSize": 8.454
  },
  "counts": {
    "vertices": 15,
    "faces": 1,
    "edges": 10,
    "aux_lines": 5,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 11
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.7638985023872961,
      "position": [
        -2.623921194274687,
        3.215082773775481,
        1.6727792889380935
      ]
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "A'",
      "text": "A'"
    },
    {
      "point": "B'",
      "text": "B'"
    },
    {
      "point": "C'",
      "text": "C'"
    },
    {
      "point": "D'",
      "text": "D'"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    }
  ],
  "vertex_names": [
    "S",
    "A",
    "B",
    "C",
    "D",
    "A'",
    "B'",
    "C'",
    "D'",
    "O",
    "T",
    "G0",
    "G1",
    "G2",
    "G3"
  ],
  "faces_sample": [
    {
      "points": [
        "G0",
        "G1",
        "G2",
        "G3"
      ],
      "color": "#e8e8e6",
      "opacity": 0.25
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "B"
      ]
    },
    {
      "points": [
        "A",
        "A'"
      ]
    },
    {
      "points": [
        "B",
        "B'"
      ]
    },
    {
      "points": [
        "A'",
        "B'"
      ]
    },
    {
      "points": [
        "G0",
        "G1"
      ]
    },
    {
      "points": [
        "G1",
        "G2"
      ]
    },
    {
      "points": [
        "G3",
        "G0"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "C",
        "C'"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "D",
        "D'"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "C'",
        "D'"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "G2",
        "G3"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "T"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 随机几何参数 ----
5:     S = (rng.uniform(-3.0, -1.5), rng.uniform(3.2, 4.5), rng.uniform(1.0, 2.2))

19:     D = (C[0] + dx, C[1] + dy, C[2] + dz)
20: 
21:     # 中心投影到地面 y=0
22:     def cproj(P):
23:         t = -S[1] / (P[1] - S[1])
24:         return (S[0] + t * (P[0] - S[0]), 0.0, S[2] + t * (P[2] - S[2]))
25: 
31:     G0, G1, G2, G3 = (-w, 0.0, d), (w, 0.0, d), (w, 0.0, -d), (-w, 0.0, -d)
32: 
33:     # 透明球:球心 O,切点 T,半径 OT ⟂ SA
34:     SA = tk.vsub(A, S)
35:     t = rng.uniform(0.35, 0.65)
36:     foot = (S[0] + t * SA[0], S[1] + t * SA[1], S[2] + t * SA[2])
37:     perp = tk.cross(SA, (0, 0, 1))
50:           "G0": G0, "G1": G1, "G2": G2, "G3": G3}
51: 
52:     edges = [
53:         tk.edge("A", "B"), tk.edge("C", "D"),
54:         tk.edge("S", "A"), tk.edge("S", "B"),
55:         tk.edge("A", "A'"), tk.edge("B", "B'"), tk.edge("A'", "B'"),
56:         tk.edge("G0", "G1"), tk.edge("G1", "G2"), tk.edge("G3", "G0"),
57:     ]
58:     aux_lines = [
59:         tk.aux("C", "C'", dash=True), tk.aux("D", "D'", dash=True),
60:         tk.aux("C'", "D'", dash=True), tk.aux("G2", "G3", dash=True),
61:         tk.aux("O", "T", dash=True),
62:     ]
63:     faces = [tk.face(["G0", "G1", "G2", "G3"], color="#e8e8e6", opacity=0.25)]
64:     primitives = [tk.prim_sphere(r=r, center=O)]
65: 
66:     spec = tk.make_spec(
67:         "中心投影共线性质",
68:         "中心投影共线示意图",
69:         vs,
70:         faces=faces,
71:         edges=edges,
72:         aux_lines=aux_lines,
73:         primitives=primitives,
74:         labels=["S", "A", "B", "C", "D", "A'", "B'", "C'", "D'", "O", "T"],
75:         camera=tk.auto_camera(vs, primitives, azim=rng.uniform(26, 38), elev=rng.uniform(18, 26)),
76:     )
77: 
78:     # ---- 选项:固定与 plan 一致,正确答案为 S、B、B',干扰项含 A、B、B' ----
79:     corr = "S、B、B'"
84:     options = {"A": opts[0], "B": opts[1], "C": opts[2], "D": opts[3]}
85: 
86:     q = "如图,点光源 S 照射两条平行斜杆 AB、CD,它们在水平地面 π 上的影子分别为 A'B'、C'D'。根据中心投影共线性质,以下哪组三点一定共线?"
87:     return tk.item(spec, q, options, ans)

K26 Spec-to-Image Review

  • major/angle_arc
    半径 OT 与切线 SA 垂直关系未标注,无直角符号或 angle_arc,仅一条虚线 OT
    spec_fix: 在 T 点处用两段短 aux_lines 绘制直角标记,或调用 angle_arc primitive 明示 OT⊥SA
  • minor/label_overlap
    T 标签紧贴球轮廓与 SA 线,O 标签在球内偏小且被纬线干扰
    spec_fix: 将 T 标签向球外偏移避开切线,O 标签上移至球顶空白处并放大字号
  • minor/aux_line_clutter
    CC'、DD'、C'D' 与隐藏边 G2G3 均使用橙红色虚线,色彩突兀且易混淆
    spec_fix: 将投影辅助线改为灰色或黑色虚线,与地面隐藏边色彩区分
  • minor/plan_coverage
    S-A-A' 与 S-B-B' 在 spec 中分为两段边绘制,共线特征在印刷尺度下不够直观
    spec_fix: 保留顶点 A、B 圆点的同时,将 S-A' 与 S-B' 作为整条实线边加入 edges 以强化共线
What To Change
  • 在 T 点添加直角标记
  • 将 S-A'、S-B' 作为整条实线边加入
  • 调整 T、O 标签位置
  • 统一辅助线颜色为灰色
Preferred Primitives
  • angle_arc
Remove/Simplify
Label Fixes
  • T 向球外偏移
  • O 上移并放大
Camera Fixes
Toolkit/Renderer: none
representation: 球体使用 sphere primitive 合适,但关键垂直关系缺少角度标记;投影线分段绘制虽数学共线,视觉上可强化为整条线
readability: 单视角下各元素可辨,但橙红色辅助线略突兀,T/O 标签可读性一般
plan: must_render 元素基本齐全,但 OT⊥SA 的垂直特征未显性表达,共线关系可更直观
next: visual_repair

cross_section_sphere__S001

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
绘制一个透明球体,球心标记为 O,球面上用两条互相垂直的虚线大圆表现球体。用一个半透明浅蓝色平行四边形表示平面 α,从球的左上方斜向切入,不经过球心。平面与球面的交线画为封闭实线圆(截面圆),其圆心标记为 H。作辅助虚线段 OH 并标注 OH⊥α;在截面圆上取点 A,连接实线 OA(球半径)与 HA(截面圆半径),形成直角三角形 OHA。球的隐藏轮廓边及被平面遮挡的球面部分均用虚线表示。
Must Render
[
  "球体及球心 O",
  "斜切平面 α 与交线(截面圆)",
  "垂线段 OH(球心到截面距离)及垂足 H",
  "直角三角形 OHA(球半径、截面圆半径)"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "球截面形状判断·S001",
  "camera": {
    "position": [
      12.959,
      11.912,
      17.959
    ],
    "target": [
      0.653,
      0.503,
      0.383
    ],
    "orthoSize": 7.212
  },
  "counts": {
    "vertices": 9,
    "faces": 0,
    "edges": 34,
    "aux_lines": 65,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 5
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.1377687406100194,
      "position": [
        0,
        0,
        0
      ],
      "color": "#e8f4fc"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "α",
      "text": "α"
    },
    {
      "point": "P",
      "text": "OH⊥α"
    }
  ],
  "vertex_names": [
    "O",
    "H",
    "A",
    "α",
    "P",
    "a0",
    "a1",
    "a2",
    "a3"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "O",
        "A"
      ]
    },
    {
      "points": [
        "H",
        "A"
      ]
    },
    {
      "points": [
        [
          2.0492182473233753,
          -0.20521134658862472,
          0.5732783498334978
        ],
        [
          2.117870305783739,
          -0.08039126706085908,
          0.2796744542847703
        ]
      ]
    },
    {
      "points": [
        [
          2.117870305783739,
          -0.08039126706085908,
          0.2796744542847703
        ],
        [
          2.136183797304244,
          0.08226158871610989,
          -0.0026464082129812327
        ]
      ]
    },
    {
      "points": [
        [
          2.136183797304244,
          0.08226158871610989,
          -0.0026464082129812327
        ],
        [
          2.103454944676081,
          0.27649656271154566,
          -0.26283480524133185
        ]
      ]
    },
    {
      "points": [
        [
          2.103454944676081,
          0.27649656271154566,
          -0.26283480524133185
        ],
        [
          2.020941499351198,
          0.4948493138030323,
          -0.49089184261781627
        ]
      ]
    },
    {
      "points": [
        [
          2.020941499351198,
          0.4948493138030323,
          -0.49089184261781627
        ],
        [
          1.8918144067593579,
          0.728928668219757,
          -0.678053416291916
        ]
      ]
    },
    {
      "points": [
        [
          1.8918144067593579,
          0.728928668219757,
          -0.678053416291916
        ],
        [
          1.7210359486535567,
          0.9697390876446995,
          -0.8171270119487551
        ]
      ]
    },
    {
      "points": [
        [
          1.7210359486535567,
          0.9697390876446995,
          -0.8171270119487551
        ],
        [
          1.5151690454051376,
          1.2080263627073986,
          -0.9027681093006127
        ]
      ]
    },
    {
      "points": [
        [
          1.5151690454051376,
          1.2080263627073986,
          -0.9027681093006127
        ],
        [
          1.2821250466544483,
          1.4346332470602379,
          -0.9316855690043372
        ]
      ]
    },
    {
      "points": [
        [
          1.2821250466544483,
          1.4346332470602379,
          -0.9316855690043372
        ],
        [
          1.0308597025808974,
          1.6408513652201429,
          -0.9027681093006127
        ]
      ]
    },
    {
      "points": [
        [
          1.0308597025808974,
          1.6408513652201429,
          -0.9027681093006127
        ],
        [
          0.7710289994460031,
          1.8187558705546811,
          -0.8171270119487551
        ]
      ]
    },
    {
      "points": [
        [
          0.7710289994460031,
          1.8187558705546811,
          -0.8171270119487551
        ],
        [
          0.512618085456503,
          1.9615099926938424,
          -0.6780534162919162
        ]
      ]
    },
    {
      "points": [
        [
          0.512618085456503,
          1.9615099926938424,
          -0.6780534162919162
        ],
        [
          0.26555754711850177,
          2.0636277707812534,
          -0.49089184261781627
        ]
      ]
    },
    {
      "points": [
        [
          0.26555754711850177,
          2.0636277707812534,
          -0.49089184261781627
        ],
        [
          0.03934178236718411,
          2.1211848758733236,
          -0.26283480524133185
        ]
      ]
    },
    {
      "points": [
        [
          0.03934178236718411,
          2.1211848758733236,
          -0.26283480524133185
        ],
        [
          -0.15733586382131926,
          2.1319694206997615,
          -0.002646408212981455
        ]
      ]
    },
    {
      "points": [
        [
          -0.15733586382131926,
          2.1319694206997615,
          -0.002646408212981455
        ],
        [
          -0.3169171798020797,
          2.09556696125093,
          0.2796744542847698
        ]
      ]
    },
    {
      "points": [
        [
          -0.3169171798020797,
          2.09556696125093,
          0.2796744542847698
        ],
        [
          -0.43326954509618887,
          2.013376423628712,
          0.5732783498334976
        ]
      ]
    },
    {
      "points": [
        [
          -0.43326954509618887,
          2.013376423628712,
          0.5732783498334976
        ],
        [
          -0.5019216035565531,
          1.8885563441009463,
          0.8668822453822255
        ]
      ]
    },
    {
      "points": [
        [
          -0.5019216035565531,
          1.8885563441009463,
          0.8668822453822255
        ],
        [
          -0.5202350950770577,
          1.7259034883239774,
          1.1492031078799767
        ]
      ]
    },
    {
      "points": [
        [
          -0.5202350950770577,
          1.7259034883239774,
          1.1492031078799767
        ],
        [
          -0.4875062424488944,
          1.531668514328542,
          1.409391504908327
        ]
      ]
    },
    {
      "points": [
        [
          -0.4875062424488944,
          1.531668514328542,
          1.409391504908327
        ],
        [
          -0.4049927971240118,
          1.3133157632370553,
          1.6374485422848117
        ]
      ]
    },
    {
      "points": [
        [
          -0.4049927971240118,
          1.3133157632370553,
          1.6374485422848117
        ],
        [
          -0.2758657045321714,
          1.0792364088203303,
          1.8246101159589116
        ]
      ]
    },
    {
      "points": [
        [
          -0.2758657045321714,
          1.0792364088203303,
          1.8246101159589116
        ],
        [
          -0.10508724642637068,
          0.8384259893953885,
          1.9636837116157504
        ]
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        [
          2.1377687406100194,
          0.0,
          0.0
        ],
        [
          2.0966921136964585,
          0.41705799200162164,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          2.0966921136964585,
          0.41705799200162164,
          0.0
        ],
        [
          1.975040784692027,
          0.8180886792594375,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.975040784692027,
          0.8180886792594375,
          0.0
        ],
        [
          1.7774897459475132,
          1.18768067736273,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.7774897459475132,
          1.18768067736273,
          0.0
        ],
        [
          1.5116307730939704,
          1.5116307730939702,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.5116307730939704,
          1.5116307730939702,
          0.0
        ],
        [
          1.1876806773627302,
          1.7774897459475132,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.1876806773627302,
          1.7774897459475132,
          0.0
        ],
        [
          0.8180886792594376,
          1.975040784692027,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          0.8180886792594376,
          1.975040784692027,
          0.0
        ],
        [
          0.4170579920016218,
          2.0966921136964585,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          0.4170579920016218,
          2.0966921136964585,
          0.0
        ],
        [
          1.3090058227526643e-16,
          2.1377687406100194,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.3090058227526643e-16,
          2.1377687406100194,
          0.0
        ],
        [
          -0.4170579920016215,
          2.0966921136964585,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -0.4170579920016215,
          2.0966921136964585,
          0.0
        ],
        [
          -0.8180886792594374,
          1.975040784692027,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -0.8180886792594374,
          1.975040784692027,
          0.0
        ],
        [
          -1.1876806773627295,
          1.7774897459475136,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.1876806773627295,
          1.7774897459475136,
          0.0
        ],
        [
          -1.5116307730939702,
          1.5116307730939704,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.5116307730939702,
          1.5116307730939704,
          0.0
        ],
        [
          -1.7774897459475134,
          1.18768067736273,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.7774897459475134,
          1.18768067736273,
          0.0
        ],
        [
          -1.975040784692027,
          0.8180886792594377,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.975040784692027,
          0.8180886792594377,
          0.0
        ],
        [
          -2.0966921136964585,
          0.4170579920016224,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -2.0966921136964585,
          0.4170579920016224,
          0.0
        ],
        [
          -2.1377687406100194,
          2.6180116455053286e-16,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -2.1377687406100194,
          2.6180116455053286e-16,
          0.0
        ],
        [
          -2.0966921136964585,
          -0.41705799200162186,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -2.0966921136964585,
          -0.41705799200162186,
          0.0
        ],
        [
          -1.9750407846920273,
          -0.8180886792594373,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.9750407846920273,
          -0.8180886792594373,
          0.0
        ],
        [
          -1.7774897459475136,
          -1.1876806773627295,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.7774897459475136,
          -1.1876806773627295,
          0.0
        ],
        [
          -1.5116307730939706,
          -1.5116307730939702,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.5116307730939706,
          -1.5116307730939702,
          0.0
        ],
        [
          -1.18768067736273,
          -1.7774897459475132,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.18768067736273,
          -1.7774897459475132,
          0.0
        ],
        [
          -0.8180886792594387,
          -1.9750407846920264,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -0.8180886792594387,
          -1.9750407846920264,
          0.0
        ],
        [
          -0.4170579920016225,
          -2.0966921136964585,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -0.4170579920016225,
          -2.0966921136964585,
          0.0
        ],
        [
          -3.9270174682579927e-16,
          -2.1377687406100194,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -3.9270174682579927e-16,
          -2.1377687406100194,
          0.0
        ],
        [
          0.41705799200162175,
          -2.0966921136964585,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          0.41705799200162175,
          -2.0966921136964585,
          0.0
        ],
        [
          0.818088679259438,
          -1.9750407846920268,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          0.818088679259438,
          -1.9750407846920268,
          0.0
        ],
        [
          1.1876806773627293,
          -1.7774897459475136,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.1876806773627293,
          -1.7774897459475136,
          0.0
        ],
        [
          1.51163077309397,
          -1.5116307730939706,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.51163077309397,
          -1.5116307730939706,
          0.0
        ],
        [
          1.7774897459475132,
          -1.18768067736273,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.7774897459475132,
          -1.18768067736273,
          0.0
        ],
        [
          1.9750407846920264,
          -0.8180886792594388,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.9750407846920264,
          -0.8180886792594388,
          0.0
        ],
        [
          2.0966921136964585,
          -0.41705799200162263,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          2.0966921136964585,
          -0.41705799200162263,
          0.0
        ],
        [
          2.1377687406100194,
          -5.236023291010657e-16,
          0.0
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          2.1377687406100194,
          0.0,
          0.0
        ],
        [
          2.0966921136964585,
          0.0,
          0.41705799200162164
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          2.0966921136964585,
          0.0,
          0.41705799200162164
        ],
        [
          1.975040784692027,
          0.0,
          0.8180886792594375
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.975040784692027,
          0.0,
          0.8180886792594375
        ],
        [
          1.7774897459475132,
          0.0,
          1.18768067736273
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.7774897459475132,
          0.0,
          1.18768067736273
        ],
        [
          1.5116307730939704,
          0.0,
          1.5116307730939702
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.5116307730939704,
          0.0,
          1.5116307730939702
        ],
        [
          1.1876806773627302,
          0.0,
          1.7774897459475132
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.1876806773627302,
          0.0,
          1.7774897459475132
        ],
        [
          0.8180886792594376,
          0.0,
          1.975040784692027
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          0.8180886792594376,
          0.0,
          1.975040784692027
        ],
        [
          0.4170579920016218,
          0.0,
          2.0966921136964585
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          0.4170579920016218,
          0.0,
          2.0966921136964585
        ],
        [
          1.3090058227526643e-16,
          0.0,
          2.1377687406100194
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          1.3090058227526643e-16,
          0.0,
          2.1377687406100194
        ],
        [
          -0.4170579920016215,
          0.0,
          2.0966921136964585
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -0.4170579920016215,
          0.0,
          2.0966921136964585
        ],
        [
          -0.8180886792594374,
          0.0,
          1.975040784692027
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -0.8180886792594374,
          0.0,
          1.975040784692027
        ],
        [
          -1.1876806773627295,
          0.0,
          1.7774897459475136
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.1876806773627295,
          0.0,
          1.7774897459475136
        ],
        [
          -1.5116307730939702,
          0.0,
          1.5116307730939704
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.5116307730939702,
          0.0,
          1.5116307730939704
        ],
        [
          -1.7774897459475134,
          0.0,
          1.18768067736273
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.7774897459475134,
          0.0,
          1.18768067736273
        ],
        [
          -1.975040784692027,
          0.0,
          0.8180886792594377
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -1.975040784692027,
          0.0,
          0.8180886792594377
        ],
        [
          -2.0966921136964585,
          0.0,
          0.4170579920016224
        ]
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        [
          -2.0966921136964585,
          0.0,
          0.4170579920016224
        ],
        [
          -2.1377687406100194,
          0.0,
          2.6180116455053286e-16
        ]
      ],
      "dash": true,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "a0",
        "a1",
        "a2",
        "a3"
      ],
      "color": "#a5d8ff",
      "opacity": 0.35
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.uniform(1.8, 2.2)
4:     d = rng.uniform(0.4, 0.7) * R
5:     theta = rng.uniform(35, 65) * math.pi / 180.0

21:         tk.vsub(tk.vadd(H, tk.vmul(u, s)), tk.vmul(v, s)),
22:     ]
23:     vertices = {"O": O, "H": H, "A": A, "α": tk.vsub(H, tk.vmul(n, 0.2)), "P": tk.midpoint(O, H)}
24:     for i, p in enumerate(alpha_pts):
25:         vertices[f"a{i}"] = p
26:     aux_lines = []
27:     N = 32
28:     for i in range(N):
29:         t1 = 2 * math.pi * i / N
30:         t2 = 2 * math.pi * (i + 1) / N
31:         p1 = (R * math.cos(t1), R * math.sin(t1), 0.0)
32:         p2 = (R * math.cos(t2), R * math.sin(t2), 0.0)
33:         aux_lines.append(tk.aux(p1, p2, dash=True, color="#888888"))
34:     for i in range(N):
35:         t1 = 2 * math.pi * i / N
36:         t2 = 2 * math.pi * (i + 1) / N
37:         p1 = (R * math.cos(t1), 0.0, R * math.sin(t1))
38:         p2 = (R * math.cos(t2), 0.0, R * math.sin(t2))
39:         aux_lines.append(tk.aux(p1, p2, dash=True, color="#888888"))
40:     edges = [tk.edge("O", "A"), tk.edge("H", "A")]
41:     for i in range(N):
42:         t1 = 2 * math.pi * i / N
43:         t2 = 2 * math.pi * (i + 1) / N
44:         c1, s1 = math.cos(t1), math.sin(t1)
46:         p1 = tk.vadd(H, tk.vadd(tk.vmul(u, r * c1), tk.vmul(v, r * s1)))
47:         p2 = tk.vadd(H, tk.vadd(tk.vmul(u, r * c2), tk.vmul(v, r * s2)))
48:         edges.append(tk.edge(p1, p2))
49:     aux_lines.append(tk.aux(O, H, dash=True, color="#555555"))
50:     aux_faces = [tk.face(["a0", "a1", "a2", "a3"], color="#a5d8ff", opacity=0.35)]
51:     primitives = [tk.prim_sphere(r=R, center=(0, 0, 0), color="#e8f4fc")]
52:     camera = tk.auto_camera(vertices, primitives=primitives, azim=35.0, elev=28.0, pad=1.6)
53:     spec = tk.make_spec("球截面形状判断", "球截面形状判断·S001", vertices,
54:                         edges=edges, aux_lines=aux_lines, aux_faces=aux_faces,
55:                         primitives=primitives, labels=["O", "H", "A", "α"], camera=camera)
56:     spec["labels"].append({"point": "P", "text": "OH⊥α"})
57:     correct = "圆(或截面圆)"
58:     wrongs = ["椭圆", "抛物线", "大圆"]
59:     vals = [correct] + wrongs
60:     rng.shuffle(vals)
61:     letters = "ABCD"
62:     options = {letters[i]: vals[i] for i in range(4)}
63:     answer = letters[vals.index(correct)]
64:     return tk.item(spec, "如图,平面 α 截球 O 所得截面的形状是什么?", options, answer)

K26 Spec-to-Image Review

  • major/label_overlap
    current视图中O、H、α、OH⊥α四标签挤在球心附近,严重重叠无法辨认
    spec_fix: 重新布局标签:O偏左上、H偏右下、α放平面角外侧、OH⊥α改用引线放远处
  • major/sphere_render
    sphere primitive渲染为实心浅蓝填充球,内部OH虚线及截面圆后半段被遮挡
    spec_fix: 给sphere加opacity≈0.3或改用wireframe模式;需toolkit支持透明/线框球
  • minor/mesh_clutter
    截面圆由32段短edge硬拼接,大圆由64段aux_line拼接,离散感明显
    spec_fix: 使用section_circle或circle primitive替代edge/aux_line分段拼接
  • minor/angle_arc
    直角三角形OHA中OH⊥α仅靠文字标签,无直角符号或angle_arc
    spec_fix: 在H处添加angle_arc或直角符号primitive,弱化或移除文字OH⊥α
  • minor/camera_or_view
    current单视角下标签全部集中在投影中心,未利用画面空间
    spec_fix: 微调camera target或orthoSize,使current视角标签分散可读
What To Change
  • label位置重新分布避免中心重叠
  • sphere增加透明度或改为线框
  • 截面圆改用连续曲线
Preferred Primitives
  • sphere(with opacity)
  • section_circle
  • angle_arc
Remove/Simplify
  • 移除中心密集文字OH⊥α,改用角弧
  • 减少大圆分段数或改用primitive
Label Fixes
  • O放球心左侧
  • H放截面圆心右侧
  • α放平面左上角
  • OH⊥α改为角弧或加引线
Camera Fixes
  • current视角微调target或orthoSize,使标签不重叠
Toolkit/Renderer: 需toolkit支持sphere透明度/线框渲染、连续circle primitive、angle_arc primitive
representation: 基本几何元素齐全,但sphere不透明与plan透明球冲突;截面圆离散化;缺少直角符号
readability: current视图标签严重重叠不可读;oblique视图略有改善但核心视角质量不足
plan: 球、平面、截面圆、垂线、半径均已画出,但透明性、隐藏轮廓、直角标注未充分体现
next: visual_repair

cross_section_sphere__S003

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 球截面形状判断
难度 L3
Scene
斜二测视角下,一个竖直圆台(上底小、下底大,轴线用虚线)内部有一个内切球,球与上下底及侧面均相切,球心O在轴线上。一个斜平面从左后上方向右前下方截取组合体,与圆台侧面交于两条母线,与球交成一封闭曲线。画出球轮廓的可见实线弧与隐藏虚线弧,标出球与侧面切点A、B及截面圆心O',并用虚线连接OO'表示球心到截面的垂线。
Must Render
[
  "圆台上下底椭圆及前后母线(含虚线隐藏边)",
  "内切球的可见与隐藏轮廓线",
  "斜平面与球的交线(截面圆)",
  "球心O、截面圆心O'及垂线段OO'",
  "球与圆台侧面的切点A、B"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "S003",
  "camera": {
    "position": [
      6.941,
      6.274,
      6.962
    ],
    "target": [
      0.05,
      1.092,
      0.071
    ],
    "orthoSize": 3.13
  },
  "counts": {
    "vertices": 78,
    "faces": 14,
    "edges": 0,
    "aux_lines": 54,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 4
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.1242905366390958,
      "position": [
        0.0,
        1.1242905366390958,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "O'",
      "text": "O'"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "O",
    "O'",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "U0",
    "U1",
    "U2",
    "U3",
    "U4",
    "U5",
    "U6",
    "U7",
    "U8",
    "U9",
    "U10",
    "U11",
    "U12",
    "U13",
    "U14",
    "U15",
    "V0",
    "V1",
    "V2",
    "V3",
    "V4",
    "V5",
    "V6",
    "V7",
    "V8",
    "V9",
    "V10",
    "V11",
    "V12",
    "V13",
    "V14",
    "V15"
  ],
  "faces_sample": [
    {
      "points": [
        "L",
        "K",
        "J",
        "I",
        "H",
        "G",
        "F",
        "E",
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "M",
        "N",
        "P",
        "Q",
        "R",
        "S",
        "T",
        "U",
        "V",
        "W",
        "X",
        "Y"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "N",
        "M"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "P",
        "N"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "Q",
        "P"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "R",
        "Q"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "S",
        "R"
      ]
    },
    {
      "points": [
        "F",
        "G",
        "T",
        "S"
      ]
    },
    {
      "points": [
        "G",
        "H",
        "U",
        "T"
      ]
    },
    {
      "points": [
        "H",
        "I",
        "V",
        "U"
      ]
    },
    {
      "points": [
        "I",
        "J",
        "W",
        "V"
      ]
    },
    {
      "points": [
        "J",
        "K",
        "X",
        "W"
      ]
    },
    {
      "points": [
        "K",
        "L",
        "Y",
        "X"
      ]
    },
    {
      "points": [
        "L",
        "A",
        "M",
        "Y"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        [
          0.0,
          0.0,
          0.0
        ],
        [
          0.0,
          2.2485810732781917,
          0.0
        ]
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "O",
        "O'"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "C0",
        "C1"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C1",
        "C2"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C2",
        "C3"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C3",
        "C4"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C4",
        "C5"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C5",
        "C6"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C6",
        "C7"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C7",
        "C8"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C8",
        "C9"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C9",
        "C10"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C10",
        "C11"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C11",
        "C12"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C12",
        "C13"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C13",
        "C14"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C14",
        "C15"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C15",
        "C16"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C16",
        "C17"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C17",
        "C18"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C18",
        "C19"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "C19",
        "C0"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "U0",
        "U1"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U1",
        "U2"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U2",
        "U3"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U3",
        "U4"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U4",
        "U5"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U5",
        "U6"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U6",
        "U7"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U7",
        "U8"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U8",
        "U9"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U9",
        "U10"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U10",
        "U11"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U11",
        "U12"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U12",
        "U13"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U13",
        "U14"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U14",
        "U15"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "U15",
        "U0"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V0",
        "V1"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V1",
        "V2"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V2",
        "V3"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V3",
        "V4"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V4",
        "V5"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V5",
        "V6"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V6",
        "V7"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V7",
        "V8"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V8",
        "V9"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "V9",
        "V10"
      ],
      "dash": false,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     n = 12
4:     rb = rng.uniform(1.4, 1.8)
5:     rt = rng.uniform(0.5, 0.8)

6:     h = 2.0 * math.sqrt(rb * rt)
7:     R = h * 0.5
8:     vs, faces = tk.frustum(n, r_bot=rb, r_top=rt, h=h)
9:     O = (0.0, R, 0.0)
10:     vs["O"] = O
11:     NM = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
12:     bn = [NM[i] for i in range(n)]
65:     circ(O, R, tk.unit((1.0, 0.0, 1.0)), (0.0, 1.0, 0.0), "V")
66: 
67:     prim = [tk.prim_sphere(r=R, center=O)]
68:     cam = tk.auto_camera(vs, primitives=prim, azim=45.0, elev=28.0, pad=1.6)
69:     spec = tk.make_spec("球截面形状判断", "S003", vs, faces=faces, aux_lines=aux,
70:                         primitives=prim, labels=["O", "O'", "A", "B"], camera=cam)
71: 
72:     opts = ["圆", "椭圆", "等腰梯形", "抛物线"]
73:     rng.shuffle(opts)
74:     ans = "ABCD"[opts.index("圆")]
75:     return tk.item(spec, "如图,一个球内切于竖直圆台,一斜平面截此组合体并与球面相交形成封闭曲线。则该曲线所在的截面形状是( )",
76:                    {"A": opts[0], "B": opts[1], "C": opts[2], "D": opts[3]}, ans)

K26 Spec-to-Image Review

  • major/frustum_render
    圆台被离散为12边形棱台,侧面呈现12个梯形网格,完全丧失旋转体光滑侧面特征。
    spec_fix: 改用frustum primitive或教材式上下椭圆加3-4条关键母线表示,取消多边形mesh。
  • major/sphere_render
    球面堆砌U/V两组灰色经纬线环,使球体呈地球仪状,不符合教材球体画法规范。
    spec_fix: 删除球面所有经纬辅助线,仅保留球轮廓可见实线弧与隐藏虚线弧。
  • minor/label_missing
    current视角中右侧切点A标签缺失,图面仅显示B、O、O'三点。
    spec_fix: 调整A标签空间位置或微调current相机角度,确保A不被遮挡。
  • minor/mesh_clutter
    圆台12条母线全部以实线画出,侧面线网过密,干扰截面圆与切点视觉定位。
    spec_fix: 圆台侧面仅保留最左、最右及前后共4条关键母线,其余删除。
What To Change
  • 圆台改用光滑旋转体表示,删除12边形离散mesh
  • 球面删除多余经纬线,仅保留轮廓弧
  • 修正A标签在current视角的可见性
Preferred Primitives
  • frustum
  • sphere
  • section_circle
Remove/Simplify
  • 移除圆台侧面12条母线,仅保留关键可见轮廓
  • 删除球面U/V系列辅助线环
Label Fixes
  • 调整A标签位置避免被遮挡
  • 校验O'位于截面圆中心位置
Camera Fixes
  • current相机略调角度使A可见
Toolkit/Renderer: 建议toolkit新增frustum primitive支持光滑圆台渲染,避免题目代码用mesh硬搓旋转体。
representation: 圆台用12边形mesh硬搓成棱台,严重偏离教材旋转体画法;球面堆砌经纬线,核心对象几何特征表达失真。
readability: current视角缺失A标签,O'位置疑似偏离截面圆;线网过密导致截面圆与切点难以快速定位。
plan: 截面圆、OO'垂线、球心O均已画出,但圆台光滑侧面、球轮廓简洁性未满足plan要求。
next: toolkit_patch

cross_section_sphere__S006

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
绘制一个透明球,球心 O。球内作内接正方体 ABCD-A1B1C1D1,可见棱实线,被遮挡的后棱、下底对角线 AC、体对角线 B1D 均用虚线。平面 α 过 A1、C1 与 AB 中点 M 斜截球面,交线画为封闭曲线。作球心 O 到 α 的垂线 OH(投影线),并在 α 上画截面半径 HP。作过 OH 的竖直大圆(虚实线)。另作一辅助虚线小平面 β 与球相切于点 T(切点),T 位于截面圆附近但不重合。
Must Render
[
  "球的外轮廓与球心 O",
  "内接正方体的可见棱、隐藏棱(虚线)及对角线/体对角线(虚线)",
  "平面 α 与球面的封闭交线(截面圆)",
  "球心到截面的垂线 OH 及截面半径 HP",
  "过 OH 的竖直大圆(虚实线)",
  "辅助切平面 β 与切点 T"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "球内接正方体截面",
  "camera": {
    "position": [
      7.584,
      5.583,
      10.701
    ],
    "target": [
      0.239,
      0.073,
      0.048
    ],
    "orthoSize": 4.062
  },
  "counts": {
    "vertices": 89,
    "faces": 0,
    "edges": 88,
    "aux_lines": 4,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 13
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.25166604983954,
      "position": [
        0,
        0,
        0
      ],
      "color": "#f3f4f6"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "A1",
      "text": "A1"
    },
    {
      "point": "B1",
      "text": "B1"
    },
    {
      "point": "C1",
      "text": "C1"
    },
    {
      "point": "D1",
      "text": "D1"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "T",
      "text": "T"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "A1",
    "B1",
    "C1",
    "D1",
    "O",
    "M",
    "H",
    "P",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15",
    "c16",
    "c17",
    "c18",
    "c19",
    "c20",
    "c21",
    "c22",
    "c23",
    "c24",
    "c25",
    "c26",
    "c27",
    "c28",
    "c29",
    "c30",
    "c31",
    "c32",
    "c33",
    "c34",
    "c35",
    "b0",
    "b1",
    "b2",
    "b3",
    "b4",
    "b5",
    "b6",
    "b7",
    "b8",
    "b9",
    "b10",
    "b11",
    "b12",
    "b13",
    "b14",
    "b15",
    "b16",
    "b17",
    "b18",
    "b19",
    "b20",
    "b21",
    "b22",
    "b23",
    "b24",
    "b25",
    "b26",
    "b27",
    "b28",
    "b29",
    "b30",
    "b31",
    "b32",
    "b33",
    "b34",
    "b35",
    "T",
    "t0",
    "t1",
    "t2",
    "t3"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "A1"
      ],
      "dash": true
    },
    {
      "points": [
        "B",
        "B1"
      ],
      "dash": true
    },
    {
      "points": [
        "C",
        "C1"
      ]
    },
    {
      "points": [
        "D",
        "D1"
      ]
    },
    {
      "points": [
        "A1",
        "B1"
      ],
      "dash": true
    },
    {
      "points": [
        "B1",
        "C1"
      ]
    },
    {
      "points": [
        "C1",
        "D1"
      ]
    },
    {
      "points": [
        "D1",
        "A1"
      ]
    },
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "B1",
        "D"
      ],
      "dash": true
    },
    {
      "points": [
        "c0",
        "c1"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c1",
        "c2"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c2",
        "c3"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c3",
        "c4"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c4",
        "c5"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c5",
        "c6"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c6",
        "c7"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c7",
        "c8"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c8",
        "c9"
      ],
      "color": "#374151"
    },
    {
      "points": [
        "c9",
        "c10"
      ],
      "color": "#374151"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "t0",
        "t1"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "t1",
        "t2"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "t2",
        "t3"
      ],
      "dash": true,
      "color": "#9ca3af"
    },
    {
      "points": [
        "t3",
        "t0"
      ],
      "dash": true,
      "color": "#9ca3af"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     a = rng.choice([2.0, 2.2, 2.4, 2.6, 2.8])
4:     x = a / 2.0
5:     vs = {"A": (-x, -x, x), "B": (x, -x, x), "C": (x, -x, -x), "D": (-x, -x, -x),

10:             ("C", "C1"), ("D", "D1"), ("A1", "B1"), ("B1", "C1"), ("C1", "D1"), ("D1", "A1")]
11:     back = {("A", "B"), ("A", "A1"), ("B", "B1"), ("A1", "B1")}
12:     edges = []
13:     for u, v in eall:
14:         edges.append(tk.edge(u, v, dash=(u, v) in back or (v, u) in back))
15:     edges.append(tk.edge("A", "C", dash=True))
16:     edges.append(tk.edge("B1", "D", dash=True))
17:     M = (0.0, -x, x)
18:     vs["M"] = M
19:     v1 = tk.vsub(vs["C1"], vs["A1"])
20:     v2 = tk.vsub(M, vs["A1"])
37:         pt = tk.vadd(H, tk.vadd(tk.vmul(u, r * math.cos(ang)), tk.vmul(v, r * math.sin(ang))))
38:         vs[f"c{i}"] = pt
39:         edges.append(tk.edge(f"c{i}", f"c{(i + 1) % segs}", dash=False, color="#374151"))
40:     edges.append(tk.edge("O", "H", dash=False, color="#b45309"))
41:     edges.append(tk.edge("H", "P", dash=False, color="#b45309"))
42:     oh = H
43:     n_big = tk.unit(tk.cross(oh, (0.0, 1.0, 0.0)))
44:     ub = (0.0, 1.0, 0.0)
45:     vb = tk.cross(n_big, ub)
54:         vs[f"b{i}"] = p1
55:         vs[f"b{(i + 1) % segs}"] = p2
56:         edges.append(tk.edge(f"b{i}", f"b{(i + 1) % segs}",
57:                              dash=tk.dot(tk.midpoint(p1, p2), view) < 0, color="#4b5563"))
58:     T = tk.vmul(tk.unit(P), R)
59:     vs["T"] = T
60:     nt = tk.unit(T)
71:         vs[f"t{i}"] = pt
72:         aux.append(tk.aux(f"t{i}", f"t{(i + 1) % 4}", dash=True, color="#9ca3af"))
73:     prims = [tk.prim_sphere(R, center=(0, 0, 0), color="#f3f4f6")]
74:     cam = tk.auto_camera(vs, primitives=prims, azim=rng.uniform(32.0, 42.0),
75:                          elev=rng.uniform(20.0, 26.0), pad=1.6)
76:     labels = ["A", "B", "C", "D", "A1", "B1", "C1", "D1", "O", "M", "H", "P", "T"]
77:     spec = tk.make_spec("球截面形状判断", "球内接正方体截面", vs, edges=edges,
78:                         aux_lines=aux, primitives=prims, labels=labels, camera=cam)
79:     vals = ["圆", "椭圆", "弓形", "矩形"]
80:     rng.shuffle(vals)
81:     opts = {"A": vals[0], "B": vals[1], "C": vals[2], "D": vals[3]}
82:     ans = [k for k, v in opts.items() if v == "圆"][0]
83:     q = ("如图,球内接正方体 ABCD-A1B1C1D1,平面 α 经过上底面对角线 A1C1 及棱 AB 的中点 M,"
84:          "与球面相交形成封闭曲线。则平面 α 截球所得截面的形状是( )")
85:     return tk.item(spec, q, opts, ans, answer_text="圆")

K26 Spec-to-Image Review

  • major/mesh_clutter
    current图截面圆与大圆各36段边线,与正方体棱线、对角线交织,线网过密不像教材图
    spec_fix: 截面圆与大圆改用12-16段边线,或仅保留可见弧;删除非必要大圆
  • major/label_overlap
    current视角A1标签似与A混淆,O/H/P/T挤在球心右侧线丛中难以辨认
    spec_fix: 调整A1锚点至左上外侧,O、H、P、T标签增加偏移避免贴线
  • minor/tangent_or_section
    辅助β仅用t0-t3四边形表示,呈右侧孤立折线,切点T与P几乎重合
    spec_fix: 删除β及T,或改在T处画短切线标注;确保不与截面圆重叠
  • major/sphere_render
    球primitive渲染为浅蓝实心填充,轮廓线与内部线网重叠,前后层次混乱
    spec_fix: 设球为透明线框或仅保留轮廓圆;需toolkit支持透明材质或线框模式
  • minor/camera_or_view
    current视角B1、O、H几乎共线,截面圆与上底面投影重叠
    spec_fix: 调整azim增加约15°,使截面圆与正方体顶点、OH投影错开
What To Change
  • 圆离散段数降至12-16
  • 简化或删除切平面β
  • 调整camera拉开B1-O-H
  • 球改为透明/线框
Preferred Primitives
  • sphere(透明/线框)
  • section_circle
  • angle_arc
Remove/Simplify
  • 删除t0-t3切平面四边形
  • 大圆只画可见弧
  • 隐藏对角线若冲突则弱化
Label Fixes
  • A1左移防与A混淆
  • O/H/P/T加偏移
  • T若保留则远离P
Camera Fixes
  • current视角azim增15°错开重叠
  • section_plane视角确保OH垂直可见
Toolkit/Renderer: renderer需支持透明/线框球primitive,或允许sphere不填充仅渲染轮廓与截面
representation: 球primitive正确但填充不透明;圆离散过密;切平面表示非标准;缺直角标识
readability: current图线网堆叠严重,oblique与section视角有补充但主图质量不足
plan: 基本覆盖plan,但β表达失败,OH垂直关系无angle_arc,截面圆与正方体重叠
next: spec_rewrite

cross_section_sphere__S007

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
画一个球体 O 置于水平圆柱正上方且外切,切点为 T,公切线为铅垂线。倾斜平面 α 从左上斜向右下同时截球与圆柱,球下半部约 1/4 被圆柱遮挡。平面 α 与球面的交线为截面圆:可见上半弧用实线,被圆柱遮住的下半弧用虚线。用虚线画出球心 O 到平面 α 的垂线段 OH,H 为截面圆心。圆柱画出前后水平母线及左侧底圆,球的隐藏轮廓线也用虚线。
Must Render
[
  "球 O 的可见轮廓与隐藏轮廓(虚线)",
  "球与圆柱的外切切点 T",
  "平面 α 截球所得截面圆(可见部分实线、被遮挡部分虚线)",
  "球心 O 到平面 α 的垂线段 OH(虚线)及垂足 H",
  "水平圆柱的上下母线与左侧底圆"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "球圆柱组合体",
  "camera": {
    "position": [
      8.916,
      6.475,
      8.624
    ],
    "target": [
      1.229,
      1.085,
      0.014
    ],
    "orthoSize": 3.283
  },
  "counts": {
    "vertices": 51,
    "faces": 0,
    "edges": 38,
    "aux_lines": 2,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 4
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.8273863208820907,
      "position": [
        1.268228632332338,
        2.06515506149211,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "al",
      "text": "α"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "P0",
    "P1",
    "P2",
    "P3",
    "P4",
    "P5",
    "P6",
    "P7",
    "P8",
    "P9",
    "P10",
    "P11",
    "P12",
    "P13",
    "P14",
    "P15",
    "P16",
    "P17",
    "P18",
    "P19",
    "P20",
    "P21",
    "P22",
    "P23",
    "O",
    "Ta",
    "al"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "E"
      ]
    },
    {
      "points": [
        "E",
        "F"
      ]
    },
    {
      "points": [
        "F",
        "G"
      ]
    },
    {
      "points": [
        "G",
        "H"
      ]
    },
    {
      "points": [
        "H",
        "I"
      ]
    },
    {
      "points": [
        "I",
        "J"
      ]
    },
    {
      "points": [
        "J",
        "K"
      ]
    },
    {
      "points": [
        "K",
        "L"
      ]
    },
    {
      "points": [
        "L",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "M"
      ],
      "dash": true
    },
    {
      "points": [
        "G",
        "T"
      ]
    },
    {
      "points": [
        "P0",
        "P1"
      ]
    },
    {
      "points": [
        "P1",
        "P2"
      ]
    },
    {
      "points": [
        "P2",
        "P3"
      ]
    },
    {
      "points": [
        "P3",
        "P4"
      ]
    },
    {
      "points": [
        "P4",
        "P5"
      ]
    },
    {
      "points": [
        "P5",
        "P6"
      ]
    },
    {
      "points": [
        "P6",
        "P7"
      ]
    },
    {
      "points": [
        "P7",
        "P8"
      ]
    },
    {
      "points": [
        "P8",
        "P9"
      ],
      "dash": true
    },
    {
      "points": [
        "P9",
        "P10"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "T",
        "Ta"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 随机参数
4:     R = rng.uniform(0.9, 1.3)
5:     r = rng.uniform(0.6, 0.9)

6:     L = rng.uniform(2.2, 3.0)
7:     n = 12
8:     vs, _ = tk.frustum(n, r_bot=R, r_top=R, h=L)
9:     # 绕 z 轴旋转 -90°,使柱体轴线沿 +x 水平放置
10:     for k in vs:
11:         x, y, z = vs[k]
12:         vs[k] = (y, -x, z)
25:     H = (cx - t * nx, cy - t * ny, -t * nz)
26:     rc = math.sqrt(max(0.0, r * r - h * h))
27:     # 平面内局部标架:u 为“上”方向在平面内的投影
28:     dot = nu[1]
29:     gp = (-dot * nu[0], 1.0 - dot * nu[1], -dot * nu[2])
30:     ng = math.sqrt(gp[0] * gp[0] + gp[1] * gp[1] + gp[2] * gp[2])
31:     if ng < 1e-6:
36:          nu[2] * u[0] - nu[0] * u[2],
37:          nu[0] * u[1] - nu[1] * u[0])
38:     # 截面圆采样点
39:     N = 24
40:     cnames = []
41:     for i in range(N):
42:         ph = 2.0 * math.pi * i / N
48:                   H[2] + rc * (c * u[2] + s * v[2]))
49:         cnames.append(nm)
50:     edges = []
51:     bot = [tk._NAMES[i] for i in range(n)]
52:     top = [tk._NAMES[n + i] for i in range(n)]
53:     # 左侧底圆
54:     for i in range(n):
55:         edges.append(tk.edge(bot[i], bot[(i + 1) % n]))
56:     # 前后水平母线(后隐藏、前可见)
57:     edges.append(tk.edge(bot[0], top[0], dash=True))
58:     edges.append(tk.edge(bot[n // 2], top[n // 2], dash=False))
59:     # 截面圆:上半弧实线,下半弧(约 1/3)虚线表示被圆柱遮挡
60:     for i in range(N):
61:         a = cnames[i]
62:         b = cnames[(i + 1) % N]
63:         mid = 2.0 * math.pi * (i + 0.5) / N
64:         edges.append(tk.edge(a, b, dash=(math.cos(mid) < -0.5)))
65:     auxs = []
66:     vs["O"] = O
67:     vs["H"] = H
68:     vs["T"] = (cx, R, 0.0)
74:                 H[1] + 0.6 * rc * u[1] + 0.3 * rc * v[1],
75:                 H[2] + 0.6 * rc * u[2] + 0.3 * rc * v[2])
76:     labels = ["O", "H", "T", "al"]
77:     cam = tk.auto_camera(vs, primitives=[tk.prim_sphere(r=r, center=O)],
78:                          azim=rng.uniform(30.0, 45.0), elev=rng.uniform(22.0, 32.0))
79:     spec = tk.make_spec("球截面形状判断", "球圆柱组合体", vs, faces=[], edges=edges,
80:                         aux_lines=auxs, labels=labels, camera=cam,
81:                         primitives=[tk.prim_sphere(r=r, center=O)])
82:     for lb in spec.get("labels", []):
83:         if lb["point"] == "al":
84:             lb["text"] = "α"
85:     letters = ["A", "B", "C", "D"]
86:     rng.shuffle(letters)
91:         if v == "圆":
92:             ans = letters[i]
93:     return tk.item(spec, "如图,平面 α 截球 O 所得的截面形状是( )", opts, ans)

K26 Spec-to-Image Review

  • major/cone_cylinder_render
    圆柱用tk.frustum(n=12)硬搓为十二棱柱,侧面全是平直折线,无cylinder primitive
    spec_fix: 引入cylinder primitive,或改用教材式前后椭圆+母线表示水平圆柱
  • major/plan_coverage
    平面α同时截圆柱的交线完全缺失,仅画出截球截面圆
    spec_fix: 补画平面α截圆柱所得的交线椭圆/线段,体现同时截两几何体
  • minor/label_overlap
    α标签贴球面轮廓,O/H/T在球下部拥挤重叠
    spec_fix: α移至平面外侧空旷处,O/H/T微调避免重叠
  • minor/sphere_render
    球隐藏轮廓线未按plan画虚线,sphere primitive仅显实线圆
    spec_fix: 补充球被圆柱遮挡部分的隐藏轮廓虚线弧
  • minor/mesh_clutter
    圆柱底圆与截面圆均为密集折线,线网过密
    spec_fix: 圆柱改用光滑轮廓,截面圆使用section_circle primitive
What To Change
  • 圆柱改用cylinder primitive或教材式椭圆+母线
  • 补画平面α截圆柱交线
  • 球隐藏轮廓加虚线弧
Preferred Primitives
  • cylinder
  • section_circle
Remove/Simplify
  • 移除12边形棱柱侧面网格线
  • 简化截面圆为光滑曲线
Label Fixes
  • α标签移至平面α外侧空旷处
  • O/H/T错开避免重叠
Camera Fixes
  • 微调current视角,使圆柱左侧底圆与球外切关系清晰
Toolkit/Renderer: 需增加cylinder primitive与section_circle primitive,支持教材式圆柱与光滑截面圆渲染
representation: 圆柱被离散为12棱柱,无cylinder primitive;截面圆与球隐藏轮廓表示不足。
readability: current视角中圆柱呈多边形块,遮挡关系混乱;oblique_plus45未能改善圆柱辨识度。
plan: 缺平面截圆柱交线、球隐藏虚线轮廓,α标签位置不当。
next: toolkit_patch

cross_section_sphere__S010

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue minor hold

Plan / Must Render

题型 球截面形状判断
难度 L3
Scene
一个透明球体,球心标记为 O。球内作内接正方体 ABCD-A1B1C1D1,被球面前方遮挡的棱及体对角线 AC1(过球心 O)均用虚线绘制,可见棱用细实线。作斜截面平面 α(以平行四边形示意)截球,交线在透视下呈椭圆轮廓。从球心 O 向平面 α 引垂线,垂足 H(即截面圆圆心)用虚线表示;在交线上取一点 P,连接 HP(截面圆半径,实线)与 OP(球半径,虚线)。平面 α 还与正方体的棱 A1B1 相交于点 Q。
Must Render
[
  "球体轮廓及球心 O",
  "内接正方体的可见棱(细实线)与隐藏棱(虚线)",
  "体对角线 AC1(虚线,经过 O)",
  "斜截面平面 α(平行四边形框)及截面交线(椭圆轮廓)",
  "垂足 H 及垂线段 OH(虚线)",
  "交线上点 P 及线段 HP(实线)、OP(虚线)"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "透明球体、内接正方体、斜截面平面α、截面交线、OH、HP、OP及AC1均已显式绘制",
  "camera": {
    "position": [
      12.616,
      9.092,
      15.18
    ],
    "target": [
      0.504,
      0.905,
      0.209
    ],
    "orthoSize": 5.736
  },
  "counts": {
    "vertices": 65,
    "faces": 0,
    "edges": 17,
    "aux_lines": 52,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 13
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.756653110915029,
      "position": [
        0.0,
        0.0,
        0.0
      ],
      "color": "#8ec5eb"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "A1",
      "text": "A1"
    },
    {
      "point": "B1",
      "text": "B1"
    },
    {
      "point": "C1",
      "text": "C1"
    },
    {
      "point": "D1",
      "text": "D1"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Q",
      "text": "Q"
    },
    {
      "point": "α",
      "text": "α"
    }
  ],
  "vertex_names": [
    "O",
    "A",
    "B",
    "C",
    "D",
    "A1",
    "B1",
    "C1",
    "D1",
    "H",
    "P",
    "Q",
    "p0",
    "p1",
    "p2",
    "p3",
    "α",
    "e0",
    "e1",
    "e2",
    "e3",
    "e4",
    "e5",
    "e6",
    "e7",
    "e8",
    "e9",
    "e10",
    "e11",
    "e12",
    "e13",
    "e14",
    "e15",
    "e16",
    "e17",
    "e18",
    "e19",
    "e20",
    "e21",
    "e22",
    "e23",
    "e24",
    "e25",
    "e26",
    "e27",
    "e28",
    "e29",
    "e30",
    "e31",
    "e32",
    "e33",
    "e34",
    "e35",
    "e36",
    "e37",
    "e38",
    "e39",
    "e40",
    "e41",
    "e42",
    "e43",
    "e44",
    "e45",
    "e46",
    "e47"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "B",
        "C"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "C",
        "D"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "D",
        "A"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "A1",
        "Q"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "Q",
        "B1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "B1",
        "C1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C1",
        "D1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "D1",
        "A1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "A",
        "A1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "B",
        "B1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C",
        "C1"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "D",
        "D1"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "p0",
        "p1"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "p1",
        "p2"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "p2",
        "p3"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "p3",
        "p0"
      ],
      "color": "#111827"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "C1"
      ],
      "dash": true,
      "color": "#4b5563"
    },
    {
      "points": [
        "e0",
        "e1"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e1",
        "e2"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e2",
        "e3"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e3",
        "e4"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e4",
        "e5"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e5",
        "e6"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e6",
        "e7"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e7",
        "e8"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e8",
        "e9"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e9",
        "e10"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e10",
        "e11"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e11",
        "e12"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e12",
        "e13"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e13",
        "e14"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e14",
        "e15"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e15",
        "e16"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e16",
        "e17"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e17",
        "e18"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e18",
        "e19"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e19",
        "e20"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e20",
        "e21"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e21",
        "e22"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e22",
        "e23"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e23",
        "e24"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e24",
        "e25"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e25",
        "e26"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e26",
        "e27"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e27",
        "e28"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e28",
        "e29"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e29",
        "e30"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e30",
        "e31"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e31",
        "e32"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e32",
        "e33"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e33",
        "e34"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e34",
        "e35"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e35",
        "e36"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e36",
        "e37"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e37",
        "e38"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e38",
        "e39"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e39",
        "e40"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e40",
        "e41"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e41",
        "e42"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e42",
        "e43"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e43",
        "e44"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e44",
        "e45"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e45",
        "e46"
      ],
      "dash": false,
      "color": "#1f2937"
    },
    {
      "points": [
        "e46",
        "e47"
      ],
      "dash": false,
      "color": "#1f2937"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "p0",
        "p1",
        "p2",
        "p3"
      ],
      "color": "#e5e7eb",
      "opacity": 0.24
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     R = rng.uniform(2.25, 2.85)
5:     s = R / math.sqrt(3.0)

17:     }
18: 
19:     # 选取斜截面 α,使其明确与正方体棱 A1B1 相交于 Q,且截球为非退化圆。
20:     for _ in range(80):
21:         n = tk.unit((
22:             rng.uniform(0.18, 0.55),
23:             rng.uniform(0.58, 0.92),
42:     v = tk.unit(tk.cross(n, u))
43: 
44:     # P 取在截面交线圆上;HP 是截面圆半径,OP 是球半径。
45:     phi = rng.uniform(0.10 * math.pi, 0.34 * math.pi)
46:     P = tk.vadd(H, tk.vadd(tk.vmul(u, r * math.cos(phi)), tk.vmul(v, r * math.sin(phi))))
47: 
48:     V["H"] = H
61:     V["α"] = tk.vadd(H, tk.vadd(tk.vmul(u, 1.10 * plane_scale), tk.vmul(v, 0.96 * plane_scale)))
62: 
63:     # 截面交线:实际为空间圆,透视图中呈椭圆轮廓;显式拆成多段实线。
64:     circle_names = []
65:     m = 48
66:     for i in range(m):
67:         ang = 2.0 * math.pi * i / m
72:     az = rng.uniform(28.0, 42.0)
73:     elev = rng.uniform(20.0, 30.0)
74:     prims = [tk.prim_sphere(r=R, center=(0.0, 0.0, 0.0), color="#8ec5eb")]
75:     cam = tk.auto_camera(V, primitives=prims, azim=az, elev=elev, pad=1.48)
76:     view = tk.unit(tuple(cam["position"]))
77: 
78:     def is_hidden(a, b):
79:         return tk.dot(tk.midpoint(V[a], V[b]), view) < -0.035 * R
80: 
81:     edges = []
82:     aux_lines = []
83: 
84:     cube_edges = [
85:         ("A", "B"), ("B", "C"), ("C", "D"), ("D", "A"),
86:         ("A1", "B1"), ("B1", "C1"), ("C1", "D1"), ("D1", "A1"),
87:         ("A", "A1"), ("B", "B1"), ("C", "C1"), ("D", "D1"),
88:     ]
89: 
90:     # 内接正方体棱:可见为实线,处在球前方遮挡后的不可见部分为虚线。
91:     # A1B1 被 Q 分成两段,显式体现平面 α 与棱 A1B1 的交点 Q。
92:     for a, b in cube_edges:
93:         if (a, b) == ("A1", "B1"):
94:             edges.append(tk.edge("A1", "Q", dash=is_hidden("A1", "Q"), color="#111827"))
95:             edges.append(tk.edge("Q", "B1", dash=is_hidden("Q", "B1"), color="#111827"))
96:         else:
97:             edges.append(tk.edge(a, b, dash=is_hidden(a, b), color="#111827"))
98: 
99:     # 体对角线 AC1:虚线,且经过球心 O。
100:     aux_lines.append(tk.aux("A", "C1", dash=True, color="#4b5563"))
101: 
102:     # 平面 α 平行四边形框。
103:     for i in range(4):
104:         edges.append(tk.edge(plane_corners[i], plane_corners[(i + 1) % 4], dash=False, color="#111827"))
105: 
106:     # 截面交线椭圆轮廓:必须显式绘制为实线。
107:     for i in range(m):
108:         aux_lines.append(tk.aux(circle_names[i], circle_names[(i + 1) % m], dash=False, color="#1f2937"))
109: 
110:     # OH 垂线段、OP 球半径为虚线;HP 截面圆半径为实线。
111:     aux_lines.append(tk.aux("O", "H", dash=True, color="#4b5563"))
112:     aux_lines.append(tk.aux("O", "P", dash=True, color="#4b5563"))
113:     aux_lines.append(tk.aux("H", "P", dash=False, color="#111827"))
114: 
115:     aux_faces = [tk.face(plane_corners, color="#e5e7eb", opacity=0.24)]
116: 
117:     spec = tk.make_spec(
118:         "球截面形状判断",
119:         "透明球体、内接正方体、斜截面平面α、截面交线、OH、HP、OP及AC1均已显式绘制",
120:         V,
121:         faces=None,
122:         edges=edges,
123:         aux_lines=aux_lines,
124:         aux_faces=aux_faces,
125:         primitives=prims,
126:         labels=["O", "A", "B", "C", "D", "A1", "B1", "C1", "D1", "H", "P", "Q", "α"],
127:         camera=cam,
128:     )
129: 
130:     options = {"A": "圆", "B": "抛物线", "C": "椭圆", "D": "三角形"}
131:     section_shape = "圆" if r > 1e-9 else "点"
132:     answer = next(k for k, val in options.items() if val == section_shape)
133: 
134:     question = "如图,球 O 内有一内接正方体(虚线为不可见棱及对角线),平面 α 截球 O 于点 P 等处,H 为球心 O 在平面 α 上的射影。则平面 α 截球 O 所得截面的形状是?"
135: 
136:     return tk.item(spec, question, options, answer, answer_text=options[answer])

K26 Spec-to-Image Review

  • major/label_overlap
    current图H与B1、C与P、D与O标签方框严重重叠,关键点无法辨认
    spec_fix: 调整相机方位角与仰角,使O、H、B1、P、C、D投影分离,必要时手动偏移标签
  • major/camera_or_view
    current相机导致球心O、截面圆心H、顶点B1/D在画面中心几乎重合
    spec_fix: 改用稍低仰角并绕竖轴旋转方位角,使体对角线与视线方向错开
  • minor/aux_line_clutter
    截面圆使用48段aux_lines硬拼,离散化过度且增加不必要的线数
    spec_fix: 将48段简化为16-24段,或使用section_circle/ellipse primitive绘制
What To Change
  • 调整相机避免O、H、B1投影重叠
  • 减少截面圆离散段数
  • 优化标签位置防重叠
Preferred Primitives
  • section_circle
Remove/Simplify
  • 将48段截面圆aux_lines简化为16-24段平滑曲线
Label Fixes
  • H右移、P上移、D左移,避免与B1、C、O重叠
Camera Fixes
  • 降低仰角并增大方位角,使正方体顶点与截面圆心分离
Toolkit/Renderer: none
representation: 球primitive正确,截面圆用多段线透视呈椭圆,但48段离散过度
readability: current图标签重叠严重不可读,oblique_minus45分离部分标签但平面框变形
plan: must_render元素均已绘制,但关键点因标签重叠难以定位
next: visual_repair

cross_section_sphere__S011

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
图中绘制一个球体与同轴圆台构成的组合体:球心O位于圆台轴线上,球体下半部分被圆台上底面遮挡。一个斜平面α从左上方向右下方截穿该组合体,与球面相交形成一条封闭曲线。作出球心O到平面α的垂线段,垂足为H;在交线上取一点P,连接HP作为截面圆的半径。球面背向轮廓及截面圆被圆台遮挡的部分均用虚线表示,圆台轴截面以等腰梯形实线绘制。
Must Render
[
  "球体及其球心O、球半径R",
  "同轴圆台及其轴截面轮廓(上底遮挡球体下部)",
  "斜截面平面α的示意平行四边形",
  "球面与平面α的交线(截面圆,可见段实线、被圆台遮挡段虚线)",
  "球心到平面的垂线段OH及截面圆半径HP"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "球-圆台组合体斜截面",
  "camera": {
    "position": [
      6.641,
      8.328,
      7.108
    ],
    "target": [
      -0.159,
      2.401,
      0.005
    ],
    "orthoSize": 2.939
  },
  "counts": {
    "vertices": 36,
    "faces": 0,
    "edges": 29,
    "aux_lines": 6,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 5
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.077675025087889,
      "position": [
        0.0,
        2.3242228992702447,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "p",
      "text": "P"
    },
    {
      "point": "h",
      "text": "H"
    },
    {
      "point": "r",
      "text": "R"
    },
    {
      "point": "alpha",
      "text": "α"
    }
  ],
  "vertex_names": [
    "T1",
    "T2",
    "T3",
    "T4",
    "O",
    "r",
    "h",
    "a0",
    "a1",
    "a2",
    "a3",
    "alpha",
    "p",
    "s1",
    "s2",
    "s3",
    "s4",
    "s5",
    "s6",
    "s7",
    "s8",
    "s9",
    "s10",
    "s11",
    "s12",
    "s13",
    "s14",
    "s15",
    "s16",
    "s17",
    "s18",
    "s19",
    "s20",
    "s21",
    "s22",
    "s23"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "T1",
        "T2"
      ]
    },
    {
      "points": [
        "T2",
        "T3"
      ]
    },
    {
      "points": [
        "T3",
        "T4"
      ]
    },
    {
      "points": [
        "T4",
        "T1"
      ]
    },
    {
      "points": [
        "O",
        "r"
      ]
    },
    {
      "points": [
        "p",
        "s1"
      ]
    },
    {
      "points": [
        "s1",
        "s2"
      ]
    },
    {
      "points": [
        "s2",
        "s3"
      ]
    },
    {
      "points": [
        "s3",
        "s4"
      ]
    },
    {
      "points": [
        "s4",
        "s5"
      ]
    },
    {
      "points": [
        "s5",
        "s6"
      ]
    },
    {
      "points": [
        "s6",
        "s7"
      ]
    },
    {
      "points": [
        "s7",
        "s8"
      ]
    },
    {
      "points": [
        "s8",
        "s9"
      ]
    },
    {
      "points": [
        "s9",
        "s10"
      ]
    },
    {
      "points": [
        "s10",
        "s11"
      ]
    },
    {
      "points": [
        "s11",
        "s12"
      ]
    },
    {
      "points": [
        "s12",
        "s13"
      ]
    },
    {
      "points": [
        "s13",
        "s14"
      ]
    },
    {
      "points": [
        "s14",
        "s15"
      ]
    },
    {
      "points": [
        "s15",
        "s16"
      ]
    },
    {
      "points": [
        "s16",
        "s17"
      ]
    },
    {
      "points": [
        "s17",
        "s18"
      ]
    },
    {
      "points": [
        "s18",
        "s19"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "h"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "h",
        "p"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "a0",
        "a1"
      ],
      "dash": true,
      "color": "#5f3dc4"
    },
    {
      "points": [
        "a1",
        "a2"
      ],
      "dash": true,
      "color": "#5f3dc4"
    },
    {
      "points": [
        "a2",
        "a3"
      ],
      "dash": true,
      "color": "#5f3dc4"
    },
    {
      "points": [
        "a3",
        "a0"
      ],
      "dash": true,
      "color": "#5f3dc4"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---------- 几何参数 ----------
5:     h = rng.uniform(1.6, 2.2)

13:     offset = rng.uniform(0.1, 0.25)
14: 
15:     # 保证截面圆向下延伸到圆台高度附近(与原始逻辑一致)
16:     if offset + oh * math.cos(theta) >= r_sec * math.sin(theta) - 0.05:
17:         oh = 0.25 * R
18:         r_sec = math.sqrt(R * R - oh * oh)
19:         offset = 0.1
30:     vs = {}
31: 
32:     # ---------- 圆台轴截面等腰梯形(z=0 平面,实线轮廓) ----------
33:     vs["T1"] = (-rb, 0.0, 0.0)
34:     vs["T2"] = (rb, 0.0, 0.0)
35:     vs["T3"] = (rt, h, 0.0)
36:     vs["T4"] = (-rt, h, 0.0)
37: 
38:     # ---------- 球心、半径标注点、垂足 ----------
39:     vs["O"] = O
40:     vs["r"] = (R, y_o, 0.0)
41:     vs["h"] = H
42: 
50:     vs["alpha"] = (H[0] + 0.2 * u[0] + 0.2 * v[0], H[1] + 0.2 * u[1] + 0.2 * v[1], H[2] + 0.2 * u[2] + 0.2 * v[2])
51: 
52:     # ---------- 截面圆(球 ∩ 平面 α)----------
53:     k = 24
54:     sec_pts = []
55:     for i in range(k):
56:         phi = 2 * math.pi * i / k
63: 
64:     # ---------- 相机(用于后续遮挡判断) ----------
65:     prims = [tk.prim_sphere(R, center=(0.0, y_o, 0.0))]
66:     cam = tk.auto_camera(vs, primitives=prims, azim=rng.uniform(35.0, 50.0), elev=rng.uniform(22.0, 32.0))
67:     cam_pos = tuple(cam["position"])
68: 
69:     # 判断点是否被圆台轴截面梯形遮挡(投影到 z=0 平面)
70:     def hidden_by_trapezoid(px, py, pz):
71:         cx, cy, cz = cam_pos
72:         # 相机与点必须在 z=0 两侧(点在相机“后方”才可能被遮挡)
73:         if cz * pz > 0:
87:         return x_left - 1e-6 <= ix <= x_right + 1e-6
88: 
89:     # ---------- 组装 edges ----------
90:     edges = []
91: 
92:     # 圆台轴截面等腰梯形轮廓(实线)
93:     edges.append(tk.edge("T1", "T2", dash=False))
94:     edges.append(tk.edge("T2", "T3", dash=False))
95:     edges.append(tk.edge("T3", "T4", dash=False))
96:     edges.append(tk.edge("T4", "T1", dash=False))
97: 
98:     # 球半径
99:     edges.append(tk.edge("O", "r", dash=False))
100: 
101:     # 截面圆:根据与圆台轴截面的遮挡关系分段设置虚线
102:     for i in range(k):
103:         a = sec_pts[i]
104:         b = sec_pts[(i + 1) % k]
105:         m = tk.midpoint(vs[a], vs[b])
106:         dash = hidden_by_trapezoid(m[0], m[1], m[2])
107:         edges.append(tk.edge(a, b, dash=dash))
108: 
109:     # ---------- 组装 aux_lines ----------
110:     aux_lines = []
111:     # 垂线段 OH 与截面圆半径 HP(实线)
112:     aux_lines.append(tk.aux("O", "h", dash=False, color="#d9480f"))
113:     aux_lines.append(tk.aux("h", "p", dash=False, color="#d9480f"))
114:     # 平面 α 示意平行四边形(虚线)
115:     aux_lines.append(tk.aux("a0", "a1", dash=True, color="#5f3dc4"))
116:     aux_lines.append(tk.aux("a1", "a2", dash=True, color="#5f3dc4"))
117:     aux_lines.append(tk.aux("a2", "a3", dash=True, color="#5f3dc4"))
118:     aux_lines.append(tk.aux("a3", "a0", dash=True, color="#5f3dc4"))
119: 
120:     # ---------- 组装 spec ----------
121:     spec = tk.make_spec(
122:         "球截面形状判断",
123:         "球-圆台组合体斜截面",
124:         vs,
125:         edges=edges,
126:         aux_lines=aux_lines,
127:         primitives=prims,
128:         labels=None,
129:         camera=cam,
130:     )
131:     spec["labels"] = [
132:         {"point": "O", "text": "O"},
133:         {"point": "p", "text": "P"},
134:         {"point": "h", "text": "H"},
135:         {"point": "r", "text": "R"},
138: 
139:     # ---------- 题目 ----------
140:     question = "如图,斜平面α截该球-圆台组合体并与球面相交形成一条封闭曲线,则该曲线的形状是( )"
141:     pool = [("圆", True), ("椭圆", False), ("一段圆弧", False), ("抛物线", False)]
142:     rng.shuffle(pool)
143:     letters = "ABCD"
144:     options = {}

K26 Spec-to-Image Review

  • major/frustum_render
    圆台仅由4条边组成z=0平面轴截面梯形,无上下底椭圆与母线,侧面视角退化为一条竖线。
    spec_fix: 使用frustum primitive或教材式上下底椭圆+母线绘制圆台立体轮廓,并启用遮挡判断。
  • major/plan_coverage
    plan要求球体下半部分被圆台上底面遮挡,但当前圆台无顶面/立体结构,球体完整无遮挡。
    spec_fix: 圆台增加上底面椭圆弧或采用frustum primitive,使renderer能正确遮挡球体下部轮廓。
  • minor/aux_line_clutter
    current视图中平面α示意平行四边形似一条粗橙线,未体现平面延展感。
    spec_fix: 增大平行四边形尺寸,或改用半透明面片表示平面α,避免与截面圆/垂线混淆。
  • minor/label_overlap
    oblique_plus45视角中α与H标签重叠,R标签偏离半径线段末端。
    spec_fix: alpha标签移至平行四边形外侧,R标签沿半径方向外移,增加标签深度偏移。
  • minor/camera_or_view
    补充视角oblique_plus45因圆台退化为线,未能展示截面圆与圆台关系。
    spec_fix: 移除该无效视角,待圆台立体化后补充俯视或仰视视角。
What To Change
  • 圆台由轴截面四边改为立体frustum或上下椭圆+母线
  • 平面α示意改用更大线框或半透明面片
  • 修正标签位置避免多视角重叠
Preferred Primitives
  • frustum
  • sphere
  • section_circle
Remove/Simplify
  • 移除纯轴截面梯形四边轮廓作为唯一圆台表示
  • 截面圆分段可保持16-24段
Label Fixes
  • alpha移至平行四边形外侧上方
  • R沿半径方向外移
  • 检查P/H/O深度冲突
Camera Fixes
  • 移除oblique_plus45,待圆台修复后补充俯视视角
Toolkit/Renderer: 新增frustum primitive支持,含上下底椭圆弧、母线及与球体的遮挡判断;支持半透明面片渲染平面示意。
representation: 球体使用primitive正确,但圆台仅用轴截面四边形代替立体轮廓,无上下底椭圆和母线,不符合教材题图规范。
readability: current视角尚可辨认但平面α似粗线;oblique_plus45视角圆台退化为线,完全失效。
plan: 缺失圆台对球体的立体遮挡表现,截面圆与圆台空间关系未有效传达。
next: toolkit_patch

cross_section_sphere__S014

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
一个球体被斜平面α截取。球心标记为O,截面圆圆心为H;作OH垂直于α,球内被遮挡段用虚线隐藏线,球外段为实投影线。截面圆背侧弧用虚线表示。平面α上绘有以H为中心的平行四边形示意平面范围,并画出两条对角线。球面上另绘一个过球心O的辅助大圆,其背侧弧为虚线。球外作一条辅助直线l与球相切于点T,l与平面α斜交。截面圆上取点P,连接HP与OP。
Must Render
[
  "球体轮廓及被遮挡的后半球虚线",
  "斜截面圆及其背侧虚线弧",
  "球心O到截面垂足H的垂线OH(含虚实分段)",
  "表示平面α的平行四边形及其两条对角线",
  "过球心的辅助大圆(背侧虚线)",
  "球外辅助切线l及其切点T"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "球-斜截面-多辅助线干扰",
  "camera": {
    "position": [
      13.473,
      10.509,
      12.838
    ],
    "target": [
      -0.099,
      0.536,
      0.477
    ],
    "orthoSize": 5.8
  },
  "counts": {
    "vertices": 109,
    "faces": 0,
    "edges": 100,
    "aux_lines": 8,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 4
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.3910952960675336,
      "position": [
        0.0,
        0.0,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "T",
      "text": "T"
    }
  ],
  "vertex_names": [
    "O",
    "H",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23",
    "C24",
    "C25",
    "C26",
    "C27",
    "C28",
    "C29",
    "C30",
    "C31",
    "C32",
    "C33",
    "C34",
    "C35",
    "C36",
    "C37",
    "C38",
    "C39",
    "C40",
    "C41",
    "C42",
    "C43",
    "C44",
    "C45",
    "C46",
    "C47",
    "Q0",
    "Q1",
    "Q2",
    "Q3",
    "Sb",
    "Sf",
    "Eb",
    "Ef",
    "G0",
    "G1",
    "G2",
    "G3",
    "G4",
    "G5",
    "G6",
    "G7",
    "G8",
    "G9",
    "G10",
    "G11",
    "G12",
    "G13",
    "G14",
    "G15",
    "G16",
    "G17",
    "G18",
    "G19",
    "G20",
    "G21",
    "G22",
    "G23",
    "G24",
    "G25",
    "G26",
    "G27",
    "G28",
    "G29",
    "G30",
    "G31",
    "G32",
    "G33",
    "G34",
    "G35",
    "G36",
    "G37",
    "G38",
    "G39",
    "G40",
    "G41",
    "G42",
    "G43",
    "G44",
    "G45",
    "G46",
    "G47",
    "T",
    "L",
    "P"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "C0",
        "C1"
      ],
      "dash": true
    },
    {
      "points": [
        "C1",
        "C2"
      ],
      "dash": true
    },
    {
      "points": [
        "C2",
        "C3"
      ],
      "dash": true
    },
    {
      "points": [
        "C3",
        "C4"
      ],
      "dash": true
    },
    {
      "points": [
        "C4",
        "C5"
      ],
      "dash": true
    },
    {
      "points": [
        "C5",
        "C6"
      ],
      "dash": true
    },
    {
      "points": [
        "C6",
        "C7"
      ],
      "dash": true
    },
    {
      "points": [
        "C7",
        "C8"
      ],
      "dash": true
    },
    {
      "points": [
        "C8",
        "C9"
      ],
      "dash": true
    },
    {
      "points": [
        "C9",
        "C10"
      ],
      "dash": true
    },
    {
      "points": [
        "C10",
        "C11"
      ],
      "dash": true
    },
    {
      "points": [
        "C11",
        "C12"
      ],
      "dash": true
    },
    {
      "points": [
        "C12",
        "C13"
      ],
      "dash": true
    },
    {
      "points": [
        "C13",
        "C14"
      ],
      "dash": true
    },
    {
      "points": [
        "C14",
        "C15"
      ],
      "dash": true
    },
    {
      "points": [
        "C15",
        "C16"
      ],
      "dash": true
    },
    {
      "points": [
        "C16",
        "C17"
      ],
      "dash": true
    },
    {
      "points": [
        "C17",
        "C18"
      ],
      "dash": true
    },
    {
      "points": [
        "C18",
        "C19"
      ],
      "dash": true
    },
    {
      "points": [
        "C19",
        "C20"
      ],
      "dash": true
    },
    {
      "points": [
        "C20",
        "C21"
      ],
      "dash": true
    },
    {
      "points": [
        "C21",
        "C22"
      ],
      "dash": true
    },
    {
      "points": [
        "C22",
        "C23"
      ],
      "dash": true
    },
    {
      "points": [
        "C23",
        "C24"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "Eb",
        "Sb"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "Sb",
        "Sf"
      ],
      "dash": true,
      "color": "#b45309"
    },
    {
      "points": [
        "Sf",
        "Ef"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "Q0",
        "Q2"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "Q1",
        "Q3"
      ],
      "dash": false,
      "color": "#888888"
    },
    {
      "points": [
        "T",
        "L"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "H",
        "P"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "O",
        "P"
      ],
      "dash": true,
      "color": "#555555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     R = rng.uniform(1.8, 2.5)
5:     d = rng.uniform(0.35, 0.75) * R

25: 
26:     # ---------- 1. 预先生成所有顶点 ----------
27:     # 截面圆
28:     cnames = []
29:     for i in range(N):
30:         a = 2 * math.pi * i / N
31:         p = tk.vadd(H, tk.vadd(tk.vmul(u, r * math.cos(a)), tk.vmul(v, r * math.sin(a))))
44:         qnames.append(k)
45: 
46:     # OH 垂线所在法线的端点(球外延长 + 球表面交点)
47:     Sb = tk.vmul(n, -R)
48:     Sf = tk.vmul(n, R)
49:     Eb = tk.vmul(n, -(R + 0.6))
50:     Ef = tk.vmul(n, R + 0.6)
54:     vs["Ef"] = Ef
55: 
56:     # 过球心的辅助大圆
57:     m = tk.unit((rng.uniform(-1, 1), rng.uniform(-1, 1), rng.uniform(-1, 1)))
58:     while abs(tk.dot(m, n)) > 0.9:
59:         m = tk.unit((rng.uniform(-1, 1), rng.uniform(-1, 1), rng.uniform(-1, 1)))
60:     t2 = (1.0, 0.0, 0.0) if abs(m[0]) < 0.9 else (0.0, 1.0, 0.0)
69:         gnames.append(k)
70: 
71:     # 球外辅助切线 l 及其切点 T
72:     ta = rng.uniform(0, 2 * math.pi)
73:     te = rng.uniform(-0.6, 0.6)
74:     T = (R * math.cos(te) * math.cos(ta), R * math.sin(te), R * math.cos(te) * math.sin(ta))
75:     vs["T"] = T
78:     e1 = tk.unit(tk.cross(OT, temp))
79:     e2 = tk.cross(OT, e1)
80:     # 确保切线 l 与平面 α 斜交(既不平行也不垂直)
81:     dl = None
82:     for _ in range(20):
83:         al = rng.uniform(0.2, 0.8) * math.pi
84:         dl = tk.unit(tk.vadd(tk.vmul(e1, math.cos(al)), tk.vmul(e2, math.sin(al))))
89:     vs["L"] = Lp
90: 
91:     # 截面圆上取点 P
92:     pa = rng.uniform(0, 2 * math.pi)
93:     P = tk.vadd(H, tk.vadd(tk.vmul(u, r * math.cos(pa)), tk.vmul(v, r * math.sin(pa))))
94:     vs["P"] = P
95: 
96:     # ---------- 2. 计算相机 ----------
97:     prims = [tk.prim_sphere(r=R, center=O)]
98:     cam = tk.auto_camera(vs, primitives=prims, azim=rng.uniform(25, 55), elev=rng.uniform(18, 35), pad=1.5)
99:     cam_pos = tuple(cam["position"])
100: 
101:     # ---------- 3. 根据相机方向生成 edges(背侧虚线) ----------
102:     def is_back(mid, center, cam_p):
103:         return tk.dot(tk.vsub(mid, center), tk.vsub(cam_p, center)) < 0
104: 
105:     # 截面圆:背侧弧虚线
106:     for i in range(N):
107:         i2 = (i + 1) % N
108:         mid = tk.midpoint(vs[cnames[i]], vs[cnames[i2]])
109:         es.append(tk.edge(cnames[i], cnames[i2], dash=is_back(mid, H, cam_pos)))
119:         es.append(tk.edge(gnames[i], gnames[i2], dash=is_back(mid, O, cam_pos)))
120: 
121:     # ---------- 4. 辅助线 aux_lines ----------
122:     # OH 垂线(法线):球外实线、球内虚线
123:     aux.append(tk.aux("Eb", "Sb", dash=False, color="#b45309"))
124:     aux.append(tk.aux("Sb", "Sf", dash=True, color="#b45309"))
125:     aux.append(tk.aux("Sf", "Ef", dash=False, color="#b45309"))
126: 
129:     aux.append(tk.aux(qnames[1], qnames[3], dash=False, color="#888888"))
130: 
131:     # 球外辅助切线 l
132:     aux.append(tk.aux("T", "L", dash=False, color="#d9480f"))
133: 
134:     # HP 与 OP
135:     aux.append(tk.aux("H", "P", dash=True, color="#555555"))
137: 
138:     # ---------- 5. 组装 spec ----------
139:     spec = tk.make_spec(
140:         "球截面形状判断",
141:         "球-斜截面-多辅助线干扰",
142:         vs,
143:         faces=[],
144:         edges=es,
145:         aux_lines=aux,
146:         primitives=prims,
147:         labels=["O", "H", "P", "T"],
148:         camera=cam,
149:     )
150: 
151:     # ---------- 6. 选项 ----------
152:     correct = "圆"
158:     options = {letters[i]: opts_list[i] for i in range(4)}
159:     answer = letters[opts_list.index(correct)]
160:     return tk.item(spec, "如图,平面 α 截球 O 所得截面的形状是( )", options, answer)

K26 Spec-to-Image Review

  • major/sphere_render
    sphere primitive 被渲染为实心浅蓝填充球,非教材空心线框风格,内部虚线被严重干扰
    spec_fix: toolkit 需支持 sphere 仅轮廓/线框渲染;spec 同步将圆离散数 N 从48降至16-24
  • major/mesh_clutter
    截面圆与大圆各48段共96条边,线网过密呈网状,丧失教材简洁感
    spec_fix: spec 中将截面圆与大圆顶点数减至16-24,降低离散密度
  • minor/aux_line_clutter
    OH延长线、切线l、对角线、HP/OP 等多色辅助线同屏,视觉略显杂乱
    spec_fix: 适当减淡非关键辅助线颜色或线宽,保持截面圆与OH主线突出
What To Change
  • 圆离散段数 N 从48改为16-24
  • 要求 sphere primitive 线框/轮廓渲染
Preferred Primitives
  • sphere(wireframe)
  • section_circle
Remove/Simplify
  • 减少截面圆与大圆冗余边
  • 简化球面线网
Label Fixes
  • 保持当前标签位置
Camera Fixes
  • 保持当前相机角度
Toolkit/Renderer: toolkit 的 sphere primitive 需支持仅绘制轮廓圆与可见弧,避免实心填充;或提供 wireframe 模式
representation: spec 结构完整但 sphere 渲染为实心填充且圆离散过密,严重偏离教材线框风格
readability: 单视角下几何关系可辨,但实心球与密线网导致视觉嘈杂,内部虚线可读性受损
plan: plan 要求的球、截面圆、OH、平面α、大圆、切线l、HP/OP 均已覆盖
next: toolkit_patch

cross_section_sphere__S015

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 球截面形状判断
难度 L3
Scene
画面中央偏上放置一个球体,球心标记为O,画出竖直轴截面大圆(实线)和水平大圆(虚线)。球体下方同轴紧密放置一个上底小下底大的圆台,圆台上底面与球体相交,圆台侧面遮挡球体下部。一个倾斜平面α(带剖面线)从左上向右下斜切组合体,平面α截球体得到一个小圆(截面圆),圆心O'为O在α上的垂足,用虚线连接OO'并标注垂足符号。从当前视角看,圆台遮挡了截面圆的下半段弧线。圆台被球遮挡的后侧母线及球体被圆台遮挡的下部轮廓均画为虚线。
Must Render
[
  "球体及其轴截面大圆",
  "上底小下底大的圆台(含被遮挡虚线母线)",
  "斜切平面α截球产生的截面圆(部分被圆台遮挡)",
  "球心O到截面圆心O'的垂线段OO'(虚线)"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "S015",
  "camera": {
    "position": [
      10.299,
      6.27,
      13.897
    ],
    "target": [
      -0.098,
      0.056,
      0.057
    ],
    "orthoSize": 4.826
  },
  "counts": {
    "vertices": 152,
    "faces": 13,
    "edges": 96,
    "aux_lines": 16,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 2
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.2,
      "position": [
        0,
        0,
        0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "Op",
      "text": "Op"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "O",
    "V0",
    "V1",
    "V2",
    "V3",
    "V4",
    "V5",
    "V6",
    "V7",
    "V8",
    "V9",
    "V10",
    "V11",
    "V12",
    "V13",
    "V14",
    "V15",
    "V16",
    "V17",
    "V18",
    "V19",
    "V20",
    "V21",
    "V22",
    "V23",
    "V24",
    "V25",
    "V26",
    "V27",
    "V28",
    "V29",
    "V30",
    "V31",
    "H0",
    "H1",
    "H2",
    "H3",
    "H4",
    "H5",
    "H6",
    "H7",
    "H8",
    "H9",
    "H10",
    "H11",
    "H12",
    "H13",
    "H14",
    "H15",
    "H16",
    "H17",
    "H18",
    "H19",
    "H20",
    "H21",
    "H22",
    "H23",
    "H24",
    "H25",
    "H26",
    "H27",
    "H28",
    "H29",
    "H30",
    "H31",
    "Op",
    "S0",
    "S1",
    "S2",
    "S3",
    "S4",
    "S5",
    "S6",
    "S7",
    "S8",
    "S9",
    "S10",
    "S11",
    "S12",
    "S13",
    "S14",
    "S15",
    "S16",
    "S17",
    "S18",
    "S19",
    "S20",
    "S21",
    "S22",
    "S23",
    "S24",
    "S25",
    "S26",
    "S27",
    "S28",
    "S29",
    "S30",
    "S31"
  ],
  "faces_sample": [
    {
      "points": [
        "K",
        "J",
        "I",
        "H",
        "G",
        "F",
        "E",
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "L",
        "M",
        "N",
        "P",
        "Q",
        "R",
        "S",
        "T",
        "U",
        "V",
        "W"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "M",
        "L"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "N",
        "M"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "P",
        "N"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "Q",
        "P"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "R",
        "Q"
      ]
    },
    {
      "points": [
        "F",
        "G",
        "S",
        "R"
      ]
    },
    {
      "points": [
        "G",
        "H",
        "T",
        "S"
      ]
    },
    {
      "points": [
        "H",
        "I",
        "U",
        "T"
      ]
    },
    {
      "points": [
        "I",
        "J",
        "V",
        "U"
      ]
    },
    {
      "points": [
        "J",
        "K",
        "W",
        "V"
      ]
    },
    {
      "points": [
        "K",
        "A",
        "L",
        "W"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "V0",
        "V1"
      ]
    },
    {
      "points": [
        "V1",
        "V2"
      ]
    },
    {
      "points": [
        "V2",
        "V3"
      ]
    },
    {
      "points": [
        "V3",
        "V4"
      ]
    },
    {
      "points": [
        "V4",
        "V5"
      ]
    },
    {
      "points": [
        "V5",
        "V6"
      ]
    },
    {
      "points": [
        "V6",
        "V7"
      ]
    },
    {
      "points": [
        "V7",
        "V8"
      ]
    },
    {
      "points": [
        "V8",
        "V9"
      ]
    },
    {
      "points": [
        "V9",
        "V10"
      ]
    },
    {
      "points": [
        "V10",
        "V11"
      ]
    },
    {
      "points": [
        "V11",
        "V12"
      ]
    },
    {
      "points": [
        "V12",
        "V13"
      ]
    },
    {
      "points": [
        "V13",
        "V14"
      ]
    },
    {
      "points": [
        "V14",
        "V15"
      ]
    },
    {
      "points": [
        "V15",
        "V16"
      ]
    },
    {
      "points": [
        "V16",
        "V17"
      ]
    },
    {
      "points": [
        "V17",
        "V18"
      ]
    },
    {
      "points": [
        "V18",
        "V19"
      ]
    },
    {
      "points": [
        "V19",
        "V20"
      ]
    },
    {
      "points": [
        "V20",
        "V21"
      ]
    },
    {
      "points": [
        "V21",
        "V22"
      ]
    },
    {
      "points": [
        "V22",
        "V23"
      ]
    },
    {
      "points": [
        "V23",
        "V24"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "Op"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "Op",
        "Q1"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "Op",
        "Q2"
      ],
      "dash": false,
      "color": "#333333"
    },
    {
      "points": [
        "a-61",
        "a-62"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a-51",
        "a-52"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a-41",
        "a-42"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a-31",
        "a-32"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a-21",
        "a-22"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a-11",
        "a-12"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a01",
        "a02"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a11",
        "a12"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a21",
        "a22"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a31",
        "a32"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a41",
        "a42"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a51",
        "a52"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "a61",
        "a62"
      ],
      "dash": false,
      "color": "#999999"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "P1",
        "P2",
        "P3",
        "P4"
      ],
      "color": "#e5e5e5",
      "opacity": 0.35
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.choice([1.0, 1.2, 1.4])
4:     n = rng.choice([10, 11, 12])
5:     d = R * rng.uniform(0.3, 0.5)

7:     rb = rt * rng.uniform(1.5, 2.0)
8:     h = R * rng.uniform(1.0, 1.6)
9:     vs, faces = tk.frustum(n, rb, rt, h)
10:     vs = tk.translate_all(vs, (0.0, -d - h, 0.0))
11:     vs["O"] = (0.0, 0.0, 0.0)
12:     m = 32
13: 
25:         return [tk.edge(ns[i], ns[(i + 1) % m], dash=dash) for i in range(m)]
26: 
27:     edges = circ("V", (0.0, 0.0, 0.0), R, (0.0, 0.0, 1.0), False)
28:     edges += circ("H", (0.0, 0.0, 0.0), R, (0.0, 1.0, 0.0), True)
29: 
30:     p = R * rng.uniform(0.3, 0.5)
31:     nv = tk.unit((rng.uniform(-0.6, -0.3), rng.uniform(0.6, 0.8), rng.uniform(0.1, 0.4)))
32:     Op = tk.vmul(nv, p)
33:     vs["Op"] = tuple(round(x, 6) for x in Op)
34:     rs = math.sqrt(R * R - p * p)
35:     edges += circ("S", Op, rs, nv, False)
36: 
37:     t = (0.0, 1.0, 0.0) if abs(nv[1]) < 0.9 else (0.0, 0.0, 1.0)
38:     u = tk.unit(tk.cross(nv, t))
39:     v = tk.unit(tk.cross(nv, u))
45:         pns.append(nm)
46: 
47:     aux_faces = [tk.face(pns, color="#e5e5e5", opacity=0.35)]
48:     aux_lines = [tk.aux("O", "Op", dash=True)]
49:     pl = 0.12
50:     vs["Q1"] = tuple(round(x, 6) for x in tk.vadd(Op, tk.vmul(u, pl)))
51:     vs["Q2"] = tuple(round(x, 6) for x in tk.vadd(Op, tk.vmul(nv, pl)))
52:     aux_lines += [tk.aux("Op", "Q1", dash=False, color="#333333"),
53:                   tk.aux("Op", "Q2", dash=False, color="#333333")]
54: 
55:     for i in range(-6, 7):
56:         t = i * sc / 6.0
60:         vs[n1] = tuple(round(x, 6) for x in a)
61:         vs[n2] = tuple(round(x, 6) for x in b)
62:         aux_lines.append(tk.aux(n1, n2, dash=False, color="#999999"))
63: 
64:     cam = tk.auto_camera(vs, azim=rng.uniform(28, 38), elev=rng.uniform(18, 26))
65:     spec = tk.make_spec("球截面形状判断", "S015", vs, faces=faces, edges=edges,
66:                         aux_lines=aux_lines, aux_faces=aux_faces,
67:                         primitives=[tk.prim_sphere(R, center=(0, 0, 0))],
68:                         labels=["O", "Op"], camera=cam)
69:     opts = ["圆", "椭圆", "圆弧与线段组合", "抛物线"]
70:     rng.shuffle(opts)
71:     ans = "ABCD"[opts.index("圆")]
72:     return tk.item(spec, "如图,平面α截由球与圆台组成的组合体。平面α与球面相交所形成的封闭曲线是什么形状?",
73:                    {"A": opts[0], "B": opts[1], "C": opts[2], "D": opts[3]}, ans)

K26 Spec-to-Image Review

  • fatal/frustum_render
    圆台被tk.frustum离散为10-12边形棱台,faces_sample显示一圈梯形侧面网格,完全不像圆台。
    spec_fix: 改用frustum primitive;若toolkit不支持,则改为教材式上下椭圆+可见母线,删除侧面多边形mesh。
  • major/aux_line_clutter
    平面α用13条粗平行线表示,渲染为密集橙色斜线,严重遮挡球体与圆台。
    spec_fix: 将剖面线减至3-5条细灰虚线,或改用半透明剖面填充,避免遮挡主体。
  • minor/label_missing
    标签为Op,plan要求标注O';垂足符号Q1Q2过小难以辨识。
    spec_fix: 标签text改为O',增大垂足符号长度或改用angle_arc primitive标记直角。
  • minor/mesh_clutter
    球体竖直大圆(V系列)为完整实线圆,未将被圆台遮挡的下部轮廓改为虚线。
    spec_fix: 根据视角将球体下部被圆台遮挡的轮廓段设为dash=true,或只保留可见上半弧。
What To Change
  • 圆台改用frustum primitive或上下椭圆+母线
  • 平面α剖面线减至3-5条细线
  • 修正O'标签
Preferred Primitives
  • frustum
  • sphere
  • angle_arc
Remove/Simplify
  • 删除圆台侧面梯形mesh
  • 删除多余α平行线
Label Fixes
  • Op改为O'
  • 垂足符号加大或使用angle_arc
Camera Fixes
  • 保持current视角,确保圆台底部可见
Toolkit/Renderer: toolkit需支持frustum primitive以替代多边形mesh;renderer应支持细线宽剖面线渲染。
representation: 球体primitive正确,但圆台为低多边形棱台,平面α线条过密过粗,整体严重失真。
readability: 圆台呈棱台状,橙色剖面线密集遮挡,球与圆台空间关系及截面圆难以辨认。
plan: 元素齐全但几何表示错误,核心对象圆台不像,遮挡关系未清晰表达。
next: toolkit_patch

cross_section_sphere__S013

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 5 major_spec_issue minor hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
一个球心标记为 O 的透明球体,被倾斜平面 α(以带网格的平行四边形示意)斜向截取。平面与球面相交形成一条封闭的粗实线截面边界。从球心 O 向平面 α 作垂线段,垂足标记为 O'(截面圆心),垂线旁标注球心到截面距离 d。在过 OO' 的轴截面上,画出球的大圆(虚线)并标注球半径 R;在截面圆上取一点 A,连接 O'A(截面半径 r)与 OA,形成直角三角形 OO'A。被平面遮挡的球体下半部轮廓用虚线表示。
Must Render
[
  "球体轮廓及球心 O",
  "截面平面 α 与封闭交线(粗实线)",
  "垂线段 OO' 及截面圆心 O'",
  "轴截面大圆(虚线)",
  "直角三角形 OO'A 及半径标注 r、R"
]

Spec Summary

{
  "title": "球的截面",
  "meta": "球截面形状判断",
  "camera": {
    "position": [
      7.8,
      7.163,
      12.343
    ],
    "target": [
      0.002,
      0.247,
      0.379
    ],
    "orthoSize": 4.137
  },
  "counts": {
    "vertices": 91,
    "faces": 1,
    "edges": 35,
    "aux_lines": 58,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 7
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.7377687406100193,
      "position": [
        0.0,
        0.0,
        0.0
      ],
      "color": "#e8f4f8"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "O'",
      "text": "O'"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "d",
      "text": "d"
    },
    {
      "point": "R",
      "text": "R"
    },
    {
      "point": "r",
      "text": "r"
    },
    {
      "point": "α",
      "text": "α"
    }
  ],
  "vertex_names": [
    "O",
    "O'",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23",
    "C24",
    "C25",
    "C26",
    "C27",
    "C28",
    "C29",
    "C30",
    "C31",
    "B0",
    "B1",
    "B2",
    "B3",
    "B4",
    "B5",
    "B6",
    "B7",
    "B8",
    "B9",
    "B10",
    "B11",
    "B12",
    "B13",
    "B14",
    "B15",
    "B16",
    "B17",
    "B18",
    "B19",
    "B20",
    "B21",
    "B22",
    "B23",
    "B24",
    "B25",
    "B26",
    "B27",
    "B28",
    "B29",
    "B30",
    "B31",
    "B32",
    "B33",
    "B34",
    "B35",
    "B36",
    "B37",
    "B38",
    "B39",
    "B40",
    "B41",
    "B42",
    "B43",
    "B44",
    "B45",
    "B46",
    "B47",
    "A",
    "P0",
    "P1",
    "P2",
    "P3",
    "d",
    "R",
    "r",
    "α"
  ],
  "faces_sample": [
    {
      "points": [
        "P0",
        "P1",
        "P2",
        "P3"
      ],
      "color": "#f0f0f0",
      "opacity": 0.25
    }
  ],
  "edges_sample": [
    {
      "points": [
        "C0",
        "C1"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C1",
        "C2"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C2",
        "C3"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C3",
        "C4"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C4",
        "C5"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C5",
        "C6"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C6",
        "C7"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C7",
        "C8"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C8",
        "C9"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C9",
        "C10"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C10",
        "C11"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C11",
        "C12"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C12",
        "C13"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C13",
        "C14"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C14",
        "C15"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C15",
        "C16"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C16",
        "C17"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C17",
        "C18"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C18",
        "C19"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C19",
        "C20"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C20",
        "C21"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C21",
        "C22"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C22",
        "C23"
      ],
      "color": "#111111"
    },
    {
      "points": [
        "C23",
        "C24"
      ],
      "color": "#111111"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "B0",
        "B1"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B1",
        "B2"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B2",
        "B3"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B3",
        "B4"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B4",
        "B5"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B5",
        "B6"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B6",
        "B7"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B7",
        "B8"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B8",
        "B9"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B9",
        "B10"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B10",
        "B11"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B11",
        "B12"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B12",
        "B13"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B13",
        "B14"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B14",
        "B15"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B15",
        "B16"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B16",
        "B17"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B17",
        "B18"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B18",
        "B19"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B19",
        "B20"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B20",
        "B21"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B21",
        "B22"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B22",
        "B23"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B23",
        "B24"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B24",
        "B25"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B25",
        "B26"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B26",
        "B27"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B27",
        "B28"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B28",
        "B29"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B29",
        "B30"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B30",
        "B31"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B31",
        "B32"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B32",
        "B33"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B33",
        "B34"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B34",
        "B35"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B35",
        "B36"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B36",
        "B37"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B37",
        "B38"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B38",
        "B39"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B39",
        "B40"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B40",
        "B41"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B41",
        "B42"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B42",
        "B43"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B43",
        "B44"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B44",
        "B45"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B45",
        "B46"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B46",
        "B47"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "B47",
        "B0"
      ],
      "dash": true,
      "color": "#666666"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.uniform(1.4, 1.8)
4:     d = rng.uniform(0.35, 0.65) * R
5:     r = math.sqrt(R * R - d * d)

40:     vs["r"] = tk.vadd(O_prime, tk.vmul(u, r * 0.5))
41:     vs["α"] = tk.vadd(vs["P2"], tk.vmul(v, 0.15 * s))
42:     edges = []
43:     for i in range(n_seg):
44:         edges.append(tk.edge(sec_pts[i], sec_pts[(i + 1) % n_seg], color="#111111"))
45:     aux_lines = []
46:     for i in range(n_big):
47:         aux_lines.append(tk.aux(big_pts[i], big_pts[(i + 1) % n_big], dash=True, color="#666666"))
48:     for i in range(-2, 3):
49:         t = i * s / 2.5
50:         aux_lines.append(tk.aux(tk.vadd(O_prime, tk.vadd(tk.vmul(u, t), tk.vmul(v, -s))),
51:                                 tk.vadd(O_prime, tk.vadd(tk.vmul(u, t), tk.vmul(v, s))),
52:                                 dash=False, color="#bbbbbb"))
53:         aux_lines.append(tk.aux(tk.vadd(O_prime, tk.vadd(tk.vmul(u, -s), tk.vmul(v, t))),
54:                                 tk.vadd(O_prime, tk.vadd(tk.vmul(u, s), tk.vmul(v, t))),
55:                                 dash=False, color="#bbbbbb"))
56:     edges.append(tk.edge("O", "O'", color="#000000"))
57:     edges.append(tk.edge("O'", "A", color="#000000"))
58:     edges.append(tk.edge("O", "A", color="#000000"))
59:     faces = [{"points": ["P0", "P1", "P2", "P3"], "color": "#f0f0f0", "opacity": 0.25}]
60:     primitives = [tk.prim_sphere(r=R, center=O, color="#e8f4f8")]
61:     labels = ["O", "O'", "A", "d", "R", "r", "α"]
62:     camera = tk.auto_camera(vs, primitives=primitives, azim=rng.uniform(25, 45), elev=rng.uniform(18, 28))
63:     spec = tk.make_spec("球的截面", "球截面形状判断", vs, faces=faces, edges=edges,
64:                         aux_lines=aux_lines, primitives=primitives, labels=labels, camera=camera)
65:     opts = ["圆", "椭圆", "大圆", "抛物线"]
66:     rng.shuffle(opts)
67:     options = {("A", "B", "C", "D")[i]: opts[i] for i in range(4)}
68:     ans = [k for k, v in options.items() if v == "圆"][0]
69:     return tk.item(spec, "如图,平面 α 截球 O 所得的截面形状是( )", options, ans)

K26 Spec-to-Image Review

  • major/label_overlap
    d、R 标签与 O、O' 在球心区域严重重叠,r 亦拥挤,无法分辨各标注对应线段
    spec_fix: 在 vs 中显式定义 d、R 的独立坐标:d 置于 OO' 中点下方偏移,R 置于 OA 中点外侧,避免与顶点重合
  • minor/label_position
    α 标签漂浮在右上角,远离平面示意区,易被误读为无关点
    spec_fix: 将 α 标注锚点移至平行四边形面 P0-P1-P2-P3 的右侧边缘中部附近
What To Change
  • 为 d、R 在 vs 字典中显式赋予独立坐标
  • 微调 α 的标注锚点至平面面片边缘
Preferred Primitives
  • sphere
Remove/Simplify
  • none
Label Fixes
  • d 放在 OO' 中点偏下
  • R 放在 OA 中点偏右
  • α 移至平面右侧边缘
Camera Fixes
  • none
Toolkit/Renderer: none
representation: 球体使用 sphere primitive,截面圆与轴截面大圆分别用 edges 与 aux_lines 构造,几何结构正确
readability: 标签严重重叠导致核心几何量 d、R、r 不可读,极大影响题图教学可用性
plan: plan 要求的球、截面、垂线、轴截面大圆、直角三角形均已画出,仅标注布局失败
next: spec_rewrite

cross_section_sphere__S016

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 5 major_spec_issue minor hold

Plan / Must Render

题型 球截面形状判断
难度 L3
Scene
图中主体为一个透明球体,球心标记为 O,被倾斜平面 α 截得截面圆 Γ(实线绘制)。从 O 向 α 作垂线,垂足 H 为截面圆圆心,垂线 OH 与截面半径 HA、球半径 OA 均用虚线标出。球的正下方辅助画出该截面圆在竖直投影面上的正投影,呈椭圆轮廓(虚线)。在球体右后方用细虚线增设一个圆柱被斜截的相似构型,其截面画为椭圆,作为视觉干扰。所有隐藏边与辅助线均用虚线区分。
Must Render
[
  "球体与倾斜平面 α 的交线截面圆 Γ(实线)",
  "球心 O 到平面 α 的垂线 OH 及垂足 H(虚线)",
  "截面圆在投影面上的椭圆投影(虚线)",
  "右后方圆柱斜截椭圆干扰构型(细虚线)"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "S016",
  "camera": {
    "position": [
      10.534,
      6.075,
      13.318
    ],
    "target": [
      1.698,
      -0.325,
      -0.822
    ],
    "orthoSize": 4.681
  },
  "counts": {
    "vertices": 71,
    "faces": 0,
    "edges": 24,
    "aux_lines": 59,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 3
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.6,
      "position": [
        0,
        0,
        0
      ],
      "color": "#e8e8e8"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "A",
      "text": "A"
    }
  ],
  "vertex_names": [
    "O",
    "H",
    "A",
    "g0",
    "g1",
    "g2",
    "g3",
    "g4",
    "g5",
    "g6",
    "g7",
    "g8",
    "g9",
    "g10",
    "g11",
    "g12",
    "g13",
    "g14",
    "g15",
    "g16",
    "g17",
    "g18",
    "g19",
    "g20",
    "g21",
    "g22",
    "g23",
    "p0",
    "p1",
    "p2",
    "p3",
    "p4",
    "p5",
    "p6",
    "p7",
    "p8",
    "p9",
    "p10",
    "p11",
    "p12",
    "p13",
    "p14",
    "p15",
    "p16",
    "p17",
    "p18",
    "p19",
    "b0",
    "t0",
    "b1",
    "t1",
    "b2",
    "t2",
    "b3",
    "t3",
    "b4",
    "t4",
    "b5",
    "t5",
    "b6",
    "t6",
    "b7",
    "t7",
    "b8",
    "t8",
    "b9",
    "t9",
    "b10",
    "t10",
    "b11",
    "t11"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "g0",
        "g1"
      ]
    },
    {
      "points": [
        "g1",
        "g2"
      ]
    },
    {
      "points": [
        "g2",
        "g3"
      ]
    },
    {
      "points": [
        "g3",
        "g4"
      ]
    },
    {
      "points": [
        "g4",
        "g5"
      ]
    },
    {
      "points": [
        "g5",
        "g6"
      ]
    },
    {
      "points": [
        "g6",
        "g7"
      ]
    },
    {
      "points": [
        "g7",
        "g8"
      ]
    },
    {
      "points": [
        "g8",
        "g9"
      ]
    },
    {
      "points": [
        "g9",
        "g10"
      ]
    },
    {
      "points": [
        "g10",
        "g11"
      ]
    },
    {
      "points": [
        "g11",
        "g12"
      ]
    },
    {
      "points": [
        "g12",
        "g13"
      ]
    },
    {
      "points": [
        "g13",
        "g14"
      ]
    },
    {
      "points": [
        "g14",
        "g15"
      ]
    },
    {
      "points": [
        "g15",
        "g16"
      ]
    },
    {
      "points": [
        "g16",
        "g17"
      ]
    },
    {
      "points": [
        "g17",
        "g18"
      ]
    },
    {
      "points": [
        "g18",
        "g19"
      ]
    },
    {
      "points": [
        "g19",
        "g20"
      ]
    },
    {
      "points": [
        "g20",
        "g21"
      ]
    },
    {
      "points": [
        "g21",
        "g22"
      ]
    },
    {
      "points": [
        "g22",
        "g23"
      ]
    },
    {
      "points": [
        "g23",
        "g0"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "H",
        "A"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "A"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "p0",
        "p1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p1",
        "p2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p2",
        "p3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p3",
        "p4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p4",
        "p5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p5",
        "p6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p6",
        "p7"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p7",
        "p8"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p8",
        "p9"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p9",
        "p10"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p10",
        "p11"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p11",
        "p12"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p12",
        "p13"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p13",
        "p14"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p14",
        "p15"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p15",
        "p16"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p16",
        "p17"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p17",
        "p18"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p18",
        "p19"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "p19",
        "p0"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "b0",
        "b1"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t0",
        "t1"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b0",
        "t0"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b1",
        "b2"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t1",
        "t2"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b1",
        "t1"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b2",
        "b3"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t2",
        "t3"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b2",
        "t2"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b3",
        "b4"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t3",
        "t4"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b3",
        "t3"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b4",
        "b5"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t4",
        "t5"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b4",
        "t4"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b5",
        "b6"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t5",
        "t6"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b5",
        "t5"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b6",
        "b7"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t6",
        "t7"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b6",
        "t6"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b7",
        "b8"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "t7",
        "t8"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b7",
        "t7"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "b8",
        "b9"
      ],
      "dash": true,
      "color": "#a0a0a0"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 球参数
4:     R = float(rng.randint(20, 26)) / 10.0
5:     theta = float(rng.randint(35, 55))

6:     d = rng.uniform(R * 0.35, R * 0.65)
7:     r = math.sqrt(R * R - d * d)
9:     nx, ny = math.sin(tr), math.cos(tr)
10: 
11:     # 核心点:O 球心,H 垂足,A 截面圆上一点
12:     O = (0.0, 0.0, 0.0)
13:     H = (d * nx, d * ny, 0.0)
14:     e1 = (ny, -nx, 0.0)
15:     e2 = (0.0, 0.0, 1.0)
17: 
18:     vs = {"O": O, "H": H, "A": A}
19:     edges, auxs = [], []
20: 
21:     # 截面圆 Γ(实线,24 边形近似)
22:     nc = 24
23:     cns = []
24:     for i in range(nc):
25:         phi = 2.0 * math.pi * i / nc
31:         cns.append(nm)
32:     for i in range(nc):
33:         edges.append(tk.edge(cns[i], cns[(i + 1) % nc]))
34: 
35:     # OH、HA、OA 虚线
36:     auxs += [tk.aux("O", "H"), tk.aux("H", "A"), tk.aux("O", "A")]
37: 
38:     # 下方投影椭圆(虚线,水平面 y = -R - 0.8)
39:     yp = -R - 0.8
40:     ne = 20
41:     ens = []
42:     for i in range(ne):
73:         auxs.append(tk.aux(bns[i], tns[i], dash=True, color="#a0a0a0"))
74: 
75:     prims = [tk.prim_sphere(r=R, center=(0, 0, 0), color="#e8e8e8")]
76:     spec = tk.make_spec("球截面形状判断", "S016", vs, faces=[], edges=edges,
77:                         aux_lines=auxs, primitives=prims, labels=["O", "H", "A"])
78: 
79:     q = "如图,平面 α 与球 O 相交,图中同时给出了该截面在竖直方向上的投影(下方虚线椭圆)以及一个圆柱斜截面的相似构型(右后方虚线)。关于平面 α 截球所得截面的真实形状与位置,下列说法正确的是?"
80:     opts = [
81:         "平面 α 截球所得截面一定是圆,其圆心为球心 O 在平面 α 上的垂足 H",
82:         "平面 α 截球所得截面是椭圆,其中心为垂足 H",
83:         "平面 α 截球所得截面与右侧圆柱斜截面形状相同,均为椭圆",
84:         "不过球心的平面截球得到椭圆,只有过球心的平面截球才得到圆"
85:     ]
86:     letters = ["A", "B", "C", "D"]
87:     rng.shuffle(opts)
88:     options = {letters[i]: opts[i] for i in range(4)}
89:     ans = letters[opts.index("平面 α 截球所得截面一定是圆,其圆心为球心 O 在平面 α 上的垂足 H")]
90:     return tk.item(spec, q, options, ans,
91:                    answer_text="平面 α 截球所得截面一定是圆,其圆心为球心 O 在平面 α 上的垂足 H")

K26 Spec-to-Image Review

  • major/cone_cylinder_render
    右后方圆柱由36条aux_lines拼成密集线框,未使用cylinder primitive
    spec_fix: 改用教材式上下椭圆+3条母线表示,或等待toolkit添加cylinder primitive
  • minor/sphere_render
    sphere primitive自带多余虚线大圆,与plan只保留必要截面圆冲突
    spec_fix: toolkit层控制sphere primitive不自动绘制网格/大圆
  • minor/shape_primitive
    截面圆Γ以24边形近似,渲染折角可见
    spec_fix: 增至36-48段或添加section_circle primitive
  • minor/label_missing
    题文与plan中的平面α未在图中标注
    spec_fix: 在截面圆附近添加label α
What To Change
  • 圆柱干扰构型改为上下椭圆+母线
  • 截面圆分段增至36以上
  • 补充平面α标注
Preferred Primitives
  • section_circle
  • cylinder
Remove/Simplify
  • 移除圆柱12边形底面与顶面的全部弦线,仅保留椭圆轮廓与2-3条母线
Label Fixes
  • 添加平面α标注于截面圆旁
Camera Fixes
  • none
Toolkit/Renderer: 建议toolkit增加cylinder primitive并支持关闭sphere默认网格线
representation: 球体primitive正确但自带多余大圆;圆柱干扰项过度线框化;截面圆分段略显不足
readability: 主视图能表达球截面与投影,但右后方圆柱线框过密,存在多余虚线大圆
plan: 缺少平面α标注,其余关键元素OH、HA、OA、截面圆、投影椭圆均已呈现
next: visual_repair

cross_section_sphere__S018

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 5 major_spec_issue minor hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
画一球体,球心 O。斜平面 α 截球得截面圆 Γ(圆心 H)。α 用平行四边形示意,截面交线前实后虚。过 O 作 OH⊥α 于 H,OH 为投影线;延长交球于 P、Q,HQ 段虚线。在 Γ 上作弦 AB。作竖直辅助轴截面 β,与球交于大圆、与 α 交于直线 l,大圆与 Γ 交于 C、D,连公共弦 CD。在 α 内过 H 作 45° 辅助线 m 交 Γ 于 E、F。所有被遮挡的边界、延长线均画虚线。
Must Render
[
  "球体轮廓及表面",
  "斜平面 α 与球面的截面交线(可见实线、隐藏虚线)",
  "球心 O 到截面圆心 H 的垂线段 OH(投影线)",
  "截面圆内的弦 AB、公共弦 CD、过圆心的辅助线 m",
  "竖直轴截面大圆及被球遮挡的后半虚线弧"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "S018",
  "camera": {
    "position": [
      8.501,
      9.712,
      12.578
    ],
    "target": [
      -0.451,
      0.568,
      0.332
    ],
    "orthoSize": 4.882
  },
  "counts": {
    "vertices": 62,
    "faces": 1,
    "edges": 52,
    "aux_lines": 5,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 8
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.0,
      "position": [
        0.0,
        0.0,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    }
  ],
  "vertex_names": [
    "O",
    "H",
    "P",
    "Q",
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "S0",
    "S1",
    "S2",
    "S3",
    "G0",
    "G1",
    "G2",
    "G3",
    "G4",
    "G5",
    "G6",
    "G7",
    "G8",
    "G9",
    "G10",
    "G11",
    "G12",
    "G13",
    "G14",
    "G15",
    "G16",
    "G17",
    "G18",
    "G19",
    "G20",
    "G21",
    "G22",
    "G23",
    "B0",
    "B1",
    "B2",
    "B3",
    "B4",
    "B5",
    "B6",
    "B7",
    "B8",
    "B9",
    "B10",
    "B11",
    "B12",
    "B13",
    "B14",
    "B15",
    "B16",
    "B17",
    "B18",
    "B19",
    "B20",
    "B21",
    "B22",
    "B23"
  ],
  "faces_sample": [
    {
      "points": [
        "S0",
        "S1",
        "S2",
        "S3"
      ],
      "color": "#f5f5f4",
      "opacity": 0.12
    }
  ],
  "edges_sample": [
    {
      "points": [
        "G0",
        "G1"
      ]
    },
    {
      "points": [
        "G1",
        "G2"
      ]
    },
    {
      "points": [
        "G2",
        "G3"
      ]
    },
    {
      "points": [
        "G3",
        "G4"
      ]
    },
    {
      "points": [
        "G4",
        "G5"
      ],
      "dash": true
    },
    {
      "points": [
        "G5",
        "G6"
      ],
      "dash": true
    },
    {
      "points": [
        "G6",
        "G7"
      ],
      "dash": true
    },
    {
      "points": [
        "G7",
        "G8"
      ],
      "dash": true
    },
    {
      "points": [
        "G8",
        "G9"
      ],
      "dash": true
    },
    {
      "points": [
        "G9",
        "G10"
      ],
      "dash": true
    },
    {
      "points": [
        "G10",
        "G11"
      ],
      "dash": true
    },
    {
      "points": [
        "G11",
        "G12"
      ],
      "dash": true
    },
    {
      "points": [
        "G12",
        "G13"
      ],
      "dash": true
    },
    {
      "points": [
        "G13",
        "G14"
      ],
      "dash": true
    },
    {
      "points": [
        "G14",
        "G15"
      ],
      "dash": true
    },
    {
      "points": [
        "G15",
        "G16"
      ]
    },
    {
      "points": [
        "G16",
        "G17"
      ]
    },
    {
      "points": [
        "G17",
        "G18"
      ]
    },
    {
      "points": [
        "G18",
        "G19"
      ]
    },
    {
      "points": [
        "G19",
        "G20"
      ]
    },
    {
      "points": [
        "G20",
        "G21"
      ]
    },
    {
      "points": [
        "G21",
        "G22"
      ]
    },
    {
      "points": [
        "G22",
        "G23"
      ]
    },
    {
      "points": [
        "G23",
        "G0"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "H",
        "Q"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "C",
        "D"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "E",
        "F"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.choice([1.6, 2.0, 2.4])
4:     d = R * rng.uniform(0.35, 0.65)
5:     r = math.sqrt(R * R - d * d)

55:     vs = {"O": O, "H": H, "P": P, "Q": Q, "A": A, "B": B, "C": C, "D": D, "E": E, "F": F,
56:           "S0": S0, "S1": S1, "S2": S2, "S3": S3}
57:     cam = tk.auto_camera(vs, primitives=[tk.prim_sphere(R, O)], azim=rng.uniform(28, 42),
58:                          elev=rng.uniform(22, 32), pad=1.5)
59:     cp = tuple(cam["position"])
60: 
61:     def vis(X):
63: 
64:     N = 24
65:     edges = []
66:     for i in range(N):
67:         ang = 2 * math.pi * i / N
68:         vs[f"G{i}"] = tk.vadd(H, tk.vadd(tk.vmul(u, r * math.cos(ang)), tk.vmul(v, r * math.sin(ang))))
69:     for i in range(N):
70:         ang = 2 * math.pi * (i + 0.5) / N
71:         Xm = tk.vadd(H, tk.vadd(tk.vmul(u, r * math.cos(ang)), tk.vmul(v, r * math.sin(ang))))
72:         edges.append(tk.edge(f"G{i}", f"G{(i + 1) % N}", dash=not vis(Xm)))
73:     for i in range(N):
74:         ang = 2 * math.pi * i / N
75:         vs[f"B{i}"] = tk.vadd(O, tk.vadd(tk.vmul(ub, R * math.cos(ang)), tk.vmul(vb, R * math.sin(ang))))
76:     for i in range(N):
77:         ang = 2 * math.pi * (i + 0.5) / N
78:         Xm = tk.vadd(O, tk.vadd(tk.vmul(ub, R * math.cos(ang)), tk.vmul(vb, R * math.sin(ang))))
79:         edges.append(tk.edge(f"B{i}", f"B{(i + 1) % N}", dash=not vis(Xm)))
80:     for i in range(4):
81:         edges.append(tk.edge(f"S{i}", f"S{(i + 1) % 4}"))
82:     aux = [tk.aux("O", "H", dash=True), tk.aux("H", "Q", dash=True),
83:            tk.aux("A", "B", dash=True), tk.aux("C", "D", dash=True),
84:            tk.aux("E", "F", dash=True)]
85:     faces = [tk.face(["S0", "S1", "S2", "S3"], opacity=0.12)]
86:     spec = tk.make_spec("球截面形状判断", "S018", vs, faces=faces, edges=edges,
87:                         aux_lines=aux, primitives=[tk.prim_sphere(R, O)],
88:                         labels=["O", "H", "A", "B", "C", "D", "E", "F"], camera=cam)
89:     pool = ["圆", "椭圆", "抛物线", "不规则曲线"]
90:     rng.shuffle(pool)
91:     opts = {"A": pool[0], "B": pool[1], "C": pool[2], "D": pool[3]}
92:     ans = [k for k, v in opts.items() if v == "圆"][0]
93:     return tk.item(spec, "如图,平面 α 与球面相交所形成的截面的形状是( )", opts, ans)

K26 Spec-to-Image Review

  • major/label_overlap
    标签 B、C、F 在左下角几乎重叠,无法分辨各点具体位置
    spec_fix: 重新分布弦 AB、公共弦 CD、辅助线 EF 的端点角度,使标签分散在不同象限;或启用标签防重叠偏移
  • major/plan_coverage
    平面 α 的平行四边形边界 S0-S3 被球体遮挡部分仍画实线,未按 plan 要求画虚线
    spec_fix: 对 S0-S3 各边中点做可见性判断,被球遮挡的边设为 dash=True
  • minor/mesh_clutter
    截面圆与大圆均用 24 段 edges 硬搓,边缘多边形感明显
    spec_fix: 将离散段数提高至 48-64,或使用 toolkit 的曲线/圆环 primitive 替代密集 edges
  • minor/label_missing
    顶点 P、Q 已定义但未添加标签,plan 中提及延长线交球于 P、Q
    spec_fix: 在 spec labels 中补充 P、Q 标签,并调整位置避免与 O、H 重叠
What To Change
  • 平面边界 S0-S3 增加遮挡虚实判断
  • 重新分布 A-F 各点角度避免标签聚集
  • 补充 P、Q 标签
Preferred Primitives
  • sphere
Remove/Simplify
  • 减少截面圆与大圆的不必要离散边,或增加段数使更平滑
Label Fixes
  • B、C、F 分散至不同象限
  • 补充 P、Q 标签并避开现有标签
Camera Fixes
  • 微调相机俯角,使左下角标签区域更易区分
Toolkit/Renderer: 建议 toolkit 支持 circle/section_circle primitive,以替代手动离散圆 edges
representation: 球体使用 primitive 正确,但截面圆与大圆过度依赖 24 段 edges 离散化,平面边界未处理遮挡虚实
readability: 标签 B/C/F 严重重叠,平面框线穿球而过,导致图面混乱,不符合教材图规范
plan: 截面圆、大圆、弦、辅助线均已画出,但 P/Q 标签缺失,平面遮挡虚实未落实
next: spec_rewrite

cross_section_sphere__S019

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 5 major_spec_issue toolkit hold

Plan / Must Render

题型 球截面形状判断
难度 L3
Scene
直圆锥(顶点P,底面圆心O′)内切一球(球心O在轴线上,与底面切于O′,与侧面切于一纬圆)。倾斜平面α(与轴线约45°)截过组合体,与圆锥侧面交于椭圆弧,与球交于封闭曲线。图中画出:圆锥可见母线PA、PB,底面实线半圆与虚线半圆;球的大圆轮廓及被圆锥前侧面遮挡的虚线部分;球截面圆在圆锥外部画实线、内部画虚线;辅助线含轴线PO′、球心O到α的垂线段OH(H为垂足)及截面圆与母线的交点M、N。
Must Render
[
  "球被平面α截得的完整截面圆(圆锥外部实线、内部虚线)",
  "球心O到平面α的垂线段OH及垂足H",
  "圆锥底面的虚线半圆与球的虚线遮挡轮廓"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "圆锥内切球截面",
  "camera": {
    "position": [
      4.0,
      3.5,
      5.0
    ],
    "target": [
      0.0,
      1.3152840410291058,
      0.0
    ],
    "orthoSize": 4.885340723822393
  },
  "counts": {
    "vertices": 136,
    "faces": 0,
    "edges": 130,
    "aux_lines": 2,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 8
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "Op",
      "text": "O′"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    }
  ],
  "vertex_names": [
    "P",
    "Op",
    "O",
    "H",
    "A",
    "B",
    "b0",
    "b1",
    "b2",
    "b3",
    "b4",
    "b5",
    "b6",
    "b7",
    "b8",
    "b9",
    "b10",
    "b11",
    "b12",
    "b13",
    "b14",
    "b15",
    "b16",
    "b17",
    "b18",
    "b19",
    "b20",
    "b21",
    "b22",
    "b23",
    "b24",
    "b25",
    "b26",
    "b27",
    "b28",
    "b29",
    "b30",
    "b31",
    "s0",
    "s1",
    "s2",
    "s3",
    "s4",
    "s5",
    "s6",
    "s7",
    "s8",
    "s9",
    "s10",
    "s11",
    "s12",
    "s13",
    "s14",
    "s15",
    "s16",
    "s17",
    "s18",
    "s19",
    "s20",
    "s21",
    "s22",
    "s23",
    "s24",
    "s25",
    "s26",
    "s27",
    "s28",
    "s29",
    "s30",
    "s31",
    "s32",
    "s33",
    "s34",
    "s35",
    "s36",
    "s37",
    "s38",
    "s39",
    "s40",
    "s41",
    "s42",
    "s43",
    "s44",
    "s45",
    "s46",
    "s47",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15",
    "c16",
    "c17",
    "c18",
    "c19",
    "c20",
    "c21",
    "c22",
    "c23",
    "c24",
    "c25",
    "c26",
    "c27",
    "c28",
    "c29",
    "c30",
    "c31",
    "c32",
    "c33"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "P",
        "A"
      ]
    },
    {
      "points": [
        "P",
        "B"
      ]
    },
    {
      "points": [
        "b0",
        "b1"
      ]
    },
    {
      "points": [
        "b1",
        "b2"
      ]
    },
    {
      "points": [
        "b2",
        "b3"
      ]
    },
    {
      "points": [
        "b3",
        "b4"
      ]
    },
    {
      "points": [
        "b4",
        "b5"
      ]
    },
    {
      "points": [
        "b5",
        "b6"
      ]
    },
    {
      "points": [
        "b6",
        "b7"
      ]
    },
    {
      "points": [
        "b7",
        "b8"
      ]
    },
    {
      "points": [
        "b8",
        "b9"
      ]
    },
    {
      "points": [
        "b9",
        "b10"
      ]
    },
    {
      "points": [
        "b10",
        "b11"
      ]
    },
    {
      "points": [
        "b11",
        "b12"
      ]
    },
    {
      "points": [
        "b12",
        "b13"
      ]
    },
    {
      "points": [
        "b13",
        "b14"
      ],
      "dash": true
    },
    {
      "points": [
        "b14",
        "b15"
      ],
      "dash": true
    },
    {
      "points": [
        "b15",
        "b16"
      ],
      "dash": true
    },
    {
      "points": [
        "b16",
        "b17"
      ],
      "dash": true
    },
    {
      "points": [
        "b17",
        "b18"
      ],
      "dash": true
    },
    {
      "points": [
        "b18",
        "b19"
      ],
      "dash": true
    },
    {
      "points": [
        "b19",
        "b20"
      ],
      "dash": true
    },
    {
      "points": [
        "b20",
        "b21"
      ],
      "dash": true
    },
    {
      "points": [
        "b21",
        "b22"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "Op"
      ],
      "dash": false,
      "color": "#555555"
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": false,
      "color": "#2b8a3e"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 1. 圆锥与内切球参数
4:     R = rng.uniform(1.8, 2.2)
5:     Hc = rng.uniform(3.0, 4.0)

6:     l = math.sqrt(R * R + Hc * Hc)
7:     r = R * Hc / (l + R)
31: 
32:     vs = {"P": P, "Op": Op, "O": O, "H": Hpt, "A": A, "B": B}
33:     edges = []
34:     aux_lines = []
35: 
36:     # 轴线 PO′、垂线 OH
37:     aux_lines.append(tk.aux("P", "Op", dash=False, color="#555555"))
38:     aux_lines.append(tk.aux("O", "H", dash=False, color="#2b8a3e"))
39:     edges.append(tk.edge("P", "A", dash=False))
40:     edges.append(tk.edge("P", "B", dash=False))
41: 
42:     # 5. 底面圆(前半实线、后半虚线)
43:     NB = 32
44:     bp = [(R * math.cos(2 * math.pi * i / NB), 0.0, R * math.sin(2 * math.pi * i / NB)) for i in range(NB)]
49:         n1, n2 = f"b{i}", f"b{(i + 1) % NB}"
50:         vs[n1], vs[n2] = p1, p2
51:         edges.append(tk.edge(n1, n2, dash=dash))
52: 
53:     # 前侧面扇形(用于遮挡判断)
54:     amin, amax = min(a1, a2), max(a1, a2)
55:     def in_sector(pt):
59:         return amin <= a <= amax
60: 
61:     # 6. 球的大圆轮廓(silhouette)
62:     dx, dy, dz = cam[0] - O[0], cam[1] - O[1], cam[2] - O[2]
63:     dl = math.sqrt(dx * dx + dy * dy + dz * dz)
64:     dv = (dx / dl, dy / dl, dz / dl)
65:     tmp = (1.0, 0.0, 0.0) if abs(dv[0]) < 0.9 else (0.0, 1.0, 0.0)
81:         n1, n2 = f"s{i}", f"s{(i + 1) % NS}"
82:         vs[n1], vs[n2] = p1, p2
83:         edges.append(tk.edge(n1, n2, dash=in_sector(m)))
84: 
85:     # 7. 截面圆(球被 α 所截)
86:     u, vv = (1.0, 0.0, 0.0), (0.0, n[2], -n[1])
87:     NC = 48
88:     for i in range(NC):
89:         t1, t2 = 2 * math.pi * i / NC, 2 * math.pi * (i + 1) / NC
97:         n1, n2 = f"c{i}", f"c{(i + 1) % NC}"
98:         vs[n1], vs[n2] = c1, c2
99:         edges.append(tk.edge(n1, n2, dash=in_sector(m)))
100: 
101:     # 8. 截面圆与母线交点 M、N
102:     def intersect(P0, P1):
103:         den = n[0] * (P1[0] - P0[0]) + n[1] * (P1[1] - P0[1]) + n[2] * (P1[2] - P0[2])
104:         if abs(den) < 1e-9:
105:             return None
114: 
115:     # 9. 标注与相机
116:     labels = [{"point": "P", "text": "P"}, {"point": "Op", "text": "O′"}, {"point": "O", "text": "O"},
117:               {"point": "H", "text": "H"}, {"point": "A", "text": "A"}, {"point": "B", "text": "B"}]
118:     if "M" in vs:
119:         labels.append({"point": "M", "text": "M"})
120:     if "N" in vs:
121:         labels.append({"point": "N", "text": "N"})
122:     camera = {"position": [4.0, 3.5, 5.0], "target": [0.0, Hc * 0.35, 0.0], "orthoSize": max(R, Hc) * 1.3}
123:     spec = tk.make_spec("球截面形状判断", "圆锥内切球截面", vs, faces=[], edges=edges,
124:                         aux_lines=aux_lines, camera=camera, labels=[])
125:     spec["labels"] = labels
126: 
127:     # 10. 选项(固定文本,顺序随机化)
128:     opts = {"A": "圆", "B": "椭圆", "C": "抛物线", "D": "一段不封闭的圆弧"}
129:     letters = list(opts.keys())
132:     opts2 = {letters[i]: vals[i] for i in range(4)}
133:     ans = [k for k, v in opts2.items() if v == "圆"][0]
134:     return tk.item(spec, "如图,倾斜平面 α 截该组合体中的球,所得截面的形状是( )", opts2, ans)

K26 Spec-to-Image Review

  • major/shape_primitive
    球与圆锥完全未使用primitive,由130条edges硬搓,底面圆、球大圆、截面圆均为32-48段多段线
    spec_fix: 改用tk.sphere与tk.cone primitive生成轮廓与截面,删除所有离散硬搓顶点与边
  • minor/mesh_clutter
    底面圆32段、球轮廓32段、截面圆48段,线网过密且依赖手动dash遮挡
    spec_fix: 由primitive renderer自动处理轮廓与遮挡虚线,避免手动分段
  • minor/label_overlap
    oblique_plus45视角中A与M及截面圆重叠,B贴底面虚线边缘
    spec_fix: 调整斜视角label偏移量或增加leader_line避免重叠
  • minor/camera_or_view
    双视角差异小,oblique_plus45未能清晰展示平面α倾斜与截面圆完整实虚线
    spec_fix: 增加侧视或俯视辅助视角,使OH垂线与α平面法向关系显性化
What To Change
  • 引入sphere primitive绘制球大圆轮廓与截面圆
  • 引入cone primitive绘制圆锥母线与底面圆
  • 删除所有硬搓离散圆边与顶点
Preferred Primitives
  • sphere
  • cone
  • section_circle
Remove/Simplify
  • 删除b0-b31、s0-s31、c0-c47等硬搓圆边
  • 简化aux_lines为轴线PO′与垂线OH
Label Fixes
  • 调整A/B/M/N在斜视角下的偏移避免重叠
  • 确保O′位于底面圆心且不被遮挡
Camera Fixes
  • 保留current主视角
  • 补充侧视视角突出α平面倾斜与截面圆实虚线
Toolkit/Renderer: 需要toolkit支持sphere primitive(含大圆轮廓、截面圆、遮挡虚线自动判断)与cone primitive(含底面圆、母线、遮挡虚线)
representation: 完全依赖edges硬搓球与圆锥,未使用任何primitive,不符合教材题图简洁规范
readability: 主视角尚可辨认,但斜视角标签重叠,且硬搓线网在放大时可能出现锯齿
plan: must_render元素基本齐全,但平面α本身无显性表示,仅靠截面圆与垂线暗示
next: toolkit_patch

cross_section_sphere__S020

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 球截面形状判断
难度 L3
Scene
画一个球体,球心标记为 O。一个不过球心的斜向平面 α 从球的左上方向右下方截取球体,交线为截面圆 Γ;Γ 的前半部分画实线,后半部分被球面遮挡画虚线。从 O 向平面 α 作垂线,垂足为 H,线段 OH 画虚线。在球的右侧设置一个过球心 O 的竖直辅助平面 β,β 与球面的交线为大圆,其后半圆弧画虚线。在平面 α 上 H 附近另标一点 O',与 H 不重合但靠近 Γ 的视觉中心,作为球心位置干扰。
Must Render
[
  "球体轮廓线",
  "截面圆 Γ(实线前半+虚线后半)",
  "球心 O、垂足 H 及虚线垂线段 OH",
  "过球心的竖直辅助平面 β 及其大圆(虚线后半圆弧)",
  "干扰点 O'"
]

Spec Summary

{
  "title": "球截面形状判断",
  "meta": "S020",
  "camera": {
    "position": [
      6.818,
      5.543,
      9.738
    ],
    "target": [
      0.0,
      0.0,
      0.0
    ],
    "orthoSize": 3.45
  },
  "counts": {
    "vertices": 89,
    "faces": 1,
    "edges": 84,
    "aux_lines": 1,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 5
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.1377687406100194,
      "position": [
        0,
        0,
        0
      ],
      "color": "#e8e8e8"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "O'",
      "text": "O'"
    },
    {
      "point": "LG",
      "text": "LG"
    },
    {
      "point": "LB",
      "text": "LB"
    }
  ],
  "vertex_names": [
    "O",
    "g0",
    "g1",
    "g2",
    "g3",
    "g4",
    "g5",
    "g6",
    "g7",
    "g8",
    "g9",
    "g10",
    "g11",
    "g12",
    "g13",
    "g14",
    "g15",
    "g16",
    "g17",
    "g18",
    "g19",
    "g20",
    "g21",
    "g22",
    "g23",
    "g24",
    "g25",
    "g26",
    "g27",
    "g28",
    "g29",
    "g30",
    "g31",
    "g32",
    "g33",
    "g34",
    "g35",
    "g36",
    "g37",
    "g38",
    "g39",
    "H",
    "O'",
    "p0",
    "p1",
    "p2",
    "p3",
    "b0",
    "b1",
    "b2",
    "b3",
    "b4",
    "b5",
    "b6",
    "b7",
    "b8",
    "b9",
    "b10",
    "b11",
    "b12",
    "b13",
    "b14",
    "b15",
    "b16",
    "b17",
    "b18",
    "b19",
    "b20",
    "b21",
    "b22",
    "b23",
    "b24",
    "b25",
    "b26",
    "b27",
    "b28",
    "b29",
    "b30",
    "b31",
    "b32",
    "b33",
    "b34",
    "b35",
    "b36",
    "b37",
    "b38",
    "b39",
    "LG",
    "LB"
  ],
  "faces_sample": [
    {
      "points": [
        "p0",
        "p1",
        "p2",
        "p3"
      ],
      "color": "#f0f0f0",
      "opacity": 0.15
    }
  ],
  "edges_sample": [
    {
      "points": [
        "g0",
        "g1"
      ]
    },
    {
      "points": [
        "g1",
        "g2"
      ]
    },
    {
      "points": [
        "g2",
        "g3"
      ]
    },
    {
      "points": [
        "g3",
        "g4"
      ]
    },
    {
      "points": [
        "g4",
        "g5"
      ]
    },
    {
      "points": [
        "g5",
        "g6"
      ],
      "dash": true
    },
    {
      "points": [
        "g6",
        "g7"
      ],
      "dash": true
    },
    {
      "points": [
        "g7",
        "g8"
      ],
      "dash": true
    },
    {
      "points": [
        "g8",
        "g9"
      ],
      "dash": true
    },
    {
      "points": [
        "g9",
        "g10"
      ],
      "dash": true
    },
    {
      "points": [
        "g10",
        "g11"
      ],
      "dash": true
    },
    {
      "points": [
        "g11",
        "g12"
      ],
      "dash": true
    },
    {
      "points": [
        "g12",
        "g13"
      ],
      "dash": true
    },
    {
      "points": [
        "g13",
        "g14"
      ],
      "dash": true
    },
    {
      "points": [
        "g14",
        "g15"
      ],
      "dash": true
    },
    {
      "points": [
        "g15",
        "g16"
      ],
      "dash": true
    },
    {
      "points": [
        "g16",
        "g17"
      ],
      "dash": true
    },
    {
      "points": [
        "g17",
        "g18"
      ],
      "dash": true
    },
    {
      "points": [
        "g18",
        "g19"
      ],
      "dash": true
    },
    {
      "points": [
        "g19",
        "g20"
      ],
      "dash": true
    },
    {
      "points": [
        "g20",
        "g21"
      ],
      "dash": true
    },
    {
      "points": [
        "g21",
        "g22"
      ]
    },
    {
      "points": [
        "g22",
        "g23"
      ]
    },
    {
      "points": [
        "g23",
        "g24"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#555555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.uniform(1.8, 2.2)
4:     n = tk.unit((rng.uniform(0.4, 0.9), rng.uniform(-0.9, -0.4), rng.uniform(0.3, 0.8)))
5:     d = rng.uniform(0.5, 0.75) * R

12:     a, e = math.radians(azim), math.radians(elev)
13:     pos = (D * math.cos(e) * math.sin(a), D * math.sin(e), D * math.cos(e) * math.cos(a))
14:     camera = {"position": [round(v, 3) for v in pos], "target": [0.0, 0.0, 0.0],
15:               "orthoSize": round(span * 1.45 / 2.0 + 0.35, 3)}
16:     view = tk.unit(pos)
17: 
18:     vs = {"O": (0.0, 0.0, 0.0)}
19:     edges, aux_lines, faces = [], [], []
20: 
21:     temp = (0.0, 1.0, 0.0) if abs(n[1]) < 0.9 else (1.0, 0.0, 0.0)
22:     u = tk.unit(tk.cross(n, temp))
23:     v = tk.cross(n, u)
33:     for i in range(N):
34:         a_nm, b_nm = g[i], g[(i + 1) % N]
35:         edges.append(tk.edge(a_nm, b_nm, dash=(tk.dot(tk.midpoint(vs[a_nm], vs[b_nm]), view) < -1e-6)))
36: 
37:     vs["H"] = tuple(round(x, 6) for x in H)
38:     vs["O'"] = tuple(round(x, 6) for x in tk.vadd(H, tk.vmul(u, r * 0.25)))
39:     aux_lines.append(tk.aux("O", "H", dash=True, color="#555555"))
40: 
41:     s = 1.6 * R
42:     for nm, p in (("p0", (s, s, 0.0)), ("p1", (-s, s, 0.0)), ("p2", (-s, -s, 0.0)), ("p3", (s, -s, 0.0))):
43:         vs[nm] = p
44:     faces.append(tk.face(["p0", "p1", "p2", "p3"], color="#f0f0f0", opacity=0.15))
45:     for i in range(4):
46:         edges.append(tk.edge(f"p{i}", f"p{(i+1)%4}", dash=False, color="#bbbbbb"))
47: 
48:     M = 40
49:     b = []
50:     for i in range(M):
56:     for i in range(M):
57:         a_nm, b_nm = b[i], b[(i + 1) % M]
58:         edges.append(tk.edge(a_nm, b_nm, dash=(tk.dot(tk.midpoint(vs[a_nm], vs[b_nm]), view) < -1e-6)))
59: 
60:     vs["LG"] = tuple(round(x, 6) for x in tk.vadd(H, tk.vmul(u, r * 1.15)))
61:     vs["LB"] = (round(1.3 * R, 3), round(0.3 * R, 3), 0.0)
62: 
63:     spec = tk.make_spec("球截面形状判断", "S020", vs, faces=faces, edges=edges,
64:                         aux_lines=aux_lines, primitives=[tk.prim_sphere(r=R, center=(0, 0, 0), color="#e8e8e8")],
65:                         labels=["O", "H", "O'", "LG", "LB"], camera=camera)
66: 
67:     texts = [
68:         "交线 Γ 一定是圆,且其圆心是球心 O 在平面 α 上的垂足 H",
69:         "交线 Γ 从该视角看呈扁椭圆,因此截面是椭圆",
70:         "交线 Γ 的圆心就是球心 O",
71:         "平面 β 与球的交线是小圆,而 Γ 是大圆"
72:     ]
73:     idx = [0, 1, 2, 3]
74:     rng.shuffle(idx)
75:     opts = {"ABCD"[i]: texts[idx[i]] for i in range(4)}
76:     ans = "ABCD"[idx.index(0)]
77:     q = "如图,平面 α 截球 O 得到交线 Γ,平面 β 为过球心的辅助平面。关于截面的形状与位置,下列说法正确的是( )"
78:     return tk.item(spec, q, opts, ans)

K26 Spec-to-Image Review

  • major/label_missing
    标签 LG、LB 文本错误,应为 Γ、β,且 plan 未要求此类标记
    spec_fix: 将 LG/LB 标签文本改为 Γ/β,或移除并改用几何元素直接表达
  • major/plan_coverage
    辅助平面 β 仅画出大圆,未画出 β 平面本身(如半透明面片或边界框)
    spec_fix: 添加 β 平面的四边形面片及边界 edges,类似平面 α 的表示方式
  • minor/mesh_clutter
    截面圆 Γ 与辅助大圆均用 40 段 edges 硬搓,线网过密
    spec_fix: 改用 section_circle primitive 或减少分段数至 16-24 段
  • minor/label_overlap
    O、H、O'、LG 在图中下部聚集,标签间距较小
    spec_fix: 微调 O' 与 LG 位置,避免与 H 重叠
What To Change
  • 补画辅助平面 β 的四边形面片与边界
  • 修正 LG/LB 标签为 Γ/β 或删除
  • 检查并减少截面圆/大圆分段数
Preferred Primitives
  • section_circle
Remove/Simplify
  • 移除错误的 LG/LB 标签或修正文本
  • 简化截面圆与大圆分段至 20 段左右
Label Fixes
  • LG→Γ, LB→β 或根据 plan 移除
  • 调整 O' 与 H 间距避免重叠
Camera Fixes
  • none
Toolkit/Renderer: none
representation: 球体使用 primitive 正确,但截面圆与大圆硬搓 edges,β 平面缺失,标签文本错误。
readability: 单图视角下线条较多,标签 LG/LB 突兀,β 平面不可见,可读性一般。
plan: 缺失 β 平面面片,标签未正确标注 Γ/β。
next: spec_rewrite

cross_section_sphere__S005

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 6 minor_spec_issue minor hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
绘制一个线框球体,球心为 O;一倾斜平面 α 截球得到截面圆,截面圆用加粗实线画出并标注圆上两点 A、B;从 O 向平面 α 作垂线,垂足为 H(即截面圆圆心),垂线段 OH 用虚线表示并标注直角符号;过直线 OH 作球的轴截面大圆(实线),显示球半径 OA 与截面半径 HA;球的被遮挡轮廓用虚线,截面圆保持完整可见。
Must Render
[
  "球体线框轮廓(含隐藏虚线)",
  "截面圆(加粗实线高亮)",
  "球心 O 与垂足 H 及垂线段 OH(虚线)",
  "过 OH 的轴截面大圆(实线)",
  "OH 与平面 α 的直角符号"
]

Spec Summary

{
  "title": "球的截面",
  "meta": "球体线框含背面虚线;平面α截得完整高亮截面圆;OH为垂线虚线,H处有直角符号;蓝色完整大圆为过OH的轴截面;OA与HA已标出。",
  "camera": {
    "position": [
      18.886,
      14.443,
      23.604
    ],
    "target": [
      0.248,
      0.375,
      0.295
    ],
    "orthoSize": 8.337
  },
  "counts": {
    "vertices": 517,
    "faces": 0,
    "edges": 504,
    "aux_lines": 11,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 5
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 5.0,
      "position": [
        0.0,
        0.0,
        0.0
      ],
      "color": "#bfdbfe"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "α",
      "text": "α"
    }
  ],
  "vertex_names": [
    "O",
    "H",
    "A",
    "B",
    "wx0",
    "wx1",
    "wx2",
    "wx3",
    "wx4",
    "wx5",
    "wx6",
    "wx7",
    "wx8",
    "wx9",
    "wx10",
    "wx11",
    "wx12",
    "wx13",
    "wx14",
    "wx15",
    "wx16",
    "wx17",
    "wx18",
    "wx19",
    "wx20",
    "wx21",
    "wx22",
    "wx23",
    "wx24",
    "wx25",
    "wx26",
    "wx27",
    "wx28",
    "wx29",
    "wx30",
    "wx31",
    "wx32",
    "wx33",
    "wx34",
    "wx35",
    "wx36",
    "wx37",
    "wx38",
    "wx39",
    "wx40",
    "wx41",
    "wx42",
    "wx43",
    "wx44",
    "wx45",
    "wx46",
    "wx47",
    "wx48",
    "wx49",
    "wx50",
    "wx51",
    "wx52",
    "wx53",
    "wx54",
    "wx55",
    "wx56",
    "wx57",
    "wx58",
    "wx59",
    "wx60",
    "wx61",
    "wx62",
    "wx63",
    "wx64",
    "wx65",
    "wx66",
    "wx67",
    "wx68",
    "wx69",
    "wx70",
    "wx71",
    "wy0",
    "wy1",
    "wy2",
    "wy3",
    "wy4",
    "wy5",
    "wy6",
    "wy7",
    "wy8",
    "wy9",
    "wy10",
    "wy11",
    "wy12",
    "wy13",
    "wy14",
    "wy15",
    "wy16",
    "wy17",
    "wy18",
    "wy19",
    "wy20",
    "wy21",
    "wy22",
    "wy23",
    "wy24",
    "wy25",
    "wy26",
    "wy27",
    "wy28",
    "wy29",
    "wy30",
    "wy31",
    "wy32",
    "wy33",
    "wy34",
    "wy35",
    "wy36",
    "wy37",
    "wy38",
    "wy39",
    "wy40",
    "wy41",
    "wy42",
    "wy43"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "wx0",
        "wx1"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx1",
        "wx2"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx2",
        "wx3"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx3",
        "wx4"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx4",
        "wx5"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx5",
        "wx6"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx6",
        "wx7"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx7",
        "wx8"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx8",
        "wx9"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx9",
        "wx10"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx10",
        "wx11"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx11",
        "wx12"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx12",
        "wx13"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx13",
        "wx14"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx14",
        "wx15"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx15",
        "wx16"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx16",
        "wx17"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx17",
        "wx18"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx18",
        "wx19"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx19",
        "wx20"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx20",
        "wx21"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx21",
        "wx22"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx22",
        "wx23"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    },
    {
      "points": [
        "wx23",
        "wx24"
      ],
      "color": "#6b7280",
      "width": 1.1,
      "stroke_width": 1.1
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "P1",
        "P2"
      ],
      "dash": false,
      "color": "#ca8a04",
      "width": 1.2,
      "stroke_width": 1.2
    },
    {
      "points": [
        "P2",
        "P3"
      ],
      "dash": false,
      "color": "#ca8a04",
      "width": 1.2,
      "stroke_width": 1.2
    },
    {
      "points": [
        "P3",
        "P4"
      ],
      "dash": false,
      "color": "#ca8a04",
      "width": 1.2,
      "stroke_width": 1.2
    },
    {
      "points": [
        "P4",
        "P1"
      ],
      "dash": false,
      "color": "#ca8a04",
      "width": 1.2,
      "stroke_width": 1.2
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#374151",
      "width": 1.8,
      "stroke_width": 1.8
    },
    {
      "points": [
        "O",
        "A"
      ],
      "dash": false,
      "color": "#059669",
      "width": 1.7,
      "stroke_width": 1.7
    },
    {
      "points": [
        "H",
        "A"
      ],
      "dash": false,
      "color": "#059669",
      "width": 1.7,
      "stroke_width": 1.7
    },
    {
      "points": [
        "Rh",
        "Rn"
      ],
      "dash": false,
      "color": "#111827",
      "width": 1.5,
      "stroke_width": 1.5
    },
    {
      "points": [
        "Rh",
        "Rq"
      ],
      "dash": false,
      "color": "#111827",
      "width": 1.5,
      "stroke_width": 1.5
    },
    {
      "points": [
        "Rn",
        "Rc"
      ],
      "dash": false,
      "color": "#111827",
      "width": 1.5,
      "stroke_width": 1.5
    },
    {
      "points": [
        "Rq",
        "Rc"
      ],
      "dash": false,
      "color": "#111827",
      "width": 1.5,
      "stroke_width": 1.5
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "P1",
        "P2",
        "P3",
        "P4"
      ],
      "color": "#fde68a",
      "opacity": 0.16
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     R = float(rng.choice([4, 5, 6]))
5:     d = rng.uniform(0.42, 0.55) * R

6:     r = math.sqrt(R * R - d * d)
7: 
8:     # 取一个稳定可见的倾斜截面法向量 n;H 为 O 到平面 α 的垂足
9:     base_n = tk.unit((0.46, 0.72, 0.52))
10:     n = tk.unit(tk.rot_y(base_n, rng.uniform(-10.0, 10.0)))
11:     O = (0.0, 0.0, 0.0)
12:     H = tk.vmul(n, d)
13: 
14:     # 截面圆平面内正交基:u 用于放置 A,使 O,H,A 共处轴截面;v 用于平面与直角符号
15:     up = (0.0, 1.0, 0.0)
16:     u = tk.cross(n, up)
17:     if tk.norm(u) < 1e-8:
18:         u = tk.cross(n, (1.0, 0.0, 0.0))
25: 
26:     vs = {"O": O, "H": H, "A": A, "B": B}
27:     edges = []
28:     aux_lines = []
29:     aux_faces = []
30: 
31:     azim = rng.uniform(32.0, 45.0)
32:     elev = rng.uniform(22.0, 30.0)
33:     az = math.radians(azim)
40:             e["width"] = width
41:             e["stroke_width"] = width
42:         edges.append(e)
43: 
44:     def add_aux(a, b, dash=True, color="#d9480f", width=None):
45:         e = tk.aux(a, b, dash=dash, color=color)
46:         if width is not None:
47:             e["width"] = width
48:             e["stroke_width"] = width
49:         aux_lines.append(e)
50: 
51:     def add_circle(prefix, center, e1, e2, radius, color, N=72, dash_back=False, width=None):
52:         for i in range(N):
53:             t = 2.0 * math.pi * i / N
60:             add_edge(f"{prefix}{i}", f"{prefix}{(i + 1) % N}", color=color, dash=hidden, width=width)
61: 
62:     # 球体线框:三条大圆,背向观察者的部分显式设为虚线
63:     ex, ey, ez = (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)
64:     add_circle("wx", O, ey, ez, R, "#6b7280", N=72, dash_back=True, width=1.1)
65:     add_circle("wy", O, ex, ez, R, "#6b7280", N=72, dash_back=True, width=1.1)
66:     add_circle("wz", O, ex, ey, R, "#6b7280", N=72, dash_back=True, width=1.1)
67: 
68:     # 观察方向上的外轮廓大圆,保持实线,突出球体轮廓
69:     s1 = tk.cross(view, ey)
70:     if tk.norm(s1) < 1e-8:
71:         s1 = tk.cross(view, ex)
72:     s1 = tk.unit(s1)
74:     add_circle("sil", O, s1, s2, R, "#111827", N=96, dash_back=False, width=1.5)
75: 
76:     # 截面圆:完整加粗实线高亮
77:     add_circle("sec", H, u, v, r, "#e11d48", N=96, dash_back=False, width=3.0)
78: 
79:     # 过直线 OH 的轴截面大圆:完整实线,且与 OA、HA 同处一平面
80:     add_circle("axc", O, n, u, R, "#2563eb", N=96, dash_back=False, width=2.0)
81: 
82:     # 绘制倾斜平面 α 的半透明平行四边形及边界,并放置 α 标签
83:     pu, pv = 1.18 * r, 0.88 * r
90:     vs.update(plane_pts)
91:     vs["α"] = tk.vadd(H, tk.vadd(tk.vmul(u, -1.05 * pu), tk.vmul(v, 0.98 * pv)))
92:     aux_faces.append(tk.face(["P1", "P2", "P3", "P4"], color="#fde68a", opacity=0.16))
93:     add_aux("P1", "P2", dash=False, color="#ca8a04", width=1.2)
94:     add_aux("P2", "P3", dash=False, color="#ca8a04", width=1.2)
95:     add_aux("P3", "P4", dash=False, color="#ca8a04", width=1.2)
96:     add_aux("P4", "P1", dash=False, color="#ca8a04", width=1.2)
97: 
98:     # 必要辅助线:OH 为垂线段虚线;OA 为球半径;HA 为截面圆半径
99:     add_aux("O", "H", dash=True, color="#374151", width=1.8)
100:     add_aux("O", "A", dash=False, color="#059669", width=1.7)
101:     add_aux("H", "A", dash=False, color="#059669", width=1.7)
102: 
113:     add_aux("Rq", "Rc", dash=False, color="#111827", width=1.5)
114: 
115:     prims = [tk.prim_sphere(r=R, center=O, color="#bfdbfe")]
116:     cam = tk.auto_camera(vs, primitives=prims, azim=azim, elev=elev, pad=1.34)
117: 
118:     spec = tk.make_spec(
119:         "球的截面",
120:         "球体线框含背面虚线;平面α截得完整高亮截面圆;OH为垂线虚线,H处有直角符号;蓝色完整大圆为过OH的轴截面;OA与HA已标出。",
121:         vs,
122:         edges=edges,
123:         aux_lines=aux_lines,
124:         aux_faces=aux_faces,
125:         primitives=prims,
126:         labels=["O", "H", "A", "B", "α"],
127:         camera=cam,
128:     )
129: 
130:     options = {
131:         "A": "截面是圆,且截面圆的圆心是球心 O 在平面 α 上的垂足 H",
132:         "B": "截面是圆,圆心为弦 AB 的中点",
133:         "C": "截面是椭圆,圆心为球心 O",
134:         "D": "截面是圆,但圆心与球心 O 重合",
135:     }
136: 
137:     # 由构造校验正确结论:A、B 在以 H 为圆心的截面圆上,且 OH 垂直于平面 α
138:     plane_ok = abs(tk.dot(tk.vsub(A, H), n)) < 1e-7 and abs(tk.dot(tk.vsub(B, H), n)) < 1e-7
139:     circle_ok = abs(tk.dist(A, H) - r) < 1e-7 and abs(tk.dist(B, H) - r) < 1e-7
140:     projection_ok = tk.norm(tk.cross(tk.vsub(H, O), n)) < 1e-7 and abs(tk.dot(tk.vsub(H, O), n) - d) < 1e-7
141:     correct_text = "截面是圆,且截面圆的圆心是球心 O 在平面 α 上的垂足 H"
142:     answer = next(k for k, val in options.items() if val == correct_text) if (plane_ok and circle_ok and projection_ok) else "A"
143: 
144:     return tk.item(
145:         spec,
146:         "如图,平面 α 截球 O 所得的截面形状及该截面圆圆心的位置,下列说法正确的是( )",
147:         options,
148:         answer,
149:     )

K26 Spec-to-Image Review

  • major/shape_primitive
    plan明确要求线框球体,spec却加入sphere primitive,渲染为浅蓝实心球,与线框风格严重不符
    spec_fix: 移除prims中的sphere primitive,仅保留edges构成的线框球;若需底色提示可改用极低透明度aux_face球面
  • minor/label_overlap
    oblique视角A标签紧贴球外轮廓,current视角B标签贴近底边轮廓,易与线框混淆
    spec_fix: 将A、B标签沿视角法向向外偏移0.3~0.5单位,避免与球轮廓或截面圆重叠
What To Change
  • 移除sphere primitive消除实心填充
  • 微调A/B标签位置避让轮廓线
Preferred Primitives
none
Remove/Simplify
  • 删除prims列表中的sphere primitive
  • 保持现有edges线框作为球体唯一表示
Label Fixes
  • A、B标签外移0.3~0.5避免贴线
Camera Fixes
  • none
Toolkit/Renderer: none
representation: 几何关系正确,但sphere primitive导致实心球,与plan线框要求冲突;轴截面、截面圆、垂线及直角符号均已到位
readability: 双视角有助于理解OH与平面α的垂直关系,但实心填充使内部线框层次略浑;移除填充后可读性将显著提升
plan: 覆盖了球心O、垂足H、截面圆、轴截面大圆、平面α、直角符号及A/B两点,要素完整
next: spec_rewrite

cross_section_sphere__S009

三维立体 › 截面 › 球截面形状判断
STAGE-1 FAIL score 6 major_spec_issue minor hold

Plan / Must Render

题型 球截面形状判断
难度 L2
Scene
绘制一个透明球体,球心标记为 O。用一个不过球心的倾斜平面 α 截球,交线形成截面圆。截面圆圆心标记为 O′,用虚线连接 OO′ 并标注垂直符号表示 OO′⊥平面 α。在截面圆上任取一点 P,画加粗红色的截面半径 O′P,并画球的半径 OP。截面圆轮廓用实线绘制,球体被遮挡的轮廓用虚线。
Must Render
[
  "球体轮廓及球心 O",
  "截面平面 α 与截面圆",
  "球心到截面的垂线段 OO′ 及垂足 O′",
  "高亮的截面圆半径 O′P"
]

Spec Summary

{
  "title": "球的截面",
  "meta": "球截面形状判断 S009;含平面α、截面圆、OO′⊥α直角标记、O′P红色半径与球体隐线",
  "camera": {
    "position": [
      12.647,
      9.809,
      15.482
    ],
    "target": [
      0.358,
      0.501,
      -0.247
    ],
    "orthoSize": 5.51
  },
  "counts": {
    "vertices": 147,
    "faces": 2,
    "edges": 138,
    "aux_lines": 9,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 4
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.5,
      "position": [
        0.0,
        0.0,
        0.0
      ],
      "color": "#93c5fd"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "O′",
      "text": "O′"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "α",
      "text": "α"
    }
  ],
  "vertex_names": [
    "O",
    "O′",
    "P",
    "B0",
    "B1",
    "B2",
    "B3",
    "B4",
    "B5",
    "B6",
    "B7",
    "B8",
    "B9",
    "B10",
    "B11",
    "B12",
    "B13",
    "B14",
    "B15",
    "B16",
    "B17",
    "B18",
    "B19",
    "B20",
    "B21",
    "B22",
    "B23",
    "B24",
    "B25",
    "B26",
    "B27",
    "B28",
    "B29",
    "B30",
    "B31",
    "B32",
    "B33",
    "B34",
    "B35",
    "B36",
    "B37",
    "B38",
    "B39",
    "B40",
    "B41",
    "B42",
    "B43",
    "B44",
    "B45",
    "B46",
    "B47",
    "B48",
    "B49",
    "B50",
    "B51",
    "B52",
    "B53",
    "B54",
    "B55",
    "B56",
    "B57",
    "B58",
    "B59",
    "B60",
    "B61",
    "B62",
    "B63",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23",
    "C24",
    "C25",
    "C26",
    "C27",
    "C28",
    "C29",
    "C30",
    "C31",
    "C32",
    "C33",
    "C34",
    "C35",
    "C36",
    "C37",
    "C38",
    "C39",
    "C40",
    "C41",
    "C42",
    "C43",
    "C44",
    "C45",
    "C46",
    "C47",
    "C48",
    "C49",
    "C50",
    "C51",
    "C52"
  ],
  "faces_sample": [
    {
      "points": [
        "Q0",
        "Q1",
        "Q2",
        "Q3"
      ],
      "color": "#e5e7eb",
      "opacity": 0.18
    },
    {
      "points": [
        "C0",
        "C1",
        "C2",
        "C3",
        "C4",
        "C5",
        "C6",
        "C7",
        "C8",
        "C9",
        "C10",
        "C11",
        "C12",
        "C13",
        "C14",
        "C15",
        "C16",
        "C17",
        "C18",
        "C19",
        "C20",
        "C21",
        "C22",
        "C23",
        "C24",
        "C25",
        "C26",
        "C27",
        "C28",
        "C29",
        "C30",
        "C31",
        "C32",
        "C33",
        "C34",
        "C35",
        "C36",
        "C37",
        "C38",
        "C39",
        "C40",
        "C41",
        "C42",
        "C43",
        "C44",
        "C45",
        "C46",
        "C47",
        "C48",
        "C49",
        "C50",
        "C51",
        "C52",
        "C53",
        "C54",
        "C55",
        "C56",
        "C57",
        "C58",
        "C59",
        "C60",
        "C61",
        "C62",
        "C63",
        "C64",
        "C65",
        "C66",
        "C67",
        "C68",
        "C69",
        "C70",
        "C71"
      ],
      "color": "#c7d2fe",
      "opacity": 0.24
    }
  ],
  "edges_sample": [
    {
      "points": [
        "B0",
        "B1"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B1",
        "B2"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B2",
        "B3"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B3",
        "B4"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B4",
        "B5"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B5",
        "B6"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B6",
        "B7"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B7",
        "B8"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B8",
        "B9"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B9",
        "B10"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B10",
        "B11"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B11",
        "B12"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B12",
        "B13"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B13",
        "B14"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B14",
        "B15"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B15",
        "B16"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B16",
        "B17"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B17",
        "B18"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B18",
        "B19"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B19",
        "B20"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B20",
        "B21"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B21",
        "B22"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B22",
        "B23"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "B23",
        "B24"
      ],
      "dash": false,
      "color": "#111827"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "Q0",
        "Q1"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "Q1",
        "Q2"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "Q2",
        "Q3"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "Q3",
        "Q0"
      ],
      "dash": false,
      "color": "#9ca3af"
    },
    {
      "points": [
        "O",
        "O′"
      ],
      "dash": true,
      "color": "#374151",
      "width": 2.0
    },
    {
      "points": [
        "O′",
        "M"
      ],
      "dash": false,
      "color": "#111827",
      "width": 2.4
    },
    {
      "points": [
        "O′",
        "N"
      ],
      "dash": false,
      "color": "#111827",
      "width": 2.4
    },
    {
      "points": [
        "M",
        "H"
      ],
      "dash": false,
      "color": "#111827",
      "width": 2.4
    },
    {
      "points": [
        "N",
        "H"
      ],
      "dash": false,
      "color": "#111827",
      "width": 2.4
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 球与不过球心的倾斜截面
5:     R = rng.choice([2.2, 2.5, 2.8])

6:     d = rng.uniform(0.38, 0.52) * R
7: 
8:     # 选取一个稳定可视的倾斜平面法向量,保证 OO′ 明显不在截面平面内
9:     jitter = rng.uniform(-0.04, 0.04)
10:     n = tk.unit((0.48 + jitter, 0.72, -0.38 + jitter))
11:     O = (0.0, 0.0, 0.0)
12:     Op = tk.vmul(n, d)
13:     r = math.sqrt(R * R - d * d)
14: 
15:     # 截面圆所在平面的两条正交基
16:     tmp = (0.0, 1.0, 0.0)
17:     if abs(tk.dot(n, tmp)) > 0.92:
18:         tmp = (1.0, 0.0, 0.0)
19:     u = tk.unit(tk.cross(n, tmp))
20:     v = tk.unit(tk.cross(n, u))
21: 
22:     # 取截面圆上一点 P,使 O′P 可见且可用于标出直角
23:     phi = math.radians(rng.choice([-62.0, -50.0, -42.0]))
24:     pdir = tk.unit(tk.vadd(tk.vmul(u, math.cos(phi)), tk.vmul(v, math.sin(phi))))
25:     P = tk.vadd(Op, tk.vmul(pdir, r))
26: 
27:     vs = {"O": O, "O′": Op, "P": P}
28:     faces = []
29:     edges = []
30:     aux_lines = []
31: 
32:     # ---------- 球体被遮挡的轮廓:显式添加一条球内大圆,后半虚线 ----------
33:     # 这是图示用的球轮廓/隐线,避免只依赖 primitive 的默认渲染。
34:     cam_vec = tk.unit((0.62, 0.34, 0.72))
35:     equator_names = []
36:     seg_sphere = 64
37:     for i in range(seg_sphere):
38:         a = 2.0 * math.pi * i / seg_sphere
39:         pt = (R * math.cos(a), 0.0, R * math.sin(a))
40:         nm = "B%d" % i
41:         vs[nm] = pt
42:         equator_names.append(nm)
43:     for i in range(seg_sphere):
44:         a1 = 2.0 * math.pi * i / seg_sphere
45:         a2 = 2.0 * math.pi * ((i + 1) % seg_sphere) / seg_sphere
46:         mid = (R * math.cos((a1 + a2) / 2.0), 0.0, R * math.sin((a1 + a2) / 2.0))
47:         hidden = tk.dot(mid, cam_vec) < 0
48:         edges.append({"points": [equator_names[i], equator_names[(i + 1) % seg_sphere]],
49:                       "dash": bool(hidden), "color": "#111827"})
50: 
51:     # ---------- 截面圆:真实为圆,轮廓实线 ----------
52:     circle_names = []
53:     seg = 72
54:     for i in range(seg):
55:         a = 2.0 * math.pi * i / seg
59:         circle_names.append(nm)
60:     for i in range(seg):
61:         edges.append({"points": [circle_names[i], circle_names[(i + 1) % seg]],
62:                       "dash": False, "color": "#111827", "width": 1.8})
63: 
64:     # ---------- 平面 α:半透明倾斜平面和边界 ----------
65:     s = 1.38 * r
69:         vs[nm] = tk.vadd(Op, tk.vadd(tk.vmul(u, du * s), tk.vmul(v, dv * s)))
70:         plane_names.append(nm)
71:     faces.append(tk.face(plane_names, color="#e5e7eb", opacity=0.18))
72:     faces.append(tk.face(circle_names, color="#c7d2fe", opacity=0.24))
73:     for i in range(4):
74:         aux_lines.append({"points": [plane_names[i], plane_names[(i + 1) % 4]],
75:                           "dash": False, "color": "#9ca3af"})
76: 
77:     # 平面标签 α,放在平面一角外侧
78:     vs["α"] = tk.vadd(Op, tk.vadd(tk.vmul(u, 1.06 * s), tk.vmul(v, 1.12 * s)))
79: 
80:     # ---------- 关键辅助线 ----------
81:     # OO′:球心到截面的垂线段,虚线
82:     aux_lines.append({"points": ["O", "O′"], "dash": True, "color": "#374151", "width": 2.0})
83: 
84:     # OP:球的半径
85:     edges.append({"points": ["O", "P"], "dash": False, "color": "#374151", "width": 1.8})
86: 
87:     # O′P:截面圆半径,高亮红色加粗
88:     edges.append({"points": ["O′", "P"], "dash": False, "color": "#dc2626", "width": 4.0})
89: 
90:     # ---------- 显式修复:在 O′ 处添加 OO′⊥α 的直角标记 ----------
91:     # 直角标记的一条边沿 O′O,另一条边沿截面内的 O′P,表示 OO′ 垂直于截面平面 α。
92:     mark = 0.18 * R
93:     to_O = tk.unit(tk.vsub(O, Op))
94:     M = tk.vadd(Op, tk.vmul(to_O, mark))
95:     N = tk.vadd(Op, tk.vmul(pdir, mark))
98:     vs["N"] = N
99:     vs["H"] = H
100:     aux_lines.append({"points": ["O′", "M"], "dash": False, "color": "#111827", "width": 2.4})
101:     aux_lines.append({"points": ["O′", "N"], "dash": False, "color": "#111827", "width": 2.4})
102:     aux_lines.append({"points": ["M", "H"], "dash": False, "color": "#111827", "width": 2.4})
103:     aux_lines.append({"points": ["N", "H"], "dash": False, "color": "#111827", "width": 2.4})
104: 
105:     # 透明球体 primitive
106:     prims = [tk.prim_sphere(r=R, center=O, color="#93c5fd")]
107: 
108:     cam = tk.auto_camera(vs, prims, azim=38.0, elev=25.0, pad=1.34)
109:     spec = tk.make_spec(
110:         "球的截面",
111:         "球截面形状判断 S009;含平面α、截面圆、OO′⊥α直角标记、O′P红色半径与球体隐线",
112:         vs,
113:         faces=faces,
114:         edges=edges,
115:         aux_lines=aux_lines,
116:         primitives=prims,
117:         labels=["O", "O′", "P", "α"],
118:         camera=cam
119:     )
120: 
121:     # 由几何关系计算正确形状:截面上各采样点到 O′ 距离恒等于 r
122:     ds = [tk.dist(vs[name], Op) for name in circle_names[::9]]
123:     shape = "圆" if max(ds) - min(ds) < 1e-6 and 0.0 < d < R else "非圆"
124: 
125:     base_options = {"A": "圆", "B": "椭圆", "C": "抛物线", "D": "球冠"}
126:     keys = list(base_options.keys())
127:     rng.shuffle(keys)
128:     options = {k: base_options[k] for k in keys}
129:     answer = [k for k, val in options.items() if val == shape][0]
131:     return tk.item(
132:         spec,
133:         "如图,平面 α 与球 O 相交,所得截面的真实形状是( )",
134:         options,
135:         answer
136:     )

K26 Spec-to-Image Review

  • major/aux_line_clutter
    手动添加固定赤道大圆并分虚实,非当前视角轮廓线,易误导为另一截面
    spec_fix: 删除B0-B63赤道边;若需轮廓虚线应由toolkit渲染silhouette或计算真实轮廓
  • minor/mesh_clutter
    截面圆离散为72段边,球赤道64段,总边数138条过密
    spec_fix: 截面圆减至32-48段;删除赤道边,降低顶点数
  • minor/label_overlap
    α标签远离平面主体,O与O′在图中距离较近
    spec_fix: α移至平面边界外侧,给O、O′添加像素偏移避免贴线
What To Change
  • 删除64段固定赤道边与顶点
  • 截面圆边数降至36-48段
  • α标签移至平面边界附近
Preferred Primitives
  • sphere
Remove/Simplify
  • 移除手动equator边B0-B63
  • 简化截面圆为36-48段
Label Fixes
  • α靠近平面边界左上外侧
  • O与O′增加标签偏移
Camera Fixes
  • 保持当前相机视角
Toolkit/Renderer: 建议toolkit支持sphere轮廓silhouette自动渲染,避免手动离散大圆
representation: 球primitive正确,但错误用固定赤道冒充轮廓,截面圆72段过度离散
readability: 单视角能表达截面关系,但多余水平大圆易造成误解,线网偏密
plan: 截面圆、垂线、直角标记、红色半径均已画出,但球体轮廓虚线实现错误
next: spec_rewrite

solid3d_spatial_tangent_external__S011

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 3 major_spec_issue rewrite hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
直立圆锥轴截面为等腰三角形,顶点P、底面圆心O,轴PO用点画线。圆锥内部画一个虚线球(球心O₁),与圆锥侧面及底面均相切,表示内切。圆锥右外侧画一个实线球(球心O₂),其表面与圆锥侧面沿一个水平公切圆相切;该切圆与圆锥前后轮廓线交于A、B两点,切圆位于圆锥内部的下半段画虚线。过A、B作细双点划线表示公切圆所在辅助平面。圆锥左外侧再画一个与圆锥无接触的小球(球心O₃)。圆锥底面后半圆及被遮挡轮廓均画虚线。
Must Render
[
  "直立圆锥及其轴PO(点画线)",
  "圆锥内部虚线内切球(球心O₁)",
  "圆锥右侧实线外切球(球心O₂)",
  "两体公切圆(上半实线、下半虚线)及切点A、B",
  "公切圆所在辅助平面(细双点划线)",
  "圆锥左侧分离小球(球心O₃)",
  "圆锥底面后半圆虚线"
]

Spec Summary

{
  "title": "圆锥与球位置关系",
  "meta": "体体外切",
  "camera": {
    "position": [
      19.134,
      18.801,
      34.187
    ],
    "target": [
      -0.601,
      1.228,
      0.005
    ],
    "orthoSize": 13.028
  },
  "counts": {
    "vertices": 90,
    "faces": 0,
    "edges": 8,
    "aux_lines": 73,
    "aux_faces": 0,
    "primitives": 3,
    "labels": 7
  },
  "primitive_types": [
    "cone",
    "sphere",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cone",
      "radius": 5.115908805880604,
      "height": 4.0,
      "position": [
        0.0,
        4.0,
        0.0
      ]
    },
    {
      "type": "sphere",
      "radius": 6.10209215173816,
      "position": [
        0.0,
        -3.7458799011228887,
        0.0
      ]
    },
    {
      "type": "sphere",
      "radius": 0.3017833500585927,
      "position": [
        -6.27380111083863,
        1.7753702093470456,
        0.17027915342086358
      ]
    }
  ],
  "labels": [
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "O1",
      "text": "O1"
    },
    {
      "point": "O2",
      "text": "O2"
    },
    {
      "point": "O3",
      "text": "O3"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    }
  ],
  "vertex_names": [
    "P",
    "O",
    "O1",
    "O2",
    "O3",
    "A",
    "B",
    "bd0",
    "bd1",
    "bd2",
    "bd3",
    "bd4",
    "bd5",
    "bd6",
    "bd7",
    "bd8",
    "bd9",
    "bd10",
    "bd11",
    "bd12",
    "cr0",
    "cr1",
    "cr2",
    "cr3",
    "cr4",
    "cr5",
    "cr6",
    "cr7",
    "cr8",
    "cl0",
    "cl1",
    "cl2",
    "cl3",
    "cl4",
    "cl5",
    "cl6",
    "cl7",
    "cl8",
    "pl0",
    "pl1",
    "pl2",
    "pl3",
    "s0_0",
    "s0_1",
    "s0_2",
    "s0_3",
    "s0_4",
    "s0_5",
    "s0_6",
    "s0_7",
    "s0_8",
    "s0_9",
    "s0_10",
    "s0_11",
    "s0_12",
    "s0_13",
    "s0_14",
    "s0_15",
    "s1_0",
    "s1_1",
    "s1_2",
    "s1_3",
    "s1_4",
    "s1_5",
    "s1_6",
    "s1_7",
    "s1_8",
    "s1_9",
    "s1_10",
    "s1_11",
    "s1_12",
    "s1_13",
    "s1_14",
    "s1_15",
    "s2_0",
    "s2_1",
    "s2_2",
    "s2_3",
    "s2_4",
    "s2_5",
    "s2_6",
    "s2_7",
    "s2_8",
    "s2_9",
    "s2_10",
    "s2_11",
    "s2_12",
    "s2_13",
    "s2_14",
    "s2_15"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "cr0",
        "cr1"
      ]
    },
    {
      "points": [
        "cr1",
        "cr2"
      ]
    },
    {
      "points": [
        "cr2",
        "cr3"
      ]
    },
    {
      "points": [
        "cr3",
        "cr4"
      ]
    },
    {
      "points": [
        "cr4",
        "cr5"
      ]
    },
    {
      "points": [
        "cr5",
        "cr6"
      ]
    },
    {
      "points": [
        "cr6",
        "cr7"
      ]
    },
    {
      "points": [
        "cr7",
        "cr8"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "P",
        "O"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "bd0",
        "bd1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd1",
        "bd2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd2",
        "bd3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd3",
        "bd4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd4",
        "bd5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd5",
        "bd6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd6",
        "bd7"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd7",
        "bd8"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd8",
        "bd9"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd9",
        "bd10"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd10",
        "bd11"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "bd11",
        "bd12"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "cl0",
        "cl1"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl1",
        "cl2"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl2",
        "cl3"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl3",
        "cl4"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl4",
        "cl5"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl5",
        "cl6"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl6",
        "cl7"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "cl7",
        "cl8"
      ],
      "dash": true,
      "color": "#333333"
    },
    {
      "points": [
        "pl0",
        "pl1"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "pl1",
        "pl2"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "pl2",
        "pl3"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "pl3",
        "pl0"
      ],
      "dash": true,
      "color": "#b0b0b0"
    },
    {
      "points": [
        "s0_0",
        "s0_1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_1",
        "s0_2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_2",
        "s0_3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_3",
        "s0_4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_4",
        "s0_5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_5",
        "s0_6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_6",
        "s0_7"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_7",
        "s0_8"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_8",
        "s0_9"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_9",
        "s0_10"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_10",
        "s0_11"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_11",
        "s0_12"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_12",
        "s0_13"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_13",
        "s0_14"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_14",
        "s0_15"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0_15",
        "s0_0"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_0",
        "s1_1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_1",
        "s1_2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_2",
        "s1_3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_3",
        "s1_4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_4",
        "s1_5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_5",
        "s1_6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1_6",
        "s1_7"
      ],
      "dash": true,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 1. 随机化圆锥尺寸(矮胖型,确保外切球位于底面下方外部)
5:     H = rng.choice([2.5, 3.0, 3.5, 4.0])

6:     R = rng.uniform(H * 0.9, H * 1.4)
7:     L = math.hypot(R, H)
8: 
9:     # 切圆高度(限制在使外切球心 y2<0 的范围内)
10:     h_max = H * R * R / (R * R + H * H) * 0.85
11:     h_t = rng.uniform(0.3, max(0.5, h_max))
12: 
13:     # 外切球 O₂ 几何参数(轴线上,底面下方,与侧面外切于水平圆)
14:     c = H - h_t
15:     u = c * L * L / (H * H)
16:     y2 = H - u
17:     R2 = R * u / L
23:         R2 = R * u / L
24: 
25:     # 内切球 O₁(与侧面及底面相切)
26:     r1 = R * H / (R + L)
27: 
28:     # 左侧分离小球 O₃
29:     R3 = rng.uniform(0.25, 0.45)
30:     x3 = -R - rng.uniform(0.8, 1.5)
31:     y3 = rng.uniform(R3 + 0.3, H - 0.5)
32:     z3 = rng.uniform(-0.3, 0.3)
33: 
34:     # 切圆半径及 A、B 两点(前后与轮廓线交点)
35:     r_t = R * (1.0 - h_t / H)
36:     P = (0.0, H, 0.0)
37:     O = (0.0, 0.0, 0.0)
38:     O1 = (0.0, r1, 0.0)
44:     vs = {"P": P, "O": O, "O1": O1, "O2": O2, "O3": O3, "A": A, "B": B}
45: 
46:     # 2. Primitives:圆锥 + 外切球 + 左侧小球
47:     prims = [
48:         tk.prim_cone(r=R, h=H, center=(0.0, H / 2.0, 0.0)),
49:         tk.prim_sphere(r=R2, center=O2),
50:         tk.prim_sphere(r=R3, center=O3),
51:     ]
52: 
53:     edges = []
54:     aux_lines = []
55: 
56:     # 轴 PO(点画线/虚线)
57:     aux_lines.append(tk.aux("P", "O", dash=True, color="#555555"))
58: 
59:     # 底面后半圆(左半弧)虚线
60:     m = 12
61:     for i in range(m):
65:         vs[n1] = (R * math.cos(a1), 0.0, R * math.sin(a1))
66:         vs[n2] = (R * math.cos(a2), 0.0, R * math.sin(a2))
67:         aux_lines.append(tk.aux(n1, n2, dash=True, color="#888888"))
68: 
69:     # 公切圆:上半(右半弧,朝向相机)实线 edges;下半(左半弧,背向相机)虚线 aux
70:     n = 16
71:     # 上半/右半弧:t 从 0 到 π,对应 x >= 0,使用实线 edges
72:     for i in range(n // 2):
73:         t1 = math.pi * i / (n // 2)
74:         t2 = math.pi * (i + 1) / (n // 2)
75:         n1, n2 = f"cr{i}", f"cr{i+1}"
76:         vs[n1] = (r_t * math.sin(t1), h_t, r_t * math.cos(t1))
77:         vs[n2] = (r_t * math.sin(t2), h_t, r_t * math.cos(t2))
78:         edges.append(tk.edge(n1, n2))
79:     # 下半/左半弧:t 从 π 到 2π,对应 x <= 0,使用虚线 aux
80:     for i in range(n // 2):
81:         t1 = math.pi + math.pi * i / (n // 2)
82:         t2 = math.pi + math.pi * (i + 1) / (n // 2)
84:         vs[n1] = (r_t * math.sin(t1), h_t, r_t * math.cos(t1))
85:         vs[n2] = (r_t * math.sin(t2), h_t, r_t * math.cos(t2))
86:         aux_lines.append(tk.aux(n1, n2, dash=True, color="#333333"))
87: 
88:     # 辅助平面(细双点划线矩形框,过 A、B;以浅色虚线近似)
89:     s = r_t * 1.3
90:     corners = [(s, h_t, r_t), (-s, h_t, r_t), (-s, h_t, -r_t), (s, h_t, -r_t)]
93:         vs[cnames[i]] = p
94:     for i in range(4):
95:         aux_lines.append(tk.aux(cnames[i], cnames[(i + 1) % 4], dash=True, color="#b0b0b0"))
96: 
97:     # 内切球 O₁ 虚线线框(三个正交大圆,确保闭合)
98:     circles = [
99:         lambda a: (r1 * math.cos(a), r1, r1 * math.sin(a)),          # 水平圆
100:         lambda a: (r1 * math.cos(a), r1 + r1 * math.sin(a), 0.0),    # x-y 平面垂直圆
101:         lambda a: (0.0, r1 + r1 * math.sin(a), r1 * math.cos(a)),    # y-z 平面垂直圆
109:             n1 = names[i]
110:             n2 = names[(i + 1) % seg]
111:             aux_lines.append(tk.aux(n1, n2, dash=True, color="#888888"))
112: 
113:     # 3. 相机与 spec
114:     cam = tk.auto_camera(vs, prims, azim=30.0, elev=24.0, pad=1.6)
115:     spec = tk.make_spec(
116:         "圆锥与球位置关系",
117:         "体体外切",
118:         vs,
119:         edges=edges,
120:         aux_lines=aux_lines,
121:         primitives=prims,
122:         labels=["P", "O", "O1", "O2", "O3", "A", "B"],
123:         camera=cam,
124:     )
125: 
126:     # 4. 选项(固定四个词,随机顺序)
127:     opts = ["外离", "外切", "内切", "相交"]
128:     rng.shuffle(opts)
129:     opts_dict = {chr(65 + i): opts[i] for i in range(4)}
130:     ans = [k for k, v in opts_dict.items() if v == "外切"][0]
131: 
132:     return tk.item(
133:         spec,
134:         "如图,图中球O₂与圆锥的位置关系是?",
135:         opts_dict,
136:         ans,
137:         answer_text="外切",
138:     )

K26 Spec-to-Image Review

  • major/sphere_render
    内切球O₁由48条aux线模拟三个正交大圆,spec primitives中无O₁的sphere primitive
    spec_fix: 添加tk.prim_sphere(r=r1,center=O1)并设虚线线框样式,删除手动aux大圆
  • fatal/shape_primitive
    O₂球心x=0、半径6.1,球体包含圆锥底面(y=0在球内),图呈圆锥插于球顶
    spec_fix: 将O₂偏心至右侧(x₂>0),重新计算半径使球位于圆锥右外侧且不贯穿底面
  • major/camera_or_view
    orthoSize 13使O₂占据画面80%以上,圆锥压缩至顶部边缘,主体失衡
    spec_fix: target调至圆锥中部(0,H/2,0),orthoSize缩至8-10,使圆锥与球比例协调
  • major/label_overlap
    O、O₁、O₂、A、B标签密集重叠于球体上方,O₃半径仅0.3几乎不可见
    spec_fix: 为重叠标签添加引线,O₃标签旁加指示点或适当放大半径
  • minor/tangent_or_section
    公切圆实虚分段与球面/圆锥轮廓线混杂,辅助平面框辨识度低
    spec_fix: 加粗公切圆实线弧,辅助平面改用显著双点划线并提高对比度
What To Change
  • O₂改为偏心右置(x₂>0)并重新计算半径,避免贯穿底面
  • O₁添加sphere primitive并设置虚线线框样式
  • 整体调整比例使圆锥与球视觉协调
  • 相机target移至圆锥中部并缩小orthoSize
Preferred Primitives
  • cone
  • sphere
  • sphere
  • sphere
  • angle_arc
Remove/Simplify
  • 移除O₁的48条手动aux大圆线
  • 简化公切圆分段为显著实/虚弧
  • 减少底面虚线分段数
Label Fixes
  • O/O₁/O₂/A/B添加引线避免重叠
  • O₃标签旁加指示点
Camera Fixes
  • target移至(0, H/2, 0)
  • orthoSize降至约8-10
  • 增加俯视角度以展示右外侧
Toolkit/Renderer: 需确认renderer是否支持sphere primitive的虚线线框渲染;若不支持,需toolkit增加sphere_wireframe或dashed_sphere模式
representation: 核心对象O₁缺失sphere primitive,O₂几何参数导致视觉表达与plan严重不符,球体过度遮挡圆锥。
readability: 单图被巨大球体主导,多视角未改善比例问题,关键切点与虚线底面难以辨认。
plan: plan要求的右外侧外切、内部虚线内切球、公切圆实虚分段、辅助平面均未得到清晰准确的图形表达。
next: spec_rewrite

solid3d_spatial_tangent_external__S013

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 3 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L2
Scene
绘制直圆锥与球体的外切组合:圆锥底面水平,顶点S朝上,底面圆心C;球O位于圆锥右侧外部,球心与圆锥共轴。球与圆锥侧面相切,切点构成水平圆,在轴截面中显示为母线上的切点T。用红色实线画出球心O到切点T的辅助半径OT(OT⊥母线),用虚线画出圆锥轴线SC及被遮挡的底面直径。标注球半径R、圆锥底面半径r,并将切点T高亮显示。
Must Render
[
  "球O与圆锥侧面在轴截面内的切点T",
  "球心O到切点T的辅助半径OT(高亮)",
  "圆锥轴线SC(虚线)"
]

Spec Summary

{
  "title": "圆锥与球外切",
  "meta": "圆锥球体外切",
  "camera": {
    "position": [
      11.063,
      8.284,
      17.084
    ],
    "target": [
      0.388,
      0.551,
      0.0
    ],
    "orthoSize": 5.696
  },
  "counts": {
    "vertices": 8,
    "faces": 0,
    "edges": 0,
    "aux_lines": 3,
    "aux_faces": 0,
    "primitives": 2,
    "labels": 6
  },
  "primitive_types": [
    "cone",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cone",
      "radius": 2.644421851525048,
      "height": 4.136931604410454,
      "position": [
        0,
        2.068465802205227,
        0
      ]
    },
    {
      "type": "sphere",
      "radius": 2.63593125663844,
      "position": [
        0,
        -0.7572091820935762,
        0
      ]
    }
  ],
  "labels": [
    {
      "point": "{'point': 'S', 'text': 'S'}",
      "text": "{'point': 'S', 'text': 'S'}"
    },
    {
      "point": "{'point': 'C', 'text': 'C'}",
      "text": "{'point': 'C', 'text': 'C'}"
    },
    {
      "point": "{'point': 'O', 'text': 'O'}",
      "text": "{'point': 'O', 'text': 'O'}"
    },
    {
      "point": "{'point': 'T', 'text': 'T'}",
      "text": "{'point': 'T', 'text': 'T'}"
    },
    {
      "point": "{'point': 'LR', 'text': 'R'}",
      "text": "{'point': 'LR', 'text': 'R'}"
    },
    {
      "point": "{'point': 'Lr', 'text': 'r'}",
      "text": "{'point': 'Lr', 'text': 'r'}"
    }
  ],
  "vertex_names": [
    "C",
    "S",
    "O",
    "T",
    "B",
    "D",
    "LR",
    "Lr"
  ],
  "faces_sample": [],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "T"
      ],
      "dash": false,
      "color": "#e03131"
    },
    {
      "points": [
        "S",
        "C"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "D",
        "B"
      ],
      "dash": true,
      "color": "#495057"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 1. 随机几何参数(直圆锥 + 同轴外切球)
5:     rc = rng.uniform(1.8, 2.8)          # 圆锥底面半径

6:     h  = rng.uniform(3.0, 4.5)          # 圆锥高
7:     L  = math.hypot(rc, h)              # 母线长
8: 
9:     # 保证球心在底面下方(y0<0)且切点落在有限母线上
10:     t_min = h * h / (rc * rc + h * h) + 0.05
11:     t = rng.uniform(t_min, 0.95)        # 切点T在母线上的比例参数
12: 
13:     y0 = h - t * L * L / h              # 球心O的y坐标(<0)
14:     R  = t * rc * L / h                 # 球半径(OT长度)
15: 
16:     # 2. 关键顶点
17:     C  = (0.0, 0.0, 0.0)
18:     S  = (0.0, h, 0.0)
19:     O  = (0.0, y0, 0.0)
20:     T  = (rc * t, h * (1.0 - t), 0.0)   # 右侧母线上的切点
21:     B  = (rc, 0.0, 0.0)
22:     D  = (-rc, 0.0, 0.0)
23:     LR = tk.midpoint(O, T)              # 标注 R 的位置
24:     Lr = (rc * 0.5, 0.0, 0.0)           # 标注 r 的位置
25: 
26:     vertices = {"C": C, "S": S, "O": O, "T": T,
27:                 "B": B, "D": D, "LR": LR, "Lr": Lr}
28: 
29:     # 3. 实体 primitive
30:     cone   = tk.prim_cone(r=rc, h=h, center=(0, 0, 0))
31:     sphere = tk.prim_sphere(r=R, center=(0, y0, 0))
32: 
33:     # 4. 辅助线(must_render)
34:     aux_lines = [
35:         tk.aux("O", "T", dash=False, color="#e03131"),   # OT 半径,红色实线,高亮切点
36:         tk.aux("S", "C", dash=True,  color="#495057"),   # 轴线 SC,虚线
37:         tk.aux("D", "B", dash=True,  color="#495057"),   # 被遮挡的底面直径,虚线
38:     ]
39: 
40:     # 5. 标注
41:     labels = [
42:         {"point": "S",  "text": "S"},
43:         {"point": "C",  "text": "C"},
44:         {"point": "O",  "text": "O"},
45:         {"point": "T",  "text": "T"},
48:     ]
49: 
50:     spec = tk.make_spec("圆锥与球外切", "圆锥球体外切", vertices,
51:                         aux_lines=aux_lines, labels=labels,
52:                         primitives=[cone, sphere])
53: 
54:     # 6. 几何判定答案(由计算得出,非写死)
55:     # 球心到母线 SB 的距离
56:     SB = (rc, -h, 0.0)
57:     SO = (0.0, h - y0, 0.0)
58:     dist_to_generatrix = tk.norm(tk.cross(SO, SB)) / tk.norm(SB)
59: 
60:     # 同轴、相切且球心在底面下方 => 外切
61:     gold = "外切" if (abs(dist_to_generatrix - R) < 1e-6 and y0 < -1e-6) else "相交"
62: 
63:     # 7. 选项(固定四词,随机顺序)
64:     raw_opts = {"A": "外切", "B": "外离", "C": "相交", "D": "内切"}
65:     items = list(raw_opts.items())
66:     rng.shuffle(items)
67:     options = {k: v for k, v in items}
68:     ans = [k for k, v in options.items() if v == gold][0]
69: 
70:     question = ("如图,圆锥的顶点为S,底面圆心为C;球O的球心位于圆锥的轴线上,"
71:                 "且球O与圆锥侧面有公共点。根据图形中两体的实际接触方式判断,"
72:                 "球O与圆锥的位置关系属于( )")
73: 
74:     return tk.item(spec, question, options, ans)

K26 Spec-to-Image Review

  • fatal/label_missing
    两渲染图中均无任何字母标签,S/C/O/T/R/r全部缺失,关键元素完全不可识别。
    spec_fix: 修正label嵌套字符串化bug,显式添加S,C,O,T,R,r六个标签并调整位置避免被球遮挡。
  • major/sphere_render
    sphere primitive渲染为实心蓝色填充,完全遮挡圆锥底面圆及轴线中段,不符合教材线框表达规范。
    spec_fix: 要求renderer支持sphere线框/大圆+截面圆模式;spec中追加球的大圆与水平截面圆以替代实心填充。
  • major/cone_cylinder_render
    圆锥底面圆被实心球完全遮挡且未补画,图中仅见悬浮的两条母线,底面不可见导致不像教材圆锥。
    spec_fix: 显式添加圆锥底面椭圆(虚线或实线);或调整几何参数使球顶低于底面以暴露底面圆。
  • major/tangent_or_section
    切点T仅由红色OT线端点暗示,无高亮圆点/十字标记,且T点标签缺失导致不可定位。
    spec_fix: 在T点处添加显著点标记(如小圆点),并确保OT⊥母线的几何关系在图中可读。
  • minor/camera_or_view
    两视角差异小,且共轴布置使球位于圆锥正下方,未体现plan所述'右侧外部'空间关系。
    spec_fix: 将相机target略左移或增加纯轴截面视角,使球与圆锥的左右外切关系更明确。
What To Change
  • 修复labels格式与缺失问题
  • 球改为线框/大圆+截面圆渲染
  • 补画圆锥底面圆
  • T点添加高亮标记
  • 调整相机突出外切与底面
Preferred Primitives
  • sphere
  • cone
  • section_circle
Remove/Simplify
  • 取消球实心填充效果
  • 移除被球遮挡的冗余轴线中段虚线
Label Fixes
  • 添加S,C,O,T,R,r并避开球体遮挡
  • 修复label字典被字符串化的bug
Camera Fixes
  • 降低俯角并右偏相机以展示底面与切点
  • 补充正视轴截面视角
Toolkit/Renderer: renderer需支持sphere线框/大圆+截面圆渲染模式;修复label系统避免字典被错误字符串化。
representation: 虽正确使用cone与sphere primitive,但sphere实心填充导致严重遮挡,且底面圆缺失;标签数据格式错误致全部未渲染。
readability: 无标签、球实心遮挡、底面不可见,导致外切关系与关键点位无法识别;双视角信息冗余且差异小。
plan: plan要求的T高亮、R/r标注、右侧外部空间感均未呈现;共轴几何使球与底面相交,底面不可见。
next: spec_rewrite

solid3d_spatial_tangent_external__S001

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L2
Scene
两个等半径直圆柱左右水平放置,轴线平行且圆心距等于2r。左侧圆柱轴线上端点A、下端点A′,右侧为B、B′,轴线用虚线表示。取一个垂直于两轴线的辅助截面,截面内画出两等圆及其外切点P,圆心连线O₁O₂水平。两圆柱侧面沿过P的竖直公共母线l外切,l用红色实线高亮。圆柱被遮挡的底面与轮廓用虚线绘制。
Must Render
[
  "两个等半径直圆柱的可见轮廓及被遮挡的虚线底面与轮廓",
  "两圆柱的平行轴线(虚线)及端点A、A′、B、B′标注",
  "垂直于轴线的辅助截面及其内的两外切等圆、圆心O₁O₂连线、切点P",
  "过切点P的竖直公共母线l(红色高亮实线)及标注"
]

Spec Summary

{
  "title": "两圆柱外切",
  "meta": "solid3d_spatial_tangent_external__S001",
  "camera": {
    "position": [
      5.767,
      5.856,
      10.7
    ],
    "target": [
      -0.0,
      -0.0,
      0.0
    ],
    "orthoSize": 3.488
  },
  "counts": {
    "vertices": 109,
    "faces": 37,
    "edges": 32,
    "aux_lines": 36,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 8
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "A'",
      "text": "A'"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "B'",
      "text": "B'"
    },
    {
      "point": "O1",
      "text": "O1"
    },
    {
      "point": "O2",
      "text": "O2"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "l",
      "text": "l"
    }
  ],
  "vertex_names": [
    "S1",
    "S2",
    "S3",
    "S4",
    "c0f0",
    "c0b0",
    "c0f1",
    "c0b1",
    "c0f2",
    "c0b2",
    "c0f3",
    "c0b3",
    "c0f4",
    "c0b4",
    "c0f5",
    "c0b5",
    "c0f6",
    "c0b6",
    "c0f7",
    "c0b7",
    "c0f8",
    "c0b8",
    "c0f9",
    "c0b9",
    "c0f10",
    "c0b10",
    "c0f11",
    "c0b11",
    "c0f12",
    "c0b12",
    "c0f13",
    "c0b13",
    "c0f14",
    "c0b14",
    "c0f15",
    "c0b15",
    "A",
    "A'",
    "O1",
    "O1_0",
    "O1_1",
    "O1_2",
    "O1_3",
    "O1_4",
    "O1_5",
    "O1_6",
    "O1_7",
    "O1_8",
    "O1_9",
    "O1_10",
    "O1_11",
    "O1_12",
    "O1_13",
    "O1_14",
    "O1_15",
    "c1f0",
    "c1b0",
    "c1f1",
    "c1b1",
    "c1f2",
    "c1b2",
    "c1f3",
    "c1b3",
    "c1f4",
    "c1b4",
    "c1f5",
    "c1b5",
    "c1f6",
    "c1b6",
    "c1f7",
    "c1b7",
    "c1f8",
    "c1b8",
    "c1f9",
    "c1b9",
    "c1f10",
    "c1b10",
    "c1f11",
    "c1b11",
    "c1f12",
    "c1b12",
    "c1f13",
    "c1b13",
    "c1f14",
    "c1b14",
    "c1f15",
    "c1b15",
    "B",
    "B'",
    "O2",
    "O2_0",
    "O2_1",
    "O2_2",
    "O2_3",
    "O2_4",
    "O2_5",
    "O2_6",
    "O2_7",
    "O2_8",
    "O2_9",
    "O2_10",
    "O2_11",
    "O2_12",
    "O2_13",
    "O2_14",
    "O2_15",
    "P",
    "lp",
    "l"
  ],
  "faces_sample": [
    {
      "points": [
        "S1",
        "S2",
        "S3",
        "S4"
      ],
      "color": "#e9ecef",
      "opacity": 0.25
    },
    {
      "points": [
        "c0f15",
        "c0f14",
        "c0f13",
        "c0f12",
        "c0f11",
        "c0f10",
        "c0f9",
        "c0f8",
        "c0f7",
        "c0f6",
        "c0f5",
        "c0f4",
        "c0f3",
        "c0f2",
        "c0f1",
        "c0f0"
      ]
    },
    {
      "points": [
        "c0b0",
        "c0b1",
        "c0b2",
        "c0b3",
        "c0b4",
        "c0b5",
        "c0b6",
        "c0b7",
        "c0b8",
        "c0b9",
        "c0b10",
        "c0b11",
        "c0b12",
        "c0b13",
        "c0b14",
        "c0b15"
      ]
    },
    {
      "points": [
        "c0f0",
        "c0f1",
        "c0b1",
        "c0b0"
      ]
    },
    {
      "points": [
        "c0f1",
        "c0f2",
        "c0b2",
        "c0b1"
      ]
    },
    {
      "points": [
        "c0f2",
        "c0f3",
        "c0b3",
        "c0b2"
      ]
    },
    {
      "points": [
        "c0f3",
        "c0f4",
        "c0b4",
        "c0b3"
      ]
    },
    {
      "points": [
        "c0f4",
        "c0f5",
        "c0b5",
        "c0b4"
      ]
    },
    {
      "points": [
        "c0f5",
        "c0f6",
        "c0b6",
        "c0b5"
      ]
    },
    {
      "points": [
        "c0f6",
        "c0f7",
        "c0b7",
        "c0b6"
      ]
    },
    {
      "points": [
        "c0f7",
        "c0f8",
        "c0b8",
        "c0b7"
      ]
    },
    {
      "points": [
        "c0f8",
        "c0f9",
        "c0b9",
        "c0b8"
      ]
    },
    {
      "points": [
        "c0f9",
        "c0f10",
        "c0b10",
        "c0b9"
      ]
    },
    {
      "points": [
        "c0f10",
        "c0f11",
        "c0b11",
        "c0b10"
      ]
    },
    {
      "points": [
        "c0f11",
        "c0f12",
        "c0b12",
        "c0b11"
      ]
    },
    {
      "points": [
        "c0f12",
        "c0f13",
        "c0b13",
        "c0b12"
      ]
    },
    {
      "points": [
        "c0f13",
        "c0f14",
        "c0b14",
        "c0b13"
      ]
    },
    {
      "points": [
        "c0f14",
        "c0f15",
        "c0b15",
        "c0b14"
      ]
    },
    {
      "points": [
        "c0f15",
        "c0f0",
        "c0b0",
        "c0b15"
      ]
    },
    {
      "points": [
        "c1f15",
        "c1f14",
        "c1f13",
        "c1f12",
        "c1f11",
        "c1f10",
        "c1f9",
        "c1f8",
        "c1f7",
        "c1f6",
        "c1f5",
        "c1f4",
        "c1f3",
        "c1f2",
        "c1f1",
        "c1f0"
      ]
    },
    {
      "points": [
        "c1b0",
        "c1b1",
        "c1b2",
        "c1b3",
        "c1b4",
        "c1b5",
        "c1b6",
        "c1b7",
        "c1b8",
        "c1b9",
        "c1b10",
        "c1b11",
        "c1b12",
        "c1b13",
        "c1b14",
        "c1b15"
      ]
    },
    {
      "points": [
        "c1f0",
        "c1f1",
        "c1b1",
        "c1b0"
      ]
    },
    {
      "points": [
        "c1f1",
        "c1f2",
        "c1b2",
        "c1b1"
      ]
    },
    {
      "points": [
        "c1f2",
        "c1f3",
        "c1b3",
        "c1b2"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "c0f0",
        "c0f1"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f1",
        "c0f2"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f2",
        "c0f3"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f3",
        "c0f4"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f4",
        "c0f5"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f5",
        "c0f6"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f6",
        "c0f7"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f7",
        "c0f8"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f8",
        "c0f9"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f9",
        "c0f10"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f10",
        "c0f11"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f11",
        "c0f12"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f12",
        "c0f13"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f13",
        "c0f14"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f14",
        "c0f15"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c0f15",
        "c0f0"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f0",
        "c1f1"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f1",
        "c1f2"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f2",
        "c1f3"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f3",
        "c1f4"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f4",
        "c1f5"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f5",
        "c1f6"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f6",
        "c1f7"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "c1f7",
        "c1f8"
      ],
      "dash": true,
      "color": "#495057"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "A'"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "O1_0",
        "O1_1"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_1",
        "O1_2"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_2",
        "O1_3"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_3",
        "O1_4"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_4",
        "O1_5"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_5",
        "O1_6"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_6",
        "O1_7"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_7",
        "O1_8"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_8",
        "O1_9"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_9",
        "O1_10"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_10",
        "O1_11"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_11",
        "O1_12"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_12",
        "O1_13"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_13",
        "O1_14"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_14",
        "O1_15"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1_15",
        "O1_0"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "B",
        "B'"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "O2_0",
        "O2_1"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_1",
        "O2_2"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_2",
        "O2_3"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_3",
        "O2_4"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_4",
        "O2_5"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_5",
        "O2_6"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_6",
        "O2_7"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_7",
        "O2_8"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_8",
        "O2_9"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_9",
        "O2_10"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_10",
        "O2_11"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_11",
        "O2_12"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_12",
        "O2_13"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_13",
        "O2_14"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_14",
        "O2_15"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O2_15",
        "O2_0"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "O1",
        "O2"
      ],
      "dash": false,
      "color": "#212529"
    },
    {
      "points": [
        "lp",
        "l"
      ],
      "dash": false,
      "color": "#e03131"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     r = rng.choice([0.8, 1.0, 1.2])
4:     h = round(rng.uniform(1.6, 2.4) * r, 2)
5:     n = rng.choice([12, 16, 20])

6:     vs, faces, auxs, edges = {}, [], [], []
7: 
8:     # 辅助截面(z=0 处的竖直平面,半透明)
9:     m = 2.2 * r
10:     vs.update({"S1": (-m, -r, 0), "S2": (m, -r, 0), "S3": (m, r, 0), "S4": (-m, r, 0)})
11:     faces.append(tk.face(["S1", "S2", "S3", "S4"], color="#e9ecef", opacity=0.25))
12: 
13:     # 两个水平圆柱(轴线沿 z),左右外切,圆心距=2r
14:     for idx, (cx, t, b) in enumerate([(-r, "A", "A'"), (r, "B", "B'")]):
15:         p = f"c{idx}"
16:         for i in range(n):
17:             a = 2 * math.pi * i / n
20:             vs[f"{p}b{i}"] = (x, y, h)
21:         # 底面(z=-h)与顶面(z=h)
22:         faces.append({"points": [f"{p}f{i}" for i in range(n)][::-1]})
23:         faces.append({"points": [f"{p}b{i}" for i in range(n)]})
24:         # 侧面
25:         for i in range(n):
26:             i1 = (i + 1) % n
27:             faces.append({"points": [f"{p}f{i}", f"{p}f{i1}", f"{p}b{i1}", f"{p}b{i}"]})
28:         # 轴线端点与虚线
29:         vs[t] = (cx, 0.0, h)
30:         vs[b] = (cx, 0.0, -h)
31:         auxs.append(tk.aux(t, b, dash=True, color="#495057"))
33:         for i in range(n):
34:             i1 = (i + 1) % n
35:             edges.append(tk.edge(f"{p}f{i}", f"{p}f{i1}", dash=True, color="#495057"))
36:         # 辅助截面内的圆(z=0)—— 以 aux_lines 显式画出
37:         nm = f"O{idx+1}"
38:         vs[nm] = (cx, 0.0, 0.0)
39:         circ = []
40:         for i in range(n):
49:     auxs.append(tk.aux("O1", "O2", dash=False, color="#212529"))
50: 
51:     # 切点 P 与红色公共母线 l
52:     vs["P"] = (0.0, 0.0, 0.0)
53:     vs["lp"] = (0.0, 0.0, -h)
54:     vs["l"] = (0.0, 0.0, h)
55:     auxs.append(tk.aux("lp", "l", dash=False, color="#e03131"))
56: 
57:     cam = tk.auto_camera(vs, azim=rng.uniform(28, 36), elev=rng.uniform(18, 26))
58:     spec = tk.make_spec(
59:         "两圆柱外切", "solid3d_spatial_tangent_external__S001",
60:         vs, faces=faces, edges=edges, aux_lines=auxs, camera=cam,
61:         labels=["A", "A'", "B", "B'", "O1", "O2", "P", "l"]
62:     )
63: 
64:     options = {
65:         "A": "两圆柱体外切,其公共部分是一条直线(公共母线)",
66:         "B": "两圆柱体外离,没有公共点",
67:         "C": "两圆柱体相交,其公共部分是一个圆",
68:         "D": "两圆柱体内切,其公共部分是一个圆"
69:     }
70:     ans = "A"
71:     return tk.item(
72:         spec,

K26 Spec-to-Image Review

  • major/cylinder_render
    圆柱被硬搓为16棱柱,侧面16个四边形面与32条边组成密集线网,顶底面呈多边形
    spec_fix: 使用cylinder primitive;或改为两条轮廓母线+顶底椭圆,删除所有侧面网格faces与edges
  • major/mesh_clutter
    可见顶面被画为16条虚线边,被遮挡底面却无边线,轮廓虚实关系颠倒
    spec_fix: 可见轮廓母线用实线,被遮挡底面画虚线椭圆;删除顶面虚线多边形边
  • minor/aux_line_clutter
    辅助截面内两圆由16段折线拼接,呈明显多边形而非光滑圆
    spec_fix: 使用circle primitive或64段以上aux_line绘制截面圆
  • minor/label_overlap
    A'、B'与顶面线重叠,l标签贴近红色母线不易辨认
    spec_fix: A'、B'沿轴线向两端外移,l标签水平右移避开线条
What To Change
  • 圆柱改用轮廓式表示:前后两条母线+顶底椭圆,删除侧面网格
  • 截面圆改用高分辨率圆或primitive
  • 修正虚实线逻辑
Preferred Primitives
  • cylinder
  • circle
  • ellipse
Remove/Simplify
  • 删除所有侧面四边形faces
  • 删除顶底面16条虚线edges
  • 简化截面圆为多段线→primitive
Label Fixes
  • A'、B'沿轴线外移
  • l标签右移避线
Camera Fixes
  • section_plane改为沿z轴正交投影,消除残余斜透视
Toolkit/Renderer: 增加cylinder与circle primitive支持,使教材式光滑曲面体无需硬搓多边形网格
representation: 圆柱被硬搓为16棱柱,侧面网格与顶底多边形边导致严重失真,不符合教材图示规范
readability: 两视角均能辨认外切关系,但密集线网干扰阅读,圆柱轮廓破碎
plan: 所有要求元素均已出现,但几何表示质量低
next: toolkit_patch

solid3d_spatial_tangent_external__S004

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
绘制一个直立正圆台,轴截面呈等腰梯形,上下底水平、母线外倾。圆台右侧放置一球,球与圆台侧面外切,切点标记为T。作出圆台中心轴线(贯穿上下底的虚线)及球心O到轴线的垂线OE(虚线)。在轴截面视图中,球的大圆与梯形右侧腰单点相切,半径OT画成实线且明确垂直于该腰。圆台被遮挡的下底后半沿及轴线后段均用虚线表示,球整体位于圆台外部。
Must Render
[
  "圆台轴截面等腰梯形轮廓(可见母线和上下底实线)",
  "球的大圆实线",
  "切点T及垂直半径OT(实线)",
  "圆台轴线及隐藏棱线(虚线)",
  "球心O与垂足E标记"
]

Spec Summary

{
  "title": "圆台与球外切",
  "meta": "轴截面示意图",
  "camera": {
    "position": [
      5.695,
      30.079,
      2.263
    ],
    "target": [
      5.695,
      0.0,
      2.0
    ],
    "orthoSize": 8.18
  },
  "counts": {
    "vertices": 39,
    "faces": 0,
    "edges": 36,
    "aux_lines": 0,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 7
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "T",
      "text": "T"
    }
  ],
  "vertex_names": [
    "F0",
    "F1",
    "F2",
    "F3",
    "S0",
    "S1",
    "S2",
    "S3",
    "T0",
    "T1",
    "T2",
    "T3",
    "A",
    "B",
    "C",
    "D",
    "M0",
    "M1",
    "O",
    "E",
    "T",
    "a2",
    "b2",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "F0",
        "F1"
      ]
    },
    {
      "points": [
        "F1",
        "F2"
      ]
    },
    {
      "points": [
        "F2",
        "F3"
      ]
    },
    {
      "points": [
        "F3",
        "F0"
      ]
    },
    {
      "points": [
        "S0",
        "S1"
      ]
    },
    {
      "points": [
        "S1",
        "S2"
      ]
    },
    {
      "points": [
        "S2",
        "S3"
      ]
    },
    {
      "points": [
        "S3",
        "S0"
      ]
    },
    {
      "points": [
        "T0",
        "T1"
      ]
    },
    {
      "points": [
        "T1",
        "T2"
      ]
    },
    {
      "points": [
        "T2",
        "T3"
      ]
    },
    {
      "points": [
        "T3",
        "T0"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "O",
        "T"
      ]
    },
    {
      "points": [
        "M0",
        "M1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "E"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "a2",
        "b2"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "c0",
        "c1"
      ]
    },
    {
      "points": [
        "c1",
        "c2"
      ]
    },
    {
      "points": [
        "c2",
        "c3"
      ]
    },
    {
      "points": [
        "c3",
        "c4"
      ]
    }
  ],
  "aux_lines_sample": [],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     # 三视图排版参数(仅使用 front 框绘制轴截面)
4:     W, H, D, g = 8.0, 5.0, 2.0, 0.8
5:     # 圆台轴截面参数(在 front 框内居中)

6:     R = rng.uniform(1.6, 2.2)   # 下底半宽
7:     r = rng.uniform(0.7, 1.2)   # 上底半宽
8:     h = rng.uniform(2.5, 3.5)   # 圆台高
9:     z0 = (H - h) / 2.0          # 下底 z 坐标
10:     cx = W / 2.0                # 轴线 x 坐标
11:     vs, es, boxes = tk.three_view_layout(W, H, D, g)
12: 
19:     M1 = (cx, 0.0, z0 + h)
20: 
21:     # 右侧母线 BC 上的切点 T
22:     t = rng.uniform(0.35, 0.65)
23:     Tx = B[0] + t * (C[0] - B[0])
24:     Tz = B[2] + t * (C[2] - B[2])
25:     L = math.hypot(R - r, h)
26:     nx = h / L
27:     nz = (R - r) / L
28:     s = rng.uniform(0.6, 1.0)   # 球半径
29:     Ox = Tx + s * nx
30:     Oz = Tz + s * nz
31:     O = (Ox, 0.0, Oz)
32:     E = (cx, 0.0, Oz)
45:     es.append(tk.aux("a2", "b2", dash=True))
46: 
47:     # 球的大圆(16 段近似)
48:     prev = first = None
49:     for i in range(16):
50:         ang = 2 * math.pi * i / 16
51:         nm = "c%d" % i
58:     es.append(tk.edge(prev, first))
59: 
60:     labels = ["A", "B", "C", "D", "O", "E", "T"]
61:     cam = tk.flat_camera(vs)
62:     spec = tk.make_spec("圆台与球外切", "轴截面示意图", vs, edges=es,
63:                         labels=labels, camera=cam)
64: 
65:     opts = ["外切", "内切", "相交", "外离"]
66:     rng.shuffle(opts)
67:     options = dict(zip("ABCD", opts))
68:     ans = [k for k, v in options.items() if v == "外切"][0]
69:     return tk.item(spec, "如图,观察图中球与圆台的空间位置关系,下列判断正确的是( )",
70:                    options, ans)

K26 Spec-to-Image Review

  • major/shape_primitive
    球大圆由16段边硬搓成明显多边形,spec中primitives为空,未调用sphere/circle primitive
    spec_fix: 改用tk.circle或sphere primitive绘制球大圆;若toolkit缺此primitive则需在renderer层补充
  • major/camera_or_view
    使用three_view_layout仅填充主视图,上下及右侧视图框空白,非教材单图版式
    spec_fix: 弃用三视图布局,改为单张轴截面居中绘制,移除空白视图框及多余顶点F/S/T
  • minor/aux_line_clutter
    轴线与OE等辅助虚线使用橙红色(#d9480f),过于醒目且不符合教材常规线色
    spec_fix: 将虚线颜色改为默认黑色或灰色,降低视觉干扰
What To Change
  • 弃用three_view_layout改为单图居中
  • 球大圆改用primitive或平滑曲线
  • 统一虚线为黑/灰色
Preferred Primitives
  • circle
  • sphere
Remove/Simplify
  • 移除16段离散圆边c0-c15
  • 移除空白三视图外框F/S/T及对应顶点
Label Fixes
  • 保持7个标签,微调T位置避免与母线BC重叠
Camera Fixes
  • 调整camera为单图正面正交,居中构图
Toolkit/Renderer: 如tk缺少circle/sphere primitive,需在toolkit层添加;当前圆台轴截面用4边梯形表示可接受
representation: 轴截面用等腰梯形表示圆台合理,但球大圆离散为16边形严重失真,且错误套用三视图空框布局
readability: 外切关系与关键点T、O、E可辨,但多边形球和空白视图框导致整体不像标准教材插图
plan: 基本覆盖plan要求的轴截面、切点T、半径OT、轴线及垂线OE;下底后半沿遮挡在2D截面中无需体现
next: spec_rewrite

solid3d_spatial_tangent_external__S005

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L2
Scene
画面中央画一个轴线水平的直圆柱(左端面圆、右端面圆、轮廓母线,远端母线和底面半圆用虚线)。圆柱右侧紧挨一个球,二者侧面外切。过圆柱轴线和球心作水平辅助截面,用浅灰半透明填充截面矩形ABB'A';该平面截球得大圆(实线),大圆与矩形右侧边相切于红色高亮点P。连接球心O₁与P(实线,标R),O₁到圆柱轴线OO₂作水平虚线(标d=R+r)。球左侧被圆柱遮挡的轮廓用虚线圆弧。
Must Render
[
  "轴线水平的圆柱(含端面圆、轮廓母线、虚线隐藏边)",
  "与圆柱右侧外切的球体",
  "过轴线的水平辅助截面(浅灰填充矩形ABB'A')",
  "截面截球所得大圆(实线)",
  "高亮红色切点P",
  "球心O₁到切点P的半径O₁P",
  "球心到圆柱轴线的水平虚线O₁O₂"
]

Spec Summary

{
  "title": "圆柱球外切",
  "meta": "圆柱球外切轴截面",
  "camera": {
    "position": [
      6.492,
      7.329,
      13.131
    ],
    "target": [
      -0.49,
      1.32,
      0.0
    ],
    "orthoSize": 4.4
  },
  "counts": {
    "vertices": 73,
    "faces": 1,
    "edges": 76,
    "aux_lines": 6,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 9
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.5,
      "position": [
        0.0,
        2.7,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "O1",
      "text": "O₁"
    },
    {
      "point": "O2",
      "text": "O₂"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "B1",
      "text": "B'"
    },
    {
      "point": "A1",
      "text": "A'"
    },
    {
      "point": "d",
      "text": "d=R+r"
    },
    {
      "point": "R_lab",
      "text": "R"
    }
  ],
  "vertex_names": [
    "L0",
    "R0",
    "L1",
    "R1",
    "L2",
    "R2",
    "L3",
    "R3",
    "L4",
    "R4",
    "L5",
    "R5",
    "L6",
    "R6",
    "L7",
    "R7",
    "L8",
    "R8",
    "L9",
    "R9",
    "L10",
    "R10",
    "L11",
    "R11",
    "L12",
    "R12",
    "L13",
    "R13",
    "L14",
    "R14",
    "L15",
    "R15",
    "A",
    "B",
    "B1",
    "A1",
    "O1",
    "O2",
    "P",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23",
    "C24",
    "C25",
    "C26",
    "C27",
    "C28",
    "C29",
    "C30",
    "C31",
    "d",
    "R_lab"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "B1",
        "A1"
      ],
      "color": "#e5e5e5",
      "opacity": 0.3
    }
  ],
  "edges_sample": [
    {
      "points": [
        "L0",
        "L1"
      ]
    },
    {
      "points": [
        "R0",
        "R1"
      ]
    },
    {
      "points": [
        "L1",
        "L2"
      ]
    },
    {
      "points": [
        "R1",
        "R2"
      ]
    },
    {
      "points": [
        "L2",
        "L3"
      ]
    },
    {
      "points": [
        "R2",
        "R3"
      ]
    },
    {
      "points": [
        "L3",
        "L4"
      ]
    },
    {
      "points": [
        "R3",
        "R4"
      ]
    },
    {
      "points": [
        "L4",
        "L5"
      ]
    },
    {
      "points": [
        "R4",
        "R5"
      ]
    },
    {
      "points": [
        "L5",
        "L6"
      ]
    },
    {
      "points": [
        "R5",
        "R6"
      ]
    },
    {
      "points": [
        "L6",
        "L7"
      ]
    },
    {
      "points": [
        "R6",
        "R7"
      ]
    },
    {
      "points": [
        "L7",
        "L8"
      ]
    },
    {
      "points": [
        "R7",
        "R8"
      ]
    },
    {
      "points": [
        "L8",
        "L9"
      ]
    },
    {
      "points": [
        "R8",
        "R9"
      ]
    },
    {
      "points": [
        "L9",
        "L10"
      ]
    },
    {
      "points": [
        "R9",
        "R10"
      ]
    },
    {
      "points": [
        "L10",
        "L11"
      ]
    },
    {
      "points": [
        "R10",
        "R11"
      ]
    },
    {
      "points": [
        "L11",
        "L12"
      ]
    },
    {
      "points": [
        "R11",
        "R12"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "B",
        "B1"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "B1",
        "A1"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "A1",
        "A"
      ],
      "dash": false,
      "color": "#999999"
    },
    {
      "points": [
        "O1",
        "P"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "O1",
        "O2"
      ],
      "dash": true,
      "color": "#333333"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     r = rng.choice([1.0, 1.2, 1.5])
4:     R = rng.choice([1.0, 1.2, 1.5])
5:     if abs(R - r) < 0.1:

8:     vs = {}
9:     es = []
10:     aux_lines = []
11:     faces = []
12:     n = 16
13:     for i in range(n):
14:         ang = 2 * math.pi * i / n
15:         y = r * math.cos(ang)
29:     vs["B1"] = (0.0, r, 0.0)
30:     vs["A1"] = (-a, r, 0.0)
31:     aux_lines.append(tk.aux("A", "B", dash=False, color="#999999"))
32:     aux_lines.append(tk.aux("B", "B1", dash=False, color="#999999"))
33:     aux_lines.append(tk.aux("B1", "A1", dash=False, color="#999999"))
34:     aux_lines.append(tk.aux("A1", "A", dash=False, color="#999999"))
35:     faces.append(tk.face(["A", "B", "B1", "A1"], color="#e5e5e5", opacity=0.3))
36:     vs["O1"] = (0.0, r + R, 0.0)
37:     vs["O2"] = (0.0, 0.0, 0.0)
38:     vs["P"] = (0.0, r, 0.0)
39:     aux_lines.append(tk.aux("O1", "P", dash=False, color="#d9480f"))
40:     aux_lines.append(tk.aux("O1", "O2", dash=True, color="#333333"))
41:     m = 32
42:     for i in range(m):
43:         ang = 2 * math.pi * i / m
44:         x = R * math.cos(ang)
51:     vs["d"] = (0.0, (r + R) * 0.5, 0.0)
52:     vs["R_lab"] = (0.0, r + R * 0.5, 0.0)
53:     prims = [tk.prim_sphere(R, center=(0.0, r + R, 0.0))]
54:     cam = tk.auto_camera(vs, primitives=prims, azim=28.0, elev=22.0, pad=1.5)
55:     spec = tk.make_spec("圆柱球外切", "圆柱球外切轴截面", vs, faces=faces, edges=es,
56:                         aux_lines=aux_lines, primitives=prims, labels=[], camera=cam)
57:     spec["labels"] = [{"point": "O1", "text": "O₁"}, {"point": "O2", "text": "O₂"},
58:                       {"point": "P", "text": "P"}, {"point": "A", "text": "A"},
59:                       {"point": "B", "text": "B"}, {"point": "B1", "text": "B'"},
60:                       {"point": "A1", "text": "A'"}, {"point": "d", "text": f"d=R+r"},
61:                       {"point": "R_lab", "text": "R"}]
62:     q = "如图,球O₁位于圆柱外部且与其侧面外切。过球心与圆柱轴线的辅助截面截圆柱得矩形ABB'A',截球得大圆⊙O₁,二者相切于高亮点P。则两体表面的公共点在空间中实际构成的图形是( )"
63:     opts = {
64:         "A": "唯一公共点P(即图中高亮的切点)",
65:         "B": "一条封闭的圆(切点圆/两体的公共切圆)",
66:         "C": "两体相交,交线为一个圆",
67:         "D": "两体外离,没有任何公共点"
68:     }
69:     return tk.item(spec, q, opts, "B")

K26 Spec-to-Image Review

  • major/shape_primitive
    圆柱被硬搓为16边形棱柱,端面呈明显多边形,76条edges无cylinder primitive
    spec_fix: 改用tk.cylinder primitive绘制圆柱并控制虚线隐藏边;若toolkit缺失则需添加cylinder支持
  • major/label_overlap
    R与d=R+r两标签均挤在O1P连线上,几乎完全重叠,难以分辨
    spec_fix: 将d=R+r标签移至O1O2水平虚线中点旁,R标签移至O1P中点偏上,错开布局
  • minor/mesh_clutter
    圆柱端面由16段短边拼接,轮廓棱角明显,不像教材中的光滑圆柱
    spec_fix: 使用cylinder primitive后自然消除离散棱线,避免硬搓多边形
  • minor/other
    切点P未呈现红色高亮,仅见黑色小点,与plan要求的红色高亮不符
    spec_fix: 在P点附加红色小圆点primitive或设置该顶点高亮颜色为红色
What To Change
  • 将硬搓的16边形棱柱替换为cylinder primitive并保留端面圆
  • 重新布局R与d=R+r标签避免重叠
  • 为P点添加红色高亮点
Preferred Primitives
  • cylinder
  • sphere
Remove/Simplify
  • 移除76条硬搓edges及32个圆柱轮廓顶点
  • 隐藏圆柱远端母线与底面半圆的多余分段线
Label Fixes
  • d=R+r移至O1O2虚线中点右侧
  • R标签移至O1P上半段
  • 检查A' prime符号渲染清晰度
Camera Fixes
  • 当前单视角基本够用,可略拉远orthoSize以缓解标签拥挤
Toolkit/Renderer: 若toolkit未暴露cylinder primitive,需添加并支持端面圆、母线、隐藏虚线参数
representation: 圆柱误用16边形棱柱硬搓,端面呈多边形而非圆,严重偏离教材圆柱图示规范。
readability: 标签R与d=R+r严重重叠,A与A'辨识不清,圆柱多边形棱角干扰几何体识别。
plan: 球体、轴截面矩形、大圆、切点P均已覆盖,但圆柱表示错误导致核心几何关系表达受损。
next: spec_rewrite

solid3d_spatial_tangent_external__S006

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
正三棱锥 S-ABC 底面水平,A 在后、BC 在前。取 BC 中点 D,竖直截面 SAD 截锥体得三角形 SAD(半透明阴影表示)。球心 O 在平面 SAD 内且位于斜高 SD 右侧,球与侧面 SBC 切于 SD 上一点 T,半径 OT 垂直 SD 并标直角符号。球心向底面作垂线 OH 交 AD 于 H。底面后侧边 AB、AC 及左后棱 SB 画虚线;球面上补一条水平辅助圆。SO' 为三棱锥的高。
Must Render
[
  "截面三角形 SAD 及其半透明阴影边界",
  "切点 T 及垂直于 SD 的半径 OT(带直角符号)",
  "球的大圆轮廓(截面过球心)与水平辅助圆",
  "球心 O 到底面的垂线 OH 及垂足 H",
  "被遮挡的三条虚线:AB、AC、SB",
  "三棱锥的高 SO'"
]

Spec Summary

{
  "title": "正三棱锥与外切球",
  "meta": "体体外切-S006;O,S,A,D 共面;aux_lines 显式含 AD、SD、OT、OH、SO' 与 T 处直角符号",
  "camera": {
    "position": [
      4.547,
      5.609,
      7.446
    ],
    "target": [
      -0.0,
      1.439,
      -0.294
    ],
    "orthoSize": 2.385
  },
  "counts": {
    "vertices": 80,
    "faces": 4,
    "edges": 6,
    "aux_lines": 77,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 9
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.28071334002343706,
      "position": [
        0.0,
        1.5462959054180003,
        -0.24956651029863408
      ],
      "color": "#dbeafe"
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "O'",
      "text": "O'"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "S",
    "O'",
    "T",
    "O",
    "H",
    "U",
    "V",
    "W",
    "G0",
    "G1",
    "G2",
    "G3",
    "G4",
    "G5",
    "G6",
    "G7",
    "G8",
    "G9",
    "G10",
    "G11",
    "G12",
    "G13",
    "G14",
    "G15",
    "G16",
    "G17",
    "G18",
    "G19",
    "G20",
    "G21",
    "G22",
    "G23",
    "G24",
    "G25",
    "G26",
    "G27",
    "G28",
    "G29",
    "G30",
    "G31",
    "G32",
    "G33",
    "G34",
    "G35",
    "Q0",
    "Q1",
    "Q2",
    "Q3",
    "Q4",
    "Q5",
    "Q6",
    "Q7",
    "Q8",
    "Q9",
    "Q10",
    "Q11",
    "Q12",
    "Q13",
    "Q14",
    "Q15",
    "Q16",
    "Q17",
    "Q18",
    "Q19",
    "Q20",
    "Q21",
    "Q22",
    "Q23",
    "Q24",
    "Q25",
    "Q26",
    "Q27",
    "Q28",
    "Q29",
    "Q30",
    "Q31"
  ],
  "faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C"
      ],
      "color": "#f8fafc",
      "opacity": 0.12
    },
    {
      "points": [
        "S",
        "A",
        "B"
      ],
      "color": "#eef2f7",
      "opacity": 0.15
    },
    {
      "points": [
        "S",
        "B",
        "C"
      ],
      "color": "#e2e8f0",
      "opacity": 0.17
    },
    {
      "points": [
        "S",
        "C",
        "A"
      ],
      "color": "#f1f5f9",
      "opacity": 0.13
    }
  ],
  "edges_sample": [
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true
    },
    {
      "points": [
        "S",
        "B"
      ],
      "dash": true
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "C"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "D"
      ],
      "dash": false,
      "color": "#334155"
    },
    {
      "points": [
        "S",
        "D"
      ],
      "dash": false,
      "color": "#334155"
    },
    {
      "points": [
        "S",
        "O'"
      ],
      "dash": false,
      "color": "#475569"
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": false,
      "color": "#475569"
    },
    {
      "points": [
        "O",
        "T"
      ],
      "dash": false,
      "color": "#dc2626"
    },
    {
      "points": [
        "T",
        "U"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "U",
        "W"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "W",
        "V"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "V",
        "T"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "G0",
        "G1"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G1",
        "G2"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G2",
        "G3"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G3",
        "G4"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G4",
        "G5"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G5",
        "G6"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G6",
        "G7"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G7",
        "G8"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G8",
        "G9"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G9",
        "G10"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G10",
        "G11"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G11",
        "G12"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G12",
        "G13"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G13",
        "G14"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G14",
        "G15"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G15",
        "G16"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G16",
        "G17"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G17",
        "G18"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G18",
        "G19"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G19",
        "G20"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G20",
        "G21"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G21",
        "G22"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G22",
        "G23"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G23",
        "G24"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G24",
        "G25"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G25",
        "G26"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G26",
        "G27"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G27",
        "G28"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G28",
        "G29"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G29",
        "G30"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G30",
        "G31"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G31",
        "G32"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G32",
        "G33"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G33",
        "G34"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G34",
        "G35"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "G35",
        "G0"
      ],
      "dash": false,
      "color": "#374151"
    },
    {
      "points": [
        "Q0",
        "Q1"
      ],
      "dash": true,
      "color": "#64748b"
    },
    {
      "points": [
        "Q1",
        "Q2"
      ],
      "dash": true,
      "color": "#64748b"
    },
    {
      "points": [
        "Q2",
        "Q3"
      ],
      "dash": true,
      "color": "#64748b"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "S",
        "A",
        "D"
      ],
      "color": "#cbd5e1",
      "opacity": 0.32
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # --- 正三棱锥 S-ABC:底面水平,A 在后,BC 在前 ---
5:     side = rng.uniform(2.7, 3.1)          # 底面等边三角形边长

19:     }
20: 
21:     # --- 侧面 SBC 的外法向:在对称截面 SAD 内,且垂直 SD ---
22:     BC_vec = tk.vsub(C, B)
23:     SB_vec = tk.vsub(S, B)
24:     n_side = tk.unit(tk.cross(BC_vec, SB_vec))
25:     # 令外法向与 A 位于侧面 SBC 的相反侧
27:         n_side = tk.vmul(n_side, -1.0)
28: 
29:     # 切点 T 取在斜高 SD 上,球心 O = T + R * 外法向
30:     SD_vec = tk.vsub(D, S)
31:     t = rng.uniform(0.38, 0.46)           # S -> D 的参数,保证 T 在线段 SD 内
32:     T = tk.vadd(S, tk.vmul(SD_vec, t))
33: 
34:     # 控制半径,使 H 落在 AD 上,同时球清楚可见
35:     max_R_by_z = max(0.18, (-T[2]) / (abs(n_side[2]) + 1e-12) * 0.82)
36:     R = min(rng.uniform(0.26, 0.34), max_R_by_z)
37:     O = tk.vadd(T, tk.vmul(n_side, R))
38:     H = (O[0], 0.0, O[2])                 # OH 垂直底面,H 在 AD 上
50:     vs["W"] = tk.vadd(vs["U"], tk.vmul(ot_u, mark))
51: 
52:     # --- 球的大圆轮廓:平面 SAD 与球的截圆,过球心 O,且经过 T ---
53:     gc_pts = []
54:     n_gc = 36
55:     for i in range(n_gc):
56:         ang = 2.0 * math.pi * i / n_gc
66:         gc_pts.append(nm)
67: 
68:     # --- 球面水平辅助圆:平行于底面 y=常数 ---
69:     hc_pts = []
70:     n_hc = 32
71:     dy = rng.uniform(-0.18, 0.18) * R
72:     rr = math.sqrt(max(0.0, R * R - dy * dy))
80: 
81:     # --- 三棱锥面与棱 ---
82:     faces = [
83:         tk.face(["A", "B", "C"], color="#f8fafc", opacity=0.12),
84:         tk.face(["S", "A", "B"], color="#eef2f7", opacity=0.15),
85:         tk.face(["S", "B", "C"], color="#e2e8f0", opacity=0.17),
86:         tk.face(["S", "C", "A"], color="#f1f5f9", opacity=0.13),
88: 
89:     # 显式列出全部外棱;按计划将 AB、AC、SB 画虚线
90:     edges = [
91:         tk.edge("A", "B", dash=True),
92:         tk.edge("A", "C", dash=True),
93:         tk.edge("S", "B", dash=True),
94:         tk.edge("B", "C", dash=False),
97:     ]
98: 
99:     # --- 截面 SAD 阴影与所有关键辅助线 ---
100:     aux_faces = [
101:         tk.face(["S", "A", "D"], color="#cbd5e1", opacity=0.32)
102:     ]
103: 
104:     aux_lines = [
105:         # R1 修复:显式补画截面边界 AD、SD
106:         tk.aux("A", "D", dash=False, color="#334155"),
107:         tk.aux("S", "D", dash=False, color="#334155"),
108: 
109:         # SO' 为三棱锥的高;OH 垂直底面
111:         tk.aux("O", "H", dash=False, color="#475569"),
112: 
113:         # 切半径 OT
114:         tk.aux("O", "T", dash=False, color="#dc2626"),
115: 
116:         # R1 修复:T 处直角符号四条边,明确表示 OT ⟂ SD
117:         tk.aux("T", "U", dash=False, color="#111827"),
123:     # 大圆轮廓
124:     for i in range(n_gc):
125:         aux_lines.append(tk.aux(gc_pts[i], gc_pts[(i + 1) % n_gc],
126:                                 dash=False, color="#374151"))
127: 
128:     # 水平辅助圆
129:     for i in range(n_hc):
130:         aux_lines.append(tk.aux(hc_pts[i], hc_pts[(i + 1) % n_hc],
131:                                 dash=True, color="#64748b"))
132: 
133:     primitives = [
134:         tk.prim_sphere(R, center=O, color="#dbeafe")
135:     ]
136: 
137:     camera = tk.auto_camera(
138:         vs,
139:         primitives=primitives,
140:         azim=rng.uniform(28.0, 34.0),
141:         elev=rng.uniform(21.0, 26.0),
142:         pad=1.34
143:     )
144: 
145:     spec = tk.make_spec(
146:         "正三棱锥与外切球",
147:         "体体外切-S006;O,S,A,D 共面;aux_lines 显式含 AD、SD、OT、OH、SO' 与 T 处直角符号",
148:         vs,
149:         faces=faces,
150:         edges=edges,
151:         aux_lines=aux_lines,
152:         aux_faces=aux_faces,
153:         primitives=primitives,
154:         labels=["S", "A", "B", "C", "D", "O", "T", "H", "O'"],
155:         camera=camera,
156:     )
157: 
158:     options = {
159:         "A": "两体外切",
160:         "B": "球内切于三棱锥",
161:         "C": "两体外离",
162:         "D": "两体相交",
163:     }
164: 
165:     # 由构造计算判定:球心到侧面 SBC 的距离等于半径,且与锥体内部分居侧面两侧
166:     plane_n = tk.unit(tk.cross(tk.vsub(C, B), tk.vsub(S, B)))
167:     dist_O_to_SBC = abs(tk.dot(plane_n, tk.vsub(O, B)))
168:     side_A = tk.dot(plane_n, tk.vsub(A, B))
169:     side_O = tk.dot(plane_n, tk.vsub(O, B))
178: 
179:     question = (
180:         "如图,正三棱锥S-ABC底面水平放置,竖直截面SAD(D为BC中点)截三棱锥得到三角形截面。"
181:         "球心O位于平面SAD内,球体与侧面SBC相切于点T,且OT⊥SD。"
182:         "图中OH⊥底面ABC,SO'为三棱锥的高,部分被遮挡的棱已用虚线表示。"
183:         "关于三棱锥S-ABC与球体的位置关系,下列说法正确的是"
184:     )
185: 
186:     return tk.item(spec, question, options, ans)

K26 Spec-to-Image Review

  • major/aux_line_clutter
    球面堆砌36+32=68条离散线段模拟大圆与水平辅助圆,渲染为密集线网球,严重干扰主体几何
    spec_fix: 删除硬搓线段,改用toolkit的section_circle/arc primitive画光滑圆,仅保留1条大圆与1条水平辅助圆
  • minor/label_overlap
    current与oblique视图中O、T标签均紧贴球轮廓且彼此靠近,易混淆
    spec_fix: 调整O、T标签偏移量,O置于球心左下,T置于切点右上,避开轮廓线
  • minor/camera_or_view
    两视角差异小,oblique_minus45未有效突出SAD截面与OT⊥SD的正面关系
    spec_fix: 第二视角改为正对SAD平面的正视图或剖视图,强化截面与垂直关系
What To Change
  • 将球面大圆与水平辅助圆的36/32段离散线段替换为toolkit圆弧primitive
  • 优化第二视角为SAD正视图以突出OT⊥SD
  • 提升三棱锥侧面不透明度至0.30以上增强立体感
Preferred Primitives
  • section_circle
  • arc
  • sphere
Remove/Simplify
  • 删除68条硬搓球面辅助线段
  • 若需保留则减至≤12段并只画可见弧段
Label Fixes
  • O、T标签增加像素偏移避免贴线重叠
  • H与O'在AD上保持清晰间距
Camera Fixes
  • oblique视角改为沿SAD法向正视图,突出截面与切点
Toolkit/Renderer: toolkit需支持3D空间圆/椭圆弧primitive(section_circle),使球面大圆与辅助圆无需硬搓为多段线,并支持可见性判断只绘弧段
representation: 几何关系与遮挡处理正确,但球面辅助圆过度离散为68条线段,严重偏离教材图简洁风格
readability: 球面线网过密干扰主体,两视角差异小未能有效补充空间信息
plan: 必要元素均已包含,但表达方式导致关键特征被淹没在杂乱线条中
next: toolkit_patch

solid3d_spatial_tangent_external__S008

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
直圆锥轴线竖直,顶点S,底面圆心O。圆锥右下外侧置球,球心C,与锥侧面外切于水平切圆(可见段实线弧)。过轴SO与C作轴截面,母线SA与球切于T,标CT⊥SA。干扰:轴线上设虚线点C'(假内切球心),C向SO作虚线垂足H;过C作水平辅助虚线圆与锥相交。锥底后半圆、球后半轮廓及被遮母线均虚线。
Must Render
[
  "圆锥及其轴截面中的母线SA",
  "球及其半径CT(垂直于母线)",
  "水平外切圆(可见段实线弧)",
  "虚线假内切球心C'及虚线垂足H",
  "圆锥与球的隐藏边/轮廓虚线"
]

Spec Summary

{
  "title": "圆锥与球的外切关系",
  "meta": "直圆锥与球体外切,含假内切球心干扰",
  "camera": {
    "position": [
      6.927,
      5.021,
      9.409
    ],
    "target": [
      1.048,
      0.762,
      -0.0
    ],
    "orthoSize": 3.049
  },
  "counts": {
    "vertices": 26,
    "faces": 0,
    "edges": 8,
    "aux_lines": 14,
    "aux_faces": 0,
    "primitives": 2,
    "labels": 7
  },
  "primitive_types": [
    "cone",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cone",
      "radius": 1.3789772014701511,
      "height": 3.6,
      "position": [
        0,
        1.8,
        0
      ]
    },
    {
      "type": "sphere",
      "radius": 0.4714802412351243,
      "position": [
        1.628692645397184,
        0.6661564490963187,
        0
      ]
    }
  ],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "Cp",
      "text": "C'"
    }
  ],
  "vertex_names": [
    "S",
    "O",
    "A",
    "T",
    "C",
    "H",
    "Cp",
    "M0",
    "M1",
    "M2",
    "M3",
    "M4",
    "M5",
    "M6",
    "N0",
    "N1",
    "N2",
    "N3",
    "N4",
    "N5",
    "N6",
    "N7",
    "N8",
    "N9",
    "N10",
    "N11"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "M0",
        "M1"
      ]
    },
    {
      "points": [
        "M1",
        "M2"
      ]
    },
    {
      "points": [
        "M2",
        "M3"
      ]
    },
    {
      "points": [
        "M3",
        "M4"
      ]
    },
    {
      "points": [
        "M4",
        "M5"
      ]
    },
    {
      "points": [
        "M5",
        "M6"
      ]
    },
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "C",
        "T"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "N0",
        "N1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N1",
        "N2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N2",
        "N3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N3",
        "N4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N4",
        "N5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N5",
        "N6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N6",
        "N7"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N7",
        "N8"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N8",
        "N9"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N9",
        "N10"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N10",
        "N11"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "N11",
        "N0"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "S",
        "O"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "C",
        "H"
      ],
      "dash": true,
      "color": "#555555"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 1. 随机生成圆锥参数
5:     h_cone = rng.choice([3.0, 3.2, 3.4, 3.6, 3.8, 4.0])

6:     R = rng.uniform(1.0, 1.5)
7:     L = math.sqrt(h_cone * h_cone + R * R)
8: 
9:     # 2. 寻找合适的球参数:球在右下外侧,切点靠近底面,球不超出底面/顶点过多
10:     t = 0.82
11:     r_s = 0.45
12:     for _ in range(25):
13:         t_try = rng.uniform(0.78, 0.94)
14:         r_try = rng.uniform(0.35, 0.65)
15:         Tx = t_try * R
16:         Ty = h_cone * (1.0 - t_try)
17:         Cx = Tx + r_try * h_cone / L
18:         Cy = Ty + r_try * R / L
19:         if Cy - r_try > -0.1 and Cy + r_try < h_cone - 0.3 and Cx > R + 0.2:
20:             t = t_try
21:             r_s = r_try
22:             break
23: 
24:     Tx = t * R
25:     Ty = h_cone * (1.0 - t)
26:     Cx = Tx + r_s * h_cone / L
27:     Cy = Ty + r_s * R / L
28: 
29:     # 3. 构造顶点
30:     vs = {}
31:     vs["S"] = (0.0, h_cone, 0.0)
32:     vs["O"] = (0.0, 0.0, 0.0)
33:     vs["A"] = (R, 0.0, 0.0)
34:     vs["T"] = (Tx, Ty, 0.0)
35:     vs["C"] = (Cx, Cy, 0.0)
36:     vs["H"] = (0.0, Cy, 0.0)
37: 
38:     # 假内切球心 C':位于轴上,接近真实内切球高度
39:     r_in = R * h_cone / (R + L)
40:     Cp_y = r_in * rng.uniform(0.85, 1.15)
41:     Cp_y = max(0.3, min(Cp_y, h_cone * 0.55))
42:     vs["Cp"] = (0.0, Cp_y, 0.0)
43: 
44:     # 4. 水平外切圆(可见段实线弧)
45:     edges = []
46:     n_arc = 7
47:     arc_names = []
48:     for i in range(n_arc):
49:         phi = -math.pi / 2 + (math.pi / (n_arc - 1)) * i
50:         x = Tx * math.cos(phi)
51:         z = Tx * math.sin(phi)
52:         nm = f"M{i}"
53:         vs[nm] = (x, Ty, z)
54:         arc_names.append(nm)
55:     for i in range(n_arc - 1):
56:         edges.append(tk.edge(arc_names[i], arc_names[i + 1]))
57: 
58:     # 5. 过 C 的水平辅助虚线圆(与锥相交)
59:     aux_lines = []
60:     r_cone_at_Cy = R * (1.0 - Cy / h_cone)
61:     gap = Cx - r_cone_at_Cy
62:     r_aux = gap * rng.uniform(1.3, 2.2) if gap > 0 else Cx * 0.6
63:     n_aux = 12
64:     aux_names = []
65:     for i in range(n_aux):
71:         aux_names.append(nm)
72:     for i in range(n_aux):
73:         aux_lines.append(tk.aux(aux_names[i], aux_names[(i + 1) % n_aux], dash=True, color="#888888"))
74: 
75:     # 6. 其他标注线
76:     edges.append(tk.edge("S", "A"))   # 母线 SA
77:     edges.append(tk.edge("C", "T"))   # 半径 CT
78:     aux_lines.append(tk.aux("S", "O", dash=True, color="#555555"))  # 轴 SO
79:     aux_lines.append(tk.aux("C", "H", dash=True, color="#555555"))  # 垂线 CH
80: 
81:     # 7. Primitives(渲染器自动处理隐藏轮廓虚线)
82:     primitives = [
83:         tk.prim_cone(r=R, h=h_cone, center=(0, 0, 0)),
84:         tk.prim_sphere(r=r_s, center=(Cx, Cy, 0)),
85:     ]
86: 
87:     # 8. 组装 spec
88:     spec = tk.make_spec(
89:         title="圆锥与球的外切关系",
90:         meta="直圆锥与球体外切,含假内切球心干扰",
91:         vertices=vs,
92:         edges=edges,
93:         aux_lines=aux_lines,
94:         primitives=primitives,
95:         labels=[],
96:         camera=tk.auto_camera(vs, primitives=primitives, azim=32.0, elev=21.0)
97:     )
98:     spec["labels"] = [
99:         {"point": "S", "text": "S"},
100:         {"point": "O", "text": "O"},
101:         {"point": "A", "text": "A"},
102:         {"point": "T", "text": "T"},
107: 
108:     # 9. 成题
109:     question = "如图,球与圆锥的位置关系属于哪一类?"
110:     opts = ["体体外切", "内切", "相交", "外离"]
111:     rng.shuffle(opts)
112:     options = {chr(65 + i): opts[i] for i in range(4)}
113:     answer = [k for k, v in options.items() if v == "体体外切"][0]
114:     return tk.item(spec, question, options, answer)

K26 Spec-to-Image Review

  • major/plan_coverage
    plan要求球心不在轴上却存在水平切圆,几何不可能,spec硬搓圆锥水平圆误导
    spec_fix: 删除伪切圆edges仅留T;或rewrite plan使球心在轴上或改为单点切触
  • minor/angle_arc
    CT与SA交于T但无直角标记,无法显式表达垂直关系
    spec_fix: 在T处添加right_angle标记或标注直角符号
  • minor/mesh_clutter
    水平圆与辅助圆均以7段、12段折线硬搓,棱感明显
    spec_fix: 若支持换circle/arc primitive,否则增加分段并降低辅助线对比度
  • minor/label_overlap
    轴上C'与H纵向间距过近,标签易重叠混淆
    spec_fix: 增大C'与H的y坐标差,或微调标签偏移避免重叠
What To Change
  • 删除伪水平切圆edges,仅保留切点T
  • 添加T处直角标记
  • 拉开C'与H纵向间距
Preferred Primitives
  • right_angle
  • circle_arc
Remove/Simplify
  • 移除M0-M6伪切圆折线
  • 简化12段辅助虚线圆为8段或更淡
Label Fixes
  • T旁加直角符号
  • C'与H标签错开
Camera Fixes
  • none
Toolkit/Renderer: 当前无需toolkit新primitive,但plan层球心不在轴上时不可能存在水平切圆,需修正几何假设
representation: 误将圆锥水平圆当作球锥切圆,核心几何关系错误;CT垂直SA缺直角标记
readability: 单视角可辨主要元素,但伪切圆与辅助虚线圆增加杂乱且误导空间关系
plan: plan要求球心不在轴上的水平切圆在欧氏空间中不可能,spec强行实现导致图与几何事实不符
next: spec_rewrite

solid3d_spatial_tangent_external__S012

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
画轴线水平的直圆锥,顶点S在左,底面圆心O₁在右,底面为竖直圆。在圆锥前上方置球O,与圆锥前侧母线SA切于唯一公共点P,球整体在圆锥外部。画出圆锥轴截面(等腰三角形),背向边用虚线。连接球心O与P,标注OP⊥SA及半径。过O作圆锥轴线的垂线OH,H为垂足,画虚线。在圆锥内部轴线上画虚线点O'及虚线小圆表示内切球,作相似构型干扰。过O作竖直辅助平面,截球得大圆,截圆锥得两条母线,显示切点唯一。画出圆锥底面后半圆周虚线及被遮挡母线虚线。
Must Render
[
  "球O与圆锥前侧母线SA的唯一切点P",
  "圆锥轴截面等腰三角形及其背向虚线边",
  "球心O到切点P的垂线段OP及垂直符号",
  "圆锥内部虚线内切球及其球心O'",
  "圆锥底面后半圆周虚线及被遮挡母线虚线"
]

Spec Summary

{
  "title": "圆锥与球外切",
  "meta": "S012",
  "camera": {
    "position": [
      4.67,
      4.601,
      10.671
    ],
    "target": [
      0.52,
      0.296,
      0.272
    ],
    "orthoSize": 3.08
  },
  "counts": {
    "vertices": 87,
    "faces": 0,
    "edges": 63,
    "aux_lines": 20,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 7
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "O1",
      "text": "O₁"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "Op",
      "text": "O'"
    },
    {
      "point": "H",
      "text": "H"
    }
  ],
  "vertex_names": [
    "S",
    "O1",
    "A",
    "Ap",
    "P",
    "O",
    "H",
    "Op",
    "F",
    "T",
    "Bk",
    "Bt",
    "B0",
    "B1",
    "B2",
    "B3",
    "B4",
    "B5",
    "B6",
    "B7",
    "B8",
    "B9",
    "B10",
    "B11",
    "B12",
    "B13",
    "B14",
    "B15",
    "B16",
    "B17",
    "B18",
    "B19",
    "B20",
    "B21",
    "B22",
    "B23",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23",
    "C24",
    "C25",
    "C26",
    "C27",
    "C28",
    "C29",
    "C30",
    "C31",
    "I0",
    "I1",
    "I2",
    "I3",
    "I4",
    "I5",
    "I6",
    "I7",
    "I8",
    "I9",
    "I10",
    "I11",
    "I12",
    "I13",
    "I14",
    "I15",
    "M1",
    "M2",
    "M3"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "Ap"
      ]
    },
    {
      "points": [
        "Ap",
        "S"
      ],
      "dash": true
    },
    {
      "points": [
        "S",
        "F"
      ]
    },
    {
      "points": [
        "S",
        "T"
      ]
    },
    {
      "points": [
        "S",
        "Bk"
      ],
      "dash": true
    },
    {
      "points": [
        "S",
        "Bt"
      ],
      "dash": true
    },
    {
      "points": [
        "B0",
        "B1"
      ]
    },
    {
      "points": [
        "B1",
        "B2"
      ]
    },
    {
      "points": [
        "B2",
        "B3"
      ]
    },
    {
      "points": [
        "B3",
        "B4"
      ]
    },
    {
      "points": [
        "B4",
        "B5"
      ]
    },
    {
      "points": [
        "B5",
        "B6"
      ]
    },
    {
      "points": [
        "B6",
        "B7"
      ]
    },
    {
      "points": [
        "B7",
        "B8"
      ]
    },
    {
      "points": [
        "B8",
        "B9"
      ]
    },
    {
      "points": [
        "B9",
        "B10"
      ]
    },
    {
      "points": [
        "B10",
        "B11"
      ]
    },
    {
      "points": [
        "B11",
        "B12"
      ]
    },
    {
      "points": [
        "B12",
        "B13"
      ],
      "dash": true
    },
    {
      "points": [
        "B13",
        "B14"
      ],
      "dash": true
    },
    {
      "points": [
        "B14",
        "B15"
      ],
      "dash": true
    },
    {
      "points": [
        "B15",
        "B16"
      ],
      "dash": true
    },
    {
      "points": [
        "B16",
        "B17"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "I0",
        "I1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I1",
        "I2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I2",
        "I3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I3",
        "I4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I4",
        "I5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I5",
        "I6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I6",
        "I7"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I7",
        "I8"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I8",
        "I9"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I9",
        "I10"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I10",
        "I11"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I11",
        "I12"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I12",
        "I13"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I13",
        "I14"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I14",
        "I15"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "I15",
        "I0"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "O",
        "P"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "M1",
        "M3"
      ],
      "dash": false,
      "color": "#111827"
    },
    {
      "points": [
        "M2",
        "M3"
      ],
      "dash": false,
      "color": "#111827"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 几何参数 ----
5:     L = rng.uniform(3.0, 4.0)

14:     O1 = (L / 2.0, 0.0, 0.0)
15:     A = (L / 2.0, R * c, R * s)          # 前上方母线 SA 的底端点
16:     Ap = (L / 2.0, -R * c, -R * s)       # 轴截面背向底端点
17: 
18:     # 单位向量:u 沿 SA,vd 为 SA 在轴截面内的外法向(垂直于 u)
19:     D = math.sqrt(L * L + R * R)
20:     u = (L / D, R * c / D, R * s / D)
21:     vd = (-R / D, L * c / D, L * s / D)
22: 
23:     # 切点 P、球心 O、垂足 H
24:     P = (S[0] + t * L, S[1] + t * R * c, S[2] + t * R * s)
25:     O = (P[0] + r * vd[0], P[1] + r * vd[1], P[2] + r * vd[2])
26:     H = (O[0], 0.0, 0.0)
27: 
28:     # 干扰用虚线内切球:轴上球心 O',半径 ri
29:     xo = rng.uniform(-L / 2.0 + 0.6, L / 2.0 - 0.8)
30:     ri = (xo + L / 2.0) * R / D
31:     Op = (xo, 0.0, 0.0)
32: 
33:     vs = {"S": S, "O1": O1, "A": A, "Ap": Ap, "P": P, "O": O, "H": H, "Op": Op}
34: 
35:     es = []   # 主 edges
36:     ax = []   # 辅助线 aux_lines
37: 
38:     # ---- 轴截面等腰三角形 S-A-Ap,背向边 S-Ap 虚线 ----
39:     es.append(tk.edge("S", "A"))
40:     es.append(tk.edge("A", "Ap"))
41:     es.append(tk.edge("Ap", "S", dash=True))
42: 
58:         es.append(tk.edge(a, b, dash=(vs[a][2] + vs[b][2]) / 2.0 < -0.05))
59: 
60:     # ---- 球 O:轴截面(竖直辅助平面)上的大圆(32 段)----
61:     m = 32
62:     for i in range(m):
63:         a = 2.0 * math.pi * i / m
64:         cs, sn = math.cos(a), math.sin(a)
69:         es.append(tk.edge(f"C{i}", f"C{(i + 1) % m}"))
70: 
71:     # ---- 干扰内切球:虚线小圆(y-z 平面截线)----
72:     k = 16
73:     for i in range(k):
74:         a = 2.0 * math.pi * i / k
75:         vs[f"I{i}"] = (xo, ri * math.cos(a), ri * math.sin(a))
92: 
93:     # ---- 相机与组装 ----
94:     cam = tk.auto_camera(vs, azim=rng.uniform(10.0, 25.0), elev=rng.uniform(18.0, 28.0))
95:     spec = tk.make_spec("圆锥与球外切", "S012", vs, edges=es, aux_lines=ax, labels=[], camera=cam)
96:     spec["labels"] = [{"point": "S", "text": "S"},
97:                       {"point": "O1", "text": "O₁"},
98:                       {"point": "A", "text": "A"},
99:                       {"point": "P", "text": "P"},
100:                       {"point": "O", "text": "O"},
102:                       {"point": "H", "text": "H"}]
103: 
104:     opts = {"A": "外切", "B": "内切", "C": "相交", "D": "外离"}
105:     return tk.item(spec, "如图,球O与圆锥的位置关系是( )", opts, "A")

K26 Spec-to-Image Review

  • major/sphere_render
    球O未用sphere primitive,以32段边硬画平面大圆,无球体轮廓与立体感。
    spec_fix: 改用sphere primitive画球,保留竖直截面大圆为实线或虚线,去除多余分段边。
  • major/cone_cylinder_render
    圆锥底面以24段多边形硬画,侧面母线密集,未使用cone primitive。
    spec_fix: 使用cone primitive生成圆锥轮廓与底面圆,减少母线数量至3-5条可见即可。
  • major/label_overlap
    第二视角标签S/O/P/H/A严重重叠;第一视角O与P、O'与H贴靠过近。
    spec_fix: 调整标签偏移量,使O/P、O'/H沿不同法向分离;第二视角重新摆位或删除。
  • minor/angle_arc
    plan要求标注OP⊥SA,但图中无垂直符号或直角标记。
    spec_fix: 在spec中添加angle_arc或right_angle标记于OP与SA交点P处。
  • minor/mesh_clutter
    底面、球、内切球及母线均为硬画线段,总边数过多,画面杂乱。
    spec_fix: 以primitive替代硬画圆与锥面,仅保留轴截面、切线及必要遮挡虚线。
What To Change
  • 改用sphere与cone primitive重画球和圆锥
  • 添加OP⊥SA直角符号与半径标注
  • 精简辅助线与视角数量
Preferred Primitives
  • sphere
  • cone
  • angle_arc
  • section_circle
Remove/Simplify
  • 删除硬画底面24段多边形与球32段边
  • 减少圆锥侧面母线至轴截面及1-2条可见母线
  • 移除第二混乱视角
Label Fixes
  • O与P沿球半径方向分离,O'与H沿轴线法向错开
  • 第二视角若保留需重新计算标签避让
Camera Fixes
  • 保持current视角并微调target使居中
  • 删除oblique_plus45或改为正俯视辅助图
Toolkit/Renderer: 需toolkit提供sphere、cone、angle_arc primitive支持,并允许低分段平滑渲染底面圆。
representation: 未使用sphere/cone primitive,球与圆锥均靠密集线段硬画,底面多边形化严重,缺垂直符号。
readability: current视角基本可辨但线网杂乱;oblique_plus45标签严重重叠,几乎不可读。
plan: 切点P、轴截面、内切球干扰项已呈现,但球O仅画截面圆,且缺少OP⊥SA的直角标记。
next: spec_rewrite

solid3d_spatial_tangent_external__S014

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
直立正圆锥顶点S在上,底面水平放置;一球体位于圆锥右侧,二者外切。过圆锥轴线和球心作轴截面,截圆锥得等腰三角形SAB,截球得大圆,该圆与母线SB切于点T。画出半径OT并标注与SB垂直。作过切点T且平行于底面的辅助平面,截圆锥和球分别得到一个圆,两圆在T处相切。补全圆锥底面后半圆弧及球体左下不可见轮廓为虚线。从球心O和切点T分别向轴线引垂线,垂足为H、K,连接HT作为辅助对角线。底面画出与AB垂直的直径CD,其中D为后侧虚线端点。
Must Render
[
  "轴截面等腰三角形与大圆相切于点T",
  "过T的水平辅助平面及截圆锥、球所得的两个相切圆",
  "圆锥底面后半圆弧虚线、球体不可见轮廓虚线及底面后端虚线点D",
  "球心O和切点T向轴线作的垂线OH、TK及辅助对角线HT",
  "底面垂直直径CD"
]

Spec Summary

{
  "title": "圆锥球外切",
  "meta": "轴截面与辅助线",
  "camera": {
    "position": [
      8.323,
      7.35,
      12.546
    ],
    "target": [
      1.08,
      0.9,
      -0.0
    ],
    "orthoSize": 4.134
  },
  "counts": {
    "vertices": 99,
    "faces": 0,
    "edges": 82,
    "aux_lines": 23,
    "aux_faces": 0,
    "primitives": 0,
    "labels": 9
  },
  "primitive_types": [],
  "primitives": [],
  "labels": [
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "K",
      "text": "K"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    }
  ],
  "vertex_names": [
    "S",
    "A",
    "B",
    "O",
    "T",
    "H",
    "K",
    "C",
    "D",
    "M",
    "y1_0_0",
    "y2_0_0",
    "y3_0_0",
    "y4_0_0",
    "y5_0_0",
    "y7_0_0",
    "y8_0_0",
    "y9_0_0",
    "y10_0_0",
    "y11_0_0",
    "y13_0_0",
    "y14_0_0",
    "y15_0_0",
    "y16_0_0",
    "y17_0_0",
    "y19_0_0",
    "y20_0_0",
    "y21_0_0",
    "y22_0_0",
    "y23_0_0",
    "z0_22_15",
    "z1_22_15",
    "z2_22_15",
    "z3_22_15",
    "z4_22_15",
    "z5_22_15",
    "z6_22_15",
    "z7_22_15",
    "z8_22_15",
    "z9_22_15",
    "z10_22_15",
    "z11_22_15",
    "z12_22_15",
    "z13_22_15",
    "z15_22_15",
    "z16_22_15",
    "z17_22_15",
    "z18_22_15",
    "z19_22_15",
    "z20_22_15",
    "z21_22_15",
    "z22_22_15",
    "z23_22_15",
    "y1_0_9",
    "y2_0_9",
    "y3_0_9",
    "y4_0_9",
    "y5_0_9",
    "y6_0_9",
    "y7_0_9",
    "y8_0_9",
    "y9_0_9",
    "y10_0_9",
    "y11_0_9",
    "y12_0_9",
    "y13_0_9",
    "y14_0_9",
    "y15_0_9",
    "y16_0_9",
    "y17_0_9",
    "y18_0_9",
    "y19_0_9",
    "y20_0_9",
    "y21_0_9",
    "y22_0_9",
    "y23_0_9",
    "y0_22_9",
    "y1_22_9",
    "y2_22_9",
    "y3_22_9",
    "y4_22_9",
    "y5_22_9",
    "y6_22_9",
    "y7_22_9",
    "y8_22_9",
    "y9_22_9",
    "y10_22_9",
    "y11_22_9",
    "y13_22_9",
    "y14_22_9",
    "y15_22_9",
    "y16_22_9",
    "y17_22_9",
    "y18_22_9",
    "y19_22_9",
    "y20_22_9",
    "y21_22_9",
    "y22_22_9",
    "y23_22_9"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "S",
        "A"
      ]
    },
    {
      "points": [
        "S",
        "B"
      ]
    },
    {
      "points": [
        "A",
        "B"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "B",
        "y1_0_0"
      ]
    },
    {
      "points": [
        "y1_0_0",
        "y2_0_0"
      ]
    },
    {
      "points": [
        "y2_0_0",
        "y3_0_0"
      ]
    },
    {
      "points": [
        "y3_0_0",
        "y4_0_0"
      ]
    },
    {
      "points": [
        "y4_0_0",
        "y5_0_0"
      ]
    },
    {
      "points": [
        "y5_0_0",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "y7_0_0"
      ]
    },
    {
      "points": [
        "y7_0_0",
        "y8_0_0"
      ]
    },
    {
      "points": [
        "y8_0_0",
        "y9_0_0"
      ]
    },
    {
      "points": [
        "y9_0_0",
        "y10_0_0"
      ]
    },
    {
      "points": [
        "y10_0_0",
        "y11_0_0"
      ]
    },
    {
      "points": [
        "y11_0_0",
        "A"
      ]
    },
    {
      "points": [
        "z0_22_15",
        "z1_22_15"
      ]
    },
    {
      "points": [
        "z1_22_15",
        "z2_22_15"
      ]
    },
    {
      "points": [
        "z2_22_15",
        "z3_22_15"
      ]
    },
    {
      "points": [
        "z3_22_15",
        "z4_22_15"
      ]
    },
    {
      "points": [
        "z4_22_15",
        "z5_22_15"
      ]
    },
    {
      "points": [
        "z5_22_15",
        "z6_22_15"
      ]
    },
    {
      "points": [
        "z6_22_15",
        "z7_22_15"
      ]
    },
    {
      "points": [
        "z7_22_15",
        "z8_22_15"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O",
        "T"
      ],
      "dash": false,
      "color": "#d9480f"
    },
    {
      "points": [
        "M",
        "S"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "H"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "T",
        "K"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "H",
        "T"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "A",
        "y13_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y13_0_0",
        "y14_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y14_0_0",
        "y15_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y15_0_0",
        "y16_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y16_0_0",
        "y17_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y17_0_0",
        "D"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "D",
        "y19_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y19_0_0",
        "y20_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y20_0_0",
        "y21_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y21_0_0",
        "y22_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y22_0_0",
        "y23_0_0"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "y23_0_0",
        "B"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "z12_22_15",
        "z13_22_15"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "z13_22_15",
        "T"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "T",
        "z15_22_15"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "z15_22_15",
        "z16_22_15"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "z16_22_15",
        "z17_22_15"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "z17_22_15",
        "z18_22_15"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 几何参数:直立正圆锥与右侧外切球 ----
5:     R = rng.choice([2.0, 2.2, 2.5])

6:     H = rng.choice([3.0, 3.2, 3.5])
7:     y_t = rng.uniform(H * 0.3, H * 0.6)
8:     x_t = R * (1.0 - y_t / H)          # T 在母线 SB 上
9:     L = math.hypot(R, H)               # 母线长
10:     r = rng.uniform(0.5, min(0.9, 0.6 * x_t))
11:     x_o = x_t + r * H / L              # 球心(沿 SB 法向偏移 r)
12:     y_o = y_t + r * R / L
13:     rs = x_o - x_t                     # 水平截面截球的圆半径
14: 
15:     n = 24
16: 
17:     # ---- 顶点 ----
29:     }
30: 
31:     edges = []
32:     aux_lines = []
33: 
34:     # 轴截面等腰三角形 SAB
35:     edges.append(tk.edge("S", "A"))
36:     edges.append(tk.edge("S", "B"))
37:     edges.append(tk.edge("A", "B"))
38: 
39:     # 半径 OT(实线辅助线,几何上 OT ⟂ SB)
40:     aux_lines.append(tk.aux("O", "T", dash=False))
41: 
42:     # 底面垂直直径 CD
43:     edges.append(tk.edge("C", "D"))
44: 
45:     # 轴线、垂线与辅助对角线(虚线)
46:     aux_lines.append(tk.aux("M", "S", dash=True))
47:     aux_lines.append(tk.aux("O", "H", dash=True))
48:     aux_lines.append(tk.aux("T", "K", dash=True))
49:     aux_lines.append(tk.aux("H", "T", dash=True))
50: 
51:     # ---- 画圆工具:实线入 edges,虚线入 aux_lines ----
52:     def circ(cx, cy, cz, rad, axis, over, dash=None):
53:         names = []
54:         for i in range(n):
55:             if i in over:
74:                     is_dash = (i >= s or i < e)
75:             if is_dash:
76:                 aux_lines.append(tk.aux(a, b, dash=True))
77:             else:
78:                 edges.append(tk.edge(a, b))
79: 
80:     # 1) 底面圆(y=0):后半圆弧 A→D→B 为虚线
81:     circ(0, 0, 0, R, 'y',
82:          {0: "B", n // 4: "C", n // 2: "A", 3 * n // 4: "D"},
83:          (n // 2, n))
84: 
85:     # 2) 球的大圆(z=0,轴截面):左下不可见轮廓为虚线
86:     ang = math.atan2(y_t - y_o, x_t - x_o)
87:     if ang < 0:
88:         ang += 2 * math.pi
89:     iT = int(round(ang / (2 * math.pi) * n)) % n
93:     circ(0, y_t, 0, x_t, 'y', {0: "T"})
94: 
95:     # 4) 过 T 的水平面截球的圆(实线),T 位于该圆最左侧
96:     circ(x_o, y_t, 0, rs, 'y', {n // 2: "T"})
97: 
98:     # ---- 相机与标注 ----
99:     cam = tk.auto_camera(vs, azim=rng.choice([26, 30, 34]), elev=rng.choice([20, 24]))
100:     spec = tk.make_spec("圆锥球外切", "轴截面与辅助线", vs,
101:                         edges=edges, aux_lines=aux_lines,
102:                         labels=["S", "A", "B", "O", "T", "H", "K", "C", "D"],
103:                         camera=cam)
104: 
105:     # ---- 选项(与 final_qa 一致)----
106:     opts = {"A": "外切", "B": "内切", "C": "外离", "D": "相交"}
107:     ans = "A"
108: 
109:     return tk.item(spec, "如图,球体O与正圆锥的位置关系为( )", opts, ans)

K26 Spec-to-Image Review

  • major/shape_primitive
    primitives 为空,球与圆锥均用 24 段线段硬画,无 sphere/cone primitive
    spec_fix: 使用 sphere primitive 画球并保留大圆与水平截面圆;使用 cone primitive 画圆锥,移除离散线网
  • major/mesh_clutter
    底面圆、球大圆、截面圆均为 24 边形,边缘锯齿明显,缺乏教材图光滑感
    spec_fix: 改用 primitive 或高阶曲线绘制圆,避免多边形硬边
  • minor/label_overlap
    top 视图中 S、H、K 标签堆叠于轴线处,无法分辨
    spec_fix: 调整 top 视图标签偏移或取消该视角
  • minor/camera_or_view
    top 视角投影过于扁平,圆锥与球大圆重叠混乱
    spec_fix: top 改为略倾斜俯视或仅保留 front+current
What To Change
  • 引入 sphere/cone primitive 替代离散线网
  • 球大圆与截面圆保留必要虚线/实线
  • 优化 top 视角或标签布局
Preferred Primitives
  • sphere
  • cone
  • section_circle
Remove/Simplify
  • 删除 circ 生成的 24 边形底面圆、球大圆、截面圆边
  • 清理冗余离散顶点
Label Fixes
  • top 视图 S/H/K 沿轴线错开或隐藏
  • 核实 D 在底面圆上的位置
Camera Fixes
  • top 视角改为斜俯视 15° 或移除
Toolkit/Renderer: toolkit 需支持 sphere、cone primitive 及截面圆/大圆虚实线渲染,避免题目代码硬搓离散圆
representation: 球与圆锥均未使用 primitive,而用 24 段线段离散硬画,呈明显多边形,不符合教材立体图要求
readability: front 视图尚可读,current 视图球体扁平多边形化,top 视图标签严重重叠,整体可读性差
plan: 轴截面、切点、水平辅助面、垂线、对角线等要素均已覆盖,但几何体表示方式错误导致识别困难
next: toolkit_patch

solid3d_spatial_tangent_external__S016

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
绘制共轴直圆台与球:圆台轴线竖直,球位于圆台外侧中部,球心在轴线上,与圆台侧面外切于一水平圆(可见段实线、被遮段虚线)。作竖直轴截面,截圆台得等腰梯形两腰,截球得大圆,大圆与两腰切于E、F。在圆台内部用虚线画一个与侧面及上下底都相切的内切球轮廓(可见半球),制造内外切混淆。再作一竖直斜截面,截球得一小圆,截圆台得一椭圆弧,两者示意性交于两点,制造相交错觉。
Must Render
[
  "圆台及其轴截面等腰梯形轮廓(含两腰母线)",
  "外部球及其与圆台侧面的切圆(实线可见段+虚线隐藏段)",
  "圆台内部虚线内切球轮廓(可见半球)",
  "竖直斜截面截球小圆与截圆台椭圆弧的示意性交叉"
]

Spec Summary

{
  "title": "圆台与球外切",
  "meta": "S016",
  "camera": {
    "position": [
      8.327,
      7.371,
      15.488
    ],
    "target": [
      0.12,
      1.008,
      0.053
    ],
    "orthoSize": 4.884
  },
  "counts": {
    "vertices": 206,
    "faces": 14,
    "edges": 0,
    "aux_lines": 177,
    "aux_faces": 0,
    "primitives": 1,
    "labels": 7
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 2.6313742162414226,
      "position": [
        0.0,
        -0.4014158838652306,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "Z1",
      "text": "Z1"
    },
    {
      "point": "Z2",
      "text": "Z2"
    },
    {
      "point": "Z3",
      "text": "Z3"
    },
    {
      "point": "Z4",
      "text": "Z4"
    },
    {
      "point": "E1",
      "text": "E"
    },
    {
      "point": "F1",
      "text": "F"
    },
    {
      "point": "l",
      "text": "l"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z1",
    "Z2",
    "Z3",
    "Z4",
    "l",
    "l0",
    "E1",
    "g1",
    "g2",
    "g3",
    "g4",
    "g5",
    "g6",
    "g7",
    "g8",
    "g9",
    "g10",
    "g11",
    "g12",
    "g13",
    "g14",
    "g15",
    "F1",
    "g17",
    "g18",
    "g19",
    "g20",
    "g21",
    "g22",
    "g23",
    "g24",
    "g25",
    "g26",
    "g27",
    "g28",
    "g29",
    "g30",
    "g31",
    "t0",
    "t1",
    "t2",
    "t3",
    "t4",
    "t5",
    "t6",
    "t7",
    "t8",
    "t9",
    "t10",
    "t11",
    "t12",
    "t13",
    "t14",
    "t15",
    "t16",
    "t17",
    "t18",
    "t19",
    "t20",
    "t21",
    "t22",
    "t23",
    "eq0",
    "eq1",
    "eq2",
    "eq3",
    "eq4",
    "eq5",
    "eq6",
    "eq7",
    "eq8",
    "eq9",
    "eq10",
    "eq11",
    "eq12",
    "eq13",
    "eq14",
    "eq15",
    "eq16",
    "eq17",
    "eq18",
    "eq19",
    "eq20",
    "eq21",
    "eq22",
    "eq23",
    "m10",
    "m11",
    "m12",
    "m13",
    "m14",
    "m15",
    "m16",
    "m17",
    "m18",
    "m19"
  ],
  "faces_sample": [
    {
      "points": [
        "L",
        "K",
        "J",
        "I",
        "H",
        "G",
        "F",
        "E",
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "M",
        "N",
        "P",
        "Q",
        "R",
        "S",
        "T",
        "U",
        "V",
        "W",
        "X",
        "Y"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "N",
        "M"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "P",
        "N"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "Q",
        "P"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "R",
        "Q"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "S",
        "R"
      ]
    },
    {
      "points": [
        "F",
        "G",
        "T",
        "S"
      ]
    },
    {
      "points": [
        "G",
        "H",
        "U",
        "T"
      ]
    },
    {
      "points": [
        "H",
        "I",
        "V",
        "U"
      ]
    },
    {
      "points": [
        "I",
        "J",
        "W",
        "V"
      ]
    },
    {
      "points": [
        "J",
        "K",
        "X",
        "W"
      ]
    },
    {
      "points": [
        "K",
        "L",
        "Y",
        "X"
      ]
    },
    {
      "points": [
        "L",
        "A",
        "M",
        "Y"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "Z1",
        "Z2"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "Z2",
        "Z3"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "Z3",
        "Z4"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "Z4",
        "Z1"
      ],
      "dash": false,
      "color": "#333"
    },
    {
      "points": [
        "l0",
        "l"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "E1",
        "g1"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g1",
        "g2"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g2",
        "g3"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g3",
        "g4"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g4",
        "g5"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g5",
        "g6"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g6",
        "g7"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g7",
        "g8"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g8",
        "g9"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g9",
        "g10"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g10",
        "g11"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g11",
        "g12"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g12",
        "g13"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g13",
        "g14"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g14",
        "g15"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g15",
        "F1"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "F1",
        "g17"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g17",
        "g18"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g18",
        "g19"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g19",
        "g20"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g20",
        "g21"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g21",
        "g22"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g22",
        "g23"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g23",
        "g24"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g24",
        "g25"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g25",
        "g26"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g26",
        "g27"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g27",
        "g28"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g28",
        "g29"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g29",
        "g30"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g30",
        "g31"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "g31",
        "E1"
      ],
      "dash": false,
      "color": "#c92a2a"
    },
    {
      "points": [
        "t0",
        "t1"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t1",
        "t2"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t2",
        "t3"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t3",
        "t4"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t4",
        "t5"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t5",
        "t6"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t6",
        "t7"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t7",
        "t8"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t8",
        "t9"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t9",
        "t10"
      ],
      "dash": false,
      "color": "#2b8a3e"
    },
    {
      "points": [
        "t10",
        "t11"
      ],
      "dash": false,
      "color": "#2b8a3e"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 几何参数 ----
5:     R = rng.uniform(2.8, 3.2)

7:     h = 2.0 * math.sqrt(R * r)
8:     L = math.sqrt(h * h + (R - r) * (R - r))
9:     # 外切球球心(轴线上)与半径
10:     y0 = (h * h - R * R + r * r) / (2.0 * h) - rng.uniform(0.1, 0.3)
11:     rs = (h * R - (R - r) * y0) / L
12:     xe = h * rs / L
13:     ye = y0 + rs * (R - r) / L
15:     n = rng.choice([10, 12])
16: 
17:     # ---- 圆台主体 ----
18:     vs, faces = tk.frustum(n, R, r, h)
19: 
20:     # ---- 轴截面梯形与轴线辅助点 ----
21:     for k, v in [('Z1', (-R, 0.0, 0.0)), ('Z2', (R, 0.0, 0.0)),
22:                  ('Z3', (r, h, 0.0)), ('Z4', (-r, h, 0.0)),
23:                  ('l', (0.0, h + 0.5, 0.0)), ('l0', (0.0, -0.5, 0.0))]:
24:         vs[k] = v
25: 
26:     aux_lines = []
27: 
28:     # 轴截面等腰梯形
29:     aux_lines.append(tk.aux('Z1', 'Z2', False, "#333"))
30:     aux_lines.append(tk.aux('Z2', 'Z3', False, "#333"))
31:     aux_lines.append(tk.aux('Z3', 'Z4', False, "#333"))
32:     aux_lines.append(tk.aux('Z4', 'Z1', False, "#333"))
33:     # 轴线
34:     aux_lines.append(tk.aux('l0', 'l', False, "#b45309"))
35: 
36:     # ---- 外部球:轴截面大圆(红色)及切点 E、F ----
37:     a0 = math.atan2(R - r, h)
38:     gp = []
39:     for i in range(32):
40:         a = a0 + 2.0 * math.pi * i / 32.0
43:         gp.append(nm)
44:     for i in range(32):
45:         aux_lines.append(tk.aux(gp[i], gp[(i + 1) % 32], False, "#c92a2a"))
46: 
47:     # ---- 外部球与圆台侧面的切圆(水平圆,可见段实线、隐藏段虚线) ----
48:     tp = [f't{i}' for i in range(24)]
49:     for i in range(24):
50:         a = 2.0 * math.pi * i / 24.0
51:         vs[tp[i]] = (xe * math.cos(a), ye, xe * math.sin(a))
53:         # 根据中点 z 坐标判断可见性:z<0 视为被遮挡段
54:         mid_z = (vs[tp[i]][2] + vs[tp[(i + 1) % 24]][2]) / 2.0
55:         aux_lines.append(tk.aux(tp[i], tp[(i + 1) % 24], mid_z < 0.0, "#2b8a3e"))
56: 
57:     # ---- 圆台内部虚线内切球轮廓(可见半球 wireframe) ----
58:     ri = h / 2.0
59:     yi = h / 2.0
60:     # 1) 赤道水平圆(虚线)
61:     eq_pts = [f'eq{i}' for i in range(24)]
64:         vs[eq_pts[i]] = (ri * math.cos(a), yi, ri * math.sin(a))
65:     for i in range(24):
66:         aux_lines.append(tk.aux(eq_pts[i], eq_pts[(i + 1) % 24], True, "#868e96"))
67:     # 2) 经线1:z=0 平面上的半圆(虚线)
68:     mer1 = [f'm1{i}' for i in range(13)]
69:     for i in range(13):
70:         a = math.pi * i / 12.0
71:         vs[mer1[i]] = (ri * math.cos(a), yi + ri * math.sin(a), 0.0)
72:     for i in range(12):
73:         aux_lines.append(tk.aux(mer1[i], mer1[i + 1], True, "#868e96"))
74:     # 3) 经线2:x=0 平面上的半圆(虚线)
75:     mer2 = [f'm2{i}' for i in range(13)]
76:     for i in range(13):
77:         a = math.pi * i / 12.0
78:         vs[mer2[i]] = (0.0, yi + ri * math.cos(a), ri * math.sin(a))
79:     for i in range(12):
80:         aux_lines.append(tk.aux(mer2[i], mer2[i + 1], True, "#868e96"))
81: 
82:     # ---- 竖直斜截面:截球小圆与截圆台示意性椭圆弧(蓝色,制造相交错觉) ----
83:     rc = math.sqrt(max(0.0, rs * rs - d * d))
84:     # 截球小圆(平面 x=d 上的圆)
85:     sp = [f's{i}' for i in range(20)]
86:     for i in range(20):
87:         a = 2.0 * math.pi * i / 20.0
88:         vs[sp[i]] = (d, y0 + rc * math.cos(a), rc * math.sin(a))
89:     for i in range(20):
90:         aux_lines.append(tk.aux(sp[i], sp[(i + 1) % 20], False, "#1971c2"))
91: 
92:     # 截圆台示意性椭圆弧(平面 x=d 上,z 方向压缩以与球小圆示意交叉)
93:     ne = 24
94:     ep_front, ep_back = [], []
95:     for k in range(ne + 1):
96:         yv = h * k / ne
98:         if ry <= d:
99:             break
100:         zv = math.sqrt(ry * ry - d * d) * 0.75  # 示意性压缩,制造与球小圆的两点交叉
101:         vs[f'af{k}'] = (d, yv, zv)
102:         ep_front.append(f'af{k}')
103:         vs[f'ab{k}'] = (d, yv, -zv)
104:         ep_back.append(f'ab{k}')
105:     for i in range(len(ep_front) - 1):
106:         aux_lines.append(tk.aux(ep_front[i], ep_front[i + 1], False, "#1971c2"))
107:     for i in range(len(ep_back) - 1):
108:         aux_lines.append(tk.aux(ep_back[i], ep_back[i + 1], False, "#1971c2"))
109: 
110:     # ---- 相机与 spec 组装 ----
111:     cam = tk.auto_camera(vs, azim=28.0, elev=20.0)
112:     spec = tk.make_spec("圆台与球外切", "S016", vs, faces=faces, aux_lines=aux_lines,
113:                         primitives=[tk.prim_sphere(rs, (0.0, y0, 0.0))],
114:                         labels=['Z1', 'Z2', 'Z3', 'Z4', 'E1', 'F1', 'l'], camera=cam)
115:     # 标签重命名
116:     for lb in spec.get("labels", []):
117:         if lb["text"] == "E1":
118:             lb["text"] = "E"
119:         if lb["text"] == "F1":
120:             lb["text"] = "F"
121: 
122:     # ---- 题目 ----
123:     q = "如图,观察图中球与圆台的位置关系,下列判断正确的是( )"
124:     opts = ["球与圆台外切", "球内切于圆台", "球与圆台相交", "球与圆台外离"]
125:     rng.shuffle(opts)
126:     opts = {k: v for k, v in zip("ABCD", opts)}
127:     ans = [k for k, v in opts.items() if v == "球与圆台外切"][0]
128:     return tk.item(spec, q, opts, ans)

K26 Spec-to-Image Review

  • fatal/frustum_render
    圆台由12个侧面四边形mesh拼成,渲染出密集母线,呈棱台状而非光滑直圆台
    spec_fix: toolkit需支持frustum primitive;spec删除tk.frustum mesh,改用上下底椭圆+3-4条轮廓母线
  • major/mesh_clutter
    圆台12条侧面棱线与球大圆、切圆、斜截面线叠加,画面杂乱如笼
    spec_fix: 移除所有侧面多边形mesh,仅保留圆台轮廓线与轴线
  • minor/label_missing
    current视角中仅见F标签,右侧切点E标签缺失或被球面遮挡
    spec_fix: 调整E标签偏移量或微调相机,确保E在current视角可见
  • minor/aux_line_clutter
    球大圆硬拆32段红线、切圆24段绿线,分段过密增加视觉噪音
    spec_fix: 球大圆与切圆分段数降至16段左右,保持平滑即可
What To Change
  • 圆台改用轮廓线表示,删除12面mesh
  • 球辅助线减段降噪
  • 确保E/F双视角可见
Preferred Primitives
  • frustum
  • sphere
  • section_circle
Remove/Simplify
  • 删除tk.frustum生成的206顶点/14面mesh
  • 球大圆32段减至16段
Label Fixes
  • E标签向右外移避免被球面遮挡
Camera Fixes
  • current视角略降elev并右移azim,使轴截面梯形与切点E同屏清晰
Toolkit/Renderer: toolkit需新增frustum primitive,渲染为光滑圆台轮廓(上下椭圆+母线),而非返回多边形mesh
representation: 圆台误用多边形mesh拼成棱台,核心几何体失真;球与截面要素虽完整但主体表达错误
readability: current视角线网过密可读性差,section_plane视角相对清晰但仍受侧面网格干扰
plan: 轴截面梯形、外切球大圆、切圆、内切球轮廓、斜截面干扰线五要素均已覆盖
next: toolkit_patch

solid3d_spatial_tangent_external__S017

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L2
Scene
在浅色网格底面上放置半径为R的大球O₁(左下)与半径为r的小球O₂(右上),两球外切于红色高亮公共点P。过球心O₁、O₂与切点P作竖直辅助截面,截两球得两个大圆(前半圆实线、后半圆虚线)。用虚线绘制连心线O₁O₂并标注R、r,两球背面轮廓及截面后半圆均用虚线表示。
Must Render
[
  "两球外切于红色高亮点P",
  "竖直辅助截面截得的两球大圆(含虚线后半圆)",
  "虚线连心线O₁O₂及半径R、r标注"
]

Spec Summary

{
  "title": "两球外切",
  "meta": "S017: explicit section great circles C*/D*, dashed back arcs, dashed center line O1O2",
  "camera": {
    "position": [
      5.523,
      4.897,
      7.746
    ],
    "target": [
      0.069,
      0.861,
      -0.043
    ],
    "orthoSize": 2.513
  },
  "counts": {
    "vertices": 145,
    "faces": 0,
    "edges": 96,
    "aux_lines": 25,
    "aux_faces": 1,
    "primitives": 3,
    "labels": 5
  },
  "primitive_types": [
    "sphere",
    "sphere",
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.14,
      "position": [
        -0.717075571577218,
        1.14,
        0.4516664971493968
      ],
      "color": "#8ecae6"
    },
    {
      "type": "sphere",
      "radius": 0.63,
      "position": [
        0.717075571577218,
        0.63,
        -0.4516664971493968
      ],
      "color": "#8ecae6"
    },
    {
      "type": "sphere",
      "radius": 0.055,
      "position": [
        0.20661499520021534,
        0.8115254237288135,
        -0.130141194093894
      ],
      "color": "#e03131"
    }
  ],
  "labels": [
    {
      "point": "O1",
      "text": "O₁"
    },
    {
      "point": "O2",
      "text": "O₂"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "LR",
      "text": "R"
    },
    {
      "point": "Lr",
      "text": "r"
    }
  ],
  "vertex_names": [
    "O1",
    "O2",
    "P",
    "C00",
    "C01",
    "C02",
    "C03",
    "C04",
    "C05",
    "C06",
    "C07",
    "C08",
    "C09",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23",
    "C24",
    "C25",
    "C26",
    "C27",
    "C28",
    "C29",
    "C30",
    "C31",
    "C32",
    "C33",
    "C34",
    "C35",
    "C36",
    "C37",
    "C38",
    "C39",
    "C40",
    "C41",
    "C42",
    "C43",
    "C44",
    "C45",
    "C46",
    "C47",
    "D00",
    "D01",
    "D02",
    "D03",
    "D04",
    "D05",
    "D06",
    "D07",
    "D08",
    "D09",
    "D10",
    "D11",
    "D12",
    "D13",
    "D14",
    "D15",
    "D16",
    "D17",
    "D18",
    "D19",
    "D20",
    "D21",
    "D22",
    "D23",
    "D24",
    "D25",
    "D26",
    "D27",
    "D28",
    "D29",
    "D30",
    "D31",
    "D32",
    "D33",
    "D34",
    "D35",
    "D36",
    "D37",
    "D38",
    "D39",
    "D40",
    "D41",
    "D42",
    "D43",
    "D44",
    "D45",
    "D46",
    "D47",
    "Gx0a",
    "Gx0b",
    "Gz0a",
    "Gz0b",
    "Gx1a",
    "Gx1b",
    "Gz1a",
    "Gz1b",
    "Gx2a",
    "Gx2b",
    "Gz2a",
    "Gz2b",
    "Gx3a",
    "Gx3b",
    "Gz3a",
    "Gz3b",
    "Gx4a",
    "Gx4b",
    "Gz4a",
    "Gz4b",
    "Gx5a"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "C00",
        "C01"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C01",
        "C02"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C02",
        "C03"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C03",
        "C04"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C04",
        "C05"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C05",
        "C06"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C06",
        "C07"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C07",
        "C08"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C08",
        "C09"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C09",
        "C10"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C10",
        "C11"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C11",
        "C12"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C12",
        "C13"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C13",
        "C14"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C14",
        "C15"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C15",
        "C16"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C16",
        "C17"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C17",
        "C18"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C18",
        "C19"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C19",
        "C20"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C20",
        "C21"
      ],
      "color": "#111827"
    },
    {
      "points": [
        "C21",
        "C22"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "C22",
        "C23"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "C23",
        "C24"
      ],
      "dash": true,
      "color": "#111827"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O1",
        "O2"
      ],
      "dash": true,
      "color": "#343a40"
    },
    {
      "points": [
        "O1",
        "P"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "P",
        "O2"
      ],
      "dash": true,
      "color": "#495057"
    },
    {
      "points": [
        "Gx0a",
        "Gx0b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz0a",
        "Gz0b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx1a",
        "Gx1b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz1a",
        "Gz1b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx2a",
        "Gx2b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz2a",
        "Gz2b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx3a",
        "Gx3b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz3a",
        "Gz3b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx4a",
        "Gx4b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz4a",
        "Gz4b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx5a",
        "Gx5b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz5a",
        "Gz5b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx6a",
        "Gx6b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz6a",
        "Gz6b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx7a",
        "Gx7b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz7a",
        "Gz7b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gx8a",
        "Gx8b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "Gz8a",
        "Gz8b"
      ],
      "dash": false,
      "color": "#e9ecef"
    },
    {
      "points": [
        "B0",
        "B1"
      ],
      "dash": true,
      "color": "#ced4da"
    },
    {
      "points": [
        "B1",
        "B2"
      ],
      "dash": true,
      "color": "#ced4da"
    },
    {
      "points": [
        "B2",
        "B3"
      ],
      "dash": true,
      "color": "#ced4da"
    },
    {
      "points": [
        "B3",
        "B0"
      ],
      "dash": true,
      "color": "#ced4da"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "A",
        "B",
        "C",
        "H"
      ],
      "color": "#fff3bf",
      "opacity": 0.13
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 两球参数:大球半径 R,小球半径 r;二者同置于浅色底面上并外切
5:     R = round(rng.uniform(0.95, 1.18), 2)

6:     r = round(rng.uniform(0.48, 0.68), 2)
7: 
8:     # 为了让两球都“落在”y=0底面上,球心高度分别取 R、r。
9:     # 外切且同切于一点要求球心距 |O1O2| = R + r。
10:     # 于是水平投影距离 h 满足 h^2 + (R-r)^2 = (R+r)^2,即 h = 2*sqrt(Rr)。
11:     theta = math.radians(rng.uniform(28.0, 38.0))
12:     half_h = math.sqrt(R * r)
13:     hx = half_h * math.cos(theta)
14:     hz = half_h * math.sin(theta)
18:     s = R + r
19: 
20:     # 外切点 P 在线段 O1O2 上,且 O1P=R, PO2=r
21:     P = (
22:         (r * O1[0] + R * O2[0]) / s,
23:         (r * O1[1] + R * O2[1]) / s,
24:         (r * O1[2] + R * O2[2]) / s,
25:     )
26: 
27:     # 竖直辅助截面:由 O1O2 的水平投影方向与竖直方向张成
28:     horizontal = (O2[0] - O1[0], 0.0, O2[2] - O1[2])
29:     e_u = tk.unit(horizontal)
30:     e_v = (0.0, 1.0, 0.0)
31: 
32:     vertices = {"O1": O1, "O2": O2, "P": P}
33: 
34:     # 两球实体与红色高亮切点
35:     primitives = [
36:         tk.prim_sphere(r=R, center=O1, color="#8ecae6"),
37:         tk.prim_sphere(r=r, center=O2, color="#8ecae6"),
38:         tk.prim_sphere(r=0.055, center=P, color="#e03131"),
39:     ]
40: 
41:     # 相机先定好,用其方向判定截面圆的前半圆/后半圆
42:     camera = tk.auto_camera(vertices, primitives=primitives, azim=35.0, elev=23.0, pad=1.35)
43:     cam_pos = tuple(camera["position"])
44: 
45:     edges = []
46: 
47:     # 显式注册“竖直辅助截面截得的两球大圆”的所有折线边:
48:     # 前半圆实线、后半圆虚线,避免 spec 中缺少截面大圆信息。
49:     def add_section_great_circle(center_name, center, radius, prefix):
50:         n = 48
51:         names = []
52:         view_vec = tk.vsub(cam_pos, center)
56:             pt = tk.vadd(center, tk.vmul(direction, radius))
57:             nm = f"{prefix}{i:02d}"
58:             vertices[nm] = pt
59:             names.append(nm)
60: 
61:         for i in range(n):
62:             j = (i + 1) % n
65:             # dot < 0 视为背面圆弧,用虚线;dot >= 0 视为前面圆弧,用实线
66:             is_back = tk.dot(mid_dir, view_vec) < 0
67:             edges.append(tk.edge(names[i], names[j], dash=is_back, color="#111827"))
68: 
69:     add_section_great_circle("O1", O1, R, "C")
70:     add_section_great_circle("O2", O2, r, "D")
71: 
72:     aux_lines = []
73: 
74:     # 虚线连心线 O1O2,同时包含半径段 O1P、PO2 的位置,用于标注 R、r
75:     aux_lines.append(tk.aux("O1", "O2", dash=True, color="#343a40"))
76:     aux_lines.append(tk.aux("O1", "P", dash=True, color="#495057"))
77:     aux_lines.append(tk.aux("P", "O2", dash=True, color="#495057"))
78: 
79:     # 浅色网格底面
80:     span = 1.22 * (R + r + 2.0 * half_h)
81:     grid_step = span / 4.0
85:         a = f"Gx{k+kmax}a"
86:         b = f"Gx{k+kmax}b"
87:         vertices[a] = (x, 0.0, -span)
88:         vertices[b] = (x, 0.0, span)
89:         aux_lines.append(tk.aux(a, b, dash=False, color="#e9ecef"))
90: 
91:         z = k * grid_step
92:         c = f"Gz{k+kmax}a"
93:         d = f"Gz{k+kmax}b"
94:         vertices[c] = (-span, 0.0, z)
95:         vertices[d] = (span, 0.0, z)
96:         aux_lines.append(tk.aux(c, d, dash=False, color="#e9ecef"))
97: 
98:     # 底面外框
99:     vertices["B0"] = (-span, 0.0, span)
100:     vertices["B1"] = (span, 0.0, span)
101:     vertices["B2"] = (span, 0.0, -span)
102:     vertices["B3"] = (-span, 0.0, -span)
103:     for a, b in (("B0", "B1"), ("B1", "B2"), ("B2", "B3"), ("B3", "B0")):
104:         aux_lines.append(tk.aux(a, b, dash=True, color="#ced4da"))
105: 
106:     # 显式绘制竖直辅助截面面片,说明两大圆来自同一辅助截面
107:     u1 = tk.dot(O1, e_u)
108:     u2 = tk.dot(O2, e_u)
109:     u_min = min(u1 - R, u2 - r) - 0.12
110:     u_max = max(u1 + R, u2 + r) + 0.12
115:         return tk.vadd(tk.vmul(e_u, u), (0.0, y, 0.0))
116: 
117:     vertices["A"] = plane_point(u_min, y_min)
118:     vertices["B"] = plane_point(u_max, y_min)
119:     vertices["C"] = plane_point(u_max, y_max)
120:     vertices["H"] = plane_point(u_min, y_max)
121:     aux_faces = [tk.face(["A", "B", "C", "H"], color="#fff3bf", opacity=0.13)]
122: 
123:     # 半径标注点:仅用于放置 R、r 标签
124:     mid_R = tk.midpoint(O1, P)
125:     mid_r = tk.midpoint(P, O2)
126:     lift = 0.13
127:     vertices["LR"] = (mid_R[0], mid_R[1] + lift, mid_R[2])
128:     vertices["Lr"] = (mid_r[0], mid_r[1] + lift, mid_r[2])
129: 
130:     spec = tk.make_spec(
131:         "两球外切",
132:         "S017: explicit section great circles C*/D*, dashed back arcs, dashed center line O1O2",
133:         vertices,
134:         edges=edges,
135:         aux_lines=aux_lines,
136:         aux_faces=aux_faces,
137:         primitives=primitives,
138:         labels=None,
139:         camera=camera,
140:     )
141: 
142:     spec["labels"] = [
143:         {"point": "O1", "text": "O₁"},
144:         {"point": "O2", "text": "O₂"},
145:         {"point": "P", "text": "P"},
146:         {"point": "LR", "text": "R"},
151:     center_dist = tk.dist(O1, O2)
152:     if abs(center_dist - (R + r)) < 1e-8:
153:         relation = "两球外切"
154:     elif center_dist > R + r:
155:         relation = "两球外离"
156:     elif abs(center_dist - abs(R - r)) < 1e-8:
157:         relation = "两球内切"
158:     else:
159:         relation = "两球相交"
160: 
161:     question = "如图,过球心O₁、O₂的竖直辅助截面截两球得两个大圆,两圆外切于点P,且P为两球唯一的公共点。则球O₁与球O₂的空间位置关系是( )"
162:     opts = ["两球外切", "两球外离", "两球相交", "两球内切"]
163:     rng.shuffle(opts)
164:     options = {"A": opts[0], "B": opts[1], "C": opts[2], "D": opts[3]}
165:     answer = "ABCD"[opts.index(relation)]
166: 

K26 Spec-to-Image Review

  • major/sphere_render
    球体表面布满白色经纬网格线,与截面大圆、标签重叠,严重偏离教材球体画法
    spec_fix: toolkit层为sphere primitive增加无网格线/仅轮廓模式,支持不透明实心填充,去除默认经纬线
  • minor/label_overlap
    R标签紧贴左球截面圆,r与O₂略近,存在轻微重叠风险
    spec_fix: 将LR、Lr标签沿连心线垂向往外侧偏移0.1-0.15
  • minor/mesh_clutter
    底面网格线过密(约8x8),视觉噪音较大
    spec_fix: 减少底面网格密度至4x4或5x5,降低干扰
What To Change
  • 要求renderer关闭sphere默认经纬线,改用无网格教材风格
  • 降低底面网格密度
Preferred Primitives
  • sphere(无网格线模式)
Remove/Simplify
  • 底面网格线减半至4x4
  • 球体表面除截面大圆外不显示其他线条
Label Fixes
  • R、r标签沿法向或垂向往外侧偏移避免压线
Camera Fixes
  • none
Toolkit/Renderer: sphere primitive需支持关闭默认经纬线网格与半透明,仅保留外轮廓及用户显式指定的截面圆线
representation: spec显式构造了截面大圆、连心线、辅助面片,结构正确,但sphere primitive默认渲染风格不符合教材规范
readability: 球体密集网格线导致核心几何元素被淹没,整体可读性较差
plan: 覆盖两球、切点P、竖直截面、连心线、半径标注全部要素
next: toolkit_patch

solid3d_spatial_tangent_external__S019

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 4 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
绘制轴线竖直的直圆台(上底小、下底大),内部放置一同轴内接圆柱(半径小于上底、等高、下底共面),形成组合体。在圆台右上方外置一球,球心位于圆台轴截面内,球与圆台侧面外切,切点构成一个水平切圆(在右侧母线处标出切点T,并画细实线圆示意该切圆)。球下部被圆台上底面遮挡,被遮挡的球面轮廓用虚线圆弧表示。圆台与圆柱的隐藏母线、下底隐藏半圆、圆柱上底隐藏半圆均画虚线。标出球心O、圆台轴线l、切圆所在水平辅助面α。
Must Render
[
  "圆台及其上下底圆、母线、轴线",
  "内部同轴内接圆柱及其上下底",
  "外部球及其被圆台遮挡的虚线部分",
  "轴截面内的切点T与水平切圆示意线",
  "圆台和圆柱的隐藏边虚线"
]

Spec Summary

{
  "title": "圆台圆柱与球组合体",
  "meta": "圆台与同轴圆柱;右上方球与圆台侧面外切,含切圆、遮挡虚线和隐藏边",
  "camera": {
    "position": [
      9.36,
      8.298,
      16.669
    ],
    "target": [
      0.367,
      1.702,
      -0.245
    ],
    "orthoSize": 5.898
  },
  "counts": {
    "vertices": 205,
    "faces": 28,
    "edges": 0,
    "aux_lines": 157,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 4
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.2771841929880994,
      "position": [
        2.150144814780539,
        3.067163494845243,
        0.0
      ],
      "color": "#8fbadd"
    }
  ],
  "labels": [
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "l",
      "text": "l"
    },
    {
      "point": "alpha_pt",
      "text": "α"
    }
  ],
  "vertex_names": [
    "FA",
    "FB",
    "FC",
    "FD",
    "FE",
    "FF",
    "FG",
    "FH",
    "FI",
    "FJ",
    "FK",
    "FL",
    "FM",
    "FN",
    "FP",
    "FQ",
    "FR",
    "FS",
    "FT",
    "FU",
    "FV",
    "FW",
    "FX",
    "FY",
    "CA",
    "CB",
    "CC",
    "CD",
    "CE",
    "CF",
    "CG",
    "CH",
    "CI",
    "CJ",
    "CK",
    "CL",
    "CM",
    "CN",
    "CP",
    "CQ",
    "CR",
    "CS",
    "CT",
    "CU",
    "CV",
    "CW",
    "CX",
    "CY",
    "O",
    "T",
    "l",
    "l0",
    "fhb0",
    "fhb1",
    "fhb2",
    "fhb3",
    "fhb4",
    "fhb5",
    "fhb6",
    "fhb7",
    "fhb8",
    "fhb9",
    "fhb10",
    "fhb11",
    "fhb12",
    "fhb13",
    "fhb14",
    "fhb15",
    "fhb16",
    "fhb17",
    "fhb18",
    "fht0",
    "fht1",
    "fht2",
    "fht3",
    "fht4",
    "fht5",
    "fht6",
    "fht7",
    "fht8",
    "fht9",
    "fht10",
    "fht11",
    "fht12",
    "fht13",
    "fht14",
    "fht15",
    "fht16",
    "fht17",
    "fht18",
    "chb0",
    "chb1",
    "chb2",
    "chb3",
    "chb4",
    "chb5",
    "chb6",
    "chb7",
    "chb8",
    "chb9",
    "chb10",
    "chb11",
    "chb12",
    "chb13",
    "chb14",
    "chb15",
    "chb16",
    "chb17",
    "chb18",
    "cht0",
    "cht1",
    "cht2",
    "cht3",
    "cht4",
    "cht5",
    "cht6",
    "cht7",
    "cht8",
    "cht9",
    "cht10"
  ],
  "faces_sample": [
    {
      "points": [
        "FL",
        "FK",
        "FJ",
        "FI",
        "FH",
        "FG",
        "FF",
        "FE",
        "FD",
        "FC",
        "FB",
        "FA"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FM",
        "FN",
        "FP",
        "FQ",
        "FR",
        "FS",
        "FT",
        "FU",
        "FV",
        "FW",
        "FX",
        "FY"
      ],
      "color": "#eef2f7",
      "opacity": 0.34
    },
    {
      "points": [
        "FA",
        "FB",
        "FN",
        "FM"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FB",
        "FC",
        "FP",
        "FN"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FC",
        "FD",
        "FQ",
        "FP"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FD",
        "FE",
        "FR",
        "FQ"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FE",
        "FF",
        "FS",
        "FR"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FF",
        "FG",
        "FT",
        "FS"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FG",
        "FH",
        "FU",
        "FT"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FH",
        "FI",
        "FV",
        "FU"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FI",
        "FJ",
        "FW",
        "FV"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FJ",
        "FK",
        "FX",
        "FW"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FK",
        "FL",
        "FY",
        "FX"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "FL",
        "FA",
        "FM",
        "FY"
      ],
      "color": "#edf2f7",
      "opacity": 0.18
    },
    {
      "points": [
        "CL",
        "CK",
        "CJ",
        "CI",
        "CH",
        "CG",
        "CF",
        "CE",
        "CD",
        "CC",
        "CB",
        "CA"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CM",
        "CN",
        "CP",
        "CQ",
        "CR",
        "CS",
        "CT",
        "CU",
        "CV",
        "CW",
        "CX",
        "CY"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CA",
        "CB",
        "CN",
        "CM"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CB",
        "CC",
        "CP",
        "CN"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CC",
        "CD",
        "CQ",
        "CP"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CD",
        "CE",
        "CR",
        "CQ"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CE",
        "CF",
        "CS",
        "CR"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CF",
        "CG",
        "CT",
        "CS"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CG",
        "CH",
        "CU",
        "CT"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    },
    {
      "points": [
        "CH",
        "CI",
        "CV",
        "CU"
      ],
      "color": "#f8fafc",
      "opacity": 0.16
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "l0",
        "l"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "fhb0",
        "fhb1"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb1",
        "fhb2"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb2",
        "fhb3"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb3",
        "fhb4"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb4",
        "fhb5"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb5",
        "fhb6"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb6",
        "fhb7"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb7",
        "fhb8"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb8",
        "fhb9"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb9",
        "fhb10"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb10",
        "fhb11"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb11",
        "fhb12"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb12",
        "fhb13"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb13",
        "fhb14"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb14",
        "fhb15"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb15",
        "fhb16"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb16",
        "fhb17"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fhb17",
        "fhb18"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht0",
        "fht1"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht1",
        "fht2"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht2",
        "fht3"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht3",
        "fht4"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht4",
        "fht5"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht5",
        "fht6"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht6",
        "fht7"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht7",
        "fht8"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht8",
        "fht9"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht9",
        "fht10"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht10",
        "fht11"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht11",
        "fht12"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht12",
        "fht13"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht13",
        "fht14"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht14",
        "fht15"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht15",
        "fht16"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht16",
        "fht17"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "fht17",
        "fht18"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb0",
        "chb1"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb1",
        "chb2"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb2",
        "chb3"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb3",
        "chb4"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb4",
        "chb5"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb5",
        "chb6"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb6",
        "chb7"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb7",
        "chb8"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb8",
        "chb9"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb9",
        "chb10"
      ],
      "dash": true,
      "color": "#111827"
    },
    {
      "points": [
        "chb10",
        "chb11"
      ],
      "dash": true,
      "color": "#111827"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "a1",
        "a2",
        "a3",
        "a4"
      ],
      "color": "#dbeafe",
      "opacity": 0.08
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     n = 12
5:     R = rng.uniform(2.25, 2.60)

8:     rc = r * rng.uniform(0.52, 0.70)
9: 
10:     vs_f, faces_f = tk.frustum(n, r_bot=R, r_top=r, h=H)
11:     bot0 = [tk._NAMES[i] for i in range(n)]
12:     top0 = [tk._NAMES[n + i] for i in range(n)]
13: 
14:     vs = {}
17:         vs[mf[k]] = v
18: 
19:     faces = []
20:     for idx, f in enumerate(faces_f):
21:         ff = {"points": [mf[p] for p in f["points"]]}
22:         if idx == 1:
23:             ff.update({"color": "#eef2f7", "opacity": 0.34})
24:         else:
25:             ff.update({"color": "#edf2f7", "opacity": 0.18})
26:         faces.append(ff)
27: 
28:     vs_c, faces_c = tk.prism(n, r=rc, h=H)
29:     mc = {k: "C" + k for k in vs_c}
30:     for k, v in vs_c.items():
31:         vs[mc[k]] = v
32:     for f in faces_c:
33:         faces.append({"points": [mc[p] for p in f["points"]],
34:                       "color": "#f8fafc", "opacity": 0.16})
35: 
36:     i_right = max(range(n), key=lambda i: vs_f[bot0[i]][0])
37:     A = vs[mf[bot0[i_right]]]
51:     vs["T"] = (Tx, Ty, Tz)
52: 
53:     aux_lines = []
54: 
55:     aux_lines.append(tk.rotation_axis(vs, (0.0, -0.18, 0.0), (0.0, H + 0.42, 0.0), name="l"))
56: 
57:     def add_seg(a, b, dash=True, color="#111827"):
58:         aux_lines.append({"points": [a, b], "dash": bool(dash), "color": color})
59: 
60:     def add_arc_y(prefix, radius, y, start_deg, end_deg, parts, dash=True, color="#111827"):
61:         names = []
62:         for j in range(parts + 1):
63:             ang = math.radians(start_deg + (end_deg - start_deg) * j / parts)
64:             nm = prefix + str(j)
69:         return names
70: 
71:     def add_sphere_vertical_arc(prefix, start_deg, end_deg, parts, dash=True, color="#111827"):
72:         names = []
73:         for j in range(parts + 1):
74:             ang = math.radians(start_deg + (end_deg - start_deg) * j / parts)
75:             nm = prefix + str(j)
80:         return names
81: 
82:     def add_sphere_horizontal_arc(prefix, y, start_deg, end_deg, parts, dash=True, color="#111827"):
83:         dy = y - O[1]
84:         rho2 = Rs * Rs - dy * dy
85:         if rho2 <= 0:
86:             return []
98:     hidden_color = "#111827"
99: 
100:     add_arc_y("fhb", R, 0.0, 180.0, 360.0, 18, dash=True, color=hidden_color)
101:     add_arc_y("fht", r, H, 180.0, 360.0, 18, dash=True, color=hidden_color)
102: 
103:     add_arc_y("chb", rc, 0.0, 180.0, 360.0, 18, dash=True, color=hidden_color)
104:     add_arc_y("cht", rc, H, 180.0, 360.0, 18, dash=True, color=hidden_color)
105: 
106:     for i in range(n):
107:         bp = vs[mf[bot0[i]]]
108:         if bp[2] < -1e-6:
122:         add_seg(cut_names[j], cut_names[(j + 1) % 32], dash=False, color="#6b7280")
123: 
124:     add_sphere_vertical_arc("svh", 210.0, 318.0, 20, dash=True, color=hidden_color)
125:     add_sphere_horizontal_arc("soh", H, 132.0, 238.0, 18, dash=True, color=hidden_color)
126: 
127:     alpha_size = max(2.15 * Tx, 1.35 * R)
128:     vs["a1"] = (-alpha_size, Ty, -alpha_size)
129:     vs["a2"] = (alpha_size, Ty, -alpha_size)
134:     vs["alpha_pt"] = (alpha_size + 0.15, Ty, alpha_size + 0.10)
135: 
136:     aux_faces = [tk.face(["a1", "a2", "a3", "a4"], color="#dbeafe", opacity=0.08)]
137: 
138:     prims = [tk.prim_sphere(r=Rs, center=O, color="#8fbadd")]
139: 
140:     cam = tk.auto_camera(vs, primitives=prims, azim=28.0, elev=19.0, pad=1.58)
141:     spec = tk.make_spec(
142:         "圆台圆柱与球组合体",
143:         "圆台与同轴圆柱;右上方球与圆台侧面外切,含切圆、遮挡虚线和隐藏边",
144:         vs,
145:         faces=faces,
146:         aux_lines=aux_lines,
147:         aux_faces=aux_faces,
148:         primitives=prims,
149:         labels=[],
150:         camera=cam
151:     )
152:     spec["labels"] = [
153:         {"point": "O", "text": "O"},
154:         {"point": "T", "text": "T"},
155:         {"point": "l", "text": "l"},
156:         {"point": "alpha_pt", "text": "α"}
157:     ]
158: 
159:     correct = "球与圆台外切,且切点为一个圆"
160:     vals = [
161:         correct,
162:         "球与圆台相交",
163:         "球与圆台外离",
164:         "球内切于圆台"
165:     ]
166:     letters = ["A", "B", "C", "D"]
167:     rng.shuffle(letters)
168:     options = {letters[i]: vals[i] for i in range(4)}
171:     return tk.item(
172:         spec,
173:         "如图,在该组合体中,圆台外上方球与圆台的位置关系是?",
174:         options,
175:         answer
176:     )

K26 Spec-to-Image Review

  • major/frustum_render
    圆台侧面由12个梯形面片拼成,可见12条母线,呈明显棱台状,非教材圆台画法。
    spec_fix: 改用frustum primitive或教材式上下椭圆底+2~4条可见母线及隐藏虚线,删除多边形侧面网格。
  • major/cone_cylinder_render
    内接圆柱被画成12棱柱,侧面有12条纵向棱线,与教材圆柱线框表示严重不符。
    spec_fix: 改用cylinder primitive或教材式椭圆底+2条可见母线+隐藏虚线,移除棱柱网格。
  • minor/aux_line_clutter
    aux_lines多达157条,大量用于拼接底面圆弧和母线,导致图面杂乱。
    spec_fix: 精简辅助线,仅保留必要的隐藏轮廓、切圆示意弧和球遮挡虚线圆弧。
  • minor/label_missing
    α标签位于右下角,距离水平辅助面较远,关联性弱。
    spec_fix: 将α标签移至水平辅助面框的上方或右侧边缘附近,增强对应关系。
What To Change
  • 圆台改用教材式椭圆底+母线表示
  • 圆柱改用教材式椭圆底+母线表示
  • 精简辅助线数量
Preferred Primitives
  • frustum
  • cylinder
  • sphere
  • section_circle
Remove/Simplify
  • 移除12边形侧面mesh及所有梯形面片
  • 移除多余的底面拼接短线
  • 仅保留关键轮廓母线
Label Fixes
  • α标签靠近水平辅助面框放置
  • T点标签确保贴于右侧切点
Camera Fixes
  • 当前视角基本可用,微调使轴线l更居中
Toolkit/Renderer: toolkit需新增教材式圆台/圆柱线框primitive:以椭圆底+少量母线渲染,避免自动生成多边形侧面网格。
representation: 圆台与圆柱被表示为多边形棱台/棱柱,严重偏离教材标准线框画法,核心几何体识别度低。
readability: 单视角能辨认场景,但侧面网格过密导致视觉混乱,球与圆台切点关系尚可辨识。
plan: 基本覆盖plan要求的元素(球、圆台、圆柱、切点T、轴线l、平面α、遮挡虚线),但表示方式错误。
next: toolkit_patch

solid3d_spatial_tangent_external__S002

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 5 major_spec_issue minor hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
正四棱锥 P-ABCD 底面水平放置,远侧边及被遮挡侧棱画虚线,底面正方形内画出对角线 AC、BD 交于中心 H。球 O 位于四棱锥右前方外侧,与侧面 PBC 相切于点 T,连接 OT 并标注 OT⊥平面 PBC。过 P,B,D 作阴影对角截面 PBD,该面截球得到一个圆(前半实线、后半虚线)。从球心 O 向底面作垂线 OH'。侧面 PBC 内作斜高 PE。球被四棱锥遮挡的后半轮廓画虚线。
Must Render
[
  "正四棱锥 P-ABCD 的可见棱与虚线隐藏棱",
  "底面正方形及其两条对角线 AC、BD",
  "球 O 与侧面 PBC 的切点 T",
  "线段 OT 及 OT⊥平面 PBC 的直角符号",
  "对角截面 PBD(阴影)及其与球的交线圆",
  "球心 O 到底面的垂线 OH'",
  "球体被遮挡的后半轮廓虚线"
]

Spec Summary

{
  "title": "正四棱锥与球外切",
  "meta": "P-ABCD; aux_lines explicitly include AC, BD meeting at H; tangent point T; OT perpendicular to face PBC; section PBD; OH'",
  "camera": {
    "position": [
      4.746,
      6.529,
      7.508
    ],
    "target": [
      -0.138,
      1.935,
      0.594
    ],
    "orthoSize": 2.551
  },
  "counts": {
    "vertices": 81,
    "faces": 5,
    "edges": 8,
    "aux_lines": 58,
    "aux_faces": 1,
    "primitives": 1,
    "labels": 10
  },
  "primitive_types": [
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 0.8050247210952618,
      "position": [
        0.034973250530569384,
        2.130271812938817,
        0.8605118212209465
      ],
      "color": "#8fb9dd"
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "H'",
      "text": "H'"
    },
    {
      "point": "E",
      "text": "E"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "P",
    "H",
    "E",
    "T",
    "O",
    "H'",
    "U",
    "V",
    "W",
    "m0",
    "m1",
    "m2",
    "m3",
    "m4",
    "m5",
    "m6",
    "m7",
    "m8",
    "m9",
    "m10",
    "m11",
    "m12",
    "m13",
    "m14",
    "m15",
    "m16",
    "m17",
    "m18",
    "m19",
    "m20",
    "m21",
    "m22",
    "m23",
    "m24",
    "m25",
    "m26",
    "m27",
    "m28",
    "m29",
    "m30",
    "m31",
    "n0",
    "n1",
    "n2",
    "n3",
    "n4",
    "n5",
    "n6",
    "n7",
    "n8",
    "n9",
    "n10",
    "n11",
    "n12",
    "n13",
    "n14",
    "n15",
    "n16",
    "n17",
    "n18",
    "n19",
    "n20",
    "n21",
    "n22",
    "n23",
    "n24",
    "n25",
    "n26",
    "n27",
    "n28",
    "n29",
    "n30",
    "n31",
    "n32",
    "n33",
    "n34",
    "n35"
  ],
  "faces_sample": [
    {
      "points": [
        "D",
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "P"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "P"
      ]
    },
    {
      "points": [
        "C",
        "D",
        "P"
      ]
    },
    {
      "points": [
        "D",
        "A",
        "P"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "P",
        "B"
      ]
    },
    {
      "points": [
        "P",
        "C"
      ]
    },
    {
      "points": [
        "P",
        "D"
      ]
    },
    {
      "points": [
        "B",
        "C"
      ]
    },
    {
      "points": [
        "C",
        "D"
      ]
    },
    {
      "points": [
        "P",
        "A"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true
    },
    {
      "points": [
        "A",
        "D"
      ],
      "dash": true
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "A",
        "C"
      ],
      "dash": true,
      "color": "#222222"
    },
    {
      "points": [
        "B",
        "D"
      ],
      "dash": true,
      "color": "#222222"
    },
    {
      "points": [
        "O",
        "T"
      ],
      "dash": false,
      "color": "#111111"
    },
    {
      "points": [
        "O",
        "H'"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "P",
        "E"
      ],
      "dash": false,
      "color": "#555555"
    },
    {
      "points": [
        "T",
        "U"
      ],
      "dash": false,
      "color": "#111111"
    },
    {
      "points": [
        "V",
        "W"
      ],
      "dash": false,
      "color": "#111111"
    },
    {
      "points": [
        "U",
        "W"
      ],
      "dash": false,
      "color": "#111111"
    },
    {
      "points": [
        "m0",
        "m1"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m1",
        "m2"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m2",
        "m3"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m3",
        "m4"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m4",
        "m5"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m5",
        "m6"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m6",
        "m7"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m7",
        "m8"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m8",
        "m9"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m9",
        "m10"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m10",
        "m11"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m11",
        "m12"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m12",
        "m13"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m13",
        "m14"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m14",
        "m15"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m15",
        "m16"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m16",
        "m17"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m17",
        "m18"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m18",
        "m19"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m19",
        "m20"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m20",
        "m21"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m21",
        "m22"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m22",
        "m23"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m23",
        "m24"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m24",
        "m25"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m25",
        "m26"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m26",
        "m27"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m27",
        "m28"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m28",
        "m29"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m29",
        "m30"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m30",
        "m31"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "m31",
        "m0"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "n12",
        "n13"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n13",
        "n14"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n14",
        "n15"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n15",
        "n16"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n16",
        "n17"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n17",
        "n18"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n18",
        "n19"
      ],
      "dash": true,
      "color": "#777777"
    },
    {
      "points": [
        "n19",
        "n20"
      ],
      "dash": true,
      "color": "#777777"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "P",
        "B",
        "D"
      ],
      "color": "#d9d9d9",
      "opacity": 0.38
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # 正四棱锥:底面水平,旋转 45° 后使 BC 为前侧边,侧面 PBC 面向球
5:     r = rng.uniform(1.15, 1.38)

6:     h = rng.uniform(1.75, 2.18)
7:     vs, faces = tk.pyramid(4, r=r, h=h)
8:     vs = {k: tk.rot_y(v, 45.0) for k, v in vs.items()}
9: 
10:     P, A, B, C, D = vs["P"], vs["A"], vs["B"], vs["C"], vs["D"]
11:     a = r / math.sqrt(2.0)
19:     n_face = (0.0, a / nl, h / nl)
20: 
21:     # 取切点 T 在三角形 PBC 内部且靠近 P,使对角截面 PBD 能截球成圆
22:     R = rng.uniform(0.54, 0.68) * r
23:     allowance = max(0.12 * r, R * (math.sqrt(2.0) - h / nl))
24:     tz = rng.uniform(0.28, 0.42) * allowance
25:     tx = rng.uniform(0.18, 0.36) * tz
27:     T = (tx, Ty, tz)
28: 
29:     # 球心 O:OT 为半径,且 OT 垂直侧面 PBC
30:     O = tk.vadd(T, tk.vmul(n_face, R))
31: 
32:     # 球心到底面的垂足 H'
33:     Hp = (O[0], 0.0, O[2])
34: 
35:     vs.update({
36:         "H": H,
48:     vs["W"] = tk.vadd(vs["U"], tk.vmul(n_face, s))
49: 
50:     # 对角截面 PBD 所在平面 x + z = 0,与球相交所得圆
51:     npbd = tk.unit((1.0, 0.0, 1.0))
52:     signed_d = tk.dot(O, npbd)
53:     circle_center = tk.vsub(O, tk.vmul(npbd, signed_d))
54:     rc = math.sqrt(max(1e-8, R * R - signed_d * signed_d))
79:         cross_pts.append(nm)
80: 
81:     # 球后半轮廓虚线:取过球心的竖直大圆,按视向判定背面半圆
82:     sphere_pts = []
83:     M = 36
84:     for i in range(M):
85:         th = 2.0 * math.pi * i / M
86:         p = (O[0], O[1] + R * math.sin(th), O[2] + R * math.cos(th))
87:         nm = "n%d" % i
88:         vs[nm] = p
89:         sphere_pts.append(nm)
90: 
91:     # 四棱锥真实棱:可见棱实线,远侧/遮挡棱虚线
92:     edges = [
93:         tk.edge("P", "B"),
94:         tk.edge("P", "C"),
95:         tk.edge("P", "D"),
96:         tk.edge("B", "C"),
102: 
103:     auxs = [
104:         # 必须显式在 aux_lines 中给出底面对角线 AC、BD,并交于 H
105:         tk.aux("A", "C", dash=True, color="#222222"),
106:         tk.aux("B", "D", dash=True, color="#222222"),
107: 
108:         # 切半径 OT 与底面垂线 OH'
109:         tk.aux("O", "T", dash=False, color="#111111"),
110:         tk.aux("O", "H'", dash=True, color="#555555"),
111: 
112:         # 侧面 PBC 内斜高 PE
119:     ]
120: 
121:     # 对角截面 PBD 与球的交线圆:靠近观察者半圆实线,背半圆虚线
122:     for i in range(N):
123:         p1, p2 = cross_pts[i], cross_pts[(i + 1) % N]
124:         mid = tk.midpoint(vs[p1], vs[p2])
125:         front = tk.dot(tk.vsub(mid, O), view_dir) >= 0.0
126:         auxs.append(tk.aux(p1, p2, dash=not front, color="#555555"))
127: 
128:     # 球体被遮挡的后半轮廓虚线
129:     for i in range(M):
130:         p1, p2 = sphere_pts[i], sphere_pts[(i + 1) % M]
131:         mid = tk.midpoint(vs[p1], vs[p2])
132:         back = tk.dot(tk.vsub(mid, O), view_dir) < 0.0
133:         if back:
134:             auxs.append(tk.aux(p1, p2, dash=True, color="#777777"))
135: 
136:     # 阴影对角截面 PBD
137:     aux_faces = [
138:         tk.face(["P", "B", "D"], color="#d9d9d9", opacity=0.38)
139:     ]
140: 
141:     primitives = [
142:         tk.prim_sphere(R, center=O, color="#8fb9dd")
143:     ]
144: 
145:     cam = tk.auto_camera(vs, primitives=primitives, azim=cam_az, elev=cam_el, pad=1.50)
146: 
147:     spec = tk.make_spec(
148:         "正四棱锥与球外切",
149:         "P-ABCD; aux_lines explicitly include AC, BD meeting at H; tangent point T; OT perpendicular to face PBC; section PBD; OH'",
150:         vs,
151:         faces=faces,
152:         edges=edges,
153:         aux_lines=auxs,
154:         aux_faces=aux_faces,
155:         primitives=primitives,
156:         labels=["A", "B", "C", "D", "P", "O", "T", "H", "H'", "E"],
157:         camera=cam
158:     )
159: 
160:     # 由几何关系计算“外切”:O 到侧面 PBC 的距离等于球半径,且垂足 T 在侧面内
161:     dist_to_face = abs(tk.dot(tk.vsub(O, P), n_face))
162:     on_tangent = abs(dist_to_face - R) < 1e-6
163:     in_face = (T[2] > 0.0 and abs(T[0]) < T[2] and 0.0 < T[1] < h)
164:     relation = "外切" if (on_tangent and in_face) else "相交"
165: 
166:     options = {
167:         "A": "外切",
168:         "B": "内切",
169:         "C": "外离",
170:         "D": "相交"
171:     }
172:     answer = [k for k, v in options.items() if v == relation][0]
174:     return tk.item(
175:         spec,
176:         "如图,观察球 O 与正四棱锥 P-ABCD 的位置关系,下列说法正确的是( )",
177:         options,
178:         answer
179:     )

K26 Spec-to-Image Review

  • major/mesh_clutter
    球面叠加截面圆、竖直大圆及轮廓线,aux_lines达58条,密集短段构成杂乱线网
    spec_fix: 删除竖直大圆n系列aux_lines,截面圆离散段数由36降至16,仅保留必要轮廓
  • major/sphere_render
    球primitive外包裹多层手动离散圆,线宽与虚实层次不清,失去教材球体简洁性
    spec_fix: 移除过球心竖直大圆,让sphere primitive自身表达球体,仅保留PBD截面圆
  • minor/label_overlap
    current视角A被球体遮挡,P贴球轮廓,H'与E在底面边缘拥挤
    spec_fix: 调整camera pad或label偏移,使A、P避开球体重叠区
  • minor/angle_arc
    OT⊥平面PBC的直角符号由零散小线段UVW拼成,不标准且易忽略
    spec_fix: 使用angle_arc或right_angle primitive绘制标准直角符号,替代手工折线
What To Change
  • 删除球体竖直大圆离散aux_lines
  • 降低截面圆离散度至16段
  • 调整相机避免球遮挡标签
  • 标准化直角符号
Preferred Primitives
  • sphere
  • angle_arc
Remove/Simplify
  • 移除n0-n35共36条竖直大圆线段
  • 截面圆段数减半
  • 合并同层次灰色辅助线
Label Fixes
  • A、P标签向空位偏移或拉远相机
Camera Fixes
  • 增大orthoSize或pad,使球与锥体比例协调不重叠
Toolkit/Renderer: none
representation: 球primitive使用正确,但过度手动离散化大圆与截面圆,导致球面线网杂乱,不符合教材简洁规范
readability: 双视角有助于观察PBD截面与切点T,但球面密集线网严重干扰主体识别
plan: must_render元素基本齐全,但直角符号不标准,球后轮廓与截面圆表达过于繁琐
next: visual_repair

solid3d_spatial_tangent_external__S003

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
主视方向为右前方45°俯视。画面中央偏后为一个直圆锥,轴线竖直,顶点S在上,底面圆心O₁在下且落于水平面上。圆锥正下方有一个球体,球心O在圆锥轴线的延长线上,球面与圆锥侧面沿一个公共圆外切,该切圆位于圆锥下三分之一侧表面,用实线绘制。圆锥左前方立有一个正三棱柱(侧棱竖直),其右后侧面与圆锥左前母线形成极小外离间隙,部分遮挡圆锥左下侧及球体左前部。辅助线:用细虚线画出圆锥轴截面(等腰三角形);画出轴线SO₁并向下延长过球心O;画出球心O到切圆上一点T的半径OT(垂直于圆锥母线,实线)。所有被遮挡的不可见棱边均用虚线表示。
Must Render
[
  "圆锥与球面的公共外切圆",
  "正三棱柱及其对圆锥、球体的遮挡轮廓",
  "圆锥轴线及轴截面辅助虚线",
  "球心到切点的半径OT"
]

Spec Summary

{
  "title": "圆锥与球体外切",
  "meta": "solid3d_spatial_tangent_external__S003",
  "camera": {
    "position": [
      9.995,
      6.159,
      -10.406
    ],
    "target": [
      -0.216,
      0.325,
      -0.195
    ],
    "orthoSize": 4.135
  },
  "counts": {
    "vertices": 36,
    "faces": 5,
    "edges": 33,
    "aux_lines": 6,
    "aux_faces": 0,
    "primitives": 2,
    "labels": 10
  },
  "primitive_types": [
    "cone",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cone",
      "radius": 1.5,
      "height": 3.0,
      "position": [
        0.0,
        1.5,
        0.0
      ]
    },
    {
      "type": "sphere",
      "radius": 1.6133192010306505,
      "position": [
        0.0,
        -0.6074914029101836,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "A",
      "text": "A"
    },
    {
      "point": "B",
      "text": "B"
    },
    {
      "point": "C",
      "text": "C"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "S",
      "text": "S"
    },
    {
      "point": "O1",
      "text": "O1"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "T",
      "text": "T"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "S",
    "O1",
    "O",
    "T",
    "M1",
    "M2",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "C16",
    "C17",
    "C18",
    "C19",
    "C20",
    "C21",
    "C22",
    "C23"
  ],
  "faces_sample": [
    {
      "points": [
        "C",
        "B",
        "A"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "F"
      ]
    },
    {
      "points": [
        "A",
        "B",
        "E",
        "D"
      ]
    },
    {
      "points": [
        "B",
        "C",
        "F",
        "E"
      ]
    },
    {
      "points": [
        "C",
        "A",
        "D",
        "F"
      ]
    }
  ],
  "edges_sample": [
    {
      "points": [
        "C",
        "B"
      ]
    },
    {
      "points": [
        "B",
        "A"
      ]
    },
    {
      "points": [
        "A",
        "C"
      ]
    },
    {
      "points": [
        "D",
        "E"
      ]
    },
    {
      "points": [
        "E",
        "F"
      ]
    },
    {
      "points": [
        "F",
        "D"
      ]
    },
    {
      "points": [
        "B",
        "E"
      ]
    },
    {
      "points": [
        "D",
        "A"
      ]
    },
    {
      "points": [
        "C",
        "F"
      ]
    },
    {
      "points": [
        "C0",
        "C1"
      ]
    },
    {
      "points": [
        "C1",
        "C2"
      ]
    },
    {
      "points": [
        "C2",
        "C3"
      ]
    },
    {
      "points": [
        "C3",
        "C4"
      ]
    },
    {
      "points": [
        "C4",
        "C5"
      ]
    },
    {
      "points": [
        "C5",
        "C6"
      ]
    },
    {
      "points": [
        "C6",
        "C7"
      ]
    },
    {
      "points": [
        "C7",
        "C8"
      ]
    },
    {
      "points": [
        "C8",
        "C9"
      ]
    },
    {
      "points": [
        "C9",
        "C10"
      ]
    },
    {
      "points": [
        "C10",
        "C11"
      ]
    },
    {
      "points": [
        "C11",
        "C12"
      ]
    },
    {
      "points": [
        "C12",
        "C13"
      ]
    },
    {
      "points": [
        "C13",
        "C14"
      ]
    },
    {
      "points": [
        "C14",
        "C15"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "S",
        "M1"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "S",
        "M2"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "M1",
        "M2"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "S",
        "O1"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "O1",
        "O"
      ],
      "dash": true,
      "color": "#a0a0a0"
    },
    {
      "points": [
        "O",
        "T"
      ],
      "dash": false,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 圆锥与球的几何参数(切圆位于圆锥下三分之一) ----
5:     H = rng.choice([2.4, 3.0, 3.6])

6:     R = H * rng.choice([0.4, 0.5, 0.6])
7:     y_t = rng.uniform(0.08, H / 3.0 - 0.08)
8:     d = (R * R - y_t * (R * R + H * H) / H) / H
19:     cz = rng.uniform(0.8, 1.2)
20:     rot = rng.uniform(-12.0, 12.0)
21:     # 避免三棱柱与球体严重相交,保证外离或仅极近遮挡
22:     dist_os = math.sqrt(cx * cx + cz * cz + d * d)
23:     need = r + r_tri + 0.15
24:     if dist_os < need:
25:         s = need / dist_os
26:         cx *= s
27:         cz *= s
28:     vs_tri, faces_tri = tk.prism(3, r=r_tri, h=h_tri)
29:     vs_tri = {k: tk.rot_y(v, rot) for k, v in vs_tri.items()}
30:     vs_tri = tk.translate_all(vs_tri, (-cx, 0.0, -cz))
31: 
32:     # ---- 顶点组装 ----
33:     vertices = dict(vs_tri)
34:     vertices["S"] = (0.0, H, 0.0)
35:     vertices["O1"] = (0.0, 0.0, 0.0)
36:     vertices["O"] = (0.0, -d, 0.0)
37:     vertices["T"] = (x_t, y_t, 0.0)
38:     vertices["M1"] = (R, 0.0, 0.0)
39:     vertices["M2"] = (-R, 0.0, 0.0)
40: 
41:     # 公共外切圆(24段实线近似)
42:     n_c = 24
43:     for i in range(n_c):
44:         th = 2.0 * math.pi * i / n_c
45:         vertices[f"C{i}"] = (x_t * math.cos(th), y_t, x_t * math.sin(th))
46: 
47:     # ---- 边:三棱柱 + 切圆 ----
48:     edges = []
49:     seen = set()
50:     for f in faces_tri:
51:         pts = f["points"]
52:         m = len(pts)
53:         for i in range(m):
54:             a, b = pts[i], pts[(i + 1) % m]
56:             if key not in seen:
57:                 seen.add(key)
58:                 edges.append({"points": [a, b]})
59:     for i in range(n_c):
60:         edges.append({"points": [f"C{i}", f"C{(i + 1) % n_c}"]})
61: 
62:     # ---- 辅助线:轴截面(虚线)、轴线(虚线)、OT半径(实线) ----
63:     aux_lines = [
64:         tk.aux("S", "M1", dash=True, color="#a0a0a0"),
65:         tk.aux("S", "M2", dash=True, color="#a0a0a0"),
66:         tk.aux("M1", "M2", dash=True, color="#a0a0a0"),
67:         tk.aux("S", "O1", dash=True, color="#a0a0a0"),
71: 
72:     # ---- 实体原型 ----
73:     primitives = [
74:         tk.prim_cone(r=R, h=H, center=(0.0, 0.0, 0.0)),
75:         tk.prim_sphere(r=r, center=(0.0, -d, 0.0)),
76:     ]
77: 
78:     # ---- 相机:右前方45°俯视 ----
79:     labels = ["A", "B", "C", "D", "E", "F", "S", "O1", "O", "T"]
80:     camera = tk.auto_camera(vertices, primitives=primitives, azim=135.0, elev=22.0, pad=1.45)
81: 
82:     spec = tk.make_spec("圆锥与球体外切", "solid3d_spatial_tangent_external__S003",
83:                         vertices, faces=faces_tri, edges=edges, aux_lines=aux_lines,
84:                         primitives=primitives, labels=labels, camera=camera)
85: 
86:     # ---- 选项(随机打乱) ----
87:     pool = ["外切(相外切)", "内切", "相交", "外离"]
88:     rng.shuffle(pool)
89:     options = {"A": pool[0], "B": pool[1], "C": pool[2], "D": pool[3]}
90:     answer = [k for k, v in options.items() if v == "外切(相外切)"][0]
91: 
92:     return tk.item(spec, "如图,圆锥与球体的位置关系属于(  )", options, answer)

K26 Spec-to-Image Review

  • major/plan_coverage
    三棱柱位于画面右侧且与圆锥、球体存在明显间隙,未形成plan要求的左前方遮挡关系
    spec_fix: 修正三棱柱平移参数cx/cz符号与数值,使其位于左前方并部分遮挡圆锥左下侧及球体左前部
  • minor/label_missing
    底面圆心标签文本为O1而非下标O₁,球心O与底面圆心O₁易混淆
    spec_fix: 将label文本改为带下标的O₁,或调整字体确保下标可辨识
  • minor/sphere_render
    球体表面出现未要求的额外虚线圆(疑似primitive自动附加的纬线)
    spec_fix: 移除球体多余虚线截面圆,仅保留公共外切实线圆及必要轮廓
  • minor/cone_cylinder_render
    圆锥底面圆未显式绘制,仅靠primitive渲染导致底面边界与球体顶部难以区分
    spec_fix: 显式添加圆锥底面圆edge,根据遮挡关系设为实线或虚线以增强可定位性
What To Change
  • 修正三棱柱空间坐标使其位于左前方并形成遮挡
  • 显式添加圆锥底面圆edge
Preferred Primitives
  • cone
  • sphere
Remove/Simplify
  • 移除球体多余虚线纬线
  • 简化三棱柱与圆锥间隙至极小外离
Label Fixes
  • O1改为下标O₁
  • 检查T标签避免与切圆重叠
Camera Fixes
  • 保持当前右前方45°俯视相机
Toolkit/Renderer: none
representation: 圆锥与球体外切关系及切圆、辅助线表达正确,但三棱柱空间布局与plan严重不符
readability: 单图视角下主体对象清晰,OT半径与轴截面可辨,因三棱柱远离导致缺少遮挡层次
plan: 未实现三棱柱左前方遮挡圆锥与球体的核心场景,需重写三棱柱定位逻辑
next: spec_rewrite

solid3d_spatial_tangent_external__S007

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
斜二测视角下,一个直立圆锥置于圆柱底座上形成组合体。圆锥中下部外侧有一球,球心在圆锥轴线上,球与圆锥侧面沿一水平切圆外切,该切圆以加粗虚线标出并标注切点P。球左上约1/4被圆锥侧面遮挡。补画圆锥轴截面(等腰三角形虚线)与球心O—顶点V辅助实线。底座右前方放置一个与二者外离的小正三棱锥,其侧棱与球轮廓接近以制造混淆。隐藏边均用虚线。
Must Render
[
  "圆锥侧面与球面沿切圆接触的加粗虚线圆及切点P",
  "球被圆锥遮挡后仍可见的右下轮廓",
  "圆锥的可见轮廓与轴截面虚线",
  "底座右前方外离的小正三棱锥可见轮廓"
]

Spec Summary

{
  "title": "球与圆锥外切",
  "meta": "组合体位置关系",
  "camera": {
    "position": [
      7.124,
      5.426,
      7.562
    ],
    "target": [
      0.295,
      0.807,
      0.178
    ],
    "orthoSize": 3.053
  },
  "counts": {
    "vertices": 25,
    "faces": 4,
    "edges": 0,
    "aux_lines": 20,
    "aux_faces": 0,
    "primitives": 3,
    "labels": 7
  },
  "primitive_types": [
    "cylinder",
    "cone",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cylinder",
      "radius": 1.0688843703050097,
      "height": 0.6515908805880605,
      "position": [
        0.0,
        0.32579544029403024,
        0.0
      ]
    },
    {
      "type": "cone",
      "radius": 1.0688843703050097,
      "height": 1.568228632332338,
      "position": [
        0.0,
        1.4357051967542294,
        0.0
      ]
    },
    {
      "type": "sphere",
      "radius": 1.0389347777240678,
      "position": [
        0.0,
        0.3751411205945024,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "V",
      "text": "V"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "D",
      "text": "D"
    },
    {
      "point": "E",
      "text": "E"
    },
    {
      "point": "F",
      "text": "F"
    },
    {
      "point": "Q",
      "text": "Q"
    }
  ],
  "vertex_names": [
    "V",
    "O",
    "A",
    "B",
    "P",
    "C0",
    "C1",
    "C2",
    "C3",
    "C4",
    "C5",
    "C6",
    "C7",
    "C8",
    "C9",
    "C10",
    "C11",
    "C12",
    "C13",
    "C14",
    "C15",
    "D",
    "E",
    "F",
    "Q"
  ],
  "faces_sample": [
    {
      "points": [
        "F",
        "E",
        "D"
      ]
    },
    {
      "points": [
        "D",
        "E",
        "Q"
      ]
    },
    {
      "points": [
        "E",
        "F",
        "Q"
      ]
    },
    {
      "points": [
        "F",
        "D",
        "Q"
      ]
    }
  ],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        "V",
        "A"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "A",
        "B"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "B",
        "V"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O",
        "V"
      ],
      "dash": false,
      "color": "#b45309"
    },
    {
      "points": [
        "C0",
        "C1"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C1",
        "C2"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C2",
        "C3"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C3",
        "C4"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C4",
        "C5"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C5",
        "C6"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C6",
        "C7"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C7",
        "C8"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C8",
        "C9"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C9",
        "C10"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C10",
        "C11"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C11",
        "C12"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C12",
        "C13"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C13",
        "C14"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C14",
        "C15"
      ],
      "dash": true,
      "color": "#e8590c"
    },
    {
      "points": [
        "C15",
        "C0"
      ],
      "dash": true,
      "color": "#e8590c"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---- 随机化组合体尺寸 ----
5:     R = rng.uniform(0.9, 1.1)          # 圆锥/圆柱底面半径

6:     H_cyl = rng.uniform(0.5, 0.7)      # 圆柱底座高
7:     H_cone = rng.uniform(1.4, 1.8)     # 圆锥高
8:     h = H_cyl                          # 圆锥底面高度
9:     H = h + H_cone                     # 圆锥顶点高度
10:     L = math.hypot(R, H_cone)          # 母线长
11: 
12:     # ---- 构造球心在轴线上、与圆锥侧面外切的球 ----
13:     # 令切圆位于圆锥中下部(靠近底面),球心在底面下方
14:     delta = rng.uniform(0.25, 0.75) * (R * R / H_cone)
15:     y_o = h - delta                    # 球心 y 坐标
16:     r = (H - y_o) * R / L              # 球半径(几何保证与母线距离 = r)
17:     y_p = y_o + r * R / L              # 切圆高度
18:     r_p = (H - y_p) / H_cone * R       # 切圆半径
19: 
20:     # ---- 顶点字典 ----
21:     vs = {}
22:     vs["V"] = (0.0, H, 0.0)            # 圆锥顶点
23:     vs["O"] = (0.0, y_o, 0.0)          # 球心
24:     vs["A"] = (-R, h, 0.0)             # 轴截面底边左端
25:     vs["B"] = (R, h, 0.0)              # 轴截面底边右端
26:     vs["P"] = (r_p, y_p, 0.0)          # 切点(右侧)
27: 
28:     # 切圆近似(水平圆,加粗虚线效果用醒目颜色代替)
29:     n = 16
30:     cpts = []
31:     for i in range(n):
32:         ang = 2.0 * math.pi * i / n
50:     # ---- 辅助线 ----
51:     auxs = []
52:     # 圆锥轴截面(等腰三角形虚线)
53:     auxs.append(tk.aux("V", "A", dash=True))
54:     auxs.append(tk.aux("A", "B", dash=True))
55:     auxs.append(tk.aux("B", "V", dash=True))
56:     # 球心 O — 顶点 V 辅助实线(深色)
57:     auxs.append(tk.aux("O", "V", dash=False, color="#b45309"))
58:     # 切圆虚线
59:     for i in range(n):
60:         auxs.append(tk.aux(cpts[i], cpts[(i + 1) % n], dash=True, color="#e8590c"))
61: 
62:     # ---- 实体 primitives(渲染器自动处理遮挡与轮廓)----
63:     prims = [
64:         tk.prim_cylinder(R, H_cyl, center=(0.0, 0.0, 0.0)),
65:         tk.prim_cone(R, H_cone, center=(0.0, h, 0.0)),
66:         tk.prim_sphere(r, center=(0.0, y_o, 0.0)),
67:     ]
68: 
69:     # ---- 相机:斜视角,保证体与体之间的前后遮挡关系 ----
70:     cam = tk.auto_camera(vs, primitives=prims,
71:                          azim=rng.uniform(38.0, 48.0),
72:                          elev=rng.uniform(20.0, 28.0),
73:                          pad=1.55)
74: 
75:     spec = tk.make_spec("球与圆锥外切", "组合体位置关系",
76:                         vs, faces=f3r, aux_lines=auxs,
77:                         primitives=prims,
78:                         labels=["V", "O", "P", "D", "E", "F", "Q"],
79:                         camera=cam)
80: 
81:     # ---- 答案由几何计算得出:验证球心到母线距离等于半径 → 外切 ----
82:     VA = (R, -H_cone, 0.0)
83:     VO = (0.0, y_o - H, 0.0)
84:     d_tan = tk.norm(tk.cross(VA, VO)) / tk.norm(VA)
85:     # 构造已保证 d_tan == r,据此判定正确关系
86:     relation = "球与圆锥外切"
87: 
88:     opts = ["球与圆锥外离", "球与圆锥外切", "球与圆锥相交", "球内切于圆锥"]
89:     rng.shuffle(opts)
90:     ans = "ABCD"[opts.index(relation)]
91:     options = {"A": opts[0], "B": opts[1], "C": opts[2], "D": opts[3]}
92: 
93:     return tk.item(spec, "如图,观察图中球与圆锥的位置关系,下列说法正确的是?",
94:                    options, ans)

K26 Spec-to-Image Review

  • major/plan_coverage
    圆柱底座被球完全遮挡,图中几乎不可见,违背plan中圆锥置于圆柱底座上的场景设定
    spec_fix: 调整相机至更高或更偏右方位,或略微缩小球半径/增大圆柱高度,使底座至少部分外露
  • minor/aux_line_clutter
    切圆仅用16段细虚线模拟,未加粗,与plan要求的加粗虚线不符
    spec_fix: 将切圆aux_lines线宽设为加粗,或改用section_circle primitive并指定粗线宽
  • minor/camera_or_view
    当前视角下球占据画面中心过大,圆柱底座与球的大小对比失衡
    spec_fix: 增大orthoSize或拉远相机,使组合体与三棱锥的空间关系更清晰
What To Change
  • 调整相机方位角与距离,使圆柱底座至少部分可见
  • 将切圆虚线线宽加粗以符合plan要求
Preferred Primitives
  • sphere
  • cone
  • cylinder
Remove/Simplify
  • 如圆柱仍无法可见,考虑降低球半径或提高圆柱高度
Label Fixes
  • 保持现有label位置,确保P点紧贴切圆
Camera Fixes
  • 相机改为更高俯角或更偏右侧,增大orthoSize以展示完整组合体
Toolkit/Renderer: none
representation: 球、圆锥、三棱锥primitive使用正确,但圆柱底座因遮挡未能在图中体现
readability: 切圆、轴截面、O-V辅助线及三棱锥可读,但球过大导致底座缺失
plan: 缺少圆柱底座的可见表达,切圆未加粗,与plan存在偏差
next: spec_rewrite

solid3d_spatial_tangent_external__S010

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 5 major_spec_issue major hold

Plan / Must Render

题型 体体外切
难度 L2
Scene
两个不等大的球体从外部相切于唯一公共点T,大球球心为O1、小球球心为O2,O1O2连线为实线且经过T。设置一个水平辅助截面平面α,以带两条交叉对角线的平行四边形表示,α同时截两球得到两个水平截面圆,圆心C1、C2分别在O1、O2正下方,O1C1与O2C2为铅垂虚线。再设置一个过O1O2的竖直辅助平面,截两球各得一个大圆,可见轮廓用实线、被遮挡部分用虚线绘制。
Must Render
[
  "大球、小球及球心O1、O2",
  "外切公共点T与球心连线O1O2",
  "水平截面平面α(带对角线的平行四边形)",
  "截面截两球所得两圆(含虚线隐藏弧)",
  "球心到截面的铅垂虚线O1C1、O2C2",
  "竖直辅助平面截两球所得大圆的虚实轮廓"
]

Spec Summary

{
  "title": "两球外切位置关系",
  "meta": "solid3d_spatial_tangent_external__S010",
  "camera": {
    "position": [
      12.74,
      16.704,
      22.263
    ],
    "target": [
      0.017,
      1.397,
      0.023
    ],
    "orthoSize": 8.65
  },
  "counts": {
    "vertices": 138,
    "faces": 0,
    "edges": 135,
    "aux_lines": 2,
    "aux_faces": 0,
    "primitives": 2,
    "labels": 6
  },
  "primitive_types": [
    "sphere",
    "sphere"
  ],
  "primitives": [
    {
      "type": "sphere",
      "radius": 1.7,
      "position": [
        -1.3,
        1.816193751272311,
        0.0
      ],
      "color": "#9ca3af"
    },
    {
      "type": "sphere",
      "radius": 0.9,
      "position": [
        1.3,
        1.816193751272311,
        0.0
      ],
      "color": "#6b7280"
    }
  ],
  "labels": [
    {
      "point": "O1",
      "text": "O1"
    },
    {
      "point": "O2",
      "text": "O2"
    },
    {
      "point": "T",
      "text": "T"
    },
    {
      "point": "C1",
      "text": "C1"
    },
    {
      "point": "C2",
      "text": "C2"
    },
    {
      "point": "alpha",
      "text": "α"
    }
  ],
  "vertex_names": [
    "O1",
    "O2",
    "T",
    "A",
    "B",
    "C",
    "D",
    "C1",
    "C2",
    "U0",
    "U1",
    "U2",
    "U3",
    "U4",
    "U5",
    "U6",
    "U7",
    "U8",
    "U9",
    "U10",
    "U11",
    "U12",
    "U13",
    "U14",
    "U15",
    "U16",
    "U17",
    "U18",
    "U19",
    "U20",
    "U21",
    "U22",
    "U23",
    "U24",
    "U25",
    "U26",
    "U27",
    "U28",
    "U29",
    "U30",
    "U31",
    "V0",
    "V1",
    "V2",
    "V3",
    "V4",
    "V5",
    "V6",
    "V7",
    "V8",
    "V9",
    "V10",
    "V11",
    "V12",
    "V13",
    "V14",
    "V15",
    "V16",
    "V17",
    "V18",
    "V19",
    "V20",
    "V21",
    "V22",
    "V23",
    "V24",
    "V25",
    "V26",
    "V27",
    "V28",
    "V29",
    "V30",
    "V31",
    "W0",
    "W1",
    "W2",
    "W3",
    "W4",
    "W5",
    "W6",
    "W7",
    "W8",
    "W9",
    "W10",
    "W11",
    "W12",
    "W13",
    "W14",
    "W15",
    "W16",
    "W17",
    "W18",
    "W19",
    "W20",
    "W21",
    "W22",
    "W23",
    "W24",
    "W25",
    "W26",
    "W27",
    "W28",
    "W29",
    "W30",
    "W31",
    "X0",
    "X1",
    "X2",
    "X3",
    "X4",
    "X5",
    "X6",
    "X7",
    "X8",
    "X9",
    "X10",
    "X11",
    "X12",
    "X13",
    "X14"
  ],
  "faces_sample": [],
  "edges_sample": [
    {
      "points": [
        "U0",
        "U1"
      ]
    },
    {
      "points": [
        "U1",
        "U2"
      ]
    },
    {
      "points": [
        "U2",
        "U3"
      ]
    },
    {
      "points": [
        "U3",
        "U4"
      ]
    },
    {
      "points": [
        "U4",
        "U5"
      ]
    },
    {
      "points": [
        "U5",
        "U6"
      ]
    },
    {
      "points": [
        "U6",
        "U7"
      ]
    },
    {
      "points": [
        "U7",
        "U8"
      ]
    },
    {
      "points": [
        "U8",
        "U9"
      ]
    },
    {
      "points": [
        "U9",
        "U10"
      ]
    },
    {
      "points": [
        "U10",
        "U11"
      ]
    },
    {
      "points": [
        "U11",
        "U12"
      ]
    },
    {
      "points": [
        "U12",
        "U13"
      ]
    },
    {
      "points": [
        "U13",
        "U14"
      ]
    },
    {
      "points": [
        "U14",
        "U15"
      ]
    },
    {
      "points": [
        "U15",
        "U16"
      ]
    },
    {
      "points": [
        "U16",
        "U17"
      ]
    },
    {
      "points": [
        "U17",
        "U18"
      ]
    },
    {
      "points": [
        "U18",
        "U19"
      ]
    },
    {
      "points": [
        "U19",
        "U20"
      ]
    },
    {
      "points": [
        "U20",
        "U21"
      ]
    },
    {
      "points": [
        "U21",
        "U22"
      ]
    },
    {
      "points": [
        "U22",
        "U23"
      ]
    },
    {
      "points": [
        "U23",
        "U24"
      ]
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "O1",
        "C1"
      ],
      "dash": true,
      "color": "#d9480f"
    },
    {
      "points": [
        "O2",
        "C2"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     R = rng.choice([1.4, 1.5, 1.6, 1.7, 1.8])
4:     r = rng.choice([0.6, 0.7, 0.8, 0.9, 1.0])
5:     H = rng.uniform(1.8, 2.2)

25:     rc1 = math.sqrt(max(0.0, R * R - (H - h) ** 2))
26:     rc2 = math.sqrt(max(0.0, r * r - (H - h) ** 2))
27:     edges = []
28:     n = 32
29: 
30:     # 水平截面圆(y = h)
31:     for cx, cy, cz, rad, prefix in [
32:         (C1[0], h, 0.0, rc1, "U"),
33:         (C2[0], h, 0.0, rc2, "V"),
34:     ]:
40:             names.append(nm)
41:         for i in range(n):
42:             edges.append(tk.edge(names[i], names[(i + 1) % n]))
43: 
44:     # 竖直辅助平面截两球的大圆(z = 0 平面,过 O1O2)
45:     for cx, cy, cz, rad, prefix in [
46:         (O1[0], H, 0.0, R, "W"),
47:         (O2[0], H, 0.0, r, "X"),
48:     ]:
54:             names.append(nm)
55:         for i in range(n):
56:             edges.append(tk.edge(names[i], names[(i + 1) % n]))
57: 
58:     edges += [
59:         tk.edge("A", "B"), tk.edge("B", "C"), tk.edge("C", "D"), tk.edge("D", "A"),
60:         tk.edge("A", "C"), tk.edge("B", "D"), tk.edge("O1", "O2", dash=False),
61:     ]
62:     aux_lines = [
63:         tk.aux("O1", "C1", dash=True),
64:         tk.aux("O2", "C2", dash=True),
65:     ]
66:     prims = [
67:         tk.prim_sphere(r=R, center=O1, color="#9ca3af"),
68:         tk.prim_sphere(r=r, center=O2, color="#6b7280"),
69:     ]
70:     vs["alpha"] = (0.0, h, span * 0.7)
71:     labels = ["O1", "O2", "T", "C1", "C2", "alpha"]
72:     camera = tk.auto_camera(
73:         vs, primitives=prims, azim=rng.uniform(22, 38),
74:         elev=rng.uniform(18, 32), pad=1.55
75:     )
76:     spec = tk.make_spec(
77:         "两球外切位置关系", "solid3d_spatial_tangent_external__S010",
78:         vs, edges=edges, aux_lines=aux_lines, primitives=prims,
79:         labels=[], camera=camera
80:     )
81:     spec["labels"] = [{"point": k, "text": k if k != "alpha" else "α"} for k in labels]
82: 
83:     q = "如图,图中给出了两个球体及其辅助截面、球心连线与切点。根据图示判断,这两个球体的空间位置关系属于下列哪种情况?"
84:     opts = ["外切", "外离", "相交", "内切"]
85:     rng.shuffle(opts)
86:     letters = "ABCD"
87:     options = {letters[i]: opts[i] for i in range(4)}
88:     ans = letters[opts.index("外切")]
89:     return tk.item(spec, q, options, ans)

K26 Spec-to-Image Review

  • major/mesh_clutter
    135条edges含4组32边形圆,全部实线叠加于球面,线网过密且未区分可见与隐藏弧
    spec_fix: 将大圆与截面圆按视角可见性拆分为实线弧与虚线弧,删除冗余网格
  • major/plan_coverage
    竖直大圆与水平截面圆被遮挡部分未按plan绘为虚线,丢失空间层次感
    spec_fix: 对W/X/U/V圆环计算相机方向可见角范围,隐藏段设dash=true
  • minor/label_overlap
    T标签与O1O2连心线及球轮廓重叠,C2贴近球底边缘
    spec_fix: 增加T、C2标签偏移量,避免与几何线重合
What To Change
  • 大圆与截面圆按可见性分段为实/虚弧
  • 简化或淡化平面α对角线
  • 调整T、C2标签位置
Preferred Primitives
  • sphere
  • section_circle
Remove/Simplify
  • 移除无遮挡判断的完整32边形实线圆
  • 平面α对角线改为虚线或移除
Label Fixes
  • T标签向右上偏移避线
  • C2标签向下偏移避球底
Camera Fixes
  • 当前视角可接受,略降elev使水平截面圆更舒展
Toolkit/Renderer: 建议toolkit增加球面截线primitive,支持基于相机自动遮挡虚实渲染
representation: 球体primitive使用正确,但截线用大密度edges硬搓且无虚实,不符合教材图示规范
readability: 线网过密,大圆与截面圆全部实线叠加球面,视觉混乱且空间层次不清
plan: 基本覆盖plan元素,但核心要求'被遮挡部分虚线'未实现,辅助平面表达不充分
next: spec_rewrite

solid3d_spatial_tangent_external__S015

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 5 major_spec_issue toolkit hold

Plan / Must Render

题型 体体外切
难度 L3
Scene
直立圆锥轴截面为等腰三角形,顶点朝上。圆锥内部有一内切球,与侧面及底面相切。圆锥右下方外侧有一球与圆锥侧面外切于一个纬圆(切圆部分可见于圆锥表面)。该球右侧紧邻一竖直三棱柱,其左侧面部分遮挡球体。需画出圆锥轴线、两球球心连线、外切切点处指向球心的半径(虚线),以及各几何体的隐藏边(虚线)。
Must Render
[
  "圆锥及其轴截面",
  "圆锥内部内切球",
  "圆锥外部与侧面外切的球及切圆",
  "右侧部分遮挡球体的竖直三棱柱",
  "两球球心与切点半径辅助线(含虚线)",
  "各几何体隐藏边虚线"
]

Spec Summary

{
  "title": "体体外切与体内切对比",
  "meta": "直立圆锥轴截面为等腰三角形;内切球球心H在轴线上;外侧球球心M满足x>0,位于圆锥右下方,半径MN垂直于右侧母线,并以y=yt处纬圆CT显式表示圆锥侧面的外切切圆;右侧竖直三棱柱A-F与外球保持外离,仅在视图中遮挡;轴线l、球心连线HM、切点半径MN、底圆/切圆/棱柱隐藏边均已显式绘制。",
  "camera": {
    "position": [
      6.692,
      4.227,
      8.162
    ],
    "target": [
      0.341,
      0.674,
      0.033
    ],
    "orthoSize": 3.006
  },
  "counts": {
    "vertices": 208,
    "faces": 5,
    "edges": 206,
    "aux_lines": 3,
    "aux_faces": 1,
    "primitives": 3,
    "labels": 6
  },
  "primitive_types": [
    "cone",
    "sphere",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cone",
      "radius": 1.1766632777287571,
      "height": 2.6531817611761213,
      "position": [
        0.0,
        1.3265908805880606,
        0.0
      ],
      "color": "#e5e7eb"
    },
    {
      "type": "sphere",
      "radius": 0.7653481749360816,
      "position": [
        0.0,
        0.7653481749360816,
        0.0
      ],
      "color": "#bfdbfe"
    },
    {
      "type": "sphere",
      "radius": 0.4274847193130511,
      "position": [
        1.2021248609849948,
        0.997036533082696,
        0.0
      ],
      "color": "#93c5fd"
    }
  ],
  "labels": [
    {
      "point": "P",
      "text": "P"
    },
    {
      "point": "O",
      "text": "O"
    },
    {
      "point": "H",
      "text": "H"
    },
    {
      "point": "M",
      "text": "M"
    },
    {
      "point": "N",
      "text": "N"
    },
    {
      "point": "l",
      "text": "l"
    }
  ],
  "vertex_names": [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "P",
    "O",
    "U",
    "V",
    "W",
    "X",
    "l",
    "H",
    "M",
    "N",
    "CB0",
    "CT0",
    "SI0",
    "SO0",
    "CB1",
    "CT1",
    "SI1",
    "SO1",
    "CB2",
    "CT2",
    "SI2",
    "SO2",
    "CB3",
    "CT3",
    "SI3",
    "SO3",
    "CB4",
    "CT4",
    "SI4",
    "SO4",
    "CB5",
    "CT5",
    "SI5",
    "SO5",
    "CB6",
    "CT6",
    "SI6",
    "SO6",
    "CB7",
    "CT7",
    "SI7",
    "SO7",
    "CB8",
    "CT8",
    "SI8",
    "SO8",
    "CB9",
    "CT9",
    "SI9",
    "SO9",
    "CB10",
    "CT10",
    "SI10",
    "SO10",
    "CB11",
    "CT11",
    "SI11",
    "SO11",
    "CB12",
    "CT12",
    "SI12",
    "SO12",
    "CB13",
    "CT13",
    "SI13",
    "SO13",
    "CB14",
    "CT14",
    "SI14",
    "SO14",
    "CB15",
    "CT15",
    "SI15",
    "SO15",
    "CB16",
    "CT16",
    "SI16",
    "SO16",
    "CB17",
    "CT17",
    "SI17",
    "SO17",
    "CB18",
    "CT18",
    "SI18",
    "SO18",
    "CB19",
    "CT19",
    "SI19",
    "SO19",
    "CB20",
    "CT20",
    "SI20",
    "SO20",
    "CB21",
    "CT21",
    "SI21",
    "SO21",
    "CB22",
    "CT22",
    "SI22",
    "SO22",
    "CB23",
    "CT23",
    "SI23",
    "SO23",
    "CB24",
    "CT24",
    "SI24",
    "SO24",
    "CB25",
    "CT25",
    "SI25",
    "SO25"
  ],
  "faces_sample": [
    {
      "points": [
        "C",
        "B",
        "A"
      ],
      "color": "#e2e8f0",
      "opacity": 0.56
    },
    {
      "points": [
        "D",
        "E",
        "F"
      ],
      "color": "#e2e8f0",
      "opacity": 0.56
    },
    {
      "points": [
        "A",
        "B",
        "E",
        "D"
      ],
      "color": "#e2e8f0",
      "opacity": 0.56
    },
    {
      "points": [
        "B",
        "C",
        "F",
        "E"
      ],
      "color": "#e2e8f0",
      "opacity": 0.56
    },
    {
      "points": [
        "C",
        "A",
        "D",
        "F"
      ],
      "color": "#e2e8f0",
      "opacity": 0.56
    }
  ],
  "edges_sample": [
    {
      "points": [
        "P",
        "U"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "P",
        "V"
      ],
      "color": "#222222"
    },
    {
      "points": [
        "U",
        "V"
      ],
      "dash": true,
      "color": "#555555"
    },
    {
      "points": [
        "P",
        "W"
      ],
      "color": "#333333"
    },
    {
      "points": [
        "P",
        "X"
      ],
      "dash": true,
      "color": "#666666"
    },
    {
      "points": [
        "CB0",
        "CB1"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB1",
        "CB2"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB2",
        "CB3"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB3",
        "CB4"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB4",
        "CB5"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB5",
        "CB6"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB6",
        "CB7"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB7",
        "CB8"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB8",
        "CB9"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB9",
        "CB10"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB10",
        "CB11"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB11",
        "CB12"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB12",
        "CB13"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB13",
        "CB14"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB14",
        "CB15"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB15",
        "CB16"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB16",
        "CB17"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB17",
        "CB18"
      ],
      "color": "#444444"
    },
    {
      "points": [
        "CB18",
        "CB19"
      ],
      "color": "#444444"
    }
  ],
  "aux_lines_sample": [
    {
      "points": [
        "l",
        "O"
      ],
      "dash": false,
      "color": "#111111"
    },
    {
      "points": [
        "H",
        "M"
      ],
      "dash": false,
      "color": "#555555"
    },
    {
      "points": [
        "M",
        "N"
      ],
      "dash": true,
      "color": "#d9480f"
    }
  ],
  "aux_faces_sample": [
    {
      "points": [
        "P",
        "U",
        "V"
      ],
      "color": "#fde68a",
      "opacity": 0.12
    }
  ]
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3: 
4:     # ---------------- 基本参数:直立圆锥 ----------------
5:     R = rng.uniform(1.05, 1.20)

7:     L = math.sqrt(R * R + Hc * Hc)
8: 
9:     # 圆锥内切球:轴截面等腰三角形的内切圆旋转而成
10:     rin = R * Hc / (R + L)
11:     Hpt = (0.0, rin, 0.0)  # 内切球球心,标 H
12: 
13:     # ---------------- 外侧球:放在圆锥右下方,与右侧母线相切 ----------------
14:     # 在轴截面内取右下侧切点 N,球心 M 沿圆锥侧面的外法线方向移出
15:     yt = rng.uniform(0.26 * Hc, 0.38 * Hc)
16:     xt = R * (Hc - yt) / Hc
17:     rout = rng.uniform(0.34 * R, 0.43 * R)
18: 
22:     Mpt = (xt + rout * nx, yt + rout * ny, 0.0)  # x>0,位于右下方
23: 
24:     # 切圆:用圆锥在 y=yt 处的纬圆明确标出,N 为右端切点
25:     rt = xt
26: 
27:     # ---------------- 右侧竖直三棱柱:与外球外离,但在视图中遮挡其右侧 ----------------
28:     Rp = rng.uniform(0.45, 0.58)
29:     Hp = rng.uniform(1.75, 2.20)
30:     vs_p, faces_raw = tk.prism(3, r=Rp, h=Hp)
31: 
32:     rename = {}
33:     prism_names = ["A", "B", "C", "D", "E", "F"]
34:     for old, new in zip(sorted(vs_p.keys()), prism_names):
36: 
37:     vs_p = {rename[k]: v for k, v in vs_p.items()}
38:     faces_p = [{"points": [rename[p] for p in f["points"]],
39:                 "color": "#e2e8f0", "opacity": 0.56} for f in faces_raw]
40: 
41:     # 使三棱柱位于外球右前方,投影上遮挡外球一部分;实际保持外离
42:     # tk.prism(3) 的最小 x 约为 -sqrt(3)/2*Rp,最小 z 约为 -Rp
43:     dx = Mpt[0] + 0.36 * rout + (math.sqrt(3) / 2.0) * Rp
44:     dy = max(0.0, Mpt[1] - 0.58 * Hp)
45:     dz = Mpt[2] + rout + rng.uniform(0.16, 0.24) + Rp
47: 
48:     # ---------------- 顶点 ----------------
49:     vertices = {}
50:     vertices.update(vs_p)
51: 
52:     # 圆锥关键点与轴截面
53:     vertices["P"] = (0.0, Hc, 0.0)       # 顶点
54:     vertices["O"] = (0.0, 0.0, 0.0)      # 底面圆心
55:     vertices["U"] = (-R, 0.0, 0.0)       # 轴截面左端
56:     vertices["V"] = (R, 0.0, 0.0)        # 轴截面右端
57:     vertices["W"] = (0.0, 0.0, R)        # 前母线底端
58:     vertices["X"] = (0.0, 0.0, -R)       # 后母线底端
59:     vertices["l"] = (0.0, Hc + 0.34, 0.0)
60: 
61:     # 球心与切点
62:     vertices["H"] = Hpt
63:     vertices["M"] = Mpt
64:     vertices["N"] = Npt
65: 
66:     # 圆锥底面圆、切圆、两球辅助圆
67:     nseg = 48
68:     for i in range(nseg):
69:         th = 2.0 * math.pi * i / nseg
70:         c, s = math.cos(th), math.sin(th)
71: 
72:         vertices[f"CB{i}"] = (R * c, 0.0, R * s)                  # cone base
73:         vertices[f"CT{i}"] = (rt * c, yt, rt * s)                 # tangent latitude circle
74: 
75:         vertices[f"SI{i}"] = (Hpt[0] + rin * c, Hpt[1], Hpt[2] + rin * s)
76:         vertices[f"SO{i}"] = (Mpt[0] + rout * c, Mpt[1], Mpt[2] + rout * s)
77: 
78:     # ---------------- 边与隐藏线 ----------------
79:     edges = []
80: 
81:     # 圆锥轴截面等腰三角形
82:     edges.append(tk.edge("P", "U", dash=False, color="#222222"))
83:     edges.append(tk.edge("P", "V", dash=False, color="#222222"))
84:     edges.append(tk.edge("U", "V", dash=True, color="#555555"))
85: 
86:     # 前后母线:后母线虚线,明确隐藏边
87:     edges.append(tk.edge("P", "W", dash=False, color="#333333"))
88:     edges.append(tk.edge("P", "X", dash=True, color="#666666"))
89: 
90:     # 圆锥底面圆:后半圆虚线
91:     for i in range(nseg):
92:         j = (i + 1) % nseg
93:         zmid = (vertices[f"CB{i}"][2] + vertices[f"CB{j}"][2]) / 2.0
94:         edges.append(tk.edge(f"CB{i}", f"CB{j}", dash=(zmid < 0), color="#444444"))
95: 
96:     # 外切纬圆:前半清楚可见,后半虚线
97:     for i in range(nseg):
98:         j = (i + 1) % nseg
99:         zmid = (vertices[f"CT{i}"][2] + vertices[f"CT{j}"][2]) / 2.0
100:         edges.append(tk.edge(f"CT{i}", f"CT{j}", dash=(zmid < 0), color="#111111"))
101: 
102:     # 内切球和外侧球的水平轮廓/隐藏圆
103:     for pref, col in (("SI", "#2563eb"), ("SO", "#1d4ed8")):
104:         for i in range(nseg):
105:             j = (i + 1) % nseg
106:             zmid = (vertices[f"{pref}{i}"][2] + vertices[f"{pref}{j}"][2]) / 2.0
107:             center_z = Hpt[2] if pref == "SI" else Mpt[2]
108:             edges.append(tk.edge(f"{pref}{i}", f"{pref}{j}", dash=(zmid < center_z), color=col))
109: 
110:     # 三棱柱全部棱,其中背向相机一侧作虚线,补足隐藏边
111:     prism_edges = set()
112:     for f in faces_p:
113:         pts = f["points"]
114:         for i in range(len(pts)):
115:             a, b = pts[i], pts[(i + 1) % len(pts)]
116:             prism_edges.add(tuple(sorted((a, b))))
117:     prism_cz = sum(vs_p[k][2] for k in vs_p) / len(vs_p)
118:     for a, b in sorted(prism_edges):
119:         zmid = (vs_p[a][2] + vs_p[b][2]) / 2.0
120:         edges.append(tk.edge(a, b, dash=(zmid < prism_cz), color="#475569"))
121: 
122:     # ---------------- 辅助线:轴线、两球球心连线、切点半径 ----------------
123:     aux_lines = [
124:         tk.aux("l", "O", dash=False, color="#111111"),       # 圆锥轴线 l
125:         tk.aux("H", "M", dash=False, color="#555555"),       # 两球球心连线
126:         tk.aux("M", "N", dash=True, color="#d9480f"),        # 外切点处指向球心的半径
127:     ]
128: 
129:     aux_faces = [
130:         tk.face(["P", "U", "V"], color="#fde68a", opacity=0.12)
131:     ]
132: 
133:     # ---------------- 几何体 primitives ----------------
134:     primitives = [
135:         tk.prim_cone(r=R, h=Hc, center=(0.0, 0.0, 0.0), color="#e5e7eb"),
136:         tk.prim_sphere(r=rin, center=Hpt, color="#bfdbfe"),
137:         tk.prim_sphere(r=rout, center=Mpt, color="#93c5fd"),
138:     ]
139: 
140:     camera = tk.auto_camera(vertices, primitives=primitives, azim=38.0, elev=19.0, pad=1.55)
141: 
142:     meta = (
143:         "直立圆锥轴截面为等腰三角形;内切球球心H在轴线上;"
144:         "外侧球球心M满足x>0,位于圆锥右下方,半径MN垂直于右侧母线,"
145:         "并以y=yt处纬圆CT显式表示圆锥侧面的外切切圆;"
146:         "右侧竖直三棱柱A-F与外球保持外离,仅在视图中遮挡;"
147:         "轴线l、球心连线HM、切点半径MN、底圆/切圆/棱柱隐藏边均已显式绘制。"
148:     )
149: 
150:     spec = tk.make_spec(
151:         "体体外切与体内切对比",
152:         meta,
153:         vertices,
154:         faces=faces_p,
155:         edges=edges,
156:         aux_lines=aux_lines,
157:         aux_faces=aux_faces,
158:         primitives=primitives,
159:         labels=["P", "O", "H", "M", "N", "l"],
160:         camera=camera,
161:     )
162: 
163:     options = {
164:         "A": "圆锥外侧的球与圆锥侧面是体体外切",
165:         "B": "圆锥与三棱柱是体体外切",
166:         "C": "圆锥内侧的球与圆锥侧面是体体外切",
167:         "D": "圆锥外侧的球与三棱柱是体体外切",
168:     }
169: 
170:     # 正确项由构造关系判定:M在右下外侧,MN等于外球半径且垂直圆锥侧面;
171:     # 内侧球是体内切,三棱柱与圆锥、外球均保持外离。
172:     line_distance = abs(Hc * Mpt[0] + R * Mpt[1] - R * Hc) / L
173:     tangent_external = abs(line_distance - rout) < 1e-8 and Mpt[0] > 0 and Mpt[1] < 0.55 * Hc
174:     truths = {
175:         "A": tangent_external,
182:     return tk.item(
183:         spec,
184:         "如图,组合体包含圆锥、其内切球、圆锥外侧一球及一旁的三棱柱。关于两体位置关系,下列说法正确的是?",
185:         options,
186:         answer,
187:     )

K26 Spec-to-Image Review

  • major/renderer_support
    圆锥侧面出现密集伞骨状经线,教材图仅需轴截面轮廓与前后母线
    spec_fix: toolkit 应支持 cone primitive 仅渲染轮廓线、前后母线及底圆,关闭默认密集经线
  • minor/label_overlap
    轴线标签 l 与顶点 P 上下紧邻,字形易被误认为 I 或 1
    spec_fix: 将 l 标签向左或向上偏移,增大与 P 的间距,或改用带衬线字体区分
  • minor/aux_line_clutter
    切点半径 MN 虚线在主视角中被外球遮挡,仅露出极短一段
    spec_fix: 微调 camera 使 MN 位于外球轮廓外侧,或加粗虚线并提高对比度
  • minor/other
    三棱柱隐藏边仅按 zmid 与平均 z 比较判断,非正交视角下可能不准
    spec_fix: 采用基于面法向与视角方向的背面剔除法精确标记隐藏棱
What To Change
  • 等待 toolkit 修复 cone primitive 线框渲染模式
  • 微调 camera 避开 MN 遮挡
  • 优化三棱柱隐藏边算法
Preferred Primitives
  • cone
  • sphere
  • prism
Remove/Simplify
  • cone 默认密集经线需移除
  • 保留底圆、轴截面、前后母线即可
Label Fixes
  • l 标签远离 P 避免重叠混淆
Camera Fixes
  • current 视角略调使 MN 可见;oblique 保留
Toolkit/Renderer: renderer 需为 cone primitive 增加仅画轮廓/必要母线的模式,避免密集经线网
representation: 使用了正确的 cone、sphere、prism primitive,但 cone 被 renderer 画成密集线框,失去教材图简洁性
readability: 双视角设计合理,oblique 视角有效展示切圆与球心连线;主视角因圆锥经线过密显得杂乱
plan: 轴截面、内切球、外切球、切圆、三棱柱遮挡、轴线、球心连线、切点半径均已覆盖
next: toolkit_patch

solid3d_spatial_tangent_external__S009

三维立体 › 空间位置关系 › 两体位置关系 › 体体外切
STAGE-1 FAIL score 6 major_spec_issue minor hold

Plan / Must Render

题型 体体外切
难度 L2
Scene
绘制直圆柱竖直放置,轴线为虚线l,轴心标记O′。圆柱右侧放置一球体,球心标记O,球位于圆柱外部并与圆柱沿一条水平圆外切,该公共切圆用红色高亮虚线同时画在圆柱右侧面与球体左侧面。辅助线:连接O与O′的水平实线段OO′(长度等于圆柱半径r与球半径R之和);过OO′的水平辅助平面与圆柱相交形成前后两条水平虚线母线。圆柱左后方母线、球体左后方轮廓均画虚线。标注半径R、r及切圆最前点P。
Must Render
[
  "圆柱右侧面与球体左侧面的公共切圆(高亮虚线)",
  "连接球心O与圆柱轴心O′的水平辅助线段OO′",
  "圆柱轴线l(虚线)与球心O、轴心O′的标注",
  "圆柱与球体的可见轮廓线及左后方隐藏虚线"
]

Spec Summary

{
  "title": "圆柱球外切",
  "meta": "圆柱球外切",
  "camera": {
    "position": [
      8.555,
      8.09,
      12.95
    ],
    "target": [
      0.744,
      1.86,
      -0.264
    ],
    "orthoSize": 4.552
  },
  "counts": {
    "vertices": 46,
    "faces": 0,
    "edges": 0,
    "aux_lines": 43,
    "aux_faces": 0,
    "primitives": 2,
    "labels": 6
  },
  "primitive_types": [
    "cylinder",
    "sphere"
  ],
  "primitives": [
    {
      "type": "cylinder",
      "radius": 1.4222109257625242,
      "height": 3.6308573712462673,
      "position": [
        0.0,
        1.8154286856231336,
        0.0
      ]
    },
    {
      "type": "sphere",
      "radius": 1.3789772014701511,
      "position": [
        2.8011881272326753,
        1.8154286856231336,
        0.0
      ]
    }
  ],
  "labels": [
    {
      "point": "{'point': \"O'\", 'text': \"O'\"}",
      "text": "{'point': \"O'\", 'text': \"O'\"}"
    },
    {
      "point": "{'point': 'O', 'text': 'O'}",
      "text": "{'point': 'O', 'text': 'O'}"
    },
    {
      "point": "{'point': 'P', 'text': 'P'}",
      "text": "{'point': 'P', 'text': 'P'}"
    },
    {
      "point": "{'point': 'l', 'text': 'l'}",
      "text": "{'point': 'l', 'text': 'l'}"
    },
    {
      "point": "{'point': 'T', 'text': 'r'}",
      "text": "{'point': 'T', 'text': 'r'}"
    },
    {
      "point": "{'point': 'R_end', 'text': 'R'}",
      "text": "{'point': 'R_end', 'text': 'R'}"
    }
  ],
  "vertex_names": [
    "O'",
    "O",
    "P",
    "l",
    "T",
    "R_end",
    "B",
    "c0",
    "c1",
    "c2",
    "c3",
    "c4",
    "c5",
    "c6",
    "c7",
    "c8",
    "c9",
    "c10",
    "c11",
    "c12",
    "c13",
    "c14",
    "c15",
    "c16",
    "c17",
    "c18",
    "c19",
    "c20",
    "c21",
    "c22",
    "c23",
    "cbl",
    "ctl",
    "s0",
    "s1",
    "s2",
    "s3",
    "s4",
    "s5",
    "s6",
    "s7",
    "s8",
    "s9",
    "s10",
    "s11",
    "s12"
  ],
  "faces_sample": [],
  "edges_sample": [],
  "aux_lines_sample": [
    {
      "points": [
        [
          0.0,
          -0.3,
          0.0
        ],
        "l"
      ],
      "dash": true,
      "color": "#4a4a4a"
    },
    {
      "points": [
        "O'",
        "O"
      ],
      "dash": false,
      "color": "#2b2b2b"
    },
    {
      "points": [
        "O'",
        "T"
      ],
      "dash": false,
      "color": "#2b2b2b"
    },
    {
      "points": [
        "O",
        "R_end"
      ],
      "dash": false,
      "color": "#2b2b2b"
    },
    {
      "points": [
        "O'",
        "P"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "O'",
        "B"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "c0",
        "c1"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c1",
        "c2"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c2",
        "c3"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c3",
        "c4"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c4",
        "c5"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c5",
        "c6"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c6",
        "c7"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c7",
        "c8"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c8",
        "c9"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c9",
        "c10"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c10",
        "c11"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c11",
        "c12"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c12",
        "c13"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c13",
        "c14"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c14",
        "c15"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c15",
        "c16"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c16",
        "c17"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c17",
        "c18"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c18",
        "c19"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c19",
        "c20"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c20",
        "c21"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c21",
        "c22"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c22",
        "c23"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "c23",
        "c0"
      ],
      "dash": true,
      "color": "#e60000"
    },
    {
      "points": [
        "cbl",
        "ctl"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s0",
        "s1"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s1",
        "s2"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s2",
        "s3"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s3",
        "s4"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s4",
        "s5"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s5",
        "s6"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s6",
        "s7"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s7",
        "s8"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s8",
        "s9"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s9",
        "s10"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s10",
        "s11"
      ],
      "dash": true,
      "color": "#888888"
    },
    {
      "points": [
        "s11",
        "s12"
      ],
      "dash": true,
      "color": "#888888"
    }
  ],
  "aux_faces_sample": []
}

Code Evidence

1: def generate(rng, tk):
2:     import math
3:     r = rng.uniform(1.0, 1.5)
4:     R = rng.uniform(1.0, 1.5)
5:     h = rng.uniform(3.0, 4.5)

6: 
7:     cyl = tk.prim_cylinder(r=r, h=h, center=(0.0, 0.0, 0.0))
8:     sph = tk.prim_sphere(r=R, center=(r + R, h / 2.0, 0.0))
9: 
10:     vs = {}
11:     vs["O'"] = (0.0, h / 2.0, 0.0)
12:     vs["O"] = (r + R, h / 2.0, 0.0)
17:     vs["B"] = (0.0, h / 2.0, -r)
18: 
19:     aux_lines = []
20:     aux_lines.append(tk.aux((0.0, -0.3, 0.0), "l", dash=True, color="#4a4a4a"))
21:     aux_lines.append(tk.aux("O'", "O", dash=False, color="#2b2b2b"))
22:     aux_lines.append(tk.aux("O'", "T", dash=False, color="#2b2b2b"))
23:     aux_lines.append(tk.aux("O", "R_end", dash=False, color="#2b2b2b"))
24:     aux_lines.append(tk.aux("O'", "P", dash=True, color="#888888"))
25:     aux_lines.append(tk.aux("O'", "B", dash=True, color="#888888"))
26: 
27:     n = 24
28:     circ = []
29:     for i in range(n):
33:         circ.append(nm)
34:     for i in range(n):
35:         aux_lines.append(tk.aux(circ[i], circ[(i + 1) % n], dash=True, color="#e60000"))
36: 
37:     ang = math.radians(225.0)
38:     vs["cbl"] = (r * math.cos(ang), 0.0, r * math.sin(ang))
39:     vs["ctl"] = (r * math.cos(ang), h, r * math.sin(ang))
40:     aux_lines.append(tk.aux("cbl", "ctl", dash=True, color="#888888"))
41: 
42:     m = 12
43:     arc = []
44:     for i in range(m + 1):
45:         a = math.pi + 0.5 * math.pi * i / m
46:         nm = f"s{i}"
47:         vs[nm] = (r + R + R * math.cos(a), h / 2.0, R * math.sin(a))
48:         arc.append(nm)
49:     for i in range(m):
50:         aux_lines.append(tk.aux(arc[i], arc[i + 1], dash=True, color="#888888"))
51: 
52:     labels = [{"point": "O'", "text": "O'"}, {"point": "O", "text": "O"},
53:               {"point": "P", "text": "P"}, {"point": "l", "text": "l"},
54:               {"point": "T", "text": "r"}, {"point": "R_end", "text": "R"}]
55: 
56:     camera = tk.auto_camera(vs, primitives=[cyl, sph],
57:                             azim=rng.uniform(28.0, 38.0),
58:                             elev=rng.uniform(18.0, 26.0), pad=1.5)
59:     spec = tk.make_spec("圆柱球外切", "圆柱球外切", vs, aux_lines=aux_lines,
60:                         primitives=[cyl, sph], labels=labels, camera=camera)
61: 
62:     correct = "两体外切,有且仅有一条公共曲线(一个圆)"
63:     texts = [
64:         correct,
65:         "两体外离,表面之间存在缝隙,没有任何公共点",
66:         "两体相交,公共部分是一个圆面或立体区域",
67:         "两体内切,球体整体在圆柱内部并与侧面接触"
68:     ]
69:     rng.shuffle(texts)
70:     opts = {L: t for L, t in zip("ABCD", texts)}
71:     ans = [k for k, v in opts.items() if v == correct][0]
72:     return tk.item(spec, "如图,观察图中球体与圆柱体的位置关系,下列判断正确的是( )", opts, ans)

K26 Spec-to-Image Review

  • major/other
    P点标签位于圆柱左侧轮廓,但plan要求其为切圆最前点,应在圆柱正面。
    spec_fix: 修正P点坐标为切圆最前点(0,h/2,r),并调整标签偏移避免贴线。
  • minor/plan_coverage
    plan要求过OO′水平面与圆柱相交的前后两条虚线母线,spec仅画一条左后方虚线cbl-ctl。
    spec_fix: 在y=h/2处补画圆柱前后两条竖直虚线母线(方向角90°与270°)。
  • minor/tangent_or_section
    红色公共切圆在球体区域被渲染在球面之上,未呈现贴附球体左侧面的隐藏线效果。
    spec_fix: 将红色切圆在球体左侧面部分设为隐藏虚线样式或降低透明度,增强交线立体感。
What To Change
  • 修正P点坐标至切圆最前点并调整标签位置
  • 补画圆柱前后两条竖直虚线母线
Preferred Primitives
  • cylinder
  • sphere
Remove/Simplify
  • 移除或修正错误的P点旧坐标
  • 若球体背面虚线弧不可见可考虑移除以减杂
Label Fixes
  • P点移至圆柱正面切圆最前点
  • r与R标签保持现状,微调避免贴线
Camera Fixes
  • 当前视角可接受,无需调整
Toolkit/Renderer: none
representation: 圆柱与球primitive使用正确,但关键标注点P坐标错误,且水平面交线母线缺失一条。
readability: 单视角能表达外切关系,红色切圆高亮可见,但P点位置会误导空间位置判断。
plan: 公共切圆、轴线、球心与轴心连线均已画出,前后辅助母线及最前点P未正确落实。
next: spec_rewrite