From cec2afcdaaa3b89beddbada94f2a31787564df08 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 12 Apr 2023 16:45:03 -0300 Subject: [PATCH 01/10] First tree data structures integration. --- fury/trees/octree.py | 644 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 644 insertions(+) create mode 100644 fury/trees/octree.py diff --git a/fury/trees/octree.py b/fury/trees/octree.py new file mode 100644 index 000000000..6c5e3c9ff --- /dev/null +++ b/fury/trees/octree.py @@ -0,0 +1,644 @@ +from vtk import * +import numpy as np + + +# QUADTREE IMPLEMENTATION +class point2d(): + '''General point class. This class only stores 2d coordinates.''' + def __init__(self, x : float, y: float): + self.coord = np.array([x, y]) + + def __call__(self): + return self.coord + + def GetXCoord(self) -> float: + '''Returns the x coordinate of the point.''' + return self.coord[0] + + def GetYCoord(self) -> float: + '''Returns the y coordinate of the point.''' + return self.coord[1] + + def GetNorm(self) -> float: + '''Returns the norm of the point in relation to the origin.''' + return np.sqrt(self.GetXCoord()**2 + self.GetYCoord()**2) + + def SetCoord(self, x : float, y : float) -> np.array: + '''Updates the coordinate of the point. + * x : float x position + * y : float y position''' + self.coord = np.array([x, y]) + + def __str__(self): + string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + "]" + return string + + def __repr__(self): + string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + "]" + return string + + + +class branch2d(): + '''General branch class. It is the base structure for building a quadtree.\n + * max_points : An integer defining the maximum number of points this branch stores + * x_min : The float lower limit for x + * x_max : The float upper limit for x + * y_min : The float lower limit for y + * y_max : The float upper limit for y \n + This branch, when divided, has four branches: \n + 1. the down-left + 2. the down-right + 3. the up-left + 4. the up-right''' + def __init__(self, max_points : int, x_min : float, x_max : float, y_min : float, y_max : float): + self._DIVIDED = False + self._MAX_POINTS = max_points + self._N_POINTS = 0 + self._xmin = x_min + self._xmax = x_max + self._ymin = y_min + self._ymax = y_max + self._POINTS = np.array([], dtype = point2d) + self._UPLEFT = None + self._UPRIGHT = None + self._DOWNLEFT = None + self._DOWNRIGHT = None + self.subBranches = {0 : self._DOWNLEFT, + 1 : self._DOWNRIGHT, + 2 : self._UPLEFT, + 3 : self._UPRIGHT} + + + def GetPointsNumber(self) -> int: + '''Returns the current number of points the branch has. + This method is different from GetTotalPoints, as it only gives the current points + stored in the branch, and not the points stored in the subbranches. + If the branch is divided, it will return 0.''' + return self._N_POINTS + + def IsDivided(self) -> bool: + '''Returns True if the branch is divided, and False otherwise.''' + return self._DIVIDED + + def GetXSize(self) -> tuple: + '''Returns the x information of the branch, as a tuple.''' + return (self._xmin, self._xmax) + + def GetYSize(self) -> tuple: + '''Returns the y information of the branch, as a tuple.''' + return (self._ymin, self._ymax) + + def GetXMiddlePoint(self) -> float: + '''Returns the middle x point of the branch.''' + return self.GetXSize()[0] + (self.GetXSize()[1] - self.GetXSize()[0])/float(2) + + def GetYMiddlePoint(self) -> float: + '''Method that returns the middle y point of the branch.''' + return self.GetYSize()[0] + (self.GetYSize()[1] - self.GetYSize()[0])/float(2) + + def __str__(self): + if self.IsDivided() == True: + str = f"number of points = {self.GetTotalPoints()}\n\ + [ {self._UPLEFT.GetPointsNumber()} | {self._UPRIGHT.GetPointsNumber()} ]\n\ + [---|---]\n\ + [ {self._DOWNLEFT.GetPointsNumber()} | {self._DOWNRIGHT.GetPointsNumber()} ]\n" + else: + str = f"number of points = {self._N_POINTS}\n[ ]\n[ ]\n[ ]\n" + return str + + + def GetPointsList(self) -> np.array: + '''Returns a list containing the points existing in that branch.''' + return self._POINTS + + + def GetTotalPoints(self) -> int: + '''Returns the total number of points in that branch, including the points in the subranches.''' + if self.IsDivided() == True: + + sum = 0 + sum += self._DOWNLEFT.GetTotalPoints() + sum += self._DOWNRIGHT.GetTotalPoints() + sum += self._UPLEFT.GetTotalPoints() + sum += self._UPRIGHT.GetTotalPoints() + return sum + + else: + return self.GetPointsNumber() + + def GetSubBranch(self, index : int): + '''Returns the sub branch by index. Below, the index correspondence: + 0 : Down-left branch + 1 : Down-right branch + 2 : Up-left branch + 3 : Up-right branch''' + self.subBranches = {0 : self._DOWNLEFT, + 1 : self._DOWNRIGHT, + 2 : self._UPLEFT, + 3 : self._UPRIGHT} + + + return self.subBranches[index] + + + def RemovePoint(self, index : int): + '''Removes the given element of the list of points''' + if self._N_POINTS != 0: + self._POINTS = np.delete(self._POINTS, index) + self._N_POINTS -= 1 + else: + raise("This branch has no point to be removed.") + + + def AddPoint(self, point : point2d): + '''Recursive method that adds a point to the branch. \n + * point : point2d to be added.\n + + Below, how the method works: + 1. The branch is first checked if divided. If it is, the coordinates of + the point is checked to add the point in one of the subbranches. + 2. If it is not, it checks if the total current number of points is lower than + the branch's defined limit, + 3. If it is, adds the point to the branch. + 4. If it's not ,the branch is then divided and the coordinates of the point are checked to + add the point in one of the newly-created subbranches.''' + + if self.IsDivided() == True: + + if point.GetXCoord() < self.GetXMiddlePoint(): + if point.GetYCoord() < self.GetYMiddlePoint(): + self._DOWNLEFT.AddPoint(point) + else: + self._UPLEFT.AddPoint(point) + else: + if point.GetYCoord() < self.GetYMiddlePoint(): + self._DOWNRIGHT.AddPoint(point) + else: + self._UPRIGHT.AddPoint(point) + + + else: + + if self.GetPointsNumber() == self._MAX_POINTS: + self._DOWNLEFT = branch2d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYSize()[0], self.GetYMiddlePoint()) + self._DOWNRIGHT = branch2d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYSize()[0], self.GetYMiddlePoint()) + self._UPLEFT = branch2d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYMiddlePoint(), self.GetYSize()[1]) + self._UPRIGHT = branch2d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYMiddlePoint(), self.GetYSize()[1]) + self._DIVIDED = True + + list = self.GetPointsList() + for i in range(self.GetPointsNumber()): + + if list[i].GetXCoord() < self.GetXMiddlePoint(): + if list[i].GetYCoord() < self.GetYMiddlePoint(): + self._DOWNLEFT.AddPoint(list[i]) + else: + self._UPLEFT.AddPoint(list[i]) + + else: + if list[i].GetYCoord() < self.GetYMiddlePoint(): + self._DOWNRIGHT.AddPoint(list[i]) + else: + + self._UPRIGHT.AddPoint(list[i]) + self.RemovePoint(0) + + + if point.GetXCoord() < self.GetXMiddlePoint(): + if point.GetYCoord() < self.GetYMiddlePoint(): + self._DOWNLEFT.AddPoint(point) + else: + self._UPLEFT.AddPoint(point) + else: + if point.GetYCoord() < self.GetYMiddlePoint(): + self._DOWNRIGHT.AddPoint(point) + else: + self._UPRIGHT.AddPoint(point) + + + else: + self._POINTS = np.append(self._POINTS, point) + self._N_POINTS += 1 + + + + + + +class Tree2d(): + '''Class that stores the root branch and general 2d tree informations. + * branch : branch2d to be the root of the tree.''' + def __init__(self, branch : branch2d): + self._ROOT = branch + self.xmin = branch.GetXSize()[0] + self.xmax = branch.GetXSize()[1] + self.ymin = branch.GetYSize()[0] + self.ymax = branch.GetYSize()[1] + self._N_POINTS = branch.GetTotalPoints() + self._STR = "" + + + def __str__(self): + if self._ROOT.IsDivided() == True: + self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n" + self._STR += f"[ {self._ROOT._UPLEFT.GetTotalPoints()} | {self._ROOT._UPRIGHT.GetTotalPoints()} ]\n" + self._STR += f"[---|---]\n" + self._STR += f"[ {self._ROOT._DOWNLEFT.GetTotalPoints()} | {self._ROOT._DOWNRIGHT.GetTotalPoints()} ]\n" + else: + self._STR = f"number of points = {self._N_POINTS}\n[ ]\n[ ]\n[ ]\n" + + return self._STR + + def AddPoint(self, point : point2d): + '''Method that adds a point to the tree. + * point : point2d to be added into the tree.''' + self._ROOT.AddPoint(point) + self._N_POINTS += 1 +# END OF QUADTREE IMPLEMENTATION + +# OCTREE IMPLEMENTATION +class point3d(point2d): + '''General point class. This class only stores 3d coordinates.''' + def __init__(self, x: float, y: float, z: float): + self.coord = np.array([x, y, z]) + + def __call__(self): + return self.coord + + def GetZCoord(self): + '''Method that returns the z coordinate of the point.''' + return self.coord[2] + + def GetNorm(self): + '''Method that returns the norm of the point in relation to the origin.''' + return np.sqrt(self.GetXCoord()**2 + self.GetYCoord()**2 + self.GetZCoord()**2) + + def SetCoord(self, x: float, y: float, z : float): + '''Method that updates the coordinate of the point. + * x : float x position + * y : float y position + * z : float z position''' + self.coord = np.array([x, y, z]) + + def __str__(self): + string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + str('%.3f' % self.coord[2]) +"]" + return string + + def __repr__(self): + string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + str('%.3f' % self.coord[2]) + "]" + return string + + +class branch3d(branch2d): + '''General 3d branch class. It is the base structure for building an octree.\n + * max_points : An integer defining the maximum number of points this branch stores + * x_min : The float lower limit for x + * x_max : The float upper limit for x + * y_min : The float lower limit for y + * y_max : The float upper limit for y + * z_min : The float front limit for z + * z_max : The float back limit for z \n + This branch, when divided, has eight branches: \n + 1. the front-down-left + 2. the front-down-right + 3. the front-up-left + 4. the front-up-right + 5. the back-down-left + 6. the back-down-right + 7. the back-up-left + 8. the back-up-right''' + def __init__(self, + max_points: int, + x_min: float, + x_max: float, + y_min: float, + y_max: float, + z_min: float, + z_max: float): + + self._DIVIDED = False + self._MAX_POINTS = max_points + self._N_POINTS = 0 + self._xmin = x_min + self._xmax = x_max + self._ymin = y_min + self._ymax = y_max + self._zmin = z_min + self._zmax = z_max + self._POINTS = np.array([], dtype = point3d) + self._FRONTUPLEFT = None + self._FRONTUPRIGHT = None + self._FRONTDOWNLEFT = None + self._FRONTDOWNRIGHT = None + self._BACKUPLEFT = None + self._BACKUPRIGHT = None + self._BACKDOWNLEFT = None + self._BACKDOWNRIGHT = None + self.subBranches = {0 : self._FRONTDOWNLEFT, + 1 : self._FRONTDOWNRIGHT, + 2 : self._FRONTUPLEFT, + 3 : self._FRONTUPRIGHT, + 4 : self._BACKDOWNLEFT, + 5 : self._BACKDOWNRIGHT, + 6 : self._BACKUPLEFT, + 7 : self._BACKUPRIGHT} + + def GetZSize(self) ->tuple: + '''Method that returns the y information of the branch, as a tuple.''' + return (self._zmin, self._zmax) + + def GetZMiddlePoint(self) -> float: + '''Method that returns the middle z point of the branch.''' + return self.GetZSize()[0] + (self.GetZSize()[1] - self.GetZSize()[0])/float(2) + + def GetTotalPoints(self) -> int: + '''Returns the total number of points in that branch, including the points in the subranches.''' + if self.IsDivided() == True: + + sum = 0 + sum += self._FRONTDOWNLEFT.GetTotalPoints() + sum += self._FRONTDOWNRIGHT.GetTotalPoints() + sum += self._FRONTUPLEFT.GetTotalPoints() + sum += self._FRONTUPRIGHT.GetTotalPoints() + sum += self._BACKDOWNLEFT.GetTotalPoints() + sum += self._BACKDOWNRIGHT.GetTotalPoints() + sum += self._BACKUPLEFT.GetTotalPoints() + sum += self._BACKUPRIGHT.GetTotalPoints() + return sum + + else: + return self.GetPointsNumber() + + def __str__(self): + if self.IsDivided() == True: + str = f"number of points = {self.GetTotalPoints()}\n\ + FRONT\n \ + [ {self._FRONTUPLEFT.GetPointsNumber()} | {self._FRONTUPRIGHT.GetPointsNumber()} ]\n\ + [---|---]\n\ + [ {self._FRONTDOWNLEFT.GetPointsNumber()} | {self._FRONTDOWNRIGHT.GetPointsNumber()} ]\n\ + | BACK\n \ + [ {self._BACKUPLEFT.GetPointsNumber()} | {self._BACKUPRIGHT.GetPointsNumber()} ]\n\ + [---|---]\n\ + [ {self._BACKDOWNLEFT.GetPointsNumber()} | {self._BACKDOWNRIGHT.GetPointsNumber()} ]\n" + else: + str = f"number of points = {self._N_POINTS}\n| FRONT\n[ ]\n[ ]\n[ ]\n| BACK\n[ ]\n[ ]\n[ ]\n" + return str + + def GetSubBranch(self, index : int): + '''Returns the sub branch by index. Below, the index correspondence: + 0 : Front-down-left branch + 1 : Front-down-right branch + 2 : Front-up-left branch + 3 : Front-up-right branch + 4 : Back-down-left branch + 5 : Back-down-right branch + 6 : Back-up-left branch + 7 : Back-up-right branch + + * index : int index of the branch requested''' + + self.subBranches = {0 : self._FRONTDOWNLEFT, + 1 : self._FRONTDOWNRIGHT, + 2 : self._FRONTUPLEFT, + 3 : self._FRONTUPRIGHT, + 4 : self._BACKDOWNLEFT, + 5 : self._BACKDOWNRIGHT, + 6 : self._BACKUPLEFT, + 7 : self._BACKUPRIGHT} + + return self.subBranches[index] + + + def AddPoint(self, point : point3d): + '''Recursive method that adds a point to the branch. \n + * point : point3d to be added.\n + + Below, how the method works: + 1. The branch is first checked if divided. If it is, the coordinates of + the point is checked to add the point in one of the subbranches. + 2. If it is not, it checks if the total current number of points is lower than + the branch's defined limit, + 3. If it is, adds the point to the branch. + 4. If it's not ,the branch is then divided and the coordinates of the point are checked to + add the point in one of the newly-created subbranches.''' + if self.IsDivided() == True: + + if point.GetXCoord() < self.GetXMiddlePoint(): + if point.GetYCoord() < self.GetYMiddlePoint(): + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNLEFT.AddPoint(point) + else: + self._BACKDOWNLEFT.AddPoint(point) + else: + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPLEFT.AddPoint(point) + else: + self._BACKUPLEFT.AddPoint(point) + else: + if point.GetYCoord() < self.GetYMiddlePoint(): + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNRIGHT.AddPoint(point) + else: + self._BACKDOWNRIGHT.AddPoint(point) + else: + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPRIGHT.AddPoint(point) + else: + self._BACKUPRIGHT.AddPoint(point) + + else: + + if self.GetPointsNumber() == self._MAX_POINTS: + self._FRONTDOWNLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZSize()[0], self.GetZMiddlePoint()) + self._FRONTDOWNRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZSize()[0], self.GetZMiddlePoint()) + self._FRONTUPLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZSize()[0], self.GetZMiddlePoint()) + self._FRONTUPRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZSize()[0], self.GetZMiddlePoint()) + self._BACKDOWNLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZMiddlePoint(), self.GetZSize()[1]) + self._BACKDOWNRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZMiddlePoint(), self.GetZSize()[1]) + self._BACKUPLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZMiddlePoint(), self.GetZSize()[1]) + self._BACKUPRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZMiddlePoint(), self.GetZSize()[1]) + self._DIVIDED = True + + list = self.GetPointsList() + for i in range(self.GetPointsNumber()): + + if list[i].GetXCoord() < self.GetXMiddlePoint(): + if list[i].GetYCoord() < self.GetYMiddlePoint(): + if list[i].GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNLEFT.AddPoint(list[i]) + else: + self._BACKDOWNLEFT.AddPoint(list[i]) + else: + if list[i].GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPLEFT.AddPoint(list[i]) + else: + self._BACKUPLEFT.AddPoint(list[i]) + else: + if list[i].GetYCoord() < self.GetYMiddlePoint(): + if list[i].GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNRIGHT.AddPoint(list[i]) + else: + self._BACKDOWNRIGHT.AddPoint(list[i]) + else: + if list[i].GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPRIGHT.AddPoint(list[i]) + else: + self._BACKUPRIGHT.AddPoint(list[i]) + self.RemovePoint(0) + + + if point.GetXCoord() < self.GetXMiddlePoint(): + if point.GetYCoord() < self.GetYMiddlePoint(): + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNLEFT.AddPoint(point) + else: + self._BACKDOWNLEFT.AddPoint(point) + else: + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPLEFT.AddPoint(point) + else: + self._BACKUPLEFT.AddPoint(point) + else: + if point.GetYCoord() < self.GetYMiddlePoint(): + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNRIGHT.AddPoint(point) + else: + self._BACKDOWNRIGHT.AddPoint(point) + else: + if point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPRIGHT.AddPoint(point) + else: + self._BACKUPRIGHT.AddPoint(point) + + + else: + self._POINTS = np.append(self._POINTS, point) + self._N_POINTS += 1 + + +class Tree3d(): + '''Class that stores the root branch and general 3d tree informations. + * branch : branch3d to be the root of the tree.''' + def __init__(self, branch : branch3d): + self._ROOT = branch + self.xmin = branch.GetXSize()[0] + self.xmax = branch.GetXSize()[1] + self.ymin = branch.GetYSize()[0] + self.ymax = branch.GetYSize()[1] + self.zmin = branch.GetZSize()[0] + self.zmax = branch.GetZSize()[1] + self._N_POINTS = branch.GetTotalPoints() + self._STR = "" + + + def __str__(self): + if self._ROOT.IsDivided() == True: + self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n\ + FRONT\n\ + [ {self._ROOT._FRONTUPLEFT.GetTotalPoints()} | {self._ROOT._FRONTUPRIGHT.GetTotalPoints()} ]\n\ + [---|---]\n\ + [ {self._ROOT._FRONTDOWNLEFT.GetTotalPoints()} | {self._ROOT._FRONTDOWNRIGHT.GetTotalPoints()} ]\n\ + | BACK\n\ + [ {self._ROOT._BACKUPLEFT.GetTotalPoints()} | {self._ROOT._BACKUPRIGHT.GetTotalPoints()} ]\n\ + [---|---]\n\ + [ {self._ROOT._BACKDOWNLEFT.GetTotalPoints()} | {self._ROOT._BACKDOWNRIGHT.GetTotalPoints()} ]\n" + else: + self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n FRONT\n\ + [ ]\n\ + [ ]\n\ + [ ]\n\ + | BACK\n\ + [ ]\n\ + [ ]\n\ + [ ]\n" + return self._STR + + def GetRoot(self) -> branch3d: + '''Returns the root branch of the tree.''' + return self._ROOT + + def AddPoint(self, point : point3d): + '''Adds a point to the tree. + * point : point3d to be added into the tree.''' + self._ROOT.AddPoint(point) + self._N_POINTS += 1 + +# END OF OCTREE IMPLEMENTATION + + + +# GRAPH IMPLEMENTATION +def BoundingBox(center : tuple = (0.0, 0.0, 0.0), + size : tuple = (1.0, 1.0, 1.0), + color : tuple = (1.0, 1.0, 0.0, 1.0), + line_width : float = 1.0): + '''Creates a bounding box with the parameters given. The box got only is edges renderized. + * center : tuple with 3 coordinates, x, y, and z, that determines where is the center of the box. + * size : tuple with 3 coordinates, x, y, and z, that determines its lateral sizes. + * color : tuple with 4 coordinates, r, g, b and a, that determines the color of the bounding box. + * line_width : float that determines the width of the box lines.''' + x_c = center[0] + y_c = center[1] + z_c = center[2] + + cube = vtkCubeSource() + cube.SetXLength(size[0]) + cube.SetYLength(size[1]) + cube.SetZLength(size[2]) + cube.SetCenter(x_c, y_c, z_c) + + edges = vtkExtractEdges() + edges.SetInputConnection(cube.GetOutputPort()) + + cubeMapper = vtkPolyDataMapper() # Replace for fury poly data mapper method + cubeMapper.SetInputConnection(edges.GetOutputPort()) + + cubeActor = vtkActor() # Replace for fury actor method + cubeActor.SetMapper(cubeMapper) + cubeActor.SetProperty(vtkProperty().SetLineWidth(line_width)) + cubeActor.GetProperty().SetColor(color[0], color[1], color[2]) + cubeActor.GetProperty().SetLighting(False) + + return cubeActor + + +def GetActorFromBranch(branch : branch3d) -> vtkActor: + '''Recursive function that creates actors for the branch given. If the branch is divided, + then the function is run for the subbranches until the function reaches a non-divided branch, + that creates the actor to be returned. This actor is then appended into a list, that is then returned. + * branch : branch3d that will have the actor created.''' + + if branch.IsDivided() == True: + actors = np.array([], dtype = vtkActor) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(0))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(1))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(2))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(3))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(4))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(5))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(6))) + actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(7))) + + + return actors + + else: + + data = np.array([]) + data0 = branch.GetPointsList() + for i in range(data0.shape[0]): + np.append(data, data0[i]()) + + x_c = branch.GetXMiddlePoint() + y_c = branch.GetYMiddlePoint() + z_c = branch.GetZMiddlePoint() + + x_l = (branch.GetXSize()[1] - branch.GetXSize()[0]) + y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) + z_l = (branch.GetZSize()[1] - branch.GetZSize()[0]) + + cubeActor = BoundingBox((x_c, y_c, z_c), (x_l, y_l, z_l), (0.5, 0.5, 0.5), 1.0) + + return cubeActor +# END OF GRAPH IMPLEMENTATION \ No newline at end of file From 7378a7d2eacb9ea8755f5e926879532e0abb3110 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 12 Apr 2023 20:46:53 -0300 Subject: [PATCH 02/10] Added a testing program for the trees and a new function for the branches. --- fury/tests/test_trees.py | 71 ++++++++++++++++++++++++++++++++++++++++ fury/trees/octree.py | 33 ++++++++++++++----- 2 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 fury/tests/test_trees.py diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py new file mode 100644 index 000000000..016c1647d --- /dev/null +++ b/fury/tests/test_trees.py @@ -0,0 +1,71 @@ +import sys +sys.path.append('C:/Users/Lampada/Desktop/GSoC/fury/fury/') + +from trees.octree import * +from fury.lib import Points, CellArray, PolyData, PolyDataMapper, Actor, Renderer, RenderWindow, RenderWindowInteractor +from fury import window, actor, utils, lib + + +# TESTING A QUADTREE OF RANDOM 2D POINTS +br = branch2d(4, 0.0, 1.0, 0.0, 1.0) + +npoints = 200 + +points = np.random.rand(npoints, 2) + +tree = Tree2d(br) +for i in range(npoints): + tree.AddPoint(point2d(points[i, 0], points[i, 1])) + +print(tree) + + + +# TESTING AN OCTREE WITH RANDOM 3D POINTS +br = branch3d(4, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0) +npoints = 200 + +data = np.random.rand(npoints, 3) +np.random.seed() + +tree = Tree3d(br) +for i in range(npoints): + tree.AddPoint(point3d(data[i, 0], data[i, 1], data[i, 2])) + +print(tree) + + +def abstract(branch : branch3d): + print(branch.GetPointsNumber()) + +tree.GetRoot().ProcessBranch(abstract) + + + +# # FOR THIS EXAMPLE, LET'S RENDER THE OCTREE WITH THE PROVIDED FUNCTIONS +# scene = window.Scene() +# scene.set_camera(position=(-6, 5, -10), +# focal_point=(tree.GetRoot().GetXMiddlePoint(), +# tree.GetRoot().GetYMiddlePoint(), +# tree.GetRoot().GetZMiddlePoint()), +# view_up=(0.0, 0.0, 0.0)) +# showmanager = window.ShowManager(scene, "trees demo", (1080, 1080), reset_camera = True, order_transparent=True) + + + +# actorPoints = actor.dots(data, (1.0, 0.5, 0.4), 1, 5) +# scene.add(actorPoints) + + +# actorslist = GetActorFromBranch(tree.GetRoot()) + +# for i in range(len(actorslist)): +# scene.add(actorslist[i]) + + +# interactor = lib.RenderWindowInteractor() +# interactor.SetRenderWindow(showmanager.window) + +# interactor.Initialize() +# showmanager.render() +# interactor.Start() diff --git a/fury/trees/octree.py b/fury/trees/octree.py index 6c5e3c9ff..eed58002b 100644 --- a/fury/trees/octree.py +++ b/fury/trees/octree.py @@ -1,4 +1,5 @@ -from vtk import * +from vtk import vtkExtractEdges, vtkProperty, vtkCubeSource +from fury import utils import numpy as np @@ -221,7 +222,23 @@ def AddPoint(self, point : point2d): self._POINTS = np.append(self._POINTS, point) self._N_POINTS += 1 - + def ProcessBranch(self, Function): + '''Abstract recursive method that process the branch or its subbranches with a given function. + If the function returns any value, it will be returned as the value itself or a list of the + values returned by each subbranch processed, if the branch is already divided. + * Function : Any function that has only a branch as input''' + if self.IsDivided() == True: + list = np.array([]) + + for i in range(len(self.subBranches)): + list = np.append(list, self.GetSubBranch(i).ProcessBranch(Function)) + + return list + + else: + return Function(self) + + @@ -563,7 +580,6 @@ def AddPoint(self, point : point3d): * point : point3d to be added into the tree.''' self._ROOT.AddPoint(point) self._N_POINTS += 1 - # END OF OCTREE IMPLEMENTATION @@ -591,11 +607,10 @@ def BoundingBox(center : tuple = (0.0, 0.0, 0.0), edges = vtkExtractEdges() edges.SetInputConnection(cube.GetOutputPort()) - cubeMapper = vtkPolyDataMapper() # Replace for fury poly data mapper method + cubeMapper = utils.PolyDataMapper() cubeMapper.SetInputConnection(edges.GetOutputPort()) - cubeActor = vtkActor() # Replace for fury actor method - cubeActor.SetMapper(cubeMapper) + cubeActor = utils.get_actor_from_polymapper(cubeMapper) cubeActor.SetProperty(vtkProperty().SetLineWidth(line_width)) cubeActor.GetProperty().SetColor(color[0], color[1], color[2]) cubeActor.GetProperty().SetLighting(False) @@ -603,14 +618,14 @@ def BoundingBox(center : tuple = (0.0, 0.0, 0.0), return cubeActor -def GetActorFromBranch(branch : branch3d) -> vtkActor: +def GetActorFromBranch(branch : branch3d) -> utils.Actor: '''Recursive function that creates actors for the branch given. If the branch is divided, then the function is run for the subbranches until the function reaches a non-divided branch, that creates the actor to be returned. This actor is then appended into a list, that is then returned. * branch : branch3d that will have the actor created.''' if branch.IsDivided() == True: - actors = np.array([], dtype = vtkActor) + actors = np.array([], dtype = utils.Actor) actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(0))) actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(1))) actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(2))) @@ -638,7 +653,7 @@ def GetActorFromBranch(branch : branch3d) -> vtkActor: y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) z_l = (branch.GetZSize()[1] - branch.GetZSize()[0]) - cubeActor = BoundingBox((x_c, y_c, z_c), (x_l, y_l, z_l), (0.5, 0.5, 0.5), 1.0) + cubeActor = BoundingBox((x_c, y_c, z_c), (x_l, y_l, z_l), (0.5, 0.5, 0.5), 3.0) return cubeActor # END OF GRAPH IMPLEMENTATION \ No newline at end of file From b143cac41d463865dbc3754b24f61691a52882db Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 13 Apr 2023 10:22:13 -0300 Subject: [PATCH 03/10] Minor change to the trees test program and octree --- fury/tests/test_trees.py | 43 ++++++++++++++++++++-------------------- fury/trees/octree.py | 11 +++++----- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index 016c1647d..c5ef1f065 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -35,37 +35,38 @@ print(tree) -def abstract(branch : branch3d): - print(branch.GetPointsNumber()) +# BELOW, THE ABSTRACT PROCESSING METHOD +def abstract(branch : branch3d, number): + print(number + branch.GetPointsNumber()) -tree.GetRoot().ProcessBranch(abstract) +tree.GetRoot().ProcessBranch(abstract, 10) -# # FOR THIS EXAMPLE, LET'S RENDER THE OCTREE WITH THE PROVIDED FUNCTIONS -# scene = window.Scene() -# scene.set_camera(position=(-6, 5, -10), -# focal_point=(tree.GetRoot().GetXMiddlePoint(), -# tree.GetRoot().GetYMiddlePoint(), -# tree.GetRoot().GetZMiddlePoint()), -# view_up=(0.0, 0.0, 0.0)) -# showmanager = window.ShowManager(scene, "trees demo", (1080, 1080), reset_camera = True, order_transparent=True) +# FOR THIS EXAMPLE, LET'S RENDER THE OCTREE WITH THE PROVIDED FUNCTIONS +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.GetRoot().GetXMiddlePoint(), + tree.GetRoot().GetYMiddlePoint(), + tree.GetRoot().GetZMiddlePoint()), + view_up=(0.0, 0.0, 0.0)) +showmanager = window.ShowManager(scene, "trees demo", (1080, 1080), reset_camera = True, order_transparent=True) -# actorPoints = actor.dots(data, (1.0, 0.5, 0.4), 1, 5) -# scene.add(actorPoints) +actorPoints = actor.dots(data, (1.0, 0.5, 0.4), 1, 5) +scene.add(actorPoints) -# actorslist = GetActorFromBranch(tree.GetRoot()) +actorslist = GetActorFromBranch(tree.GetRoot()) -# for i in range(len(actorslist)): -# scene.add(actorslist[i]) +for i in range(len(actorslist)): + scene.add(actorslist[i]) -# interactor = lib.RenderWindowInteractor() -# interactor.SetRenderWindow(showmanager.window) +interactor = lib.RenderWindowInteractor() +interactor.SetRenderWindow(showmanager.window) -# interactor.Initialize() -# showmanager.render() -# interactor.Start() +interactor.Initialize() +showmanager.render() +interactor.Start() diff --git a/fury/trees/octree.py b/fury/trees/octree.py index eed58002b..a7d73fa09 100644 --- a/fury/trees/octree.py +++ b/fury/trees/octree.py @@ -222,21 +222,22 @@ def AddPoint(self, point : point2d): self._POINTS = np.append(self._POINTS, point) self._N_POINTS += 1 - def ProcessBranch(self, Function): - '''Abstract recursive method that process the branch or its subbranches with a given function. + def ProcessBranch(self, Function, *args): + '''Abstract recursive method that process the branch or its subbranches with a given function and its arguments. If the function returns any value, it will be returned as the value itself or a list of the values returned by each subbranch processed, if the branch is already divided. - * Function : Any function that has only a branch as input''' + * Function : Any function that has only a branch as input + * *args : arguments of the function in the order requested by the funciton passed''' if self.IsDivided() == True: list = np.array([]) for i in range(len(self.subBranches)): - list = np.append(list, self.GetSubBranch(i).ProcessBranch(Function)) + list = np.append(list, self.GetSubBranch(i).ProcessBranch(Function, *args)) return list else: - return Function(self) + return Function(self, *args) From 61f7f2f8817cdccf353a70d9a57d17ae4ca36124 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Thu, 13 Apr 2023 16:39:49 -0300 Subject: [PATCH 04/10] Reimplemented the octree rendering function with fury's existing functions. Also improved printing of both trees with procedural printing of it sizes. --- fury/tests/test_trees.py | 52 +-- fury/{trees/octree.py => trees.py} | 561 ++++++++++++++++++----------- 2 files changed, 374 insertions(+), 239 deletions(-) rename fury/{trees/octree.py => trees.py} (60%) diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index c5ef1f065..cfeeb0af4 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -1,67 +1,73 @@ import sys sys.path.append('C:/Users/Lampada/Desktop/GSoC/fury/fury/') +import trees +from fury import window, actor, lib +import numpy as np -from trees.octree import * -from fury.lib import Points, CellArray, PolyData, PolyDataMapper, Actor, Renderer, RenderWindow, RenderWindowInteractor -from fury import window, actor, utils, lib - # TESTING A QUADTREE OF RANDOM 2D POINTS -br = branch2d(4, 0.0, 1.0, 0.0, 1.0) +br = trees.Branch2d(4, 0.0, 1.0, 0.0, 1.0) npoints = 200 points = np.random.rand(npoints, 2) -tree = Tree2d(br) +tree = trees.Tree2d(br) for i in range(npoints): - tree.AddPoint(point2d(points[i, 0], points[i, 1])) - -print(tree) + tree.AddPoint(trees.Point2d(points[i, 0], points[i, 1])) +print(tree) # TESTING AN OCTREE WITH RANDOM 3D POINTS -br = branch3d(4, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0) +br = trees.Branch3d(4, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0) npoints = 200 data = np.random.rand(npoints, 3) np.random.seed() -tree = Tree3d(br) +tree = trees.Tree3d(br) for i in range(npoints): - tree.AddPoint(point3d(data[i, 0], data[i, 1], data[i, 2])) + tree.AddPoint(trees.Point3d(data[i, 0], data[i, 1], data[i, 2])) print(tree) -# BELOW, THE ABSTRACT PROCESSING METHOD -def abstract(branch : branch3d, number): +# BELOW, THE ABSTRACT PROCESSING METHOD +def abstract(branch : trees.Branch3d, number): print(number + branch.GetPointsNumber()) -tree.GetRoot().ProcessBranch(abstract, 10) +tree.GetRoot().ProcessBranch(abstract, 10) # FOR THIS EXAMPLE, LET'S RENDER THE OCTREE WITH THE PROVIDED FUNCTIONS scene = window.Scene() -scene.set_camera(position=(-6, 5, -10), - focal_point=(tree.GetRoot().GetXMiddlePoint(), - tree.GetRoot().GetYMiddlePoint(), +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.GetRoot().GetXMiddlePoint(), + tree.GetRoot().GetYMiddlePoint(), tree.GetRoot().GetZMiddlePoint()), view_up=(0.0, 0.0, 0.0)) -showmanager = window.ShowManager(scene, "trees demo", (1080, 1080), reset_camera = True, order_transparent=True) - +showmanager = window.ShowManager( + scene, + "trees demo", + (1080, + 1080), + reset_camera=True, + order_transparent=True) actorPoints = actor.dots(data, (1.0, 0.5, 0.4), 1, 5) scene.add(actorPoints) -actorslist = GetActorFromBranch(tree.GetRoot()) +actorslist = trees.GetActorFromBranch(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) -for i in range(len(actorslist)): - scene.add(actorslist[i]) +if tree.GetRoot().IsDivided(): + for i in range(len(actorslist)): + scene.add(actorslist[i]) +else: + scene.add(actorslist) interactor = lib.RenderWindowInteractor() diff --git a/fury/trees/octree.py b/fury/trees.py similarity index 60% rename from fury/trees/octree.py rename to fury/trees.py index a7d73fa09..6a0aa8128 100644 --- a/fury/trees/octree.py +++ b/fury/trees.py @@ -1,46 +1,50 @@ -from vtk import vtkExtractEdges, vtkProperty, vtkCubeSource -from fury import utils +from fury.utils import Actor +from fury.actor import line import numpy as np # QUADTREE IMPLEMENTATION -class point2d(): +class Point2d(): '''General point class. This class only stores 2d coordinates.''' + def __init__(self, x : float, y: float): self.coord = np.array([x, y]) def __call__(self): return self.coord - + def GetXCoord(self) -> float: '''Returns the x coordinate of the point.''' return self.coord[0] - + def GetYCoord(self) -> float: '''Returns the y coordinate of the point.''' return self.coord[1] - + def GetNorm(self) -> float: '''Returns the norm of the point in relation to the origin.''' return np.sqrt(self.GetXCoord()**2 + self.GetYCoord()**2) - + def SetCoord(self, x : float, y : float) -> np.array: '''Updates the coordinate of the point. * x : float x position * y : float y position''' self.coord = np.array([x, y]) - + def __str__(self): - string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + "]" + string = "[" + str('%.3f' % + self.coord[0]) + ", " + str('%.3f' % + self.coord[1]) + "]" return string - + def __repr__(self): - string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + "]" + string = "[" + str('%.3f' % + self.coord[0]) + ", " + str('%.3f' % + self.coord[1]) + "]" return string - -class branch2d(): +class Branch2d(): '''General branch class. It is the base structure for building a quadtree.\n * max_points : An integer defining the maximum number of points this branch stores * x_min : The float lower limit for x @@ -49,10 +53,12 @@ class branch2d(): * y_max : The float upper limit for y \n This branch, when divided, has four branches: \n 1. the down-left - 2. the down-right + 2. the down-right 3. the up-left 4. the up-right''' - def __init__(self, max_points : int, x_min : float, x_max : float, y_min : float, y_max : float): + + def __init__(self, max_points : int, x_min : float, + x_max : float, y_min : float, y_max : float): self._DIVIDED = False self._MAX_POINTS = max_points self._N_POINTS = 0 @@ -60,21 +66,20 @@ def __init__(self, max_points : int, x_min : float, x_max : float, y_min : float self._xmax = x_max self._ymin = y_min self._ymax = y_max - self._POINTS = np.array([], dtype = point2d) + self._POINTS = np.array([], dtype=Point2d) self._UPLEFT = None self._UPRIGHT = None self._DOWNLEFT = None self._DOWNRIGHT = None - self.subBranches = {0 : self._DOWNLEFT, - 1 : self._DOWNRIGHT, - 2 : self._UPLEFT, + self.subBranches = {0 : self._DOWNLEFT, + 1 : self._DOWNRIGHT, + 2 : self._UPLEFT, 3 : self._UPRIGHT} - def GetPointsNumber(self) -> int: - '''Returns the current number of points the branch has. + '''Returns the current number of points the branch has. This method is different from GetTotalPoints, as it only gives the current points - stored in the branch, and not the points stored in the subbranches. + stored in the branch, and not the points stored in the subbranches. If the branch is divided, it will return 0.''' return self._N_POINTS @@ -85,19 +90,19 @@ def IsDivided(self) -> bool: def GetXSize(self) -> tuple: '''Returns the x information of the branch, as a tuple.''' return (self._xmin, self._xmax) - + def GetYSize(self) -> tuple: '''Returns the y information of the branch, as a tuple.''' return (self._ymin, self._ymax) - + def GetXMiddlePoint(self) -> float: '''Returns the middle x point of the branch.''' return self.GetXSize()[0] + (self.GetXSize()[1] - self.GetXSize()[0])/float(2) - + def GetYMiddlePoint(self) -> float: '''Method that returns the middle y point of the branch.''' return self.GetYSize()[0] + (self.GetYSize()[1] - self.GetYSize()[0])/float(2) - + def __str__(self): if self.IsDivided() == True: str = f"number of points = {self.GetTotalPoints()}\n\ @@ -107,41 +112,36 @@ def __str__(self): else: str = f"number of points = {self._N_POINTS}\n[ ]\n[ ]\n[ ]\n" return str - - + def GetPointsList(self) -> np.array: '''Returns a list containing the points existing in that branch.''' return self._POINTS - + + def GetSubBranch(self, index : int): + '''Returns the sub branch by index. Below, the index correspondence: + 0 : Down-left branch + 1 : Down-right branch + 2 : Up-left branch + 3 : Up-right branch + + * index : int index of the branch requested''' + if self.IsDivided() == True: + return self.subBranches[index] + else: + raise ("Error: The branch got no subbranches.") def GetTotalPoints(self) -> int: '''Returns the total number of points in that branch, including the points in the subranches.''' if self.IsDivided() == True: sum = 0 - sum += self._DOWNLEFT.GetTotalPoints() - sum += self._DOWNRIGHT.GetTotalPoints() - sum += self._UPLEFT.GetTotalPoints() - sum += self._UPRIGHT.GetTotalPoints() + for i in range(len(self.subBranches)): + sum += self.GetSubBranch(i).GetTotalPoints() + return sum - + else: return self.GetPointsNumber() - - def GetSubBranch(self, index : int): - '''Returns the sub branch by index. Below, the index correspondence: - 0 : Down-left branch - 1 : Down-right branch - 2 : Up-left branch - 3 : Up-right branch''' - self.subBranches = {0 : self._DOWNLEFT, - 1 : self._DOWNRIGHT, - 2 : self._UPLEFT, - 3 : self._UPRIGHT} - - - return self.subBranches[index] - def RemovePoint(self, index : int): '''Removes the given element of the list of points''' @@ -149,20 +149,19 @@ def RemovePoint(self, index : int): self._POINTS = np.delete(self._POINTS, index) self._N_POINTS -= 1 else: - raise("This branch has no point to be removed.") - + raise ("This branch has no point to be removed.") - def AddPoint(self, point : point2d): - '''Recursive method that adds a point to the branch. \n - * point : point2d to be added.\n + def AddPoint(self, point : Point2d): + '''Recursively adds a point to the branch. \n + * point : Point2d to be added.\n Below, how the method works: 1. The branch is first checked if divided. If it is, the coordinates of the point is checked to add the point in one of the subbranches. 2. If it is not, it checks if the total current number of points is lower than - the branch's defined limit, + the branch's defined limit, 3. If it is, adds the point to the branch. - 4. If it's not ,the branch is then divided and the coordinates of the point are checked to + 4. If it's not ,the branch is then divided and the coordinates of the point are checked to add the point in one of the newly-created subbranches.''' if self.IsDivided() == True: @@ -178,16 +177,40 @@ def AddPoint(self, point : point2d): else: self._UPRIGHT.AddPoint(point) - else: if self.GetPointsNumber() == self._MAX_POINTS: - self._DOWNLEFT = branch2d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYSize()[0], self.GetYMiddlePoint()) - self._DOWNRIGHT = branch2d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYSize()[0], self.GetYMiddlePoint()) - self._UPLEFT = branch2d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYMiddlePoint(), self.GetYSize()[1]) - self._UPRIGHT = branch2d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYMiddlePoint(), self.GetYSize()[1]) + self._DOWNLEFT = Branch2d( + self._MAX_POINTS, + self.GetXSize()[0], + self.GetXMiddlePoint(), + self.GetYSize()[0], + self.GetYMiddlePoint()) + self._DOWNRIGHT = Branch2d( + self._MAX_POINTS, + self.GetXMiddlePoint(), + self.GetXSize()[1], + self.GetYSize()[0], + self.GetYMiddlePoint()) + self._UPLEFT = Branch2d( + self._MAX_POINTS, + self.GetXSize()[0], + self.GetXMiddlePoint(), + self.GetYMiddlePoint(), + self.GetYSize()[1]) + self._UPRIGHT = Branch2d( + self._MAX_POINTS, + self.GetXMiddlePoint(), + self.GetXSize()[1], + self.GetYMiddlePoint(), + self.GetYSize()[1]) self._DIVIDED = True + self.subBranches = {0 : self._DOWNLEFT, + 1 : self._DOWNRIGHT, + 2 : self._UPLEFT, + 3 : self._UPRIGHT} + list = self.GetPointsList() for i in range(self.GetPointsNumber()): @@ -196,7 +219,7 @@ def AddPoint(self, point : point2d): self._DOWNLEFT.AddPoint(list[i]) else: self._UPLEFT.AddPoint(list[i]) - + else: if list[i].GetYCoord() < self.GetYMiddlePoint(): self._DOWNRIGHT.AddPoint(list[i]) @@ -205,7 +228,6 @@ def AddPoint(self, point : point2d): self._UPRIGHT.AddPoint(list[i]) self.RemovePoint(0) - if point.GetXCoord() < self.GetXMiddlePoint(): if point.GetYCoord() < self.GetYMiddlePoint(): self._DOWNLEFT.AddPoint(point) @@ -217,13 +239,12 @@ def AddPoint(self, point : point2d): else: self._UPRIGHT.AddPoint(point) - else: self._POINTS = np.append(self._POINTS, point) self._N_POINTS += 1 def ProcessBranch(self, Function, *args): - '''Abstract recursive method that process the branch or its subbranches with a given function and its arguments. + '''Recursively process the branch or its subbranches with a given function and its arguments. If the function returns any value, it will be returned as the value itself or a list of the values returned by each subbranch processed, if the branch is already divided. * Function : Any function that has only a branch as input @@ -232,22 +253,21 @@ def ProcessBranch(self, Function, *args): list = np.array([]) for i in range(len(self.subBranches)): - list = np.append(list, self.GetSubBranch(i).ProcessBranch(Function, *args)) + list = np.append( + list, self.GetSubBranch(i).ProcessBranch( + Function, *args)) return list - + else: return Function(self, *args) - - - - class Tree2d(): '''Class that stores the root branch and general 2d tree informations. - * branch : branch2d to be the root of the tree.''' - def __init__(self, branch : branch2d): + * branch : Branch2d to be the root of the tree.''' + + def __init__(self, branch : Branch2d): self._ROOT = branch self.xmin = branch.GetXSize()[0] self.xmax = branch.GetXSize()[1] @@ -255,29 +275,42 @@ def __init__(self, branch : branch2d): self.ymax = branch.GetYSize()[1] self._N_POINTS = branch.GetTotalPoints() self._STR = "" - def __str__(self): if self._ROOT.IsDivided() == True: - self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n" + points_list = [] + for i in range(len(self.GetRoot().subBranches)): + points_list.append(self.GetRoot().GetSubBranch(i).GetTotalPoints()) + length = [len(str(x)) for x in points_list] + length = [np.max([length[j], length[j + 2]]) + for j in range(len(points_list)//2)] + self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n" self._STR += f"[ {self._ROOT._UPLEFT.GetTotalPoints()} | {self._ROOT._UPRIGHT.GetTotalPoints()} ]\n" - self._STR += f"[---|---]\n" + self._STR += "[---" + "-"*(length[0] - 1) + \ + "|---" + "-"*(length[1] - 1) + f"]\n" self._STR += f"[ {self._ROOT._DOWNLEFT.GetTotalPoints()} | {self._ROOT._DOWNRIGHT.GetTotalPoints()} ]\n" else: self._STR = f"number of points = {self._N_POINTS}\n[ ]\n[ ]\n[ ]\n" return self._STR - - def AddPoint(self, point : point2d): - '''Method that adds a point to the tree. - * point : point2d to be added into the tree.''' + + def GetRoot(self): + '''Returns the root branch of the tree.''' + return self._ROOT + + def AddPoint(self, point : Point2d): + '''Adds a point to the tree. + * point : Point2d to be added into the tree.''' self._ROOT.AddPoint(point) self._N_POINTS += 1 # END OF QUADTREE IMPLEMENTATION # OCTREE IMPLEMENTATION -class point3d(point2d): + + +class Point3d(Point2d): '''General point class. This class only stores 3d coordinates.''' + def __init__(self, x: float, y: float, z: float): self.coord = np.array([x, y, z]) @@ -285,56 +318,62 @@ def __call__(self): return self.coord def GetZCoord(self): - '''Method that returns the z coordinate of the point.''' + '''Returns the z coordinate of the point.''' return self.coord[2] - + def GetNorm(self): - '''Method that returns the norm of the point in relation to the origin.''' + '''Returns the norm of the point in relation to the origin.''' return np.sqrt(self.GetXCoord()**2 + self.GetYCoord()**2 + self.GetZCoord()**2) - + def SetCoord(self, x: float, y: float, z : float): - '''Method that updates the coordinate of the point. + '''Updates the coordinate of the point. * x : float x position * y : float y position * z : float z position''' self.coord = np.array([x, y, z]) def __str__(self): - string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + str('%.3f' % self.coord[2]) +"]" + string = "[" + str('%.3f' % + self.coord[0]) + ", " + str('%.3f' % + self.coord[1]) + str('%.3f' % + self.coord[2]) + "]" return string - + def __repr__(self): - string = "[" + str('%.3f' % self.coord[0]) + ", " + str('%.3f' % self.coord[1]) + str('%.3f' % self.coord[2]) + "]" + string = "[" + str('%.3f' % + self.coord[0]) + ", " + str('%.3f' % + self.coord[1]) + str('%.3f' % + self.coord[2]) + "]" return string -class branch3d(branch2d): +class Branch3d(Branch2d): '''General 3d branch class. It is the base structure for building an octree.\n * max_points : An integer defining the maximum number of points this branch stores * x_min : The float lower limit for x * x_max : The float upper limit for x * y_min : The float lower limit for y - * y_max : The float upper limit for y + * y_max : The float upper limit for y * z_min : The float front limit for z * z_max : The float back limit for z \n This branch, when divided, has eight branches: \n 1. the front-down-left - 2. the front-down-right + 2. the front-down-right 3. the front-up-left 4. the front-up-right 5. the back-down-left - 6. the back-down-right + 6. the back-down-right 7. the back-up-left 8. the back-up-right''' - def __init__(self, - max_points: int, - x_min: float, - x_max: float, - y_min: float, - y_max: float, - z_min: float, + + def __init__(self, + max_points: int, + x_min: float, + x_max: float, + y_min: float, + y_max: float, + z_min: float, z_max: float): - self._DIVIDED = False self._MAX_POINTS = max_points self._N_POINTS = 0 @@ -344,7 +383,7 @@ def __init__(self, self._ymax = y_max self._zmin = z_min self._zmax = z_max - self._POINTS = np.array([], dtype = point3d) + self._POINTS = np.array([], dtype=Point3d) self._FRONTUPLEFT = None self._FRONTUPRIGHT = None self._FRONTDOWNLEFT = None @@ -353,40 +392,22 @@ def __init__(self, self._BACKUPRIGHT = None self._BACKDOWNLEFT = None self._BACKDOWNRIGHT = None - self.subBranches = {0 : self._FRONTDOWNLEFT, - 1 : self._FRONTDOWNRIGHT, - 2 : self._FRONTUPLEFT, - 3 : self._FRONTUPRIGHT, + self.subBranches = {0 : self._FRONTDOWNLEFT, + 1 : self._FRONTDOWNRIGHT, + 2 : self._FRONTUPLEFT, + 3 : self._FRONTUPRIGHT, 4 : self._BACKDOWNLEFT, 5 : self._BACKDOWNRIGHT, 6 : self._BACKUPLEFT, 7 : self._BACKUPRIGHT} - - def GetZSize(self) ->tuple: - '''Method that returns the y information of the branch, as a tuple.''' + + def GetZSize(self) -> tuple: + '''Returns the y information of the branch, as a tuple.''' return (self._zmin, self._zmax) def GetZMiddlePoint(self) -> float: - '''Method that returns the middle z point of the branch.''' + '''Returns the middle z point of the branch.''' return self.GetZSize()[0] + (self.GetZSize()[1] - self.GetZSize()[0])/float(2) - - def GetTotalPoints(self) -> int: - '''Returns the total number of points in that branch, including the points in the subranches.''' - if self.IsDivided() == True: - - sum = 0 - sum += self._FRONTDOWNLEFT.GetTotalPoints() - sum += self._FRONTDOWNRIGHT.GetTotalPoints() - sum += self._FRONTUPLEFT.GetTotalPoints() - sum += self._FRONTUPRIGHT.GetTotalPoints() - sum += self._BACKDOWNLEFT.GetTotalPoints() - sum += self._BACKDOWNRIGHT.GetTotalPoints() - sum += self._BACKUPLEFT.GetTotalPoints() - sum += self._BACKUPRIGHT.GetTotalPoints() - return sum - - else: - return self.GetPointsNumber() def __str__(self): if self.IsDivided() == True: @@ -402,7 +423,7 @@ def __str__(self): else: str = f"number of points = {self._N_POINTS}\n| FRONT\n[ ]\n[ ]\n[ ]\n| BACK\n[ ]\n[ ]\n[ ]\n" return str - + def GetSubBranch(self, index : int): '''Returns the sub branch by index. Below, the index correspondence: 0 : Front-down-left branch @@ -413,32 +434,24 @@ def GetSubBranch(self, index : int): 5 : Back-down-right branch 6 : Back-up-left branch 7 : Back-up-right branch - + * index : int index of the branch requested''' - - self.subBranches = {0 : self._FRONTDOWNLEFT, - 1 : self._FRONTDOWNRIGHT, - 2 : self._FRONTUPLEFT, - 3 : self._FRONTUPRIGHT, - 4 : self._BACKDOWNLEFT, - 5 : self._BACKDOWNRIGHT, - 6 : self._BACKUPLEFT, - 7 : self._BACKUPRIGHT} - - return self.subBranches[index] - + if self.IsDivided() == True: + return self.subBranches[index] + else: + raise ("Error: The branch got no subbranches.") - def AddPoint(self, point : point3d): - '''Recursive method that adds a point to the branch. \n - * point : point3d to be added.\n + def AddPoint(self, point : Point3d): + '''Recursively adds a point to the branch. \n + * point : Point3d to be added.\n Below, how the method works: 1. The branch is first checked if divided. If it is, the coordinates of the point is checked to add the point in one of the subbranches. 2. If it is not, it checks if the total current number of points is lower than - the branch's defined limit, + the branch's defined limit, 3. If it is, adds the point to the branch. - 4. If it's not ,the branch is then divided and the coordinates of the point are checked to + 4. If it's not ,the branch is then divided and the coordinates of the point are checked to add the point in one of the newly-created subbranches.''' if self.IsDivided() == True: @@ -464,20 +477,85 @@ def AddPoint(self, point : point3d): self._FRONTUPRIGHT.AddPoint(point) else: self._BACKUPRIGHT.AddPoint(point) - + else: if self.GetPointsNumber() == self._MAX_POINTS: - self._FRONTDOWNLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZSize()[0], self.GetZMiddlePoint()) - self._FRONTDOWNRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZSize()[0], self.GetZMiddlePoint()) - self._FRONTUPLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZSize()[0], self.GetZMiddlePoint()) - self._FRONTUPRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZSize()[0], self.GetZMiddlePoint()) - self._BACKDOWNLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZMiddlePoint(), self.GetZSize()[1]) - self._BACKDOWNRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYSize()[0], self.GetYMiddlePoint(), self.GetZMiddlePoint(), self.GetZSize()[1]) - self._BACKUPLEFT = branch3d(self._MAX_POINTS, self.GetXSize()[0], self.GetXMiddlePoint(), self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZMiddlePoint(), self.GetZSize()[1]) - self._BACKUPRIGHT = branch3d(self._MAX_POINTS, self.GetXMiddlePoint(), self.GetXSize()[1], self.GetYMiddlePoint(), self.GetYSize()[1], self.GetZMiddlePoint(), self.GetZSize()[1]) + self._FRONTDOWNLEFT = Branch3d( + self._MAX_POINTS, + self.GetXSize()[0], + self.GetXMiddlePoint(), + self.GetYSize()[0], + self.GetYMiddlePoint(), + self.GetZSize()[0], + self.GetZMiddlePoint()) + self._FRONTDOWNRIGHT = Branch3d( + self._MAX_POINTS, + self.GetXMiddlePoint(), + self.GetXSize()[1], + self.GetYSize()[0], + self.GetYMiddlePoint(), + self.GetZSize()[0], + self.GetZMiddlePoint()) + self._FRONTUPLEFT = Branch3d( + self._MAX_POINTS, + self.GetXSize()[0], + self.GetXMiddlePoint(), + self.GetYMiddlePoint(), + self.GetYSize()[1], + self.GetZSize()[0], + self.GetZMiddlePoint()) + self._FRONTUPRIGHT = Branch3d( + self._MAX_POINTS, + self.GetXMiddlePoint(), + self.GetXSize()[1], + self.GetYMiddlePoint(), + self.GetYSize()[1], + self.GetZSize()[0], + self.GetZMiddlePoint()) + self._BACKDOWNLEFT = Branch3d( + self._MAX_POINTS, + self.GetXSize()[0], + self.GetXMiddlePoint(), + self.GetYSize()[0], + self.GetYMiddlePoint(), + self.GetZMiddlePoint(), + self.GetZSize()[1]) + self._BACKDOWNRIGHT = Branch3d( + self._MAX_POINTS, + self.GetXMiddlePoint(), + self.GetXSize()[1], + self.GetYSize()[0], + self.GetYMiddlePoint(), + self.GetZMiddlePoint(), + self.GetZSize()[1]) + self._BACKUPLEFT = Branch3d( + self._MAX_POINTS, + self.GetXSize()[0], + self.GetXMiddlePoint(), + self.GetYMiddlePoint(), + self.GetYSize()[1], + self.GetZMiddlePoint(), + self.GetZSize()[1]) + self._BACKUPRIGHT = Branch3d( + self._MAX_POINTS, + self.GetXMiddlePoint(), + self.GetXSize()[1], + self.GetYMiddlePoint(), + self.GetYSize()[1], + self.GetZMiddlePoint(), + self.GetZSize()[1]) self._DIVIDED = True + self.subBranches = {0 : self._FRONTDOWNLEFT, + 1 : self._FRONTDOWNRIGHT, + 2 : self._FRONTUPLEFT, + 3 : self._FRONTUPRIGHT, + 4 : self._BACKDOWNLEFT, + 5 : self._BACKDOWNRIGHT, + 6 : self._BACKUPLEFT, + 7 : self._BACKUPRIGHT} + list = self.GetPointsList() for i in range(self.GetPointsNumber()): @@ -505,7 +583,6 @@ def AddPoint(self, point : point3d): self._BACKUPRIGHT.AddPoint(list[i]) self.RemovePoint(0) - if point.GetXCoord() < self.GetXMiddlePoint(): if point.GetYCoord() < self.GetYMiddlePoint(): if point.GetZCoord() < self.GetZMiddlePoint(): @@ -529,16 +606,16 @@ def AddPoint(self, point : point3d): else: self._BACKUPRIGHT.AddPoint(point) - else: self._POINTS = np.append(self._POINTS, point) self._N_POINTS += 1 -class Tree3d(): +class Tree3d(Tree2d): '''Class that stores the root branch and general 3d tree informations. - * branch : branch3d to be the root of the tree.''' - def __init__(self, branch : branch3d): + * branch : Branch3d to be the root of the tree.''' + + def __init__(self, branch : Branch3d): self._ROOT = branch self.xmin = branch.GetXSize()[0] self.xmax = branch.GetXSize()[1] @@ -548,18 +625,23 @@ def __init__(self, branch : branch3d): self.zmax = branch.GetZSize()[1] self._N_POINTS = branch.GetTotalPoints() self._STR = "" - def __str__(self): if self._ROOT.IsDivided() == True: + points_list = [] + for i in range(len(self.GetRoot().subBranches)): + points_list.append(self.GetRoot().GetSubBranch(i).GetTotalPoints()) + length = [len(str(x)) for x in points_list] + length = [np.max([length[j], length[j + 2]]) + for j in range(len(points_list)//2)] self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n\ FRONT\n\ [ {self._ROOT._FRONTUPLEFT.GetTotalPoints()} | {self._ROOT._FRONTUPRIGHT.GetTotalPoints()} ]\n\ - [---|---]\n\ + [---" + "-"*(length[0] - 1) + "|---" + "-"*(length[1] - 1) + f"]\n\ [ {self._ROOT._FRONTDOWNLEFT.GetTotalPoints()} | {self._ROOT._FRONTDOWNRIGHT.GetTotalPoints()} ]\n\ | BACK\n\ [ {self._ROOT._BACKUPLEFT.GetTotalPoints()} | {self._ROOT._BACKUPRIGHT.GetTotalPoints()} ]\n\ - [---|---]\n\ + [---" + "-"*(length[2] - 1) + "|---" + "-"*(length[3] - 1) + f"]\n\ [ {self._ROOT._BACKDOWNLEFT.GetTotalPoints()} | {self._ROOT._BACKDOWNRIGHT.GetTotalPoints()} ]\n" else: self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n FRONT\n\ @@ -571,24 +653,13 @@ def __str__(self): [ ]\n\ [ ]\n" return self._STR - - def GetRoot(self) -> branch3d: - '''Returns the root branch of the tree.''' - return self._ROOT - - def AddPoint(self, point : point3d): - '''Adds a point to the tree. - * point : point3d to be added into the tree.''' - self._ROOT.AddPoint(point) - self._N_POINTS += 1 # END OF OCTREE IMPLEMENTATION - -# GRAPH IMPLEMENTATION -def BoundingBox(center : tuple = (0.0, 0.0, 0.0), - size : tuple = (1.0, 1.0, 1.0), - color : tuple = (1.0, 1.0, 0.0, 1.0), +# GRAPHICAL IMPLEMENTATION +def BoundingBox(center : tuple = (0.0, 0.0, 0.0), + size : tuple = (1.0, 1.0, 1.0), + color : tuple = (1.0, 1.0, 1.0, 1.0), line_width : float = 1.0): '''Creates a bounding box with the parameters given. The box got only is edges renderized. * center : tuple with 3 coordinates, x, y, and z, that determines where is the center of the box. @@ -599,46 +670,104 @@ def BoundingBox(center : tuple = (0.0, 0.0, 0.0), y_c = center[1] z_c = center[2] - cube = vtkCubeSource() - cube.SetXLength(size[0]) - cube.SetYLength(size[1]) - cube.SetZLength(size[2]) - cube.SetCenter(x_c, y_c, z_c) - - edges = vtkExtractEdges() - edges.SetInputConnection(cube.GetOutputPort()) - - cubeMapper = utils.PolyDataMapper() - cubeMapper.SetInputConnection(edges.GetOutputPort()) - - cubeActor = utils.get_actor_from_polymapper(cubeMapper) - cubeActor.SetProperty(vtkProperty().SetLineWidth(line_width)) - cubeActor.GetProperty().SetColor(color[0], color[1], color[2]) - cubeActor.GetProperty().SetLighting(False) - - return cubeActor - - -def GetActorFromBranch(branch : branch3d) -> utils.Actor: - '''Recursive function that creates actors for the branch given. If the branch is divided, - then the function is run for the subbranches until the function reaches a non-divided branch, + x_l = size[0]/2 + y_l = size[1]/2 + z_l = size[2]/2 + + vertices = np.array([ + [x_c - x_l, y_c - y_l, z_c - z_l], + [x_c + x_l, y_c - y_l, z_c - z_l], + [x_c + x_l, y_c + y_l, z_c - z_l], + [x_c - x_l, y_c + y_l, z_c - z_l], + [x_c - x_l, y_c - y_l, z_c + z_l], + [x_c + x_l, y_c - y_l, z_c + z_l], + [x_c + x_l, y_c + y_l, z_c + z_l], + [x_c - x_l, y_c + y_l, z_c + z_l] + + ]) + + data = np.array([ + [vertices[0], vertices[4]], + [vertices[3], vertices[7]], + [vertices[1], vertices[5]], + [vertices[2], vertices[6]], + [vertices[0], vertices[1]], + [vertices[3], vertices[2]], + [vertices[7], vertices[6]], + [vertices[4], vertices[5]], + [vertices[0], vertices[3]], + [vertices[1], vertices[2]], + [vertices[4], vertices[7]], + [vertices[5], vertices[6]] + ]) + + lines = line(data, colors=color) + lines.GetProperty().SetLineWidth(line_width) + lines.GetProperty().SetLighting(False) + + return lines + + +def GetActorFromBranch(branch : Branch3d, color=( + 1.0, 1.0, 1.0), linewidth=1.0) -> Actor: + '''Recursively creates actors for the branch given. If the branch is divided, + then the function is run for the subbranches until the function reaches a non-divided branch, that creates the actor to be returned. This actor is then appended into a list, that is then returned. - * branch : branch3d that will have the actor created.''' + * branch : Branch3d that will have the actor created.''' if branch.IsDivided() == True: - actors = np.array([], dtype = utils.Actor) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(0))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(1))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(2))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(3))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(4))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(5))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(6))) - actors = np.append(actors, GetActorFromBranch(branch.GetSubBranch(7))) - + actors = np.array([], dtype=Actor) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(0), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(1), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(2), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(3), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(4), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(5), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(6), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch( + branch.GetSubBranch(7), + color, + linewidth)) return actors - + else: data = np.array([]) @@ -654,7 +783,7 @@ def GetActorFromBranch(branch : branch3d) -> utils.Actor: y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) z_l = (branch.GetZSize()[1] - branch.GetZSize()[0]) - cubeActor = BoundingBox((x_c, y_c, z_c), (x_l, y_l, z_l), (0.5, 0.5, 0.5), 3.0) + cubeActor = BoundingBox((x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) return cubeActor -# END OF GRAPH IMPLEMENTATION \ No newline at end of file +# END OF GRAPH IMPLEMENTATION From bf446e79cfaa2043901dd7749d0de838ba2c9acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Dell=20Agli=20Floriano?= <61475630+JoaoDell@users.noreply.github.com> Date: Thu, 13 Apr 2023 20:38:45 -0300 Subject: [PATCH 05/10] Removed local path import --- fury/tests/test_trees.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index cfeeb0af4..6f902b0b7 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -1,7 +1,4 @@ -import sys -sys.path.append('C:/Users/Lampada/Desktop/GSoC/fury/fury/') -import trees -from fury import window, actor, lib +from fury import window, actor, lib, trees import numpy as np From 7a3ca0933b477e0a6ecb77c8c6a4f66c05458a3b Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Fri, 14 Apr 2023 13:59:40 -0300 Subject: [PATCH 06/10] Added a 2d rendering for quadtrees and improved exception handling --- fury/tests/test_trees.py | 51 +++++++- fury/trees.py | 260 ++++++++++++++++++++++----------------- 2 files changed, 191 insertions(+), 120 deletions(-) diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index cfeeb0af4..cc3fc6a7b 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -1,5 +1,3 @@ -import sys -sys.path.append('C:/Users/Lampada/Desktop/GSoC/fury/fury/') import trees from fury import window, actor, lib import numpy as np @@ -19,11 +17,55 @@ print(tree) +actorslist = trees.GetActorFromBranch2d(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) + +zeros = np.zeros((npoints, 1)) +actorPoints = actor.dots(np.hstack((points, zeros)), (1.0, 0.5, 0.4), 1, 5) + +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.GetRoot().GetXMiddlePoint(), + tree.GetRoot().GetYMiddlePoint(), + 0.0), + view_up=(0.0, 0.0, 0.0)) +showmanager = window.ShowManager( + scene, + "trees demo", + (1080, + 1080), + reset_camera=True, + order_transparent=True) + +scene.add(actorPoints) + +if tree.GetRoot().IsDivided(): + for i in range(len(actorslist)): + scene.add(actorslist[i]) +else: + scene.add(actorslist) + + +interactor = lib.RenderWindowInteractor() +interactor.SetRenderWindow(showmanager.window) + +interactor.Initialize() +showmanager.render() +interactor.Start() + + +xl = 1.0 +yl = 2.0 +zl = 3.0 + + # TESTING AN OCTREE WITH RANDOM 3D POINTS -br = trees.Branch3d(4, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0) +br = trees.Branch3d(4, 0.0, xl, 0.0, yl, 0.0, zl) npoints = 200 data = np.random.rand(npoints, 3) +data[:, 0] = xl*data[:, 0] +data[:, 1] = yl*data[:, 1] +data[:, 2] = zl*data[:, 2] np.random.seed() tree = trees.Tree3d(br) @@ -61,7 +103,8 @@ def abstract(branch : trees.Branch3d, number): scene.add(actorPoints) -actorslist = trees.GetActorFromBranch(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) +actorslist = trees.GetActorFromBranch3d(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) + if tree.GetRoot().IsDivided(): for i in range(len(actorslist)): diff --git a/fury/trees.py b/fury/trees.py index 6a0aa8128..d8e808240 100644 --- a/fury/trees.py +++ b/fury/trees.py @@ -128,7 +128,7 @@ def GetSubBranch(self, index : int): if self.IsDivided() == True: return self.subBranches[index] else: - raise ("Error: The branch got no subbranches.") + raise AttributeError("The branch got no subbranches.") def GetTotalPoints(self) -> int: '''Returns the total number of points in that branch, including the points in the subranches.''' @@ -149,7 +149,30 @@ def RemovePoint(self, index : int): self._POINTS = np.delete(self._POINTS, index) self._N_POINTS -= 1 else: - raise ("This branch has no point to be removed.") + raise AttributeError("This branch has no point to be removed.") + + # def SubAddPoint(self): + + def SubAddPoint(self, point : Point2d): + if self.GetXSize()[0] <= point.GetXCoord() < self.GetXMiddlePoint(): + if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): + self._DOWNLEFT.AddPoint(point) + elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: + self._UPLEFT.AddPoint(point) + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}).") + elif self.GetXMiddlePoint() <= point.GetXCoord() <= self.GetXSize()[1]: + if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): + self._DOWNRIGHT.AddPoint(point) + elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: + self._UPRIGHT.AddPoint(point) + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}).") + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}).") def AddPoint(self, point : Point2d): '''Recursively adds a point to the branch. \n @@ -166,16 +189,7 @@ def AddPoint(self, point : Point2d): if self.IsDivided() == True: - if point.GetXCoord() < self.GetXMiddlePoint(): - if point.GetYCoord() < self.GetYMiddlePoint(): - self._DOWNLEFT.AddPoint(point) - else: - self._UPLEFT.AddPoint(point) - else: - if point.GetYCoord() < self.GetYMiddlePoint(): - self._DOWNRIGHT.AddPoint(point) - else: - self._UPRIGHT.AddPoint(point) + self.SubAddPoint(point) else: @@ -214,30 +228,10 @@ def AddPoint(self, point : Point2d): list = self.GetPointsList() for i in range(self.GetPointsNumber()): - if list[i].GetXCoord() < self.GetXMiddlePoint(): - if list[i].GetYCoord() < self.GetYMiddlePoint(): - self._DOWNLEFT.AddPoint(list[i]) - else: - self._UPLEFT.AddPoint(list[i]) - - else: - if list[i].GetYCoord() < self.GetYMiddlePoint(): - self._DOWNRIGHT.AddPoint(list[i]) - else: - - self._UPRIGHT.AddPoint(list[i]) + self.SubAddPoint(list[i]) self.RemovePoint(0) - if point.GetXCoord() < self.GetXMiddlePoint(): - if point.GetYCoord() < self.GetYMiddlePoint(): - self._DOWNLEFT.AddPoint(point) - else: - self._UPLEFT.AddPoint(point) - else: - if point.GetYCoord() < self.GetYMiddlePoint(): - self._DOWNRIGHT.AddPoint(point) - else: - self._UPRIGHT.AddPoint(point) + self.SubAddPoint(point) else: self._POINTS = np.append(self._POINTS, point) @@ -439,7 +433,48 @@ def GetSubBranch(self, index : int): if self.IsDivided() == True: return self.subBranches[index] else: - raise ("Error: The branch got no subbranches.") + raise AttributeError("The branch got no subbranches.") + + def SubAddPoint(self, point : Point3d): + if self.GetXSize()[0] <= point.GetXCoord() < self.GetXMiddlePoint(): + if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): + if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNLEFT.AddPoint(point) + elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: + self._BACKDOWNLEFT.AddPoint(point) + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + + elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: + if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPLEFT.AddPoint(point) + elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: + self._BACKUPLEFT.AddPoint(point) + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + elif self.GetXMiddlePoint() <= point.GetXCoord() <= self.GetXSize()[1]: + if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): + if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTDOWNRIGHT.AddPoint(point) + elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: + self._BACKDOWNRIGHT.AddPoint(point) + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: + if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): + self._FRONTUPRIGHT.AddPoint(point) + elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: + self._BACKUPRIGHT.AddPoint(point) + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + + else: + raise ValueError( + f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") def AddPoint(self, point : Point3d): '''Recursively adds a point to the branch. \n @@ -455,28 +490,7 @@ def AddPoint(self, point : Point3d): add the point in one of the newly-created subbranches.''' if self.IsDivided() == True: - if point.GetXCoord() < self.GetXMiddlePoint(): - if point.GetYCoord() < self.GetYMiddlePoint(): - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNLEFT.AddPoint(point) - else: - self._BACKDOWNLEFT.AddPoint(point) - else: - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPLEFT.AddPoint(point) - else: - self._BACKUPLEFT.AddPoint(point) - else: - if point.GetYCoord() < self.GetYMiddlePoint(): - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNRIGHT.AddPoint(point) - else: - self._BACKDOWNRIGHT.AddPoint(point) - else: - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPRIGHT.AddPoint(point) - else: - self._BACKUPRIGHT.AddPoint(point) + self.SubAddPoint(point) else: @@ -559,52 +573,10 @@ def AddPoint(self, point : Point3d): list = self.GetPointsList() for i in range(self.GetPointsNumber()): - if list[i].GetXCoord() < self.GetXMiddlePoint(): - if list[i].GetYCoord() < self.GetYMiddlePoint(): - if list[i].GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNLEFT.AddPoint(list[i]) - else: - self._BACKDOWNLEFT.AddPoint(list[i]) - else: - if list[i].GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPLEFT.AddPoint(list[i]) - else: - self._BACKUPLEFT.AddPoint(list[i]) - else: - if list[i].GetYCoord() < self.GetYMiddlePoint(): - if list[i].GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNRIGHT.AddPoint(list[i]) - else: - self._BACKDOWNRIGHT.AddPoint(list[i]) - else: - if list[i].GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPRIGHT.AddPoint(list[i]) - else: - self._BACKUPRIGHT.AddPoint(list[i]) + self.SubAddPoint(list[i]) self.RemovePoint(0) - if point.GetXCoord() < self.GetXMiddlePoint(): - if point.GetYCoord() < self.GetYMiddlePoint(): - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNLEFT.AddPoint(point) - else: - self._BACKDOWNLEFT.AddPoint(point) - else: - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPLEFT.AddPoint(point) - else: - self._BACKUPLEFT.AddPoint(point) - else: - if point.GetYCoord() < self.GetYMiddlePoint(): - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNRIGHT.AddPoint(point) - else: - self._BACKDOWNRIGHT.AddPoint(point) - else: - if point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPRIGHT.AddPoint(point) - else: - self._BACKUPRIGHT.AddPoint(point) + self.SubAddPoint(point) else: self._POINTS = np.append(self._POINTS, point) @@ -657,10 +629,10 @@ def __str__(self): # GRAPHICAL IMPLEMENTATION -def BoundingBox(center : tuple = (0.0, 0.0, 0.0), - size : tuple = (1.0, 1.0, 1.0), - color : tuple = (1.0, 1.0, 1.0, 1.0), - line_width : float = 1.0): +def BoundingBox3d(center : tuple = (0.0, 0.0, 0.0), + size : tuple = (1.0, 1.0, 1.0), + color : tuple = (1.0, 1.0, 1.0, 1.0), + line_width : float = 1.0): '''Creates a bounding box with the parameters given. The box got only is edges renderized. * center : tuple with 3 coordinates, x, y, and z, that determines where is the center of the box. * size : tuple with 3 coordinates, x, y, and z, that determines its lateral sizes. @@ -683,7 +655,6 @@ def BoundingBox(center : tuple = (0.0, 0.0, 0.0), [x_c + x_l, y_c - y_l, z_c + z_l], [x_c + x_l, y_c + y_l, z_c + z_l], [x_c - x_l, y_c + y_l, z_c + z_l] - ]) data = np.array([ @@ -708,7 +679,64 @@ def BoundingBox(center : tuple = (0.0, 0.0, 0.0), return lines -def GetActorFromBranch(branch : Branch3d, color=( +def GetActorFromBranch2d(branch : Branch2d, color=( + 1.0, 1.0, 1.0), linewidth=1.0) -> Actor: + '''Recursively creates actors for the branch given. If the branch is divided, + then the function is run for the subbranches until the function reaches a non-divided branch, + that creates the actor to be returned. This actor is then appended into a list, that is then returned. + NOTE: This returns a 3d actor. + * branch : Branch3d that will have the actor created.''' + + if branch.IsDivided() == True: + actors = np.array([], dtype=Actor) + actors = np.append( + actors, + GetActorFromBranch2d( + branch.GetSubBranch(0), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch2d( + branch.GetSubBranch(1), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch2d( + branch.GetSubBranch(2), + color, + linewidth)) + actors = np.append( + actors, + GetActorFromBranch2d( + branch.GetSubBranch(3), + color, + linewidth)) + + return actors + + else: + + data = np.array([]) + data0 = branch.GetPointsList() + for i in range(data0.shape[0]): + np.append(data, data0[i]()) + + x_c = branch.GetXMiddlePoint() + y_c = branch.GetYMiddlePoint() + z_c = 0.0 + + x_l = (branch.GetXSize()[1] - branch.GetXSize()[0]) + y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) + z_l = 0.0 + + cubeActor = BoundingBox3d((x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) + + return cubeActor + + +def GetActorFromBranch3d(branch : Branch3d, color=( 1.0, 1.0, 1.0), linewidth=1.0) -> Actor: '''Recursively creates actors for the branch given. If the branch is divided, then the function is run for the subbranches until the function reaches a non-divided branch, @@ -719,49 +747,49 @@ def GetActorFromBranch(branch : Branch3d, color=( actors = np.array([], dtype=Actor) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(0), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(1), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(2), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(3), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(4), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(5), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(6), color, linewidth)) actors = np.append( actors, - GetActorFromBranch( + GetActorFromBranch3d( branch.GetSubBranch(7), color, linewidth)) @@ -783,7 +811,7 @@ def GetActorFromBranch(branch : Branch3d, color=( y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) z_l = (branch.GetZSize()[1] - branch.GetZSize()[0]) - cubeActor = BoundingBox((x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) + cubeActor = BoundingBox3d((x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) return cubeActor # END OF GRAPH IMPLEMENTATION From b5db0ff41fe8f2f95bf4fd1497ca268a2dc78bd1 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Fri, 14 Apr 2023 14:10:02 -0300 Subject: [PATCH 07/10] Improved exception handling and added rendering for quadtrees --- fury/tests/test_trees.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index 5ebd0399c..07ed9e347 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -1,7 +1,4 @@ -import sys -sys.path.append('C:/Users/Lampada/Desktop/GSoC/fury/fury/') -import trees -from fury import window, actor, lib +from fury import window, actor, lib, trees import numpy as np @@ -20,7 +17,7 @@ actorslist = trees.GetActorFromBranch2d(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) -zeros = np.zeros((10, 1)) +zeros = np.zeros((npoints, 1)) actorPoints = actor.dots(np.hstack((points, zeros)), (1.0, 0.5, 0.4), 1, 5) scene = window.Scene() @@ -37,7 +34,7 @@ reset_camera=True, order_transparent=True) -# scene.add(actorPoints) +scene.add(actorPoints) if tree.GetRoot().IsDivided(): for i in range(len(actorslist)): From acaca5ca86f8c2cd482648a2d5df7dda1de22285 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Fri, 21 Apr 2023 11:37:46 -0300 Subject: [PATCH 08/10] Changed test_trees.py to a pytest testing program, implemented some additional functions and overloads into trees.py, and moved the last version of test_trees as an example into exmaples. --- docs/examples/trees_example.py | 217 ++++++ fury/tests/test_trees.py | 566 +++++++++++--- fury/trees.py | 1343 +++++++++++++++++++------------- 3 files changed, 1484 insertions(+), 642 deletions(-) create mode 100644 docs/examples/trees_example.py diff --git a/docs/examples/trees_example.py b/docs/examples/trees_example.py new file mode 100644 index 000000000..af0be0cf8 --- /dev/null +++ b/docs/examples/trees_example.py @@ -0,0 +1,217 @@ +import trees +from fury import window, actor, lib +import numpy as np + + +# TESTING A QUADTREE OF RANDOM 2D POINTS +br = trees.Branch2d(4, 0.0, 1.0, 0.0, 1.0) + +npoints = 5 + +points = np.random.rand(npoints, 2) + +tree = trees.Tree2d(br) +for i in range(npoints): + tree.add_point(trees.Point2d(points[i, 0], points[i, 1])) + +actorslist = trees.actor_from_branch_2d(tree.root(), (1.0, 1.0, 1.0), 1.0) +zeros = np.zeros((npoints, 1)) +actorPoints = actor.dots(np.hstack((points, zeros)), (1.0, 0.5, 0.4), 1, 5) + +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.root().x_mid_point(), + tree.root().y_mid_point(), + 0.0), + view_up=(0.0, 0.0, 0.0)) +showmanager = window.ShowManager( + scene, + "trees demo", + (1920, + 1080), + reset_camera=True, + order_transparent=True) + +scene.add(actorPoints) + +if tree.root().is_divided(): + for i in range(len(actorslist)): + scene.add(actorslist[i]) +else: + scene.add(actorslist) + + +interactor = lib.RenderWindowInteractor() +interactor.SetRenderWindow(showmanager.window) + +interactor.Initialize() +showmanager.render() +interactor.Start() + + +# TESTING THE UPDATE FUNCTION +def divide(branch, div : float): + for i in range(branch.points_list().shape[0]): + update_coord = branch.points_list()[i]()/div + branch.points_list()[i].set_coord(update_coord[0], update_coord[1]) + +div = 2.0 +tree.root().process_branch(divide, div) + +print("Before update") +print(tree) +tree.root().update() +print("After update") +print(tree) +actorslist = trees.actor_from_branch_2d(tree.root(), (1.0, 1.0, 1.0), 1.0) +zeros = np.zeros((npoints, 1)) +actorPoints = actor.dots(np.hstack((points, zeros))/div, (1.0, 0.5, 0.4), 1, 5) + +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.root().x_mid_point(), + tree.root().y_mid_point(), + 0.0), + view_up=(0.0, 0.0, 0.0)) +showmanager = window.ShowManager( + scene, + "trees demo", + (1920, + 1080), + reset_camera=True, + order_transparent=True) + +scene.add(actorPoints) + +if tree.root().is_divided(): + for i in range(len(actorslist)): + scene.add(actorslist[i]) +else: + scene.add(actorslist) + + +interactor = lib.RenderWindowInteractor() +interactor.SetRenderWindow(showmanager.window) + +interactor.Initialize() +showmanager.render() +interactor.Start() + + + +# TESTING AN OCTREE WITH RANDOM 3D POINTS +xl = 1.0 +yl = 2.0 +zl = 3.0 +br = trees.Branch3d(4, 0.0, xl, 0.0, yl, 0.0, zl) +npoints = 200 + +np.random.seed(101) +data = np.random.rand(npoints, 3) +data[:, 0] = xl*data[:, 0] +data[:, 1] = yl*data[:, 1] +data[:, 2] = zl*data[:, 2] + +tree = trees.Tree3d(br) +for i in range(npoints): + tree.add_point(trees.Point3d(data[i, 0], data[i, 1], data[i, 2])) + +print(tree) + + +# BELOW, THE ABSTRACT PROCESSING METHOD +def abstract(branch : trees.Branch3d, number): + return number + branch.n_points() + + +print(tree.root().process_branch(abstract, 0)) + + +# FOR THIS EXAMPLE, LET'S RENDER THE OCTREE WITH THE PROVIDED FUNCTIONS +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.root().x_mid_point(), + tree.root().y_mid_point(), + tree.root().z_mid_point()), + view_up=(0.0, 0.0, 0.0)) +showmanager = window.ShowManager( + scene, + "trees demo", + (1920, + 1080), + reset_camera=True, + order_transparent=True) + + +actorPoints = actor.dots(data, (1.0, 0.5, 0.4), 1, 5) +scene.add(actorPoints) + + +actorslist = trees.actor_from_branch_3d(tree.root(), (1.0, 1.0, 1.0), 1.0) + + +if tree.root().is_divided(): + for i in range(len(actorslist)): + scene.add(actorslist[i]) +else: + scene.add(actorslist) + + +interactor = lib.RenderWindowInteractor() +interactor.SetRenderWindow(showmanager.window) + +interactor.Initialize() +showmanager.render() +interactor.Start() + + +def divide(branch, div : float): + for i in range(branch.points_list().shape[0]): + update_coord = branch.points_list()[i]()/div + branch.points_list()[i].set_coord(update_coord[0], update_coord[1], update_coord[2]) + + +div = 2.0 +tree.root().process_branch(divide, div) + +tree.root().update() + +actorslist = trees.actor_from_branch_3d(tree.root(), (1.0, 1.0, 1.0), 1.0) +zeros = np.zeros((npoints, 1)) +actorPoints = actor.dots(np.hstack((data, zeros))/div, (1.0, 0.5, 0.4), 1, 5) + +scene = window.Scene() +scene.set_camera(position=(-6, 5, -10), + focal_point=(tree.root().x_mid_point(), + tree.root().y_mid_point(), + tree.root().z_mid_point()), + view_up=(0.0, 0.0, 0.0)) +showmanager = window.ShowManager( + scene, + "trees demo", + (1920, + 1080), + reset_camera=True, + order_transparent=True) + + +actorPoints = actor.dots(data/div, (1.0, 0.5, 0.4), 1, 5) +scene.add(actorPoints) + + +actorslist = trees.actor_from_branch_3d(tree.root(), (1.0, 1.0, 1.0), 1.0) + + +if tree.root().is_divided(): + for i in range(len(actorslist)): + scene.add(actorslist[i]) +else: + scene.add(actorslist) + + +interactor = lib.RenderWindowInteractor() +interactor.SetRenderWindow(showmanager.window) + +interactor.Initialize() +showmanager.render() +interactor.Start() diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index 07ed9e347..21a68fdf0 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -1,121 +1,447 @@ -from fury import window, actor, lib, trees import numpy as np - - -# TESTING A QUADTREE OF RANDOM 2D POINTS -br = trees.Branch2d(4, 0.0, 1.0, 0.0, 1.0) - -npoints = 200 - -points = np.random.rand(npoints, 2) - -tree = trees.Tree2d(br) -for i in range(npoints): - tree.AddPoint(trees.Point2d(points[i, 0], points[i, 1])) - -print(tree) - - -actorslist = trees.GetActorFromBranch2d(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) -zeros = np.zeros((npoints, 1)) -actorPoints = actor.dots(np.hstack((points, zeros)), (1.0, 0.5, 0.4), 1, 5) - -scene = window.Scene() -scene.set_camera(position=(-6, 5, -10), - focal_point=(tree.GetRoot().GetXMiddlePoint(), - tree.GetRoot().GetYMiddlePoint(), - 0.0), - view_up=(0.0, 0.0, 0.0)) -showmanager = window.ShowManager( - scene, - "trees demo", - (1080, - 1080), - reset_camera=True, - order_transparent=True) - -scene.add(actorPoints) - -if tree.GetRoot().IsDivided(): - for i in range(len(actorslist)): - scene.add(actorslist[i]) -else: - scene.add(actorslist) - - -interactor = lib.RenderWindowInteractor() -interactor.SetRenderWindow(showmanager.window) - -interactor.Initialize() -showmanager.render() -interactor.Start() - - - - -xl = 1.0 -yl = 2.0 -zl = 3.0 - - -# TESTING AN OCTREE WITH RANDOM 3D POINTS -br = trees.Branch3d(4, 0.0, xl, 0.0, yl, 0.0, zl) -npoints = 200 - -data = np.random.rand(npoints, 3) -data[:, 0] = xl*data[:, 0] -data[:, 1] = yl*data[:, 1] -data[:, 2] = zl*data[:, 2] -np.random.seed() - -tree = trees.Tree3d(br) -for i in range(npoints): - tree.AddPoint(trees.Point3d(data[i, 0], data[i, 1], data[i, 2])) - -print(tree) - - -# BELOW, THE ABSTRACT PROCESSING METHOD -def abstract(branch : trees.Branch3d, number): - print(number + branch.GetPointsNumber()) - - -tree.GetRoot().ProcessBranch(abstract, 10) - - -# FOR THIS EXAMPLE, LET'S RENDER THE OCTREE WITH THE PROVIDED FUNCTIONS -scene = window.Scene() -scene.set_camera(position=(-6, 5, -10), - focal_point=(tree.GetRoot().GetXMiddlePoint(), - tree.GetRoot().GetYMiddlePoint(), - tree.GetRoot().GetZMiddlePoint()), - view_up=(0.0, 0.0, 0.0)) -showmanager = window.ShowManager( - scene, - "trees demo", - (1080, - 1080), - reset_camera=True, - order_transparent=True) - - -actorPoints = actor.dots(data, (1.0, 0.5, 0.4), 1, 5) -scene.add(actorPoints) - - -actorslist = trees.GetActorFromBranch3d(tree.GetRoot(), (1.0, 1.0, 1.0), 1.0) - - -if tree.GetRoot().IsDivided(): - for i in range(len(actorslist)): - scene.add(actorslist[i]) -else: - scene.add(actorslist) - - -interactor = lib.RenderWindowInteractor() -interactor.SetRenderWindow(showmanager.window) - -interactor.Initialize() -showmanager.render() -interactor.Start() +import numpy.testing as npt +from fury.utils import Actor + +import trees + + +def test_point2d(): + array = np.array([1, 2]) + point = trees.Point2d(array[0], array[1]) + new_array = np.array([1, 1]) + npt.assert_array_equal(array, point()) + npt.assert_equal(array[0], point.get_x_coord()) + npt.assert_equal(array[1], point.get_y_coord()) + point.set_coord(new_array[0], new_array[1]) + npt.assert_array_equal(new_array, point()) + npt.assert_string_equal(str(new_array), str(point)) + npt.assert_string_equal(repr(new_array), repr(point)) + + +def test_point3d(): + array = np.array([1, 2, 3]) + point = trees.Point3d(array[0], array[1], array[2]) + new_array = np.array([1, 1, 1]) + npt.assert_array_equal(array, point()) + npt.assert_equal(array[2], point.get_z_coord()) + point.set_coord(new_array[0], new_array[1], new_array[2]) + npt.assert_array_equal(new_array, point()) + + +def test_branch2d(): + max_points = 4 + x_size = (0.0, 1.0) + y_size = (0.0, 1.0) + branch = trees.Branch2d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1]) + positions = np.array([[0.1, 0.2], + [0.6, 0.1], + [0.4, 0.9], + [0.8, 0.7], + [0.1, 0.6]]) + mid_x = x_size[0] + (x_size[1] - x_size[0]) / float(2) + mid_y = y_size[0] + (y_size[1] - y_size[0]) / float(2) + + npt.assert_equal(0, branch.n_points()) + npt.assert_equal(max_points, branch.max_points()) + npt.assert_equal(False, branch.is_divided()) + npt.assert_equal(x_size, branch.x_size()) + npt.assert_equal(y_size, branch.y_size()) + npt.assert_equal(mid_x, branch.x_mid_point()) + npt.assert_equal(mid_y, branch.y_mid_point()) + + points = np.array([trees.Point2d(pos[0], pos[1]) for pos in positions]) + for i in range(points.shape[0] - 1): + branch.add_point(points[i]) + npt.assert_equal(points.shape[0] - 1, branch.n_points()) + npt.assert_equal( + True, positions[:-1] in np.array([point().tolist() for point in branch.points_list()])) + branch.add_point(points[-1]) + + def all_points_inside(branch): + return branch.points_list() + + npt.assert_equal(True, positions in np.array( + [point().tolist() for point in branch.process_branch(all_points_inside)])) + npt.assert_array_equal([], np.array([point().tolist() + for point in branch.points_list()])) + npt.assert_equal(points.size, branch.total_points()) + npt.assert_equal(True, branch.is_divided()) + npt.assert_equal(0, branch.n_points()) + npt.assert_equal(True, branch._downleft == branch.sub_branch(0)) + npt.assert_equal(True, branch._downright == branch.sub_branch(1)) + npt.assert_equal(True, branch._upleft == branch.sub_branch(2)) + npt.assert_equal(True, branch._upright == branch.sub_branch(3)) + div = 2.0 + new_positions = positions / div + new_points = np.array([trees.Point2d(pos[0], pos[1]) + for pos in new_positions]) + new_branch = trees.Branch2d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1]) + for i in range(new_points.shape[0]): + new_branch.add_point(new_points[i]) + + def divide(branch, div: float): + for i in range(branch.points_list().shape[0]): + update_coord = branch.points_list()[i]() / div + branch.points_list()[i].set_coord(update_coord[0], update_coord[1]) + branch.process_branch(divide, div) + branch.update() + npt.assert_equal(True, branch == new_branch) + branch.sub_branch(0).sub_branch(0).remove_point(0) + npt.assert_equal(0, branch.sub_branch(0).sub_branch(0).n_points()) + npt.assert_array_equal([], branch.sub_branch(0).points_list()) + + +def test_branch3d(): + max_points = 4 + x_size = (0.0, 1.0) + y_size = (0.0, 1.0) + z_size = (0.0, 1.0) + branch = trees.Branch3d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1], + z_size[0], + z_size[1]) + positions = np.array([[0.1, 0.2, 0.1], + [0.6, 0.1, 0.7], + [0.4, 0.9, 0.5], + [0.8, 0.7, 0.5], + [0.1, 0.6, 0.3]]) + mid_z = z_size[0] + (z_size[1] - z_size[0]) / float(2) + npt.assert_equal(0, branch.n_points()) + npt.assert_equal(max_points, branch.max_points()) + npt.assert_equal(False, branch.is_divided()) + npt.assert_equal(x_size, branch.x_size()) + npt.assert_equal(y_size, branch.y_size()) + npt.assert_equal(z_size, branch.z_size()) + npt.assert_equal(mid_z, branch.z_mid_point()) + + points = np.array([trees.Point3d(pos[0], pos[1], pos[2]) + for pos in positions]) + for i in range(points.shape[0] - 1): + branch.add_point(points[i]) + npt.assert_equal(points.shape[0] - 1, branch.n_points()) + npt.assert_equal( + True, positions[:-1] in np.array([point().tolist() for point in branch.points_list()])) + branch.add_point(points[-1]) + + def all_points_inside(branch): + return branch.points_list() + + npt.assert_equal(True, positions in np.array( + [point().tolist() for point in branch.process_branch(all_points_inside)])) + npt.assert_array_equal([], np.array([point().tolist() + for point in branch.points_list()])) + npt.assert_equal(points.size, branch.total_points()) + npt.assert_equal(True, branch.is_divided()) + npt.assert_equal(0, branch.n_points()) + npt.assert_equal(True, branch._front_down_left == branch.sub_branch(0)) + npt.assert_equal(True, branch._front_down_right == branch.sub_branch(1)) + npt.assert_equal(True, branch._front_up_left == branch.sub_branch(2)) + npt.assert_equal(True, branch._front_up_right == branch.sub_branch(3)) + npt.assert_equal(True, branch._back_down_left == branch.sub_branch(4)) + npt.assert_equal(True, branch._back_down_right == branch.sub_branch(5)) + npt.assert_equal(True, branch._back_up_left == branch.sub_branch(6)) + npt.assert_equal(True, branch._back_up_right == branch.sub_branch(7)) + div = 2.0 + new_positions = positions / div + new_points = np.array([trees.Point3d(pos[0], pos[1], pos[2]) + for pos in new_positions]) + new_branch = trees.Branch3d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1], + z_size[0], + z_size[1]) + for i in range(new_points.shape[0]): + new_branch.add_point(new_points[i]) + + def divide(branch, div: float): + for i in range(branch.points_list().shape[0]): + update_coord = branch.points_list()[i]() / div + branch.points_list()[i].set_coord( + update_coord[0], update_coord[1], update_coord[2]) + branch.process_branch(divide, div) + branch.update() + npt.assert_equal(True, branch == new_branch) + branch.sub_branch(0).sub_branch(0).remove_point(0) + npt.assert_equal(0, branch.sub_branch(0).sub_branch(0).n_points()) + npt.assert_array_equal([], branch.sub_branch(0).points_list()) + + +def test_tree2d(): + max_points = 4 + x_size = (0.0, 1.0) + y_size = (0.0, 1.0) + branch = trees.Branch2d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1]) + positions = np.array([[0.1, 0.2], + [0.6, 0.1], + [0.4, 0.9], + [0.8, 0.7], + [0.1, 0.6]]) + tree = trees.Tree2d(branch) + npt.assert_equal(True, tree.root() == tree._root) + npt.assert_equal(0, tree.n_points()) + npt.assert_equal(x_size, tree.x_size()) + npt.assert_equal(y_size, tree.y_size()) + point = trees.Point2d(positions[0, 0], positions[0, 1]) + tree.add_point(point) + npt.assert_equal(1, tree.root().n_points()) + npt.assert_equal(True, tree.root().all_points_list()[0] == point) + + +def test_tree3d(): + max_points = 4 + x_size = (0.0, 1.0) + y_size = (0.0, 1.0) + z_size = (0.0, 1.0) + branch = trees.Branch3d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1], + z_size[0], + z_size[1]) + positions = np.array([[0.1, 0.2, 0.1], + [0.6, 0.1, 0.6], + [0.4, 0.9, 0.7], + [0.8, 0.7, 0.3], + [0.1, 0.6, 0.9]]) + tree = trees.Tree3d(branch) + npt.assert_equal(x_size, tree.x_size()) + npt.assert_equal(y_size, tree.y_size()) + npt.assert_equal(z_size, tree.z_size()) + point = trees.Point3d(positions[0, 0], positions[0, 1], positions[0, 2]) + tree.add_point(point) + npt.assert_equal(1, tree.root().n_points()) + npt.assert_equal(True, tree.root().all_points_list()[0] == point) + + +def test_bounding_Box(): + center = [0.0, 0.0, 0.0] + size = [1.0, 1.0, 1.0] + color = (1.0, 1.0, 1.0) + line_width = 1.0 + x_c = center[0] + y_c = center[1] + z_c = center[2] + + x_l = size[0] / 2 + y_l = size[1] / 2 + z_l = size[2] / 2 + bounds = [x_c - x_l, x_c + x_l, y_c - y_l, y_c + y_l, z_c - z_l, z_c + z_l] + lines = trees.bounding_box_3d(center, size, color, line_width) + npt.assert_equal(center, lines.GetCenter()) + npt.assert_equal(bounds, lines.GetBounds()) + + +def test_actor_from_branch_2d(): + max_points = 4 + x_size = (0.0, 1.0) + y_size = (0.0, 1.0) + color = (1.0, 1.0, 1.0) + line_width = 1.0 + branch = trees.Branch2d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1]) + positions = np.array([[0.1, 0.2], + [0.6, 0.1], + [0.4, 0.9], + [0.8, 0.7], + [0.1, 0.6]]) + branch = trees.Branch2d( + max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1]) + points = np.array([trees.Point2d(pos[0], pos[1]) for pos in positions]) + for i in range(points.shape[0]): + branch.add_point(points[i]) + + actors = trees.actor_from_branch_2d(branch, color, line_width) + + def get_actors(branch, color, linewidth): + x_c = branch.x_mid_point() + y_c = branch.y_mid_point() + z_c = 0.0 + + x_l = (branch.x_size()[1] - branch.x_size()[0]) + y_l = (branch.y_size()[1] - branch.y_size()[0]) + z_l = 0.0 + + return trees.bounding_box_3d( + (x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) + + actors_test = np.empty(0, dtype=Actor) + if branch.is_divided() == True: + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(0), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(1), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(2), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(3), + color, + line_width)) + + else: + x_c = branch.x_mid_point() + y_c = branch.y_mid_point() + z_c = 0.0 + + x_l = (branch.x_size()[1] - branch.x_size()[0]) + y_l = (branch.y_size()[1] - branch.y_size()[0]) + z_l = 0.0 + + actors = np.append(actors, trees.bounding_box_3d( + (x_c, y_c, z_c), (x_l, y_l, z_l), color, line_width)) + + for i in range(actors.shape[0]): + npt.assert_equal(actors_test[i].GetCenter(), actors[i].GetCenter()) + npt.assert_equal(actors_test[i].GetBounds(), actors[i].GetBounds()) + + +def test_actor_from_branch_3d(): + max_points = 4 + x_size = (0.0, 1.0) + y_size = (0.0, 1.0) + z_size = (0.0, 1.0) + color = (1.0, 1.0, 1.0) + line_width = 1.0 + branch = trees.Branch3d(max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1], + z_size[0], + z_size[1]) + positions = np.array([[0.1, 0.2, 0.1], + [0.6, 0.1, 0.7], + [0.4, 0.9, 0.5], + [0.8, 0.7, 0.5], + [0.1, 0.6, 0.3]]) + + branch = trees.Branch3d( + max_points, + x_size[0], + x_size[1], + y_size[0], + y_size[1], + z_size[0], + z_size[1]) + points = np.array([trees.Point3d(pos[0], pos[1], pos[2]) + for pos in positions]) + for i in range(points.shape[0]): + branch.add_point(points[i]) + actors = trees.actor_from_branch_3d(branch, color, line_width) + + def get_actors(branch, color, linewidth): + x_c = branch.x_mid_point() + y_c = branch.y_mid_point() + z_c = branch.z_mid_point() + + x_l = (branch.x_size()[1] - branch.x_size()[0]) + y_l = (branch.y_size()[1] - branch.y_size()[0]) + z_l = (branch.z_size()[1] - branch.z_size()[0]) + + return trees.bounding_box_3d( + (x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) + + actors_test = np.empty(0, dtype=Actor) + if branch.is_divided() == True: + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(0), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(1), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(2), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(3), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(4), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(5), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(6), + color, + line_width)) + actors_test = np.append( + actors_test, + get_actors( + branch.sub_branch(7), + color, + line_width)) + + else: + x_c = branch.x_mid_point() + y_c = branch.y_mid_point() + z_c = branch.z_mid_point() + + x_l = (branch.x_size()[1] - branch.x_size()[0]) + y_l = (branch.y_size()[1] - branch.y_size()[0]) + z_l = (branch.z_size()[1] - branch.z_size()[0]) + + actors = np.append(actors, trees.bounding_box_3d( + (x_c, y_c, z_c), (x_l, y_l, z_l), color, line_width)) + + for i in range(actors.shape[0]): + npt.assert_equal(actors_test[i].GetCenter(), actors[i].GetCenter()) + npt.assert_equal(actors_test[i].GetBounds(), actors[i].GetBounds()) \ No newline at end of file diff --git a/fury/trees.py b/fury/trees.py index d8e808240..7555e17a7 100644 --- a/fury/trees.py +++ b/fury/trees.py @@ -4,179 +4,246 @@ # QUADTREE IMPLEMENTATION -class Point2d(): - '''General point class. This class only stores 2d coordinates.''' +class Point2d: + """General point class. This class only stores 2d coordinates, + and is to be used as base for any class that intends to use the quadtree structure. + """ - def __init__(self, x : float, y: float): + def __init__(self, x: float, y: float): self.coord = np.array([x, y]) def __call__(self): return self.coord - def GetXCoord(self) -> float: - '''Returns the x coordinate of the point.''' + def __eq__(self, point): + return np.allclose(self.coord, point.coord, rtol=10 ** + (-np.finfo(self.coord[0].dtype).precision)) + + def get_x_coord(self) -> float: + """Returns the x coordinate of the point.""" return self.coord[0] - def GetYCoord(self) -> float: - '''Returns the y coordinate of the point.''' + def get_y_coord(self) -> float: + """Returns the y coordinate of the point.""" return self.coord[1] - def GetNorm(self) -> float: - '''Returns the norm of the point in relation to the origin.''' - return np.sqrt(self.GetXCoord()**2 + self.GetYCoord()**2) - - def SetCoord(self, x : float, y : float) -> np.array: - '''Updates the coordinate of the point. + def set_coord(self, x: float, y: float) -> np.array: + """Updates the coordinate of the point. + ## Parameters * x : float x position - * y : float y position''' + * y : float y position""" self.coord = np.array([x, y]) def __str__(self): - string = "[" + str('%.3f' % - self.coord[0]) + ", " + str('%.3f' % - self.coord[1]) + "]" - return string + return str(self.coord) def __repr__(self): - string = "[" + str('%.3f' % - self.coord[0]) + ", " + str('%.3f' % - self.coord[1]) + "]" - return string + return repr(self.coord) class Branch2d(): - '''General branch class. It is the base structure for building a quadtree.\n - * max_points : An integer defining the maximum number of points this branch stores - * x_min : The float lower limit for x - * x_max : The float upper limit for x - * y_min : The float lower limit for y - * y_max : The float upper limit for y \n - This branch, when divided, has four branches: \n + """General branch class. It is the base structure for building a quadtree.\n + This branch, when divided, has four branches: \n 1. the down-left 2. the down-right 3. the up-left - 4. the up-right''' - - def __init__(self, max_points : int, x_min : float, - x_max : float, y_min : float, y_max : float): - self._DIVIDED = False - self._MAX_POINTS = max_points - self._N_POINTS = 0 + 4. the up-right + + Parameters + ---------- + * max_points : int\n + Defines the maximum number of points this branch stores + * x_min : float\n + Lower limit for x + * x_max : float\n + Upper limit for x + * y_min : float\n + Lower limit for y + * y_max : float + Upper limit for y \n + """ + + def __init__(self, max_points: int, x_min: float, + x_max: float, y_min: float, y_max: float): + self._divided = False + self._max_points = max_points + self._n_points = 0 self._xmin = x_min self._xmax = x_max self._ymin = y_min self._ymax = y_max - self._POINTS = np.array([], dtype=Point2d) - self._UPLEFT = None - self._UPRIGHT = None - self._DOWNLEFT = None - self._DOWNRIGHT = None - self.subBranches = {0 : self._DOWNLEFT, - 1 : self._DOWNRIGHT, - 2 : self._UPLEFT, - 3 : self._UPRIGHT} - - def GetPointsNumber(self) -> int: - '''Returns the current number of points the branch has. - This method is different from GetTotalPoints, as it only gives the current points + self._points = np.empty(0, dtype=Point2d) + self._upleft = None + self._upright = None + self._downleft = None + self._downright = None + self.sub_branches = {0: self._downleft, + 1: self._downright, + 2: self._upleft, + 3: self._upright} + + def __eq__(self, branch): + if self.is_divided() == True: + eq = True + if (branch.is_divided() == True): + if (self.n_points() == branch.n_points() and + self.max_points() == branch.max_points() and + self.x_size() == branch.x_size() and + self.y_size() == branch.y_size()) == True: + + eq = eq and True + else: + return False + + eq = eq and self._downleft.__eq__(branch._downleft) + eq = eq and self._downright.__eq__(branch._downright) + eq = eq and self._upleft.__eq__(branch._upleft) + eq = eq and self._upright.__eq__(branch._upright) + + return eq + + else: + return False + + else: + if (self.n_points() == 0): + if (self.n_points() == branch.n_points() and + self.max_points() == branch.max_points() and + self.x_size() == branch.x_size() and + self.y_size() == branch.y_size()) == True: + return True + else: + return False + else: + if (self.n_points() == branch.n_points() and + self.max_points() == branch.max_points() and + self.x_size() == branch.x_size() and + self.y_size() == branch.y_size() and + np.any(self.points_list() == branch.points_list()) == True) == True: + return True + else: + return False + + def max_points(self) -> int: + """Returns the current number of maximum points allowed inside the branch.""" + return self._max_points + + def n_points(self) -> int: + """Returns the current number of points the branch has. + + NOTE: This method is different from total_points, as it only gives the current points stored in the branch, and not the points stored in the subbranches. - If the branch is divided, it will return 0.''' - return self._N_POINTS + If the branch is divided, it will return 0. + """ + return self._n_points - def IsDivided(self) -> bool: - '''Returns True if the branch is divided, and False otherwise.''' - return self._DIVIDED + def is_divided(self) -> bool: + """Returns True if the branch is divided, and False otherwise.""" + return self._divided - def GetXSize(self) -> tuple: - '''Returns the x information of the branch, as a tuple.''' + def x_size(self) -> tuple: + """Returns the x information of the branch, as a tuple.""" return (self._xmin, self._xmax) - def GetYSize(self) -> tuple: - '''Returns the y information of the branch, as a tuple.''' + def y_size(self) -> tuple: + """Returns the y information of the branch, as a tuple.""" return (self._ymin, self._ymax) - def GetXMiddlePoint(self) -> float: - '''Returns the middle x point of the branch.''' - return self.GetXSize()[0] + (self.GetXSize()[1] - self.GetXSize()[0])/float(2) + def x_mid_point(self) -> float: + """Returns the middle x point of the branch.""" + return self.x_size()[0] + (self.x_size()[1] - + self.x_size()[0]) / float(2) - def GetYMiddlePoint(self) -> float: - '''Method that returns the middle y point of the branch.''' - return self.GetYSize()[0] + (self.GetYSize()[1] - self.GetYSize()[0])/float(2) + def y_mid_point(self) -> float: + """Method that returns the middle y point of the branch.""" + return self.y_size()[0] + (self.y_size()[1] - + self.y_size()[0]) / float(2) def __str__(self): - if self.IsDivided() == True: - str = f"number of points = {self.GetTotalPoints()}\n\ - [ {self._UPLEFT.GetPointsNumber()} | {self._UPRIGHT.GetPointsNumber()} ]\n\ - [---|---]\n\ - [ {self._DOWNLEFT.GetPointsNumber()} | {self._DOWNRIGHT.GetPointsNumber()} ]\n" + if self.is_divided() == True: + str = f"number of points = {self.total_points()}\n" + str += f"[ {self._upleft.n_points()} | {self._upright.n_points()} ]\n" + str += "[---|---]\n" + str += f"[ {self._downleft.n_points()} | {self._downright.n_points()} ]\n" else: - str = f"number of points = {self._N_POINTS}\n[ ]\n[ ]\n[ ]\n" + str = f"number of points = {self._n_points}\n[ ]\n[ ]\n[ ]\n" return str - def GetPointsList(self) -> np.array: - '''Returns a list containing the points existing in that branch.''' - return self._POINTS + def points_list(self): + """Returns a list containing the points existing in that branch.""" + return self._points - def GetSubBranch(self, index : int): - '''Returns the sub branch by index. Below, the index correspondence: + def sub_branch(self, index: int): + """Returns the sub branch by index. Below, the index correspondence: 0 : Down-left branch 1 : Down-right branch 2 : Up-left branch - 3 : Up-right branch - - * index : int index of the branch requested''' - if self.IsDivided() == True: - return self.subBranches[index] + 3 : Up-right branch\n + Parameters + ---------- + * index : int\n + Index of the branch requested + """ + if self.is_divided() == True: + return self.sub_branches[index] else: raise AttributeError("The branch got no subbranches.") - def GetTotalPoints(self) -> int: - '''Returns the total number of points in that branch, including the points in the subranches.''' - if self.IsDivided() == True: + def total_points(self) -> int: + """Returns the total number of points in that branch, including the points in the subranches.""" + if self.is_divided() == True: sum = 0 - for i in range(len(self.subBranches)): - sum += self.GetSubBranch(i).GetTotalPoints() + for i in range(len(self.sub_branches)): + sum += self.sub_branch(i).total_points() return sum else: - return self.GetPointsNumber() - - def RemovePoint(self, index : int): - '''Removes the given element of the list of points''' - if self._N_POINTS != 0: - self._POINTS = np.delete(self._POINTS, index) - self._N_POINTS -= 1 + return self.n_points() + + def remove_point(self, index: int): + """Removes the given element of the list of points\n + Parameters + ---------- + index : int\n + Index of the point to be removed. + """ + if self._n_points != 0: + self._points = np.delete(self._points, index) + self._n_points -= 1 else: raise AttributeError("This branch has no point to be removed.") - # def SubAddPoint(self): + # def sub_add_point(self): - def SubAddPoint(self, point : Point2d): - if self.GetXSize()[0] <= point.GetXCoord() < self.GetXMiddlePoint(): - if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): - self._DOWNLEFT.AddPoint(point) - elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: - self._UPLEFT.AddPoint(point) + def sub_add_point(self, point: Point2d): + if self.x_size()[0] <= point()[0] < self.x_mid_point(): + if self.y_size()[0] <= point()[1] < self.y_mid_point(): + self._downleft.add_point(point) + elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: + self._upleft.add_point(point) else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}).") - elif self.GetXMiddlePoint() <= point.GetXCoord() <= self.GetXSize()[1]: - if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): - self._DOWNRIGHT.AddPoint(point) - elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: - self._UPRIGHT.AddPoint(point) + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") + elif self.x_mid_point() <= point()[0] <= self.x_size()[1]: + if self.y_size()[0] <= point()[1] < self.y_mid_point(): + self._downright.add_point(point) + elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: + self._upright.add_point(point) else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}).") + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}).") + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") - def AddPoint(self, point : Point2d): - '''Recursively adds a point to the branch. \n - * point : Point2d to be added.\n + def add_point(self, point: Point2d): + """Recursively adds a point to the branch.\n + Parameters + ---------- + point : np.array\n + Point to be added.\n Below, how the method works: 1. The branch is first checked if divided. If it is, the coordinates of @@ -185,171 +252,257 @@ def AddPoint(self, point : Point2d): the branch's defined limit, 3. If it is, adds the point to the branch. 4. If it's not ,the branch is then divided and the coordinates of the point are checked to - add the point in one of the newly-created subbranches.''' + add the point in one of the newly-created subbranches. + """ - if self.IsDivided() == True: + if self.is_divided() == True: - self.SubAddPoint(point) + self.sub_add_point(point) else: - if self.GetPointsNumber() == self._MAX_POINTS: - self._DOWNLEFT = Branch2d( - self._MAX_POINTS, - self.GetXSize()[0], - self.GetXMiddlePoint(), - self.GetYSize()[0], - self.GetYMiddlePoint()) - self._DOWNRIGHT = Branch2d( - self._MAX_POINTS, - self.GetXMiddlePoint(), - self.GetXSize()[1], - self.GetYSize()[0], - self.GetYMiddlePoint()) - self._UPLEFT = Branch2d( - self._MAX_POINTS, - self.GetXSize()[0], - self.GetXMiddlePoint(), - self.GetYMiddlePoint(), - self.GetYSize()[1]) - self._UPRIGHT = Branch2d( - self._MAX_POINTS, - self.GetXMiddlePoint(), - self.GetXSize()[1], - self.GetYMiddlePoint(), - self.GetYSize()[1]) - self._DIVIDED = True - - self.subBranches = {0 : self._DOWNLEFT, - 1 : self._DOWNRIGHT, - 2 : self._UPLEFT, - 3 : self._UPRIGHT} - - list = self.GetPointsList() - for i in range(self.GetPointsNumber()): - - self.SubAddPoint(list[i]) - self.RemovePoint(0) - - self.SubAddPoint(point) + if self.n_points() == self._max_points: + self._downleft = Branch2d( + self._max_points, + self.x_size()[0], + self.x_mid_point(), + self.y_size()[0], + self.y_mid_point()) + self._downright = Branch2d( + self._max_points, + self.x_mid_point(), + self.x_size()[1], + self.y_size()[0], + self.y_mid_point()) + self._upleft = Branch2d( + self._max_points, + self.x_size()[0], + self.x_mid_point(), + self.y_mid_point(), + self.y_size()[1]) + self._upright = Branch2d( + self._max_points, + self.x_mid_point(), + self.x_size()[1], + self.y_mid_point(), + self.y_size()[1]) + self._divided = True + + self.sub_branches = {0: self._downleft, + 1: self._downright, + 2: self._upleft, + 3: self._upright} + + list = self.points_list() + for i in range(self.n_points()): + self.sub_add_point(list[i]) + self.remove_point(0) + + self.sub_add_point(point) else: - self._POINTS = np.append(self._POINTS, point) - self._N_POINTS += 1 + self._points = np.append(self._points, point) + self._n_points += 1 - def ProcessBranch(self, Function, *args): - '''Recursively process the branch or its subbranches with a given function and its arguments. + def process_branch(self, function, *args): + """Recursively process the branch and its subbranches with a given function and its arguments. If the function returns any value, it will be returned as the value itself or a list of the - values returned by each subbranch processed, if the branch is already divided. - * Function : Any function that has only a branch as input - * *args : arguments of the function in the order requested by the funciton passed''' - if self.IsDivided() == True: - list = np.array([]) + values returned by each subbranch processed, if the branch is already divided.\n + Parameters + ---------- + function : Any\n + function that has only a branch as input\n + *args : Any\n + Arguments of the function in the order requested by the function passed + """ + if self.is_divided() == True: + list = np.empty(0) + + for i in range(len(self.sub_branches)): + list = np.append( + list, self.sub_branch(i).process_branch( + function, *args)) + + return list + + else: + return function(self, *args) + + def all_points_list(self): + """Returns a list with all the point inside a branch, including its subbranches.""" + if self.is_divided() == True: + list = np.empty(0) - for i in range(len(self.subBranches)): + for i in range(len(self.sub_branches)): list = np.append( - list, self.GetSubBranch(i).ProcessBranch( - Function, *args)) + list, self.sub_branch(i).all_points_list()) return list else: - return Function(self, *args) + return self.points_list() + + def update(self): + """Recursively checks if all the points in the branch and its subbranches still belong there. + Returns an array with all the points that had positions out of the branch's bounds. + """ + return_list = np.empty(0) + if self.is_divided() == True: + update_points = np.empty(0) + for i in range(len(self.sub_branches)): + update_points = np.append( + update_points, self.sub_branch(i).update()) + + removed = 0 + remain_points = np.copy(update_points) + for i in range(update_points.size): + if ((self.x_size()[0] > update_points[i]()[0]) or + (update_points[i]()[0] > self.x_size()[1]) or + (self.y_size()[0] > update_points[i]()[1]) or + (update_points[i]()[1] > self.y_size()[1])): + return_list = np.append(return_list, update_points[i]) + remain_points = np.delete(remain_points, i - removed) + removed += 1 + + if self.total_points() <= self._max_points: + remain_points = np.append( + remain_points, self.all_points_list()) + self._divided = False + self._upleft = None + self._upright = None + self._downleft = None + self._downright = None + self.sub_branches = {0: self._downleft, + 1: self._downright, + 2: self._upleft, + 3: self._upright} + + for i in range(remain_points.shape[0]): + self.add_point(remain_points[i]) + + return return_list + + else: + list = self.points_list() + removed = 0 + for i in range(list.size): + if ((self.x_size()[0] > list[i]()[0]) or + (list[i]()[0] > self.x_size()[1]) or + (self.y_size()[0] > list[i]()[1]) or + (list[i]()[1] > self.y_size()[1])): + self.remove_point(i - removed) + removed += 1 + return_list = np.append(return_list, list[i]) + + return return_list class Tree2d(): - '''Class that stores the root branch and general 2d tree informations. - * branch : Branch2d to be the root of the tree.''' - - def __init__(self, branch : Branch2d): - self._ROOT = branch - self.xmin = branch.GetXSize()[0] - self.xmax = branch.GetXSize()[1] - self.ymin = branch.GetYSize()[0] - self.ymax = branch.GetYSize()[1] - self._N_POINTS = branch.GetTotalPoints() - self._STR = "" + """Class that stores the root branch and general 2d tree information.\n + Parameters + ---------- + branch : Branch2d\n + Branch to be the root of the tree. + """ + + def __init__(self, branch: Branch2d): + self._root = branch + self._xmin = branch.x_size()[0] + self._xmax = branch.x_size()[1] + self._ymin = branch.y_size()[0] + self._ymax = branch.y_size()[1] + self._n_points = branch.total_points() + self._str = "" + + def x_size(self): + """Returns the x size of the tree, as a tuple.""" + return (self._xmin, self._xmax) + + def y_size(self): + """Returns the y size of the tree, as a tuple.""" + return (self._ymin, self._ymax) + + def n_points(self): + """Returns the number of points inside the tree.""" + return self._n_points + + def root(self): + """Returns the root branch of the tree.""" + return self._root def __str__(self): - if self._ROOT.IsDivided() == True: + if self._root.is_divided() == True: points_list = [] - for i in range(len(self.GetRoot().subBranches)): - points_list.append(self.GetRoot().GetSubBranch(i).GetTotalPoints()) + for i in range(len(self.root().sub_branches)): + points_list.append(self.root().sub_branch(i).total_points()) length = [len(str(x)) for x in points_list] length = [np.max([length[j], length[j + 2]]) - for j in range(len(points_list)//2)] - self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n" - self._STR += f"[ {self._ROOT._UPLEFT.GetTotalPoints()} | {self._ROOT._UPRIGHT.GetTotalPoints()} ]\n" - self._STR += "[---" + "-"*(length[0] - 1) + \ - "|---" + "-"*(length[1] - 1) + f"]\n" - self._STR += f"[ {self._ROOT._DOWNLEFT.GetTotalPoints()} | {self._ROOT._DOWNRIGHT.GetTotalPoints()} ]\n" + for j in range(len(points_list) // 2)] + self._str = f"Number of points = {self._root.total_points()}\n" + self._str += f"[ {self._root._upleft.total_points()} | {self._root._upright.total_points()} ]\n" + self._str += "[---" + "-" * (length[0] - 1) + \ + "|---" + "-" * (length[1] - 1) + f"]\n" + self._str += f"[ {self._root._downleft.total_points()} | {self._root._downright.total_points()} ]\n" else: - self._STR = f"number of points = {self._N_POINTS}\n[ ]\n[ ]\n[ ]\n" - - return self._STR - - def GetRoot(self): - '''Returns the root branch of the tree.''' - return self._ROOT - - def AddPoint(self, point : Point2d): - '''Adds a point to the tree. - * point : Point2d to be added into the tree.''' - self._ROOT.AddPoint(point) - self._N_POINTS += 1 + self._str = f"Number of points = {self._n_points}\n[ ]\n[ ]\n[ ]\n" + + return self._str + + def add_point(self, point: Point2d): + """Adds a point to the tree.\n + Parameters + ---------- + point : np.array\n + Point to be added into the tree.""" + self._root.add_point(point) + self._n_points += 1 # END OF QUADTREE IMPLEMENTATION # OCTREE IMPLEMENTATION class Point3d(Point2d): - '''General point class. This class only stores 3d coordinates.''' + """General point class. This class only stores 3d coordinates, + and is to be used as base for any class that intends to use the octree structure. + """ def __init__(self, x: float, y: float, z: float): self.coord = np.array([x, y, z]) - def __call__(self): - return self.coord - - def GetZCoord(self): - '''Returns the z coordinate of the point.''' + def get_z_coord(self): + """Returns the z coordinate of the point.""" return self.coord[2] - def GetNorm(self): - '''Returns the norm of the point in relation to the origin.''' - return np.sqrt(self.GetXCoord()**2 + self.GetYCoord()**2 + self.GetZCoord()**2) - - def SetCoord(self, x: float, y: float, z : float): - '''Updates the coordinate of the point. - * x : float x position - * y : float y position - * z : float z position''' + def set_coord(self, x: float, y: float, z: float): + """Updates the coordinate of the point. + ## Parameters + * x : float\n + x position + * y : float\n + y position + * z : float\n + z position""" self.coord = np.array([x, y, z]) - def __str__(self): - string = "[" + str('%.3f' % - self.coord[0]) + ", " + str('%.3f' % - self.coord[1]) + str('%.3f' % - self.coord[2]) + "]" - return string - - def __repr__(self): - string = "[" + str('%.3f' % - self.coord[0]) + ", " + str('%.3f' % - self.coord[1]) + str('%.3f' % - self.coord[2]) + "]" - return string - class Branch3d(Branch2d): - '''General 3d branch class. It is the base structure for building an octree.\n - * max_points : An integer defining the maximum number of points this branch stores - * x_min : The float lower limit for x - * x_max : The float upper limit for x - * y_min : The float lower limit for y - * y_max : The float upper limit for y - * z_min : The float front limit for z - * z_max : The float back limit for z \n + """General 3d branch class. It is the base structure for building an octree.\n + Parameters + ---------- + max_points : int\n + Defining the maximum number of points this branch stores\n + x_min : float\n + Lower limit for x\n + x_max : float\n + Upper limit for x\n + y_min : float\n + Lower limit for y\n + y_max : float\n + Upper limit for y\n + z_min : float\n + Front limit for z\n + z_max : float\n + Back limit for z \n This branch, when divided, has eight branches: \n 1. the front-down-left 2. the front-down-right @@ -358,7 +511,8 @@ class Branch3d(Branch2d): 5. the back-down-left 6. the back-down-right 7. the back-up-left - 8. the back-up-right''' + 8. the back-up-right + """ def __init__(self, max_points: int, @@ -368,58 +522,111 @@ def __init__(self, y_max: float, z_min: float, z_max: float): - self._DIVIDED = False - self._MAX_POINTS = max_points - self._N_POINTS = 0 + self._divided = False + self._max_points = max_points + self._n_points = 0 self._xmin = x_min self._xmax = x_max self._ymin = y_min self._ymax = y_max self._zmin = z_min self._zmax = z_max - self._POINTS = np.array([], dtype=Point3d) - self._FRONTUPLEFT = None - self._FRONTUPRIGHT = None - self._FRONTDOWNLEFT = None - self._FRONTDOWNRIGHT = None - self._BACKUPLEFT = None - self._BACKUPRIGHT = None - self._BACKDOWNLEFT = None - self._BACKDOWNRIGHT = None - self.subBranches = {0 : self._FRONTDOWNLEFT, - 1 : self._FRONTDOWNRIGHT, - 2 : self._FRONTUPLEFT, - 3 : self._FRONTUPRIGHT, - 4 : self._BACKDOWNLEFT, - 5 : self._BACKDOWNRIGHT, - 6 : self._BACKUPLEFT, - 7 : self._BACKUPRIGHT} - - def GetZSize(self) -> tuple: - '''Returns the y information of the branch, as a tuple.''' + self._points = np.empty(0, dtype=Point3d) + self._front_up_left = None + self._front_up_right = None + self._front_down_left = None + self._front_down_right = None + self._back_up_left = None + self._back_up_right = None + self._back_down_left = None + self._back_down_right = None + self.sub_branches = {0: self._front_down_left, + 1: self._front_down_right, + 2: self._front_up_left, + 3: self._front_up_right, + 4: self._back_down_left, + 5: self._back_down_right, + 6: self._back_up_left, + 7: self._back_up_right} + + def __eq__(self, branch): + if self.is_divided() == True: + eq = True + if (branch.is_divided() == True): + if (self.n_points() == branch.n_points() and + self.max_points() == branch.max_points() and + self.x_size() == branch.x_size() and + self.y_size() == branch.y_size() and + self.z_size() == branch.z_size()) == True: + + eq = eq and True + else: + return False + + eq = eq and self._front_down_left.__eq__( + branch._front_down_left) + eq = eq and self._front_down_right.__eq__( + branch._front_down_right) + eq = eq and self._front_up_left.__eq__(branch._front_up_left) + eq = eq and self._front_up_right.__eq__(branch._front_up_right) + eq = eq and self._back_down_left.__eq__(branch._back_down_left) + eq = eq and self._back_down_right.__eq__( + branch._back_down_right) + eq = eq and self._back_up_left.__eq__(branch._back_up_left) + eq = eq and self._back_up_right.__eq__(branch._back_up_right) + + return eq + + else: + return False + + else: + if (self.n_points() == 0): + if (self.n_points() == branch.n_points() and + self.max_points() == branch.max_points() and + self.x_size() == branch.x_size() and + self.y_size() == branch.y_size() and + self.z_size() == branch.z_size()) == True: + return True + else: + return False + else: + if (self.n_points() == branch.n_points() and + self.max_points() == branch.max_points() and + self.x_size() == branch.x_size() and + self.y_size() == branch.y_size() and + self.z_size() == branch.z_size() and + np.any(self.points_list() == branch.points_list()) == True) == True: + return True + else: + return False + + def z_size(self) -> tuple: + """Returns the y information of the branch, as a tuple.""" return (self._zmin, self._zmax) - def GetZMiddlePoint(self) -> float: - '''Returns the middle z point of the branch.''' - return self.GetZSize()[0] + (self.GetZSize()[1] - self.GetZSize()[0])/float(2) + def z_mid_point(self) -> float: + """Returns the middle z point of the branch.""" + return self.z_size()[0] + (self.z_size()[1] - + self.z_size()[0]) / float(2) def __str__(self): - if self.IsDivided() == True: - str = f"number of points = {self.GetTotalPoints()}\n\ - FRONT\n \ - [ {self._FRONTUPLEFT.GetPointsNumber()} | {self._FRONTUPRIGHT.GetPointsNumber()} ]\n\ - [---|---]\n\ - [ {self._FRONTDOWNLEFT.GetPointsNumber()} | {self._FRONTDOWNRIGHT.GetPointsNumber()} ]\n\ - | BACK\n \ - [ {self._BACKUPLEFT.GetPointsNumber()} | {self._BACKUPRIGHT.GetPointsNumber()} ]\n\ - [---|---]\n\ - [ {self._BACKDOWNLEFT.GetPointsNumber()} | {self._BACKDOWNRIGHT.GetPointsNumber()} ]\n" + if self.is_divided() == True: + str = f"number of points = {self.total_points()}\n" + str += " FRONT\n" + str += f"[ {self._front_up_left.n_points()} | {self._front_up_right.n_points()} ]\n" + str += "[---|---]\n" + str += f"[ {self._front_down_left.n_points()} | {self._front_down_right.n_points()} ]\n" + str += "| BACK\n" + str += f"[ {self._back_up_left.n_points()} | {self._back_up_right.n_points()} ]\n" + str += "[---|---]\n" + str += f"[ {self._back_down_left.n_points()} | {self._back_down_right.n_points()} ]\n" else: - str = f"number of points = {self._N_POINTS}\n| FRONT\n[ ]\n[ ]\n[ ]\n| BACK\n[ ]\n[ ]\n[ ]\n" + str = f"number of points = {self._n_points}\n| FRONT\n[ ]\n[ ]\n[ ]\n| BACK\n[ ]\n[ ]\n[ ]\n" return str - def GetSubBranch(self, index : int): - '''Returns the sub branch by index. Below, the index correspondence: + def sub_branch(self, index: int): + """Returns the sub branch by index. Below, the index correspondence: 0 : Front-down-left branch 1 : Front-down-right branch 2 : Front-up-left branch @@ -427,58 +634,64 @@ def GetSubBranch(self, index : int): 4 : Back-down-left branch 5 : Back-down-right branch 6 : Back-up-left branch - 7 : Back-up-right branch - - * index : int index of the branch requested''' - if self.IsDivided() == True: - return self.subBranches[index] + 7 : Back-up-right branch\n + Parameters + ---------- + index : int\n + index of the branch requested + """ + if self.is_divided() == True: + return self.sub_branches[index] else: raise AttributeError("The branch got no subbranches.") - def SubAddPoint(self, point : Point3d): - if self.GetXSize()[0] <= point.GetXCoord() < self.GetXMiddlePoint(): - if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): - if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNLEFT.AddPoint(point) - elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: - self._BACKDOWNLEFT.AddPoint(point) + def sub_add_point(self, point: Point3d): + if self.x_size()[0] <= point()[0] < self.x_mid_point(): + if self.y_size()[0] <= point()[1] < self.y_mid_point(): + if self.z_size()[0] <= point()[2] < self.z_mid_point(): + self._front_down_left.add_point(point) + elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: + self._back_down_left.add_point(point) else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") - elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: - if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPLEFT.AddPoint(point) - elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: - self._BACKUPLEFT.AddPoint(point) + elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: + if self.z_size()[0] <= point()[2] < self.z_mid_point(): + self._front_up_left.add_point(point) + elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: + self._back_up_left.add_point(point) else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") - elif self.GetXMiddlePoint() <= point.GetXCoord() <= self.GetXSize()[1]: - if self.GetYSize()[0] <= point.GetYCoord() < self.GetYMiddlePoint(): - if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTDOWNRIGHT.AddPoint(point) - elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: - self._BACKDOWNRIGHT.AddPoint(point) + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + elif self.x_mid_point() <= point()[0] <= self.x_size()[1]: + if self.y_size()[0] <= point()[1] < self.y_mid_point(): + if self.z_size()[0] <= point()[2] < self.z_mid_point(): + self._front_down_right.add_point(point) + elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: + self._back_down_right.add_point(point) else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") - elif self.GetYMiddlePoint() <= point.GetYCoord() <= self.GetYSize()[1]: - if self.GetZSize()[0] <= point.GetZCoord() < self.GetZMiddlePoint(): - self._FRONTUPRIGHT.AddPoint(point) - elif self.GetZMiddlePoint() <= point.GetZCoord() <= self.GetZSize()[1]: - self._BACKUPRIGHT.AddPoint(point) + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: + if self.z_size()[0] <= point()[2] < self.z_mid_point(): + self._front_up_right.add_point(point) + elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: + self._back_up_right.add_point(point) else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") else: raise ValueError( - f"The point {point()} is outside the tree's bounds : ({self.GetXSize()}, {self.GetYSize()}, {self.GetZSize()}).") + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") - def AddPoint(self, point : Point3d): - '''Recursively adds a point to the branch. \n - * point : Point3d to be added.\n + def add_point(self, point: Point3d): + """Recursively adds a point to the branch.\n + Parameters + ---------- + point : np.array\n + Point to be added.\n Below, how the method works: 1. The branch is first checked if divided. If it is, the coordinates of @@ -487,136 +700,213 @@ def AddPoint(self, point : Point3d): the branch's defined limit, 3. If it is, adds the point to the branch. 4. If it's not ,the branch is then divided and the coordinates of the point are checked to - add the point in one of the newly-created subbranches.''' - if self.IsDivided() == True: + add the point in one of the newly-created subbranches. + """ + if self.is_divided() == True: - self.SubAddPoint(point) + self.sub_add_point(point) else: - if self.GetPointsNumber() == self._MAX_POINTS: - self._FRONTDOWNLEFT = Branch3d( - self._MAX_POINTS, - self.GetXSize()[0], - self.GetXMiddlePoint(), - self.GetYSize()[0], - self.GetYMiddlePoint(), - self.GetZSize()[0], - self.GetZMiddlePoint()) - self._FRONTDOWNRIGHT = Branch3d( - self._MAX_POINTS, - self.GetXMiddlePoint(), - self.GetXSize()[1], - self.GetYSize()[0], - self.GetYMiddlePoint(), - self.GetZSize()[0], - self.GetZMiddlePoint()) - self._FRONTUPLEFT = Branch3d( - self._MAX_POINTS, - self.GetXSize()[0], - self.GetXMiddlePoint(), - self.GetYMiddlePoint(), - self.GetYSize()[1], - self.GetZSize()[0], - self.GetZMiddlePoint()) - self._FRONTUPRIGHT = Branch3d( - self._MAX_POINTS, - self.GetXMiddlePoint(), - self.GetXSize()[1], - self.GetYMiddlePoint(), - self.GetYSize()[1], - self.GetZSize()[0], - self.GetZMiddlePoint()) - self._BACKDOWNLEFT = Branch3d( - self._MAX_POINTS, - self.GetXSize()[0], - self.GetXMiddlePoint(), - self.GetYSize()[0], - self.GetYMiddlePoint(), - self.GetZMiddlePoint(), - self.GetZSize()[1]) - self._BACKDOWNRIGHT = Branch3d( - self._MAX_POINTS, - self.GetXMiddlePoint(), - self.GetXSize()[1], - self.GetYSize()[0], - self.GetYMiddlePoint(), - self.GetZMiddlePoint(), - self.GetZSize()[1]) - self._BACKUPLEFT = Branch3d( - self._MAX_POINTS, - self.GetXSize()[0], - self.GetXMiddlePoint(), - self.GetYMiddlePoint(), - self.GetYSize()[1], - self.GetZMiddlePoint(), - self.GetZSize()[1]) - self._BACKUPRIGHT = Branch3d( - self._MAX_POINTS, - self.GetXMiddlePoint(), - self.GetXSize()[1], - self.GetYMiddlePoint(), - self.GetYSize()[1], - self.GetZMiddlePoint(), - self.GetZSize()[1]) - self._DIVIDED = True - - self.subBranches = {0 : self._FRONTDOWNLEFT, - 1 : self._FRONTDOWNRIGHT, - 2 : self._FRONTUPLEFT, - 3 : self._FRONTUPRIGHT, - 4 : self._BACKDOWNLEFT, - 5 : self._BACKDOWNRIGHT, - 6 : self._BACKUPLEFT, - 7 : self._BACKUPRIGHT} - - list = self.GetPointsList() - for i in range(self.GetPointsNumber()): - - self.SubAddPoint(list[i]) - self.RemovePoint(0) - - self.SubAddPoint(point) + if self.n_points() == self._max_points: + self._front_down_left = Branch3d( + self._max_points, + self.x_size()[0], + self.x_mid_point(), + self.y_size()[0], + self.y_mid_point(), + self.z_size()[0], + self.z_mid_point()) + self._front_down_right = Branch3d( + self._max_points, + self.x_mid_point(), + self.x_size()[1], + self.y_size()[0], + self.y_mid_point(), + self.z_size()[0], + self.z_mid_point()) + self._front_up_left = Branch3d( + self._max_points, + self.x_size()[0], + self.x_mid_point(), + self.y_mid_point(), + self.y_size()[1], + self.z_size()[0], + self.z_mid_point()) + self._front_up_right = Branch3d( + self._max_points, + self.x_mid_point(), + self.x_size()[1], + self.y_mid_point(), + self.y_size()[1], + self.z_size()[0], + self.z_mid_point()) + self._back_down_left = Branch3d( + self._max_points, + self.x_size()[0], + self.x_mid_point(), + self.y_size()[0], + self.y_mid_point(), + self.z_mid_point(), + self.z_size()[1]) + self._back_down_right = Branch3d( + self._max_points, + self.x_mid_point(), + self.x_size()[1], + self.y_size()[0], + self.y_mid_point(), + self.z_mid_point(), + self.z_size()[1]) + self._back_up_left = Branch3d( + self._max_points, + self.x_size()[0], + self.x_mid_point(), + self.y_mid_point(), + self.y_size()[1], + self.z_mid_point(), + self.z_size()[1]) + self._back_up_right = Branch3d( + self._max_points, + self.x_mid_point(), + self.x_size()[1], + self.y_mid_point(), + self.y_size()[1], + self.z_mid_point(), + self.z_size()[1]) + self._divided = True + + self.sub_branches = {0: self._front_down_left, + 1: self._front_down_right, + 2: self._front_up_left, + 3: self._front_up_right, + 4: self._back_down_left, + 5: self._back_down_right, + 6: self._back_up_left, + 7: self._back_up_right} + + list = self.points_list() + for i in range(self.n_points()): + + self.sub_add_point(list[i]) + self.remove_point(0) + + self.sub_add_point(point) else: - self._POINTS = np.append(self._POINTS, point) - self._N_POINTS += 1 + self._points = np.append(self._points, point) + self._n_points += 1 + + def update(self): + """Recursively checks if all the points in the branch and its subbranches still belong there. + Returns an array with all the points that had positions out of the branch's bounds. + """ + return_list = np.empty(0) + if self.is_divided() == True: + update_points = np.empty(0) + for i in range(len(self.sub_branches)): + update_points = np.append( + update_points, self.sub_branch(i).update()) + + removed = 0 + remain_points = np.copy(update_points) + for i in range(update_points.size): + if ((self.x_size()[0] > update_points[i]()[0]) or + (update_points[i]()[0] > self.x_size()[1]) or + (self.y_size()[0] > update_points[i]()[1]) or + (update_points[i]()[1] > self.y_size()[1]) or + (self.z_size()[0] > update_points[i]()[2]) or + (update_points[i]()[2] > self.z_size()[1])): + return_list = np.append(return_list, update_points[i]) + remain_points = np.delete(remain_points, i - removed) + removed += 1 + + if self.total_points() <= self._max_points: + remain_points = np.append( + remain_points, self.all_points_list()) + self._divided = False + self._front_down_left = None + self._front_down_right = None + self._front_up_left = None + self._front_up_right = None + self._back_down_left = None + self._back_down_right = None + self._back_up_left = None + self._back_up_right = None + self.sub_branches = {0: self._front_down_left, + 1: self._front_down_right, + 2: self._front_up_left, + 3: self._front_up_right, + 4: self._back_down_left, + 5: self._back_down_right, + 6: self._back_up_left, + 7: self._back_up_right} + + for i in range(remain_points.shape[0]): + self.add_point(remain_points[i]) + + return return_list + + else: + list = self.points_list() + removed = 0 + for i in range(list.size): + if ((self.x_size()[0] > list[i]()[0]) or + (list[i]()[0] > self.x_size()[1]) or + (self.y_size()[0] > list[i]()[1]) or + (list[i]()[1] > self.y_size()[1]) or + (self.z_size()[0] > list[i]()[2]) or + (list[i]()[2] > self.z_size()[1]) + ): + self.remove_point(i - removed) + removed += 1 + return_list = np.append(return_list, list[i]) + + return return_list class Tree3d(Tree2d): - '''Class that stores the root branch and general 3d tree informations. - * branch : Branch3d to be the root of the tree.''' - - def __init__(self, branch : Branch3d): - self._ROOT = branch - self.xmin = branch.GetXSize()[0] - self.xmax = branch.GetXSize()[1] - self.ymin = branch.GetYSize()[0] - self.ymax = branch.GetYSize()[1] - self.zmin = branch.GetZSize()[0] - self.zmax = branch.GetZSize()[1] - self._N_POINTS = branch.GetTotalPoints() - self._STR = "" + """Class that stores the root branch and general 3d tree information.\n + Parameters + ---------- + branch : Branch3d\n + Branch to be the root of the tree. + """ + + def __init__(self, branch: Branch3d): + self._root = branch + self._xmin = branch.x_size()[0] + self._xmax = branch.x_size()[1] + self._ymin = branch.y_size()[0] + self._ymax = branch.y_size()[1] + self._zmin = branch.z_size()[0] + self._zmax = branch.z_size()[1] + self._n_points = branch.total_points() + self._str = "" + + def z_size(self): + return (self._zmin, self._zmax) def __str__(self): - if self._ROOT.IsDivided() == True: + if self._root.is_divided() == True: points_list = [] - for i in range(len(self.GetRoot().subBranches)): - points_list.append(self.GetRoot().GetSubBranch(i).GetTotalPoints()) + for i in range(len(self.root().sub_branches)): + points_list.append(self.root().sub_branch(i).total_points()) length = [len(str(x)) for x in points_list] length = [np.max([length[j], length[j + 2]]) - for j in range(len(points_list)//2)] - self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n\ - FRONT\n\ - [ {self._ROOT._FRONTUPLEFT.GetTotalPoints()} | {self._ROOT._FRONTUPRIGHT.GetTotalPoints()} ]\n\ - [---" + "-"*(length[0] - 1) + "|---" + "-"*(length[1] - 1) + f"]\n\ - [ {self._ROOT._FRONTDOWNLEFT.GetTotalPoints()} | {self._ROOT._FRONTDOWNRIGHT.GetTotalPoints()} ]\n\ - | BACK\n\ - [ {self._ROOT._BACKUPLEFT.GetTotalPoints()} | {self._ROOT._BACKUPRIGHT.GetTotalPoints()} ]\n\ - [---" + "-"*(length[2] - 1) + "|---" + "-"*(length[3] - 1) + f"]\n\ - [ {self._ROOT._BACKDOWNLEFT.GetTotalPoints()} | {self._ROOT._BACKDOWNRIGHT.GetTotalPoints()} ]\n" + for j in range(len(points_list) // 2)] + self._str = f"Number of points = {self._root.total_points()}\n" + self._str += "| FRONT\n" + self._str += f"[ {self._root._front_up_left.total_points()} | {self._root._front_up_right.total_points()} ]\n" + self._str += "[---" + "-" * \ + (length[0] - 1) + "|---" + "-" * (length[1] - 1) + "]\n" + self._str += f"[ {self._root._front_down_left.total_points()} | {self._root._front_down_right.total_points()} ]\n" + self._str += "| BACK\n" + self._str += f"[ {self._root._back_up_left.total_points()} | {self._root._back_up_right.total_points()} ]\n" + self._str += "[---" + "-" * \ + (length[2] - 1) + "|---" + "-" * (length[3] - 1) + "]\n" + self._str += f"[ {self._root._back_down_left.total_points()} | {self._root._back_down_right.total_points()} ]\n" else: - self._STR = f"number of points = {self._ROOT.GetTotalPoints()}\n FRONT\n\ + self._str = f"Number of points = {self._root.total_points()}\n FRONT\n\ [ ]\n\ [ ]\n\ [ ]\n\ @@ -624,53 +914,61 @@ def __str__(self): [ ]\n\ [ ]\n\ [ ]\n" - return self._STR + return self._str + # END OF OCTREE IMPLEMENTATION # GRAPHICAL IMPLEMENTATION -def BoundingBox3d(center : tuple = (0.0, 0.0, 0.0), - size : tuple = (1.0, 1.0, 1.0), - color : tuple = (1.0, 1.0, 1.0, 1.0), - line_width : float = 1.0): - '''Creates a bounding box with the parameters given. The box got only is edges renderized. - * center : tuple with 3 coordinates, x, y, and z, that determines where is the center of the box. - * size : tuple with 3 coordinates, x, y, and z, that determines its lateral sizes. - * color : tuple with 4 coordinates, r, g, b and a, that determines the color of the bounding box. - * line_width : float that determines the width of the box lines.''' +def bounding_box_3d(center: tuple = (0.0, 0.0, 0.0), + size: tuple = (1.0, 1.0, 1.0), + color: tuple = (1.0, 1.0, 1.0, 1.0), + line_width: float = 1.0): + """Creates a bounding box with the parameters given. The box got only is edges renderized.\n + Parameters + ---------- + center : tuple\n + x, y, and z, that determines where is the center of the box.\n + size : tuple\n + x, y, and z, that determines its lateral sizes.\n + color : tuple\n + r, g, b and a, that determines the color of the bounding box.\n + line_width : float\n + Determines the width of the box lines. + """ x_c = center[0] y_c = center[1] z_c = center[2] - x_l = size[0]/2 - y_l = size[1]/2 - z_l = size[2]/2 + x_l = size[0] / 2 + y_l = size[1] / 2 + z_l = size[2] / 2 vertices = np.array([ - [x_c - x_l, y_c - y_l, z_c - z_l], - [x_c + x_l, y_c - y_l, z_c - z_l], - [x_c + x_l, y_c + y_l, z_c - z_l], - [x_c - x_l, y_c + y_l, z_c - z_l], - [x_c - x_l, y_c - y_l, z_c + z_l], - [x_c + x_l, y_c - y_l, z_c + z_l], - [x_c + x_l, y_c + y_l, z_c + z_l], - [x_c - x_l, y_c + y_l, z_c + z_l] - ]) + [x_c - x_l, y_c - y_l, z_c - z_l], + [x_c + x_l, y_c - y_l, z_c - z_l], + [x_c + x_l, y_c + y_l, z_c - z_l], + [x_c - x_l, y_c + y_l, z_c - z_l], + [x_c - x_l, y_c - y_l, z_c + z_l], + [x_c + x_l, y_c - y_l, z_c + z_l], + [x_c + x_l, y_c + y_l, z_c + z_l], + [x_c - x_l, y_c + y_l, z_c + z_l] + ]) data = np.array([ - [vertices[0], vertices[4]], - [vertices[3], vertices[7]], - [vertices[1], vertices[5]], - [vertices[2], vertices[6]], - [vertices[0], vertices[1]], - [vertices[3], vertices[2]], - [vertices[7], vertices[6]], - [vertices[4], vertices[5]], - [vertices[0], vertices[3]], - [vertices[1], vertices[2]], - [vertices[4], vertices[7]], - [vertices[5], vertices[6]] - ]) + [vertices[0], vertices[4]], + [vertices[3], vertices[7]], + [vertices[1], vertices[5]], + [vertices[2], vertices[6]], + [vertices[0], vertices[1]], + [vertices[3], vertices[2]], + [vertices[7], vertices[6]], + [vertices[4], vertices[5]], + [vertices[0], vertices[3]], + [vertices[1], vertices[2]], + [vertices[4], vertices[7]], + [vertices[5], vertices[6]] + ]) lines = line(data, colors=color) lines.GetProperty().SetLineWidth(line_width) @@ -679,38 +977,42 @@ def BoundingBox3d(center : tuple = (0.0, 0.0, 0.0), return lines -def GetActorFromBranch2d(branch : Branch2d, color=( +def actor_from_branch_2d(branch: Branch2d, color=( 1.0, 1.0, 1.0), linewidth=1.0) -> Actor: - '''Recursively creates actors for the branch given. If the branch is divided, + """Recursively creates actors for the branch given. If the branch is divided, then the function is run for the subbranches until the function reaches a non-divided branch, that creates the actor to be returned. This actor is then appended into a list, that is then returned. - NOTE: This returns a 3d actor. - * branch : Branch3d that will have the actor created.''' - - if branch.IsDivided() == True: - actors = np.array([], dtype=Actor) + NOTE: This returns a 3d actor.\n + Parameters + ---------- + branch : Branch3d\n + Branch that will have the actor created. + """ + + if branch.is_divided() == True: + actors = np.empty(0, dtype=Actor) actors = np.append( actors, - GetActorFromBranch2d( - branch.GetSubBranch(0), + actor_from_branch_2d( + branch.sub_branch(0), color, linewidth)) actors = np.append( actors, - GetActorFromBranch2d( - branch.GetSubBranch(1), + actor_from_branch_2d( + branch.sub_branch(1), color, linewidth)) actors = np.append( actors, - GetActorFromBranch2d( - branch.GetSubBranch(2), + actor_from_branch_2d( + branch.sub_branch(2), color, linewidth)) actors = np.append( actors, - GetActorFromBranch2d( - branch.GetSubBranch(3), + actor_from_branch_2d( + branch.sub_branch(3), color, linewidth)) @@ -718,79 +1020,80 @@ def GetActorFromBranch2d(branch : Branch2d, color=( else: - data = np.array([]) - data0 = branch.GetPointsList() - for i in range(data0.shape[0]): - np.append(data, data0[i]()) - - x_c = branch.GetXMiddlePoint() - y_c = branch.GetYMiddlePoint() + x_c = branch.x_mid_point() + y_c = branch.y_mid_point() z_c = 0.0 - x_l = (branch.GetXSize()[1] - branch.GetXSize()[0]) - y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) + x_l = (branch.x_size()[1] - branch.x_size()[0]) + y_l = (branch.y_size()[1] - branch.y_size()[0]) z_l = 0.0 - cubeActor = BoundingBox3d((x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) + cubeActor = bounding_box_3d( + (x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) return cubeActor -def GetActorFromBranch3d(branch : Branch3d, color=( +def actor_from_branch_3d(branch: Branch3d, color=( 1.0, 1.0, 1.0), linewidth=1.0) -> Actor: - '''Recursively creates actors for the branch given. If the branch is divided, - then the function is run for the subbranches until the function reaches a non-divided branch, - that creates the actor to be returned. This actor is then appended into a list, that is then returned. - * branch : Branch3d that will have the actor created.''' - - if branch.IsDivided() == True: - actors = np.array([], dtype=Actor) + """Recursively creates actors for the branch given. + If the branch is divided, then the function is run for the subbranches + until the function reaches a non-divided branch, that creates the actor to be returned. + This actor is then appended into a list, that is then returned.\n + Parameters + ---------- + branch : Branch3d\n + Branch that will have the actor created. + """ + + if branch.is_divided() == True: + actors = np.empty(0, dtype=Actor) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(0), + actor_from_branch_3d( + branch.sub_branch(0), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(1), + actor_from_branch_3d( + branch.sub_branch(1), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(2), + actor_from_branch_3d( + branch.sub_branch(2), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(3), + actor_from_branch_3d( + branch.sub_branch(3), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(4), + actor_from_branch_3d( + branch.sub_branch(4), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(5), + actor_from_branch_3d( + branch.sub_branch(5), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(6), + actor_from_branch_3d( + branch.sub_branch(6), color, linewidth)) actors = np.append( actors, - GetActorFromBranch3d( - branch.GetSubBranch(7), + actor_from_branch_3d( + branch.sub_branch(7), color, linewidth)) @@ -798,20 +1101,16 @@ def GetActorFromBranch3d(branch : Branch3d, color=( else: - data = np.array([]) - data0 = branch.GetPointsList() - for i in range(data0.shape[0]): - np.append(data, data0[i]()) - - x_c = branch.GetXMiddlePoint() - y_c = branch.GetYMiddlePoint() - z_c = branch.GetZMiddlePoint() + x_c = branch.x_mid_point() + y_c = branch.y_mid_point() + z_c = branch.z_mid_point() - x_l = (branch.GetXSize()[1] - branch.GetXSize()[0]) - y_l = (branch.GetYSize()[1] - branch.GetYSize()[0]) - z_l = (branch.GetZSize()[1] - branch.GetZSize()[0]) + x_l = (branch.x_size()[1] - branch.x_size()[0]) + y_l = (branch.y_size()[1] - branch.y_size()[0]) + z_l = (branch.z_size()[1] - branch.z_size()[0]) - cubeActor = BoundingBox3d((x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) + cubeActor = bounding_box_3d( + (x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) return cubeActor # END OF GRAPH IMPLEMENTATION From ffe478866835a887ee6e50a6d18471cd801ec795 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Tue, 23 May 2023 16:44:55 -0300 Subject: [PATCH 09/10] Point storing and tracking overhaul --- docs/examples/trees_example.py | 35 +- fury/tests/test_trees.py | 158 ++++---- fury/trees.py | 684 ++++++++++++++++++++++----------- 3 files changed, 570 insertions(+), 307 deletions(-) diff --git a/docs/examples/trees_example.py b/docs/examples/trees_example.py index af0be0cf8..002faf690 100644 --- a/docs/examples/trees_example.py +++ b/docs/examples/trees_example.py @@ -11,12 +11,19 @@ points = np.random.rand(npoints, 2) tree = trees.Tree2d(br) + +points_dic = {} for i in range(npoints): - tree.add_point(trees.Point2d(points[i, 0], points[i, 1])) + points_dic[tree.add_point(points[i])] = points[i] + +print("Points and its ids") +print(points_dic) +print() + actorslist = trees.actor_from_branch_2d(tree.root(), (1.0, 1.0, 1.0), 1.0) zeros = np.zeros((npoints, 1)) -actorPoints = actor.dots(np.hstack((points, zeros)), (1.0, 0.5, 0.4), 1, 5) +actorPoints = actor.dots(np.hstack((tree.root().all_points_list(), zeros)), (1.0, 0.5, 0.4), 1, 5) scene = window.Scene() scene.set_camera(position=(-6, 5, -10), @@ -51,9 +58,11 @@ # TESTING THE UPDATE FUNCTION def divide(branch, div : float): + dic = branch.points_dic() + id_list = list(dic.keys()) for i in range(branch.points_list().shape[0]): - update_coord = branch.points_list()[i]()/div - branch.points_list()[i].set_coord(update_coord[0], update_coord[1]) + update_coord = branch.points_list()[i]/div + branch.points_dic()[id_list[i]] = update_coord div = 2.0 tree.root().process_branch(divide, div) @@ -63,6 +72,7 @@ def divide(branch, div : float): tree.root().update() print("After update") print(tree) + actorslist = trees.actor_from_branch_2d(tree.root(), (1.0, 1.0, 1.0), 1.0) zeros = np.zeros((npoints, 1)) actorPoints = actor.dots(np.hstack((points, zeros))/div, (1.0, 0.5, 0.4), 1, 5) @@ -98,7 +108,6 @@ def divide(branch, div : float): interactor.Start() - # TESTING AN OCTREE WITH RANDOM 3D POINTS xl = 1.0 yl = 2.0 @@ -112,9 +121,12 @@ def divide(branch, div : float): data[:, 1] = yl*data[:, 1] data[:, 2] = zl*data[:, 2] + tree = trees.Tree3d(br) + +points_dic = {} for i in range(npoints): - tree.add_point(trees.Point3d(data[i, 0], data[i, 1], data[i, 2])) + points_dic[tree.add_point(data[i])] = data[i] print(tree) @@ -166,9 +178,11 @@ def abstract(branch : trees.Branch3d, number): def divide(branch, div : float): + dic = branch.points_dic() + id_list = list(dic.keys()) for i in range(branch.points_list().shape[0]): - update_coord = branch.points_list()[i]()/div - branch.points_list()[i].set_coord(update_coord[0], update_coord[1], update_coord[2]) + update_coord = branch.points_list()[i]/div + branch.points_dic()[id_list[i]] = update_coord div = 2.0 @@ -178,7 +192,7 @@ def divide(branch, div : float): actorslist = trees.actor_from_branch_3d(tree.root(), (1.0, 1.0, 1.0), 1.0) zeros = np.zeros((npoints, 1)) -actorPoints = actor.dots(np.hstack((data, zeros))/div, (1.0, 0.5, 0.4), 1, 5) +actorPoints = actor.dots(np.hstack((tree.root().all_points_list(), zeros))/div, (1.0, 0.5, 0.4), 1, 5) scene = window.Scene() scene.set_camera(position=(-6, 5, -10), @@ -195,7 +209,7 @@ def divide(branch, div : float): order_transparent=True) -actorPoints = actor.dots(data/div, (1.0, 0.5, 0.4), 1, 5) +actorPoints = actor.dots(tree.root().all_points_list(), (1.0, 0.5, 0.4), 1, 5) scene.add(actorPoints) @@ -209,6 +223,7 @@ def divide(branch, div : float): scene.add(actorslist) + interactor = lib.RenderWindowInteractor() interactor.SetRenderWindow(showmanager.window) diff --git a/fury/tests/test_trees.py b/fury/tests/test_trees.py index 21a68fdf0..4b4a0cc44 100644 --- a/fury/tests/test_trees.py +++ b/fury/tests/test_trees.py @@ -5,29 +5,6 @@ import trees -def test_point2d(): - array = np.array([1, 2]) - point = trees.Point2d(array[0], array[1]) - new_array = np.array([1, 1]) - npt.assert_array_equal(array, point()) - npt.assert_equal(array[0], point.get_x_coord()) - npt.assert_equal(array[1], point.get_y_coord()) - point.set_coord(new_array[0], new_array[1]) - npt.assert_array_equal(new_array, point()) - npt.assert_string_equal(str(new_array), str(point)) - npt.assert_string_equal(repr(new_array), repr(point)) - - -def test_point3d(): - array = np.array([1, 2, 3]) - point = trees.Point3d(array[0], array[1], array[2]) - new_array = np.array([1, 1, 1]) - npt.assert_array_equal(array, point()) - npt.assert_equal(array[2], point.get_z_coord()) - point.set_coord(new_array[0], new_array[1], new_array[2]) - npt.assert_array_equal(new_array, point()) - - def test_branch2d(): max_points = 4 x_size = (0.0, 1.0) @@ -53,22 +30,48 @@ def test_branch2d(): npt.assert_equal(mid_x, branch.x_mid_point()) npt.assert_equal(mid_y, branch.y_mid_point()) - points = np.array([trees.Point2d(pos[0], pos[1]) for pos in positions]) - for i in range(points.shape[0] - 1): - branch.add_point(points[i]) - npt.assert_equal(points.shape[0] - 1, branch.n_points()) - npt.assert_equal( - True, positions[:-1] in np.array([point().tolist() for point in branch.points_list()])) - branch.add_point(points[-1]) + points_dic = {} + for i in range(positions.shape[0] - 1): + points_dic[branch.add_point(positions[i])] = positions[i] + npt.assert_equal(positions.shape[0] - 1, branch.n_points()) + npt.assert_equal(True, np.isin( + positions[:-1], np.array([point.tolist() for point in branch.points_list()])).all()) + points_dic[branch.add_point(positions[-1])] = positions[-1] def all_points_inside(branch): return branch.points_list() - npt.assert_equal(True, positions in np.array( - [point().tolist() for point in branch.process_branch(all_points_inside)])) - npt.assert_array_equal([], np.array([point().tolist() + ids = list(points_dic.keys()) + + def get_branch(s : str, branch): + branch = branch + for c in s: + if c == 'f': + break + branch = branch.sub_branch(int(c)) + return branch + + ref_branch = get_branch(branch.search(points_dic[ids[0]], ids[0]), branch) + + npt.assert_equal( + True, ref_branch.points_dic()[ + ids[0]] == branch.sub_branch(0).points_dic()[ + ids[0]]) + npt.assert_equal(True, branch.sub_branch( + 0) == branch.branch_from_point(points_dic[ids[0]], ids[0])) + npt.assert_equal(True, branch.sub_branch(0).points_dic() == + branch.relatives_from_point(points_dic[ids[0]], ids[0])) + + processing_points = np.array([branch.sub_branch(i).points_dic() if len(branch.sub_branch( + i).points_dic()) != 0 else None for i in range(len(branch.sub_branches))]) + + npt.assert_equal(True, np.isin(processing_points, branch.points_to_process()).all()) + + npt.assert_equal(True, np.isin(positions, np.array( + [point.tolist() for point in branch.process_branch(all_points_inside)])).all()) + npt.assert_array_equal([], np.array([point.tolist() for point in branch.points_list()])) - npt.assert_equal(points.size, branch.total_points()) + npt.assert_equal(positions.shape[0], branch.total_points()) npt.assert_equal(True, branch.is_divided()) npt.assert_equal(0, branch.n_points()) npt.assert_equal(True, branch._downleft == branch.sub_branch(0)) @@ -77,26 +80,27 @@ def all_points_inside(branch): npt.assert_equal(True, branch._upright == branch.sub_branch(3)) div = 2.0 new_positions = positions / div - new_points = np.array([trees.Point2d(pos[0], pos[1]) - for pos in new_positions]) + new_branch = trees.Branch2d(max_points, x_size[0], x_size[1], y_size[0], y_size[1]) - for i in range(new_points.shape[0]): - new_branch.add_point(new_points[i]) + for i in range(new_positions.shape[0]): + new_branch.add_point(new_positions[i], new_points=False, existing_key=ids[i]) - def divide(branch, div: float): + def divide(branch, div : float): + dic = branch.points_dic() + ids_list = list(dic.keys()) for i in range(branch.points_list().shape[0]): - update_coord = branch.points_list()[i]() / div - branch.points_list()[i].set_coord(update_coord[0], update_coord[1]) + update_coord = branch.points_list()[i]/div + branch.points_dic()[ids_list[i]] = update_coord branch.process_branch(divide, div) branch.update() npt.assert_equal(True, branch == new_branch) branch.sub_branch(0).sub_branch(0).remove_point(0) npt.assert_equal(0, branch.sub_branch(0).sub_branch(0).n_points()) - npt.assert_array_equal([], branch.sub_branch(0).points_list()) + npt.assert_array_equal([], branch.sub_branch(0).points_list().tolist()) def test_branch3d(): @@ -125,23 +129,24 @@ def test_branch3d(): npt.assert_equal(z_size, branch.z_size()) npt.assert_equal(mid_z, branch.z_mid_point()) - points = np.array([trees.Point3d(pos[0], pos[1], pos[2]) - for pos in positions]) - for i in range(points.shape[0] - 1): - branch.add_point(points[i]) - npt.assert_equal(points.shape[0] - 1, branch.n_points()) - npt.assert_equal( - True, positions[:-1] in np.array([point().tolist() for point in branch.points_list()])) - branch.add_point(points[-1]) + points_dic = {} + for i in range(positions.shape[0] - 1): + points_dic[branch.add_point(positions[i])] = positions[i] + npt.assert_equal(positions.shape[0] - 1, branch.n_points()) + npt.assert_equal(True, np.isin( + positions[:-1], np.array([point.tolist() for point in branch.points_list()])).all()) + points_dic[branch.add_point(positions[-1])] = positions[-1] + + ids = list(points_dic.keys()) def all_points_inside(branch): return branch.points_list() - npt.assert_equal(True, positions in np.array( - [point().tolist() for point in branch.process_branch(all_points_inside)])) - npt.assert_array_equal([], np.array([point().tolist() + npt.assert_equal(True, np.isin(positions, np.array( + [point.tolist() for point in branch.process_branch(all_points_inside)])).all()) + npt.assert_array_equal([], np.array([point.tolist() for point in branch.points_list()])) - npt.assert_equal(points.size, branch.total_points()) + npt.assert_equal(positions.shape[0], branch.total_points()) npt.assert_equal(True, branch.is_divided()) npt.assert_equal(0, branch.n_points()) npt.assert_equal(True, branch._front_down_left == branch.sub_branch(0)) @@ -154,8 +159,7 @@ def all_points_inside(branch): npt.assert_equal(True, branch._back_up_right == branch.sub_branch(7)) div = 2.0 new_positions = positions / div - new_points = np.array([trees.Point3d(pos[0], pos[1], pos[2]) - for pos in new_positions]) + new_branch = trees.Branch3d(max_points, x_size[0], x_size[1], @@ -163,20 +167,21 @@ def all_points_inside(branch): y_size[1], z_size[0], z_size[1]) - for i in range(new_points.shape[0]): - new_branch.add_point(new_points[i]) + for i in range(new_positions.shape[0]): + new_branch.add_point(new_positions[i], new_points=False, existing_key=ids[i]) - def divide(branch, div: float): + def divide(branch, div : float): + dic = branch.points_dic() + ids_list = list(dic.keys()) for i in range(branch.points_list().shape[0]): - update_coord = branch.points_list()[i]() / div - branch.points_list()[i].set_coord( - update_coord[0], update_coord[1], update_coord[2]) + update_coord = branch.points_list()[i]/div + branch.points_dic()[ids_list[i]] = update_coord branch.process_branch(divide, div) branch.update() npt.assert_equal(True, branch == new_branch) branch.sub_branch(0).sub_branch(0).remove_point(0) npt.assert_equal(0, branch.sub_branch(0).sub_branch(0).n_points()) - npt.assert_array_equal([], branch.sub_branch(0).points_list()) + npt.assert_array_equal([], branch.sub_branch(0).points_list().tolist()) def test_tree2d(): @@ -198,10 +203,9 @@ def test_tree2d(): npt.assert_equal(0, tree.n_points()) npt.assert_equal(x_size, tree.x_size()) npt.assert_equal(y_size, tree.y_size()) - point = trees.Point2d(positions[0, 0], positions[0, 1]) - tree.add_point(point) + tree.add_point(positions[0]) npt.assert_equal(1, tree.root().n_points()) - npt.assert_equal(True, tree.root().all_points_list()[0] == point) + npt.assert_equal(True, tree.root().all_points_list()[0] == positions[0]) def test_tree3d(): @@ -225,10 +229,9 @@ def test_tree3d(): npt.assert_equal(x_size, tree.x_size()) npt.assert_equal(y_size, tree.y_size()) npt.assert_equal(z_size, tree.z_size()) - point = trees.Point3d(positions[0, 0], positions[0, 1], positions[0, 2]) - tree.add_point(point) + tree.add_point(positions[0]) npt.assert_equal(1, tree.root().n_points()) - npt.assert_equal(True, tree.root().all_points_list()[0] == point) + npt.assert_equal(True, tree.root().all_points_list()[0] == positions[0]) def test_bounding_Box(): @@ -271,9 +274,8 @@ def test_actor_from_branch_2d(): x_size[1], y_size[0], y_size[1]) - points = np.array([trees.Point2d(pos[0], pos[1]) for pos in positions]) - for i in range(points.shape[0]): - branch.add_point(points[i]) + for i in range(positions.shape[0]): + branch.add_point(positions[i]) actors = trees.actor_from_branch_2d(branch, color, line_width) @@ -290,7 +292,7 @@ def get_actors(branch, color, linewidth): (x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) actors_test = np.empty(0, dtype=Actor) - if branch.is_divided() == True: + if branch.is_divided(): actors_test = np.append( actors_test, get_actors( @@ -361,10 +363,8 @@ def test_actor_from_branch_3d(): y_size[1], z_size[0], z_size[1]) - points = np.array([trees.Point3d(pos[0], pos[1], pos[2]) - for pos in positions]) - for i in range(points.shape[0]): - branch.add_point(points[i]) + for i in range(positions.shape[0]): + branch.add_point(positions[i]) actors = trees.actor_from_branch_3d(branch, color, line_width) def get_actors(branch, color, linewidth): @@ -380,7 +380,7 @@ def get_actors(branch, color, linewidth): (x_c, y_c, z_c), (x_l, y_l, z_l), color, linewidth) actors_test = np.empty(0, dtype=Actor) - if branch.is_divided() == True: + if branch.is_divided(): actors_test = np.append( actors_test, get_actors( @@ -444,4 +444,4 @@ def get_actors(branch, color, linewidth): for i in range(actors.shape[0]): npt.assert_equal(actors_test[i].GetCenter(), actors[i].GetCenter()) - npt.assert_equal(actors_test[i].GetBounds(), actors[i].GetBounds()) \ No newline at end of file + npt.assert_equal(actors_test[i].GetBounds(), actors[i].GetBounds()) diff --git a/fury/trees.py b/fury/trees.py index 7555e17a7..f89fb4c47 100644 --- a/fury/trees.py +++ b/fury/trees.py @@ -1,46 +1,10 @@ from fury.utils import Actor from fury.actor import line import numpy as np +from uuid import uuid4 # QUADTREE IMPLEMENTATION -class Point2d: - """General point class. This class only stores 2d coordinates, - and is to be used as base for any class that intends to use the quadtree structure. - """ - - def __init__(self, x: float, y: float): - self.coord = np.array([x, y]) - - def __call__(self): - return self.coord - - def __eq__(self, point): - return np.allclose(self.coord, point.coord, rtol=10 ** - (-np.finfo(self.coord[0].dtype).precision)) - - def get_x_coord(self) -> float: - """Returns the x coordinate of the point.""" - return self.coord[0] - - def get_y_coord(self) -> float: - """Returns the y coordinate of the point.""" - return self.coord[1] - - def set_coord(self, x: float, y: float) -> np.array: - """Updates the coordinate of the point. - ## Parameters - * x : float x position - * y : float y position""" - self.coord = np.array([x, y]) - - def __str__(self): - return str(self.coord) - - def __repr__(self): - return repr(self.coord) - - class Branch2d(): """General branch class. It is the base structure for building a quadtree.\n This branch, when divided, has four branches: \n @@ -60,7 +24,7 @@ class Branch2d(): * y_min : float\n Lower limit for y * y_max : float - Upper limit for y \n + Upper limit for y\n """ def __init__(self, max_points: int, x_min: float, @@ -72,7 +36,7 @@ def __init__(self, max_points: int, x_min: float, self._xmax = x_max self._ymin = y_min self._ymax = y_max - self._points = np.empty(0, dtype=Point2d) + self._points_dic = {} self._upleft = None self._upright = None self._downleft = None @@ -83,13 +47,13 @@ def __init__(self, max_points: int, x_min: float, 3: self._upright} def __eq__(self, branch): - if self.is_divided() == True: + if self.is_divided(): eq = True - if (branch.is_divided() == True): + if (branch.is_divided()): if (self.n_points() == branch.n_points() and self.max_points() == branch.max_points() and self.x_size() == branch.x_size() and - self.y_size() == branch.y_size()) == True: + self.y_size() == branch.y_size()): eq = eq and True else: @@ -110,7 +74,7 @@ def __eq__(self, branch): if (self.n_points() == branch.n_points() and self.max_points() == branch.max_points() and self.x_size() == branch.x_size() and - self.y_size() == branch.y_size()) == True: + self.y_size() == branch.y_size()): return True else: return False @@ -119,7 +83,8 @@ def __eq__(self, branch): self.max_points() == branch.max_points() and self.x_size() == branch.x_size() and self.y_size() == branch.y_size() and - np.any(self.points_list() == branch.points_list()) == True) == True: + np.any(self.points_list() == branch.points_list()) and + np.any(self.points_id() == branch.points_id())): return True else: return False @@ -160,7 +125,7 @@ def y_mid_point(self) -> float: self.y_size()[0]) / float(2) def __str__(self): - if self.is_divided() == True: + if self.is_divided(): str = f"number of points = {self.total_points()}\n" str += f"[ {self._upleft.n_points()} | {self._upright.n_points()} ]\n" str += "[---|---]\n" @@ -171,7 +136,15 @@ def __str__(self): def points_list(self): """Returns a list containing the points existing in that branch.""" - return self._points + return np.array(list(self._points_dic.values())) + + def points_id(self): + """Returns a list containing the points existing in that branch.""" + return np.array(list(self._points_dic.keys())) + + def points_dic(self): + """Returns a dictionary containing the points and its ids existing in that branch.""" + return self._points_dic def sub_branch(self, index: int): """Returns the sub branch by index. Below, the index correspondence: @@ -184,14 +157,14 @@ def sub_branch(self, index: int): * index : int\n Index of the branch requested """ - if self.is_divided() == True: + if self.is_divided(): return self.sub_branches[index] else: raise AttributeError("The branch got no subbranches.") def total_points(self) -> int: """Returns the total number of points in that branch, including the points in the subranches.""" - if self.is_divided() == True: + if self.is_divided(): sum = 0 for i in range(len(self.sub_branches)): @@ -210,27 +183,39 @@ def remove_point(self, index: int): Index of the point to be removed. """ if self._n_points != 0: - self._points = np.delete(self._points, index) + id = list(self._points_dic.keys())[index] + self._points_dic.pop(id) self._n_points -= 1 + return id else: raise AttributeError("This branch has no point to be removed.") - # def sub_add_point(self): - - def sub_add_point(self, point: Point2d): - if self.x_size()[0] <= point()[0] < self.x_mid_point(): - if self.y_size()[0] <= point()[1] < self.y_mid_point(): - self._downleft.add_point(point) - elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: - self._upleft.add_point(point) + def sub_add_point( + self, + point: np.array, + points_id_list: np.array = np.array( + []), + new_points: bool = True, + existing_key=0): + id = existing_key + + if self.x_size()[0] <= point[0] < self.x_mid_point(): + if self.y_size()[0] <= point[1] < self.y_mid_point(): + id = self._downleft.add_point( + point, points_id_list, new_points, existing_key) + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + id = self._upleft.add_point( + point, points_id_list, new_points, existing_key) else: raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") - elif self.x_mid_point() <= point()[0] <= self.x_size()[1]: - if self.y_size()[0] <= point()[1] < self.y_mid_point(): - self._downright.add_point(point) - elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: - self._upright.add_point(point) + elif self.x_mid_point() <= point[0] <= self.x_size()[1]: + if self.y_size()[0] <= point[1] < self.y_mid_point(): + id = self._downright.add_point( + point, points_id_list, new_points, existing_key) + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + id = self._upright.add_point( + point, points_id_list, new_points, existing_key) else: raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") @@ -238,7 +223,15 @@ def sub_add_point(self, point: Point2d): raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") - def add_point(self, point: Point2d): + return id + + def add_point( + self, + point: np.array, + points_id_list: np.array = np.array( + []), + new_points: bool = True, + existing_key=0): """Recursively adds a point to the branch.\n Parameters ---------- @@ -254,10 +247,10 @@ def add_point(self, point: Point2d): 4. If it's not ,the branch is then divided and the coordinates of the point are checked to add the point in one of the newly-created subbranches. """ + id = existing_key + if self.is_divided(): - if self.is_divided() == True: - - self.sub_add_point(point) + id = self.sub_add_point(point, points_id_list, new_points, id) else: @@ -293,16 +286,36 @@ def add_point(self, point: Point2d): 2: self._upleft, 3: self._upright} - list = self.points_list() + dic = self.points_dic() + items_list = list(dic.values()) + ids_list = list(dic.keys()) for i in range(self.n_points()): - self.sub_add_point(list[i]) + self.sub_add_point( + items_list[i], + ids_list, + new_points=False, + existing_key=ids_list[i]) self.remove_point(0) - self.sub_add_point(point) + id = self.sub_add_point( + point, points_id_list, new_points, id) else: - self._points = np.append(self._points, point) - self._n_points += 1 + if new_points: + self._n_points += 1 + + id = uuid4().bytes + while np.isin(id, points_id_list): + id = uuid4().bytes + + self._points_dic[id] = point + + else: + self._n_points += 1 + + self._points_dic[id] = point + + return id def process_branch(self, function, *args): """Recursively process the branch and its subbranches with a given function and its arguments. @@ -315,58 +328,79 @@ def process_branch(self, function, *args): *args : Any\n Arguments of the function in the order requested by the function passed """ - if self.is_divided() == True: - list = np.empty(0) + if self.is_divided(): + list = [] for i in range(len(self.sub_branches)): - list = np.append( - list, self.sub_branch(i).process_branch( - function, *args)) + list_aux = self.sub_branch(i).process_branch(function, *args) + if isinstance(list_aux, np.ndarray): + for j in range(list_aux.shape[0]): + list.append(list_aux[j]) - return list + else: + list.append(list_aux) + + return np.array(list) else: return function(self, *args) def all_points_list(self): """Returns a list with all the point inside a branch, including its subbranches.""" - if self.is_divided() == True: - list = np.empty(0) + if self.is_divided(): + list = [] for i in range(len(self.sub_branches)): - list = np.append( - list, self.sub_branch(i).all_points_list()) + list_aux = self.sub_branch(i).all_points_list() + if isinstance(list_aux, np.ndarray): + for j in range(list_aux.shape[0]): + list.append(list_aux[j]) - return list + else: + list.append(list_aux) + + return np.array(list) else: return self.points_list() - def update(self): + def all_points_dic(self): + """Returns a list with all the point inside a branch, including its subbranches.""" + if self.is_divided(): + list = {} + + for i in range(len(self.sub_branches)): + list.update(self.sub_branch(i).all_points_dic()) + + return list + + else: + return self.points_dic() + + def update(self, points_id_list: np.array = np.array([])): """Recursively checks if all the points in the branch and its subbranches still belong there. - Returns an array with all the points that had positions out of the branch's bounds. - """ - return_list = np.empty(0) - if self.is_divided() == True: - update_points = np.empty(0) + Returns an array with all the points that had positions out of the branch's bounds.""" + return_list = {} + if self.is_divided(): + update_points = {} for i in range(len(self.sub_branches)): - update_points = np.append( - update_points, self.sub_branch(i).update()) + update_points.update(self.sub_branch(i).update(points_id_list)) - removed = 0 - remain_points = np.copy(update_points) - for i in range(update_points.size): - if ((self.x_size()[0] > update_points[i]()[0]) or - (update_points[i]()[0] > self.x_size()[1]) or - (self.y_size()[0] > update_points[i]()[1]) or - (update_points[i]()[1] > self.y_size()[1])): - return_list = np.append(return_list, update_points[i]) - remain_points = np.delete(remain_points, i - removed) - removed += 1 + remain_points = update_points.copy() + + items_list = list(update_points.values()) + ids_list = list(update_points.keys()) + for i in range(len(update_points)): + if ((self.x_size()[0] > items_list[i][0]) or + (items_list[i][0] > self.x_size()[1]) or + (self.y_size()[0] > items_list[i][1]) or + (items_list[i][1] > self.y_size()[1])): + + return_list[ids_list[i]] = items_list[i] + remain_points.pop(ids_list[i]) if self.total_points() <= self._max_points: - remain_points = np.append( - remain_points, self.all_points_list()) + remain_points.update(self.all_points_dic()) self._divided = False self._upleft = None self._upright = None @@ -377,24 +411,126 @@ def update(self): 2: self._upleft, 3: self._upright} - for i in range(remain_points.shape[0]): - self.add_point(remain_points[i]) + items_list = list(remain_points.values()) + ids_list = list(remain_points.keys()) - return return_list + for i in range(len(ids_list)): + self.add_point( + items_list[i], + points_id_list, + False, + ids_list[i]) else: - list = self.points_list() + items_list = list(self._points_dic.values()) + ids_list = list(self._points_dic.keys()) removed = 0 - for i in range(list.size): - if ((self.x_size()[0] > list[i]()[0]) or - (list[i]()[0] > self.x_size()[1]) or - (self.y_size()[0] > list[i]()[1]) or - (list[i]()[1] > self.y_size()[1])): + for i in range(len(ids_list)): + if ((self.x_size()[0] > items_list[i][0]) or + (items_list[i][0] > self.x_size()[1]) or + (self.y_size()[0] > items_list[i][1]) or + (items_list[i][1] > self.y_size()[1])): self.remove_point(i - removed) removed += 1 - return_list = np.append(return_list, list[i]) + return_list[ids_list[i]] = items_list[i] + + return return_list + + def search(self, point, id): + """Returns a string that encodes the requested point location inside the tree. + The enconding works as it follows:\n + * If the branch is divided, every character of the string is the sub-branch that point is located, + until an 'f' character is reached, that means the point is located inside + the last acessed sub-branch. For example, the point at 031f is located at the second sub-branch, + from the fourth sub-branch, from the first sub-branch. + * If the branch is not divided, it will return 'f' if found, or '' if not found.\n + Parameters + --------- + point : np.array + Coordinates of the point to be found. + id : bytes + Id of the point to be found.""" + + b = '' + if self.is_divided(): + if self.x_size()[0] <= point[0] < self.x_mid_point(): + if self.y_size()[0] <= point[1] < self.y_mid_point(): + c = self._downleft.search(point, id) + if 'f' in list(c): + b += '0' + c + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + c = self._upleft.search(point, id) + if 'f' in list(c): + b += '2' + c + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") + elif self.x_mid_point() <= point[0] <= self.x_size()[1]: + if self.y_size()[0] <= point[1] < self.y_mid_point(): + c = self._downright.search(point, id) + if 'f' in list(c): + b += '1' + c + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + c = self._upright.search(point, id) + if 'f' in list(c): + b += '3' + c + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}).") + else: + points_dic = self.points_dic() + if (id in list(points_dic.keys())) and (point in points_dic[id]): + b = 'f' + + return b + + def branch_from_point(self, point: np.array, id: bytes): + """Returns the branch where the point is located.\n + Parameters + ---------- + point : np.array + Coordinates of the point to be found. + id : bytes + Id of the point to be found.""" + s = self.search(point, id) + branch = self + if s == '': + raise ValueError( + f"The point {point} with id {id} could not be found inside the requested branch.") + + for c in s: + if c == 'f': + break + branch = branch.sub_branch(int(c)) - return return_list + return branch + + def relatives_from_point(self, point: np.array, id: bytes): + """Returns the dictionary of the points inside the branch a given point is located, including the point itself.\n + Parameters + ---------- + point : np.array + Coordinates of the point to be found. + id : bytes + Id of the point to be found.""" + return self.branch_from_point(point, id).points_dic() + + def points_to_process(self): + """Returns an array with every entry being the dictionary of points of every sub-branch.""" + processing = np.empty(0, dtype=dict) + if self.is_divided(): + for i in range(len(self.sub_branches)): + processing = np.append( + processing, self.sub_branch(i).points_to_process()) + + else: + if len(self.points_dic()) != 0: + processing = np.append(processing, self.points_dic()) + + return processing class Tree2d(): @@ -431,7 +567,7 @@ def root(self): return self._root def __str__(self): - if self._root.is_divided() == True: + if self._root.is_divided(): points_list = [] for i in range(len(self.root().sub_branches)): points_list.append(self.root().sub_branch(i).total_points()) @@ -448,43 +584,26 @@ def __str__(self): return self._str - def add_point(self, point: Point2d): + def add_point( + self, + point: np.array, + new_points: bool = True, + existing_key: bytes = 0): """Adds a point to the tree.\n Parameters ---------- point : np.array\n Point to be added into the tree.""" - self._root.add_point(point) + existing_points_id = np.array( + list(self.root().all_points_dic().keys())) + id = self.root().add_point(point, existing_points_id, new_points, existing_key) self._n_points += 1 + return id # END OF QUADTREE IMPLEMENTATION # OCTREE IMPLEMENTATION -class Point3d(Point2d): - """General point class. This class only stores 3d coordinates, - and is to be used as base for any class that intends to use the octree structure. - """ - - def __init__(self, x: float, y: float, z: float): - self.coord = np.array([x, y, z]) - - def get_z_coord(self): - """Returns the z coordinate of the point.""" - return self.coord[2] - - def set_coord(self, x: float, y: float, z: float): - """Updates the coordinate of the point. - ## Parameters - * x : float\n - x position - * y : float\n - y position - * z : float\n - z position""" - self.coord = np.array([x, y, z]) - - class Branch3d(Branch2d): """General 3d branch class. It is the base structure for building an octree.\n Parameters @@ -531,7 +650,7 @@ def __init__(self, self._ymax = y_max self._zmin = z_min self._zmax = z_max - self._points = np.empty(0, dtype=Point3d) + self._points_dic = {} self._front_up_left = None self._front_up_right = None self._front_down_left = None @@ -550,14 +669,14 @@ def __init__(self, 7: self._back_up_right} def __eq__(self, branch): - if self.is_divided() == True: + if self.is_divided(): eq = True - if (branch.is_divided() == True): + if (branch.is_divided()): if (self.n_points() == branch.n_points() and self.max_points() == branch.max_points() and self.x_size() == branch.x_size() and self.y_size() == branch.y_size() and - self.z_size() == branch.z_size()) == True: + self.z_size() == branch.z_size()): eq = eq and True else: @@ -586,7 +705,7 @@ def __eq__(self, branch): self.max_points() == branch.max_points() and self.x_size() == branch.x_size() and self.y_size() == branch.y_size() and - self.z_size() == branch.z_size()) == True: + self.z_size() == branch.z_size()): return True else: return False @@ -596,7 +715,8 @@ def __eq__(self, branch): self.x_size() == branch.x_size() and self.y_size() == branch.y_size() and self.z_size() == branch.z_size() and - np.any(self.points_list() == branch.points_list()) == True) == True: + np.any(self.points_list() == branch.points_list()) and + np.any(self.points_id() == branch.points_id())): return True else: return False @@ -611,7 +731,7 @@ def z_mid_point(self) -> float: self.z_size()[0]) / float(2) def __str__(self): - if self.is_divided() == True: + if self.is_divided(): str = f"number of points = {self.total_points()}\n" str += " FRONT\n" str += f"[ {self._front_up_left.n_points()} | {self._front_up_right.n_points()} ]\n" @@ -640,44 +760,59 @@ def sub_branch(self, index: int): index : int\n index of the branch requested """ - if self.is_divided() == True: + if self.is_divided(): return self.sub_branches[index] else: raise AttributeError("The branch got no subbranches.") - def sub_add_point(self, point: Point3d): - if self.x_size()[0] <= point()[0] < self.x_mid_point(): - if self.y_size()[0] <= point()[1] < self.y_mid_point(): - if self.z_size()[0] <= point()[2] < self.z_mid_point(): - self._front_down_left.add_point(point) - elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: - self._back_down_left.add_point(point) + def sub_add_point( + self, + point: np.array, + points_id_list: np.array = np.array( + []), + new_points: bool = True, + existing_key=0): + id = existing_key + if self.x_size()[0] <= point[0] < self.x_mid_point(): + if self.y_size()[0] <= point[1] < self.y_mid_point(): + if self.z_size()[0] <= point[2] < self.z_mid_point(): + id = self._front_down_left.add_point( + point, points_id_list, new_points, existing_key) + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + id = self._back_down_left.add_point( + point, points_id_list, new_points, existing_key) else: raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") - elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: - if self.z_size()[0] <= point()[2] < self.z_mid_point(): - self._front_up_left.add_point(point) - elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: - self._back_up_left.add_point(point) + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + if self.z_size()[0] <= point[2] < self.z_mid_point(): + id = self._front_up_left.add_point( + point, points_id_list, new_points, existing_key) + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + id = self._back_up_left.add_point( + point, points_id_list, new_points, existing_key) else: raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") - elif self.x_mid_point() <= point()[0] <= self.x_size()[1]: - if self.y_size()[0] <= point()[1] < self.y_mid_point(): - if self.z_size()[0] <= point()[2] < self.z_mid_point(): - self._front_down_right.add_point(point) - elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: - self._back_down_right.add_point(point) + elif self.x_mid_point() <= point[0] <= self.x_size()[1]: + if self.y_size()[0] <= point[1] < self.y_mid_point(): + if self.z_size()[0] <= point[2] < self.z_mid_point(): + id = self._front_down_right.add_point( + point, points_id_list, new_points, existing_key) + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + id = self._back_down_right.add_point( + point, points_id_list, new_points, existing_key) else: raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") - elif self.y_mid_point() <= point()[1] <= self.y_size()[1]: - if self.z_size()[0] <= point()[2] < self.z_mid_point(): - self._front_up_right.add_point(point) - elif self.z_mid_point() <= point()[2] <= self.z_size()[1]: - self._back_up_right.add_point(point) + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + if self.z_size()[0] <= point[2] < self.z_mid_point(): + id = self._front_up_right.add_point( + point, points_id_list, new_points, existing_key) + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + id = self._back_up_right.add_point( + point, points_id_list, new_points, existing_key) else: raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") @@ -686,7 +821,15 @@ def sub_add_point(self, point: Point3d): raise ValueError( f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") - def add_point(self, point: Point3d): + return id + + def add_point( + self, + point: np.array, + points_id_list: np.array = np.array( + []), + new_points: bool = True, + existing_key=0): """Recursively adds a point to the branch.\n Parameters ---------- @@ -702,9 +845,11 @@ def add_point(self, point: Point3d): 4. If it's not ,the branch is then divided and the coordinates of the point are checked to add the point in one of the newly-created subbranches. """ - if self.is_divided() == True: + id = existing_key + if self.is_divided(): - self.sub_add_point(point) + id = self.sub_add_point( + point, points_id_list, new_points, existing_key) else: @@ -784,45 +929,62 @@ def add_point(self, point: Point3d): 6: self._back_up_left, 7: self._back_up_right} - list = self.points_list() + dic = self.points_dic() + items_list = list(dic.values()) + ids_list = list(dic.keys()) for i in range(self.n_points()): - - self.sub_add_point(list[i]) + self.sub_add_point( + items_list[i], + ids_list, + new_points=False, + existing_key=ids_list[i]) self.remove_point(0) - self.sub_add_point(point) + id = self.sub_add_point( + point, points_id_list, new_points, existing_key) else: - self._points = np.append(self._points, point) - self._n_points += 1 + if new_points: + self._n_points += 1 + + id = uuid4().bytes + while np.isin(id, points_id_list): + id = uuid4().bytes + + self._points_dic[id] = point + + else: + self._n_points += 1 + + self._points_dic[id] = point + return id - def update(self): + def update(self, points_id_list: np.array = np.array([])): """Recursively checks if all the points in the branch and its subbranches still belong there. - Returns an array with all the points that had positions out of the branch's bounds. - """ - return_list = np.empty(0) - if self.is_divided() == True: - update_points = np.empty(0) + Returns an array with all the points that had positions out of the branch's bounds.""" + return_list = {} + if self.is_divided(): + update_points = {} for i in range(len(self.sub_branches)): - update_points = np.append( - update_points, self.sub_branch(i).update()) + update_points.update(self.sub_branch(i).update(points_id_list)) - removed = 0 - remain_points = np.copy(update_points) - for i in range(update_points.size): - if ((self.x_size()[0] > update_points[i]()[0]) or - (update_points[i]()[0] > self.x_size()[1]) or - (self.y_size()[0] > update_points[i]()[1]) or - (update_points[i]()[1] > self.y_size()[1]) or - (self.z_size()[0] > update_points[i]()[2]) or - (update_points[i]()[2] > self.z_size()[1])): - return_list = np.append(return_list, update_points[i]) - remain_points = np.delete(remain_points, i - removed) - removed += 1 + remain_points = update_points.copy() + + items_list = list(update_points.values()) + ids_list = list(update_points.keys()) + for i in range(len(update_points)): + if ((self.x_size()[0] > items_list[i][0]) or + (items_list[i][0] > self.x_size()[1]) or + (self.y_size()[0] > items_list[i][1]) or + (items_list[i][1] > self.y_size()[1]) or + (self.z_size()[0] > items_list[i][2]) or + (items_list[i][2] > self.z_size()[1])): + + return_list[ids_list[i]] = items_list[i] + remain_points.pop(ids_list[i]) if self.total_points() <= self._max_points: - remain_points = np.append( - remain_points, self.all_points_list()) + remain_points.update(self.all_points_dic()) self._divided = False self._front_down_left = None self._front_down_right = None @@ -841,27 +1003,113 @@ def update(self): 6: self._back_up_left, 7: self._back_up_right} - for i in range(remain_points.shape[0]): - self.add_point(remain_points[i]) + items_list = list(remain_points.values()) + ids_list = list(remain_points.keys()) - return return_list + for i in range(len(items_list)): + self.add_point( + items_list[i], + points_id_list, + False, + ids_list[i]) else: - list = self.points_list() + + items_list = list(self._points_dic.values()) + ids_list = list(self._points_dic.keys()) removed = 0 - for i in range(list.size): - if ((self.x_size()[0] > list[i]()[0]) or - (list[i]()[0] > self.x_size()[1]) or - (self.y_size()[0] > list[i]()[1]) or - (list[i]()[1] > self.y_size()[1]) or - (self.z_size()[0] > list[i]()[2]) or - (list[i]()[2] > self.z_size()[1]) + for i in range(len(items_list)): + if ((self.x_size()[0] > items_list[i][0]) or + (items_list[i][0] > self.x_size()[1]) or + (self.y_size()[0] > items_list[i][1]) or + (items_list[i][1] > self.y_size()[1]) or + (self.z_size()[0] > items_list[i][2]) or + (items_list[i][2] > self.z_size()[1]) ): self.remove_point(i - removed) removed += 1 - return_list = np.append(return_list, list[i]) + return_list[ids_list[i]] = items_list[i] + + return return_list + + def search(self, point, id): + """Returns a string that encodes the requested point location inside the tree. + The enconding works as it follows:\n + * If the branch is divided, every character of the string is the sub-branch that point is located, + until an 'f' character is reached, that means the point is located inside + the last acessed sub-branch. For example, the point at 031f is located at the second sub-branch, + from the fourth sub-branch, from the first sub-branch. + * If the branch is not divided, it will return 'f' if found, or '' if not found.\n + Parameters + --------- + point : np.array + Coordinates of the point to be found. + id : bytes + Id of the point to be found.""" + + b = '' + if self.is_divided(): + if self.x_size()[0] <= point[0] < self.x_mid_point(): + if self.y_size()[0] <= point[1] < self.y_mid_point(): + if self.z_size()[0] <= point[2] < self.z_mid_point(): + c = self._front_down_left.search(point, id) + if 'f' in list(c): + b += '0' + c + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + c = self._back_down_left.search(point, id) + if 'f' in list(c): + b += '4' + c + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + if self.z_size()[0] <= point[2] < self.z_mid_point(): + c = self._front_up_left.search(point, id) + if 'f' in list(c): + b += '2' + c + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + c = self._back_up_left.search(point, id) + if 'f' in list(c): + b += '6' + c + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + elif self.x_mid_point() <= point[0] <= self.x_size()[1]: + if self.y_size()[0] <= point[1] < self.y_mid_point(): + if self.z_size()[0] <= point[2] < self.z_mid_point(): + c = self._front_down_right.search(point, id) + if 'f' in list(c): + b += '1' + c + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + c = self._back_down_right.search(point, id) + if 'f' in list(c): + b += '5' + c + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + elif self.y_mid_point() <= point[1] <= self.y_size()[1]: + if self.z_size()[0] <= point[2] < self.z_mid_point(): + c = self._front_up_right.search(point, id) + if 'f' in list(c): + b += '3' + c + elif self.z_mid_point() <= point[2] <= self.z_size()[1]: + c = self._back_up_right.search(point, id) + if 'f' in list(c): + b += '7' + c + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + + else: + raise ValueError( + f"The point {point} is outside the tree's bounds : ({self.x_size()}, {self.y_size()}, {self.z_size()}).") + else: + points_dic = self.points_dic() + if ((id in list(points_dic.keys())) and (point in points_dic[id])): + b = 'f' - return return_list + return b class Tree3d(Tree2d): @@ -887,7 +1135,7 @@ def z_size(self): return (self._zmin, self._zmax) def __str__(self): - if self._root.is_divided() == True: + if self._root.is_divided(): points_list = [] for i in range(len(self.root().sub_branches)): points_list.append(self.root().sub_branch(i).total_points()) @@ -989,7 +1237,7 @@ def actor_from_branch_2d(branch: Branch2d, color=( Branch that will have the actor created. """ - if branch.is_divided() == True: + if branch.is_divided(): actors = np.empty(0, dtype=Actor) actors = np.append( actors, @@ -1046,7 +1294,7 @@ def actor_from_branch_3d(branch: Branch3d, color=( Branch that will have the actor created. """ - if branch.is_divided() == True: + if branch.is_divided(): actors = np.empty(0, dtype=Actor) actors = np.append( actors, From ad5c4d9bcc57aeafc22cec00790088e9bd500f94 Mon Sep 17 00:00:00 2001 From: Joao Victor Dell Agli Date: Wed, 7 Jun 2023 17:39:33 -0300 Subject: [PATCH 10/10] Corrected code spell issues --- fury/trees.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fury/trees.py b/fury/trees.py index f89fb4c47..c281028df 100644 --- a/fury/trees.py +++ b/fury/trees.py @@ -438,10 +438,10 @@ def update(self, points_id_list: np.array = np.array([])): def search(self, point, id): """Returns a string that encodes the requested point location inside the tree. - The enconding works as it follows:\n + The encoding works as it follows:\n * If the branch is divided, every character of the string is the sub-branch that point is located, until an 'f' character is reached, that means the point is located inside - the last acessed sub-branch. For example, the point at 031f is located at the second sub-branch, + the last accessed sub-branch. For example, the point at 031f is located at the second sub-branch, from the fourth sub-branch, from the first sub-branch. * If the branch is not divided, it will return 'f' if found, or '' if not found.\n Parameters @@ -1034,10 +1034,10 @@ def update(self, points_id_list: np.array = np.array([])): def search(self, point, id): """Returns a string that encodes the requested point location inside the tree. - The enconding works as it follows:\n + The encoding works as it follows:\n * If the branch is divided, every character of the string is the sub-branch that point is located, until an 'f' character is reached, that means the point is located inside - the last acessed sub-branch. For example, the point at 031f is located at the second sub-branch, + the last accessed sub-branch. For example, the point at 031f is located at the second sub-branch, from the fourth sub-branch, from the first sub-branch. * If the branch is not divided, it will return 'f' if found, or '' if not found.\n Parameters