139 lines
11 KiB
JavaScript
139 lines
11 KiB
JavaScript
/* Presentation-derived vector techniques; generic conceptual teaching scenes.
|
|
* One finite Anime.js 4.5 clock, no runtime permissions or external data. */
|
|
(() => {
|
|
'use strict';
|
|
window.industryRenderers ??= {};
|
|
const C={bg:'#e5f6ff',ink:'#142f46',blue:'#235a89',pale:'#d0e2ff',copper:'#95653e',green:'#006b5e',muted:'#46647a',white:'#ffffff'};
|
|
const clamp=(v,a,b,f)=>Number.isFinite(Number(v))?Math.max(a,Math.min(b,Number(v))):f;
|
|
function scene(svg,c,kit,title,phases){
|
|
const duration=clamp(c.duration,3000,18000,6000),u=duration/6;
|
|
const tl=kit.anime.createTimeline({autoplay:false,defaults:{ease:'linear'}});
|
|
const E=(tag,a={},t='',p=svg)=>kit.el(tag,a,t,p);
|
|
const R=(x,y,w,h,fill=C.pale,p=svg,a={})=>E('rect',{x,y,width:w,height:h,rx:6,fill,...a},'',p);
|
|
const T=(x,y,t,size=18,p=svg,a={})=>E('text',{x,y,fill:C.ink,style:`font:${size}px system-ui,sans-serif;fill:${C.ink}`,...a},t,p);
|
|
const P=(d,color=C.blue,p=svg,a={})=>E('path',{d,fill:'none',stroke:color,'stroke-width':2,'stroke-linecap':'round','stroke-linejoin':'round',...a},'',p);
|
|
const G=(a={},p=svg)=>E('g',a,'',p);
|
|
// Explicit holds prevent direction-dependent restoration in gaps between
|
|
// repeated SVG-attribute tweens (notably elbow and hand coordinates).
|
|
const tracks=new WeakMap();
|
|
const A=(e,props,at,len)=>{
|
|
const previous=tracks.get(e)||{};
|
|
for(const [key,value] of Object.entries(props)){
|
|
const p=previous[key];
|
|
if(p&&at>p.end+1e-8)tl.add(e,{[key]:[p.value,p.value],duration:(at-p.end)*u},p.end*u);
|
|
previous[key]={end:at+len,value:Array.isArray(value)?value[1]:value};
|
|
}
|
|
tracks.set(e,previous);
|
|
return tl.add(e,{...props,duration:len*u},at*u);
|
|
};
|
|
const draw=(d,at,len=1,color=C.copper,a={})=>{const e=P(d,color,svg,{'pathLength':1,'stroke-dasharray':1,'stroke-dashoffset':1,...a});A(e,{'stroke-dashoffset':[1,0]},at,len);return e;};
|
|
R(0,0,960,540,C.bg,svg,{rx:0});T(30,40,title,25);P('M30 60 H930',C.blue);
|
|
phases.forEach((s,i)=>{const x=30+i*900/phases.length,w=900/phases.length-8;R(x,468,w,40);T(x+8,494,s,15);const bar=R(x,465,w,3,C.copper);A(bar,{opacity:[.18,1]},i*5/phases.length,.4);});
|
|
T(30,526,'Conceptual illustration · human review remains separate from animation',15);
|
|
A(G({'data-clock':'finite'}),{opacity:[1,1]},0,6);
|
|
tl.recipeBeats=phases.map((label,i)=>({label,at:i*u})).concat({label:'Complete',at:duration});
|
|
return {E,R,T,P,G,A,draw,tl,u};
|
|
}
|
|
// Source silhouette: antenna, rounded silver head, recessed dark visor,
|
|
// copper ears, small torso and two jointed arms. Structural scale is isolated.
|
|
function robot(s,x,y,scale=1,expressive=false,c={}){
|
|
const {E,R,P,G,A}=s,g=G({transform:`translate(${x} ${y}) scale(${scale})`,'data-robot':'body'});
|
|
P('M0 -25 V-34','#dcecf5',g,{'stroke-width':2.5});E('circle',{cx:0,cy:-36,r:3,fill:C.copper},'',g);
|
|
R(-25,-24,50,36,'#b9cfdf',g,{rx:7,stroke:'#eff8ff','stroke-width':1.5});R(-20,-18,40,18,'#14232f',g,{rx:4});
|
|
for(const x of [-10,10]){const eye=E('ellipse',{cx:x,cy:-9,rx:3.6,ry:3.6,fill:'#9adfff','data-eye':x},'',g);if(expressive)for(const at of [.65,4.65]){A(eye,{ry:[3.6,.65]},at,.08);A(eye,{ry:[.65,3.6]},at+.12,.08);}}
|
|
const mouth=P('M-7 4 Q0 5 7 4','#304859',g,{'stroke-width':2.5,'data-smile':''});
|
|
if(expressive)A(mouth,{d:['M-7 4 Q0 5 7 4',`M-7 4 Q0 ${clamp(c.smileDepth,8,14,12)} 7 4`]},3.8,.7);
|
|
else mouth.setAttribute('d','M-7 4 Q0 12 7 4');
|
|
for(const x of [-30,26])R(x,-14,4,13,C.copper,g,{rx:1});P('M0 14 V17','#dcecf5',g,{'stroke-width':3});
|
|
P('M-16 29 L-16 24 Q-16 18 -10 18 L10 18 Q16 18 16 24 L16 29 Z','#c9e3f4',g,{fill:'#809eb3','stroke-width':1.2});E('circle',{cx:0,cy:23,r:2,fill:C.copper},'',g);
|
|
for(const side of [-1,1]){
|
|
const rest=`M${side*14} 22 L${side*23} 25 L${side*26} 14`,arm=P(rest,'#476c89',g,{'stroke-width':3.2,'data-arm':side});
|
|
const elbow=E('circle',{cx:side*23,cy:25,r:2.5,fill:'#b9cfdf',stroke:'#476c89','data-elbow':side},'',g);
|
|
const hand=E('circle',{cx:side*26,cy:14,r:3.4,fill:'#cce6f6',stroke:'#476c89','stroke-width':1.2,'data-hand':side},'',g);
|
|
if(expressive&&side===1){
|
|
const pose=h=>`M14 22 L35 5 L${h} -17`;
|
|
A(arm,{d:[rest,pose(23)]},1,.55);A(elbow,{cx:[23,35],cy:[25,5]},1,.55);A(hand,{cx:[26,23],cy:[14,-17]},1,.55);
|
|
let h=23;for(let i=0;i<6;i++){const next=i%2?23:25;A(arm,{d:[pose(h),pose(next)]},1.55+i*.2,.2);A(hand,{cx:[h,next]},1.55+i*.2,.2);h=next;}
|
|
A(arm,{d:[pose(h),rest]},2.75,.55);A(elbow,{cx:[35,23],cy:[5,25]},2.75,.55);A(hand,{cx:[h,26],cy:[-17,14]},2.75,.55);
|
|
}
|
|
}
|
|
return g;
|
|
}
|
|
const register=(id,fn)=>window.industryRenderers['presentation-patterns-'+id]=fn;
|
|
register('expressive-robot',(svg,c,kit)=>{
|
|
const s=scene(svg,c,kit,'EXPRESSIVE ROBOT / A FINITE PERFORMANCE',['Blink','Raise hand','Scratch','Lower hand','Smile']);
|
|
robot(s,480,270,clamp(c.scale,2.5,4,3.7),true,c);
|
|
s.T(48,140,'Acknowledge',21);s.T(48,171,'Brief visor blinks',17);
|
|
s.T(678,210,'Think',21);s.T(678,241,'Shoulder → elbow → hand',17);
|
|
s.T(678,345,'Respond',21);s.T(678,376,'A held, readable smile',17);
|
|
s.P('M270 165 H330 L385 230');s.P('M665 250 H630 L610 280');
|
|
s.T(48,427,'Replay starts one bounded gesture sequence. No perpetual idle loop.',18);
|
|
return s.tl;
|
|
});
|
|
register('approval-gate',(svg,c,kit)=>{
|
|
const s=scene(svg,c,kit,'APPROVAL GATE / WAIT BEFORE CROSSING',['Wait','Approval','Open barrier','Cross','Evidence return']);
|
|
const {E,R,T,P,G,A,draw}=s;
|
|
T(100,110,'PROPOSE',20);T(399,110,'HUMAN DECISION',20);T(760,110,'EXECUTE',20);
|
|
E('circle',{cx:480,cy:140,r:10,fill:C.blue});P('M459 172 Q459 151 480 151 Q501 151 501 172 Z',C.blue,svg,{fill:C.blue});
|
|
const wait=T(550,190,'Waiting for human decision',17),approved=T(550,190,'Approved · opening permitted',17);approved.setAttribute('opacity','0');
|
|
A(wait,{opacity:[1,0]},1,.01);A(approved,{opacity:[0,1]},1,.01);
|
|
R(70,250,820,80);P('M80 290 H880',C.blue,svg,{'stroke-opacity':.3});
|
|
R(467,218,26,20,'#a6c8ff');R(467,342,26,20,'#a6c8ff');
|
|
const top=R(467,238,26,52,'#a56eff',svg,{'data-leaf':'top',stroke:C.blue}),bottom=R(467,290,26,52,'#a56eff',svg,{'data-leaf':'bottom',stroke:C.blue});
|
|
// Leaves clear the entire lane before any item movement through the barrier.
|
|
A(top,{y:[238,184],fill:['#a56eff','#9fd8cb']},2,.7);A(bottom,{y:[290,344],fill:['#a56eff','#9fd8cb']},2,.7);
|
|
const item=G({'data-work-item':'proposal'});R(330,271,60,38,C.white,item,{stroke:C.copper,'stroke-width':2});P('M333 275 L360 292 L387 275',C.copper,item);
|
|
A(item,{translateX:[0,clamp(c.travelPx,380,450,420)]},3,.8);
|
|
T(100,377,'Wait here until fully open',17);T(718,377,'Authorized work',17);
|
|
const proof=draw('M805 405 H480 V422 H180',4,1,C.blue,{'data-evidence-route':''});
|
|
P('M190 415 L180 422 L190 429');T(320,447,'Evidence returns to the decision owner',17);
|
|
const packet=E('circle',{cx:805,cy:405,r:5,fill:C.copper,'data-evidence-packet':'',opacity:0});A(packet,{opacity:[0,1]},4,.01);A(packet,{cx:[805,480]},4,.45);A(packet,{cy:[405,422]},4.45,.1);A(packet,{cx:[480,180]},4.55,.45);
|
|
return s.tl;
|
|
});
|
|
register('delivery-cycle',(svg,c,kit)=>{
|
|
const s=scene(svg,c,kit,'DELIVERY CYCLE / SPLIT, JOIN, ACCEPT, LEARN',['Scope','Parallel design','Join delivery','Accept / release','Feedback']);
|
|
const {E,R,T,P,A,draw}=s;
|
|
const pts=[[90,250],[245,250],[430,150],[430,350],[625,250],[815,250],[815,410],[245,410]];
|
|
const labels=['Intent','Scope','Architecture','Experience','Delivery','Acceptance','Release','Learning'];
|
|
const edges=[[0,1],[1,2],[1,3],[2,4],[3,4],[4,5],[5,6],[6,7],[7,0]];
|
|
const times=[0,1,1,2,2,3,3.45,4,4.5];
|
|
edges.forEach(([a,b],i)=>{
|
|
const [x,y]=pts[a],[xx,yy]=pts[b],dx=xx-x,dy=yy-y,len=Math.hypot(dx,dy),r=23;
|
|
const d=`M${x+dx*r/len} ${y+dy*r/len} L${xx-dx*r/len} ${yy-dy*r/len}`;
|
|
P(d,C.blue,svg,{'stroke-opacity':.2});draw(d,times[i],i<5?.65:.45,i>6?C.blue:C.copper,{'data-edge':`${a}-${b}`});
|
|
const ex=xx-dx*r/len,ey=yy-dy*r/len;P(`M${ex-dx*8/len-dy*4/len} ${ey-dy*8/len+dx*4/len} L${ex} ${ey} L${ex-dx*8/len+dy*4/len} ${ey-dy*8/len-dx*4/len}`,C.blue);
|
|
});
|
|
pts.forEach(([x,y],i)=>{E('circle',{cx:x,cy:y,r:23,fill:C.bg,stroke:i===6?C.copper:C.blue,'stroke-width':3});T(x,y+(i===6||i===7?34:-36),labels[i],18,svg,{'text-anchor':'middle'});});
|
|
T(324,94,'Two design streams, one delivery join',18);
|
|
T(510,392,'Feedback informs the next cycle',17);
|
|
return s.tl;
|
|
});
|
|
register('connected-memory',(svg,c,kit)=>{
|
|
const s=scene(svg,c,kit,'CONNECTED MEMORY / LEARN, RETAIN, REVIEW, REUSE',['Learn','Retain','Review','Reuse','Source access']);
|
|
const {E,R,T,P,G,A,draw}=s;
|
|
T(160,98,'LEARNING',18,svg,{'text-anchor':'middle'});T(475,98,'RETAINED KNOWLEDGE',18,svg,{'text-anchor':'middle'});T(790,98,'REVIEWED INSTRUCTIONS',18,svg,{'text-anchor':'middle'});
|
|
robot(s,160,177,1.7);
|
|
// Open book and layered instruction sheets stay distinct, as in the source.
|
|
P('M415 140 L475 151 L535 140 V202 L475 213 L415 202 Z',C.blue,svg,{fill:C.white});P('M475 151 V213',C.copper);
|
|
for(let j=0;j<4;j++)P(`M424 ${156+j*11} L465 ${164+j*11} M485 ${164+j*11} L526 ${156+j*11}`);
|
|
for(let j=2;j>=0;j--)R(754+j*8,135-j*7,70,73,j?C.pale:C.white,svg,{stroke:C.blue,rx:1});T(765,166,'GUIDE',15);P('M764 179 H810 M764 190 H800',C.copper);
|
|
draw('M212 165 Q310 125 415 165',0,1,C.blue);T(265,126,'Capture lessons',16);
|
|
draw('M535 165 Q640 125 754 165',1,1);T(585,126,'Human review',16);
|
|
const review=R(750,218,100,23,'#b7dfcf');T(759,235,'Reviewed',14);A(review,{opacity:[.2,1]},2,.7);
|
|
draw('M840 173 H906 V274 H245 V205 H211',3,.9);P('M221 199 L211 205 L221 211',C.copper);T(340,263,'Reuse approved instructions on a later task',17);
|
|
T(80,324,'INDEPENDENT EXTERNAL SOURCES',18);T(80,350,'Project sources available to the agent',16);
|
|
const xs=[110,290,470,650,830],names=['Documents','Repositories','Services','Records','Research'];
|
|
P('M48 377 H830 M48 377 V177 H109',C.blue,svg,{'data-access-trunk':''});
|
|
xs.forEach((x,i)=>{
|
|
P(`M${x} 395 V377`,C.blue,svg,{'data-source-branch':i});
|
|
R(x-66,395,132,44,C.white,svg,{stroke:C.blue,'data-source':i});T(x,422,names[i],16,svg,{'text-anchor':'middle'});
|
|
const route=`M${x} 395 L${x} 377 L48 377 L48 177 L109 177`;
|
|
const track=P(route,C.blue,svg,{opacity:0,'data-source-route':i,'data-from':`source-${i}`,'data-to':'agent'});
|
|
const packet=E('rect',{x:-4,y:-3,width:8,height:6,fill:C.white,stroke:C.blue,'data-source-packet':i},'');
|
|
// Actual arc-length motion: each independent source reaches the same agent port.
|
|
s.tl.add(packet,{...kit.anime.svg.createMotionPath(track),duration:s.u*1.5,ease:'linear'},s.u*(4+i*.1));
|
|
});
|
|
return s.tl;
|
|
});
|
|
})();
|