def intersection(p1, p2, p3, p4): """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 isBetween(p1, p2, p): """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 drawPath(self, path=None, p=None, sx=1, sy=None): if p is None: xpt = ypt = 0 else: xpt, ypt = point2D(upt(p)) # TODO: xpt, ypt? shape = self._getShape() if shape is not None: self.ensure_page() self.page.place(shape.path(self._path.commands))
def getPixelColor(self, p, scaled=True): """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 p = x, y return self.doc.context.imagePixelColor(self.path, p)
def text(self, sOrBs, p): """Draw the sOrBs text string, can be a str or BabelString, including a DrawBot FormattedString at position p. NOTE: signature differs from DrawBot. """ if not isinstance(sOrBs, str): sOrBs = sOrBs.s # Assume here is's a BabelString with a FormattedString inside. ppt = point2D(upt(p)) self.b.text(sOrBs, ppt) # Render point units to value tuple
def textBox(self, sOrBs, r): """Draw the sOrBs text string, can be a str or BabelString, including a DrawBot FormattedString in rectangle r.""" if not isinstance(sOrBs, str): sOrBs = sOrBs.s # Assume here is's a BabelString with a FormattedString inside. x, y, w, h = r t = self._drawing.text(sOrBs, insert=point2D(upt(x, y)), stroke=color(self._stroke).css, stroke_width=upt(self._strokeWidth), fill=color(self._fill).css, font_size=upt(self._fontSize), font_family=self._font) self._drawing.add(t)
def build_inds(self, view, origin): """It is better to have a separate InDesignContext build tree, because we need more information than just drawing instructions. We just pass the PageBot Element to the InDesignContext, using it's own API.""" context = view.context p = pointOffset(self.origin, origin) px, py = p2D = point2D( self._applyAlignment(p)) # Ignore z-axis for now. context.oval(px, py, e=self) for e in self.elements: e.build_inds(view, p2D)
def curveTo(self, bcp1, bcp2, p): """Curve to point p i nthe running path. Create a new path if none is open. >>> context = DrawBotContext() >>> # Create a new self._path by property self.path >>> context.moveTo(pt(100, 100)) >>> context.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> context.closePath() >>> # Drawing on a separate path >>> path = context.newPath() >>> path.moveTo(pt(100, 100)) >>> path.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> path.closePath() >>> context.drawPath(path) """ b1pt = point2D(upt(bcp1)) b2pt = point2D(upt(bcp2)) ppt = point2D(upt(p)) self.path.curveTo(b1pt, b2pt, ppt) # Render units tuples to value tuples
def build_inds(self, view, origin=None, **kwargs): """It is better to have a separate InDesignContext build tree, because we need more information down there than just drawing instructions. This way the InDesignContext just gets passed the PageBot Element, using it's own API.""" context = view.context p = pointOffset(self.origin, origin) p2D = point2D(self._applyAlignment(p)) # Ignore z-axis for now. context.textBox(self.bs, p2D, e=self) for e in self.elements: e.build_inds(view, p2D)
def drawCropMarks(self, e, origin): """If the show flag is set, then draw the cropmarks or page frame. >>> from pagebot.toolbox.units import mm >>> from pagebot.contexts.platform import getContext >>> context = getContext() >>> from pagebot.elements.element import Element >>> from pagebot.style import getRootStyle >>> style = getRootStyle() # Get default values >>> e = Element() >>> view = PageView(context=context, style=style) >>> view.showCropMarks = True >>> view.folds = [(mm(40), mm(60)),] >>> view.drawCropMarks(e, pt(0, 0)) """ if (self.showCropMarks and e.isPage) or e.showCropMarks: context = self.context x, y = point2D(origin) # Ignore z-axus for now. w, h = e.size folds = self.css('folds') cmDistance = self.css('viewCropMarkDistance') # From the side cmSize = min(self.css('viewCropMarkSize', pt(32)), self.pl) cmStrokeWidth = self.css('viewCropMarkStrokeWidth') context.fill(noColor) context.stroke( registrationColor, w=cmStrokeWidth) # For CMYK, draw all colors color(cmyk=1)) # Bottom left context.line((x - cmDistance, y), (x - cmSize, y)) context.line((x, y - cmDistance), (x, y - cmSize)) # Bottom right context.line((x + w + cmDistance, y), (x + w + cmSize, y)) context.line((x + w, y - cmDistance), (x + w, y - cmSize)) # Top left context.line((x - cmDistance, y + h), (x - cmSize, y + h)) context.line((x, y + h + cmDistance), (x, y + h + cmSize)) # Top right context.line((x + w + cmDistance, y + h), (x + w + cmSize, y + h)) context.line((x + w, y + h + cmDistance), (x + w, y + h + cmSize)) # Any fold lines to draw on the page? if folds is not None: for fx, fy in folds: if fx is not None: context.line((x + fx, y - cmDistance), (x + fx, y - cmSize)) context.line((x + fx, y + h + cmDistance), (x + fx, y + h + cmSize)) if fy is not None: context.line((x - cmDistance, y + fy), (x - cmSize, y + fy)) context.line((x + w + cmDistance, y + fy), (x + w + cmSize, y + fy))
def curveTo(self, bcp1, bcp2, p): """Makes a curve to point `p` in the running path. Creates a new path if none is open. >>> from pagebot.toolbox.units import mm >>> from pagebot.contexts import getContext >>> context = getContext() >>> path = context.newPath() >>> path.moveTo(pt(100, 100)) >>> path.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> path.closePath() >>> context.drawPath(path) """ assert self.isOpenPath, ( '%s.curveTo: Pen path is not open. Call self.beginPath() first.' % self.__class__.__name__) b1pt = point2D(upt(bcp1)) b2pt = point2D(upt(bcp2)) ppt = point2D(upt(p)) self.bp.curveTo(b1pt, b2pt, ppt) # Render units tuples to value tuples
def _drawNameLabel(self, aa, view, p): x, y = point2D(p) context = self.context bs = context.newString(self.name, style=dict(fontSize=18, textFill=(1, 0, 0))) tw, th = bs.textSize M = pt(8) context.fill(color(1, 1, 1, 0.6)) context.rect(x + self.w / 2 - tw / 2 - M, y + self.h / 2 - th / 2 - M, tw + 2 * M, th + 2 * M) context.text(bs, (x + self.w / 2 - tw / 2, y + self.h / 2 - th / 4))
def arcTo(self, pt1, pt2, radius): """Arc from one point to an other point with a given radius. >>> from pagebot.toolbox.units import p, pt >>> from pagebot import getContext >>> context = getContext('Flat') >>> path = BezierPath(context=context) >>> path.moveTo((0, 0)) >>> p1 = pt(100), p(6) >>> p2 = p(10), pt(200) >>> r = pt(300) """ """ >>> # TODO: implement for Flat. >>> path.arcTo(p1, p2, r) >>> path.closePath() """ pt1 = upt(point2D(pt1)) pt2 = upt(point2D(pt2)) ptRadius = upt(radius or DEFAULT_WIDTH / 2) self.bp.arcTo(pt1, pt2, ptRadius)
def image(self, path, p, alpha=1, pageNumber=None, w=None, h=None): """Draw the image. If w or h is defined, then scale the image to fit.""" if w is None or h is None: w, h = self.imageSize(path) xpt, ypt = point2D(upt(p)) self.save() img = self.b.image(path) img.resize(width=w.pt, height=h.pt) placed = self.page.place(img) placed.position(xpt, ypt) self.restore()
def build_inds(self, view, origin): """It is better to have a separate InDesignContext build tree, since we need more information down there than just drawing instructions. This way the InDesignContext just gets the PageBot Element passed over, using it's own API.""" context = view.context p = pointOffset(self.origin, origin) # Ignore z-axis for now. px, py = p2D = point2D(self._applyAlignment(p)) context.rect(px, py, e=self) for e in self.elements: e.build_inds(view, p2D)
def rotate(self, angle, center=None): """Rotate the canvas by angle. If angle is not a units.Angle instance, then convert. >>> context = DrawBotContext() >>> context.rotate(40) """ if center is None: center = (0, 0) else: center = point2D(upt(center)) if isinstance(angle, Angle): angle = angle.degrees # Otherwise assume the value to be a degrees number. self.b.rotate(angle, center=center)
def translatePoint(self, p): """Simpler function to translate a point based on origin coordinates (`self._ox` and `self._oy`). TODO: merge with getTransformed. """ x, y = point2D(upt(p)) x = self._ox + x '''Because the origin is at the bottom, like in DrawBot and as opposed to Flat, we need to subtract all vertical coordinates from the page height before an object gets placed. In case of (bounding) boxes, we also need to subtract the box height.''' y = self.height - (self._oy + y) return upt(x, y)
def curveTo(self, bcp1, bcp2, p): """Curve to point p i nthe open path. Create a new path if none is open. >>> from pagebot import getContext >>> context = getContext() >>> hasattr(context, 'newPath') True >>> context.newPage(420, 420) >>> # Create a new self._bezierpath by property self.bezierpath >>> context.moveTo(pt(100, 100)) >>> context.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> context.closePath() >>> # Drawing on a separate path >>> path = context.newPath() >>> path.moveTo(pt(100, 100)) >>> path.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> path.closePath() >>> context.drawPath(path) """ b1pt = upt(point2D(bcp1)) b2pt = upt(point2D(bcp2)) ppt = upt(point2D(p)) self.bezierpath.curveTo(b1pt, b2pt, ppt)
def translate(self, p): """Translate the path with a given offset. >>> from pagebot.contexts import getContext >>> context = getContext() >>> path = PageBotPath(context=context) >>> path.rect(0, 0, 200, 200) >>> path.points [(0.0, 0.0), (200.0, 0.0), (200.0, 200.0), (0.0, 200.0), (0.0, 0.0)] >>> path.translate(pt(20, 30)) >>> path.points [(20.0, 30.0), (220.0, 30.0), (220.0, 230.0), (20.0, 230.0), (20.0, 30.0)] """ ptx, pty = upt(point2D(p)) self.bp.translate(ptx, pty)
def pointProjectedOnLine(p1, p2, p): """Answers the projected point **(px, py)** on line **((x1, y1), (x2, y2))**. Answers **(x1, y1)** 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 = dotProduct(v1, v2) return x1 + (dot * tx) / dd, y1 + (dot * ty) / dd
def lineTo(self, p): """Makes a line to the path to point `p`. >>> from pagebot.toolbox.units import mm >>> from pagebot.contexts import getContext >>> context = getContext() >>> path = PageBotPath(context=context) >>> p = pt(100), mm(50) # Mixing different unit types >>> path.moveTo(p) >>> p = 100, 50 # Plain values are interpreted as pt >>> path.lineTo(p) >>> path.closePath() >>> context.drawPath(path) """ ptp = upt(point2D(p)) self.bp.lineTo(ptp)
def moveTo(self, p): """Move to point p in the running path. Create a new self._path if none is open. >>> from pagebot.toolbox.units import pt >>> context = DrawBotContext() >>> context.moveTo(pt(100, 100)) >>> context.moveTo((100, 100)) >>> # Drawing on a separate path >>> path = context.newPath() >>> path.moveTo(pt(100, 100)) >>> path.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> path.closePath() >>> context.drawPath(path) """ ppt = point2D(upt(p)) self.path.moveTo(ppt) # Render units point tuple to tuple of values
def drawPath(self, path=None, p=None, sx=1, sy=None): """Draw the NSBezierPath, or equivalent in other contexts. Scaled image is drawn on (x, y), in that order.""" if path is None: path = self.path self.save() if sy is None: sy = sx if p is None: xpt = ypt = 0 else: xpt, ypt = point2D(upt(p)) self.scale(sx, sy) self.translate(xpt / sx, ypt / sy) self.b.drawPath(path) self.restore()
def lineTo(self, p): """Line to point p in the running path. Create a new self._path if none is open. >>> context = DrawBotContext() >>> # Create a new self._path by property self.path >>> context.moveTo(pt(100, 100)) >>> context.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> context.closePath() >>> # Drawing on a separate path >>> path = context.newPath() >>> path.moveTo(pt(100, 100)) >>> path.curveTo(pt(100, 200), pt(200, 200), pt(200, 100)) >>> path.closePath() >>> context.drawPath(path) """ ppt = point2D(upt(p)) self.path.lineTo(ppt) # Render units point tuple to tuple of values
def moveTo(self, p): """Moves the path to point `p`. >>> from pagebot.toolbox.units import p >>> from pagebot.contexts import getContext >>> context = getContext() >>> path = PageBotPath(context=context) >>> point = pt(100), p(5) # Mixing different unit types >>> path.moveTo(point) >>> point = 100, 50 # Plain values are interpreted as pt >>> path.lineTo(point) >>> path.points [(100.0, 60.0), (100.0, 50.0)] """ self.isOpenLine = True ptp = upt(point2D(p)) self.bp.moveTo(ptp)
def scale(self, sx=1, sy=None, center=None): """Scale the path with a given x (horizontal scale) and y (vertical scale). If only 1 argument is provided a proportional scale is applied. The center of scaling can optionally be set via the center keyword argument. By default this is the origin. >>> from pagebot.contexts import getContext >>> context = getContext() >>> path = PageBotPath(context=context) >>> path.rect(0, 0, 200, 200) >>> path.scale(0.5) >>> path.points [(0.0, 0.0), (100.0, 0.0), (100.0, 100.0), (0.0, 100.0), (0.0, 0.0)] """ if center is None: center = ORIGIN center = upt(point2D(center)) self.bp.scale(sx, sy, center)
def onBlack(self, p, path=None): """Answers if the single point (x, y) is on black. >>> from pagebot.fonttoolbox.objects.font import findFont >>> context = DrawBotContext() >>> f = findFont('Roboto-Regular') >>> g = f['H'] >>> path = context.getGlyphPath(g) >>> p = (0, 0) >>> context.onBlack(p, path=path) False >>> # TODO: find out coordinate that is inside 'H'. >>> #context.onBlack((120, 10), path=path) """ if path is None: path = self.bezierpath p = point2D(p) return path._path.containsPoint_(p)
def drawColorBars(self, e, origin): """Draw the color bars for offset printing color calibration. """ # TODO Get this to work for content of the parameter set. showColorBars = e.showColorBars or (e.isPage and self.showColorBars) if not showColorBars: return # Nothing to do. context = self.context ox, oy = point2D(origin) # TODO: Add more types of color bars and switch from scaling PDF to drawing them by script if ECI_GrayConL in showColorBars: path = getResourcesPath() + '/' + ECI_GrayConL if COLORBAR_LEFT in showColorBars: context.image(path, p=(ox - self.pl + pt(3), oy), h=e.h) if COLORBAR_RIGHT in showColorBars: # TODO: Does not generate the right position? context.image(path, p=(ox + e.w + self.pr * 2 / 3, oy), h=e.h)
def lineTo(self, p): """Line to point p in the open path. Create a new self._path if none is open. >>> from pagebot.contexts.drawbotcontext import DrawBotContext >>> context = DrawBotContext() >>> # Create a new self._path by property self.path >>> context.moveTo(pt(100, 100)) >>> context.lineTo(pt(100, 200)) >>> context.closePath() >>> # Drawing on a separate path >>> path = context.newPath() >>> path.moveTo(pt(100, 100)) >>> path.lineTo(pt(100, 200)) >>> path.closePath() >>> context.drawPath(path) """ ppt = upt(point2D(p)) self.path.lineTo(ppt) # Render units point tuple to tuple of values
def translate(self, p): """Translate the path with a given offset. >>> from pagebot import getContext >>> from pagebot.toolbox.units import pt >>> context = getContext('Flat') >>> path = BezierPath(context=context) >>> path.rect(0.0, 0.0, 200.0, 200.0) >>> path.points ((0.0, 0.0), (200.0, 0.0), (200.0, 200.0), (0.0, 200.0), (0.0, 0.0)) """ """ TODO: implement for Flat. >>> path.translate(pt(20, 30)) >>> path.points ((20.0, 30.0), (220.0, 30.0), (220.0, 230.0), (20.0, 230.0), (20.0, 30.0)) """ ptx, pty = upt(point2D(p)) self.bp.translate(ptx, pty)