finish task_gen
This commit is contained in:
634
task_gen_dependencies/layout_2d.py
Normal file
634
task_gen_dependencies/layout_2d.py
Normal file
@@ -0,0 +1,634 @@
|
||||
import copy
|
||||
import random
|
||||
import time
|
||||
import numpy as np
|
||||
from rtree import index
|
||||
from scipy.interpolate import interp1d
|
||||
from shapely.geometry import Polygon, Point, box, LineString
|
||||
|
||||
class SolutionFound(Exception):
|
||||
def __init__(self, solution):
|
||||
self.solution = solution
|
||||
|
||||
class DFS_Solver_Floor:
|
||||
def __init__(self, grid_size, random_seed=0, max_duration=5, constraint_bouns=0.2):
|
||||
self.grid_size = grid_size # grid的边长(不是数量)
|
||||
self.random_seed = random_seed
|
||||
self.max_duration = max_duration # maximum allowed time in seconds
|
||||
self.constraint_bouns = constraint_bouns
|
||||
self.start_time = None
|
||||
self.solutions = []
|
||||
|
||||
# Define the functions in a dictionary to avoid if-else conditions
|
||||
self.func_dict = {
|
||||
"global": {"edge": self.place_edge},
|
||||
"relative": self.place_relative,
|
||||
"direction": self.place_face,
|
||||
"alignment": self.place_alignment_center,
|
||||
"distance": self.place_distance,
|
||||
}
|
||||
|
||||
self.constraint_type2weight = {
|
||||
"global": 1.0,
|
||||
"relative": 0.5,
|
||||
"direction": 0.5,
|
||||
"alignment": 0.5,
|
||||
"distance": 1.8,
|
||||
}
|
||||
|
||||
self.edge_bouns = 0.0 # worth more than one constraint
|
||||
|
||||
def get_solution(
|
||||
self, bounds, objects_list, constraints, initial_state, use_milp=False
|
||||
):
|
||||
self.start_time = time.time()
|
||||
if use_milp:
|
||||
# iterate through the constraints list
|
||||
# for each constraint type "distance", add the same constraint to the target object
|
||||
new_constraints = constraints.copy()
|
||||
for object_name, object_constraints in constraints.items():
|
||||
for constraint in object_constraints:
|
||||
if constraint["type"] == "distance":
|
||||
target_object_name = constraint["target"]
|
||||
if target_object_name in constraints.keys():
|
||||
# if there is already a distance constraint of target object_name, continue
|
||||
if any(
|
||||
constraint["type"] == "distance"
|
||||
and constraint["target"] == object_name
|
||||
for constraint in constraints[target_object_name]
|
||||
):
|
||||
continue
|
||||
new_constraint = constraint.copy()
|
||||
new_constraint["target"] = object_name
|
||||
new_constraints[target_object_name].append(new_constraint)
|
||||
# iterate through the constraints list
|
||||
# for each constraint type "left of" or "right of", add the same constraint to the target object
|
||||
# for object_name, object_constraints in constraints.items():
|
||||
# for constraint in object_constraints: if constraint["type"] == "relative":
|
||||
# if constraint["constraint"] == "left of":
|
||||
constraints = new_constraints
|
||||
|
||||
print(f"Time taken: {time.time() - self.start_time}")
|
||||
|
||||
else:
|
||||
grid_points = self.create_grids(bounds)
|
||||
grid_points = self._remove_points(grid_points, initial_state)
|
||||
try:
|
||||
self._dfs(
|
||||
bounds, objects_list, constraints, grid_points, initial_state, 30
|
||||
)
|
||||
except SolutionFound as e:
|
||||
print(f"Time taken: {time.time() - self.start_time}")
|
||||
|
||||
print(f"Number of solutions found: {len(self.solutions)}")
|
||||
max_solution = self._get_max_solution(self.solutions)
|
||||
|
||||
return max_solution
|
||||
|
||||
def _get_max_solution(self, solutions):
|
||||
path_weights = []
|
||||
for solution in solutions:
|
||||
path_weights.append(sum([obj[-1] for obj in solution.values()]))
|
||||
max_index = np.argmax(path_weights)
|
||||
return solutions[max_index]
|
||||
|
||||
def _dfs(
|
||||
self,
|
||||
room_poly,
|
||||
objects_list,
|
||||
constraints,
|
||||
grid_points,
|
||||
placed_objects,
|
||||
branch_factor,
|
||||
):
|
||||
if len(objects_list) == 0:
|
||||
self.solutions.append(placed_objects)
|
||||
return placed_objects
|
||||
|
||||
if time.time() - self.start_time > self.max_duration:
|
||||
print(f"Time limit reached.")
|
||||
raise SolutionFound(self.solutions)
|
||||
|
||||
object_name, object_dim = objects_list[0]
|
||||
placements = self._get_possible_placements(
|
||||
room_poly, object_dim, constraints[object_name], grid_points, placed_objects
|
||||
)
|
||||
|
||||
if len(placements) == 0 and len(placed_objects) != 0:
|
||||
self.solutions.append(placed_objects)
|
||||
|
||||
paths = []
|
||||
if branch_factor > 1:
|
||||
random.shuffle(placements) # shuffle the placements of the first object
|
||||
|
||||
for placement in placements[:branch_factor]:
|
||||
placed_objects_updated = copy.deepcopy(placed_objects)
|
||||
placed_objects_updated[object_name] = placement
|
||||
grid_points_updated = self._remove_points(
|
||||
grid_points, placed_objects_updated
|
||||
)
|
||||
|
||||
sub_paths = self._dfs(
|
||||
room_poly,
|
||||
objects_list[1:],
|
||||
constraints,
|
||||
grid_points_updated,
|
||||
placed_objects_updated,
|
||||
1,
|
||||
)
|
||||
paths.extend(sub_paths)
|
||||
|
||||
return paths
|
||||
|
||||
def _get_possible_placements(
|
||||
self, room_poly, object_dim, constraints, grid_points, placed_objects
|
||||
):
|
||||
solutions = self.filter_collision(
|
||||
placed_objects, self.get_all_solutions(room_poly, grid_points, object_dim)
|
||||
)
|
||||
solutions = self.filter_facing_wall(room_poly, solutions, object_dim)
|
||||
edge_solutions = self.place_edge(
|
||||
room_poly, copy.deepcopy(solutions), object_dim
|
||||
)
|
||||
|
||||
if len(edge_solutions) == 0:
|
||||
return edge_solutions
|
||||
|
||||
global_constraint = next(
|
||||
(
|
||||
constraint
|
||||
for constraint in constraints
|
||||
if constraint["type"] == "global"
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if global_constraint is None:
|
||||
global_constraint = {"type": "global", "constraint": "edge"}
|
||||
|
||||
if global_constraint["constraint"] == "edge":
|
||||
candidate_solutions = copy.deepcopy(
|
||||
edge_solutions
|
||||
) # edge is hard constraint
|
||||
else:
|
||||
if len(constraints) > 1:
|
||||
candidate_solutions = (
|
||||
solutions + edge_solutions
|
||||
) # edge is soft constraint
|
||||
else:
|
||||
candidate_solutions = copy.deepcopy(solutions) # the first object
|
||||
|
||||
candidate_solutions = self.filter_collision(
|
||||
placed_objects, candidate_solutions
|
||||
) # filter again after global constraint
|
||||
|
||||
if candidate_solutions == []:
|
||||
return candidate_solutions
|
||||
random.shuffle(candidate_solutions)
|
||||
placement2score = {
|
||||
tuple(solution[:3]): solution[-1] for solution in candidate_solutions
|
||||
}
|
||||
|
||||
# add a bias to edge solutions
|
||||
for solution in candidate_solutions:
|
||||
if solution in edge_solutions and len(constraints) >= 1:
|
||||
placement2score[tuple(solution[:3])] += self.edge_bouns
|
||||
|
||||
for constraint in constraints:
|
||||
if "target" not in constraint:
|
||||
continue
|
||||
|
||||
func = self.func_dict.get(constraint["type"])
|
||||
valid_solutions = func(
|
||||
constraint["constraint"],
|
||||
placed_objects[constraint["target"]],
|
||||
candidate_solutions,
|
||||
)
|
||||
|
||||
weight = self.constraint_type2weight[constraint["type"]]
|
||||
if constraint["type"] == "distance":
|
||||
for solution in valid_solutions:
|
||||
bouns = solution[-1]
|
||||
placement2score[tuple(solution[:3])] += bouns * weight
|
||||
else:
|
||||
for solution in valid_solutions:
|
||||
placement2score[tuple(solution[:3])] += (
|
||||
self.constraint_bouns * weight
|
||||
)
|
||||
|
||||
# normalize the scores
|
||||
for placement in placement2score:
|
||||
placement2score[placement] /= max(len(constraints), 1)
|
||||
|
||||
sorted_placements = sorted(
|
||||
placement2score, key=placement2score.get, reverse=True
|
||||
)
|
||||
sorted_solutions = [
|
||||
list(placement) + [placement2score[placement]]
|
||||
for placement in sorted_placements
|
||||
]
|
||||
|
||||
return sorted_solutions
|
||||
|
||||
def create_grids(self, room_poly):
|
||||
# get the min and max bounds of the room
|
||||
min_x, min_z, max_x, max_z = room_poly.bounds
|
||||
|
||||
# create grid points
|
||||
grid_points = []
|
||||
|
||||
for x in range(int(min_x), int(max_x), self.grid_size):
|
||||
for y in range(int(min_z), int(max_z), self.grid_size):
|
||||
point = Point(x, y)
|
||||
if room_poly.contains(point):
|
||||
grid_points.append((x, y))
|
||||
|
||||
return grid_points
|
||||
|
||||
def _remove_points(self, grid_points, objects_dict):
|
||||
# Create an r-tree index
|
||||
idx = index.Index()
|
||||
|
||||
# Populate the index with bounding boxes of the objects
|
||||
for i, (_, _, obj, _) in enumerate(objects_dict.values()):
|
||||
idx.insert(i, Polygon(obj).bounds)
|
||||
|
||||
# Create Shapely Polygon objects only once
|
||||
polygons = [Polygon(obj) for _, _, obj, _ in objects_dict.values()]
|
||||
|
||||
valid_points = []
|
||||
|
||||
for point in grid_points:
|
||||
p = Point(point)
|
||||
# Get a list of potential candidates
|
||||
candidates = [polygons[i] for i in idx.intersection(p.bounds)]
|
||||
# Check if point is in any of the candidate polygons
|
||||
if not any(candidate.contains(p) for candidate in candidates):
|
||||
valid_points.append(point)
|
||||
|
||||
return valid_points
|
||||
|
||||
def get_all_solutions(self, room_poly, grid_points, object_dim):
|
||||
obj_length, obj_width = object_dim
|
||||
obj_half_length, obj_half_width = obj_length / 2, obj_width / 2
|
||||
|
||||
rotation_adjustments = {
|
||||
0: ((-obj_half_length, -obj_half_width), (obj_half_length, obj_half_width)),
|
||||
90: (
|
||||
(-obj_half_width, -obj_half_length),
|
||||
(obj_half_width, obj_half_length),
|
||||
),
|
||||
180: (
|
||||
(-obj_half_length, obj_half_width),
|
||||
(obj_half_length, -obj_half_width),
|
||||
),
|
||||
270: (
|
||||
(obj_half_width, -obj_half_length),
|
||||
(-obj_half_width, obj_half_length),
|
||||
),
|
||||
}
|
||||
|
||||
solutions = []
|
||||
for rotation in [0, 90, 180, 270]:
|
||||
for point in grid_points:
|
||||
center_x, center_y = point
|
||||
lower_left_adjustment, upper_right_adjustment = rotation_adjustments[
|
||||
rotation
|
||||
]
|
||||
lower_left = (
|
||||
center_x + lower_left_adjustment[0],
|
||||
center_y + lower_left_adjustment[1],
|
||||
)
|
||||
upper_right = (
|
||||
center_x + upper_right_adjustment[0],
|
||||
center_y + upper_right_adjustment[1],
|
||||
)
|
||||
obj_box = box(*lower_left, *upper_right)
|
||||
|
||||
if room_poly.contains(obj_box):
|
||||
solutions.append(
|
||||
[point, rotation, tuple(obj_box.exterior.coords[:]), 1]
|
||||
)
|
||||
|
||||
return solutions
|
||||
|
||||
def filter_collision(self, objects_dict, solutions):
|
||||
valid_solutions = []
|
||||
object_polygons = [
|
||||
Polygon(obj_coords) for _, _, obj_coords, _ in list(objects_dict.values())
|
||||
]
|
||||
for solution in solutions:
|
||||
sol_obj_coords = solution[2]
|
||||
sol_obj = Polygon(sol_obj_coords)
|
||||
if not any(sol_obj.intersects(obj) for obj in object_polygons):
|
||||
valid_solutions.append(solution)
|
||||
return valid_solutions
|
||||
|
||||
def filter_facing_wall(self, room_poly, solutions, obj_dim):
|
||||
valid_solutions = []
|
||||
obj_width = obj_dim[1]
|
||||
obj_half_width = obj_width / 2
|
||||
|
||||
front_center_adjustments = {
|
||||
0: (0, obj_half_width),
|
||||
90: (obj_half_width, 0),
|
||||
180: (0, -obj_half_width),
|
||||
270: (-obj_half_width, 0),
|
||||
}
|
||||
|
||||
valid_solutions = []
|
||||
for solution in solutions:
|
||||
center_x, center_y = solution[0]
|
||||
rotation = solution[1]
|
||||
|
||||
front_center_adjustment = front_center_adjustments[rotation]
|
||||
front_center_x, front_center_y = (
|
||||
center_x + front_center_adjustment[0],
|
||||
center_y + front_center_adjustment[1],
|
||||
)
|
||||
|
||||
front_center_distance = room_poly.boundary.distance(
|
||||
Point(front_center_x, front_center_y)
|
||||
)
|
||||
|
||||
if front_center_distance >= 30: # TODO: make this a parameter
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_edge(self, room_poly, solutions, obj_dim):
|
||||
valid_solutions = []
|
||||
obj_width = obj_dim[1]
|
||||
obj_half_width = obj_width / 2
|
||||
|
||||
back_center_adjustments = {
|
||||
0: (0, -obj_half_width),
|
||||
90: (-obj_half_width, 0),
|
||||
180: (0, obj_half_width),
|
||||
270: (obj_half_width, 0),
|
||||
}
|
||||
|
||||
for solution in solutions:
|
||||
center_x, center_y = solution[0]
|
||||
rotation = solution[1]
|
||||
|
||||
back_center_adjustment = back_center_adjustments[rotation]
|
||||
back_center_x, back_center_y = (
|
||||
center_x + back_center_adjustment[0],
|
||||
center_y + back_center_adjustment[1],
|
||||
)
|
||||
|
||||
back_center_distance = room_poly.boundary.distance(
|
||||
Point(back_center_x, back_center_y)
|
||||
)
|
||||
center_distance = room_poly.boundary.distance(Point(center_x, center_y))
|
||||
|
||||
if (
|
||||
back_center_distance <= self.grid_size
|
||||
and back_center_distance < center_distance
|
||||
):
|
||||
solution[-1] += self.constraint_bouns
|
||||
# valid_solutions.append(solution) # those are still valid solutions, but we need to move the object to the edge
|
||||
|
||||
# move the object to the edge
|
||||
center2back_vector = np.array(
|
||||
[back_center_x - center_x, back_center_y - center_y]
|
||||
)
|
||||
center2back_vector /= np.linalg.norm(center2back_vector)
|
||||
offset = center2back_vector * (
|
||||
back_center_distance + 4.5
|
||||
) # add a small distance to avoid the object cross the wall
|
||||
solution[0] = (center_x + offset[0], center_y + offset[1])
|
||||
solution[2] = (
|
||||
(solution[2][0][0] + offset[0], solution[2][0][1] + offset[1]),
|
||||
(solution[2][1][0] + offset[0], solution[2][1][1] + offset[1]),
|
||||
(solution[2][2][0] + offset[0], solution[2][2][1] + offset[1]),
|
||||
(solution[2][3][0] + offset[0], solution[2][3][1] + offset[1]),
|
||||
)
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_corner(self, room_poly, solutions, obj_dim):
|
||||
obj_length, obj_width = obj_dim
|
||||
obj_half_length, _ = obj_length / 2, obj_width / 2
|
||||
|
||||
rotation_center_adjustments = {
|
||||
0: ((-obj_half_length, 0), (obj_half_length, 0)),
|
||||
90: ((0, obj_half_length), (0, -obj_half_length)),
|
||||
180: ((obj_half_length, 0), (-obj_half_length, 0)),
|
||||
270: ((0, -obj_half_length), (0, obj_half_length)),
|
||||
}
|
||||
|
||||
edge_solutions = self.place_edge(room_poly, solutions, obj_dim)
|
||||
|
||||
valid_solutions = []
|
||||
|
||||
for solution in edge_solutions:
|
||||
(center_x, center_y), rotation = solution[:2]
|
||||
(dx_left, dy_left), (dx_right, dy_right) = rotation_center_adjustments[
|
||||
rotation
|
||||
]
|
||||
|
||||
left_center_x, left_center_y = center_x + dx_left, center_y + dy_left
|
||||
right_center_x, right_center_y = center_x + dx_right, center_y + dy_right
|
||||
|
||||
left_center_distance = room_poly.boundary.distance(
|
||||
Point(left_center_x, left_center_y)
|
||||
)
|
||||
right_center_distance = room_poly.boundary.distance(
|
||||
Point(right_center_x, right_center_y)
|
||||
)
|
||||
|
||||
if min(left_center_distance, right_center_distance) < self.grid_size:
|
||||
solution[-1] += self.constraint_bouns
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_relative(self, place_type, target_object, solutions):
|
||||
valid_solutions = []
|
||||
_, target_rotation, target_coords, _ = target_object
|
||||
target_polygon = Polygon(target_coords)
|
||||
|
||||
min_x, min_y, max_x, max_y = target_polygon.bounds
|
||||
mean_x = (min_x + max_x) / 2
|
||||
mean_y = (min_y + max_y) / 2
|
||||
|
||||
comparison_dict = {
|
||||
"left of": {
|
||||
0: lambda sol_center: sol_center[0] < min_x
|
||||
and min_y <= sol_center[1] <= max_y,
|
||||
90: lambda sol_center: sol_center[1] > max_y
|
||||
and min_x <= sol_center[0] <= max_x,
|
||||
180: lambda sol_center: sol_center[0] > max_x
|
||||
and min_y <= sol_center[1] <= max_y,
|
||||
270: lambda sol_center: sol_center[1] < min_y
|
||||
and min_x <= sol_center[0] <= max_x,
|
||||
},
|
||||
"right of": {
|
||||
0: lambda sol_center: sol_center[0] > max_x
|
||||
and min_y <= sol_center[1] <= max_y,
|
||||
90: lambda sol_center: sol_center[1] < min_y
|
||||
and min_x <= sol_center[0] <= max_x,
|
||||
180: lambda sol_center: sol_center[0] < min_x
|
||||
and min_y <= sol_center[1] <= max_y,
|
||||
270: lambda sol_center: sol_center[1] > max_y
|
||||
and min_x <= sol_center[0] <= max_x,
|
||||
},
|
||||
"in front of": {
|
||||
0: lambda sol_center: sol_center[1] > max_y
|
||||
and mean_x - self.grid_size
|
||||
< sol_center[0]
|
||||
< mean_x + self.grid_size, # in front of and centered
|
||||
90: lambda sol_center: sol_center[0] > max_x
|
||||
and mean_y - self.grid_size < sol_center[1] < mean_y + self.grid_size,
|
||||
180: lambda sol_center: sol_center[1] < min_y
|
||||
and mean_x - self.grid_size < sol_center[0] < mean_x + self.grid_size,
|
||||
270: lambda sol_center: sol_center[0] < min_x
|
||||
and mean_y - self.grid_size < sol_center[1] < mean_y + self.grid_size,
|
||||
},
|
||||
"behind": {
|
||||
0: lambda sol_center: sol_center[1] < min_y
|
||||
and min_x <= sol_center[0] <= max_x,
|
||||
90: lambda sol_center: sol_center[0] < min_x
|
||||
and min_y <= sol_center[1] <= max_y,
|
||||
180: lambda sol_center: sol_center[1] > max_y
|
||||
and min_x <= sol_center[0] <= max_x,
|
||||
270: lambda sol_center: sol_center[0] > max_x
|
||||
and min_y <= sol_center[1] <= max_y,
|
||||
},
|
||||
"side of": {
|
||||
0: lambda sol_center: min_y <= sol_center[1] <= max_y,
|
||||
90: lambda sol_center: min_x <= sol_center[0] <= max_x,
|
||||
180: lambda sol_center: min_y <= sol_center[1] <= max_y,
|
||||
270: lambda sol_center: min_x <= sol_center[0] <= max_x,
|
||||
},
|
||||
}
|
||||
|
||||
compare_func = comparison_dict.get(place_type).get(target_rotation)
|
||||
|
||||
for solution in solutions:
|
||||
sol_center = solution[0]
|
||||
|
||||
if compare_func(sol_center):
|
||||
solution[-1] += self.constraint_bouns
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_distance(self, distance_type, target_object, solutions):
|
||||
target_coords = target_object[2]
|
||||
target_poly = Polygon(target_coords)
|
||||
distances = []
|
||||
valid_solutions = []
|
||||
for solution in solutions:
|
||||
sol_coords = solution[2]
|
||||
sol_poly = Polygon(sol_coords)
|
||||
distance = target_poly.distance(sol_poly)
|
||||
distances.append(distance)
|
||||
|
||||
solution[-1] = distance
|
||||
valid_solutions.append(solution)
|
||||
|
||||
min_distance = min(distances)
|
||||
max_distance = max(distances)
|
||||
|
||||
if distance_type == "near":
|
||||
if min_distance < 80:
|
||||
points = [(min_distance, 1), (80, 0), (max_distance, 0)]
|
||||
else:
|
||||
points = [(min_distance, 0), (max_distance, 0)]
|
||||
|
||||
elif distance_type == "far":
|
||||
points = [(min_distance, 0), (max_distance, 1)]
|
||||
|
||||
x = [point[0] for point in points]
|
||||
y = [point[1] for point in points]
|
||||
|
||||
f = interp1d(x, y, kind="linear", fill_value="extrapolate")
|
||||
|
||||
for solution in valid_solutions:
|
||||
distance = solution[-1]
|
||||
solution[-1] = float(f(distance))
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_face(self, face_type, target_object, solutions):
|
||||
if face_type == "face to":
|
||||
return self.place_face_to(target_object, solutions)
|
||||
|
||||
elif face_type == "face same as":
|
||||
return self.place_face_same(target_object, solutions)
|
||||
|
||||
elif face_type == "face opposite to":
|
||||
return self.place_face_opposite(target_object, solutions)
|
||||
|
||||
def place_face_to(self, target_object, solutions):
|
||||
# Define unit vectors for each rotation
|
||||
unit_vectors = {
|
||||
0: np.array([0.0, 1.0]), # Facing up
|
||||
90: np.array([1.0, 0.0]), # Facing right
|
||||
180: np.array([0.0, -1.0]), # Facing down
|
||||
270: np.array([-1.0, 0.0]), # Facing left
|
||||
}
|
||||
|
||||
target_coords = target_object[2]
|
||||
target_poly = Polygon(target_coords)
|
||||
|
||||
valid_solutions = []
|
||||
|
||||
for solution in solutions:
|
||||
sol_center = solution[0]
|
||||
sol_rotation = solution[1]
|
||||
|
||||
# Define an arbitrarily large point in the direction of the solution's rotation
|
||||
far_point = sol_center + 1e6 * unit_vectors[sol_rotation]
|
||||
|
||||
# Create a half-line from the solution's center to the far point
|
||||
half_line = LineString([sol_center, far_point])
|
||||
|
||||
# Check if the half-line intersects with the target polygon
|
||||
if half_line.intersects(target_poly):
|
||||
solution[-1] += self.constraint_bouns
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_face_same(self, target_object, solutions):
|
||||
target_rotation = target_object[1]
|
||||
valid_solutions = []
|
||||
|
||||
for solution in solutions:
|
||||
sol_rotation = solution[1]
|
||||
if sol_rotation == target_rotation:
|
||||
solution[-1] += self.constraint_bouns
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_face_opposite(self, target_object, solutions):
|
||||
target_rotation = (target_object[1] + 180) % 360
|
||||
valid_solutions = []
|
||||
|
||||
for solution in solutions:
|
||||
sol_rotation = solution[1]
|
||||
if sol_rotation == target_rotation:
|
||||
solution[-1] += self.constraint_bouns
|
||||
valid_solutions.append(solution)
|
||||
|
||||
return valid_solutions
|
||||
|
||||
def place_alignment_center(self, alignment_type, target_object, solutions):
|
||||
target_center = target_object[0]
|
||||
valid_solutions = []
|
||||
eps = 5
|
||||
for solution in solutions:
|
||||
sol_center = solution[0]
|
||||
if (
|
||||
abs(sol_center[0] - target_center[0]) < eps
|
||||
or abs(sol_center[1] - target_center[1]) < eps
|
||||
):
|
||||
solution[-1] += self.constraint_bouns
|
||||
valid_solutions.append(solution)
|
||||
return valid_solutions
|
||||
Reference in New Issue
Block a user