Each year there are several challenges for generative arts in the month January called “Genuary” ( https://genuary.art/prompts ). For this #genuary 2022, I envisioned a box of balls colliding, at each collision swap some style element of the ball.
We define the size and the force that the wall exerts:
SetDirectory[NotebookDirectory[]];size=600;ClearAll[ForceWall,DoStep,BounceBall,ShowScene]ForceWall[z_,r_]:=20If[Abs[z]>1-r,(1-Abs[z]-r)Sign[z],0]
Define what to do in each step:
DoStep[state_List,δt_]:=Module[{new,a,len,overlapbefore,overlapafter,poss,styles,ri},overlapbefore=Table[Boole[Norm[s["p"]-r["p"]]-s["r"]-r["r"]<0],{s,state},{r,state}];new=state;len=Length[new];new[[All,"p"]]+=new[[All,"v"]]δt;a=ConstantArray[{0,0},Length[state]];a=Table[ForceWall[#,n[["r"]]]&/@n[["p"]],{n,new}];a+=Table[Total@Table[BounceBall[new[[s1i]],new[[s2i]]],{s2i,DeleteCases[Range[len],s1i]}],{s1i,len}];new[[All,"v"]]+=aδt;(*Allthisstuffistocheckifitchangesfromnon-overlappingtooverlapping,andifsoswapsomestyleelement*)overlapafter=Table[Boole[Norm[s["p"]-r["p"]]-s["r"]-r["r"]<0],{s,new},{r,new}];overlapafter-=overlapbefore;overlapafter//=Abs;overlapafter//=UpperTriangularize[#,1]&;poss=Position[overlapafter,1,{2}];Do[styles=new[[p,"style"]];styles//=Transpose;ri=RandomInteger[{1,2}];styles[[ri]]//=Reverse;styles//=Transpose;new[[p,"style"]]=styles;,{p,poss}];new]
Define the force that ball 1 receives in the presence of ball 2 (nothing if the distance is too large)
BounceBall[s1_Association,s2_Association]:=Module[{diff,d},diff=s1["p"]-s2["p"];d=Norm[diff]-s1["r"]-s2["r"];If[d<0,-5Normalize[diff]d,{0,0}]]
Draw the scene and rasterize is:
ShowScene[state_]:=Module[{g},g=Graphics[{Style[Disk[#["p"],#["r"]],#["style"]]&/@state,FaceForm[],EdgeForm[White],Rectangle[{-1,-1},{1,1}]},PlotRange1.2,ImageSize2size,BackgroundRGBColor[0.,0.21,0.46]];Rasterize[g,"Image",RasterSizesize,ImageSizesize]]
Create some random state, iterate it, and save some of the images
SeedRandom[1234];state=Table[<|"p"->RandomReal[{-0.5,0.5},2],"v"->RandomReal[{-0.1,0.1},2],"r"->0.1,"style"->{EdgeForm[{AbsoluteThickness[3],RandomColor[]}],FaceForm[RandomColor[]]}|>,{15}];imgs=Table[state=DoStep[state,0.05];If[Mod[i,3]==0,ShowScene[state],{}],{i,600}];imgs=imgs[[3;;;;3]];
Export the images to a gif:
In[]:=
Export["2022_6.gif",imgs,"DisplayDurations"->1/60]
Out[]=
2022_6.gif