def cornerMitre(self, mitreSize=5, isRadius=False): # - Calculate unit vectors and shifts nextNode = self.getNextOn(False) prevNode = self.getPrevOn(False) nextUnit = Coord(nextNode.asCoord() - self.asCoord()).unit prevUnit = Coord(prevNode.asCoord() - self.asCoord()).unit if not isRadius: angle = math.atan2(nextUnit | prevUnit, nextUnit & prevUnit) radius = abs((float(mitreSize) / 2) / math.sin(angle / 2)) else: radius = mitreSize nextShift = nextUnit * radius prevShift = prevUnit * radius # - Insert Node and process nextNode = self.__class__( self.insertAfter(.01)) # Was 0?, something went wrong in 6871 nextNode.smartReloc( self.x, self.y) # Go back because something went wrong in 6871 self.smartShift(*prevShift.tuple) nextNode.smartShift(*nextShift.tuple) nextNode.fl.convertToLine() return (self.fl, nextNode.fl)
def alignTo(self, entity, alignMode='', align=(True, True)): '''Align Abstract nodes container to. Arguments: entity () alignMode (String) : L(left), R(right), C(center), T(top), B(bottom), E(vertical center) !ORDER MATTERS ''' # - Helper def getAlignDict(item): align_dict = { 'L': item.x(), 'R': item.x() + item.width(), 'C': item.x() + item.width() / 2, 'B': item.y(), 'T': item.y() + item.height(), 'E': item.y() + item.height() / 2 } return align_dict # - Init if len(alignMode) == 2: alignX, alignY = alignMode.upper() # -- Get target for alignment if isinstance( entity, (fl6.flNode, pNode, eNode, Coord, pqt.QtCore.QPointF)): target = Coord(entity.x, entity.y) elif isinstance( entity, (fl6.flContour, self.__class__)): #(fl6.flContour, pContour, self.__class__) if isinstance(entity, fl6.flContour): temp_entity = self.__class__(fl6.flContour.nodes()) else: temp_entity = entity align_dict = getAlignDict(temp_entity) target = Coord(align_dict[alignX], align_dict[alignY]) # -- Get source for alignment align_dict = getAlignDict(self) source = Coord(align_dict[alignX], align_dict[alignY]) # - Process shift = source - target shift_dx = abs( shift.x) * [1, -1][source.x > target.x] if align[0] else 0. shift_dy = abs( shift.y) * [1, -1][source.y > target.y] if align[1] else 0. self.shift(shift_dx, shift_dy) else: print('ERROR:\t Invalid Align Mode: {}'.format(alignMode))
def cornerRound(self, size=5, proportion=None, curvature=None, isRadius=False): # - Calculate unit vectors and shifts nextNode = self.getNextOn(False) prevNode = self.getPrevOn(False) nextUnit = Coord(nextNode.asCoord() - self.asCoord()).unit prevUnit = Coord(prevNode.asCoord() - self.asCoord()).unit if not isRadius: angle = math.atan2(nextUnit | prevUnit, nextUnit & prevUnit) radius = abs((float(size) / 2) / math.sin(angle / 2)) else: radius = size nextShift = nextUnit * radius prevShift = prevUnit * radius # - Insert Nodes and process nextNode = self.__class__( self.insertAfter(.01)) # Was 0?, something went wrong in 6871 nextNode.smartReloc( self.x, self.y) # Go back because something went wrong in 6871 self.smartShift(*prevShift.tuple) nextNode.smartShift(*nextShift.tuple) # -- Make round corner nextNode.fl.convertToCurve(True) segment = self.getSegmentNodes() # -- Curvature and handle length curve = Curve(segment) if proportion is not None: new_curve = curve.solve_proportional_handles(proportion) segment[1].x = new_curve.p1.x segment[1].y = new_curve.p1.y segment[2].x = new_curve.p2.x segment[2].y = new_curve.p2.y if curvature is not None: new_curve = curve.solve_hobby(curvature) segment[1].x = new_curve.p1.x segment[1].y = new_curve.p1.y segment[2].x = new_curve.p2.x segment[2].y = new_curve.p2.y return segment
def slantShift(self, shift_x, shift_y, angle): '''Slanted move - move a node (in inclined space) according to Y coordinate slanted at given angle. Arguments: shift_x, shift_y (float) angle (float): Angle in degrees ''' # - Init cNode = Coord((self.x + shift_x, self.y)) cNode.angle = angle # - Calculate & set newX = cNode.solve_width(cNode.y + shift_y) #self.fl.smartSetXY(pqt.QtCore.QPointF(newX, self.y + shift_y)) self.smartReloc(newX, self.y + shift_y)
def interpShift(self, shift_x, shift_y): '''Interpolated move aka Interpolated Nudge. Arguments: shift_x, shift_y (float) ''' if self.isOn: # - Init shift = Coord(shift_x, shift_y) currSegmet, prevSegment = self.getSegment(), self.getSegment(-1) if prevSegment == None: prevSegment = self.contour.segments()[-1] # - Process segments if len(currSegmet) == 4: currCurve = Curve(currSegmet) new_currCurve = currCurve.lerp_first(shift) currNode_bcpOut = self.getNext(False) nextNode_bcpIn = currNode_bcpOut.getNext(False) nextNode = nextNode_bcpIn.getOn(False) currSegmetNodes = [ self, currNode_bcpOut, nextNode_bcpIn, nextNode ] # - Set node positions for i in range(len(currSegmetNodes)): currSegmetNodes[i].smartReloc( *new_currCurve.points[i].tuple) if len(prevSegment) == 4: prevCurve = Curve(prevSegment) new_prevCurve = prevCurve.lerp_last(shift) currNode_bcpIn = self.getPrev(False) prevNode_bcpOut = currNode_bcpIn.getPrev(False) prevNode = prevNode_bcpOut.getOn(False) prevSegmentNodes = [ prevNode, prevNode_bcpOut, currNode_bcpIn, self ] # - Set node positions for i in range(len(prevSegmentNodes) - 1, -1, -1): prevSegmentNodes[i].smartReloc( *new_prevCurve.points[i].tuple) if len(currSegmet) == 2 and len(prevSegment) == 2: self.smartShift(*shift.tuple)
def cornerTrapInc(self, incision=10, depth=50, trap=2, smooth=True): '''Trap a corner by given incision into the glyph flesh. Arguments: incision (float): How much to cut into glyphs flesh based from that corner inward; depth (float): Length of the traps sides; trap (float): Width of the traps bottom; smooth (bool): Creates a smooth trap. Returns: tuple(flNode, flNode, flNode, flNode) four base (ON) nodes of the trap. ''' # - Init remains = depth - incision base_coord = self.asCoord() # - Calculate for aperture postision and structure nextNode = self.getNextOn(False) prevNode = self.getPrevOn(False) nextUnit = Coord(nextNode.asCoord() - self.asCoord()).unit prevUnit = Coord(prevNode.asCoord() - self.asCoord()).unit angle = math.atan2(nextUnit | prevUnit, nextUnit & prevUnit) aperture = abs(2 * (remains / math.sin(math.radians(90) - angle / 2) * math.sin(angle / 2))) adjust = float(aperture - trap) / 2 radius = abs((float(aperture) / 2) / math.sin(angle / 2)) bCoord = self.asCoord() + (nextUnit * -radius) cCoord = self.asCoord() + (prevUnit * -radius) aCoord = self.asCoord() + (prevUnit * radius) dCoord = self.asCoord() + (nextUnit * radius) # - Calculate for depth abUnit = Coord(aCoord - bCoord).unit dcUnit = Coord(dCoord - cCoord).unit bCoord = aCoord + abUnit * -depth cCoord = dCoord + dcUnit * -depth # - Calculate for trap (size) bcUnit = (bCoord - cCoord).unit cbUnit = (cCoord - bCoord).unit bCoord += bcUnit * -adjust cCoord += cbUnit * -adjust # - Insert Nodes and cleanup b = self.__class__( self.insertAfter(0.01)) # .01 quickfix - should be 0 c = self.__class__(b.insertAfter(0.01)) d = self.__class__(c.insertAfter(0.01)) b.fl.convertToLine() c.fl.convertToLine() d.fl.convertToLine() # - Position nodes self.smartReloc(*aCoord.tuple) b.smartReloc(*bCoord.tuple) d.smartReloc(*dCoord.tuple) c.smartReloc(*cCoord.tuple) # - Make smooth trap transition if smooth: # -- Convert nodes and extend bpc-s b.fl.convertToCurve() d.fl.convertToCurve() # -- Set nodes as smooth self.fl.smooth = True d.fl.smooth = True # -- Align bpc-s to the virtual lines connection sides of the trap with the original base node side_ab = Line(self.asCoord(), base_coord) side_cd = Line(base_coord, d.asCoord()) control = (True, False) bpc_a, bpc_c = self.getNext(False), c.getNext(False) bpc_b, bpc_d = b.getPrev(False), d.getPrev(False) bpc_a.alignTo(side_ab, control) bpc_b.alignTo(side_ab, control) bpc_c.alignTo(side_cd, control) bpc_d.alignTo(side_cd, control) return (self.fl, b.fl, c.fl, d.fl)
def cornerTrap(self, aperture=10, depth=20, trap=2): '''Trap a corner by given aperture. Arguments: aperture (float): Width of the traps mouth (opening); depth (float): Length of the traps sides; trap (float): Width of the traps bottom. Returns: tuple(flNode, flNode, flNode, flNode) ''' # - Init adjust = float(aperture - trap) / 2 # - Calculate for aperture postision and structure nextNode = self.getNextOn(False) prevNode = self.getPrevOn(False) nextUnit = Coord(nextNode.asCoord() - self.asCoord()).unit prevUnit = Coord(prevNode.asCoord() - self.asCoord()).unit angle = math.atan2(nextUnit | prevUnit, nextUnit & prevUnit) radius = abs((float(aperture) / 2) / math.sin(angle / 2)) bCoord = self.asCoord() + (nextUnit * -radius) cCoord = self.asCoord() + (prevUnit * -radius) aCoord = self.asCoord() + (prevUnit * radius) dCoord = self.asCoord() + (nextUnit * radius) # - Calculate for depth abUnit = Coord(aCoord - bCoord).unit dcUnit = Coord(dCoord - cCoord).unit bCoord = aCoord + abUnit * -depth cCoord = dCoord + dcUnit * -depth # - Calculate for trap (size) bcUnit = (bCoord - cCoord).unit cbUnit = (cCoord - bCoord).unit bCoord += bcUnit * -adjust cCoord += cbUnit * -adjust # - Insert Nodes and cleanup b = self.__class__( self.insertAfter(0.01)) # .01 quickfix - should be 0 c = self.__class__(b.insertAfter(0.01)) d = self.__class__(c.insertAfter(0.01)) b.fl.convertToLine() c.fl.convertToLine() d.fl.convertToLine() # - Position nodes self.smartReloc(*aCoord.tuple) b.smartReloc(*bCoord.tuple) d.smartReloc(*dCoord.tuple) c.smartReloc(*cCoord.tuple) return (self.fl, b.fl, c.fl, d.fl)
def asCoord(self): '''Returns Coord object of the node.''' return Coord(float(self.x), float(self.y))
def getCoord(self): return [Coord(node) for node in self.nodes]
def asCoord(self): '''Returns Coord object of the Bottom lest corner.''' return Coord(float(self.x()), float(self.y()))