Skip to content

Commit

Permalink
Add 3d representation for json-ld models
Browse files Browse the repository at this point in the history
  • Loading branch information
argenos committed Aug 22, 2024
1 parent 8b14685 commit 57c8d3c
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 16 deletions.
34 changes: 29 additions & 5 deletions src/floorplan_dsl/classes/fpm2/floorplan.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,28 @@ def __init__(
# TODO: Move to semantics processor?
self.compute_outer_wall_edges()
self.process_shape_semantics()
self.compute_3d_shape()


class Wall(WallSemantics):
def __init__(
self, parent, points, idx, thickness, height, shape=None, frame=None
self,
parent,
points,
idx,
thickness,
height,
shape=None,
frame=None,
shape_3d=None,
) -> None:
self.parent = parent
self.idx = idx
self.points = points
self.thickness = thickness
self.height = height
self.shape = shape
self.shape_3d = shape_3d

# Semantics
self.name = "{}-wall-{}".format(self.parent.name, self.idx)
Expand All @@ -72,43 +82,54 @@ class Feature(FeatureSemantics):


class Column(Feature):
def __init__(self, parent, name, shape, height, location, frame=None) -> None:
def __init__(
self, parent, name, shape, height, location, frame=None, shape_3d=None
) -> None:
self.parent = parent
self.name = name
self.shape = shape
self.height = height
self.location = location
self.shape_3d = shape_3d

if frame is None:
self.frame = Frame(self, "column-{}".format(self.name))

self.process_shape_semantics()
self.compute_3d_shape()


class Divider(Feature):
def __init__(self, parent, name, shape, height, location, frame=None) -> None:
def __init__(
self, parent, name, shape, height, location, frame=None, shape_3d=None
) -> None:
self.parent = parent
self.name = name
self.shape = shape
self.height = height
self.location = location
self.shape_3d = shape_3d

if frame is None:
self.frame = Frame(self, "divider-{}".format(self.name))

self.process_shape_semantics()
self.compute_3d_shape()


class Opening(OpeningSemantics):
pass


class Entryway(Opening):
def __init__(self, parent, name, shape, location, frame=None) -> None:
def __init__(
self, parent, name, shape, location, frame=None, shape_3d=None
) -> None:
self.parent = parent
self.name = name
self.shape = shape
self.location = location
self.shape_3d = shape_3d

if frame is None:
self.frame = Frame(self, "entryway-{}".format(self.name))
Expand All @@ -117,11 +138,14 @@ def __init__(self, parent, name, shape, location, frame=None) -> None:


class Window(Opening):
def __init__(self, parent, name, shape, location, frame=None) -> None:
def __init__(
self, parent, name, shape, location, frame=None, shape_3d=None
) -> None:
self.parent = parent
self.name = name
self.shape = shape
self.location = location
self.shape_3d = shape_3d

if frame is None:
self.frame = Frame(self, "window-{}".format(self.name))
Expand Down
81 changes: 80 additions & 1 deletion src/floorplan_dsl/classes/fpm2/geometry.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import itertools

from textx import textx_isinstance, get_metamodel

from floorplan_dsl.classes.fpm2.qudt import Length, Angle
Expand Down Expand Up @@ -88,7 +90,7 @@ class Polygon:

class Rectangle(Polygon):
def __init__(
self, parent, width, length, height, coordinates=None, points=None, center=True
self, parent, width, length, height, coordinates=None, points=None
) -> None:
self.parent = parent
self.width = width
Expand Down Expand Up @@ -136,3 +138,80 @@ def __init__(self, parent, coordinates, points=None) -> None:
self.parent = parent
self.coordinates = coordinates
self.points = points


class Polyhedron:

def __init__(
self,
parent,
base,
height=None,
thickness=None,
coordinates=None,
points=None,
faces=None,
) -> None:
self.parent = parent
self.base = base
self.height = height
self.thickness = thickness

self.name = "{}-polyhedron".format(self.parent.name)

# Semantics
self.coordinates = list()
self.coordinates.extend(self.base.coordinates)
for p in self.base.coordinates:
if self.height:
coord = PointCoordinate(self, p.x.value, p.y.value, self.height.value)
else:
# For windows and entryways use thickness
coord = PointCoordinate(self, p.x.value, self.thickness, p.z.value)

self.coordinates.append(coord)

# TODO Move to semantics. Temporarily here to avoid circular imports
self.points = list()
for i, c in enumerate(self.coordinates):
p = Point(self, "{}-corner-{}".format(self.parent.name, i))
self.points.append(p)

self.faces = list()
face_idx = self._get_face_index()
for face in face_idx:
points = [self.points[idx] for idx in face]
self.faces.append(Face(self, face, points=points))

def _get_face_index(self):
face_idx = list()
face_idx.append([self.coordinates.index(p) for p in self.base.coordinates])

top = self.coordinates[len(self.base.coordinates) :]
face_idx.append([self.coordinates.index(p) for p in top])
top.append(top[0])
top = list(itertools.pairwise(top))
bottom = self.base.coordinates
bottom.append(bottom[0])
for idx, b in enumerate(itertools.pairwise(bottom)):
t = top[idx]
p1, p2 = b
p3, p4 = t
face_idx.append(
[
self.coordinates.index(p1),
self.coordinates.index(p2),
self.coordinates.index(p4),
self.coordinates.index(p3),
]
)

return face_idx


class Face:
def __init__(self, parent, coord_idx, coordinates=None, points=None):
self.parent = parent
self.coord_idx = coord_idx
self.coordinates = coordinates
self.points = points
5 changes: 5 additions & 0 deletions src/floorplan_dsl/generators/fpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ def jsonld_floorplan_generator(
)
textx_jinja_generator(template_folder, output_path, context, overwrite=True)

template_folder = os.path.join(
this_folder, "../templates/json-ld/polyhedron.json.jinja"
)
textx_jinja_generator(template_folder, output_path, context, overwrite=True)


def v1_to_v2_converter(metamodel, model, output_path, overwrite, debug, **kwargs):
if not os.path.exists(output_path):
Expand Down
4 changes: 4 additions & 0 deletions src/floorplan_dsl/grammar/fpm2/floorplan.tx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Divider:
'height: ' height=LengthValue
'location:' location=FeatureLocation
(frame=Frame)? //Semantics
shape_3d=Polyhedron?
;

Column:
Expand All @@ -64,6 +65,7 @@ Column:
'height: ' height=LengthValue
'location:' location=FeatureLocation
(frame=Frame)? //Semantics
shape_3d=Polyhedron?
;

/* ------------------------------------------ */
Expand All @@ -78,13 +80,15 @@ Entryway:
'shape:' shape=Polygon
'location:' location=WallOpeningLocation
(frame=Frame)? //Semantics
shape_3d=Polyhedron?
;

Window:
'Window' name=ID ':'
'shape:' shape=Polygon
'location:'location=WallOpeningLocation
(frame=Frame)? //Semantics
shape_3d=Polyhedron?
;
/* ------------------------------------------ */
// Syntax: Locations
Expand Down
15 changes: 15 additions & 0 deletions src/floorplan_dsl/grammar/fpm2/geometry.tx
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ SimplePolygon:
points*=Point? //Semantics
;

Polytope: Polygon|Polyhedron;

Polyhedron:
base=Polygon
(height=LengthValue|thickness=LengthValue)
coordinates*=PointCoordinate?
faces*=Face?
points*=Point?
;

Face:
coord_idx+=INT
coordinates*=PointCoordinate?
points*=Point?
;
1 change: 1 addition & 0 deletions src/floorplan_dsl/grammar/fpm2/walls.tx
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ Wall:
('height: ' height=LengthValue)?
(frame=Frame)?
('shape:' shape=Polygon)?
shape_3d=Polyhedron?
idx=INT?
;
32 changes: 27 additions & 5 deletions src/floorplan_dsl/processors/semantics/fpm2.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Point,
Frame,
SimplePolygon,
Polyhedron,
)

