food_chain/water.js
2026-09-29 19:10:03 +09:00

22 lines
1.6 KiB
JavaScript

// Smooth deterministic shoreline shared by drawing and physical habitat checks.
export function shoreRadius(w,theta){return w.r*(1+.095*Math.sin(3*theta+(w.seed||0))+.055*Math.sin(5*theta-(w.seed||0)*1.7)+.03*Math.cos(8*theta+(w.seed||0)*.6))}
export function waterDistance(water,x,y){
if(!water.length)return Infinity;
// Choose a likely nearest shore with only cheap bounds. Every other shore
// still receives the exact test, so this changes neither terrain nor physics.
let first=0,lower=Infinity;
for(let i=0;i<water.length;i++){const w=water[i],bound=Math.max(Math.abs(x-w.x),Math.abs(y-w.y))-w.r*1.18;if(bound<lower){lower=bound;first=i}}
const a=water[first],ax=x-a.x,ay=y-a.y;
let d=Math.hypot(ax,ay)-shoreRadius(a,Math.atan2(ay,ax));
for(let i=0;i<water.length;i++){
if(i===first)continue;
const w=water[i],dx=x-w.x,dy=y-w.y,limit=d+w.r*1.18;
if(limit<=0||dx*dx+dy*dy>=limit*limit)continue;
const v=Math.hypot(dx,dy)-shoreRadius(w,Math.atan2(dy,dx));if(v<d)d=v;
}
return d;
}
export function withinWater(w,x,y){const dx=x-w.x,dy=y-w.y,d2=dx*dx+dy*dy;if(d2<(w.r*.82)**2)return true;if(d2>(w.r*1.18)**2)return false;const r=shoreRadius(w,Math.atan2(dy,dx));return d2<r*r}
export function inWater(water,x,y){for(const w of water)if(withinWater(w,x,y))return true;return false}
export function waterPath(ctx,w,append=false){const n=72;if(!append)ctx.beginPath();for(let i=0;i<=n;i++){const theta=i*2*Math.PI/n,r=shoreRadius(w,theta),x=w.x+Math.cos(theta)*r,y=w.y+Math.sin(theta)*r;if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y)}ctx.closePath()}
export function habitat(affinity){return affinity<1/3?'陸棲':affinity<2/3?'両棲':'水棲'}