def shortestPathXY(start, end, neighbor_map, vh_set, point_map, grid_type, scale_factor, radius): # TODO[NF]: Docstring x_y_path = [] coordinate_path = ShortestPathHelper.shortestPathAStar( start=start, end=end, neighbor_map=neighbor_map, vh_set=vh_set, point_map=point_map) for node in coordinate_path: row = -node[0] column = node[1] if grid_type is GridType.HONEYCOMB: parity = 0 if HoneycombDnaPart.isOddParity( row=row, column=column) else 1 node_pos = HoneycombDnaPart.latticeCoordToPositionXY( radius=radius, row=row, column=column, scale_factor=scale_factor) else: parity = None node_pos = SquareDnaPart.latticeCoordToPositionXY( radius=radius, row=row, column=column, scale_factor=scale_factor) x_y_path.append((node_pos[0], node_pos[1], parity)) return x_y_path
def shortestPathXY(start, end, vh_set, grid_type, scale_factor, part_radius): # TODO[NF]: Docstring assert part_radius == DEFAULT_RADIUS x_y_path = [] coordinate_path = ShortestPathHelper.shortestPathAStar( start=start, end=end, vh_set=vh_set, part_radius=part_radius, grid_type=grid_type, scale_factor=scale_factor) for node in coordinate_path: row = -node[0] column = node[1] if grid_type is GridEnum.HONEYCOMB: parity = 0 if HoneycombDnaPart.isEvenParity( row=row, column=column) else 1 node_pos = HoneycombDnaPart.latticeCoordToModelXY( radius=part_radius, row=row, column=column) else: parity = None node_pos = SquareDnaPart.latticeCoordToModelXY( radius=part_radius, row=row, column=column) x_y_path.append((node_pos[0], node_pos[1], parity)) return x_y_path
def determineLatticeType(part_dict: dict) -> EnumType: """Guess the lattice type based on the sum of the vector distances between VHs and the closest lattice position. Args: part_dict: the ``dict`` corresponding to the part to be imported Returns: ``GridEnum.HONEYCOMB`` or ``GridEnum.SQUARE`` or ``GridEnum.NONE`` """ grid_type = part_dict.get('grid_type') if grid_type is not None: return grid_type vh_id_list = part_dict.get('vh_list') origins = part_dict.get('origins') square_delta_x = 0. square_delta_y = 0. honeycomb_delta_x = 0. honeycomb_delta_y = 0. for vh_id, _ in vh_id_list: vh_x, vh_y = origins[vh_id] hcd, honeycomb_guess_coordinates = HoneycombDnaPart.distanceFromClosestLatticeCoord( DEFAULT_RADIUS, vh_x, vh_y) sqd, square_guess_coordinates = SquareDnaPart.distanceFromClosestLatticeCoord( DEFAULT_RADIUS, vh_x, vh_y) honeycomb_guess_x, honeycomb_guess_y = HoneycombDnaPart.latticeCoordToQtXY( DEFAULT_RADIUS, honeycomb_guess_coordinates[0], honeycomb_guess_coordinates[1]) square_guess_x, square_guess_y = SquareDnaPart.latticeCoordToQtXY( DEFAULT_RADIUS, square_guess_coordinates[0], square_guess_coordinates[1]) honeycomb_delta_x += (vh_x - honeycomb_guess_x) honeycomb_delta_y += (vh_y - honeycomb_guess_y) square_delta_x += (vh_x - square_guess_x) square_delta_y += (vh_y - square_guess_y) sum_honeycomb_distance = (honeycomb_delta_x**2 + honeycomb_delta_y**2)**0.5 sum_square_distance = (square_delta_x**2 + square_delta_y**2)**0.5 if abs(sum_honeycomb_distance) < abs(sum_square_distance): return GridEnum.HONEYCOMB else: return GridEnum.SQUARE
def determineLatticeType(part_dict: dict) -> EnumType: """Guess the lattice type based on the sum of the vector distances between VHs and the closest lattice position. Args: part_dict: the ``dict`` corresponding to the part to be imported Returns: ``GridEnum.HONEYCOMB`` or ``GridEnum.SQUARE`` or ``GridEnum.NONE`` """ grid_type = part_dict.get('grid_type') if grid_type is not None: return grid_type vh_id_list = part_dict.get('vh_list') origins = part_dict.get('origins') square_delta_x = 0. square_delta_y = 0. honeycomb_delta_x = 0. honeycomb_delta_y = 0. for vh_id, _ in vh_id_list: vh_x, vh_y = origins[vh_id] hcd, honeycomb_guess_coordinates = HoneycombDnaPart.distanceFromClosestLatticeCoord(DEFAULT_RADIUS, vh_x, vh_y) sqd, square_guess_coordinates = SquareDnaPart.distanceFromClosestLatticeCoord(DEFAULT_RADIUS, vh_x, vh_y) honeycomb_guess_x, honeycomb_guess_y = HoneycombDnaPart.latticeCoordToQtXY(DEFAULT_RADIUS, honeycomb_guess_coordinates[0], honeycomb_guess_coordinates[1]) square_guess_x, square_guess_y = SquareDnaPart.latticeCoordToQtXY(DEFAULT_RADIUS, square_guess_coordinates[0], square_guess_coordinates[1]) honeycomb_delta_x += (vh_x - honeycomb_guess_x) honeycomb_delta_y += (vh_y - honeycomb_guess_y) square_delta_x += (vh_x - square_guess_x) square_delta_y += (vh_y - square_guess_y) sum_honeycomb_distance = (honeycomb_delta_x**2 + honeycomb_delta_y**2)**0.5 sum_square_distance = (square_delta_x**2 + square_delta_y**2)**0.5 if abs(sum_honeycomb_distance) < abs(sum_square_distance): return GridEnum.HONEYCOMB else: return GridEnum.SQUARE
def showCreateHint(self, coord, next_idnums=(0, 1), show_hint=True, color=None): point_item = self.points_dict.get(coord, None) if point_item is None: return if show_hint is False: point_item.showCreateHint(show_hint=False) if point_item: row, column = coord if self.grid_type is GridType.HONEYCOMB: parity = 0 if HoneycombDnaPart.isOddParity( row=row, column=column) else 1 elif self.grid_type is GridType.SQUARE: parity = 0 if SquareDnaPart.isEvenParity(row=row, column=column) else 1 else: return id_num = next_idnums[1] if parity else next_idnums[0] point_item.showCreateHint(id_num=id_num, show_hint=show_hint, color=color) return parity == 1
def showCreateHint(self, coord: Tuple[int, int], next_idnums: Tuple[int, int] = (0, 1), show_hint: bool = True, color: str = None) -> Optional[bool]: point_item = self.points_dict.get(coord) if point_item is None: return if not show_hint: point_item.showCreateHint(show_hint=False) if point_item: row, column = coord if self.grid_type is GridEnum.HONEYCOMB: parity = 0 if HoneycombDnaPart.isEvenParity( row=row, column=column) else 1 elif self.grid_type is GridEnum.SQUARE: parity = 0 if SquareDnaPart.isEvenParity(row=row, column=column) else 1 else: return id_num = next_idnums[1] if parity else next_idnums[0] point_item.showCreateHint(id_num=id_num, show_hint=show_hint, color=color) return parity == 1
def _getCoordinateParity(self, row, column): if self.griditem.grid_type is GridType.HONEYCOMB: return 0 if HoneycombDnaPart.isOddParity(row=row, column=column) else 1 elif self.griditem.grid_type is GridType.SQUARE: return 0 if SquareDnaPart.isEvenParity(row=row, column=column) else 1 else: return None
def _getModelXYforCoord(self, row, column): radius = DEFAULT_RADIUS if self.griditem.grid_type is GridType.HONEYCOMB: return HoneycombDnaPart.latticeCoordToQtXY(radius, row, column) elif self.griditem.grid_type is GridType.SQUARE: return SquareDnaPart.latticeCoordToQtXY(radius, row, column) else: return None
def getNeighborsForCoordinate(grid_type, row, column): # TODO[NF]: Docstring if grid_type is GridEnum.HONEYCOMB: if not HoneycombDnaPart.isEvenParity(row, column): return ((row + 1, column), (row, column - 1), (row, column + 1)) else: return ((row - 1, column), (row, column + 1), (row, column - 1)) elif grid_type is GridEnum.SQUARE: return ((row, column + 1), (row, column - 1), (row - 1, column), (row + 1, column)) else: return ()
def partVirtualHelixAddedSlot(self, sender, id_num, virtual_helix, neighbors): """Summary Args: sender (obj): Model object that emitted the signal. id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods. neighbors (TYPE): Description Args: TYPE: Description """ # print('[NAPI] ADDED SLOT CALLED ON VH %s; called by %s' % (id_num, sender)) vhi = SliceVirtualHelixItem(virtual_helix, self) self._virtual_helix_item_hash[id_num] = vhi self._refreshVirtualHelixItemGizmos(id_num, vhi) for neighbor_id in neighbors: nvhi = self._virtual_helix_item_hash.get(neighbor_id, False) if nvhi: self._refreshVirtualHelixItemGizmos(neighbor_id, nvhi) self.enlargeRectToFit() position = sender.locationQt(id_num=id_num, scale_factor=self.scale_factor) if self.griditem.grid_type is GridType.HONEYCOMB: coordinates = HoneycombDnaPart.positionModelToLatticeCoord( DEFAULT_RADIUS, position[0], position[1], scale_factor=self.scale_factor) else: coordinates = SquareDnaPart.positionModelToLatticeCoord( DEFAULT_RADIUS, position[0], position[1], scale_factor=self.scale_factor) assert id_num not in self.coordinates_to_vhid.values() if coordinates in self.coordinates_to_vhid.values(): print('COORDINATES DUPLICATE %s in %s' % (coordinates, self.coordinates_to_vhid.values())) self.coordinates_to_vhid[coordinates] = id_num assert len(self.coordinates_to_vhid.keys()) == len( set(self.coordinates_to_vhid.keys())) assert len(self.coordinates_to_vhid.values()) == len( set(self.coordinates_to_vhid.values()))
def determineSliceViewType(document, part_dict, grid_type): THRESHOLD = 0.0005 vh_id_list = part_dict.get('vh_list') origins = part_dict.get('origins') for vh_id, size in vh_id_list: vh_x, vh_y = origins[vh_id] if grid_type is GridType.HONEYCOMB: distance, point = HoneycombDnaPart.distanceFromClosestLatticeCoord(vh_x, vh_y, DEFAULT_RADIUS) if distance > THRESHOLD: return SliceViewType.GRID elif grid_type is GridType.SQUARE: if SquareDnaPart.distanceFromClosestLatticeCoord(vh_x, vh_y, DEFAULT_RADIUS)[0] > THRESHOLD: return SliceViewType.GRID return SliceViewType.SLICE
def determineOrthoViewType(part_dict: dict, grid_type: EnumType): THRESHOLD = 0.0005 vh_id_list = part_dict.get('vh_list') origins = part_dict.get('origins') for vh_id, size in vh_id_list: vh_x, vh_y = origins[vh_id] if grid_type == GridEnum.HONEYCOMB: distance, point = HoneycombDnaPart.distanceFromClosestLatticeCoord( radius=DEFAULT_RADIUS, x=vh_x, y=vh_y) if distance > THRESHOLD: return OrthoViewEnum.GRID elif grid_type == GridEnum.SQUARE: if SquareDnaPart.distanceFromClosestLatticeCoord( radius=DEFAULT_RADIUS, x=vh_x, y=vh_y)[0] > THRESHOLD: return OrthoViewEnum.GRID return OrthoViewEnum.GRID
def testCreateVirtualHelixGui(cnapp): """Create some VHs""" # Create a new Honeycomb part toolbar = cnapp.window.main_toolbar action_new_honeycomb = toolbar.widgetForAction( cnapp.window.action_new_dnapart_honeycomb) QTest.mouseClick(action_new_honeycomb, Qt.LeftButton, delay=DELAY) slicerootitem = cnapp.window.slice_root assert len(slicerootitem.instance_items) == 1 slice_part_item = list(slicerootitem.instance_items.values())[0] QTest.keyClick(cnapp.window, Qt.Key_H, delay=DELAY) QTest.keyClick(cnapp.window, Qt.Key_C, delay=DELAY) cnapp.processEvents() cmd_count = 1 # already added the part for row in range(-2, 2): for col in range(-2, 2): # print(row, col) x, y = HoneycombDnaPart.latticeCoordToModelXY(RADIUS, row, col) pt = QPointF(x, y) cnapp.graphicsItemClick(slice_part_item, Qt.LeftButton, pos=pt, delay=DELAY) cmd_count += 1 cnapp.processEvents() vh_count = len(cnapp.document.activePart().getidNums()) # undo and redo all for i in range(cmd_count): cnapp.document.undoStack().undo() cnapp.processEvents() for i in range(cmd_count): cnapp.document.undoStack().redo() cnapp.processEvents() part = list(cnapp.document.children())[0] vh_count_after_redo = len(part.getidNums()) assert vh_count == vh_count_after_redo
def showCreateHint(self, coord: Tuple[int, int], next_idnums: Tuple[int, int] = (0, 1), show_hint: bool = True, color: str = None) -> Optional[bool]: point_item = self.points_dict.get(coord) if point_item is None: return if not show_hint: point_item.showCreateHint(show_hint=False) if point_item: row, column = coord if self.grid_type is GridEnum.HONEYCOMB: parity = 0 if HoneycombDnaPart.isEvenParity(row=row, column=column) else 1 elif self.grid_type is GridEnum.SQUARE: parity = 0 if SquareDnaPart.isEvenParity(row=row, column=column) else 1 else: return id_num = next_idnums[1] if parity else next_idnums[0] point_item.showCreateHint(id_num=id_num, show_hint=show_hint, color=color) return parity == 1
def testCreateVirtualHelixGui(cnapp): """Create some VHs""" # Create a new Honeycomb part toolbar = cnapp.window.main_toolbar action_new_honeycomb = toolbar.widgetForAction(cnapp.window.action_new_dnapart_honeycomb) QTest.mouseClick(action_new_honeycomb, Qt.LeftButton, delay=DELAY) slicerootitem = cnapp.window.views['slice'].root_item assert len(slicerootitem.instance_items) == 1 slice_part_item = list(slicerootitem.instance_items.values())[0] QTest.keyClick(cnapp.window, Qt.Key_H, delay=DELAY) QTest.keyClick(cnapp.window, Qt.Key_C, delay=DELAY) cnapp.processEvents() cmd_count = 1 # already added the part for row in range(-2, 2): for col in range(-2, 2): # print(row, col) x, y = HoneycombDnaPart.latticeCoordToModelXY(RADIUS, row, col) pt = QPointF(x, y) cnapp.graphicsItemClick(slice_part_item, Qt.LeftButton, pos=pt, delay=DELAY) cmd_count += 1 cnapp.processEvents() vh_count = len(cnapp.document.activePart().getidNums()) # undo and redo all for i in range(cmd_count): cnapp.document.undoStack().undo() cnapp.processEvents() for i in range(cmd_count): cnapp.document.undoStack().redo() cnapp.processEvents() part = list(cnapp.document.children())[0] vh_count_after_redo = len(part.getidNums()) assert vh_count == vh_count_after_redo
def createToolHoverMove(self, tool, event): """Summary Args: tool (TYPE): Description event (TYPE): Description Returns: TYPE: Description """ is_alt = True if event.modifiers() & Qt.AltModifier else False mapped_position = self.griditem.mapFromScene(event.scenePos()) event_xy = (mapped_position.x(), mapped_position.y()) if self.griditem.grid_type is GridType.HONEYCOMB: event_coord = HoneycombDnaPart.positionModelToLatticeCoord( DEFAULT_RADIUS, event_xy[0], event_xy[1], scale_factor=self.scale_factor, strict=True) elif self.griditem.grid_type is GridType.SQUARE: event_coord = SquareDnaPart.positionModelToLatticeCoord( DEFAULT_RADIUS, event_xy[0], event_xy[1], scale_factor=self.scale_factor, strict=True) else: event_coord = None self.last_mouse_position = event_xy if event_coord: try: grid_point = self.griditem.points_dict[(event_coord)] self.setLastHoveredItem(grid_point) except KeyError: pass # Un-highlight GridItems if necessary by calling createToolHoverLeave if len(self._highlighted_path) > 1 or ( self._highlighted_path and self._highlighted_path[0] != event_coord): self.removeAllCreateHints() self._highlighted_grid_point = event_coord if event_coord: self.griditem.highlightGridPoint(row=event_coord[0], column=event_coord[1], on=True) # Highlight GridItems if alt is being held down if is_alt and self.shortest_path_add_mode and event_coord is not None: self._previewSpa(event_xy) else: if is_alt and event_coord is not None: self.highlightOneGridPoint(self.getLastHoveredCoordinates(), styles.SPA_START_HINT_COLOR) elif not is_alt and event_coord is not None: part = self._model_part next_idnums = (part._getNewIdNum(0), part._getNewIdNum(1)) self.griditem.showCreateHint(event_coord, next_idnums=next_idnums) self._highlighted_path.append(event_coord) tool.hoverMoveEvent(self, event) return QGraphicsItem.hoverMoveEvent(self, event)
def createHoneycombGrid(self, part_item, radius, bounds): """Instantiate an area of griditems arranged on a honeycomb lattice. Args: part_item (TYPE): Description radius (TYPE): Description bounds (TYPE): Description Returns: TYPE: Description """ doLattice = HoneycombDnaPart.latticeCoordToPositionXY doPosition = HoneycombDnaPart.positionToLatticeCoordRound isEven = HoneycombDnaPart.isEvenParity x_l, x_h, y_l, y_h = bounds x_l = x_l + HoneycombDnaPart.PAD_GRID_XL x_h = x_h + HoneycombDnaPart.PAD_GRID_XH y_h = y_h + HoneycombDnaPart.PAD_GRID_YL y_l = y_l + HoneycombDnaPart.PAD_GRID_YH dot_size, half_dot_size = self.dots sf = part_item.scale_factor points = self.points row_l, col_l = doPosition(radius, x_l, -y_l, False, False, scale_factor=sf) row_h, col_h = doPosition(radius, x_h, -y_h, True, True, scale_factor=sf) redo_neighbors = (row_l, col_l, row_h, col_h) != self.previous_grid_bounds or\ self.previous_grid_type != self.grid_type self.previous_grid_type = self.grid_type path = QPainterPath() is_pen_down = False draw_lines = self.draw_lines if redo_neighbors: point_coordinates = dict() neighbor_map = dict() self.points_dict = dict() for row in range(row_l, row_h): for column in range(col_l, col_h + 1): x, y = doLattice(radius, row, column, scale_factor=sf) if draw_lines: if is_pen_down: path.lineTo(x, -y) else: is_pen_down = True path.moveTo(x, -y) """ +x is Left and +y is down origin of ellipse is Top Left corner so we subtract half in X and subtract in y """ pt = GridPoint(x - half_dot_size, -y - half_dot_size, dot_size, self, coord=(row, column)) if self._draw_gridpoint_coordinates: font = QFont(styles.THE_FONT) path.addText(x - 10, -y + 5, font, "%s,%s" % (-row, column)) pt.setPen( getPenObj(styles.GRAY_STROKE, styles.EMPTY_HELIX_STROKE_WIDTH)) # if x == 0 and y == 0: # pt.setBrush(getBrushObj(Qt.gray)) points.append(pt) self.points_dict[(-row, column)] = pt if redo_neighbors: point_coordinates[(-row, column)] = (x, -y) # This is reversed since the Y is mirrored if not HoneycombDnaPart.isEvenParity(row, column): neighbor_map[(-row, column)] = [(-row - 1, column), (-row, column + 1), (-row, column - 1)] else: neighbor_map[(-row, column)] = [(-row + 1, column), (-row, column - 1), (-row, column + 1)] self.previous_grid_bounds = (row_l, col_l, row_h, col_h) is_pen_down = False if draw_lines: for column in range(col_l, col_h + 1): for row in range(row_l, row_h): x, y = doLattice(radius, row, column, scale_factor=sf) if is_pen_down and isEven(row, column): path.lineTo(x, -y) is_pen_down = False else: is_pen_down = True path.moveTo(x, -y) is_pen_down = False # end for j self._path.setPath(path) if redo_neighbors: self.part_item.setNeighborMap(neighbor_map=neighbor_map) self.part_item.setPointMap(point_map=point_coordinates)