from floorplan_dsl.utils.geometry import get_intersection, get_angle_between_vectors
Expand Down Expand Up @@ -50,21 +51,23 @@ def process_angle_units(v):


class FloorPlanElement:
def set_shape_points(self):
def set_shape_points(self, start=0):

points = list()
for i, c in enumerate(self.shape.coordinates):
for i, c in enumerate(self.shape.coordinates, start=start):
p = Point(self, "{}-corner-{}".format(self.name, i))
points.append((p))
points.append(p)

return points

def set_polytope_name(self):
self.shape.name = "{}-polygon".format(self.name)

def get_shape_point_positions(self):
def get_shape_point_positions(self, shape=None):
if shape is None:
shape = self.shape
position_coords = list()
for c, p in zip(self.shape.coordinates, self.shape.points):
for c, p in zip(shape.coordinates, shape.points):
coord = PositionCoordinate(self, c, p, self.frame)
position_coords.append(coord)
return position_coords
Expand Down Expand Up @@ -187,6 +190,10 @@ def compute_outer_wall_edges(self):
wall.compute_2d_shape(outer_edge_wrt_wall_frame)
wall.process_shape_semantics()

def compute_3d_shape(self):
for w in self.walls:
w.compute_3d_shape()


class WallSemantics(FloorPlanElement):

