"""Paired concept views: application entry points versus authorized object operations.""" from pathlib import Path import math import random import drawsvg as d from .architecture import INK, GREEN, BLUE, PURPLE, MUTED, LINE, FONT def build_purpose(output: Path) -> None: svg = d.Drawing(1800, 1290) svg.append_title('Endor 的意义:从访问程序到操作对象的对比示意') svg.append(d.Rectangle(0,0,1800,1290,fill='#fcfdfb')) rng = random.Random(20261028) def text(s,x,y,size=16,color=INK,weight='400',anchor='start'): svg.append(d.Text(s,size,x,y,fill=color,font_family=FONT,font_weight=weight,text_anchor=anchor)) def box(x,y,w,h,fill='white',stroke=LINE,r=12): svg.append(d.Rectangle(x,y,w,h,rx=r,fill=fill,stroke=stroke,stroke_width=1.3)) def wire(points,color=GREEN,arrow=False,dash=None,width=1.5): p=d.Path(fill='none',stroke=color,stroke_width=width,stroke_linejoin='round',stroke_dasharray=dash) p.M(*points[0]) for pt in points[1:]: p.L(*pt) svg.append(p) if arrow: x,y=points[-1]; px,py=points[-2]; a=math.atan2(y-py,x-px) for sign in [-1,1]: svg.append(d.Line(x-7*math.cos(a+sign*.48),y-7*math.sin(a+sign*.48),x,y,stroke=color,stroke_width=width)) def objects(x,y,w,h,count,color=GREEN,r=3.5): points=[] while len(points)r*3.7 for qx,qy in points): points.append((px,py)) for px,py in points: svg.append(d.Circle(px,py,r,fill='#eff6f1',stroke=color,stroke_width=1.2)) return points def os(x,y): for dy in [-21,-3,15]: svg.append(d.Rectangle(x-40,y+dy,80,13,rx=3,fill='white',stroke=MUTED,stroke_width=1.6)) svg.append(d.Circle(x-30,y+dy+6,1.5,fill=MUTED)) svg.append(d.Line(x-17,y+dy+6,x+25,y+dy+6,stroke=MUTED,stroke_width=1.2)) text('OS',x,y+65,24,INK,'600','middle') def person(x,y,ai=False): if ai: box(x-20,y-25,40,36,'#f5f1fa',PURPLE,8) for dx in [-8,8]: svg.append(d.Circle(x+dx,y-8,2,fill=PURPLE)) wire([(x-8,y+4),(x+8,y+4)],PURPLE) wire([(x,y-25),(x,y-35)],PURPLE) svg.append(d.Circle(x,y-38,3,fill='white',stroke=PURPLE)) else: svg.append(d.Circle(x,y-20,13,fill='#f1f6f3',stroke=GREEN,stroke_width=1.8)) svg.append(d.Path(fill='none',stroke=GREEN,stroke_width=1.8).M(x-25,y+23).Q(x-25,y-1,x,y-1).Q(x+25,y-1,x+25,y+23).Z()) text('AI' if ai else '用户',x,y+57,18,PURPLE if ai else INK,'600','middle') text('ENDOR / WHY OBJECTS',40,43,14,GREEN,'600') text('从程序入口,到对象的操作权',40,90,31,INK,'600') text('一对概念图:改变用户与 AI 接触系统的基本单位。',40,123,16,MUTED) # Panel one: unequal groups of processes map to application boundaries. text('01',40,181,20,BLUE,'600') text('以程序为入口',95,181,24,INK,'600') text('OS',135,225,13,MUTED,'600','middle') text('进程区 · 圆圈为进程中的对象',585,225,14,MUTED,'500','middle') text('程序',1100,225,14,MUTED,'600','middle') text('用户入口',1615,225,14,MUTED,'600','middle') os(135,414) wire([(178,414),(250,414),(250,286),(300,286)],BLUE) wire([(250,414),(250,545),(300,545)],BLUE) wire([(250,414),(300,414)],BLUE) for row,(count,cy) in enumerate([(2,286),(4,414),(3,545)]): box(300,cy-49,570,98,'#f7f9fb') text(f'{count} 个进程',315,cy-27,11,MUTED) pw=112 start=585-(count*pw+(count-1)*16)/2 for i in range(count): px=start+i*(pw+16) box(px,cy-15,pw,53,'white','#c6d8e7',7) objects(px,cy-14,pw,50,6+rng.randrange(4),BLUE,r=3) wire([(870,cy),(999,cy)],BLUE,arrow=True) box(1000,cy-33,200,66,'white','#bdcfdc') wire([(1013,cy-15),(1187,cy-15)],BLUE,width=1) text(f'程序 {chr(65+row)}',1100,cy+14,20,INK,'600','middle') wire([(1568,414),(1400,414),(1400,cy),(1202,cy)],BLUE,arrow=True) person(1615,414) text('界面 / Web / API',1394,611,15,BLUE,anchor='middle') text('用户通过程序提供的入口访问功能;对象的组织与操作通常藏在程序内部。',300,640,16,MUTED) # Panel two: OS, a small process pool and Endorphin belong to Endor OS. text('02',40,709,20,GREEN,'600') text('以对象为操作单位',95,709,24,INK,'600') box(40,742,1410,421,'#f8fbf8','#a9cbbd',18) box(64,758,162,36,'#eaf3ec','none',5) text('Endor OS',145,783,21,GREEN,'600','middle') text('OS',135,837,13,MUTED,'600','middle') text('少量进程',415,837,14,MUTED,'600','middle') os(135,961) wire([(178,961),(270,961),(270,886),(335,886)],GREEN) wire([(270,961),(335,961)],GREEN) wire([(270,961),(270,1036),(335,1036)],GREEN) for cy in [886,961,1036]: box(335,cy-22,160,44,'white','#bdd5c8',7) text('进程',415,cy+6,15,MUTED,anchor='middle') wire([(495,cy),(555,cy),(555,961),(615,961)],GREEN,arrow=True,width=1.2) box(615,807,805,322,'white','#cbded1',14) text('Endorphin',644,845,25,INK,'600') text('对象运行与操作空间',644,874,14,MUTED) # Seeded randomness gives an organic network and reproducible repository assets. nodes=[] for i in range(43): while True: px,py=rng.uniform(845,1380),rng.uniform(843,1090) if all(math.hypot(px-qx,py-qy)>33 for qx,qy in nodes): break nodes.append((px,py)) edges=set() for i,(px,py) in enumerate(nodes): nearest=sorted((math.hypot(px-qx,py-qy),j) for j,(qx,qy) in enumerate(nodes) if j!=i) for _,j in nearest[:2 if i%3 else 3]: edges.add(tuple(sorted((i,j)))) for i,j in sorted(edges): wire([nodes[i],nodes[j]],'#c8dcd2',width=1) selected=sorted(range(len(nodes)),key=lambda i:nodes[i][0],reverse=True)[:7] for i,(px,py) in enumerate(nodes): svg.append(d.Circle(px,py,7 if i in selected else 5, fill='#e5f1e8' if i in selected else 'white', stroke=GREEN,stroke_width=1.8 if i in selected else 1.2)) text('对象',647,1000,17,GREEN,'600') text('可引用 · 可组合',647,1028,13,MUTED) text('按权限操作',647,1053,13,MUTED) person(1630,888) person(1630,1055,ai=True) # Users/AI reach selected objects with explicit cubic Bezier curves. for k,i in enumerate(selected): px,py=nodes[i] sy=888 if k<4 else 1055 color=GREEN if k<4 else PURPLE control_y=790+k*45 svg.append(d.Path(fill='none',stroke=color,stroke_width=1.7,opacity=.85) .M(1597,sy).C(1505,control_y,px+90,py-35,px,py)) svg.append(d.Circle(px,py,10,fill='none',stroke=color,stroke_width=1,opacity=.6)) text('选择对象 · 发起操作',1620,1196,16,GREEN,anchor='middle') text('进程数量仅作概念对比,不代表性能测量或固定部署要求。',40,1220,13,MUTED) text('圆圈 = 对象 细线 = 对象关系 贝塞尔曲线 = 用户 / AI 对选定对象的操作',40,1253,14,MUTED) text('讨论草案',1730,1253,13,MUTED,anchor='end') svg.save_svg(str(output))