Reputation: 1437
I am using matplotlib to plot 3D image. when plotting, the length/width/height is automatically scaled which is not proportional to its actual value, i.e. the length is 6 times bigger than the height but the picture shows almost the same scale for the three axis (see below first one pic).
How do I modify the configuration to allow it display in a correct propotion according to the acual axis value like the below one?
below is my code:
from py3dbp import Packer, Bin, Item
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import matplotlib.pyplot as plt
import random
def cuboid_data2(o, size=(1, 1, 1)):
X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]],
[[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]],
[[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],
[[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],
[[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]],
[[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]]
X = np.array(X).astype(float)
for i in range(3):
X[:, :, i] *= size[i]
X += np.array(o)
return X
def plotCubeAt2(positions, sizes=None, colors=None, **kwargs):
if not isinstance(colors, (list, np.ndarray)): colors = ["C0"] * len(positions)
if not isinstance(sizes, (list, np.ndarray)): sizes = [(1, 1, 1)] * len(positions)
g = []
for p, s, c in zip(positions, sizes, colors):
g.append(cuboid_data2(p, size=s))
return Poly3DCollection(np.concatenate(g),
facecolors=np.repeat(colors, 6), **kwargs)
# containers = [
# [250, 250, 500],
# [500, 500, 400],
# [300, 300, 300],
# [300, 300, 200],
# [300, 300, 100],
# [500, 500, 500]
# ]
# containers = [
# # [12000000, 2350000, 2697000],
# # [12000000, 2350000, 2697000],
# # [12000000, 2350000, 2697000],
# # # [12000000, 2350000, 2697000],
# # # [12000000, 2350000, 2697000],
# # # [12000000, 2350000, 2697000],
# # ]
containers = [
[589.5, 235, 239],
[1202.4, 235, 269],
# [1202.4, 235, 269],
# [12.024, 2.350, 2.69],
# [12.024, 2.350, 2.69],
# [12.024, 2.350, 2.69],
]
packer = Packer()
containerX = 0
containerY = 0
containerZ = 0
for i, t in enumerate(range(len(containers))):
containerX = containers[t][0]
containerY = containers[t][1]
containerZ = containers[t][2]
i += 1
packer.add_bin(Bin('40HC-' + str(i), containerX, containerY, containerZ, 18000.0))
for i in range(50):
packer.add_item(Item('BoxA_' + str(i), 44, 39, 70, 8.20))
for i in range(35):
packer.add_item(Item('BoxB_' + str(i), 65, 38, 40, 14))
for i in range(31):
packer.add_item(Item('BoxC_' + str(i), 43, 52, 47, 10))
for i in range(38):
packer.add_item(Item('BoxD_' + str(i), 60, 45, 40, 14))
for i in range(11):
packer.add_item(Item('BoxE_' + str(i), 42, 46, 54, 9.70))
for i in range(525):
packer.add_item(Item('BoxF_' + str(i), 62, 45, 35, 14.5))
# packer.pack()
# packer.pack(bigger_first=False)
packer.pack(bigger_first=False, distribute_items=True, number_of_decimals=3)
for b in packer.bins:
positions = []
sizes = []
colors = []
print(":::::::::::", b.string())
print("FITTED ITEMS:")
for item in b.items:
print("====> ", item.string())
x = float(item.position[0])
y = float(item.position[1])
z = float(item.position[2])
positions.append((x, y, z))
sizes.append(
(float(item.get_dimension()[0]), float(item.get_dimension()[1]), float(item.get_dimension()[2])))
colorList = ["crimson", "limegreen", "g", "r", "c", "m", "y", "k"]
if item.width == 44:
colors.append(colorList[0])
if item.width == 65:
colors.append(colorList[1])
if item.width == 43:
colors.append(colorList[2])
if item.width == 60:
colors.append(colorList[3])
if item.width == 42:
colors.append(colorList[4])
if item.width == 62:
colors.append(colorList[5])
print("UNFITTED ITEMS:")
for item in b.unfitted_items:
print("====> ", item.string())
print("***************************************************")
print("***************************************************")
# colorList = ["crimson", "limegreen", "g", "r", "c", "m", "y", "k"]
#
# for i in range(len(b.items)):
# f = random.randint(0, 7)
# colors.append(colorList[f])
if len(colors) > 0:
fig = plt.figure()
fig.canvas.set_window_title(b.string().split("(")[0])
ax = fig.gca(projection='3d')
ax.set_aspect('auto')
pc = plotCubeAt2(positions, sizes, colors=colors, edgecolor="k")
ax.add_collection3d(pc)
ax.set_xlim([0, float(b.string().split(",")[0].split("(")[1].split("x")[0])])
ax.set_ylim([0, float(b.string().split(",")[0].split("(")[1].split("x")[1])])
ax.set_zlim([0, float(b.string().split(",")[0].split("(")[1].split("x")[2])])
plt.show()
containers = [
[1203, 235, 259],
[1203, 235, 259],
# [1202.4, 235, 269],
# [12.024, 2.350, 2.69],
# [12.024, 2.350, 2.69],
# [12.024, 2.350, 2.69],
]
I have updated matplotlib to use .set_box_aspect function. there seems to be two problems:
the x y z axis label is severely overlaped. It does not auto adjust.
the drawing picture becomes much smaller after using .set_box_aspect. When enlarging the drawing image, the axis does not auto enlarge accordingly. Hence, the image at center will overlap with the axis at edge, which is akward.
plus, there are showing error messages despite of sucessful drawing.
RuntimeWarning: divide by zero encountered in double_scalars
dz /= az
..........
x, y, z = proj3d.inv_transform(xd, yd, z, self.M)
File "C:\Users\Jack\AppData\Local\Programs\Python\Python36\lib\site-packages\mpl_toolkits\mplot3d\proj3d.py", line 125, in inv_transform
iM = linalg.inv(M)
File "<__array_function__ internals>", line 6, in inv
File "C:\Users\Jack\AppData\Roaming\Python\Python36\site-packages\numpy\linalg\linalg.py", line 546, in inv
ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)
File "C:\Users\Jack\AppData\Roaming\Python\Python36\site-packages\numpy\linalg\linalg.py", line 88, in _raise_linalgerror_singular
raise LinAlgError("Singular matrix")
numpy.linalg.LinAlgError: Singular matrix
Are there ways to mitigate above problems?
Upvotes: 2
Views: 1637
Reputation: 12496
If you have the coordinates of verteces of boxes, then you can use matplotlib.axes.Axes.set_box_aspect
as explained in this answer.
This means that you have to add these lines at the bottom of your code, just before plotting:
positions_array = np.array(positions)
ax.set_box_aspect((np.ptp(positions_array[:, 0]), np.ptp(positions_array[:, 1]), np.ptp(positions_array[:, 2])))
from py3dbp import Packer, Bin, Item
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import matplotlib.pyplot as plt
import random
def cuboid_data2(o, size=(1, 1, 1)):
X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]],
[[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]],
[[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],
[[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],
[[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]],
[[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]]
X = np.array(X).astype(float)
for i in range(3):
X[:, :, i] *= size[i]
X += np.array(o)
return X
def plotCubeAt2(positions, sizes=None, colors=None, **kwargs):
if not isinstance(colors, (list, np.ndarray)): colors = ["C0"] * len(positions)
if not isinstance(sizes, (list, np.ndarray)): sizes = [(1, 1, 1)] * len(positions)
g = []
for p, s, c in zip(positions, sizes, colors):
g.append(cuboid_data2(p, size=s))
return Poly3DCollection(np.concatenate(g),
facecolors=np.repeat(colors, 6), **kwargs)
# containers = [
# [250, 250, 500],
# [500, 500, 400],
# [300, 300, 300],
# [300, 300, 200],
# [300, 300, 100],
# [500, 500, 500]
# ]
# containers = [
# # [12000000, 2350000, 2697000],
# # [12000000, 2350000, 2697000],
# # [12000000, 2350000, 2697000],
# # # [12000000, 2350000, 2697000],
# # # [12000000, 2350000, 2697000],
# # # [12000000, 2350000, 2697000],
# # ]
containers = [
[589.5, 235, 239],
[1202.4, 235, 269],
# [1202.4, 235, 269],
# [12.024, 2.350, 2.69],
# [12.024, 2.350, 2.69],
# [12.024, 2.350, 2.69],
]
packer = Packer()
containerX = 0
containerY = 0
containerZ = 0
for i, t in enumerate(range(len(containers))):
containerX = containers[t][0]
containerY = containers[t][1]
containerZ = containers[t][2]
i += 1
packer.add_bin(Bin('40HC-' + str(i), containerX, containerY, containerZ, 18000.0))
for i in range(50):
packer.add_item(Item('BoxA_' + str(i), 44, 39, 70, 8.20))
for i in range(35):
packer.add_item(Item('BoxB_' + str(i), 65, 38, 40, 14))
for i in range(31):
packer.add_item(Item('BoxC_' + str(i), 43, 52, 47, 10))
for i in range(38):
packer.add_item(Item('BoxD_' + str(i), 60, 45, 40, 14))
for i in range(11):
packer.add_item(Item('BoxE_' + str(i), 42, 46, 54, 9.70))
for i in range(525):
packer.add_item(Item('BoxF_' + str(i), 62, 45, 35, 14.5))
# packer.pack()
# packer.pack(bigger_first=False)
packer.pack(bigger_first=False, distribute_items=True, number_of_decimals=3)
for b in packer.bins:
positions = []
sizes = []
colors = []
print(":::::::::::", b.string())
print("FITTED ITEMS:")
for item in b.items:
print("====> ", item.string())
x = float(item.position[0])
y = float(item.position[1])
z = float(item.position[2])
positions.append((x, y, z))
sizes.append(
(float(item.get_dimension()[0]), float(item.get_dimension()[1]), float(item.get_dimension()[2])))
colorList = ["crimson", "limegreen", "g", "r", "c", "m", "y", "k"]
if item.width == 44:
colors.append(colorList[0])
if item.width == 65:
colors.append(colorList[1])
if item.width == 43:
colors.append(colorList[2])
if item.width == 60:
colors.append(colorList[3])
if item.width == 42:
colors.append(colorList[4])
if item.width == 62:
colors.append(colorList[5])
print("UNFITTED ITEMS:")
for item in b.unfitted_items:
print("====> ", item.string())
print("***************************************************")
print("***************************************************")
# colorList = ["crimson", "limegreen", "g", "r", "c", "m", "y", "k"]
#
# for i in range(len(b.items)):
# f = random.randint(0, 7)
# colors.append(colorList[f])
if len(colors) > 0:
fig = plt.figure()
fig.canvas.set_window_title(b.string().split("(")[0])
ax = fig.gca(projection='3d')
ax.set_aspect('auto')
pc = plotCubeAt2(positions, sizes, colors=colors, edgecolor="k")
ax.add_collection3d(pc)
ax.set_xlim([0, float(b.string().split(",")[0].split("(")[1].split("x")[0])])
ax.set_ylim([0, float(b.string().split(",")[0].split("(")[1].split("x")[1])])
ax.set_zlim([0, float(b.string().split(",")[0].split("(")[1].split("x")[2])])
positions_array = np.array(positions)
ax.set_box_aspect((np.ptp(positions_array[:, 0]), np.ptp(positions_array[:, 1]), np.ptp(positions_array[:, 2])))
plt.show()
Upvotes: 1