Initial commit
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""For every node whose width VARIES across viewports (=> would emit a width band),
|
||||
classify the underlying law: hairline / parent-fill / fraction-of-parent / step.
|
||||
Tells us how many bands each recovery law would eliminate."""
|
||||
import json, glob, os, sys, collections
|
||||
run = sys.argv[1] if len(sys.argv) > 1 else "compiler/output/sample/.clone"
|
||||
caps = {}
|
||||
for f in sorted(glob.glob(os.path.join(run, "source/capture/dom-*.json"))):
|
||||
caps[int(os.path.basename(f)[4:-5])] = json.load(open(f))["root"]
|
||||
VPS = sorted(caps)
|
||||
# flatten each viewport in pre-order, aligned by index
|
||||
F = {vp: [] for vp in VPS}
|
||||
PARENT = []
|
||||
def flat(n, a, parent_idx, store_parent):
|
||||
idx = len(a); a.append(n)
|
||||
if store_parent: PARENT.append(parent_idx)
|
||||
for c in (n.get("children") or []):
|
||||
flat(c, a, idx, store_parent)
|
||||
for vp in VPS:
|
||||
flat(caps[vp], F[vp], -1, vp == VPS[0])
|
||||
N = len(F[VPS[0]])
|
||||
def pf(v):
|
||||
try: return float(str(v).replace("px",""))
|
||||
except: return 0.0
|
||||
def cs(i, vp): return F[vp][i].get("computed") or {}
|
||||
def bb(i, vp): return F[vp][i].get("bbox") or {}
|
||||
def vis(i, vp):
|
||||
if not F[vp][i].get("visible"): return False
|
||||
if (cs(i,vp).get("display")=="none"): return False
|
||||
return True
|
||||
def width(i, vp): return bb(i, vp).get("width", 0)
|
||||
def parent_content_w(i, vp):
|
||||
p = PARENT[i]
|
||||
if p < 0: return bb(i,vp).get("width",0)
|
||||
pc = cs(p, vp)
|
||||
return bb(p,vp).get("width",0) - pf(pc.get("paddingLeft")) - pf(pc.get("paddingRight")) - pf(pc.get("borderLeftWidth")) - pf(pc.get("borderRightWidth"))
|
||||
|
||||
FRACTIONS = {1/2:"1/2",1/3:"1/3",2/3:"2/3",1/4:"1/4",3/4:"3/4",1/5:"1/5",2/5:"2/5",3/5:"3/5",4/5:"4/5",1/6:"1/6",5/6:"5/6"}
|
||||
cat = collections.Counter()
|
||||
band_cat = collections.Counter()
|
||||
examples = collections.defaultdict(list)
|
||||
for i in range(N):
|
||||
ws = {vp: width(i,vp) for vp in VPS if vis(i,vp)}
|
||||
if len(ws) < 2: continue
|
||||
wv = list(ws.values())
|
||||
spread = max(wv) - min(wv)
|
||||
if spread <= 1.0: continue # constant => no band
|
||||
# how many band variants would this node emit? (# distinct rounded widths across the 4 band vps) - 1
|
||||
distinct = len(set(round(w) for w in wv))
|
||||
bands = distinct - 1
|
||||
if bands < 1: continue
|
||||
# --- classify ---
|
||||
if max(wv) < 2.0:
|
||||
c = "hairline(<2px)"
|
||||
else:
|
||||
ratios = []
|
||||
ok_parent = True
|
||||
for vp, w in ws.items():
|
||||
pcw = parent_content_w(i, vp)
|
||||
if pcw <= 0: ok_parent = False; break
|
||||
ratios.append(w / pcw)
|
||||
if not ok_parent:
|
||||
c = "no-parent"
|
||||
elif max(ratios) - min(ratios) < 0.03 and abs(sum(ratios)/len(ratios) - 1.0) < 0.03:
|
||||
c = "parent-fill(100%)"
|
||||
else:
|
||||
avg = sum(ratios)/len(ratios)
|
||||
# nearest simple fraction
|
||||
best = min(FRACTIONS, key=lambda f: abs(f-avg))
|
||||
if max(ratios)-min(ratios) < 0.03 and abs(best-avg) < 0.015:
|
||||
c = f"fraction~{FRACTIONS[best]}"
|
||||
elif max(ratios)-min(ratios) < 0.04:
|
||||
c = "const-%(other)"
|
||||
else:
|
||||
c = "step/other"
|
||||
cat[c] += 1
|
||||
band_cat[c] += bands
|
||||
if len(examples[c]) < 4:
|
||||
examples[c].append((round(min(wv),1), round(max(wv),1), [round(width(i,vp)) for vp in VPS]))
|
||||
|
||||
print(f"nodes with width-bands: {sum(cat.values())} total band-variants: {sum(band_cat.values())}")
|
||||
print(f"{'category':22} {'nodes':>6} {'bands':>6}")
|
||||
for c,_ in band_cat.most_common():
|
||||
print(f"{c:22} {cat[c]:>6} {band_cat[c]:>6} eg {examples[c][:3]}")
|
||||
Reference in New Issue
Block a user