kind=space (fixed oblique 3D: points, vectors, planes, box) + ~190 figured questions for grade 12 (geometry, discrete, calculus)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 01:04:08 +03:30
parent 998b68e4bc
commit 733796c25a
4 changed files with 6434 additions and 2 deletions

View File

@@ -152,6 +152,14 @@ _SOLID = (
'مثلث، پنج‌ضلعی و شش‌ضلعی)'
)
_SPACE = (
'space (مختصات و بردار در فضای سه‌بعدی، تصویرِ مایل؛ برای R³، بردارِ سه‌مؤلفه‌ای، ضربِ داخلی و خارجی، خط و صفحه در فضا): '
'{"kind":"space","title":"…","axes":true,"points":{"A":[1,2,3],"B":[3,0,1]},"items":[…]}. محورها: x رو به بیننده، '
'y راست، z بالا. items: {"type":"point","point":"A","label":"A(1,2,3)"} · {"type":"vector","from":"O","to":"A","label":"a"} · '
'{"type":"segment","from":"A","to":"B","dashed":true} · {"type":"polygon","points":["A","B","C"],"label":"P"} (مثلث یا صفحه‌ی '
'متوازی‌الاضلاعی؛ نیمه‌شفاف) · {"type":"box","to":[3,2,4]} (مکعب‌مستطیلِ مختصاتی از مبدأ) · {"type":"drop","point":"A"} '
'(خط‌چین‌های تصویرِ نقطه روی صفحه‌های مختصات). مبدأ "O" خودکار است. رنگ فقط: blue red green orange purple teal.'
)
_W = lambda words: re.compile('|'.join(words)) # noqa: E731
_GEOMETRY_WORDS = (r'مثلث', r'دایره(?!‌ای)', r'زاویه', r'ضلع', r'چندضلعی', r'مربع', r'مستطیل', r'لوزی', r'ذوزنقه',
r'متوازی', r'شعاع', r'قطر', r'وتر', r'محیط', r'مساحت', r'کمان', r'محاطی', r'مماس', r'موازی',
@@ -175,6 +183,7 @@ SECTIONS = [
(_W((r'احتمال', r'سکه', r'تاس', r'درختی')), _TREE),
(_W((r'الگو', r'چوب\s*کبریت', r'دنباله', r'شکل[\u0650\s]*(?:بعدی|شماره|[0-9۰-۹])')), _PATTERN),
(_W((r'مکعب', r'منشور', r'هرم', r'استوانه', r'مخروط', r'کره', r'حجم', r'نیم‌کره')), _SOLID),
(_W((r'سه‌بعدی', r'R³', r'ضرب\s*(?:داخلی|خارجی)', r'مختصات[^.؟]{0,12}فضا', r'بردار[^.؟]{0,20}فضا')), _SPACE),
]
DIAGRAM_SPEC = '\n'.join([_HEAD] + [text for _, text in SECTIONS])
@@ -750,6 +759,68 @@ def _tree(node, depth=0) -> dict:
return out
_SPACE_COLORS = {'blue', 'red', 'green', 'orange', 'purple', 'teal'}
def _space(spec: dict, title: str) -> dict | None:
"""
فضای سه‌بعدی با تصویرِ مایل (ثابت، نه چرخان). مثلِ plane: آیتمِ خراب حذف می‌شود، شکلِ بی‌آیتم None است.
مختصات در بازه‌ی ±۱۰۰ و فقط عدد؛ هیچ متنِ آزادی جز برچسب‌های کوتاه نمی‌گذرد.
"""
points = {'O': [0.0, 0.0, 0.0]}
raw = spec.get('points') or {}
if not isinstance(raw, dict):
return None
for name, value in list(raw.items())[:MAX_POINTS]:
if not isinstance(name, str) or not _NAME_OK.match(name):
continue
try:
if not (isinstance(value, (list, tuple)) and len(value) == 3):
raise ValueError('triple')
points[name] = [_num(v, -100, 100) for v in value]
except ValueError:
continue
items = []
for it in (spec.get('items') or [])[:MAX_ITEMS + 8]:
if not isinstance(it, dict):
continue
try:
kind = it.get('type')
out = {'type': kind}
if it.get('color') in _SPACE_COLORS:
out['color'] = it['color']
if it.get('label'):
out['label'] = _label(it['label'], 24)
if kind in ('point', 'drop'):
if it.get('point') not in points:
continue
out['point'] = it['point']
elif kind in ('vector', 'segment'):
if it.get('from') not in points or it.get('to') not in points:
continue
out['from'], out['to'] = it['from'], it['to']
if it.get('dashed'):
out['dashed'] = True
elif kind == 'polygon':
names = it.get('points')
if not (isinstance(names, list) and 3 <= len(names) <= 6 and all(n in points for n in names)):
continue
out['points'] = list(names)
elif kind == 'box':
to = it.get('to')
if not (isinstance(to, (list, tuple)) and len(to) == 3):
continue
out['to'] = [_num(v, -100, 100) for v in to]
else:
continue
items.append(out)
except ValueError:
continue
if not items and len(points) < 2:
return None
return {'kind': 'space', 'title': title, 'axes': spec.get('axes') is not False, 'points': points, 'items': items[:MAX_ITEMS + 8]}
def validate(spec) -> dict | None:
"""
نسخه‌ی تمیزِ spec، یا None. فقط کلیدها و نوع‌های شناخته‌شده رد می‌شوند؛
@@ -767,6 +838,8 @@ def validate(spec) -> dict | None:
return _plane(spec, title)
if kind == 'numberline':
return _numberline(spec, title)
if kind == 'space':
return _space(spec, title)
if kind == 'fraction':
shape = spec.get('shape') if spec.get('shape') in ('circle', 'rect') else 'circle'
compare = []