Skip to content

Instantly share code, notes, and snippets.

@adamml
Created September 1, 2022 21:06
Show Gist options
  • Save adamml/577c48a142f14d4e3f88e3c1f7e327b9 to your computer and use it in GitHub Desktop.
Save adamml/577c48a142f14d4e3f88e3c1f7e327b9 to your computer and use it in GitHub Desktop.
Render wave time series from Erddap in Blender
import json
import bmesh
import urllib.request
import datetime
import math
def drawpolygon(xdata, ydata,
xnorm = None, ynorm=None, zscale=None, colour=(1, 1, 1, 1)):
if xnorm is not None:
xdata = list(map(lambda x, seriesmax=max(xdata), seriesmin=min(xdata), norm=xnorm: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], xdata))
if ynorm is not None:
ydata = list(map(lambda x, seriesmax=max(ydata), seriesmin=min(ydata), norm=ynorm: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], ydata))
verts = list(map(lambda x, y, z: (x, y, z), xdata, ydata, [0] * len(xdata)))
if zscale is not None:
verts += list(map(lambda x, y, z: (x, y, z), xdata, ydata, [zscale] * len(xdata)))
edges = []
faces = [tuple(range(0, len(xdata) - 1))]
if zscale is not None:
faces += [tuple(range(len(xdata), (len(xdata)*2) - 1))]
faces += list(map(lambda x: (x, x+1, x+1+len(xdata), x+len(xdata)), range(0, len(xdata)-2)))
__mesh = bpy.data.meshes.new('foo')
__mesh.from_pydata(verts, edges, faces)
__material = bpy.data.materials.new("MaterialName")
__material.diffuse_color = colour
__mesh.materials.append(__material)
__mesh.update()
__object = bpy.data.objects.new('boo', __mesh)
__collection = bpy.data.collections.new('baz')
bpy.context.scene.collection.children.link(__collection)
__collection.objects.link(__object)
def blenderplot(xdata, ydata, zdata, xnorm=None, ynorm=None, znorm=None,
penwidth=0.05, penheight=0.05, colour=(1, 1, 1, 1), name='Foo'):
""" Creates an x,y,z line plot in Blender
:param xdata:
:type xdata: list
:param ydata:
:type ydata: list
:param zdata:
:type zdata: list
:param xnorm:
:type xnorm: list, defaults to None
:param ynorm:
:type ynorm: list, defaults to None
:param znorm:
:type znorm: list, defaults to None
:param penwidth:
:type penwidth: float, defaults to 0.1
:param penheight:
:type penheight: float, defaults to 0.1
:raises AttributeError:
:return: None
:rtype: None
"""
if len(xdata) != len(ydata):
raise AttributeError('xdata and ydata must be of equal length')
if len(ydata) != len(zdata):
raise AttributeError('ydata and zdata must be of equal length')
if xnorm is not None and len(xnorm) != 2:
raise AttributeError('xnorm must be None or length 2')
if ynorm is not None and len(ynorm) != 2:
raise AttributeError('ynorm must be None or length 2')
if znorm is not None and len(znorm) != 2:
raise AttributeError('znorm must be None or length 2')
if xnorm is not None:
xdata = list(map(lambda x, seriesmax=max(xdata), seriesmin=min(xdata), norm=xnorm: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], xdata))
if ynorm is not None:
ydata = list(map(lambda x, seriesmax=max(ydata), seriesmin=min(ydata), norm=ynorm: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], ydata))
if znorm is not None:
zdata = list(map(lambda x, seriesmax=max(zdata), seriesmin=min(zdata), norm=znorm: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], zdata))
verts = list(map(lambda x, y, z: (x, y-(penwidth/2), z-(penheight/2)), xdata, ydata, zdata))
verts += list(map(lambda x, y, z: (x, y+(penwidth/2), z-(penheight/2)), xdata, ydata, zdata))
verts += list(map(lambda x, y, z: (x, y-(penwidth/2), z+(penheight/2)), xdata, ydata, zdata))
verts += list(map(lambda x, y, z: (x, y+(penwidth/2), z+(penheight/2)), xdata, ydata, zdata))
edges = []
faces = list(map(lambda x, sizex=len(xdata): [x, x+1, x+sizex+1, x+sizex,], range(0, len(xdata)-1)))
faces += list(map(lambda x, sizex=len(xdata): [x+(sizex*2), x+(sizex*2)+1, x+(sizex*3)+1, x+(sizex*3)], range(0, len(xdata)-1)))
faces += [[1, len(xdata), len(xdata)*3, len(xdata)*2]]
faces += [[len(xdata)-1, (len(xdata)*2)-1, (len(xdata)*4)-1, (len(xdata)*3)-1]]
faces += list(map(lambda x, sizex=len(xdata): [x, x+1, x+(sizex*2)+1, x+(sizex*2)], range(0, len(xdata)-1)))
faces += list(map(lambda x, sizex=len(xdata): [x+(sizex), x+(sizex)+1,x+(sizex*3)+1, x+(sizex*3)], range(0, len(xdata)-1)))
__mesh = bpy.data.meshes.new(name)
__mesh.from_pydata(verts, edges, faces)
__material = bpy.data.materials.new("MaterialName")
__material.diffuse_color = colour
__mesh.materials.append(__material)
__mesh.update()
__object = bpy.data.objects.new(name, __mesh)
__collection = bpy.data.collections.new(name)
bpy.context.scene.collection.children.link(__collection)
__collection.objects.link(__object)
with open('counties.geojson',
encoding='utf-8', errors='replace') as data:
dat = json.load(data)
for row in dat['features']:
if row['geometry']['type'] == 'Polygon':
x = list(map(lambda x: x[0], row['geometry']['coordinates'][0]))
y = list(map(lambda x: x[1], row['geometry']['coordinates'][0]))
drawpolygon(x, y, zscale=0.1, colour=(0.0157, 0.3882, 0.0275, 1))
elif row['geometry']['type'] == 'MultiPolygon':
print('MultiPolygon', len(row['geometry']['coordinates']))
for poly in row['geometry']['coordinates']:
x = list(map(lambda x: x[0], poly[0]))
y = list(map(lambda x: x[1], poly[0]))
drawpolygon(x, y, zscale=0.1, colour=(0.0157, 0.3882, 0.0275, 1))
#
# Add a white base for rendering
#
vertices = [(-20,-40,0), (20, -40, 0), (20,70,0), (-20, 70, 0)]
faces=[[0, 1, 2, 3]]
__mesh = bpy.data.meshes.new('base_mesh')
__mesh.from_pydata(vertices, [], faces)
__material = bpy.data.materials.new("BaseMAterial")
__material.diffuse_color = (1.0, 1.0, 1.0, 1.0)
__mesh.materials.append(__material)
__mesh.update()
__object = bpy.data.objects.new('base_object', __mesh)
__collection = bpy.data.collections.new('base')
bpy.context.scene.collection.children.link(__collection)
__collection.objects.link(__object)
with urllib.request.urlopen('https://erddap.marine.ie/erddap/tabledap/IWBNetwork.json?station_id%2Clongitude%2Clatitude%2Ctime%2CHmax&time%3E=2021-11-01T00%3A00%3A00Z&time%3C2021-11-30T23%3A59%3A59Z') as resp:
__json = json.loads(resp.read())
m1 = {'latitude': None, 'longitude': None, 'waveheight': []}
m2 = {'latitude': None, 'longitude': None, 'waveheight': []}
m3 = {'latitude': None, 'longitude': None, 'waveheight': []}
m4 = {'latitude': None, 'longitude': None, 'waveheight': []}
m5 = {'latitude': None, 'longitude': None, 'waveheight': []}
m6 = {'latitude': None, 'longitude': None, 'waveheight': []}
ametsa = {'latitude': None, 'longitude': None, 'waveheight': []}
smartbay = {'latitude': None, 'longitude': None, 'waveheight': []}
brandon = {'latitude': None, 'longitude': None, 'waveheight': []}
bantry = {'latitude': None, 'longitude': None, 'waveheight': []}
clew = {'latitude': None, 'longitude': None, 'waveheight': []}
times_weather = list(map(lambda x: datetime.datetime.timestamp(datetime.datetime(2021, 11, 1, 0, 0, 0)) + (x * 3600), range(0, 30 * 24)))
times_wave = list(map(lambda x: datetime.datetime.timestamp(datetime.datetime(2021, 11, 1, 0, 0, 0)) + (x * 1800), range(0, 30 * 48)))
for row in __json['table']['rows']:
if row[0] == 'M1':
if m1['latitude'] == None:
m1['latitude'] = row[2]
if m1['longitude'] == None:
m1['longitude'] = row[1]
if not len(m1['waveheight']):
m1['waveheight'] = [0] * len(times_weather)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[3], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_weather))
m1['waveheight'][__timediff.index(min(__timediff))] = row[4]
if row[0] == 'M2':
if m2['latitude'] == None:
m2['latitude'] = row[2]
if m2['longitude'] == None:
m2['longitude'] = row[1]
if not len(m2['waveheight']):
m2['waveheight'] = [0] * len(times_weather)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[3], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_weather))
m2['waveheight'][__timediff.index(min(__timediff))] = row[4]
if row[0] == 'M3':
if m3['latitude'] == None:
m3['latitude'] = row[2]
if m3['longitude'] == None:
m3['longitude'] = row[1]
if not len(m3['waveheight']):
m3['waveheight'] = [0] * len(times_weather)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[3], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_weather))
m3['waveheight'][__timediff.index(min(__timediff))] = row[4]
if row[0] == 'M4':
if m4['latitude'] == None:
m4['latitude'] = row[2]
if m4['longitude'] == None:
m4['longitude'] = row[1]
if not len(m4['waveheight']):
m4['waveheight'] = [0] * len(times_weather)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[3], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_weather))
m4['waveheight'][__timediff.index(min(__timediff))] = row[4]
if row[0] == 'M5':
if m5['latitude'] == None:
m5['latitude'] = row[2]
if m5['longitude'] == None:
m5['longitude'] = row[1]
if not len(m5['waveheight']):
m5['waveheight'] = [0] * len(times_weather)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[3], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_weather))
m5['waveheight'][__timediff.index(min(__timediff))] = row[4]
if row[0] == 'M6':
if m6['latitude'] == None:
m6['latitude'] = row[2]
if m6['longitude'] == None:
m6['longitude'] = row[1]
if not len(m6['waveheight']):
m6['waveheight'] = [0] * len(times_weather)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[3], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_weather))
m6['waveheight'][__timediff.index(min(__timediff))] = row[4]
with urllib.request.urlopen('https://erddap.marine.ie/erddap/tabledap/IWaveBNetwork30Min.json?longitude%2Clatitude%2Ctime%2Cstation_id%2CHmax&time%3E=2021-11-01T00%3A00%3A00Z&time%3C2021-11-30T23%3A59%3A59Z') as resp:
__json = json.loads(resp.read())
for row in __json['table']['rows']:
if(row[4] is not None):
if row[3] == 'AMETS Berth A Wave Buoy':
if ametsa['latitude'] == None:
ametsa['latitude'] = row[1]
if ametsa['longitude'] == None:
ametsa['longitude'] = row[0]
if not len(ametsa['waveheight']):
ametsa['waveheight'] = [0] * len(times_wave)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[2], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_wave))
ametsa['waveheight'][__timediff.index(min(__timediff))] = (row[4]/100)
if row[3] == 'SmartBay Wave Buoy':
if smartbay['latitude'] == None:
smartbay['latitude'] = row[1]
if smartbay['longitude'] == None:
smartbay['longitude'] = row[0]
if not len(smartbay['waveheight']):
smartbay['waveheight'] = [0] * len(times_wave)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[2], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_wave))
smartbay['waveheight'][__timediff.index(min(__timediff))] = (row[4]/100)
if row[3] == 'Brandon Bay':
if brandon['latitude'] == None:
brandon['latitude'] = row[1]
if brandon['longitude'] == None:
brandon['longitude'] = row[0]
if not len(brandon['waveheight']):
brandon['waveheight'] = [0] * len(times_wave)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[2], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_wave))
brandon['waveheight'][__timediff.index(min(__timediff))] = (row[4]/100)
if row[3] == 'Clew Bay':
if clew['latitude'] == None:
clew['latitude'] = row[1]
if clew['longitude'] == None:
clew['longitude'] = row[0]
if not len(clew['waveheight']):
clew['waveheight'] = [0] * len(times_wave)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[2], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_wave))
clew['waveheight'][__timediff.index(min(__timediff))] = (row[4]/100)
if row[3] == 'Bantry Bay':
if bantry['latitude'] == None:
bantry['latitude'] = row[1]
if bantry['longitude'] == None:
bantry['longitude'] = row[0]
if not len(bantry['waveheight']):
bantry['waveheight'] = [0] * len(times_wave)
__timestamp = datetime.datetime.timestamp(datetime.datetime.strptime(row[2], '%Y-%m-%dT%H:%M:%S%z'))
__timediff = list(map(lambda x: abs(x - __timestamp), times_wave))
bantry['waveheight'][__timediff.index(min(__timediff))] = (row[4]/100)
maxwh = (max(m1['waveheight']+m2['waveheight']+m3['waveheight']+m4['waveheight']+m5['waveheight']+m6['waveheight']+ametsa['waveheight']+brandon['waveheight']+bantry['waveheight']+clew['waveheight']+smartbay['waveheight']))
m1['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], m1['waveheight']))
m2['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], m2['waveheight']))
m3['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], m3['waveheight']))
m4['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], m4['waveheight']))
m5['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], m5['waveheight']))
m6['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], m6['waveheight']))
smartbay['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], smartbay['waveheight']))
ametsa['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], ametsa['waveheight']))
brandon['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], brandon['waveheight']))
bantry['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], bantry['waveheight']))
clew['waveheight'] = list(map(lambda x, seriesmax=maxwh, seriesmin=0, norm=[0, maxwh]: (((x - seriesmin)/(seriesmax - seriesmin)) * (norm[1]-norm[0])) + norm[0], clew['waveheight']))
i = 0
m1plot = [0] * 24
m2plot = [0] * 24
m3plot = [0] * 24
m4plot = [0] * 24
m5plot = [0] * 24
m6plot = [0] * 24
ametsplot = [0] * 48
bantryplot = [0] * 48
brandonplot = [0] * 48
clewplot = [0] * 48
smartbayplot = [0] * 48
print(m2['waveheight'])
frame = 0
for scene in times_wave:
bpy.context.scene.frame_set(frame)
if i == 0 or i % 2 == 0:
if i / 2 < 24:
if len(m1['waveheight']):
m1plot[int(i/2)] = m1['waveheight'][int(i/2)]
if len(m2['waveheight']):
m2plot[int(i/2)] = m2['waveheight'][int(i/2)]
if len(m3['waveheight']):
m3plot[int(i/2)] = m3['waveheight'][int(i/2)]
if len(m4['waveheight']):
m4plot[int(i/2)] = m4['waveheight'][int(i/2)]
if len(m5['waveheight']):
m5plot[int(i/2)] = m5['waveheight'][int(i/2)]
if len(m6['waveheight']):
m6plot[int(i/2)] = m6['waveheight'][int(i/2)]
else:
if len(m1['waveheight']):
del m1plot[0]
m1plot.append(m1['waveheight'][int(i/2)])
if len(m2['waveheight']):
del m2plot[0]
m2plot.append(m2['waveheight'][int(i/2)])
if len(m3['waveheight']):
del m3plot[0]
m3plot.append(m3['waveheight'][int(i/2)])
if len(m4['waveheight']):
del m4plot[0]
m4plot.append(m4['waveheight'][int(i/2)])
if len(m5['waveheight']):
del m5plot[0]
m5plot.append(m5['waveheight'][int(i/2)])
if len(m6['waveheight']):
del m6plot[0]
m6plot.append(m6['waveheight'][int(i/2)])
if i < 48:
if len(ametsa['waveheight']):
ametsplot[i] = ametsa['waveheight'][i]
if len(bantry['waveheight']):
bantryplot[i] = bantry['waveheight'][i]
if len(brandon['waveheight']):
brandonplot[i] = brandon['waveheight'][i]
if len(clew['waveheight']):
clewplot[i] = clew['waveheight'][i]
if len(smartbay['waveheight']):
smartbayplot[i] = smartbay['waveheight'][i]
else:
if len(ametsa['waveheight']):
del ametsplot[0]
ametsplot.append(ametsa['waveheight'][i])
if len(bantry['waveheight']):
del bantryplot[0]
bantryplot.append(bantry['waveheight'][i])
if len(brandon['waveheight']):
del brandonplot[0]
brandonplot.append(brandon['waveheight'][i])
if len(clew['waveheight']):
del clewplot[0]
clewplot.append(clew['waveheight'][i])
if len(smartbay['waveheight']):
del smartbayplot[0]
smartbayplot.append(smartbay['waveheight'][i])
for obj in bpy.context.scene.objects:
if obj.name in ['m1', 'm2', 'm3', 'm4', 'm5', 'm6', 'ametsa', 'bantry', 'brandon', 'clew', 'smartbay']:
obj.select_set(True)
else:
obj.select_set(False)
bpy.ops.object.delete()
i += 1
if len(m1['waveheight']):
blenderplot(range(0,len(m1plot),1), m1plot, xnorm=[m1['longitude']-0.3, m1['longitude']+0.3], ynorm=[m1['latitude']-0.1, m1['latitude']+0.1], name='m1')
if len(m2['waveheight']):
blenderplot(range(0,len(m2plot),1), m2plot,[0.15]*len(m2plot), xnorm=[m2['longitude']-0.3, m2['longitude']+0.3], ynorm=[m2['latitude']-0.1, m2['latitude']+0.1], name='m2')
if len(m3['waveheight']):
blenderplot(range(0,len(m3plot),1), m3plot,[0.15]*len(m3plot), xnorm=[m3['longitude']-0.3, m3['longitude']+0.3], ynorm=[m3['latitude']-0.1, m3['latitude']+0.1], name='m3')
if len(m4['waveheight']):
blenderplot(range(0,len(m4plot),1), m4plot,[0.15]*len(m4plot), xnorm=[m4['longitude']-0.3, m4['longitude']+0.3], ynorm=[m4['latitude']-0.1, m4['latitude']+0.1], name='m4')
if len(m5['waveheight']):
blenderplot(range(0,len(m5plot),1), m5plot,[0.15]*len(m5plot), xnorm=[m5['longitude']-0.3, m5['longitude']+0.3], ynorm=[m5['latitude']-0.1, m5['latitude']+0.1], name='m5')
if len(m6['waveheight']):
blenderplot(range(0,len(m6plot),1), m6plot,[0.15]*len(m6plot), xnorm=[m6['longitude']-0.3, m6['longitude']+0.3], ynorm=[m6['latitude']-0.1, m6['latitude']+0.1], name='m6')
if len(smartbay['waveheight']):
blenderplot(range(0,len(smartbayplot),1), smartbayplot,[0.15]*len(smartbayplot), xnorm=[smartbay['longitude']-0.3, smartbay['longitude']+0.3], ynorm=[smartbay['latitude']-0.1, smartbay['latitude']+0.1], name='smartbay')
if len(brandon['waveheight']):
blenderplot(range(0,len(brandonplot),1), brandonplot,[0.15]*len(brandonplot), xnorm=[brandon['longitude']-0.3, brandon['longitude']+0.3], ynorm=[brandon['latitude']-0.1, brandon['latitude']+0.1], name='brandon')
if len(bantry['waveheight']):
blenderplot(range(0,len(bantryplot),1), bantryplot,[0.15]*len(bantryplot), xnorm=[bantry['longitude']-0.3, bantry['longitude']+0.3], ynorm=[bantry['latitude']-0.1, bantry['latitude']+0.1], name='bantry')
if len(clew['waveheight']):
blenderplot(range(0,len(clewplot),1), clewplot,[0.15]*len(clewplot), xnorm=[clew['longitude']-0.3, clew['longitude']+0.3], ynorm=[clew['latitude']-0.1, clew['latitude']+0.1], name='clew')
if len(ametsa['waveheight']):
blenderplot(range(0,len(ametsplot),1), ametsplot,[0.15]*len(ametsplot), xnorm=[ametsa['longitude']-0.3, ametsa['longitude']+0.3], ynorm=[ametsa['latitude']-0.1, ametsa['latitude']+0.1], name='amets')
frame += math.floor((140 / len(times_wave)) * 24)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment