diff --git a/innernaut.py b/innernaut.py index 92e003e..95cdaec 100644 --- a/innernaut.py +++ b/innernaut.py @@ -3,165 +3,159 @@ from flight.server import FlightServer -server = FlightServer.ensure_threaded_instance() +#server = FlightServer.ensure_threaded_instance() #%% import math +import random from flight.case import * from flight.client import * from flight.expressions import * -client = FlightClient() - - -def RoundedRect(x, y, width, height, radius): - x2 = x + width - y2 = y + height - MoveTo(x, y + radius) - QuadraticCurveTo(x + radius, y, x, y) - LineTo(x2 - radius, y) - QuadraticCurveTo(x2, y + radius, x2, y) - LineTo(x2, y2 - radius) - QuadraticCurveTo(x2 - radius, y2, x2, y2) - LineTo(x + radius, y2) - QuadraticCurveTo(x, y2 - radius, x, y2) - ClosePath() +client = FlightClient('http://mirror.local:8888') #%% client.activate() -MaskRadius = 500 -EyeSeparation = 200 -EyeXRadius = 120 +MaskOuterRadius = 460 +MaskInnerRadius = 400 +BeadSeparation = 15 +BeadRadius = 5 +EyeSeparation = 160 +EyeXRadius = 140 EyeYRadius = 160 -PupilHeight = 30 +PupilHeight = 25 PupilWidth = 150 -EyelidWidth = 60 -EyelidHeight = 40 -EyeOutsideThickness = 50 -NoseHeight = 250 -NoseWidth = 120 -NoseUpturn = 25 -MouthOffset = 280 +EyeRimThickness = 50 +EyelinerThickness = 5 +NoseHeight = 240 +NoseWidth = 130 +NoseBottom = 25 +MouthOffset = 285 MouthWidth = 180 -MouthHeight = 120 -MouthCornerRadius = 30 +MouthHeight = 230 +TongueHeight = 180 +MouthCornerRadius = 20 LipThickness = 50 TriangleCount = 30 +TriangleWidth = 60 TriangleHeight = 40 -TriangleSeparation = 20 -OuterTriangleRadius = 450 -InnerTriangleRadius = 430 +TriangleRadius = 430 -with Screen('main', width=1080, height=1080) as screen: - with Pattern('beads', width=40, height=40) as beads: + +def SplitTriangle(width, height, bottom=0, **kwargs): + with Group(**kwargs): + for side, multiplier in [('left', -1), ('right', 1)]: + with Path(tags=side): + MoveTo(multiplier * width / 2, height / 2) + LineTo(0, -height / 2) + LineTo(0, height / 2 - bottom) + FillPath() + if bottom: + with Path(tags='bottom'): + MoveTo(-width / 2, height / 2) + LineTo(0, height / 2 - bottom) + LineTo(width / 2, height / 2) + FillPath() + + +with Screen('main', width=1920, height=1200) as screen: + with Pattern('beads', width=BeadSeparation * 2, height=BeadSeparation * 2) as beads: for x in range(3): for y in range(3): - odd = (x + y) % 2 == 1 - with Path(tags='odd' if odd else 'even', - color='magenta' if odd else 'cyan', - x=x * beads.width / 2, y=(y + (x % 2) / 3) * beads.height / 2): - Ellipse(0, 0, beads.width / 5, beads.height / 5) - FillPath() + with Group(x=x * beads.width / 2, y=(y + (x % 2) / 3) * beads.height / 2): + with Path(tags={'bead', 'odd' if (x + y) % 2 == 1 else 'even'}): + Ellipse(0, 0, BeadRadius, BeadRadius) + FillPath() + with Group('stars', color='white'): + for j in range(100): + radius = 10 + with Path(x=screen.width / 2, y=screen.height / 2, tags='star'): + MoveTo(radius / 2, 0) + for k in range(1, 10): + theta = 2 * math.pi * k / 10 + r = (1 + (k % 2)) * radius / 2 + LineTo(r * math.cos(theta), r * math.sin(theta)) + ClosePath() + FillPath() with Group('mask', x=screen.width/2, y=screen.height/2): - with Group('background'): - with Path(tags='outer', color='#330'): - Ellipse(x=0, y=0, radiusX=MaskRadius, radiusY=MaskRadius) + with Path(tags='background'): + Ellipse(x=0, y=0, radiusX=MaskOuterRadius, radiusY=MaskOuterRadius) + FillPath() + with Path(tags='background patterning'): + Ellipse(x=0, y=0, radiusX=MaskInnerRadius, radiusY=MaskInnerRadius) + FillPath() + with Group('triangles'): + for j in range(TriangleCount): + with Group(rotate=j / TriangleCount): + SplitTriangle(y=-TriangleRadius, tags='triangle', width=TriangleWidth, height=TriangleHeight) + with Group('mouth', y=MouthOffset): + with Path(tags='lips background'): + RoundedRect(-MouthWidth / 2 - LipThickness, -MouthHeight / 2 - LipThickness, + MouthWidth + LipThickness * 2, MouthHeight + LipThickness * 2, MouthCornerRadius + LipThickness) FillPath() - with Path(tags='inner', pattern='beads', fader=0.25): - Ellipse(x=0, y=0, radiusX=InnerTriangleRadius, radiusY=InnerTriangleRadius) + with Path(tags='hole'): + RoundedRect(-MouthWidth / 2, -MouthHeight / 2, MouthWidth, MouthHeight, MouthCornerRadius) FillPath() + with Path(tags='tongue inside', y=MouthHeight / 2, rotate=0.5): + RoundedRect(-MouthWidth / 2, 0, MouthWidth, TongueHeight, MouthCornerRadius) + FillPath() + with Path(tags='liner', lineWidth=EyelinerThickness): + RoundedRect(-MouthWidth / 2, -MouthHeight / 2, MouthWidth, MouthHeight, MouthCornerRadius) + StrokePath() with Group('eyes'): for side, multiplier in [('left', -1), ('right', 1)]: with Group(tags={'eye', side}, x=multiplier * (EyeSeparation/2 + EyeXRadius)): - with Path(tags='inside', color='#220'): + with Path(tags='inside'): Ellipse(x=0, y=0, radiusX=EyeXRadius, radiusY=EyeYRadius) FillPath() - with Path(tags='pupil', color='white'): + SplitTriangle(tags='eyelid upper', y=-EyeYRadius / 2, width=TriangleWidth, height=TriangleHeight) + SplitTriangle(tags='eyelid lower', y=EyeYRadius / 2, width=TriangleWidth, height=-TriangleHeight) + with Path(): + Ellipse(x=0, y=0, radiusX=EyeXRadius, radiusY=EyeYRadius) + with Path(tags='pupil hole'): Rect(-PupilWidth / 2, -PupilHeight / 2, PupilWidth, PupilHeight) FillPath() - with Path(tags='eyelid upper', color='#fc3', y=-EyeYRadius / 2): - MoveTo(0, -EyelidHeight / 2) - LineTo(-EyelidWidth / 2, EyelidHeight / 2) - LineTo(EyelidWidth / 2, EyelidHeight / 2) - FillPath() - with Path(tags='eyelid lower', color='#fc3', y=EyeYRadius / 2): - MoveTo(0, EyelidHeight / 2) - LineTo(-EyelidWidth / 2, -EyelidHeight / 2) - LineTo(EyelidWidth / 2, -EyelidHeight / 2) - FillPath() - with Path(tags='outside', lineWidth=EyeOutsideThickness, color='#072'): - Ellipse(x=0, y=0, radiusX=EyeXRadius + EyeOutsideThickness/2, radiusY=EyeYRadius + EyeOutsideThickness / 2) + with Path(tags='rim background', lineWidth=EyeRimThickness): + Ellipse(x=0, y=0, radiusX=EyeXRadius + EyeRimThickness/2, radiusY=EyeYRadius + EyeRimThickness / 2) StrokePath() - with Group('nose', color='#fc3'): - for side, multiplier in [('left', -1), ('right', 1)]: - with Path(tags={'nose', side}, fader=0.9 + multiplier*.1): - MoveTo(multiplier * NoseWidth / 2, NoseHeight / 2) - LineTo(0, -NoseHeight / 2) - LineTo(0, NoseHeight / 2 - NoseUpturn) - FillPath() - with Path(tags='nose bottom', fader=0.5): - MoveTo(-NoseWidth / 2, NoseHeight / 2) - LineTo(0, NoseHeight / 2 - NoseUpturn) - LineTo(NoseWidth / 2, NoseHeight / 2) - FillPath() - with Group('mouth', y=MouthOffset): - with Path(tags='inside', color='white'): - RoundedRect(-MouthWidth / 2, -MouthHeight / 2, MouthWidth, MouthHeight, MouthCornerRadius) - FillPath() - with Path(tags='outside', color='#220', lineWidth=LipThickness): - RoundedRect(-(MouthWidth + LipThickness) / 2, -(MouthHeight + LipThickness) / 2, - MouthWidth + LipThickness, MouthHeight + LipThickness, MouthCornerRadius + LipThickness) - StrokePath() - with Group('triangles', color='#fc3'): - with Group('outer_triangles'): - triangle_width = 2*math.pi * OuterTriangleRadius / TriangleCount - TriangleSeparation - for j in range(TriangleCount): - with Group(rotate=j / TriangleCount): - with Group(y=-OuterTriangleRadius, tags='triangle outer'): - with Path(fader=0.75): - MoveTo(-triangle_width / 2, 0) - LineTo(0, -TriangleHeight) - LineTo(0, 0) - FillPath() - with Path(): - MoveTo(0, 0) - LineTo(0, -TriangleHeight) - LineTo(triangle_width / 2, 0) - FillPath() - with Group('inner_triangles'): - triangle_width = 2*math.pi * InnerTriangleRadius / TriangleCount - TriangleSeparation - for j in range(TriangleCount): - with Group(rotate=j / TriangleCount): - with Group(y=-InnerTriangleRadius, tags='triangle inner'): - with Path(fader=0.75): - MoveTo(-triangle_width / 2, 0) - LineTo(0, TriangleHeight) - LineTo(0, 0) - FillPath() - with Path(): - MoveTo(0, 0) - LineTo(0, TriangleHeight) - LineTo(triangle_width / 2, 0) - FillPath() + with Path(tags='liner', lineWidth=EyelinerThickness): + Ellipse(x=0, y=0, radiusX=EyeXRadius, radiusY=EyeYRadius) + StrokePath() + SplitTriangle(id='nose', width=NoseWidth, height=NoseHeight, bottom=NoseBottom) #%% client.activate() +with Cue('Q0', name='Standard colours') as Q0: + gold = hsv(0.11, 0.8, 1) + brown = hsv(0.09, 0.8, 0.25); + Match('.background').color = brown + Match('.patterning').pattern = 'beads' + Match('#beads .odd').color = red(1) + Match('#beads .even').color = white(0) + Match('.hole').color = white(1) + Match('.inside').color = brown * 0.25 + Match('.left').color = gold + Match('.right').color = gold * 0.9 + Match('.bottom').color = gold * 0.8 + Match('.liner').color = green(0.5) + Match('.liner').lineDash = EyelinerThickness + + with Cue('Q1', name='Spinning triangles') as Q1: period = 10 count = t / period - Match('#outer').rotate = count - Match('#inner').rotate = -count + Match('#triangles').rotate = count with Cue('Q2', name='Blinking eyes') as Q2: period = 3 @@ -176,21 +170,53 @@ with Cue('Q2', name='Blinking eyes') as Q2: with Cue('Q3', name='Nightmare spots') as Q3: period = 1 count = t / period - Match('#beads .odd').scale = 1.5 * sine(count) - Match('#beads .even').scale = 1.5 * (1 - sine(count)) + Match('.bead.odd').scale = 2 * sine(count) + Match('.bead.even').scale = 2 * (1 - sine(count)) with Cue('Q4', name='Rotating triangles'): period = 5 count = t / period - Match('.triangle.outer').rotate = count - Match('.triangle.outer').scale = 1 + 2*sine((count + i / n) * 3) - Match('.triangle.outer').y = -OuterTriangleRadius - TriangleHeight * sine((count + i / n) * 3) - Match('#inner_triangles').fader = 0 + scale = 2*sine((count + i / n) * 5) + Match('.triangle').rotate = count + Match('.triangle').scale = 1 + scale + Match('.triangle').y = -TriangleRadius - TriangleHeight * scale +with Cue('Q5', name='Talking'): + period = 0.5 + duration = 0.1 + 0.2 * beta('Q5.duration')[count] + count = t / period + cycle = t % period + offset = beta('Q5.offset')[count] * (period - duration) + wag = when((cycle > offset) & (cycle < offset + duration), impulse((cycle - offset) / duration), 0) + height = uniform('Q5.height')[count] * (TongueHeight - MouthCornerRadius * 2) + Match('.tongue > RoundedRect').height = TongueHeight - height * wag + +with Cue('Q6', name='Rotating mask'): + period = 10 + count = t / period + Match('#mask').rotate = count + +with Cue('Q7', name='Rainbow triangles'): + period = 5 + count = t / period + color = hsv(count + i / n, 1, 1) + Match('.triangle .left').color = color + Match('.triangle .right').color = color * 0.9 + +with Cue('Q8', name='Twinkling stars'): + period = 0.5 + phase = uniform('Q8.phase')[i] + count = t / period + phase + with Match('.star') as m: + m.x = (beta('Q8.x', i)[count] + 0.5) % 1 * screen.width + m.y = (beta('Q8.y', i)[count] + 0.5) % 1 * screen.height + m.scale = uniform('Q8.scale', i)[count] + m.fader = sine(count) #%% +client.start('#Q0') client.stop('#Q1') client.start('#Q2') -client.start('#Q4') - +client.stop('#Q3') +client.stop('#Q4') diff --git a/research/4437_mask3a.jpg b/research/4437_mask3a.jpg index 6a1cf16..a018579 100644 Binary files a/research/4437_mask3a.jpg and b/research/4437_mask3a.jpg differ