aardio 文档
aardio 范例: 种出一颗星球 · A Planet from a Seed
import win.ui;
import gdip.path;
import gdip.pathGradientBrush;
import gdip.font;
/*DSG{{*/
var winform = win.form(text="种出一颗星球 · A Planet from a Seed";right=979;bottom=649;bgcolor=0x100B07)
winform.add(
canvas={cls="canvas";left=0;top=0;right=980;bottom=650;bgcolor=0x100B07;anchors={left=1;top=1;right=1;bottom=1};z=1}
)
/*}}*/
/*
《种出一颗星球》— 纯 aardio canvas / GDI+ 绘制,无外部素材。
一个周期 15 秒:种子呼吸 → 花瓣展开 → 微型宇宙运转 → 折叠归位。
空格暂停/继续,R 重新开始,Esc 关闭窗口。
*/
var cycleMs = 15000;
var tau = math.pi * 2;
var elapsedMs = 0;
var paused = false;
var smooth = function(a,b,x){
var q = math.clamp((x-a)/(b-a),0,1);
return q*q*(3-2*q);
};
var withAlpha = function(rgb,alpha){
return math.floor(math.clamp(alpha,0,255))*0x1000000 + (rgb & 0xFFFFFF);
};
// 状态只由周期内的时间决定;首帧与末帧是同一个状态。
var sceneState = function(ms){
var t = (ms % cycleMs) / 1000;
var open = smooth(1.15,4.30,t) * (1-smooth(10.35,13.55,t));
var planet = smooth(1.95,4.45,t) * (1-smooth(10.05,12.90,t));
var camera = 1 + 0.20*smooth(3.40,8.50,t)*(1-smooth(10.30,13.85,t));
var delays = [0,0.20,0.10,0.30];
var petals = [];
for(i=1,4){
var d = delays[i];
petals[i] = smooth(1.18+d,4.03+d,t) * (1-smooth(10.35+0.30-d,13.20+0.30-d,t));
}
return {
t=t; open=open; planet=planet; camera=camera; petals=petals;
bob=3.2*math.sin(tau*t/3);
breath=1+0.018*math.sin(tau*t/3)*(1-open);
shimmer=0.5+0.5*math.sin(tau*t/3);
orbit1=tau*t/5-1.0;
orbit2=tau*t/7.5+1.5;
gear=t*72;
};
};
// 固定形状和绘图资源只创建一次,不在每帧创建和销毁。
var shellPath = gdip.path();
// 相邻壳片共用同一条曲线接缝;四片旋转后仍能拼成完整球面。
shellPath.addBezier(0,0,-11,-4,-33,-24,-34,-34);
shellPath.addBezier(-34,-34,-19,-53,19,-53,34,-34);
shellPath.addBezier(34,-34,24,-33,4,-11,0,0);
shellPath.closeFigure();
var seedPath = gdip.path(); seedPath.addCircle(0,0,48.5);
var seedBrush = gdip.pathGradientBrush(seedPath);
seedBrush.setCenterPoint(-14,-18);
seedBrush.setCenterColor(0xFFB19B68);
seedBrush.setSurroundColors([0xFF29251F]);
// 闭合时给所有壳片一套共同的高光和暗面,让它们读作同一颗球。
var seedLight = gdip.pathGradientBrush(seedPath);
seedLight.setCenterPoint(-21,-25);
seedLight.setCenterColor(0x60FFF0B8);
seedLight.setSurroundColors([0x00FFF0B8]);
var seedShadow = gdip.pathGradientBrush(seedPath);
seedShadow.setCenterPoint(23,25);
seedShadow.setCenterColor(0x78040A0F);
seedShadow.setSurroundColors([0x00040A0F]);
// 同一球面光源反变换到每片壳的局部坐标;闭合时高光连续,展开后随壳片翻转。
var shellMaterials=[];
for(i=1,4){
var a=(i-1)*math.pi/2;
var lx=-18*math.cos(a)-21*math.sin(a);
var ly=18*math.sin(a)-21*math.cos(a);
var material=gdip.pathGradientBrush(seedPath);
material.setCenterPoint(lx,ly);
material.setCenterColor(0xFFD0B77F);
material.setSurroundColors([0xFF302A22]);
material.setFocusScales(0.05,0.05);
shellMaterials[i]=material;
}
var planetPath = gdip.path(); planetPath.addCircle(0,0,61);
var planetBrush = gdip.pathGradientBrush(planetPath);
planetBrush.setCenterPoint(-20,-25);
planetBrush.setCenterColor(0xFF52929A);
planetBrush.setSurroundColors([0xFF091D2B]);
planetBrush.setFocusScales(0.08,0.08);
var land1 = gdip.path();
land1.addClosedCurve2(0.23,[-49,-19,-39,-34,-27,-43,-15,-40,-8,-31,2,-34,10,-23,5,-14,-8,-13,-13,-4,-25,-7,-30,1,-42,-5]);
var land2 = gdip.path();
land2.addClosedCurve2(0.22,[16,2,28,5,39,13,34,24,27,29,24,42,14,38,8,27,4,18,9,12]);
var land3 = gdip.path();
land3.addClosedCurve2(0.24,[33,-39,44,-34,50,-24,44,-17,34,-19,29,-28]);
// 12 齿齿轮;大小和转角由其所属的铰链驱动。
var gearPath = gdip.path();
var gearPoints = [];
for(i=0,11){
for(j=0,3){
var a = tau*(i+j/4)/12;
var radius = 16;
if(j==1 || j==2) radius = 20;
table.push(gearPoints,radius*math.cos(a),radius*math.sin(a));
}
}
gearPath.addPolygon(gearPoints);
// 齿数 12:7,齿距相同,啮合时角速度按齿数反比。
var gear7Path = gdip.path();
var gear7Points = [];
for(i=0,6){
for(j=0,3){
var a = tau*(i+j/4)/7;
var radius = 16;
if(j==1 || j==2) radius = 20;
table.push(gear7Points,radius*math.cos(a),radius*math.sin(a));
}
}
gear7Path.addPolygon(gear7Points);
var darkBrush = gdip.solidBrush(0xFF1B2022);
var gearBrush = gdip.solidBrush(0xFF796641);
var goldBrush = gdip.solidBrush(0xFFBA9E68);
var highlightBrush = gdip.solidBrush(0xFFE4C98D);
var blueBrush = gdip.solidBrush(0xFF70D6E1);
var landBrush = gdip.solidBrush(0xFF85866A);
var landShade = gdip.solidBrush(0xFF687764);
var inkBrush = gdip.solidBrush(0xFF0B1820);
var mainPen = gdip.pen(0xFFB89C67,1);
var thinPen = gdip.pen(0xFFB89C67,0.8);
var heavyPen = gdip.pen(0xFF3D352A,3);
var bluePen = gdip.pen(0xFF6AC9D8,1);
var titleFont = gdip.font("Microsoft YaHei UI",20,0);
var smallFont = gdip.font("Segoe UI",9,0);
var labelFont = gdip.font("Segoe UI",10,0);
// 一张小型透明光晕精灵:缩放使用,不重复构造渐变。
var glowBitmap = gdip.bitmap(240,240,0xE200B);
var glowGraphics = gdip.graphics(glowBitmap);
glowGraphics.clear(0x00000000);
var glowPath = gdip.path(); glowPath.addCircle(120,120,118);
var glowBrush = gdip.pathGradientBrush(glowPath);
glowBrush.setCenterColor(0x5648BFCB);
glowBrush.setSurroundColors([0x0048BFCB]);
glowGraphics.fillPath(glowBrush,glowPath);
glowBrush.delete(); glowPath.delete(); glowGraphics.delete();
var backgroundBitmap;
var backgroundW,backgroundH,backgroundDpiX,backgroundDpiY = 0,0,0,0;
// 背景只在客户区大小或 DPI 改变时重建。
var makeBackground = function(w,h,dpiX,dpiY){
var bmp = gdip.bitmap(w,h,0xE200B);
var g = gdip.graphics(bmp);
g.clear(0xFF070C13);
g.smoothingMode = 4;
var state = g.save();
g.scale(dpiX,dpiY);
var lw,lh = w/dpiX,h/dpiY;
var backPath = gdip.path();
backPath.addEllipse(-lw*0.23,-lh*0.33,lw*1.46,lh*1.65);
var backGradient = gdip.pathGradientBrush(backPath);
backGradient.setCenterPoint(lw*0.50,lh*0.47);
backGradient.setCenterColor(0xFF172934);
backGradient.setSurroundColors([0xFF060A10]);
g.fillPath(backGradient,backPath);
backGradient.delete(); backPath.delete();
// 稀疏的固定星点和边角刻线,不参与逐帧动画。
g.compositingQuality = 2;
var pen = gdip.pen(0x39475B61,0.7);
var dot = gdip.solidBrush(0x546D898A);
if(lw>220 && lh>180){
for(i=1,46){
var x = 30 + ((i*181+31) % (lw-60));
var y = 44 + ((i*127+29) % (lh-88));
var dx = (x-lw*0.5)/(lw*0.5);
var dy = (y-lh*0.48)/(lh*0.5);
if(dx*dx+dy*dy>0.24){
var rr = 0.55;
if(i%9==0) rr=1.0;
g.fillCircle(dot,x,y,rr);
if(i%15==0){
g.drawLine(pen,x-3.0,y,x+3.0,y);
g.drawLine(pen,x,y-3.0,x,y+3.0);
}
}
}
}
pen.color = 0x38667776;
g.drawLine(pen,24,24,45,24); g.drawLine(pen,24,24,24,45);
g.drawLine(pen,lw-24,24,lw-45,24); g.drawLine(pen,lw-24,24,lw-24,45);
g.drawLine(pen,24,lh-24,45,lh-24); g.drawLine(pen,24,lh-24,24,lh-45);
g.drawLine(pen,lw-24,lh-24,lw-45,lh-24); g.drawLine(pen,lw-24,lh-24,lw-24,lh-45);
if(lw>500 && lh>360){
dot.color = 0xFFBBAA83;
g.drawString("种出一颗星球",titleFont,39,34,dot);
dot.color = 0xFF718181;
g.drawString("A PLANET FROM A SEED",smallFont,40,66,dot);
pen.color = 0x557D8C87;
g.drawLine(pen,40,88,118,88);
dot.color = 0xFF71817D;
g.drawString("MECHANICAL BOTANICA / 01",smallFont,39,lh-51,dot);
g.drawString("15 SEC · LOOP",labelFont,lw-153,lh-51,dot);
}
pen.delete(); dot.delete();
g.restore(state); g.delete();
return bmp;
};
// 圆环在种子内部缩至零;四个铰链始终落在各自的径向上。
var drawFrameRing = function(g,s){
var e = s.open;
var r = 132*e;
if(r<3) return;
var opacity = smooth(0.06,0.45,e);
heavyPen.width=5.8; heavyPen.color=withAlpha(0x4B3D2A,220*opacity);
mainPen.width=2.0; mainPen.color=withAlpha(0xC7A86B,215*opacity);
thinPen.width=0.75; thinPen.color=withAlpha(0xE5CA8F,180*opacity);
for(i=0,3){
g.drawArc(heavyPen,-r,-r,2*r,2*r,i*90+7,76);
g.drawArc(mainPen,-r,-r,2*r,2*r,i*90+7,76);
if(r>11) g.drawArc(thinPen,-r+5,-r+5,2*(r-5),2*(r-5),i*90+14,62);
}
if(r>20){
for(i=0,35){
var a=tau*i/36;
var inner=r-3.5;
if(i%3==0) inner=r-7.0;
g.drawLine(thinPen,inner*math.cos(a),inner*math.sin(a),r*math.cos(a),r*math.sin(a));
}
}
};
var drawArms = function(g,s){
for(i=1,4){
var r = 132*s.petals[i];
if(r>42){
var a=(i-1)*math.pi/2;
var ux,uy=math.sin(a),-math.cos(a);
heavyPen.width=6; heavyPen.color=0xFF3C3429;
mainPen.width=2.4; mainPen.color=0xFF90774A;
thinPen.width=0.75; thinPen.color=0xFFDDC082;
g.drawLine(heavyPen,ux*38,uy*38,ux*r,uy*r);
g.drawLine(mainPen,ux*38,uy*38,ux*r,uy*r);
g.drawLine(thinPen,ux*45-uy*1.4,uy*45+ux*1.4,ux*(r-2)-uy*1.4,uy*(r-2)+ux*1.4);
g.fillCircle(darkBrush,ux*r,uy*r,5.3);
g.fillCircle(goldBrush,ux*r,uy*r,3.8);
g.fillCircle(inkBrush,ux*r,uy*r,1.3);
}
}
};
var drawGear = function(g,x,y,scale,angle,shape){
if(scale<0.01) return;
shape = shape : gearPath;
var st = g.save();
g.translate(x,y); g.rotate(angle); g.scale(scale,scale);
g.fillPath(darkBrush,shape);
var shadow = g.save(); g.translate(0,-1.5);
g.fillPath(gearBrush,shape);
mainPen.color=0xFFBDA36B; mainPen.width=1.0;
g.drawPath(mainPen,shape);
g.fillCircle(inkBrush,0,0,9.3);
thinPen.color=0xFFC2A56B; thinPen.width=0.9;
g.drawCircle(thinPen,0,0,9.3);
for(k=0,3){
var a=tau*k/4;
g.drawLine(thinPen,3*math.cos(a),3*math.sin(a),8*math.cos(a),8*math.sin(a));
}
g.fillCircle(goldBrush,0,0,2.6);
g.restore(shadow); g.restore(st);
};
var drawGears = function(g,s){
var size = smooth(0.18,0.80,s.open);
if(size<0.01) return;
var x = 42+90*s.open;
var y = 44*s.open;
heavyPen.color=0xFF3D3529; heavyPen.width=5;
mainPen.color=0xFF8B764D; mainPen.width=1.6;
var leftX,leftY=-x+10*size,y+10*size;
g.drawLine(heavyPen,leftX,leftY,leftX+18,leftY-20); g.drawLine(heavyPen,x,y,x-18,y-20);
g.drawLine(mainPen,leftX,leftY,leftX+18,leftY-20); g.drawLine(mainPen,x,y,x-18,y-20);
drawGear(g,leftX,leftY,size,s.gear);
drawGear(g,x,y,size,-s.gear);
// 右侧的小齿轮与大齿轮反向联动。
drawGear(g,x+29*size,y+2*size,size*(7/12),s.gear*(12/7)+6.4,gear7Path);
};
// 外壳是同一片路径:沿铰链伸出、旋转并压扁,最终成为轨道的金属护片。
var drawPetal = function(g,s,index){
var e = s.petals[index];
// 闭合时半透明壳面与底下的球体共享曲面光影;张开后显露完整金属厚度。
var panelOpacity=0.25+0.75*smooth(0,0.30,e);
var st = g.save();
g.rotate((index-1)*90);
g.translate(0,-132*e);
var direction=1;
if(index%2==0) direction=-1;
g.rotate(direction*e*39);
g.scale(1+0.25*e,1-0.30*e);
var sh = g.save(); g.translate(1.4,2.5);
darkBrush.color=withAlpha(0x1B2022,255*smooth(0.02,0.40,e));
g.fillPath(darkBrush,shellPath); g.restore(sh);
shellMaterials[index].setCenterColor(withAlpha(0xD0B77F,255*panelOpacity));
shellMaterials[index].setSurroundColors([withAlpha(0x302A22,255*panelOpacity)]);
g.fillPath(shellMaterials[index],shellPath);
heavyPen.width=0.75+1.05*e; heavyPen.color=withAlpha(0x302A22,(125+130*e)*panelOpacity);
g.drawPath(heavyPen,shellPath);
thinPen.width=0.8; thinPen.color=withAlpha(0xD7BC83,(65+190*smooth(0,0.4,e))*panelOpacity);
g.drawBezier(thinPen,-29,-33,-15,-46,15,-46,29,-33);
thinPen.color=withAlpha(0x6B5B3C,(100+145*e)*panelOpacity);
g.drawBezier(thinPen,-21,-29,-13,-25,-8,-15,-5,-8);
g.drawBezier(thinPen,21,-29,13,-25,8,-15,5,-8);
darkBrush.color=withAlpha(0x1B2022,255*panelOpacity);
highlightBrush.color=withAlpha(0xE4C98D,255*panelOpacity);
goldBrush.color=withAlpha(0xBA9E68,255*panelOpacity);
inkBrush.color=withAlpha(0x0B1820,255*panelOpacity);
// 靠球缘的铆钉被透视压扁,长轴沿球面切向。
g.fillEllipse(darkBrush,-21.3,-35.3,4.6,2.7); g.fillEllipse(darkBrush,16.7,-35.3,4.6,2.7);
g.fillCircle(highlightBrush,-19,-34,0.8); g.fillCircle(highlightBrush,19,-34,0.8);
g.fillCircle(darkBrush,0,-37,3.7); g.fillCircle(goldBrush,0,-37,2.3);
g.fillCircle(inkBrush,0,-37,0.85);
darkBrush.color=0xFF1B2022; highlightBrush.color=0xFFE4C98D;
goldBrush.color=0xFFBA9E68; inkBrush.color=0xFF0B1820;
g.restore(st);
};
// 轨道以椭圆投影;sin(theta) 决定其在星球前面还是后面。
var orbitPoint = function(rx,ry,tilt,angle){
var u,v=rx*math.cos(angle),ry*math.sin(angle);
var c,ss=math.cos(tilt),math.sin(tilt);
return u*c-v*ss,u*ss+v*c,math.sin(angle);
};
var drawOrbit = function(g,s,which,front){
var visibility = smooth(0.12,0.62,s.open);
if(visibility<=0) return;
var rx,ry,tilt,color;
if(which==1){
rx=65+101*s.open; ry=19+37*s.open; tilt=-19; color=0xC8A96D;
}
else {
rx=58+80*s.open; ry=31+40*s.open; tilt=51; color=0x65A7AE;
}
var st=g.save(); g.rotate(tilt);
thinPen.width=0.9; thinPen.color=withAlpha(color,160*visibility);
heavyPen.width=3.0; heavyPen.color=withAlpha(color,35*visibility);
var start=180;
if(front) start=0;
g.drawArc(heavyPen,-rx,-ry,rx*2,ry*2,start,180);
g.drawArc(thinPen,-rx,-ry,rx*2,ry*2,start,180);
g.restore(st);
};
var drawSatellite = function(g,s,which,front){
var visibility = smooth(0.20,0.72,s.open);
if(visibility<=0) return;
var rx,ry,tilt,angle;
if(which==1){ rx=65+101*s.open; ry=19+37*s.open; tilt=-19*math.pi/180; angle=s.orbit1; }
else { rx=58+80*s.open; ry=31+40*s.open; tilt=51*math.pi/180; angle=s.orbit2; }
var x,y,depth = orbitPoint(rx,ry,tilt,angle);
if((depth>=0) != front) return;
var st=g.save(); g.translate(x,y);
var size=0.45+0.55*visibility;
g.scale(size,size);
if(which==1){
// 小型机械卫星:金属舱体与两侧的冷蓝面板随轨道一起运动。
g.fillPolygon(inkBrush,[-16,-3.5,-9,-3.5,-9,3.5,-16,3.5]);
g.fillPolygon(inkBrush,[9,-3.5,16,-3.5,16,3.5,9,3.5]);
thinPen.color=0xFF6EADB5; thinPen.width=0.8;
g.drawPolygon(thinPen,[-16,-3.5,-9,-3.5,-9,3.5,-16,3.5]);
g.drawPolygon(thinPen,[9,-3.5,16,-3.5,16,3.5,9,3.5]);
g.drawLine(thinPen,-12.5,-3.5,-12.5,3.5); g.drawLine(thinPen,12.5,-3.5,12.5,3.5);
}
g.fillCircle(darkBrush,1,1.5,8.3);
g.fillCircle(gearBrush,0,0,7.4);
mainPen.width=1; mainPen.color=0xFFE1C689;
g.drawCircle(mainPen,0,0,7.4);
g.fillCircle(inkBrush,0,0,4.2);
blueBrush.color=0xFF70D6E1;
g.fillCircle(blueBrush,-1.1,-1.2,2.1);
thinPen.color=0xFFC8A86C; thinPen.width=0.8;
g.drawLine(thinPen,-10,0,-7.3,0); g.drawLine(thinPen,7.3,0,10,0);
g.restore(st);
};
var drawPlanet = function(g,s){
var p = s.planet;
if(p<0.001) return;
var st = g.save();
g.scale(p,p);
g.drawImageRect(glowBitmap,-118,-118,236,236);
g.fillPath(planetBrush,planetPath);
var clip = g.save();
g.setClipPath(planetPath);
var terrain = g.save();
g.translate(8*math.sin(tau*s.t/15),0);
g.fillPath(landShade,land1); g.fillPath(landBrush,land2); g.fillPath(landShade,land3);
thinPen.width=0.65; thinPen.color=0xB9B7C6A3;
g.drawPath(thinPen,land1); g.drawPath(thinPen,land2); g.drawPath(thinPen,land3);
blueBrush.color=0xFFA7E9E5;
g.fillCircle(blueBrush,-28,-20,1.4); g.fillCircle(blueBrush,18,18,1.1);
g.restore(terrain);
var grid = g.save(); g.rotate(-17);
thinPen.width=0.65; thinPen.color=0x5889C4CA;
g.drawEllipse(thinPen,-63,-17,126,34);
g.drawEllipse(thinPen,-55,-43,110,86);
g.drawArc(thinPen,-25,-61,50,122,270,180);
g.restore(grid);
// 极淡的大气亮面,仍保留深色的夜半球。
thinPen.width=1.0; thinPen.color=0x668FE0E0;
g.drawArc(thinPen,-58,-58,116,116,200,105);
g.restore(clip);
heavyPen.color=0xFF163641; heavyPen.width=2.4;
g.drawCircle(heavyPen,0,0,61);
thinPen.color=0xFF80B7B5; thinPen.width=0.9;
g.drawCircle(thinPen,0,0,60);
g.restore(st);
};
var drawSeedFinish = function(g,s){
var opacity=1-smooth(0,0.13,s.open);
if(opacity<=0) return;
seedLight.setCenterColor(withAlpha(0xFFF0B8,92*opacity));
seedShadow.setCenterColor(withAlpha(0x040A0F,112*opacity));
g.fillPath(seedLight,seedPath);
g.fillPath(seedShadow,seedPath);
heavyPen.width=0.9; heavyPen.color=withAlpha(0x16191B,175*opacity);
g.drawCircle(heavyPen,0,0,48.3);
thinPen.width=0.85; thinPen.color=withAlpha(0xF0D59B,185*opacity);
g.drawArc(thinPen,-45,-45,90,90,200,86);
};
var drawSeam = function(g,s){
var opacity = 1-smooth(0.06,0.48,s.open);
if(opacity<=0) return;
var st=g.save(); g.rotate(-16);
// 先刻出暗槽,再在槽内画一线冷蓝光,沿球面轻轻弯曲。
bluePen.width=3.5; bluePen.color=withAlpha(0x081722,190*opacity);
g.drawBezier(bluePen,-45,-3,-17,8,17,8,45,-3);
bluePen.width=5; bluePen.color=withAlpha(0x54D3E8,(28+13*s.shimmer)*opacity);
g.drawBezier(bluePen,-45,-3,-17,8,17,8,45,-3);
bluePen.width=1.15; bluePen.color=withAlpha(0x7DDBE9,(145+65*s.shimmer)*opacity);
g.drawBezier(bluePen,-45,-3,-17,8,17,8,45,-3);
bluePen.width=0.5; bluePen.color=withAlpha(0xD0FAF5,180*opacity);
g.drawBezier(bluePen,-32,1,-12,7,12,7,32,1);
g.restore(st);
};
var drawScene = function(g,s){
// 背层:种子底壳、扩张的机械结构、后半轨道。
var shellBase = 1-smooth(0.02,0.82,s.open);
if(shellBase>0.001){
var st=g.save(); g.scale(shellBase,shellBase); g.fillPath(seedBrush,seedPath); g.restore(st);
}
drawFrameRing(g,s);
drawArms(g,s);
drawGears(g,s);
drawPetal(g,s,1); drawPetal(g,s,3);
drawOrbit(g,s,1,false); drawOrbit(g,s,2,false);
drawSatellite(g,s,1,false); drawSatellite(g,s,2,false);
drawPlanet(g,s);
// 前半轨道和卫星后绘制,形成可靠的前后遮挡关系。
drawOrbit(g,s,1,true); drawOrbit(g,s,2,true);
drawSatellite(g,s,1,true); drawSatellite(g,s,2,true);
drawPetal(g,s,2); drawPetal(g,s,4);
drawSeedFinish(g,s);
drawSeam(g,s);
};
// drawFrame 也可用于离屏渲染;所有场景几何均使用 96-DPI 逻辑单位。
var drawFrame = function(g,w,h,dpiX,dpiY,ms){
if(w<=0 || h<=0) return;
if(!backgroundBitmap || w!=backgroundW || h!=backgroundH || dpiX!=backgroundDpiX || dpiY!=backgroundDpiY){
if(backgroundBitmap) backgroundBitmap.delete();
backgroundBitmap=makeBackground(w,h,dpiX,dpiY);
backgroundW,backgroundH,backgroundDpiX,backgroundDpiY=w,h,dpiX,dpiY;
}
var saved=g.save();
g.compositingMode=1; // 不透明背景的快速拷贝
g.drawImage(backgroundBitmap,0,0);
g.compositingMode=0;
g.smoothingMode=4;
var s=sceneState(ms);
var lw,lh=w/dpiX,h/dpiY;
var fit=math.min(lw/900,lh/600);
g.scale(dpiX,dpiY);
g.translate(lw*0.50,lh*0.48+s.bob*fit);
var seedFraming=1+0.12*(1-smooth(0,0.70,s.open));
var zoom=fit*s.camera*s.breath*seedFraming;
g.scale(zoom,zoom);
drawScene(g,s);
g.restore(saved);
};
winform.canvas.onDrawForeground = function(g,rc){
var dpiX,dpiY=winform.dpiScale(1,1);
drawFrame(g,rc.width,rc.height,dpiX,dpiY,elapsedMs);
};
winform.canvas.onAnimation = function(previous,beginning,change,timestamp,duration,deltaMs){
// 画面隐藏/最小化时不把长时间的暂停补进当前一帧。
if(!paused && deltaMs<1000) elapsedMs += deltaMs;
return true;
};
winform.canvas.dlgCode=4;
winform.canvas.onKeyDown = function(vk){
if(vk==0x20) paused=!paused; // Space
elseif(vk==0x52) elapsedMs=0; // R
elseif(vk==0x1B) { winform.close(); return; } // Esc
winform.canvas.redraw();
};
winform.onDestroy = function(){
if(backgroundBitmap) backgroundBitmap.delete();
for(i=1;#shellMaterials) shellMaterials[i].delete();
var resources=[shellPath,seedPath,seedBrush,seedLight,seedShadow,planetPath,planetBrush,
land1,land2,land3,gearPath,gear7Path,darkBrush,gearBrush,goldBrush,highlightBrush,blueBrush,
landBrush,landShade,inkBrush,mainPen,thinPen,heavyPen,bluePen,titleFont,smallFont,labelFont,glowBitmap];
for(i=1;#resources) resources[i].delete();
};
winform.show();
winform.canvas.setFocus();
winform.canvas.startAnimation(16);
win.loopMessage();
Markdown 格式