为了账号安全,请及时绑定邮箱和手机立即绑定

由角点定义的边界框对象的嵌套属性

由角点定义的边界框对象的嵌套属性

慕森王 2023-09-12 17:20:22
通常,搅拌器脚本必须从 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)).Tbbox_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)},            }        }       }通常,搅拌器脚本必须从 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)).Tbbox_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)},            }        }       }
查看完整描述

3 回答

?
牧羊人nacy

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)


查看完整回答
反对 回复 2023-09-12
?
一只斗牛犬

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。


查看完整回答
反对 回复 2023-09-12
?
小怪兽爱吃肉

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])


查看完整回答
反对 回复 2023-09-12
  • 3 回答
  • 0 关注
  • 98 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信