Expand Down Expand Up @@ -303,6 +310,10 @@ def get_pose_coord_wrt_parent(self):
self, self.frame, self.parent.frame, translation, rotation
)

def compute_3d_shape(self):
self.shape_3d = Polyhedron(self, self.shape, self.height)
self.shape_position_coords = self.get_shape_point_positions(self.shape_3d)


class FeatureSemantics(FloorPlanElement):
def process_location(self):
Expand All @@ -329,6 +340,10 @@ def get_pose_coord_wrt_location(self):
self, self.frame, self.location.wrt, translation, rotation
)

def compute_3d_shape(self):
self.shape_3d = Polyhedron(self, self.shape, self.height)
self.shape_position_coords = self.get_shape_point_positions(self.shape_3d)


class OpeningSemantics(FloorPlanElement):
def process_location(self):
Expand Down Expand Up @@ -359,3 +374,10 @@ def get_pose_coord_wrt_location(self):
return PoseCoordinate(
self, self.frame, self.location.walls[0], translation, rotation
)

def compute_3d_shape(self):
thickness = self.location.walls[0].thickness.value
if len(self.location.walls) == 2:
thickness = thickness + self.location.walls[1].thickness.value
self.shape_3d = Polyhedron(self, self.shape, thickness=thickness)
self.shape_position_coords = self.get_shape_point_positions(self.shape_3d)
2 changes: 2 additions & 0 deletions src/floorplan_dsl/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def fpv2_metamodel():
geom.PoseCoordinate,
geom.PositionCoordinate,
geom.EulerAngles,
geom.Polyhedron,
geom.Face,
],
)
floorplan_mm.register_obj_processors(
Expand Down
24 changes: 24 additions & 0 deletions src/floorplan_dsl/templates/json-ld/geometry/polyhedron.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"@id": "floorplan:{% block poly_id scoped %}{{ shape.name }}{% endblock %}",
"@type": "Polyhedron",
"points": [
{% for p in shape.points %}
"floorplan:{% block point_id scoped %}{{ p.name }}{% endblock %}"
{%- if not loop.last %},
{% endif %}
{% endfor +%}
],
"faces": [
{% for face in shape.faces %}
[
{% for p in face.points %}
"floorplan:{% block face_point_id scoped %}{{ p.name }}{% endblock %}"
{%- if not loop.last %},
{% endif %}
{% endfor +%}
]
{%- if not loop.last %},
{% endif %}
{% endfor +%}
]
}
Loading

0 comments on commit 57c8d3c

Please sign in to comment.