in mujoco_worldgen/util/placement.py [0:0]
def place_boxes(random_state, boxes, width, height, placement_margin=0.0):
'''
Tries to randomly place rectangular boxes between (0,0) and (width, height)
boxes - list of dictionaries, each with keys 'size' and 'placement_xy'
'size' must be (x,y)
'placement_xy' is either None or (x_frac, y_frac) between 0 and 1
(0, 0) is left lower corner, (.5, .5) is the center, etc
width, height - dimensions of the outer bound to place boxes in
placement_margin - minimum distance (orthogonal) between placed boxes
Returns list of box positions (x,y) from left lower corner, or None if
no solution found.
'''
width = float(width)
height = float(height)
assert width > 0 and height > 0, "invalid width, height"
area = 0
sizes = [box["size"] for box in boxes]
for s in sizes:
if s[0] > width - 2 * placement_margin or s[0] <= 0.0 or \
s[1] > height - 2 * placement_margin or s[1] <= 0.0:
return None
area += (s[0] + placement_margin // 2) * (s[1] + placement_margin // 2)
if area > 0.6 * (width - placement_margin) * (height - placement_margin) and len(sizes) > 1:
return None
if area > width * height:
return None
a_edge, b_edge = _get_edge_constraints(sizes, width, height, placement_margin)
for _ in range(10):
def get_matrices(xy):
a_eq = []
b_eq = []
for idx, box in enumerate(boxes):
if box["placement_xy"] is not None:
xy[idx] = box["placement_xy"][0] * (width - box["size"][0])
xy[idx + len(sizes)] = box["placement_xy"][1] * \
(height - box["size"][1])
for i in [idx, idx + len(sizes)]:
a = np.zeros(2 * len(sizes))
a[i] = 1.0
a_eq.append(a)
b_eq.append(xy[i])
a_pairwise, b_pairwise, violated = _get_pairwise_constraints(xy, boxes, placement_margin)
for idx, v in enumerate(violated):
if not v:
for i in [idx, idx + len(sizes)]:
a = np.zeros(2 * len(sizes))
a[i] = 1.0
a_eq.append(a)
b_eq.append(xy[i])
if len(a_eq) > 0:
a_eq = np.stack(a_eq)
b_eq = np.stack(b_eq)
else:
a_eq = None
b_eq = None
return a_eq, b_eq, a_pairwise, b_pairwise, violated
best_xy = None
best_violated = [True for _ in range(100)]
for _ in range(10):
xy = _get_random_xy(random_state, boxes, width, height, placement_margin)
a_eq, b_eq, a_pairwise, b_pairwise, violated = get_matrices(xy)
if sum(violated) < sum(best_violated):
best_xy = xy
best_violated = violated
xy = best_xy
a_eq, b_eq, a_pairwise, b_pairwise, violated = get_matrices(xy)
# If it's not violated, than further optimization is not needed.
if sum(violated) > -1e-4:
return [(a, b) for a, b in zip(xy[:len(sizes)], xy[len(sizes):])]
if len(a_pairwise) == 0:
a = a_edge
b = b_edge
else:
a = np.concatenate([a_edge, np.stack(a_pairwise)], 0)
b = np.concatenate([b_edge, np.array(b_pairwise)], 0)
random = random_state.uniform(-1, 1, 2 * len(sizes))
sol = scipy.optimize.linprog(random, A_ub=-a, b_ub=-b, A_eq=a_eq, b_eq=b_eq)
if sol.success:
return _further_randomize(random_state, boxes, a, b, sol.x)
return None