Instructions to use XXXpilar/Fastfill-test with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use XXXpilar/Fastfill-test with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
Download code/fastfill/validate.py from XXXpilar/Fastfill-test: direct link, hf CLI and curl.
- Browser
- Download file 11.8 kB
-
https://huggingface.co/XXXpilar/Fastfill-test/resolve/main/code/fastfill/validate.py
- Command line
-
hf download hf://XXXpilar/Fastfill-test/code/fastfill/validate.py
-
curl -L -o validate.py https://huggingface.co/XXXpilar/Fastfill-test/resolve/main/code/fastfill/validate.py
11.8 kB
| """Geometric checks for a placed room (IR format, see scene.py). Box-level, upright objects. | |
| valid = no out-of-bounds object, no support failure, nothing through the ceiling. Collisions are reported separately in buckets | |
| because reference layouts themselves collide at box level (chairs tucked under tables, nightstands against | |
| beds, boxes inside cabinets): 8-18% of GT rooms still have large "main" overlaps, so collisions are only | |
| meaningful relative to the GT value on the same rooms. | |
| oob footprint leaves boundary.buffer(oob_tol) (only for boundary_type polygon/hull) | |
| support_fail parent missing, bottom not within tol of the parent top, center off the parent footprint, | |
| or no parent and |z| > tol (floating / sunk) | |
| Support distances are inclusive: <= tol + SUPPORT_EPS, in XY and Z, including the floor. | |
| collisions pairs with footprint overlap > min_area and height overlap > tol, supports excluded: | |
| rugs / mats / boxes under 6 cm are ignored; | |
| tuck (seat x table, bed x nightstand), contained (a smaller box inside another), main (the | |
| rest, including a duplicate: same category over half their union, or near-equal footprints), | |
| severe (main pairs whose overlap covers > 30% of the smaller footprint) | |
| oor OptiScene's object overlap rate (axis-aligned w x d boxes, no rotation), for comparison only | |
| fixed_collisions (object, fixed box) pairs overlapping like a collision: placements on a column, in a doorway | |
| or through a stair; reported next to the GT value, not part of valid (reference layouts have some) | |
| fixed_blocking the fixed_collisions whose fixed box is not a window: furniture in front of a window (its box | |
| sticks 5 cm into the room) is normal, a table through a column or a doorway is not. serve.py fails a | |
| layout on these, build flags reference layouts with them (flag fixed_collision) | |
| """ | |
| import math | |
| from shapely.geometry import LineString, Point, Polygon | |
| from fastfill.anchors import FLAT | |
| from fastfill.scene import footprint, head, norm_cat | |
| SEAT = {"chair", "armchair", "stool", "bench", "seat", "ottoman", "pouf"} | |
| TABLE = {"table", "desk", "counter", "island", "bar", "workstation", "vanity", "workbench"} | |
| SUPPORT_EPS = 1e-9 # meters: floating-point allowance, not an additional geometric tolerance | |
| def support_contains(parent, xy, tol=0.05): | |
| """Whether XY is within tol + SUPPORT_EPS of the parent's footprint, inclusive. | |
| Use Euclidean distance, not a polygonal buffer approximation, so corners and edges share the same rule. | |
| Accepts IR parent geometry (pos, size, yaw) and an XY coordinate; also used during runtime repair. | |
| """ | |
| return Polygon(footprint(parent)).distance(Point(xy[:2])) <= tol + SUPPORT_EPS | |
| def support_ok(child, parent=None, tol=0.05): | |
| """Inclusive support check for IR objects; parent=None means the floor. | |
| Callers must reject an unresolved parent reference before passing None. Both the absolute bottom/top | |
| height error and Euclidean XY distance to the parent footprint must be <= tol + SUPPORT_EPS. | |
| """ | |
| top = parent["pos"][2] + parent["size"][2] if parent is not None else 0.0 | |
| return abs(child["pos"][2] - top) <= tol + SUPPORT_EPS and \ | |
| (parent is None or support_contains(parent, child["pos"], tol)) | |
| def collision_kind(a, b, fa, fb, tol=0.05): | |
| ha, hb = head(a), head(b) | |
| if (ha in SEAT and hb in TABLE) or (hb in SEAT and ha in TABLE): | |
| return "tuck" | |
| na, nb = norm_cat(a["category"]), norm_cat(b["category"]) | |
| if (ha == "bed" and ("night" in nb or "bedside" in nb)) or (hb == "bed" and ("night" in na or "bedside" in na)): | |
| return "tuck" | |
| # a copy of the same furniture on (almost) the same spot is a duplicate, not a box inside a cabinet | |
| small, big = sorted((fa.area, fb.area)) | |
| if (na == nb and fa.intersection(fb).area >= 0.5 * fa.union(fb).area) or small >= 0.8 * big: | |
| return "main" | |
| for x, y, fx, fy in ((a, b, fa, fb), (b, a, fb, fa)): | |
| if fy.buffer(tol).contains(fx) and x["pos"][2] + x["size"][2] <= y["pos"][2] + y["size"][2] + tol: | |
| return "contained" | |
| return "main" | |
| def check(room, tol=0.05, oob_tol=0.10, min_area=0.01, severe=0.3): | |
| objs = room["objects"] | |
| fps = [Polygon(footprint(o)) for o in objs] | |
| res = {"n": len(objs), "oob": [], "support_fail": [], | |
| "collisions": {"main": [], "severe": [], "tuck": [], "contained": []}} | |
| if room.get("boundary") and room.get("boundary_type") in ("polygon", "hull"): | |
| inner = Polygon(room["boundary"]).buffer(oob_tol + 1e-9) | |
| res["oob"] = [o["id"] for o, fp in zip(objs, fps) if not inner.contains(fp)] | |
| h = room.get("height") # build drops a height that its own boxes exceed by > tol, so GT always passes | |
| res["ceiling"] = [o["id"] for o in objs if h and o["pos"][2] + o["size"][2] > h + tol] | |
| for i in range(len(objs)): | |
| for j in range(i + 1, len(objs)): | |
| a, b = objs[i], objs[j] | |
| if a.get("parent") == b["id"] or b.get("parent") == a["id"]: | |
| continue | |
| if min(a["pos"][2] + a["size"][2], b["pos"][2] + b["size"][2]) - max(a["pos"][2], b["pos"][2]) <= tol: | |
| continue | |
| if FLAT.intersection((head(a), head(b))) or min(a["size"][2], b["size"][2]) < 0.06: | |
| continue | |
| area = fps[i].intersection(fps[j]).area | |
| if area > min_area: | |
| kind = collision_kind(a, b, fps[i], fps[j], tol) | |
| res["collisions"][kind].append((a["id"], b["id"])) | |
| if kind == "main" and area > severe * min(fps[i].area, fps[j].area): | |
| res["collisions"]["severe"].append((a["id"], b["id"])) | |
| res["fixed_collisions"] = [] | |
| for o, fp in zip(objs, fps): | |
| if head(o) in FLAT or o["size"][2] < 0.06: | |
| continue | |
| for f in room.get("fixed") or []: | |
| if min(o["pos"][2] + o["size"][2], f["pos"][2] + f["size"][2]) - max(o["pos"][2], f["pos"][2]) <= tol: | |
| continue | |
| if fp.intersection(Polygon(footprint(f))).area > min_area: | |
| res["fixed_collisions"].append((o["id"], f["id"])) | |
| windows = {f["id"] for f in room.get("fixed") or [] | |
| if {"window", "windows"}.intersection(norm_cat(f.get("category")).split())} | |
| res["fixed_blocking"] = [p for p in res["fixed_collisions"] if p[1] not in windows] | |
| ids = {o["id"]: k for k, o in enumerate(objs)} | |
| for o in objs: | |
| k = ids.get(o.get("parent")) | |
| if not o.get("parent"): | |
| bad = not support_ok(o, tol=tol) | |
| elif k is None: | |
| bad = True | |
| else: | |
| bad = not support_ok(o, objs[k], tol) | |
| if bad: | |
| res["support_fail"].append(o["id"]) | |
| boxes = [] | |
| for o in objs: | |
| x, y, hx, hy = o["pos"][0], o["pos"][1], o["size"][0] / 2, o["size"][1] / 2 | |
| boxes.append(Polygon([(x - hx, y - hy), (x + hx, y - hy), (x + hx, y + hy), (x - hx, y + hy)])) | |
| total = sum(b.area for b in boxes) | |
| inter = sum(boxes[i].intersection(boxes[j]).area for i in range(len(boxes)) for j in range(i + 1, len(boxes))) | |
| res["oor"] = inter / total if total > 0 else 0.0 | |
| res["valid"] = not (res["oob"] or res["support_fail"] or res["ceiling"]) | |
| return res | |
| def holds(c, objs, boundary, index=None): | |
| """Constraint checker; semantics word for word as in scene.SYSTEM_PROMPT. index: an id -> object dict of objs, | |
| passed by callers that test many constraints on one room (build.extract_constraints).""" | |
| o = index if index is not None else {x["id"]: x for x in objs} | |
| fp = lambda i: Polygon(footprint(o[i])) | |
| t = c[0] | |
| if t == "on": | |
| return o[c[1]].get("parent") == c[2] | |
| if t == "faces": | |
| a, b = o[c[1]], o[c[2]] | |
| ang = math.atan2(b["pos"][1] - a["pos"][1], b["pos"][0] - a["pos"][0]) - a["yaw"] | |
| return abs((ang + math.pi) % (2 * math.pi) - math.pi) <= math.radians(30) + 1e-9 | |
| if t == "near": | |
| return fp(c[1]).distance(fp(c[2])) <= c[3] + 1e-9 | |
| if t == "against_wall": # every point of the side, not samples of it | |
| q, zone = footprint(o[c[1]]), Polygon(boundary).exterior.buffer(0.1 + 1e-9) | |
| return any(zone.contains(LineString([q[i], q[(i + 1) % 4]])) for i in range(4)) | |
| if t == "between": # the segment runs through a's footprint, not just touching its edge | |
| seg, f = LineString([o[c[2]]["pos"][:2], o[c[3]]["pos"][:2]]), fp(c[1]) | |
| return seg.intersects(f) and not seg.touches(f) | |
| raise ValueError(t) | |
| if __name__ == "__main__": | |
| room = {"boundary": [[0, 0], [4, 0], [4, 3], [0, 3]], "boundary_type": "polygon", | |
| "objects": [{"id": "bed", "category": "bed", "size": [2.0, 1.6, 0.5], "pos": [1.1, 1.5, 0.0], "yaw": 0.0}, | |
| {"id": "desk", "category": "desk", "size": [1.2, 0.6, 0.75], "pos": [3.6, 0.8, 0.0], "yaw": math.pi / 2}, | |
| {"id": "lamp", "category": "lamp", "size": [0.2, 0.2, 0.4], "pos": [3.6, 0.8, 0.75], "yaw": 0.0, "parent": "desk"}, | |
| {"id": "chair", "category": "chair", "size": [.5, .5, .9], "pos": [3.2, 0.8, 0.0], "yaw": 0.0}]} | |
| r = check(room) | |
| assert r["valid"] and r["collisions"]["tuck"] == [("desk", "chair")] and not r["collisions"]["main"], r | |
| # a column through the bed and a window on the south wall behind the desk: only the column is a fixed collision | |
| fx = [{"id": "col", "category": "column", "size": [.3, .3, 2.8], "pos": [1.5, 1.5, 0], "yaw": 0}, | |
| {"id": "win", "category": "window", "size": [1, .1, 1.2], "pos": [3.6, 0.05, 0.9], "yaw": 0}] | |
| assert check({**room, "fixed": fx})["fixed_collisions"] == [("bed", "col")] and check({**room, "fixed": fx})["valid"] | |
| tall = [{"id": "win2", "category": "window", "size": [1.5, .1, 1.2], "pos": [1.1, 2.3, 0.4], "yaw": 0}] # behind the bed | |
| r2 = check({**room, "fixed": fx + tall}) | |
| assert ("bed", "win2") in r2["fixed_collisions"] and r2["fixed_blocking"] == [("bed", "col")], r2 | |
| room["objects"][1]["yaw"] = 0.0 # unrotated desk spans x 3.0..4.2: out of bounds (tol 0.1) | |
| r = check(room) | |
| assert r["oob"] == ["desk"] and not r["valid"], r | |
| room["objects"][0]["pos"] = [2.9, 1.0, 0.0] # bed moved onto the desk area: main + severe collision | |
| r = check(room) | |
| assert ("bed", "desk") in r["collisions"]["main"] and ("bed", "desk") in r["collisions"]["severe"], r | |
| room["objects"][2]["pos"][2] = 0.3 # lamp sunk into the desk | |
| assert check(room)["support_fail"] == ["lamp"] | |
| assert check({"objects": [{"id": "l", "category": "lamp", "size": [.2, .2, .4], "pos": [1, 1, 0.3], "yaw": 0}]})["support_fail"] == ["l"] | |
| stack = {"height": 2.5, "objects": [{"id": "w", "category": "wardrobe", "size": [1, .6, 2.2], "pos": [1, 1, 0], "yaw": 0}, | |
| {"id": "b", "category": "box", "size": [.4, .4, .8], "pos": [1, 1, 2.2], "yaw": 0, "parent": "w"}]} | |
| r = check(stack) # top at 3.0 m under a 2.5 m ceiling | |
| assert r["ceiling"] == ["b"] and not r["valid"] and check({**stack, "height": None})["valid"], r | |
| b = [[0, 0], [4, 0], [4, 3], [0, 3]] | |
| a = [{"id": "a", "size": [1, .5, 1], "pos": [3.75, 1.5, 0], "yaw": math.pi / 2}] | |
| assert holds(["against_wall", "a"], a, b) and not holds(["against_wall", "a"], [{**a[0], "pos": [3.5, 1.5, 0]}], b) | |
| two = [{"id": "c1", "size": [.5, .5, .9], "pos": [1, 1, 0], "yaw": 0}, {"id": "c2", "size": [.5, .5, .9], "pos": [3, 1, 0], "yaw": math.pi}] | |
| assert holds(["faces", "c1", "c2"], two, b) and holds(["faces", "c2", "c1"], two, b) and holds(["near", "c1", "c2", 1.5], two, b) | |
| print("validate.py self-check ok") | |