diff --git a/index.html b/index.html index 685506f..13f12c4 100644 --- a/index.html +++ b/index.html @@ -12128,6 +12128,7 @@ var moved=codeNodeDragFrame.moved; codeNodeDragFrame.moved=null; if(moved&&moved.length)redrawMovedGraphNodes(moved); + if(updateHullsRef.current)updateHullsRef.current(); } node.call(d3.drag().on('start',function(e,d){ d.fx=d.x;d.fy=d.y; @@ -12148,7 +12149,8 @@ if(!isCodeViewDragBusy())setCodeViewInteractionBusy(codeViewDragBusyRoot(),true); } codeNodeDragPrev[d.id]={x:d.x,y:d.y}; - var siblings=translateCodeViewSiblings(sim.nodes(),d,dx,dy,codeCardPathsRef.current); + var moveGroup=e.sourceEvent&&(e.sourceEvent.shiftKey||e.sourceEvent.altKey); + var siblings=moveGroup?translateCodeViewSiblings(sim.nodes(),d,dx,dy,codeCardPathsRef.current):[]; if(codeViewDragRefresh('move')){ redrawGraphLinksAndNodes(); if(updateHullsRef.current)updateHullsRef.current(); @@ -12157,12 +12159,14 @@ scheduleCodeViewDragFrame(codeNodeDragFrame,requestAnimationFrame,paintCodeNodeDragFrame); } if(codeViewDragShouldDrawMinimap('move')&&drawMinimapRef.current)drawMinimapRef.current(); + if(updateHullsRef.current)updateHullsRef.current(); return; } }).on('end',function(e,d){ if(!keepReadable){ if(!e.active)sim.alphaTarget(0); d.fx=null;d.fy=null; + if(updateHullsRef.current)updateHullsRef.current(); return; } d.fx=d.x;d.fy=d.y; @@ -12170,6 +12174,7 @@ flushCodeViewDragFrame(codeNodeDragFrame,cancelAnimationFrame); codeNodeDragFrame.moved=null; setCodeViewInteractionBusy(codeViewDragBusyRoot(),false); + if(updateHullsRef.current)updateHullsRef.current(); var end=noteCodeCardPointerEnd(leftoverDragMoved); leftoverIgnoreClick=end.ignoreNextClick; if(leftoverIgnoreClick)setTimeout(function(){leftoverIgnoreClick=false;},400); @@ -12240,6 +12245,197 @@ }); return grouped; } + var cellCurveGen=d3.line().curve(d3.curveCatmullRomClosed.alpha(0.5)); + function buildTwoCellBridge(p1,p2){ + var dx=p2.x-p1.x,dy=p2.y-p1.y; + var d=Math.hypot(dx,dy); + var r1=p1.r,r2=p2.r; + if(d<=Math.abs(r1-r2)){ + var bigger=r1>=r2?p1:p2; + return buildHumanCellMembrane([bigger]); + } + var baseAngle=Math.atan2(dy,dx); + var ux=dx/d,uy=dy/d,nx=-uy,ny=ux; + var stretchDist=Math.max(0,d-(r1+r2)*0.5); + var minWaist=Math.min(r1,r2)*0.28; + var maxWaist=(r1+r2)*0.52; + var waistR=Math.max(minWaist,maxWaist*Math.max(0.15,1-(stretchDist/280)*0.75)); + var pts=[],arcSamples=9; + for(var i=0;i<=arcSamples;i++){ + var a=baseAngle+Math.PI/2+(i/arcSamples)*Math.PI; + pts.push([p1.x+Math.cos(a)*r1,p1.y+Math.sin(a)*r1]); + } + var midX=(p1.x+p2.x)*0.5,midY=(p1.y+p2.y)*0.5; + pts.push([midX-nx*waistR,midY-ny*waistR]); + for(var j=0;j<=arcSamples;j++){ + var a2=baseAngle-Math.PI/2+(j/arcSamples)*Math.PI; + pts.push([p2.x+Math.cos(a2)*r2,p2.y+Math.sin(a2)*r2]); + } + pts.push([midX+nx*waistR,midY+ny*waistR]); + return cellCurveGen(pts); + } + function buildStretchedClusterPath(clusterNodes,stretchedNode){ + var restPts=[]; + var samples=16; + clusterNodes.forEach(function(n){ + for(var k=0;kMath.PI)a-=Math.PI*2; + while(a<-Math.PI)a+=Math.PI*2; + if(amaxA){maxA=a;idxMax=i;} + } + + if(idxMin===-1||idxMax===-1||idxMin===idxMax)return null; + + var d=minDist; + var r1=anchor.r,r2=sr; + var stretchDist=Math.max(0,d-(r1+r2)*0.5); + var minWaist=Math.min(r1,r2)*0.28; + var maxWaist=(r1+r2)*0.5; + var waistR=Math.max(minWaist,maxWaist*Math.max(0.18,1-(stretchDist/280)*0.72)); + + var ux=(sx-anchor.x)/d,uy=(sy-anchor.y)/d; + var nx=-uy,ny=ux; + var midX=(anchor.x+sx)*0.5,midY=(anchor.y+sy)*0.5; + var w1=[midX-nx*waistR,midY-ny*waistR]; + var w2=[midX+nx*waistR,midY+ny*waistR]; + + var chain1=[]; + var cur=idxMin; + while(true){ + chain1.push(cur); + if(cur===idxMax)break; + cur=(cur+1)%nH; + } + + var chain2=[]; + cur=idxMax; + while(true){ + chain2.push(cur); + if(cur===idxMin)break; + cur=(cur+1)%nH; + } + + var sumD1=0; + for(var c=0;c=avgD2?chain1:chain2; + var startIdx=farChain[0]; + var endIdx=farChain[farChain.length-1]; + + var endPt=restHull[endIdx]; + var startPt=restHull[startIdx]; + var firstWaist=Math.hypot(endPt[0]-w1[0],endPt[1]-w1[1])Math.PI*2)sweep-=Math.PI*2; + while(sweep<0)sweep+=Math.PI*2; + if(sweep<=Math.PI)sweep-=Math.PI*2; + + var sArc=[]; + var arcSamples=9; + for(var s=1;srSum*1.15&&minDist>maxStretchDist){ + maxStretchDist=minDist; + stretchedNode=ni; + anchorNode=closest; + } + } + if(stretchedNode){ + var restNodes=pts.filter(function(n){return n!==stretchedNode;}); + var stretchedPath=buildStretchedClusterPath(restNodes,stretchedNode); + if(stretchedPath)return stretchedPath; + } + var samples=16,ptsAll=[]; + pts.forEach(function(n){ + for(var i=0;i