# pxy_building_digital_twins/views.py from django.shortcuts import render from django.http import JsonResponse from django.views.decorators.clickjacking import xframe_options_sameorigin import random, math # ---------- page views (allow same-origin iframe) ---------- @xframe_options_sameorigin def viewer(request): resp = render(request, "pxy_building_digital_twins/viewer.html") resp["Content-Security-Policy"] = "frame-ancestors 'self'" return resp @xframe_options_sameorigin def wire_viewer(request): resp = render(request, "pxy_building_digital_twins/wire.html") resp["Content-Security-Policy"] = "frame-ancestors 'self'" return resp @xframe_options_sameorigin def wire_babylon(request): resp = render(request, "pxy_building_digital_twins/wire_babylon.html") resp["Content-Security-Policy"] = "frame-ancestors 'self'" return resp # ---------- geometry helpers ---------- def rect_poly(cx, cy, w, h): x1, x2 = cx - w/2, cx + w/2 y1, y2 = cy - h/2, cy + h/2 return {"type": "Polygon", "coordinates": [[[x1,y1],[x2,y1],[x2,y2],[x1,y2],[x1,y1]]]} def ept(cx, cy, rx, ry, a): return [cx + rx*math.cos(a), cy + ry*math.sin(a)] def ellipse_ring(cx, cy, rx_out, ry_out, rx_in, ry_in, segments=128): outer = [ept(cx, cy, rx_out, ry_out, i/segments*2*math.pi) for i in range(segments)] inner = [ept(cx, cy, rx_in, ry_in, i/segments*2*math.pi) for i in range(segments-1, -1, -1)] return {"type":"Polygon","coordinates":[outer, inner]} def bins_on_ellipse(cx, cy, rx, ry, count, start_rad=0.0, jitter_r=0.0): feats=[] kinds = ["recyclable","organic","landfill","special"] for i in range(count): a = start_rad + (i/count)*2*math.pi + random.uniform(-0.03, 0.03) rxf = rx + random.uniform(-jitter_r, jitter_r) ryf = ry + random.uniform(-jitter_r, jitter_r) x,y = ept(cx, cy, rxf, ryf, a) kind = kinds[i % len(kinds)] cap = random.choice([40,60,80]) feats.append({ "type":"Feature", "geometry":{"type":"Point","coordinates":[x,y]}, "properties":{"id":f"BIN_{i+1}","kind":kind,"capacity_l":cap} }) return feats # ---------- API ---------- def api_random_stadium(request): seed = request.GET.get("seed") if seed is not None: try: random.seed(int(seed)) except ValueError: random.seed(seed) concourse_bins = int(request.GET.get("concourse_bins", random.randint(42,66))) dry_toilets = int(request.GET.get("dry_toilets", random.randint(4,8))) FIELD_W = random.uniform(100, 110) FIELD_H = random.uniform(64, 72) CONC_RX_IN = random.uniform(62, 68); CONC_RY_IN = random.uniform(56, 64) CONC_RX_OUT = CONC_RX_IN + random.uniform(6,10); CONC_RY_OUT = CONC_RY_IN + random.uniform(6,10) STANDS_RX_IN = CONC_RX_OUT + random.uniform(3,5); STANDS_RY_IN = CONC_RY_OUT + random.uniform(3,5) STANDS_RX_OUT = STANDS_RX_IN + random.uniform(18,26); STANDS_RY_OUT = STANDS_RY_IN + random.uniform(18,26) spaces = {"type":"FeatureCollection","features":[]} spaces["features"].append({"type":"Feature","geometry": rect_poly(0,0, FIELD_W, FIELD_H), "properties": {"type":"field","name":"Field"}}) spaces["features"].append({"type":"Feature","geometry": ellipse_ring(0,0, CONC_RX_OUT, CONC_RY_OUT, CONC_RX_IN, CONC_RY_IN, 128), "properties": {"type":"concourse","name":"Main Concourse"}}) spaces["features"].append({"type":"Feature","geometry": ellipse_ring(0,0, STANDS_RX_OUT, STANDS_RY_OUT, STANDS_RX_IN, STANDS_RY_IN, 160), "properties": {"type":"stands","name":"Stands"}}) RX_MID = (CONC_RX_IN + CONC_RX_OUT)/2 RY_MID = (CONC_RY_IN + CONC_RY_OUT)/2 bins = {"type":"FeatureCollection","features":[]} bins["features"].extend(bins_on_ellipse(0,0, RX_MID, RY_MID, concourse_bins, random.uniform(0, math.pi/3), jitter_r=1.5)) OUT_RX = STANDS_RX_OUT + random.uniform(10,16) OUT_RY = STANDS_RY_OUT + random.uniform(10,16) for i in range(dry_toilets): a = (i/dry_toilets)*2*math.pi + random.uniform(-0.05,0.05) x,y = ept(0,0, OUT_RX, OUT_RY, a) bins["features"].append({"type":"Feature","geometry":{"type":"Point","coordinates":[x,y]}, "properties":{"id":f"DT_{i+1}","kind":"dry_toilet","capacity_l":0}}) return JsonResponse({"spaces": spaces, "bins": bins})