diff --git a/.gitignore b/.gitignore index 254321a6..c63bc14d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ blender blender.tar.xz Blender.app +.nfs* + *.c .coverage 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/decorate.py b/infinigen/core/constraints/example_solver/room/decorate.py index 909c04a4..a567f575 100644 --- a/infinigen/core/constraints/example_solver/room/decorate.py +++ b/infinigen/core/constraints/example_solver/room/decorate.py @@ -45,7 +45,7 @@ from infinigen.core.surface import write_attr_data from infinigen.core.util import blender as butil from infinigen.core.util.blender import deep_clone_obj -from infinigen.core.util.math import int_hash +from infinigen.core.util.math import FixedSeed, int_hash from infinigen.core.util.random import log_uniform from infinigen.core.util.random import random_general as rg @@ -93,7 +93,7 @@ def split_rooms(rooms_meshed: list[bpy.types.Object]): def import_material(factory_name): - with gin.unlock_config(): + with gin.unlock_config(), FixedSeed(0): try: return importlib.import_module(f"infinigen.assets.materials.{factory_name}") except ImportError: diff --git a/infinigen/core/constraints/example_solver/room/solidifier.py b/infinigen/core/constraints/example_solver/room/solidifier.py index 82215d7c..0077cba3 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 @@ -538,7 +540,7 @@ def make_door_cutter(self, mls, direction): wt = self.constants.wall_thickness cutter.scale = ( self.constants.door_width / 2, - self.constants.door_width / 2 + wt, + self.constants.door_width + wt / 2, self.constants.door_size / 2 - _snap / 2, ) cutter.location[-1] += _snap / 2 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 diff --git a/infinigen_examples/generate_individual_assets.py b/infinigen_examples/generate_individual_assets.py index bcc7b810..cd954dcf 100644 --- a/infinigen_examples/generate_individual_assets.py +++ b/infinigen_examples/generate_individual_assets.py @@ -290,8 +290,9 @@ def build_and_save_asset(payload: dict): ) if args.cam_center > 0 and asset: - co = read_base_co(asset) + asset.location - center.location = (np.amin(co, 0) + np.amax(co, 0)) / 2 + co = read_base_co(asset) + location = (np.amin(co, 0) + np.amax(co, 0)) / 2 + center.location = (np.array(asset.matrix_world) @ np.array([*location, 1]))[:-1] center.location[-1] += args.cam_zoff if args.cam_dist <= 0 and asset: diff --git a/tests/integration/integration_test_parse_logs.py b/tests/integration/compare.py similarity index 89% rename from tests/integration/integration_test_parse_logs.py rename to tests/integration/compare.py index a3084216..eca7c474 100644 --- a/tests/integration/integration_test_parse_logs.py +++ b/tests/integration/compare.py @@ -53,15 +53,11 @@ def parse_scene_log( "blendergt": [], } - log_folder = scene_path/"logs" - coarse_folder = scene_path/"coarse" - fine_folder = scene_path/"fine" - - if not ( - log_folder.exists() - and coarse_folder.exists() - and fine_folder.exists() - ): + log_folder = scene_path / "logs" + coarse_folder = scene_path / "coarse" + fine_folder = scene_path / "fine" + + if not (log_folder.exists() and coarse_folder.exists() and fine_folder.exists()): return ret_dict for filepath in log_folder.glob("*.err"): @@ -133,11 +129,12 @@ def parse_scene_log( "gt_time": gt_time, } - fine_poly = parse_poly_file(fine_folder/"polycounts.txt") + fine_poly = parse_poly_file(fine_folder / "polycounts.txt") ret_dict["gen_triangles"] = fine_poly.get("Triangles", "NAN") return ret_dict + def parse_poly_file(path): res = {} @@ -153,18 +150,20 @@ def parse_poly_file(path): return res -def parse_asset_log(asset_path): - poly = parse_poly_file(asset_path/"polycounts.txt") +def parse_asset_log(asset_path): + poly = parse_poly_file(asset_path / "polycounts.txt") return { "triangles": poly.get("Tris", "NAN"), "gen_mem": poly.get("Memory", "NAN"), } + def format_stats(d): return ", ".join(f"{k}: {v}" for k, v in d.items()) + def parse_run_df(run_path: Path): runs = { "_".join((x.name.split("_")[2:])): x for x in run_path.iterdir() if x.is_dir() @@ -255,16 +254,14 @@ def find_run(base_path: str, run: str) -> Path: def fuzzy_merge(dfA, dfB, keyA, keyB, threshold=1): - from rapidfuzz import fuzz, process matches_A = [] matches_B = [] - def preproc(x): - x = x.split('/')[-1] - x = re.sub(r'(?