text: import numpy as np
  from math import *
  from Data.Objects import NamedObservableObject, IdObject
  from Data.Vertex import Vertex
  
  
  class Plane(NamedObservableObject, IdObject):
  	def __init__(self, x_axis=Vertex(1, 0, 0), y_axis=Vertex(0, 1, 0), position=Vertex(0, 0, 0), name="New plane"):
  		NamedObservableObject.__init__(self, name)
  		IdObject.__init__(self)
  		self._position = position
  		self._x_axis = x_axis
  		self._y_axis = y_axis
  		self._pm = None
  
  	@property
  	def normal(self):
  		cp = np.cross(self._x_axis.xyz, self._y_axis.xyz)
  		n = cp / np.linalg.norm(cp)
  		return Vertex(n[0], n[1], n[2])
  
  	def get_local_projection_matrix(self):
  		if self._pm is None:
  			v1 = self._x_axis.xyz / np.linalg.norm(self._x_axis.xyz)
  			v2 = self._y_axis.xyz / np.linalg.norm(self._y_axis.xyz)
  			v3 = self.normal.xyz
  			pm = np.array([v1, v2, v3])
  			self._pm = pm
  		else:
  			pm = self._pm
  		return pm
  
  	def get_global_projection_matrix(self):
  		return self.get_local_projection_matrix().transpose()
  
  	def get_global_vertex(self, vertex: Vertex):
  		pm = self.get_global_projection_matrix()
  		c = self._position.xyz + pm.dot(vertex.xyz)
  		return Vertex(c[0], c[1], c[2])
  
  	def get_global_xyz_array(self, xyz):
  		pm = self.get_global_projection_matrix()
  		c = self._position.xyz + pm.dot(np.array(xyz))
  		return c
  
  	def get_global_xyz(self, x, y, z):
  		pm = self.get_global_projection_matrix()
  		c = self._position.xyz + pm.dot(np.array([x, y, z]))
  		return c
  
  	def get_z_rotated_plane(self, rz):
  		new_x = self.get_global_xyz(cos(rz), sin(rz), 0)
  		new_x = new_x / np.linalg.norm(new_x)
  		cp = np.cross(new_x, -self.normal.xyz)
  		new_y = cp / np.linalg.norm(cp)
  		new_plane = Plane(Vertex.from_xyz(new_x), Vertex.from_xyz(new_y), self._position)
  		return new_plane
  
  	def get_local_vertex(self, global_vertex):
  		pm = self.get_local_projection_matrix()
  		c = pm.dot(global_vertex.xyz - self._position.xyz)
  		return Vertex(c[0], c[1], c[2])
  
  	def get_local_xyz(self, x, y, z):
  		pm = self.get_local_projection_matrix()
  		c = pm.dot(np.array([x, y, z]) - self._position.xyz)
  		return c
  
  	def get_local_xyz_array(self, xyz):
  		pm = self.get_local_projection_matrix()
  		c = pm.dot(xyz - self._position.xyz)
  		return c
  
  	def distance_vertex(self, global_vertex):
  		return abs(self.get_local_vertex(global_vertex).z)
  
  	def distance_xyz(self, x, y, z):
  		return abs(self.get_local_xyz(x, y, z)[2])
  
  	def distance_xyz_array(self, xyz):
  		return abs(self.get_local_xyz(xyz)[2])
  
  	def serialize_json(self):
  		return {
  			'uid': IdObject.serialize_json(self),
  			'no': NamedObservableObject.serialize_json(self),
  			'x_axis': self._x_axis,
  			'y_axis': self._y_axis,
  			'position': self._position
  		}
  
  	@staticmethod
  	def deserialize(data):
  		plane = Plane()
  		if data is not None:
  			plane.deserialize_data(data)
  		return plane
  
  	def deserialize_data(self, data):
  		IdObject.deserialize_data(self, data['uid'])
  		NamedObservableObject.deserialize_data(self, data.get('no', None))
  		self._x_axis = Vertex.deserialize(data['x_axis'])
  		self._y_axis = Vertex.deserialize(data['y_axis'])
  		self._position = Vertex.deserialize(data['position'])
  