def baseAtPoint(self, virtual_helix_item, pt): """Returns the (strand_type, base_idx) corresponding to pt in virtual_helix_item.""" x, strand_idx = self.helixIndex(pt) vh = virtual_helix_item.virtualHelix() if vh.isEvenParity(): strand_type = (StrandType.SCAFFOLD, StrandType.STAPLE)[util.clamp(strand_idx, 0, 1)] else: strand_type = (StrandType.STAPLE, StrandType.SCAFFOLD)[util.clamp(strand_idx, 0, 1)] return (strand_type, x, strand_idx)
def attemptToCreateStrand(self, virtual_helix_item, strand_set, idx): self._temp_strand_item.hideIt() s_idx = self._start_idx if abs(s_idx - idx) > 1: idx = util.clamp(idx, self._low_drag_bound, self._high_drag_bound) idxs = (idx, s_idx) if self.isDragLow(idx) else (s_idx, idx) self._start_strand_set.createStrand(*idxs)
def selectToolMouseMove(self, modifiers, idx): """ Given a new index (pre-validated as different from the prev index), calculate the new x coordinate for self, move there, and notify the parent strandItem to redraw its horizontal line. """ idx = util.clamp(idx, self._low_drag_bound, self._high_drag_bound)
def safeScale(self, delta): current_scale_level = self.transform().m11() scale_factor = 1 + delta * (self._scale_down_rate if delta < 0 else self._scale_up_rate) * \ (app().prefs.zoom_speed/100.) new_scale_level = current_scale_level * scale_factor new_scale_level = util.clamp(current_scale_level * scale_factor, self._scale_limit_min, self._scale_limit_max) scale_change = new_scale_level / current_scale_level self.scale(scale_change, scale_change) self._resetLOD()
def safeScale(self, delta: float): current_scale_level = self.transform().m11() scale_factor = 1 + delta * (self._scale_down_rate if delta < 0 else self._scale_up_rate) * \ (app().prefs.zoom_speed/100.) new_scale_level = current_scale_level * scale_factor new_scale_level = util.clamp(current_scale_level * scale_factor, self._scale_limit_min, self._scale_limit_max) scale_change = new_scale_level / current_scale_level self.scale(scale_change, scale_change) self._resetLOD()
def updateIndexSlot(self, part, base_index): """The slot that receives active slice changed notifications from the part and changes the receiver to reflect the part""" label = self._label bw = _BASE_WIDTH bi = util.clamp(int(base_index), 0, self.part().maxBaseIdx()) self.setPos(bi * bw, -styles.PATH_HELIX_PADDING) self._active_slice = bi if label: label.setText("%d" % bi) label.setX((bw - label.boundingRect().width()) / 2)
def baseAtPoint(self, virtual_helix_item, pt): """Returns the (is_fwd, base_idx, strand_idx) corresponding to pt in virtual_helix_item. Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): Description pt (TYPE): Description """ x, strand_idx = self.helixIndex(pt) is_fwd = False if util.clamp(strand_idx, 0, 1) else True return (is_fwd, x, strand_idx)
def baseAtPoint(self, virtual_helix_item, pt): """Returns the (is_fwd, base_idx, strand_idx) corresponding to pt in virtual_helix_item. Args: virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): Description pt (TYPE): Description """ x, strand_idx = self.helixIndex(pt) is_fwd = False if util.clamp(strand_idx, 0, 1) else True return (is_fwd, x, strand_idx)
def baseAtPoint(self, pos): """ Returns the (strand_type, index) under the location x,y or None. It shouldn't be possible to click outside a pathhelix and still call this function. However, this sometimes happens if you click exactly on the top or bottom edge, resulting in a negative y value. """ x, y = pos.x(), pos.y() mVH = self._model_virtual_helix base_idx = int(floor(x / _BASE_WIDTH)) min_base, max_base = 0, mVH.part().maxBaseIdx() if base_idx < min_base or base_idx >= max_base: base_idx = util.clamp(base_idx, min_base, max_base) if y < 0: y = 0 # HACK: zero out y due to erroneous click strandIdx = floor(y * 1. / _BASE_WIDTH) if strandIdx < 0 or strandIdx > 1: strandIdx = int(util.clamp(strandIdx, 0, 1)) strand_set = mVH.getStrandSetByIdx(strandIdx) return (strand_set, base_idx)
def selectToolMouseMove(self, modifiers, idx): """ Given a new index (pre-validated as different from the prev index), calculate the new x coordinate for self, move there, and notify the parent strandItem to redraw its horizontal line. """ idx = util.clamp(idx, self._low_drag_bound, self._high_drag_bound) x = int(idx * _BASE_WIDTH) self.setPos(x, self.y()) self.updateIndexSlot(None, idx) self._setActiveBaseIndex(idx) self._part_item.updateStatusBar("%d" % self.part().activeBaseIndex())
def baseAtPoint(self, virtual_helix_item: PathVirtualHelixItemT, pt: QPointF) -> Tuple[bool, int, int]: """Returns the ``(is_fwd, base_idx, strand_idx)`` corresponding to pt in virtual_helix_item. Args: virtual_helix_item: :class:`PathVirtualHelixItem` pt: Point on helix """ x, strand_idx = self.helixIndex(pt) is_fwd = False if util.clamp(strand_idx, 0, 1) else True return (is_fwd, x, strand_idx)
def baseAtPoint(self, pos: QPointF) -> Tuple[StrandSetT, int]: """Returns the (Strandset, index) under the location x, y or None. It shouldn't be possible to click outside a pathhelix and still call this function. However, this sometimes happens if you click exactly on the top or bottom edge, resulting in a negative y value. Args: pos: Description """ x, y = pos.x(), pos.y() part = self._model_part id_num = self._id_num base_idx = int(floor(x / _BASE_WIDTH)) min_base, max_base = 0, part.maxBaseIdx(id_num) if base_idx < min_base or base_idx >= max_base: base_idx = util.clamp(base_idx, min_base, max_base) if y < 0: y = 0 # HACK: zero out y due to erroneous click strand_type = floor(y * 1. / _BASE_WIDTH) # 0 for fwd, 1 for rev strand_type = int(util.clamp(strand_type, 0, 1)) strand_set = part.getStrandSets(id_num)[strand_type] return (strand_set, base_idx)
def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): """Converts event coords into an idx delta and updates if changed. """ delta = int(floor((self.x()+event.pos().x()) / BASE_WIDTH)) - self._offset_idx delta = util.clamp(delta, self._low_drag_bound-self._start_idx_low, self._high_drag_bound-self._start_idx_high+self.width()) if self._delta != delta: self._idx_low = int(self._start_idx_low + delta) self._idx_high = int(self._start_idx_high + delta) self._delta = delta self.reconfigureRect((), ()) self.resize_handle_group.updateText(HandleEnum.LEFT, self._idx_low) self.resize_handle_group.updateText(HandleEnum.RIGHT, self._idx_high) return QGraphicsItem.mouseMoveEvent(self, event)
def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent): """Repeates mouseMove calculation in case any new movement. """ self.setCursor(Qt.ArrowCursor) delta = int(floor((self.x()+event.pos().x()) / BASE_WIDTH)) - self._offset_idx delta = util.clamp(delta, self._low_drag_bound-self._start_idx_low, self._high_drag_bound-self._start_idx_high+self.width()) if self._delta != delta: self._idx_low = int(self._start_idx_low + delta) self._idx_high = int(self._start_idx_high + delta) self._delta = delta self.reconfigureRect((), ()) self._high_drag_bound = self._model_part.getProperty('max_vhelix_length') # reset for handles return QGraphicsItem.mouseReleaseEvent(self, event)
def mouseReleaseEvent(self, event): """ Repeates mouseMove calculation in case any new movement. """ self.setCursor(Qt.ArrowCursor) delta = int(floor((self.x()+event.pos().x()) / BASE_WIDTH)) - self._offset_idx delta = util.clamp(delta, self._low_drag_bound-self._start_idx_low, self._high_drag_bound-self._start_idx_high+self.width()) if self._delta != delta: self._idx_low = int(self._start_idx_low + delta) self._idx_high = int(self._start_idx_high + delta) self._delta = delta self.reconfigureRect((), ()) self._high_drag_bound = self._model_part.getProperty('max_vhelix_length') # reset for handles
def mouseMoveEvent(self, event): """ Converts event coords into an idx delta and updates if changed. """ delta = int(floor((self.x()+event.pos().x()) / BASE_WIDTH)) - self._offset_idx delta = util.clamp(delta, self._low_drag_bound-self._start_idx_low, self._high_drag_bound-self._start_idx_high+self.width()) if self._delta != delta: self._idx_low = int(self._start_idx_low + delta) self._idx_high = int(self._start_idx_high + delta) self._delta = delta self.reconfigureRect((), ()) self.resize_handle_group.updateText(HandleType.LEFT, self._idx_low) self.resize_handle_group.updateText(HandleType.RIGHT, self._idx_high)
def attemptToCreateStrand(self, virtual_helix_item, strand_set, idx): """Attempt to create a new strand within the VHI `strand_set` with bounds of the original mouse press index (stored as `self._start_idx`) and the mouse release index `idx`. Args: virtual_helix_item (cadnano.gui.views.pathview.virtualhelixitem.VirtualHelixItem): from vhi strand_set (StrandSet): Description idx (int): mouse release index """ self._temp_strand_item.hideIt() s_idx = self._start_idx if abs(s_idx - idx) > 1: idx = util.clamp(idx, self._low_drag_bound, self._high_drag_bound) idxs = (idx, s_idx) if self.isDragLow(idx) else (s_idx, idx) self._start_strand_set.createStrand(*idxs)
def attemptToCreateStrand(self, virtual_helix_item, strand_set, idx): """Attempt to create a new strand within the VHI `strand_set` with bounds of the original mouse press index (stored as `self._start_idx`) and the mouse release index `idx`. Args: virtual_helix_item (cadnano.views.pathview.virtualhelixitem.VirtualHelixItem): from vhi strand_set (StrandSet): Description idx (int): mouse release index """ self._temp_strand_item.hideIt() s_idx = self._start_idx if abs(s_idx - idx) > 1: idx = util.clamp(idx, self._low_drag_bound, self._high_drag_bound) idxs = (idx, s_idx) if self.isDragLow(idx) else (s_idx, idx) self._start_strand_set.createStrand(*idxs)
def _removeBasesClicked(self): """ Determines the minimum maxBase index where index modulo stepsize == 0 and is to the right of the rightmost nonempty base, and then resize each calls the resizeVirtualHelices to adjust to that size. """ part = self._model_part step_size = part.stepSize() # first find out the right edge of the part idx = part.indexOfRightmostNonemptyBase() # next snap to a multiple of stepsize idx = ceil((idx + 1) / step_size) * step_size # finally, make sure we're a minimum of step_size bases idx = util.clamp(idx, step_size, 10000) delta = idx - (part.maxBaseIdx() + 1) if delta < 0: part.resizeVirtualHelices(0, delta)
def attemptToCreateStrand(self, virtual_helix_item: PathVirtualHelixItemT, strand_set: StrandSetT, idx: int): """Attempt to create a new strand within the VHI ``strand_set`` with bounds of the original mouse press index (stored as ``self._start_idx``) and the mouse release index ``idx``. Args: virtual_helix_item: from vhi strand_set: Description idx: mouse release index """ self._temp_strand_item.hideIt() s_idx = self._start_idx if abs(s_idx - idx) > 1: idx = util.clamp(idx, self._low_drag_bound, self._high_drag_bound) idxs = (idx, s_idx) if self.isDragLow(idx) else (s_idx, idx) self._start_strand_set.createStrand(*idxs)
def _removeBasesClicked(self): """ Determines the minimum maxBase index where index modulo stepsize == 0 and is to the right of the rightmost nonempty base, and then resize each calls the resizeVirtualHelices to adjust to that size. """ part = self._model_part step_size = part.stepSize() # first find out the right edge of the part idx = part.indexOfRightmostNonemptyBase() # next snap to a multiple of stepsize idx = ceil((idx + 1) / step_size)*step_size # finally, make sure we're a minimum of step_size bases idx = util.clamp(idx, step_size, 10000) delta = idx - (part.maxBaseIdx() + 1) if delta < 0: part.resizeVirtualHelices(0, delta)
def reconfigureRect(self, top_left: Vec2T, bottom_right: Vec2T, finish: bool = False) -> QRectF: """Update the workplane rect to draw from top_left to bottom_right, snapping the x values to the nearest base width. Updates the outline and resize handles. Args: top_left: topLeft (x, y) bottom_right: bottomRight (x, y) Returns: the new rect. """ if top_left: xTL = max(top_left[0], self._low_drag_bound) xTL = xTL - (xTL % BASE_WIDTH) # snap to nearest base self._idx_low = int(xTL / BASE_WIDTH) self.resize_handle_group.updateText(HandleEnum.LEFT, self._idx_low) else: xTL = self._idx_low * BASE_WIDTH if bottom_right: xBR = util.clamp(bottom_right[0], (self._idx_low + self._MIN_WIDTH) * BASE_WIDTH, (self._high_drag_bound) * BASE_WIDTH) xBR = xBR - (xBR % BASE_WIDTH) # snap to nearest base self._idx_high = int(xBR / BASE_WIDTH) self.resize_handle_group.updateText(HandleEnum.RIGHT, self._idx_high) else: xBR = self._idx_high * BASE_WIDTH yTL = self._part_item._vh_rect.top() yBR = self._part_item._vh_rect.bottom() - BASE_WIDTH * 3 self.setRect(QRectF(QPointF(xTL, yTL), QPointF(xBR, yBR))) self.outline.setRect(self.rect()) self.outline.updateAppearance() self.resize_handle_group.alignHandles(self.rect()) self._model_part.setProperty('workplane_idxs', (self._idx_low, self._idx_high), use_undostack=False) return self.rect()
def reconfigureRect(self, top_left: Vec2T, bottom_right: Vec2T, finish: bool = False) -> QRectF: """Update the workplane rect to draw from top_left to bottom_right, snapping the x values to the nearest base width. Updates the outline and resize handles. Args: top_left: topLeft (x, y) bottom_right: bottomRight (x, y) Returns: the new rect. """ if top_left: xTL = max(top_left[0], self._low_drag_bound) xTL = xTL - (xTL % BASE_WIDTH) # snap to nearest base self._idx_low = int(xTL/BASE_WIDTH) self.resize_handle_group.updateText(HandleEnum.LEFT, self._idx_low) else: xTL = self._idx_low*BASE_WIDTH if bottom_right: xBR = util.clamp(bottom_right[0], (self._idx_low+self._MIN_WIDTH)*BASE_WIDTH, (self._high_drag_bound)*BASE_WIDTH) xBR = xBR - (xBR % BASE_WIDTH) # snap to nearest base self._idx_high = int(xBR/BASE_WIDTH) self.resize_handle_group.updateText(HandleEnum.RIGHT, self._idx_high) else: xBR = self._idx_high*BASE_WIDTH yTL = self._part_item._vh_rect.top() yBR = self._part_item._vh_rect.bottom()-BASE_WIDTH*3 self.setRect(QRectF(QPointF(xTL, yTL), QPointF(xBR, yBR))) self.outline.setRect(self.rect()) self.outline.updateAppearance() self.resize_handle_group.alignHandles(self.rect()) self._model_part.setProperty('workplane_idxs', (self._idx_low, self._idx_high), use_undostack=False) return self.rect()