|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- '''
- Filename nodes.py
- Author: Bryan "ObsidianBlk" Miller
- Date Created: 8/1/2018
- Python Version: 3.7
- '''
-
- from .display import Display
- from .events import Events
- import pygame
-
-
- class NodeError(Exception):
- pass
-
-
- class Node:
- def __init__(self, name="Node", parent=None):
- self._parent = None
- self._name = name
- self._children = []
- if parent is not None:
- try:
- self.parent = parent
- except NodeError as e:
- raise e
-
- @property
- def parent(self):
- return self._parent
-
- @parent.setter
- def parent(self, new_parent):
- try:
- self.parent_to_node(new_parent)
- except NodeError as e:
- raise e
-
- @property
- def root(self):
- if self._parent is None:
- return self
- return self._parent.root
-
- @property
- def name(self):
- return self._name
-
- @name.setter
- def name(self, value):
- if self._parent is not None:
- if self._parent.get_node(value) is not None:
- raise NodeError("Parent already contains node named '{}'.".format(name))
- self._name = value
-
- @property
- def full_name(self):
- if self._parent is None:
- return self._name
- return self._parent.full_name + "." + self._name
-
- @property
- def child_count(self):
- return len(this._children)
-
- def parent_to_node(self, parent, allow_reparenting=False):
- if not isinstance(value, Node):
- raise NodeError("Node may only parent to another Node instance.")
- if self._parent is None or self._parent != parent:
- if self._parent is not None:
- if allow_Reparenting == False:
- raise NodeError("Node already assigned a parent Node.")
- if self._parent.remove_node(self) != self:
- raise NodeError("Failed to remove self from current parent.")
- try:
- parent.attach_node(self)
- except NodeError as e:
- raise e
-
-
- def attach_node(self, node, reparent=False, index=-1):
- if node.parent is not None:
- if node.parent == self:
- return # Nothing to do. Given node already parented to this node.
- if reparent == False:
- raise NodeError("Node already parented.")
- if node.parent.remove_node(node) != node:
- raise NodeError("Failed to remove given node from it's current parent.")
- if self.get_node(node.name) is not None:
- raise NodeError("Node with name '{}' already attached.".format(node.name))
- node._parent = self
- if index < 0 or index >= len(self._children):
- self._children.append(node)
- else:
- self._children.insert(index, node)
-
- def remove_node(self, node):
- if isinstance(node, (str, unicode)):
- n = self.get_node(node)
- if n is not None:
- try:
- return self.remove_node(n)
- except NodeError as e:
- raise e
- elif isinstance(node, Node):
- if node.parent != self:
- if node.parent == None:
- raise NodeError("Cannot remove an unparented node.")
- try:
- return node.parent.remove_node(node)
- except NodeError as e:
- raise e
- if node in self._children:
- self._children.remove(node)
- node._parent = None
- return node
- else:
- raise NodeError("Expected a Node instance or a string.")
- return None
-
-
- def get_node(self, name):
- if len(self._children) <= 0:
- return None
-
- subnames = name.split(".")
- for c in self._children:
- if c.name == subnames[0]:
- if len(subnames) > 1:
- return c.get_node(".".join(subnames[1:-1]))
- return c
- return None
-
-
- def _update(self, dt):
- if hasattr(self, "on_update"):
- self.on_update(dt)
-
- for c in self._children:
- c._update(dt)
-
- def _render(self, surface):
- for c in self._children:
- c._render(surface)
-
-
- class Node2D(Node):
- def __init__(self, name="Node2D", parent=None):
- try:
- Node.__init__(self, name, parent)
- except NodeError as e:
- raise e
-
- def _render(self, surface):
- Node._render(self, surface)
-
- if hasattr(self, "on_render"):
- self._ACTIVE_SURF = surface
- self.on_render()
- del self._ACTIVE_SURF
-
- def blit(self, img, pos=(0,0), rect=None):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- self._ACTIVE_SURF.blit(img, pos, rect)
-
- def fill(self, color):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- self._ACTIVE_SURF.fill(color)
-
- def draw_lines(self, points, color, thickness=1, closed=False):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- pygame.draw.lines(self._ACTIVE_SURF, color, closed, points, thickness)
-
- def draw_rect(self, rect, color, thickness=1):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- pygame.draw.rect(self._ACTIVE_SURF, color, rect, thickness)
-
- def draw_ellipse(self, rect, color, thickness=1, fill_color=None):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- if fill_color is not None:
- pygame.draw.ellipse(self._ACTIVE_SURF, fill_color, rect)
- if thickness > 0:
- pygame.draw.ellipse(self._ACTIVE_SURF, color, rect, thickness)
-
- def draw_circle(self, pos, radius, color, thickness=1, fill_color=None):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- if fill_color is not None:
- pygame.draw.circle(self._ACTIVE_SURF, fill_color, pos, radius)
- if thickness > 0:
- pygame.draw.circle(self._ACTIVE_SURF, color, pos, radius, thickness)
-
- def draw_polygon(self, points, color, thickness=1, fill_color=None):
- if not hasattr(self, "_ACTIVE_SURF"):
- return
- if fill_color is not None:
- pygame.draw.polygon(self._ACTIVE_SURF, fill_color, points)
- if thickness >= 1:
- pygame.draw.polygon(self._ACTIVE_SURF, color, points, thickness)
-
-
-
-
- class NodeSurface(Node2D):
- def __init__(self, name="NodeSurface", parent=None):
- try:
- Node2D.__init__(self, name, parent)
- except NodeError as e:
- raise e
- self._offset = (0.0, 0.0)
- self._scale = (1.0, 1.0)
- self._scaleToDisplay = False
- self._scaleDirty = False
- self._keepAspectRatio = False
- self._alignCenter = False
- self._surface = None
- self._tsurface = None
- self.set_surface()
-
- def _updateTransformSurface(self):
- if self._surface is None:
- return
-
- self._scaleDirty = False
- if self._scaleToDisplay:
- dsize = Display.resolution
- ssize = self._surface.get_size()
- self._scale = (dsize[0] / ssize[0], dsize[1] / ssize[1])
- if self._keepAspectRatio:
- if self._scale[0] < self._scale[1]:
- self._scale = (self._scale[0], self._scale[0])
- else:
- self._scale = (self._scale[1], self._scale[1])
-
- if self._scale[0] == 1.0 and self._scale[1] == 1.0:
- self._tsurface = None
- return
- size = self._surface.get_size()
- nw = size[0] * self._scale[0]
- nh = 0
- if self._keepAspectRatio:
- nh = size[1] * self._scale[0]
- else:
- nh = size[1] * self._scale[1]
- self._tsurface = pygame.Surface((nw, nh), pygame.SRCALPHA, self._surface)
- self._tsurface.fill(pygame.Color(0,0,0,0))
-
- @property
- def resolution(self):
- if self._surface is None:
- return (0,0)
- return self._surface.get_size()
- @resolution.setter
- def resolution(self, res):
- try:
- self.set_surface(res)
- except (TypeError, ValueError) as e:
- raise e
-
- @property
- def width(self):
- return self.resolution[0]
-
- @property
- def height(self):
- return self.resolution[1]
-
- @property
- def offset(self):
- return self._offset
- @offset.setter
- def offset(self, offset):
- if not isinstance(offset, tuple):
- raise TypeError("Expected a tuple")
- if len(offset) != 2:
- raise ValueError("Expected tuple of length two.")
- if not isinstance(offset[0], (int, float)) or not isinstance(offset[1], (int, float)):
- raise TypeError("Expected number values.")
- self._offset = (float(offset[0]), float(offset[1]))
-
- @property
- def offset_x(self):
- return self._offset[0]
- @offset_x.setter
- def offset_x(self, x):
- if not isinstance(x, (int, float)):
- raise TypeError("Expected number value.")
- self._offset = (x, self._offset[1])
-
- @property
- def offset_y(self):
- return self._offset[1]
- @offset_y.setter
- def offset_y(self, y):
- if not isinstance(y, (int, float)):
- raise TypeError("Expected number value.")
- self._offset = (self._offset[0], y)
-
- @property
- def scale(self):
- return self._scale
- @scale.setter
- def scale(self, scale):
- if self._keepAspectRatio:
- if not isinstance(scale, (int, float)):
- raise TypeError("Expected number value.")
- self._scale = (scale, self._scale[1])
- else:
- if not isinstance(scale, tuple):
- raise TypeError("Expected a tuple")
- if len(scale) != 2:
- raise ValueError("Expected tuple of length two.")
- if not isinstance(scale[0], (int, float)) or not isinstance(scale[1], (int, float)):
- raise TypeError("Expected number values.")
- self._scale = scale
- self._updateTransformSurface()
-
- @property
- def keep_aspect_ratio(self):
- return self._keepAspectRatio
- @keep_aspect_ratio.setter
- def keep_aspect_ratio(self, keep):
- self._keepAspectRatio = (keep == True)
- self._updateTransformSurface()
-
- @property
- def align_center(self):
- return self._alignCenter
- @align_center.setter
- def align_center(self, center):
- self._alignCenter = (center == True)
-
- @property
- def scale_to_display(self):
- return self._scaleToDisplay
- @scale_to_display.setter
- def scale_to_display(self, todisplay):
- if todisplay == True:
- self._scaleToDisplay = True
- Events.listen("VIDEORESIZE", self._OnVideoResize)
- else:
- self._scaleToDisplay = False
- Events.unlisten("VIDEORESIZE", self._OnVideoResize)
- self._updateTransformSurface()
-
- def scale_to(self, target_resolution):
- if self._surface is not None:
- size = self._surface.get_size()
- nscale = (float(size[0]) / float(target_resolution[0]), float(size[1]) / float(target_resolution[1]))
- self.scale = nscale
-
-
- def set_surface(self, resolution=None):
- dsurf = Display.surface
- if resolution is None:
- if dsurf is not None:
- self._surface = dsurf.convert_alpha()
- self._surface.fill(pygame.Color(0,0,0,0))
- self._updateTransformSurface()
- else:
- if not isinstance(resolution, tuple):
- raise TypeError("Expected a tuple.")
- if len(resolution) != 2:
- raise ValueError("Expected a tuple of length two.")
- if not isinstance(resolution[0], int) or not isinstance(resolution[1], int):
- raise TypeError("Tuple expected to contain integers.")
- if dsurf is not None:
- self._surface = pygame.Surface(resolution, pygame.SRCALPHA, dsurf)
- else:
- self._surface = pygame.Surface(resolution, pygame.SRCALPHA)
- self._surface.fill(pygame.Color(0,0,0,0))
- self._updateTransformSurface()
-
- def _render(self, surface):
- if self._surface is None:
- self.set_surface()
- if self._surface is not None:
- if self._scaleDirty:
- self._updateTransformSurface()
- Node2D._render(self, self._surface)
- else:
- Node2D._render(self, surface)
- self._scale_and_blit(surface)
-
-
- def _scale_and_blit(self, dest):
- dsize = dest.get_size()
-
- src = self._surface
- if self._tsurface is not None:
- pygame.transform.scale(self._surface, self._tsurface.get_size(), self._tsurface)
- src = self._tsurface
-
- ssize = src.get_size()
- posx = self._offset[0]
- posy = self._offset[1]
- if self._alignCenter:
- if dsize[0] > ssize[0]:
- posx += (dsize[0] - ssize[0]) * 0.5
- if dsize[1] > ssize[1]:
- posy += (dsize[1] - ssize[1]) * 0.5
- pos = (int(posx), int(posy))
- dest.blit(src, pos)
-
- def _OnVideoResize(self, event, data):
- if self._scaleToDisplay:
- self._scaleDirty = True
-
-
-
-
|