diff --git a/infinigen/assets/utils/shapes.py b/infinigen/assets/utils/shapes.py index f9dc7575..82f35671 100644 --- a/infinigen/assets/utils/shapes.py +++ b/infinigen/assets/utils/shapes.py @@ -121,7 +121,7 @@ def obj2polygon(obj): for p in obj.data.polygons ] ) - return shapely.make_valid(shapely.simplify(p, 1e-6)) + return shapely.ops.orient(shapely.make_valid(shapely.simplify(p, 1e-6))) def buffer(p, distance): diff --git a/infinigen/core/constraints/constraint_language/constants.py b/infinigen/core/constraints/constraint_language/constants.py index 5704f709..c1178da5 100644 --- a/infinigen/core/constraints/constraint_language/constants.py +++ b/infinigen/core/constraints/constraint_language/constants.py @@ -150,7 +150,7 @@ def canonicalize(self, p): break if not is_valid_polygon(p): raise NotImplementedError("Invalid polygon") - return p + return orient(p) except AttributeError: raise NotImplementedError("Invalid multi polygon") diff --git a/infinigen/core/constraints/example_solver/room/contour.py b/infinigen/core/constraints/example_solver/room/contour.py index 7d822ad2..4a7277f1 100644 --- a/infinigen/core/constraints/example_solver/room/contour.py +++ b/infinigen/core/constraints/example_solver/room/contour.py @@ -142,12 +142,14 @@ def decorate(self, state): quad_segs = ( 1 if corner_func == "sharp" else np.random.randint(4, 7) ) - exterior_ = ( + exterior_ = self.constants.canonicalize( exterior.difference(cutter) .union(shapely.Point(q).buffer(length, quad_segs=quad_segs)) .buffer(0) ) - new = state[k].polygon.intersection(exterior_).buffer(0) + new = self.constants.canonicalize( + state[k].polygon.intersection(exterior_).buffer(0) + ) if all( new.buffer(-m + 1e-2).geom_type == "Polygon" for m in np.linspace(0, 0.75, 4) diff --git a/infinigen/core/constraints/example_solver/room/solidifier.py b/infinigen/core/constraints/example_solver/room/solidifier.py index 82215d7c..27a6f733 100644 --- a/infinigen/core/constraints/example_solver/room/solidifier.py +++ b/infinigen/core/constraints/example_solver/room/solidifier.py @@ -42,6 +42,7 @@ from infinigen.core.util.random import random_general as rg from .base import RoomGraph, room_type, valid_rooms +from .utils import mls_ccw logger = logging.getLogger(__name__) @@ -210,7 +211,8 @@ def solidify(self, state): } exterior = next(k for k in state.objs if room_type(k) == Semantics.Exterior) exterior_edges = { - r.target_name: canonicalize_mls(r.value) for r in state[exterior].relations + r.target_name: mls_ccw(canonicalize_mls(r.value), state, r.target_name) + for r in state[exterior].relations } exterior_buffer = shapely.simplify( state[exterior].polygon.buffer(-wt / 2 - _eps, join_style="mitre"), 1e-3 diff --git a/infinigen/core/constraints/example_solver/room/utils.py b/infinigen/core/constraints/example_solver/room/utils.py index 84da9855..960d8218 100644 --- a/infinigen/core/constraints/example_solver/room/utils.py +++ b/infinigen/core/constraints/example_solver/room/utils.py @@ -50,6 +50,18 @@ def update_exterior(state: State, i: str): r.value = MultiLineString(v) +def mls_ccw(mls: MultiLineString, state: State, i: str): + exterior = state[i].polygon.exterior + coords = np.array(exterior.coords[:-1]) + mls_ = [] + for ls in mls.geoms: + u, v = ls.coords[:2] + x = np.argmin(np.linalg.norm(coords - np.array(u)[np.newaxis], axis=-1)) + y = np.argmin(np.linalg.norm(coords - np.array(v)[np.newaxis], axis=-1)) + mls_.append(ls if x < y else shapely.reverse(ls)) + return MultiLineString(mls_) + + def update_staircase(state: State, i: str): pholder = room_name(Semantics.Staircase, room_level(i)) r = next(r for r in state[pholder].relations if r.target_name == i) diff --git a/infinigen/core/constraints/example_solver/state_def.py b/infinigen/core/constraints/example_solver/state_def.py index 3ede7e61..3754b311 100644 --- a/infinigen/core/constraints/example_solver/state_def.py +++ b/infinigen/core/constraints/example_solver/state_def.py @@ -74,7 +74,7 @@ def __repr__(self): obj = self.obj tags = self.tags relations = self.relations - return f"{self.__class__.__name__}(obj.name={obj.name if obj is not None else obj.name}, polygon={self.polygon}, {tags=}, {relations=})" + return f"{self.__class__.__name__}(obj.name={obj.name if obj is not None else None}, polygon={self.polygon}, {tags=}, {relations=})" @dataclass