牧羊人nacy
这是两个相似的版本。两者的想法都是,您始终返回一个BBox对象,并且仅更改一个变量x ,该变量指示您通过left, , ... 指定的尺寸。最后,您有一个用于计算剩余角的中心的right函数。x第一种方法使用函数,因此您必须调用它们bbox.bottom().front().left().c()。这里的主要区别在于并非所有组合toptop lefttop righttop left front...在创建对象时计算,但仅在调用它们时计算。import numpy as npimport itertoolsclass 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)
一只斗牛犬
这是使用迭代方法创建字典的另一个解决方案:import numpy import itertoolsdirections = ['left', 'right', 'front', 'back', 'bottom', 'top']dims = np.array([ 0, 0, 1, 1, 2, 2]) # xyzdef 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_id# {'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。
小怪兽爱吃肉
我到了哪里了。以某种方式添加了这个作为答案,以更好地解释我的问题循环遍历立方体的 8 个顶点,将 3 个名称与每个有效角相匹配。“swizzle”是构成角的三个轴方向的排列。直接输入自嵌套字典d[i][j][k] = value是创建它们的一种轻松方式。(pprint(d)下)高兴的是,从那里开始,它变得丑陋,一些鸭子打字从简单的 8 垂直真值表中获取元素索引。没有特殊原因,将返回生成类的方法作为包装器,但我没有这样使用它。import numpy as npimport pprintimport operatorfrom itertools import product, permutationsfrom functools import reducefrom collections import defaultdictclass 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]] = valuedef 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_swizzleclass BBox: def __init__(self, *coords, **kwargs): pass试驾:>>> bbox = BBox(coords) # used coords instead of corners>>> bbox.coarray([ 5.96046448e-08, -1.19209290e-07, 0.00000000e+00])>>> bbox.left.bottom<BBEdge [0, 2]/>>>> bbox.left.bottom.vertices[0, 2]>>> bbox.left.bottom.coarray([-1.00000036e+00, -1.19209290e-07, 0.00000000e+00])