"""Build reproducible examples and the interactive visualization from Python results.""" from dataclasses import asdict import json import math from pathlib import Path from mcm26b import Measurement, solve ROOT = Path(__file__).resolve().parents[1] ASSETS = Path(__file__).resolve().parent / "assets" def measured(stations, target, errors): return [Measurement(x,y,(math.degrees(math.atan2(target[1]-y,target[0]-x))+e)%360) for (x,y),e in zip(stations,errors)] def scenarios(): target=(80,90) normal=measured([(-600,-250),(650,-100),(100,700)],target,[.65,-.55,.35]) h=20*math.sqrt(3) triangle=[Measurement(-1000,0,1),Measurement(540,-500*math.sqrt(3),121), Measurement(520,h+500*math.sqrt(3),241)] return [ dict(id='normal',name='三点交会',source='synthetic',target=target,measurements=normal), dict(id='triangle',name='直径圆反例',source='synthetic',target=(20,h/3),measurements=triangle), dict(id='poor',name='近共线检测点',source='synthetic',target=(900,50), measurements=measured([(0,0),(100,0),(200,0)],(900,50),[0,0,0])), dict(id='empty',name='不相容观测',source='synthetic',target=None, measurements=[Measurement(-100,0,180),Measurement(100,0,0)]), dict(id='segment',name='零误差退化线段',source='synthetic',target=(40,0), measurements=[Measurement(0,0,0,0),Measurement(100,0,180,0)]), ] def main(): cases=scenarios() practice=ROOT/'output/q1/practice' for path in sorted(practice.glob('*_observations.json')): doc=json.loads(path.read_text(encoding='utf-8')) cases.append(dict(id='practice-'+doc['case_code'],name='官方演练 '+doc['case_code'], source='official_practice',target=None, measurements=[Measurement(**m) for m in doc['measurements']])) examples=ROOT/'examples/q1' results=ROOT/'output/q1' examples.mkdir(parents=True,exist_ok=True) results.mkdir(parents=True,exist_ok=True) dataset=[] for case in cases: ms=case['measurements'] frames=[solve(ms[:n]) for n in range(1,len(ms)+1)] data={**case,'measurements':[asdict(m) for m in ms],'frames':frames} dataset.append(data) if case['source']=='synthetic': (examples/(case['id']+'.json')).write_text(json.dumps({k:v for k,v in data.items() if k!='frames'},ensure_ascii=False,indent=2),encoding='utf-8') (results/(case['id']+'_result.json')).write_text(json.dumps(frames[-1],ensure_ascii=False,indent=2,allow_nan=False),encoding='utf-8') (results/'demo_data.json').write_text(json.dumps(dataset,ensure_ascii=False,indent=2,allow_nan=False),encoding='utf-8') template=(ASSETS / 'q1.template.html').read_text(encoding='utf-8') fragment=template.replace('/*__Q1_DATA__*/',json.dumps(dataset,ensure_ascii=False,allow_nan=False)) (ROOT/'output'/'q1'/'question1_full.html').write_text(fragment,encoding='utf-8') # A standalone companion uses the same fragment and computation results. theme=(ASSETS / 'standalone.css').read_text(encoding='utf-8') page='