python - 由其角点定义的边界框对象的嵌套属性
问题描述
通常对于搅拌机脚本来说,必须从 3D 点的集合中计算出一个包围盒,例如为了默认的搅拌机立方体边界盒作为输入,
coords = np.array(
[[-1. 1. -1.],
[-1. 1. 1.],
[ 1. -1. -1.],
[ 1. -1. 1.],
[ 1. 1. -1.],
[ 1. 1. 1.]]
)
bfl = coords.min(axis=0)
tbr = coords.max(axis=0)
G = np.array((bfl, tbr)).T
bbox_coords = [i for i in itertools.product(*G)]
例如,边界框坐标将是相同顺序的立方体坐标
使用上面的和寻找一些python“迭代魔法” ("left", "right"), ("front", "back"),("top", "bottom")
,制作一个帮助类
>>> bbox = BBox(bfl, tbr)
>>> bbox.bottom.front.left
(-1, -1, -1)
>>> bbox.top.front
(0, -1, 1)
>> bbox.bottom
(0, 0, -1)
即角顶点,边缘的中心,矩形的中心。(1、2 或 4 个角的平均总和)在搅拌机顶部是 +Z,前面是 -Y。
最初是在寻找类似用静态计算值填充嵌套字典的东西
d = {
"front" : {
"co" : (0, -1, 0),
"top" : {
"co" : (0, -1, 1),
"left" : {"co" : (-1, -1, 1)},
}
}
}
编辑
为了避免发布XY Problem,即以我一直在处理的方式发布问题,在下面添加了一个答案,其中包含我在哪里使用它。抱歉,我忘了提到,可以改为选择 x 和 y 轴方向的北、南、东和西,并希望能够改变。
感觉循环超过 8 个角顶点是重新制作以顶点索引作为叶节点的“swizzle”字典的方法。“正面”面或右下角的顶点索引不会改变。
它使用它作为使用坐标或 bfl 实例化的类的基础,tbr 是无论我做什么,我总是觉得有比我现在做的“更好”的方法。
解决方案
这里有两个类似的版本。两者的想法是您始终返回一个BBox
对象,并且只更改一个变量,该变量指示您通过, , ...x
指定的维度。最后,您有一个函数用于计算剩余角的中心。left
right
x
第一种方法使用函数,因此您必须调用它们bbox.bottom().front().left().c()
。这里的主要区别是不是所有的组合
top
top left
top right
top left front
...
在创建对象时计算,但仅在您调用它们时计算。
import numpy as np
import itertools
class BBox:
"""
("left", "right"), -x, +x
("front", "back"), -y, +y
("bottom", "top"), -z, +z
"""
def __init__(self, bfl, tbr):
self.bfl = bfl
self.tbr = tbr
self.g = np.array((bfl, tbr)).T
self.x = [[0, 1], [0, 1], [0, 1]]
def c(self): # get center coordinates
return np.mean([i for i in itertools.product(*[self.g[i][self.x[i]] for i in range(3)])], axis=0)
def part(self, i, xi):
assert len(self.x[i]) == 2
b2 = BBox(bfl=self.bfl, tbr=self.tbr)
b2.x = self.x.copy()
b2.x[i] = [xi]
return b2
def left(self):
return self.part(i=0, xi=0)
def right(self):
return self.part(i=0, xi=1)
def front(self):
return self.part(i=1, xi=0)
def back(self):
return self.part(i=1, xi=1)
def bottom(self):
return self.part(i=2, xi=0)
def top(self):
return self.part(i=2, xi=1)
bbox = BBox(bfl=[-1, -1, -1], tbr=[1, 1, 1])
>>> bbox.bottom().front().left().c()
(-1, -1, -1)
>>> bbox.top().front().c()
(0, -1, 1)
>>> bbox.bottom().c()
(0, 0, -1)
第二种方法使用本身就是BBox
对象的属性。当您取消注释init
函数中的 print 语句时,您会了解在构造过程中发生的所有递归调用。因此,虽然查看这里发生的事情可能会更复杂,但在访问属性时会更方便。
class BBox:
def __init__(self, bfl, tbr, x=None):
self.bfl = bfl
self.tbr = tbr
self.g = np.array((bfl, tbr)).T
self.x = [[0, 1], [0, 1], [0, 1]] if x is None else x
# print(self.x) # Debugging
self.left = self.part(i=0, xi=0)
self.right = self.part(i=0, xi=1)
self.front = self.part(i=1, xi=0)
self.back = self.part(i=1, xi=1)
self.bottom = self.part(i=2, xi=0)
self.top = self.part(i=2, xi=1)
def c(self): # get center coordinates
return np.mean([i for i in itertools.product(*[self.g[i][self.x[i]]
for i in range(3)])], axis=0)
def part(self, i, xi):
if len(self.x[i]) < 2:
return None
x2 = self.x.copy()
x2[i] = [xi]
return BBox(bfl=self.bfl, tbr=self.tbr, x=x2)
bbox = BBox(bfl=[-1, -1, -1], tbr=[1, 1, 1])
>>> bbox.bottom.front.left.c()
(-1, -1, -1)
您还可以在构造函数的末尾添加类似的内容,以删除无效属性。(以防止类似的东西bbox.right.left.c()
)。它们是None
以前的,但AttributeError
可能更合适。
def __init__(self, bfl, tbr, x=None):
...
for name in ['left', 'right', 'front', 'back', 'bottom', 'top']:
if getattr(self, name) is None:
delattr(self, name)
你也可以添加一个__repr__()
方法:
def __repr__(self):
return repr(self.get_vertices())
def get_vertices(self):
return [i for i in itertools.product(*[self.g[i][self.x[i]]
for i in range(3)])]
def c(self): # get center coordinates
return np.mean(self.get_vertices(), axis=0)
bbox.left.front
# [(-1, -1, -1), (-1, -1, 1)]
bbox.left.front.c()
# array([-1., -1., 0.])
编辑
一段时间后回到这个问题后,我认为最好只添加相关属性而不添加所有属性,然后删除其中的一半。所以我能想到的最紧凑/最方便的课程是:
class BBox:
def __init__(self, bfl, tbr, x=None):
self.bfl, self.tbr = bfl, tbr
self.g = np.array((bfl, tbr)).T
self.x = [[0, 1], [0, 1], [0, 1]] if x is None else x
for j, name in enumerate(['left', 'right', 'front', 'back', 'bottom', 'top']):
temp = self.part(i=j//2, xi=j%2)
if temp is not None:
setattr(self, name, temp)
def c(self): # get center coordinates
return np.mean([x for x in itertools.product(*[self.g[i][self.x[i]]
for i in range(3)])], axis=0)
def part(self, i, xi):
if len(self.x[i]) == 2:
x2, x2[i] = self.x.copy(), [xi]
return BBox(bfl=self.bfl, tbr=self.tbr, x=x2)
推荐阅读
- python - 使用 matplotlib 在 Python 中绘制热图
- node.js - 如何从通过 AWS API 以字符串格式传入的多部分/表单数据中提取 FROM、TO、SUBJECT、BODY、ATTACHMENT 字段(包含 \n\r)
- java - Eclipse e4 应用程序 - 包含在插件中时找不到 ContextFactory
- c# - 如何将特定的字典条目添加到 Linq 表达式?
- node.js - 如何为 cube.js 动态生成模式?
- c# - 如何在 asp.net/webforms c# 中动态创建运行时控件?
- c - 在 C 中删除结构问题的元素数组
- c++ - c++ Excel COM自动化:查询服务器状态
- python - 打印用于解析 json 的详细堆栈跟踪
- git - 如何配置 Git 以使用 Windows 的系统证书存储?