from __future__ import annotations
import argparse
import math
import sys
from pathlib import Path
from geomcore import Frame3D, Point3D, Vector3D
from geomcore.surfaces import Cone, Cylinder, Plane, Sphere, Torus
from geomcore_viewer import Viewer
from geomcore_viewer.sampling import sample_curve, sample_surface_grid
TYPES = ["Plane", "Cylinder", "Cone", "Sphere", "Torus"]
TAU = 2.0 * math.pi
WINDOWS = {
"Plane": (24, 24, (-6.0, 6.0), (-6.0, 6.0)),
"Cylinder": (48, 16, (0.0, TAU), (-6.0, 6.0)),
"Cone": (48, 20, (0.0, TAU), (-4.0, 6.0)),
"Sphere": (48, 24, (0.0, TAU), (-1.55, 1.55)),
"Torus": (48, 24, (0.0, TAU), (0.0, TAU)),
}
SURFACE_COLORS = ("#4d94ff", "#35c163")
CURVE_COLOR = "#ff5533"
CELL_STEP = 18.0
def canonical(name: str):
if name == "Plane":
return Plane(Point3D.origin(), Vector3D.z())
if name == "Cylinder":
return Cylinder(Point3D.origin(), Vector3D.z(), 2.0)
if name == "Cone":
return Cone.from_frame(Frame3D.world(), 0.4, 2.0)
if name == "Sphere":
return Sphere(Point3D.origin(), 3.0)
if name == "Torus":
return Torus(Point3D.origin(), Vector3D.z(), 4.0, 1.0)
raise KeyError(name)
def sample_surface(surface, kind: str):
nu, nv, (u0, u1), (v0, v1) = WINDOWS[kind]
return sample_surface_grid(surface.eval_point, u0, u1, v0, v1, nu, nv)
def shift(points, offset):
ox, oy, oz = offset
return [[p[0] + ox, p[1] + oy, p[2] + oz] for p in points]
def pair_plane_plane():
s1 = canonical("Plane")
s2 = Plane(Point3D.origin(), Vector3D.x())
kind, payload = s1.intersect_plane(s2)
assert kind == "line", kind
curve = sample_curve(payload.eval_point, -7.0, 7.0, 128)
return (s1, "Plane"), (s2, "Plane"), (curve, "line")
def pair_plane_sphere():
s1 = canonical("Plane")
s2 = canonical("Sphere")
kind, payload = s1.intersect_sphere(s2)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Plane"), (s2, "Sphere"), (curve, "circle")
def pair_sphere_plane():
s1 = canonical("Sphere")
s2 = canonical("Plane")
kind, payload = s2.intersect_sphere(s1)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Sphere"), (s2, "Plane"), (curve, "circle")
def pair_sphere_sphere():
s1 = canonical("Sphere")
s2 = Sphere(Point3D(3.0, 0.0, 0.0), 2.0)
kind, payload = s1.intersect_sphere(s2)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Sphere"), (s2, "Sphere"), (curve, "circle")
def pair_sphere_cylinder():
s1 = canonical("Sphere")
s2 = canonical("Cylinder")
kind, payload = s1.intersect_cylinder(s2)
assert kind == "two_circles", kind
curves = [sample_curve(c.eval_point, 0.0, TAU, 128) for c in payload]
return (s1, "Sphere"), (s2, "Cylinder"), (curves, "two_circles")
def pair_cylinder_sphere():
s1 = canonical("Cylinder")
s2 = canonical("Sphere")
kind, payload = s2.intersect_cylinder(s1)
assert kind == "two_circles", kind
curves = [sample_curve(c.eval_point, 0.0, TAU, 128) for c in payload]
return (s1, "Cylinder"), (s2, "Sphere"), (curves, "two_circles")
def pair_sphere_cone():
s1 = canonical("Sphere")
s2 = canonical("Cone")
kind, payload = s1.intersect_cone(s2)
assert kind == "two_circles", kind
curves = [sample_curve(c.eval_point, 0.0, TAU, 128) for c in payload]
return (s1, "Sphere"), (s2, "Cone"), (curves, "two_circles")
def pair_cone_sphere():
s1 = canonical("Cone")
s2 = canonical("Sphere")
kind, payload = s2.intersect_cone(s1)
assert kind == "two_circles", kind
curves = [sample_curve(c.eval_point, 0.0, TAU, 128) for c in payload]
return (s1, "Cone"), (s2, "Sphere"), (curves, "two_circles")
def pair_cylinder_cylinder():
s1 = canonical("Cylinder")
s2 = Cylinder(Point3D(3.0, 0.0, 0.0), Vector3D.z(), 2.0)
kind, payload = s1.intersect_cylinder(s2)
assert kind == "two_lines", kind
curves = [sample_curve(line.eval_point, -7.0, 7.0, 128) for line in payload]
return (s1, "Cylinder"), (s2, "Cylinder"), (curves, "two_lines")
def pair_plane_cylinder():
s1 = canonical("Plane")
s2 = canonical("Cylinder")
kind, payload = s1.intersect_cylinder(s2)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Plane"), (s2, "Cylinder"), (curve, "circle")
def pair_cylinder_plane():
s1 = canonical("Cylinder")
s2 = canonical("Plane")
kind, payload = s2.intersect_cylinder(s1)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Cylinder"), (s2, "Plane"), (curve, "circle")
def pair_plane_cone():
s1 = canonical("Plane")
s2 = canonical("Cone")
kind, payload = s1.intersect_cone(s2)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Plane"), (s2, "Cone"), (curve, "circle")
def pair_cone_plane():
s1 = canonical("Cone")
s2 = canonical("Plane")
kind, payload = s2.intersect_cone(s1)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Cone"), (s2, "Plane"), (curve, "circle")
def pair_cone_cylinder():
s1 = canonical("Cone")
s2 = canonical("Cylinder")
kind, payload = s1.intersect_cylinder(s2)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Cone"), (s2, "Cylinder"), (curve, "circle")
def pair_cylinder_cone():
s1 = canonical("Cylinder")
s2 = canonical("Cone")
kind, payload = s2.intersect_cylinder(s1)
assert kind == "circle", kind
curve = sample_curve(payload.eval_point, 0.0, TAU, 128)
return (s1, "Cylinder"), (s2, "Cone"), (curve, "circle")
PAIR_BUILDERS = {
("Plane", "Plane"): pair_plane_plane,
("Plane", "Sphere"): pair_plane_sphere,
("Plane", "Cylinder"): pair_plane_cylinder,
("Plane", "Cone"): pair_plane_cone,
("Sphere", "Plane"): pair_sphere_plane,
("Sphere", "Sphere"): pair_sphere_sphere,
("Sphere", "Cylinder"): pair_sphere_cylinder,
("Sphere", "Cone"): pair_sphere_cone,
("Cylinder", "Plane"): pair_cylinder_plane,
("Cylinder", "Sphere"): pair_cylinder_sphere,
("Cylinder", "Cylinder"): pair_cylinder_cylinder,
("Cylinder", "Cone"): pair_cylinder_cone,
("Cone", "Plane"): pair_cone_plane,
("Cone", "Sphere"): pair_cone_sphere,
("Cone", "Cylinder"): pair_cone_cylinder,
}
def build_cell(row: str, col: str):
builder = PAIR_BUILDERS.get((row, col))
if builder is not None:
return builder()
if row == col:
s = canonical(row)
return (s, row), None, None
return (canonical(row), row), (canonical(col), col), None
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--out", default=None, help="output scene.json path")
ap.add_argument("--serve", action="store_true", help="serve the local viewer frontend")
ap.add_argument("--port", type=int, default=8765, help="local server port")
ap.add_argument("--frontend", default=None, help="geomcore-viewer frontend/dist dir")
args = ap.parse_args()
viewer = Viewer()
with_curve, pending = [], []
for i, row in enumerate(TYPES):
for j, col in enumerate(TYPES):
(s1, k1), second, curve_info = build_cell(row, col)
ox, oy = j * CELL_STEP, -i * CELL_STEP
tag = f"cell_{row}_{col}"
verts, idx = sample_surface(s1, k1)
viewer.add_surface(
f"{tag}_s1", shift(verts, (ox, oy, 0.0)), idx,
color=SURFACE_COLORS[0], name=f"{tag} {k1}#1",
)
if second is not None:
(s2, k2) = second
verts, idx = sample_surface(s2, k2)
viewer.add_surface(
f"{tag}_s2", shift(verts, (ox, oy, 0.0)), idx,
color=SURFACE_COLORS[1], name=f"{tag} {k2}#2",
)
if curve_info is not None:
pts, label = curve_info
polylines = pts if isinstance(pts[0][0], list) else [pts]
for k, poly in enumerate(polylines):
suffix = f"_x{k}" if len(polylines) > 1 else "_x"
viewer.add_curve3d(
f"{tag}{suffix}", shift(poly, (ox, oy, 0.0)),
color=CURVE_COLOR, name=f"{tag} {label}",
)
with_curve.append(f"({row},{col})")
else:
pending.append(f"({row},{col})")
out = Path(args.out) if args.out else Path(__file__).with_suffix(".scene.json")
viewer.save(out)
print(f"saved {out} ({len(viewer.entities)} entities)")
print(f"cells with intersection curves ({len(with_curve)}): {' '.join(with_curve)}")
print(f"cells pending analytic intersection ({len(pending)}): {' '.join(pending)}")
if args.serve:
import threading
frontend = Path(args.frontend) if args.frontend else (
Path.home() / "Documentos/open_source/geomcore-viewer/frontend/dist"
)
server = viewer.serve(frontend, port=args.port)
print(f"serving at {server.url} — Ctrl-C to stop")
threading.Event().wait()
else:
print("view it: drag the .json onto the geomcore-viewer page, or re-run with --serve")
return 0
if __name__ == "__main__":
sys.exit(main())