Ekaropolus 7d1d4a43bd
All checks were successful
continuous-integration/drone/push Build is passing
Building digital Twin 2D and 3D
2025-09-03 11:30:29 -06:00

111 lines
4.4 KiB
Python

from django.shortcuts import render
from django.http import JsonResponse
import random, math
def viewer(request):
return render(request, "pxy_building_digital_twins/viewer.html")
def wire_viewer(request):
return render(request, "pxy_building_digital_twins/wire.html")
def wire_babylon(request):
return render(request, "pxy_building_digital_twins/wire_babylon.html")
# ---------- 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):
"""
Returns a random stadium (spaces + bins) as GeoJSON FeatureCollections.
Query params (optional):
- seed: int (deterministic output for a given seed)
- concourse_bins: int (default random 42..66)
- dry_toilets: int (default random 4..8)
"""
seed = request.GET.get("seed")
if seed is not None:
try:
random.seed(int(seed))
except ValueError:
random.seed(seed) # allow string seeds too
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 sizes (soccer-ish)
FIELD_W = random.uniform(100, 110)
FIELD_H = random.uniform(64, 72)
# Ellipse radii for concourse / stands
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)
# Build spaces
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"}
})
# Bins on concourse + outside dry toilets
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})