Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import vtk | |
| from custom_types import * | |
| from ui import ui_utils | |
| import constants | |
| import vtk.util.numpy_support as numpy_support | |
| class GaussianData: | |
| def make_symmetric(self, other: GaussianData): | |
| # reflection = np.eye(3) | |
| # reflection[0, 0] = -1 | |
| # self.total_translate = np.einsum('ad,d->a', reflection, other.total_translate) | |
| self.total_translate = other.total_translate.copy() | |
| self.total_translate[0] *= -1 | |
| self.total_rotate = other.total_rotate.copy() | |
| # self.total_rotate = np.einsum('ab,bc->ac', reflection, other.total_rotate) | |
| def to_positive(p): | |
| up_dir = 1 | |
| eye = np.eye(3) | |
| all_dots = (p[:, :] * eye[up_dir, None, :]).sum(-1) | |
| up_axis = all_dots.__abs__().argmax() | |
| return up_axis, all_dots[up_axis] < 0 | |
| def permute_p(self, p): | |
| up_dir = 1 | |
| p_new = np.eye(3) | |
| p_new[up_dir] = p[self.up_axis] | |
| if self.reflect_up: | |
| p_new[up_dir] = -p_new[up_dir] | |
| p_new[(up_dir + 1) % 3] = p[(self.up_axis + 1) % 3] | |
| p_new[(up_dir + 2) % 3, :] = np.cross(p_new[up_dir, :], p_new[(up_dir + 1) % 3, :]) | |
| return p_new | |
| def rotate(self, transition: ui_utils.Transition): | |
| mu = self.mu_baked - transition.transition_origin | |
| mu = np.einsum('ab,b->a', transition.rotation, mu) + transition.transition_origin | |
| self.total_translate = mu - self.mu | |
| self.total_rotate = np.einsum('ab,bc->ac', transition.rotation, self.total_rotate) | |
| def stretch(self, amount): | |
| scale = 0.9 if amount < 0 else 1 / .9 | |
| self.eigen = self.eigen * scale | |
| def translate(self, transition: ui_utils.Transition): | |
| self.total_translate = self.total_translate + transition.translation | |
| def get_view_eigen(self): | |
| scale = (self.mu_baked ** 2).sum() / (self.mu ** 2).sum() | |
| return self.eigen * scale | |
| def get_raw_data(self): | |
| # p = np.einsum('da,db->ba', self.p, self.total_rotate) | |
| p = np.einsum('ab,bc->ac', self.total_rotate, self.p.transpose()).transpose() | |
| return self.phi, self.mu_baked, self.eigen, p | |
| def copy_data(self): | |
| return [item.copy() if type(item) is ARRAY else item for item in self.get_raw_data()] | |
| def get_view_data(self): | |
| phi, mu, eigen, p = self.get_raw_data() | |
| p = self.permute_p(p) | |
| return phi, mu, eigen, p | |
| def mu_baked(self) -> ARRAY: | |
| return self.mu + self.total_translate | |
| def phi(self) -> float: | |
| return self.data[0] | |
| def mu(self) -> ARRAY: | |
| return self.data[1] | |
| def eigen(self) -> ARRAY: | |
| return self.data[2] | |
| def p(self) -> ARRAY: | |
| return self.data[3] | |
| def mu(self, new_mu: ARRAY): | |
| self.data[1] = new_mu | |
| def p(self, new_p: ARRAY): | |
| self.data[3] = new_p | |
| def eigen(self, new_eigen: ARRAY): | |
| self.data[2] = new_eigen | |
| def reset(self): | |
| self.total_translate = np.zeros(3) | |
| self.total_rotate = np.eye(3) | |
| def __getitem__(self, item): | |
| return self.data[item] | |
| def __init__(self, gaussian): | |
| self.recover_data = [item.copy() if type(item) is ARRAY else item for item in gaussian] | |
| self.data = list(gaussian) | |
| self.up_axis, self.reflect_up = self.to_positive(self.p) | |
| self.total_translate = np.zeros(3) | |
| self.total_rotate = np.eye(3) | |
| class GaussianStatus(GaussianData): | |
| # copy_constructor | |
| def copy(self: GaussianStatus, render: vtk.vtkRenderer, view_style: ui_utils.ViewStyle, | |
| gaussian_id: Optional[Tuple[int, int]] = None, is_selected: Optional[bool] = None) -> GaussianStatus: | |
| if self.disabled: | |
| return self | |
| gaussian_id = self.gaussian_id if gaussian_id is None else gaussian_id | |
| return GaussianStatus(self.copy_data(), gaussian_id, is_selected or self.is_selected, view_style, render, 1) | |
| def get_new_gaussian() -> vtk.vtkSphereSource: | |
| return ui_utils.load_vtk_obj(f"{constants.DATA_ROOT}/ui_resources/simple_brick.obj") | |
| def update_gaussian_transform(self, source): | |
| phi, mu, eigen, p = self.get_view_data() | |
| # def replace_mesh(self, mesh: T_Mesh): | |
| # vs, faces = mesh | |
| # vs, faces = vs.detach().cpu(), faces.detach().cpu() | |
| # # vs, faces = mesh_utils.scale_from_ref(mesh, *self.scale) | |
| # source = vtk.vtkPolyData() | |
| # new_vs_vtk = numpy_support.numpy_to_vtk(vs.numpy()) | |
| # cells_npy = np.column_stack( | |
| # [np.full(faces.shape[0], 3, dtype=np.int64), faces.numpy().astype(np.int64)]).ravel() | |
| # vs_vtk, faces_vtk = vtk.vtkPoints(), vtk.vtkCellArray() | |
| # vs_vtk.SetData(new_vs_vtk) | |
| # faces_vtk.SetCells(faces.shape[0], numpy_support.numpy_to_vtkIdTypeArray(cells_npy)) | |
| # | |
| # source.SetPolys(faces_vtk) | |
| # self.mapper.SetInputData(source) | |
| # self.is_changed = True | |
| # if not self.to_init: | |
| # self.to_init = True | |
| # self.render.AddActor(self.actor) | |
| transform = vtk.vtkTransform() | |
| mat = vtk.vtkMatrix4x4() | |
| p = p * .005 | |
| # p = p * eigen[:, None] | |
| for i in range(4): | |
| for j in range(4): | |
| if i > 2: | |
| mat.SetElement(i, j, 0) | |
| elif j > 2: | |
| mat.SetElement(i, j, float(mu[i])) | |
| # mat.SetElement(i, j, 0) | |
| else: | |
| mat.SetElement(i, j, p[j, i]) | |
| # mat_t[i, j] = mat.GetElement(i,j) | |
| mat.SetElement(3, 3, 1) | |
| transform.SetMatrix(mat) | |
| transformFilter = vtk.vtkTransformPolyDataFilter() | |
| transformFilter.SetInputData(source) | |
| transformFilter.SetTransform(transform) | |
| transformFilter.Update() | |
| return transformFilter | |
| def update_gaussian(self): | |
| if self.disabled: | |
| return | |
| # source = self.mapper.GetInput() | |
| # source.SetPoints(self.init_points) | |
| # source = self.update_gaussian_transform(source) | |
| # self.mapper.SetInputConnection(source.GetOutputPort()) | |
| def end_transition(self, transition: ui_utils.Transition) -> bool: | |
| if self.init_points is None: | |
| return False | |
| if transition.transition_type is ui_utils.EditType.Translating: | |
| self.translate(transition) | |
| return True | |
| elif transition.transition_type is ui_utils.EditType.Rotating: | |
| self.rotate(transition) | |
| return True | |
| elif transition.transition_type is ui_utils.EditType.Scaling: | |
| self.rotate(transition) | |
| return True | |
| return False | |
| def temporary_transition(self, transition: ui_utils.Transition) -> bool: | |
| if self.init_points is None: | |
| return False | |
| source = self.mapper.GetInput() | |
| vs = self.init_points | |
| if transition.transition_type is ui_utils.EditType.Translating: | |
| vs = vs + transition.translation[None, :] | |
| elif transition.transition_type is ui_utils.EditType.Rotating: | |
| vs = vs - transition.transition_origin[None, :] | |
| vs = np.einsum('ad,nd->na', transition.rotation, vs) | |
| vs = vs + transition.transition_origin[None, :] | |
| source.GetPoints().SetData(numpy_support.numpy_to_vtk(vs)) | |
| return True | |
| def get_address(self): | |
| if self.disabled: | |
| return f"disabled" | |
| return self.actor.GetAddressAsString('') | |
| def add_gaussian(render, actor: Optional[vtk.vtkActor]) -> vtk.vtkActor: | |
| if actor is None: | |
| actor = vtk.vtkActor() | |
| mapper = vtk.vtkPolyDataMapper() | |
| actor.GetProperty().SetOpacity(0.3) | |
| actor.SetMapper(mapper) | |
| # source = self.get_new_gaussian() | |
| # init_points = source.GetPoints() | |
| # source = self.update_gaussian_transform(source) | |
| # actor, _ = ui_utils.wrap_mesh(source.GetOutput(), color) | |
| render.AddActor(actor) | |
| ui_utils.set_default_properties(actor, (1., 1., .1)) | |
| return actor | |
| def replace_part(self, part_mesh: Optional[vtk.vtkPolyData]): | |
| if part_mesh is not None and not self.disabled: | |
| self.init_points = numpy_support.vtk_to_numpy(part_mesh.GetPoints().GetData()) | |
| points = vtk.vtkPoints() | |
| points.SetData(numpy_support.numpy_to_vtk(self.init_points)) | |
| part_mesh.SetPoints(points) | |
| self.mapper.SetInputData(part_mesh) | |
| def set_color(self): | |
| if self.disabled: | |
| return | |
| properties = self.actor.GetProperty() | |
| properties.SetOpacity(self.opacity) | |
| properties.SetColor(*self.color) | |
| def turn_off(self): | |
| if self.disabled: | |
| return | |
| self.actor.GetProperty().SetOpacity(0) | |
| self.actor.PickableOff() | |
| def turn_on(self): | |
| if self.disabled: | |
| return | |
| self.actor.GetProperty().SetOpacity(self.opacity) | |
| self.actor.PickableOn() | |
| def is_not_selected(self): | |
| return not self.is_selected | |
| def disabled(self): | |
| return self.mapper is None #or self.mapper.GetInput() is None | |
| def make_symmetric(self, force_include: bool): | |
| if self.disabled or self.twin is None or (not force_include and self.included != self.twin.included): | |
| return | |
| super(GaussianStatus, self).make_symmetric(self.twin) | |
| if force_include: | |
| self.included = self.twin.included | |
| self.update_gaussian() | |
| def apply_affine(self, button: ui_utils.Buttons, key: str): | |
| axis = {"left": 0, "right": 0, "up": 2, "down": 2, "a": 1, "z": 1}[key] | |
| sign = {"left": 1, "right": -1, "up": 1, "down": -1, "a": 1, "z": -1}[key] | |
| if self.disabled or button not in (ui_utils.Buttons.translate, ui_utils.Buttons.stretch, ui_utils.Buttons.rotate): | |
| return | |
| elif button is ui_utils.Buttons.translate: | |
| vec = np.zeros(3) | |
| vec[axis] = .01 * sign | |
| self.translate(vec) | |
| elif button is ui_utils.Buttons.rotate: | |
| self.rotate(sign * .1, axis) | |
| else: | |
| self.stretch(.01 * sign) | |
| self.update_gaussian() | |
| def opacity(self) -> float: | |
| if self.is_selected: | |
| if self.included: | |
| opacity = self.view_style.opacity + 0.4 | |
| else: | |
| opacity = self.view_style.opacity | |
| else: | |
| if self.included: | |
| opacity = self.view_style.opacity + 0.2 | |
| else: | |
| opacity = self.view_style.opacity | |
| return max(0., min(1., opacity)) | |
| def color(self) -> Tuple[float, float, float]: | |
| if self.is_selected: | |
| return self.view_style.selected_color | |
| if self.included: | |
| return self.view_style.included_color | |
| else: | |
| return self.view_style.base_color | |
| def toggle_inclusion(self, included: Optional[bool] = None): | |
| if self.disabled: | |
| return | |
| if included is None: | |
| included = not self.included | |
| if included != self.included: | |
| self.included = not self.included | |
| self.set_color() | |
| def toggle_selection(self): | |
| if self.disabled: | |
| return | |
| self.is_selected = not self.is_selected | |
| self.set_color() | |
| def reset(self): | |
| if self.disabled: | |
| return | |
| super(GaussianStatus, self).reset() | |
| self.included = False | |
| self.is_selected = False | |
| self.set_color() | |
| # self.update_gaussian() | |
| def delete(self, render): | |
| if not self.disabled: | |
| render.RemoveActor(self.actor) | |
| if self.twin is not None: | |
| self.twin.twin = None | |
| def parent_id(self): | |
| return self.gaussian_id[0] | |
| def child_id(self): | |
| return self.gaussian_id[1] | |
| def mapper(self): | |
| if self.actor is None: | |
| return None | |
| return self.actor.GetMapper() | |
| def __init__(self, gaussian, gaussian_id: Tuple[int, int], is_selected: bool, view_style: ui_utils.ViewStyle, | |
| render: vtk.vtkRenderer, normalized_phi: float, actor: Optional[vtk.vtkActor] = None): | |
| self.view_style = view_style | |
| super(GaussianStatus, self).__init__(gaussian) | |
| self.gaussian_id = gaussian_id | |
| self.twin: Optional[GaussianStatus] = None | |
| self.init_points = None | |
| if normalized_phi > 0.001 or actor is not None: | |
| self.actor = self.add_gaussian(render, actor) | |
| self.is_selected = is_selected | |
| self.included = True | |
| self.set_color() | |
| else: | |
| self.actor = None | |
| self.is_selected = False | |
| self.included = False | |