import json, numpy as np d=json.load(open('die.json')); V=np.array(d['vertices']); G=np.array(d['G']); Nn=np.array(d['normals'],float) faces=d['faces']; L=np.ptp(V[:,0]) nn=Nn/np.linalg.norm(Nn,axis=1)[:,None] def edges_of(fi): f=faces[fi]; return [(f[a],f[(a+1)%len(f)]) for a in range(len(f))] def adj(fi,e): s=set(e) for g in range(14): if g!=fi and s<=set(faces[g]): return g def info(fi,Gp=G): n=nn[fi]; f=V[faces[fi]]; P=Gp+np.dot(f[0]-Gp,n)*n out=[] for (a,b) in edges_of(fi): A,B=V[a],V[b]; e=B-A s=np.dot(np.cross(e,P-A),n)/np.linalg.norm(e) t=np.dot(P-A,e)/np.dot(e,e) dist=np.linalg.norm(P-(A+np.clip(t,0,1)*e)) out.append((s,t,dist,(a,b))) # orientation: inside if all s same sign as polygon orientation orient=np.sign(sum(np.dot(np.cross(V[faces[fi][k]]-V[faces[fi][0]],V[faces[fi][k+1]]-V[faces[fi][0]]),n) for k in range(1,len(faces[fi])-1))) out=[(s*orient,t,dist,e) for s,t,dist,e in out] return out def step(fi,Gp=G): inf=info(fi,Gp) if all(s>0 for s,_,_,_ in inf): return None # tip over the outside edge nearest to P whose foot is on the segment, else nearest outside edge outs=[x for x in inf if x[0]<0] cand=[x for x in outs if 0<=x[1]<=1] or outs s,t,dist,e=min(cand,key=lambda x:x[0]) return adj(fi,e) # distances: how far must G move (fraction of length) before another face becomes stable def poly_dist(fi,Gp=G): inf=info(fi,Gp) if all(s>0 for s,_,_,_ in inf): return -min(s for s,_,_,_ in inf) return min(d for s,t,d,e in inf) def roll_path(start,Gp=G): path=[start]; f=start while True: g=step(f,Gp) if g is None: return path if g in path: return path+[g] path.append(g); f=g if __name__=='__main__': for start in range(14): print(start, roll_path(start)) for fi in range(14): print(fi, '%.4f mm per 100 mm'%(poly_dist(fi)/L*100))