def point2Line(p1, p2, p): u"""Answers the distance from point <i>(px, py)</i> to line <i>((x1, y1), (x2, y2))</i>.""" x1, y1 = point2D(p1) x2, y2 = point2D(p2) px, py = point2D(p) x, y = pointProjectedOnLine(p1, p2, p) tx, ty = px - x, py - y # Vector p1->p2 return math.sqrt(tx * tx + ty * ty) # Length of p1->p2
def getPixelColor(self, p, scaled=True): u"""Answer the color in either the scaled point (x, y) or original image size point.""" assert self.path is not None x, y = point2D(p) if scaled: x = self.w / self.iw y = self.h / self.ih return imagePixelColor(self.path, (x, y))
def isBetween(p1, p2, p): u"""Checks if point is on line between line endpoints. Uses epsilon margin for float values, can be substituted by zero for integer values.""" x1, y1 = point2D(p1) x2, y2 = point2D(p2) px, py = point2D(p) epsilon = 1e-6 crossproduct = (py - y1) * (x2 - x1) - (px - x1) * (y2 - y1) if abs(crossproduct) > epsilon: return False # (or != 0 if using integers) dotproduct = (px - x1) * (x2 - x1) + (py - y1) * (y2 - y1) if dotproduct < 0: return False squaredlengthba = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) if dotproduct > squaredlengthba: return False return True
def intersection(p1, p2, p3, p4): u"""Returns 2D intersection point if it exists. Otherwise (None, None, None) is answered. Different from the RoboFont intersection tool, we intersect on infinite line lengths. See also: http://en.wikipedia.org/wiki/Line-line_intersection """ x1, y1 = point2D(p1) x2, y2 = point2D(p2) x3, y3 = point2D(p3) x4, y4 = point2D(p4) d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4) if d != 0: m1 = (x1 * y2 - y1 * x2) m2 = (x3 * y4 - y3 * x4) return (m1 * (x3 - x4) - m2 * (x1 - x2)) / d, (m1 * (y3 - y4) - m2 * (y1 - y2)) / d return None, None
def pointProjectedOnLine(cls, p1, p2, p): u"""Answers the projected point <b>(px, py)</b> on line <b>((x1, y1), (x2, y2))</b>. Answers <b>(x1, y1)</b> if there is not distance between the two points of the line.""" # Line vector. x1, y1 = point2D(p1) x2, y2 = point2D(p2) px, py = point2D(p) tx, ty = float(x2 - x1), float(y2 - y1) v1 = (tx, ty) # Vector from line start to point. t1x, t1y = float(px - x1), float(py - y1) v2 = (t1x, t1y) # Square length of line to normalize. dd = tx * tx + ty * ty if dd == 0: return x1, y1 dot = cls.dotProduct(v1, v2) return x1 + (dot * tx) / dd, y1 + (dot * ty) / dd
def onBlack(self, p): u"""Answers the boolean flag is the single point (x, y) is on black.""" p = point2D(p) return self.path._path.containsPoint_(p)
def onBlack(self, p): u"""Answers the boolean flag if the single point (x, y) is on black. For now this only work in DrawBotContext.""" p = point2D(p) return self.path._path.containsPoint_(p)
def build(self, view, origin): u"""Draw the circle info-graphic, showing axis info about the variation font as can be interpreted from self.font. """ context = self.context # Get context from the parent doc. context.fill(0.9) context.stroke(None) x, y = point2D(pointOffset(self.oPoint, origin)) mx = x + self.w/2 my = y + self.h/2 # Gray circle that defines the area of context.oval(x, y, self.w, self.h) # Draw axis spikes first, so we can cover them by the circle markers. axes = self.font.axes fontSize = self.style.get('fontSize', self.DEFAULT_FONT_SIZE) # Draw name of the font bs = context.newString(self.font.info.familyName, style=dict(font=self.style['labelFont'], fontSize=self.style['axisNameFontSize'], textFill=0)) context.text(bs, (x-fontSize/2, y+self.h+fontSize/2)) # Draw spokes context.fill(None) context.stroke(0) context.strokeWidth(1) context.newPath() for axisName, angle in self.angles.items(): markerX, markerY = self._angle2XY(angle, self.w/2) context.moveTo((mx, my)) context.lineTo((mx+markerX, my+markerY)) context.drawPath() # Draw default glyph marker in middle. defaultLocation = {} self._drawGlyphIcon(mx, my, self.glyphName, fontSize, defaultLocation, strokeW=3) # Draw DeltaLocation circles. for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] # Outside maxValue location = {axisName: maxValue} markerX, markerY = self._angle2XY(angle, self.w/2) self._drawGlyphIcon(mx+markerX, my+markerY, glyphName, fontSize/2, location) # Interpolated DeltaLocation circles. location = {axisName: minValue + (maxValue - minValue)*INTERPOLATION} markerX, markerY = self._angle2XY(angle, self.w/4) self._drawGlyphIcon(mx+markerX*INTERPOLATION*2, my+markerY*INTERPOLATION*2, glyphName, fontSize/2, location) # Draw axis names and DeltaLocation values if self.showAxisNames: for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] location = {axisName: maxValue} valueFontSize = self.style.get('valueFontSize', 12) axisNameFontSize = self.style.get('axisNameFontSize', 12) markerX, markerY = self._angle2XY(angle, self.w/2) fs = context.newString(self.makeAxisName(axisName), style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=axisNameFontSize, fill=self.style.get('axisNameColor', 0))) tw, th = context.textSize(fs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) context.rect(mx+markerX-tw/2-4, my+markerY-axisNameFontSize/2-th*1.5-4, tw+8, th) context.text(fs, (mx+markerX-tw/2, my+markerY-axisNameFontSize/2-th*1.5)) # DeltaLocation master value if maxValue < 10: sMaxValue = '%0.2f' % maxValue else: sMaxValue = `int(round(maxValue))` fs = context.newString(sMaxValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = context.textSize(fs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) context.rect(mx+markerX-tw/2-4, my+markerY+valueFontSize/2+th*1.5-4, tw+8, th) context.text(fs, (mx+markerX-tw/2, my+markerY+valueFontSize/2+th*1.5)) # DeltaLocation value interpolationValue = minValue + (maxValue - minValue)*INTERPOLATION if interpolationValue < 10: sValue = '%0.2f' % interpolationValue else: sValue = `int(round(interpolationValue))` bs = context.newString(sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = context.textSize(bs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) context.rect(mx+markerX*INTERPOLATION-tw/2-4, my+markerY*INTERPOLATION+valueFontSize/2+th*1.5-4, tw+8, th) context.text(fs, (mx+markerX*INTERPOLATION-tw/2, my+markerY*INTERPOLATION+valueFontSize/2+th*1.5)) # DeltaLocation value if minValue < 10: sValue = '%0.2f' % minValue else: sValue = `int(round(minValue))` bs = context.newString(sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = context.textSize(bs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) minM = 0.2 context.rect(mx+markerX*minM-tw/2-4, my+markerY*minM+th*0.5-4, tw+8, th) context.text(fs, (mx+markerX*minM-tw/2, my+markerY*minM+th*0.5))