# KicadModTree is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# KicadModTree is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kicad-footprint-generator. If not, see < http://www.gnu.org/licenses/ >.
#
# (C) 2016 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
from copy import copy, deepcopy
from KicadModTree.Vector import *
[docs]class MultipleParentsError(RuntimeError):
def __init__(self, message):
# Call the base class constructor with the parameters it needs
super(MultipleParentsError, self).__init__(message)
[docs]class RecursionDetectedError(RuntimeError):
def __init__(self, message):
# Call the base class constructor with the parameters it needs
super(RecursionDetectedError, self).__init__(message)
[docs]class Node(object):
def __init__(self):
self._parent = None
self._childs = []
[docs] def append(self, node):
'''
add node to child
'''
if not isinstance(node, Node):
raise TypeError('invalid object, has to be based on Node')
if node._parent:
raise MultipleParentsError('muliple parents are not allowed!')
self._childs.append(node)
node._parent = self
[docs] def extend(self, nodes):
'''
add list of nodes to child
'''
new_nodes = []
for node in nodes:
if not isinstance(node, Node):
raise TypeError('invalid object, has to be based on Node')
if node._parent or node in new_nodes:
raise MultipleParentsError('muliple parents are not allowed!')
new_nodes.append(node)
# when all went smooth by now, we can set the parent nodes to ourself
for node in new_nodes:
node._parent = self
self._childs.extend(new_nodes)
[docs] def remove(self, node):
'''
remove child from node
'''
if not isinstance(node, Node):
raise TypeError('invalid object, has to be based on Node')
while node in self._childs:
self._childs.remove(node)
node._parent = None
[docs] def insert(self, node):
'''
moving all childs into the node, and using the node as new parent of those childs
'''
if not isinstance(node, Node):
raise TypeError('invalid object, has to be based on Node')
for child in copy(self._childs):
self.remove(child)
node.append(child)
self.append(node)
[docs] def copy(self):
copy = deepcopy(self)
copy._parent = None
return copy
[docs] def serialize(self):
nodes = [self]
for child in self.getAllChilds():
nodes += child.serialize()
return nodes
[docs] def getNormalChilds(self):
'''
Get all normal childs of this node
'''
return self._childs
[docs] def getVirtualChilds(self):
'''
Get virtual childs of this node
'''
return []
[docs] def getAllChilds(self):
'''
Get virtual and normal childs of this node
'''
return self.getNormalChilds() + self.getVirtualChilds()
[docs] def getParent(self):
'''
get Parent Node of this Node
'''
return self._parent
[docs] def getRootNode(self):
'''
get Root Node of this Node
'''
# TODO: recursion detection
if not self.getParent():
return self
return self.getParent().getRootNode()
[docs] def getRealPosition(self, coordinate, rotation=None):
'''
return position of point after applying all transformation and rotation operations
'''
if not self._parent:
if rotation is None:
# TODO: most of the points are 2D Nodes
return Vector3D(coordinate)
else:
return Vector3D(coordinate), rotation
return self._parent.getRealPosition(coordinate, rotation)
[docs] def calculateBoundingBox(self, outline=None):
min_x, min_y = 0, 0
max_x, max_y = 0, 0
if outline:
min_x = outline['min']['x']
min_y = outline['min']['y']
max_x = outline['max']['x']
max_y = outline['max']['y']
for child in self.getAllChilds():
child_outline = child.calculateBoundingBox()
min_x = min([min_x, child_outline['min']['x']])
min_y = min([min_y, child_outline['min']['y']])
max_x = max([max_x, child_outline['max']['x']])
max_y = max([max_y, child_outline['max']['y']])
return {'min': Vector2D(min_x, min_y), 'max': Vector2D(max_x, max_y)}
def _getRenderTreeText(self):
'''
Text which is displayed when generating a render tree
'''
return type(self).__name__
def _getRenderTreeSymbol(self):
'''
Symbol which is displayed when generating a render tree
'''
if self._parent is None:
return "+"
return "*"
[docs] def getRenderTree(self, rendered_nodes=None):
'''
print render tree
'''
if rendered_nodes is None:
rendered_nodes = set()
if self in rendered_nodes:
raise RecursionDetectedError('recursive definition of render tree!')
rendered_nodes.add(self)
tree_str = "{0} {1}".format(self._getRenderTreeSymbol(), self._getRenderTreeText())
for child in self.getNormalChilds():
tree_str += '\n '
tree_str += ' '.join(child.getRenderTree(rendered_nodes).splitlines(True))
return tree_str
[docs] def getCompleteRenderTree(self, rendered_nodes=None):
'''
print virtual render tree
'''
if rendered_nodes is None:
rendered_nodes = set()
if self in rendered_nodes:
raise RecursionDetectedError('recursive definition of render tree!')
rendered_nodes.add(self)
tree_str = "{0} {1}".format(self._getRenderTreeSymbol(), self._getRenderTreeText())
for child in self.getAllChilds():
tree_str += '\n '
tree_str += ' '.join(child.getCompleteRenderTree(rendered_nodes).splitlines(True))
return tree_str