3 回答
TA贡献1862条经验 获得超7个赞
这是两个相似的版本。两者的想法都是,您始终返回一个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对象的属性。当您取消注释函数中的 print 语句时,init您可以了解构造过程中发生的所有递归调用。因此,虽然查看这里发生的情况可能会更复杂,但访问属性时会更方便。
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)
TA贡献1784条经验 获得超2个赞
这是使用迭代方法创建字典的另一个解决方案:
import numpy
import itertools
directions = ['left', 'right', 'front', 'back', 'bottom', 'top']
dims = np.array([ 0, 0, 1, 1, 2, 2]) # xyz
def get_vertices(bfl, tbr, x):
g = np.array((bfl, tbr)).T
return [v for v in itertools.product(*[g[ii][x[ii]] for ii in range(3)])]
bfl = [-1, -1, -1]
tbr = [1, 1, 1]
d = {}
for i in range(6):
x = [[0, 1], [0, 1], [0, 1]]
x[i//2] = [i % 2] # x[dim[i] = min or max
d_i = dict(c=np.mean(get_vertices(bfl=bfl, tbr=tbr, x=x), axis=0))
for j in np.nonzero(dims != dims[i])[0]:
x[j//2] = [j % 2]
d_ij = dict(c=np.mean(get_vertices(bfl=bfl, tbr=tbr, x=x), axis=0))
for k in np.nonzero(np.logical_and(dims != dims[i], dims != dims[j]))[0]:
x[k//2] = [k % 2]
d_ij[directions[k]] = dict(c=np.mean(get_vertices(bfl=bfl, tbr=tbr, x=x), axis=0))
d_i[directions[j]] = d_ij
d[directions[i]] = d_i
d
# {'left': {'c': array([-1., 0., 0.]),
# 'front': {'c': array([-1., -1., 0.]),
# 'bottom': {'c': array([-1., -1., -1.])},
# 'top': {'c': array([-1., -1., 1.])}},
# 'back': {'c': array([-1., 1., 1.]),
# 'bottom': {'c': array([-1., 1., -1.])},
# 'top': {'c': array([-1., 1., 1.])}},
# ....
您可以将其与链接的问题结合起来,通过 访问字典的键d.key1.key2。
TA贡献1852条经验 获得超1个赞
我到了哪里了。
以某种方式添加了这个作为答案,以更好地解释我的问题
循环遍历立方体的 8 个顶点,将 3 个名称与每个有效角相匹配。
“swizzle”是构成角的三个轴方向的排列。
直接输入自嵌套字典d[i][j][k] = value是创建它们的一种轻松方式。(pprint(d)下)
高兴的是,从那里开始,它变得丑陋,一些鸭子打字从简单的 8 垂直真值表中获取元素索引。
没有特殊原因,将返回生成类的方法作为包装器,但我没有这样使用它。
import numpy as np
import pprint
import operator
from itertools import product, permutations
from functools import reduce
from collections import defaultdict
class NestedDefaultDict(defaultdict):
def __init__(self, *args, **kwargs):
super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)
def __repr__(self):
return repr(dict(self))
def set_by_path(root, items, value):
reduce(operator.getitem, items[:-1], root)[items[-1]] = value
def create_bbox_swizzle(cls, dirx=("left", "right"), diry=("front", "back"), dirz=("bottom", "top")):
d = NestedDefaultDict()
data = {}
for i, cnr in enumerate(product(*(dirx, diry, dirz))):
vert = {"index": i}
data[frozenset(cnr)] = i
for perm in permutations(cnr, 3):
set_by_path(d, perm, vert)
pprint.pprint(d)
def wire_up(names, d):
class Mbox:
@property
def co(self):
return self.coords[self.vertices].mean(axis=0)
def __init__(self, coords):
self.coords = np.array(coords)
self.vertices = [v for k, v in data.items() if k.issuperset(names)]
pass
def __repr__(self):
if len(names) == 1:
return f"<BBFace {self.vertices}/>"
elif len(names) == 2:
return f"<BBEdge {self.vertices}/>"
elif len(names) == 3:
return f"<BBVert {self.vertices}/>"
return "<BBox/>"
pass
def f(k, v):
def g(self):
return wire_up(names + [k], v)(self.coords)
return property(g)
for k, v in d.items():
if isinstance(v, dict):
setattr(Mbox, k, (f(k, v)))
else:
setattr(Mbox, k, v)
return Mbox
return wire_up([], d)
@create_bbox_swizzle
class BBox:
def __init__(self, *coords, **kwargs):
pass
试驾:
>>> bbox = BBox(coords) # used coords instead of corners
>>> bbox.co
array([ 5.96046448e-08, -1.19209290e-07, 0.00000000e+00])
>>> bbox.left.bottom
<BBEdge [0, 2]/>
>>> bbox.left.bottom.vertices
[0, 2]
>>> bbox.left.bottom.co
array([-1.00000036e+00, -1.19209290e-07, 0.00000000e+00])
添加回答
举报