How to draw a wiggle line with Python



  • Last year I wrote a Drawbot tutorial on Medium.
    If you want to jump directly into the code, here's the source:

    ##############################
    # Draw Wiggles using Drawbot #
    ##############################
    
    """
    Script by Roberto Arista, you can find the related tutorial here: https://medium.com/@roberto_arista/how-to-draw-a-wiggle-between-two-points-with-python-and-drawbot-788006c18fb0
    You can find drawbot here: http://www.drawbot.com/
    Code distributed with no guarantee, use at your own risk
    """
    
    ### Modules
    from math import radians, atan2, sqrt, sin, cos
    from collections import namedtuple
    
    
    ### Constants
    BLACK = (0, 0, 0)
    Point = namedtuple('Point', ['x', 'y'])
    
    
    ### Function & procedures
    def calcAngle(pt1, pt2):
        return atan2((pt2.y - pt1.y), (pt2.x - pt1.x))
    
    
    def calcDistance(pt1, pt2):
        return sqrt((pt1.x - pt2.x)**2 + (pt1.y - pt2.y)**2)
    
    
    def calcWiggle(pt1, pt2, waveLength, waveHeight, curveSquaring=.57):
        assert 0 <= curveSquaring <= 1, 'curveSquaring should be a value between 0 and 1: {}'.format(curveSquaring)
        assert waveLength > 0, 'waveLength smaller or equal to zero: {}'.format(waveLength)
    
        diagonal = calcDistance(pt1, pt2)
        angleRad = calcAngle(pt1, pt2)
    
        howManyWaves = diagonal//int(waveLength)
        waveInterval = diagonal/float(howManyWaves)
        maxBcpLength = sqrt((waveInterval/4.)**2+(waveHeight/2.)**2)
        bcpLength = maxBcpLength*curveSquaring
        bcpInclination = calcAngle(Point(0,0), Point(waveInterval/4., waveHeight/2.))
    
        wigglePoints = [pt1]
        prevFlexPt = pt1
        polarity = 1
        for waveIndex in range(0, int(howManyWaves*2)):
            bcpOutAngle = angleRad+bcpInclination*polarity
            bcpOut = Point(prevFlexPt.x+cos(bcpOutAngle)*bcpLength, prevFlexPt.y+sin(bcpOutAngle)*bcpLength)
    
            flexPt = Point(prevFlexPt.x+cos(angleRad)*waveInterval/2., prevFlexPt.y+sin(angleRad)*waveInterval/2.)
    
            bcpInAngle = angleRad+(radians(180)-bcpInclination)*polarity
            bcpIn = Point(flexPt.x+cos(bcpInAngle)*bcpLength, flexPt.y+sin(bcpInAngle)*bcpLength)
    
            wigglePoints.append((bcpOut, bcpIn, flexPt))
    
            polarity *= -1
            prevFlexPt = flexPt
    
        return wigglePoints
    
    
    def drawCurvesSequence(wigglePoints):
        myBez = BezierPath()
        myBez.moveTo(wigglePoints[0])
        for eachBcpOut, eachBcpIn, eachAnchor in wigglePoints[1:]:
            myBez.curveTo(eachBcpOut, eachBcpIn, eachAnchor)
        myBez.endPath()
        drawPath(myBez)
    
    
    ### Variables
    pt1 = Point(50, 50)
    pt2 = Point(150, 60)
    
    ### Instructions
    size(400, 400)
    
    oval(pt1.x-1, pt1.y-1, 2, 2)
    oval(pt2.x-1, pt2.y-1, 2, 2)
    
    stroke(*BLACK)
    strokeWidth(.5)
    fill(None)
    
    wigglePoints = calcWiggle(pt1, pt2, 16, 36, .7)
    drawCurvesSequence(wigglePoints)